diff --git a/apps/www/package.json b/apps/www/package.json index 8ffdde38..74167edc 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -12,7 +12,8 @@ "typecheck": "vue-tsc", "typecheck:registry": "vue-tsc -p tsconfig.registry.json", "build:registry": "tsx ./scripts/build-registry.ts", - "build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts" + "build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts", + "docs:gen": "tsx ./scripts/autogen.ts" }, "dependencies": { "@formkit/auto-animate": "^0.8.2", @@ -44,6 +45,7 @@ "zod": "^3.23.3" }, "devDependencies": { + "@babel/traverse": "^7.24.1", "@iconify-json/lucide": "^1.1.180", "@iconify-json/ph": "^1.1.12", "@iconify-json/radix-icons": "^1.1.14", @@ -61,7 +63,9 @@ "@vue/compiler-dom": "^3.4.24", "@vue/tsconfig": "^0.5.1", "autoprefixer": "^10.4.19", + "fast-glob": "^3.3.2", "lodash-es": "^4.17.21", + "markdown-it": "^14.1.0", "pathe": "^1.1.2", "rimraf": "^5.0.5", "shiki": "^1.3.0", @@ -71,6 +75,7 @@ "typescript": "^5.4.5", "unplugin-icons": "^0.18.5", "vitepress": "^1.1.3", + "vue-component-meta": "^2.0.13", "vue-tsc": "^2.0.14" } } diff --git a/apps/www/scripts/autogen.ts b/apps/www/scripts/autogen.ts new file mode 100644 index 00000000..ca386e81 --- /dev/null +++ b/apps/www/scripts/autogen.ts @@ -0,0 +1,154 @@ +import { join, parse, resolve } from 'node:path' +import { existsSync, mkdirSync, writeFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import fg from 'fast-glob' +import MarkdownIt from 'markdown-it' +import type { ComponentMeta, MetaCheckerOptions, PropertyMeta, PropertyMetaSchema } from 'vue-component-meta' +import { createComponentMetaChecker } from 'vue-component-meta' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +const md = new MarkdownIt() + +const ROOTPATH = '../' +const OUTPUTPATH = '../src/content/meta' + +const checkerOptions: MetaCheckerOptions = { + forceUseTs: true, + printer: { newLine: 1 }, +} + +const tsconfigChecker = createComponentMetaChecker( + resolve(__dirname, ROOTPATH, 'tsconfig.registry.json'), + checkerOptions, +) + +const components = fg.sync(['chart/**/*.vue', 'chart*/**/*.vue'], { + cwd: resolve(__dirname, ROOTPATH, 'src/lib/registry/default/ui/'), + absolute: true, +}) + +components.forEach((componentPath) => { + try { + const componentName = parse(componentPath).name + const meta = parseMeta(tsconfigChecker.getComponentMeta(componentPath)) + + const metaDirPath = resolve(__dirname, OUTPUTPATH) + // if meta dir doesn't exist create + if (!existsSync(metaDirPath)) + mkdirSync(metaDirPath) + + const metaMdFilePath = join(metaDirPath, `${componentName}.md`) + + let parsedString = '\n\n' + if (meta.props.length) + parsedString += `\n` + + if (meta.events.length) + parsedString += `\n\n` + + if (meta.slots.length) + parsedString += `\n\n` + + if (meta.methods.length) + parsedString += `\n\n` + + writeFileSync(metaMdFilePath, parsedString) + } + catch (err) { + console.log(err) + } +}) + +function parseTypeFromSchema(schema: PropertyMetaSchema): string { + if (typeof schema === 'object' && (schema.kind === 'enum' || schema.kind === 'array')) { + const isFlatEnum = schema.schema?.every(val => typeof val === 'string') + const enumValue = schema?.schema?.filter(i => i !== 'undefined') ?? [] + + if (isFlatEnum && /^[A-Z]/.test(schema.type)) + return enumValue.join(' | ') + else if (typeof schema.schema?.[0] === 'object' && schema.schema?.[0].kind === 'enum') + return schema.schema.map((s: PropertyMetaSchema) => parseTypeFromSchema(s)).join(' | ') + else + return schema.type + } + else if (typeof schema === 'object' && schema.kind === 'object') { + return schema.type + } + else if (typeof schema === 'string') { + return schema + } + else { + return '' + } +} + +// Utilities +function parseMeta(meta: ComponentMeta) { + const props = meta.props + // Exclude global props + .filter(prop => !prop.global) + .map((prop) => { + let defaultValue = prop.default + let type = prop.type + const { name, description, required } = prop + + if (name === 'as') + defaultValue = defaultValue ?? '"div"' + + if (defaultValue === 'undefined') + defaultValue = undefined + + if (!type.includes('AcceptableValue')) + type = parseTypeFromSchema(prop.schema) || type + + return ({ + name, + description: md.render(description), + type: type.replace(/\s*\|\s*undefined/g, ''), + required, + default: defaultValue ?? undefined, + }) + }) + + const events = meta.events + .map((event) => { + const { name, type } = event + return ({ + name, + type: type.replace(/\s*\|\s*undefined/g, ''), + }) + }) + + const defaultSlot = meta.slots?.[0] + const slots: { name: string, description: string, type: string }[] = [] + + if (defaultSlot && defaultSlot.type !== '{}') { + const schema = defaultSlot.schema + if (typeof schema === 'object' && schema.schema) { + Object.values(schema.schema).forEach((childMeta: PropertyMeta) => { + slots.push({ + name: childMeta.name, + description: md.render(childMeta.description), + type: parseTypeFromSchema(childMeta.schema), + }) + }) + } + } + + // exposed method + const methods = meta.exposed + .filter(expose => typeof expose.schema === 'object' && expose.schema.kind === 'event') + .map(expose => ({ + name: expose.name, + description: md.render(expose.description), + type: expose.type, + })) + + return { + props, + events, + slots, + methods, + } +} diff --git a/apps/www/src/content/meta/AreaChart.md b/apps/www/src/content/meta/AreaChart.md new file mode 100644 index 00000000..c709e645 --- /dev/null +++ b/apps/www/src/content/meta/AreaChart.md @@ -0,0 +1,116 @@ + + + + + diff --git a/apps/www/src/content/meta/BarChart.md b/apps/www/src/content/meta/BarChart.md new file mode 100644 index 00000000..1800c227 --- /dev/null +++ b/apps/www/src/content/meta/BarChart.md @@ -0,0 +1,116 @@ + + + + + diff --git a/apps/www/src/content/meta/ChartCrosshair.md b/apps/www/src/content/meta/ChartCrosshair.md new file mode 100644 index 00000000..b90a9c32 --- /dev/null +++ b/apps/www/src/content/meta/ChartCrosshair.md @@ -0,0 +1,29 @@ + + + diff --git a/apps/www/src/content/meta/ChartLegend.md b/apps/www/src/content/meta/ChartLegend.md new file mode 100644 index 00000000..de294215 --- /dev/null +++ b/apps/www/src/content/meta/ChartLegend.md @@ -0,0 +1,22 @@ + + + + + diff --git a/apps/www/src/content/meta/ChartSingleTooltip.md b/apps/www/src/content/meta/ChartSingleTooltip.md new file mode 100644 index 00000000..ddb8e99f --- /dev/null +++ b/apps/www/src/content/meta/ChartSingleTooltip.md @@ -0,0 +1,43 @@ + + + + + diff --git a/apps/www/src/content/meta/ChartTooltip.md b/apps/www/src/content/meta/ChartTooltip.md new file mode 100644 index 00000000..9fc0fb40 --- /dev/null +++ b/apps/www/src/content/meta/ChartTooltip.md @@ -0,0 +1,16 @@ + + + diff --git a/apps/www/src/content/meta/DonutChart.md b/apps/www/src/content/meta/DonutChart.md new file mode 100644 index 00000000..bdaecbd6 --- /dev/null +++ b/apps/www/src/content/meta/DonutChart.md @@ -0,0 +1,82 @@ + + + diff --git a/apps/www/src/content/meta/LineChart.md b/apps/www/src/content/meta/LineChart.md new file mode 100644 index 00000000..c6f2ac09 --- /dev/null +++ b/apps/www/src/content/meta/LineChart.md @@ -0,0 +1,109 @@ + + + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79d339a9..96fa1de3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,6 +123,9 @@ importers: specifier: ^3.23.3 version: 3.23.3 devDependencies: + '@babel/traverse': + specifier: ^7.24.1 + version: 7.24.1 '@iconify-json/lucide': specifier: ^1.1.180 version: 1.1.184 @@ -174,9 +177,15 @@ importers: autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.38) + fast-glob: + specifier: ^3.3.2 + version: 3.3.2 lodash-es: specifier: ^4.17.21 version: 4.17.21 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 pathe: specifier: ^1.1.2 version: 1.1.2 @@ -204,6 +213,9 @@ importers: vitepress: specifier: ^1.1.3 version: 1.1.3(@algolia/client-search@4.23.3)(@types/node@20.12.7)(axios@0.18.1)(postcss@8.4.38)(search-insights@2.13.0)(terser@5.30.4)(typescript@5.4.5) + vue-component-meta: + specifier: ^2.0.13 + version: 2.0.14(typescript@5.4.5) vue-tsc: specifier: ^2.0.14 version: 2.0.14(typescript@5.4.5) @@ -4846,6 +4858,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + lint-staged@15.2.2: resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==} engines: {node: '>=18.12.0'} @@ -5001,6 +5016,10 @@ packages: mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} @@ -5013,6 +5032,9 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} @@ -5917,6 +5939,10 @@ packages: pumpify@1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6761,6 +6787,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} @@ -7229,6 +7258,17 @@ packages: vue-bundle-renderer@2.0.0: resolution: {integrity: sha512-oYATTQyh8XVkUWe2kaKxhxKVuuzK2Qcehe+yr3bGiaQAhK3ry2kYE4FWOfL+KO3hVFwCdLmzDQTzYhTi9C+R2A==} + vue-component-meta@2.0.14: + resolution: {integrity: sha512-6ycN+5bkLLNsjno5pX+OFmFxrAXllJo95lk7jPD7g7cbtWsZY5F+pg+/YMXldHA36STrBHTfCu5QoklDV9Gynw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vue-component-type-helpers@2.0.14: + resolution: {integrity: sha512-DInfgOyXlMyliyqAAD9frK28tTfch0+tMi4qoWJcZlRxUf+NFAtraJBnAsKLep+FOyLMiajkhfyEb3xLK08i7w==} + vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -12721,6 +12761,10 @@ snapshots: lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + lint-staged@15.2.2: dependencies: chalk: 5.3.0 @@ -12946,6 +12990,15 @@ snapshots: mark.js@8.11.1: {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + mdast-util-from-markdown@0.8.5: dependencies: '@types/mdast': 3.0.15 @@ -12962,6 +13015,8 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + meow@12.1.1: {} merge-stream@2.0.0: {} @@ -14077,6 +14132,8 @@ snapshots: inherits: 2.0.4 pump: 2.0.1 + punycode.js@2.3.1: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -14962,6 +15019,8 @@ snapshots: typescript@5.4.5: {} + uc.micro@2.1.0: {} + ufo@1.5.3: {} ultrahtml@1.5.3: {} @@ -15598,6 +15657,17 @@ snapshots: dependencies: ufo: 1.5.3 + vue-component-meta@2.0.14(typescript@5.4.5): + dependencies: + '@volar/typescript': 2.2.0-alpha.10 + '@vue/language-core': 2.0.14(typescript@5.4.5) + path-browserify: 1.0.1 + vue-component-type-helpers: 2.0.14 + optionalDependencies: + typescript: 5.4.5 + + vue-component-type-helpers@2.0.14: {} + vue-demi@0.14.7(vue@3.4.24(typescript@5.4.5)): dependencies: vue: 3.4.24(typescript@5.4.5)