Initial docs rendering kinda works

This commit is contained in:
Benjamin Bädorf 2022-03-16 00:03:45 +01:00
parent ada6cbf461
commit b699a39b0f
No known key found for this signature in database
GPG key ID: 4406E80E13CD656C
28 changed files with 11437 additions and 10845 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,11 @@
import { Component } from 'vue'; import { Component } from 'vue';
/**
* Schlechtenburg keeps track of the rendered block tree.
* This is useful for e.g. the tree select component in the editor header.
*
* @internal
*/
export interface ITreeNode { export interface ITreeNode {
id: string; id: string;
name: string; name: string;
@ -7,20 +13,131 @@ export interface ITreeNode {
children: ITreeNode[]; children: ITreeNode[];
} }
/**
* Schlechtenburg inputs and outputs a plain JS Object that can be JSON stringified. This is the
* interface type for that data structure. <T> will be the data type of the specific block being
*
* @see SbMain
*/
export interface IBlockData<T> { export interface IBlockData<T> {
id: string; id: string;
name: string; name: string;
data: T; data: T;
} }
export type OnUpdateSelfCb<T> = (updated: Partial<T>) => void;
/**
* Callback type for sending full block updates. SbBlock takes this as a prop.
*
* ```
* <SbBlock onUpdate={myFn as OnUpdateSelfCb}></SbBlock>
* ```
*
* @see SbBlock
*/
export type OnUpdateBlockCb = (updated: IBlockData<any>) => void; export type OnUpdateBlockCb = (updated: IBlockData<any>) => void;
/**
* Callback type for sending partial self-updates in edit mode.
*
* ```
* props: {
* onUpdate: {
* type: (null as unknown) as PropType<OnUpdateSelfCb<IYourComponentData>>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnUpdateSelfCb<T> = (updated: Partial<T>) => void;
/**
* Callback type for sending blocks that should be prepended as a sibling before the current block
*
* ```
* props: {
* onPrependBlock: {
* type: (null as unknown) as PropType<OnPrependBlockCb<IComponentToBePrependedData>>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnPrependBlockCb = (block: IBlockData<any>) => void; export type OnPrependBlockCb = (block: IBlockData<any>) => void;
/**
* Callback type for sending blocks that should be appended as a sibling after the current block
*
* ```
* props: {
* onAppendBlock: {
* type: (null as unknown) as PropType<OnAppendBlockCb<IComponentToBeAppendedData>>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnAppendBlockCb = (block: IBlockData<any>) => void; export type OnAppendBlockCb = (block: IBlockData<any>) => void;
/**
* Callback type for removing the current block.
*
* ```
* props: {
* onRemoveSelf: {
* type: (null as unknown) as PropType<OnRemoveSelfCb>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnRemoveSelfCb = () => void; export type OnRemoveSelfCb = () => void;
/**
* Callback type for activating the previous block.
*
* ```
* props: {
* onActivatePrevious: {
* type: (null as unknown) as PropType<OnActivatePreviousCb>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnActivatePreviousCb = () => void; export type OnActivatePreviousCb = () => void;
/**
* Callback type for activating the next block.
*
* ```
* props: {
* onActivateNext: {
* type: (null as unknown) as PropType<OnActivateNextCb>,
* default: () => {},
* },
* }
* ```
*
* @see SbBlock
*/
export type OnActivateNextCb = () => void; export type OnActivateNextCb = () => void;
/**
* Any Block that you create
*
* @see IBlockDefinition
*/
export interface IBlockProps<T> { export interface IBlockProps<T> {
blockId?: string; blockId?: string;
data?: T, data?: T,
@ -32,6 +149,11 @@ export interface IBlockProps<T> {
onActivatePrevious?: OnActivatePreviousCb; onActivatePrevious?: OnActivatePreviousCb;
} }
/**
* Any Block that you create
*
* @see IBlockProps
*/
export interface IBlockDefinition<T> { export interface IBlockDefinition<T> {
name: string; name: string;
icon?: string; icon?: string;
@ -40,6 +162,11 @@ export interface IBlockDefinition<T> {
display: Component<IBlockProps<T>>; display: Component<IBlockProps<T>>;
} }
/**
* Schlechtenburg maintains a library of blocks that are available
*
* @internal
*/
export interface IBlockLibrary { export interface IBlockLibrary {
[name: string]: IBlockDefinition<any>; [name: string]: IBlockDefinition<any>;
} }

View file

@ -4,18 +4,30 @@ import { parse } from 'vue-docgen-api'
import { join } from 'path' import { join } from 'path'
import glob from 'glob-promise' import glob from 'glob-promise'
import TypeDoc from 'typedoc' import TypeDoc from 'typedoc'
import { writeFile } from 'fs/promises' import { writeFile, readFile } from 'fs/promises'
const DOCS_PATH = join(process.cwd(), process.argv[2] || './docs'); const DOCS_PATH = join(process.cwd(), process.argv[2] || './docs');
const LIB_PATH = join(process.cwd(), process.argv[3] || './lib'); const LIB_PATH = join(process.cwd(), process.argv[3] || './lib');
const getTSDocs = (outputFile) => { const transformTSDocs = (docs) => {
return {
...docs,
children: docs.children.map(child => ({
...child,
typeParameters: child.typeParameter,
})),
};
};
const getTSDocs = async (outputFile) => {
const app = new TypeDoc.Application(); const app = new TypeDoc.Application();
app.options.addReader(new TypeDoc.TSConfigReader()); app.options.addReader(new TypeDoc.TSConfigReader());
app.bootstrap(); app.bootstrap();
const project = app.convert(); const project = app.convert();
return app.generateJson(project, outputFile); await app.generateJson(project, outputFile);
const data = JSON.parse(await readFile(outputFile));
await writeFile(outputFile, JSON.stringify(transformTSDocs(data), null, 2))
}; };
const getVueComponentDocs = async (dir) => { const getVueComponentDocs = async (dir) => {
@ -37,11 +49,17 @@ const getVueComponentDocs = async (dir) => {
}; };
(async () => { (async () => {
const tsDocsOutput = join(DOCS_PATH, 'lib.json'); await Promise.all([
await getTSDocs(tsDocsOutput); (() => {
const tsDocsOutput = join(DOCS_PATH, 'lib.json');
return getTSDocs(tsDocsOutput);
})(),
const vueComponents = await getVueComponentDocs(LIB_PATH); (async () => {
const componentJsonPath = join(DOCS_PATH, 'components.json'); const vueComponents = await getVueComponentDocs(LIB_PATH);
console.log(`Info: JSON written to ${componentJsonPath}`); const componentJsonPath = join(DOCS_PATH, 'components.json');
await writeFile(componentJsonPath, JSON.stringify(vueComponents, null, 2)); console.log(`Info: JSON written to ${componentJsonPath}`);
return writeFile(componentJsonPath, JSON.stringify(vueComponents, null, 2));
})(),
])
})(); })();

View file

@ -2,7 +2,7 @@ import {
defineComponent, defineComponent,
} from 'vue'; } from 'vue';
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
import SideMenu from './Sidemenu'; import SideMenu from './sidemenu/Sidemenu';
import './App.scss'; import './App.scss';

View file

@ -0,0 +1,25 @@
import {
defineComponent,
PropType,
} from 'vue';
import { ComponentDoc } from 'vue-docgen-api';
import './ComponentDocs.scss';
export default defineComponent({
name: 'ComponentDocs',
props: {
docs: {
type: (null as unknown) as PropType<ComponentDoc>,
required: true,
},
},
setup(props) {
const docs = props.docs;
return () => <section class="docs component-docs">
<h2 id={docs.exportName}>{docs.exportName}</h2>
</section>;
},
});

View file

@ -1,12 +0,0 @@
import { defineComponent } from 'vue';
import './Sidemenu.scss';
export default defineComponent({
name: 'Sidemenu',
setup() {
return () => <div class="sidemenu">
</div>;
},
});

View file

@ -0,0 +1,36 @@
import {
defineComponent,
PropType,
} from 'vue';
import {
DeclarationReflection,
TypeParameterReflection,
} from 'typedoc';
import './TsDocs.scss';
const getTypeParamString = (params: TypeParameterReflection[]) => `<${params.map(p => p.name).join(', ')}>`;
export default defineComponent({
name: 'TsDocs',
props: {
docs: {
type: (null as unknown) as PropType<DeclarationReflection>,
required: true,
},
},
setup(props) {
const docs = props.docs;
console.log(docs);
return () => <section class="docs ts-docs">
<h2 id={docs.name}>
{docs.name}
{docs.typeParameters ? getTypeParamString(docs.typeParameters) : ''}
</h2>
<p>{docs.kindString}</p>
<p>{docs.comment}</p>
</section>;
},
});

View file

@ -1,9 +1,23 @@
import { ComponentDoc } from 'vue-docgen-api'; import { ComponentDoc } from 'vue-docgen-api';
import { ProjectReflection } from 'typedoc'; import { ProjectReflection } from 'typedoc';
import { getShortPackageName } from './package';
import coreComponents from '@schlechtenburg/core/docs/components.json'; import coreComponents from '@schlechtenburg/core/docs/components.json';
import coreLib from '@schlechtenburg/core/docs/lib.json'; import coreLib from '@schlechtenburg/core/docs/lib.json';
import layoutComponents from '@schlechtenburg/layout/docs/components.json';
import layoutLib from '@schlechtenburg/layout/docs/lib.json';
import headingComponents from '@schlechtenburg/heading/docs/components.json';
import headingLib from '@schlechtenburg/heading/docs/lib.json';
import paragraphComponents from '@schlechtenburg/paragraph/docs/components.json';
import paragraphLib from '@schlechtenburg/paragraph/docs/lib.json';
import imageComponents from '@schlechtenburg/image/docs/components.json';
import imageLib from '@schlechtenburg/image/docs/lib.json';
export interface IDocs { export interface IDocs {
components: ComponentDoc; components: ComponentDoc;
lib: ProjectReflection; lib: ProjectReflection;
@ -12,4 +26,33 @@ export interface IDocs {
export const core = { export const core = {
lib: coreLib, lib: coreLib,
components: coreComponents, components: coreComponents,
}; } as unknown as IDocs;
export const layout = {
lib: layoutLib,
components: layoutComponents,
} as unknown as IDocs;
export const heading = {
lib: headingLib,
components: headingComponents,
} as unknown as IDocs;
export const paragraph = {
lib: paragraphLib,
components: paragraphComponents,
} as unknown as IDocs;
export const image = {
lib: imageLib,
components: imageComponents,
} as unknown as IDocs;
export const getByName = (name: string) => ({
core,
layout,
heading,
paragraph,
image,
})[name];

View file

@ -7,6 +7,13 @@ import './main.scss';
const router = createRouter({ const router = createRouter({
routes, routes,
history: createWebHistory(), history: createWebHistory(),
scrollBehavior(to) {
if (to.hash) {
return { el: to.hash };
}
return { top: 0 };
}
}); });
const app = createApp(App); const app = createApp(App);

View file

@ -0,0 +1,5 @@
export const getShortPackageName = (name: string) => {
const parts = name.split('/');
console.log(parts);
return parts[1] || parts[0] || name;
}

View file

View file

@ -0,0 +1,38 @@
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { ComponentDoc } from 'vue-docgen-api';
import { DeclarationReflection } from 'typedoc';
import { getByName } from '../docs';
import ComponentDocs from '../ComponentDocs';
import TsDocs from '../TsDocs';
import './Package.scss';
export default defineComponent({
name: 'Package',
setup() {
const route = useRoute();
const packageName = route.params.package;
const docs = getByName(Array.isArray(packageName) ? packageName[0] : packageName);
if (!docs) {
return () => <div>Unknown package name {packageName}</div>
}
const { lib, components } = docs;
return () => <main class="package">
<h1>{lib.name}</h1>
<p>{lib.comment}</p>
<p>{lib.flags}</p>
{...(lib.children || []).map((child:DeclarationReflection) => {
const componentDocs = components.find((c: ComponentDoc) => c.exportName === child.name);
if (componentDocs) {
return <ComponentDocs docs={componentDocs}></ComponentDocs>
}
return <TsDocs docs={child}></TsDocs>
})}
</main>;
},
});

View file

@ -1,11 +1,13 @@
export default [ export default [
{ {
name: 'home',
path: '/', path: '/',
component: () => import('./Introduction').then(d => d.default), component: () => import('./pages/Introduction').then(d => d.default),
}, },
{ {
path: '/example', name: 'package',
component: () => import('./Example').then(d => d.default), path: '/@schlechtenburg/:package',
component: () => import('./pages/Package').then(d => d.default),
}, },
] ]

View file

View file

@ -0,0 +1,25 @@
import { defineComponent } from 'vue';
import {
core,
layout,
heading,
paragraph,
image,
} from '../docs';
import SidemenuPackage from './SidemenuPackage';
import './Sidemenu.scss';
export default defineComponent({
name: 'Sidemenu',
setup() {
return () => <div class="sidemenu">
<SidemenuPackage package={core}></SidemenuPackage>
<SidemenuPackage package={layout}></SidemenuPackage>
<SidemenuPackage package={heading}></SidemenuPackage>
<SidemenuPackage package={paragraph}></SidemenuPackage>
<SidemenuPackage package={image}></SidemenuPackage>
</div>;
},
});

View file

@ -0,0 +1,38 @@
import {
defineComponent,
PropType,
} from 'vue';
import { RouterLink } from 'vue-router';
import { getShortPackageName } from '../package';
import { IDocs } from '../docs';
export default defineComponent({
name: 'Sidemenu',
props: {
package: {
type: (null as unknown) as PropType<IDocs>,
required: true,
},
},
setup(props) {
const { lib, components } = props.package;
const shortName = getShortPackageName(lib.name);
return () => <div class="sidemenu-package">
<RouterLink to={{
name: 'package',
params: { package: shortName },
}}>{lib.name}</RouterLink>
<ul>
{...(lib.children || []).map(child => <li>
<RouterLink to={{
name: 'package',
params: { package: shortName },
hash: '#' + child.name,
}}>{child.name}</RouterLink>
</li>)}
</ul>
</div>;
},
});

View file

@ -431,6 +431,12 @@
"minimist": "^1.2.0" "minimist": "^1.2.0"
} }
}, },
"@cush/relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@cush/relative/-/relative-1.0.0.tgz",
"integrity": "sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==",
"dev": true
},
"@intlify/core": { "@intlify/core": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/@intlify/core/-/core-9.0.0.tgz", "resolved": "https://registry.npmjs.org/@intlify/core/-/core-9.0.0.tgz",
@ -1171,6 +1177,12 @@
"@types/istanbul-lib-report": "*" "@types/istanbul-lib-report": "*"
} }
}, },
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/micromatch": { "@types/micromatch": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz",
@ -3162,12 +3174,24 @@
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
} }
}, },
"glob-regex": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/glob-regex/-/glob-regex-0.3.2.tgz",
"integrity": "sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw==",
"dev": true
},
"globals": { "globals": {
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true "dev": true
}, },
"globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true
},
"graceful-fs": { "graceful-fs": {
"version": "4.2.9", "version": "4.2.9",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
@ -6188,6 +6212,18 @@
} }
} }
}, },
"recrawl-sync": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/recrawl-sync/-/recrawl-sync-2.2.1.tgz",
"integrity": "sha512-A2yLDgeXNaduJJMlqyUdIN7fewopnNm/mVeeGytS1d2HLXKpS5EthQ0j8tWeX+as9UXiiwQRwfoslKC+/gjqxg==",
"dev": true,
"requires": {
"@cush/relative": "^1.0.0",
"glob-regex": "^0.3.0",
"slash": "^3.0.0",
"tslib": "^1.9.3"
}
},
"regex-not": { "regex-not": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@ -7075,6 +7111,35 @@
"integrity": "sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==", "integrity": "sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==",
"dev": true "dev": true
}, },
"tsconfig-paths": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz",
"integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
}
}
},
"tslib": { "tslib": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -7305,6 +7370,18 @@
} }
} }
}, },
"vite-tsconfig-paths": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-3.4.1.tgz",
"integrity": "sha512-SgK3/pnTuJ3i+gMSAWLR6VCPSw26bnxawrmXGvCDjJgk8MAQgmbCrFrAzfwbwZBXSqSuvWEuX04Wt73qJKx8fQ==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"globrex": "^0.1.2",
"recrawl-sync": "^2.0.3",
"tsconfig-paths": "^3.9.0"
}
},
"void-elements": { "void-elements": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",

View file

@ -47,6 +47,7 @@
"typescript": "^4.2.3", "typescript": "^4.2.3",
"vite": "^2.0.5", "vite": "^2.0.5",
"vite-plugin-md2vue": "^1.1.1", "vite-plugin-md2vue": "^1.1.1",
"vite-tsconfig-paths": "^3.4.1",
"vue-docgen-api": "^4.44.18" "vue-docgen-api": "^4.44.18"
} }
} }

View file

@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {
// "~/*": ["./lib/*"]
}
}
}

View file

@ -1,8 +1,10 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import vueJsx from '@vitejs/plugin-vue-jsx'; import vueJsx from '@vitejs/plugin-vue-jsx';
import vitePluginMd2Vue from 'vite-plugin-md2vue'; import vitePluginMd2Vue from 'vite-plugin-md2vue';
import viteTSConfigPaths from 'vite-tsconfig-paths';
export default defineConfig({ export default defineConfig({
root: './',
base: './', base: './',
resolve: { resolve: {
dedupe: [ 'vue' ], dedupe: [ 'vue' ],
@ -10,6 +12,7 @@ export default defineConfig({
plugins: [ plugins: [
vueJsx({}), vueJsx({}),
vitePluginMd2Vue(), vitePluginMd2Vue(),
viteTSConfigPaths(),
], ],
esbuild: { esbuild: {
jsxFactory: 'h', jsxFactory: 'h',

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff