@do11y/docs 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,7 @@ A very bare-bones tool to help document Vue components.
5
5
  - Write documentation in Markdown files that get treated as Vue components.
6
6
  - Import markdown files as routes.
7
7
  - Add sandbox components - e.g. `Button.sandbox.vue` will be available on the url `/sandbox?id=button`.
8
+ - Easily document components with [vue-component-meta](https://www.npmjs.com/package/vue-component-meta) using meta imports - e.g. `Button.vue?meta`.
8
9
 
9
10
  ## Setup
10
11
 
@@ -14,18 +15,12 @@ A very bare-bones tool to help document Vue components.
14
15
  import { fileURLToPath, URL } from 'node:url';
15
16
 
16
17
  import { defineConfig } from 'vite';
18
+ import { vueOptions } from '@do11y/docs';
17
19
 
18
20
  import vue from '@vitejs/plugin-vue';
19
- import vueDevTools from 'vite-plugin-vue-devtools';
20
21
 
21
22
  export default defineConfig({
22
- plugins: [vue({ include: [/\.vue$/, /\.md$/] }), vueDevTools()],
23
-
24
- resolve: {
25
- alias: {
26
- '@': fileURLToPath(new URL('./src', import.meta.url)),
27
- },
28
- },
23
+ plugins: [vue(vueOptions)],
29
24
  });
30
25
  ```
31
26
 
@@ -64,19 +59,24 @@ npm docs:dev
64
59
 
65
60
  ### `site/index.ts`
66
61
 
67
- This file will be used to configure the documentation site.
62
+ This file will be used to configure the site.
68
63
 
69
64
  > [!WARNING]
70
- > Both the documentation site and the sandbox uses the same `setup` function. This means importing styles or components directly in this file will also import them to the sandbox.
65
+ > Both the documentation site and the sandbox import this file - this is why it is recommended to import necessary files/components _inside_ the functions.
71
66
 
72
67
  ```ts
73
68
  import type { Site } from '@do11y/docs';
74
69
 
75
70
  export default {
71
+ // The main component for the site.
76
72
  Site: () => import('./Site.vue'),
77
73
 
78
- setup(app) {
79
- // App setup
74
+ async setup(app, router) {
75
+ // Additional setup for the app.
76
+ },
77
+
78
+ async setupSandbox(app) {
79
+ // Additional setup for the sandbox app.
80
80
  },
81
81
  } satisfies Site;
82
82
  ```
@@ -89,11 +89,12 @@ This file will be used to configure the different plugins available.
89
89
  import type { PluginOptions } from '@do11y/docs';
90
90
 
91
91
  export default {
92
- metaRenderer(meta, title) {
93
- return `
94
- <h3>${title}</h3>
95
- <pre><code>${JSON.stringify(meta, null, 2)}</code></pre>
96
- `;
97
- },
92
+ setup(md) {
93
+ // Additional markdown-it setup.
94
+ }
95
+
96
+ highlight(md, code, lang, attrs) {
97
+ // The highlight option for `markdown-it`.
98
+ }
98
99
  } satisfies PluginOptions;
99
100
  ```
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  import type { PluginOptions } from './plugin-options.js';
2
2
  import type { Site } from './plugins/site/site.js';
3
3
  export type { Site, PluginOptions };
4
+ export declare const vueOptions: {
5
+ include: RegExp[];
6
+ exclude: RegExp[];
7
+ };
package/dist/index.js CHANGED
@@ -1 +1,4 @@
1
- export {};
1
+ export const vueOptions = {
2
+ include: [/\.vue$/, /\.md$/],
3
+ exclude: [/\.vue\?meta$/],
4
+ };
@@ -1,5 +1,4 @@
1
1
  import { type MarkdownSfcBlocks } from '@mdit-vue/plugin-sfc';
2
- import type { PluginOptions, MarkdownItEnv as Env } from 'markdown-it-vue-meta';
3
2
  import type { PluginSimple } from 'markdown-it';
4
3
  import type { Plugin } from 'vite';
5
4
  import type MarkdownIt from 'markdown-it';
@@ -12,12 +11,8 @@ export interface MarkdownPluginOptions {
12
11
  * The highlight option for `markdown-it`.
13
12
  */
14
13
  highlight?: (md: MarkdownIt, code: string, lang: string, attrs: string) => string;
15
- /**
16
- * The renderer option for `markdown-it-vue-meta`.
17
- */
18
- metaRenderer?: PluginOptions['renderer'];
19
14
  }
20
- export interface MarkdownItEnv extends Env {
15
+ export interface MarkdownItEnv {
21
16
  /**
22
17
  * Blocks extracted by `@mdit-vue/plugin-sfc`.
23
18
  */
@@ -1,12 +1,8 @@
1
- import { join } from 'node:path';
2
- import { existsSync } from 'node:fs';
3
1
  import { componentPlugin } from '@mdit-vue/plugin-component';
4
2
  import { sfcPlugin } from '@mdit-vue/plugin-sfc';
5
3
  import { frontmatterPlugin } from '@mdit-vue/plugin-frontmatter';
6
4
  import attrsPlugin from 'markdown-it-attrs';
7
- import metaPlugin from 'markdown-it-vue-meta';
8
5
  import markdown from 'markdown-it';
9
- import { root } from '../../files.js';
10
6
  /**
11
7
  * Processes blocks with the lang set to `md` into HTML,
12
8
  * and turns `.md` files into single file vue components
@@ -23,16 +19,6 @@ export default (options) => {
23
19
  md.use(sfcPlugin);
24
20
  md.use(componentPlugin);
25
21
  md.use(attrsPlugin);
26
- if (options?.metaRenderer) {
27
- let tsconfig = join(root, 'tsconfig.app.json');
28
- if (!existsSync(tsconfig)) {
29
- tsconfig = join(root, 'tsconfig.json');
30
- }
31
- md.use(metaPlugin, {
32
- renderer: options.metaRenderer,
33
- tsconfig,
34
- });
35
- }
36
22
  if (options?.setup) {
37
23
  md.use(options.setup);
38
24
  }
@@ -41,9 +27,7 @@ export default (options) => {
41
27
  enforce: 'pre',
42
28
  transform(code, id) {
43
29
  if (id.endsWith('.md')) {
44
- const env = {
45
- path: id.replace(/[?#].*$/, ''),
46
- };
30
+ const env = {};
47
31
  const html = md.render(code, env);
48
32
  /**
49
33
  * If it's a markdown block, return the
@@ -0,0 +1,42 @@
1
+ import type { ComponentMeta } from 'vue-component-meta';
2
+ export declare const mapMeta: (meta: ComponentMeta, render: (input: string) => string) => {
3
+ modelValues: {
4
+ default: string | undefined;
5
+ required: boolean;
6
+ name: string;
7
+ type: string;
8
+ description: string;
9
+ deprecated: string | boolean;
10
+ tags: {
11
+ name: string;
12
+ text: string | undefined;
13
+ }[];
14
+ }[];
15
+ props: {
16
+ default: string | undefined;
17
+ required: boolean;
18
+ name: string;
19
+ type: string;
20
+ description: string;
21
+ deprecated: string | boolean;
22
+ tags: {
23
+ name: string;
24
+ text: string | undefined;
25
+ }[];
26
+ }[];
27
+ events: {
28
+ name: string;
29
+ type: string;
30
+ description: string;
31
+ deprecated: string | boolean;
32
+ tags: {
33
+ name: string;
34
+ text: string | undefined;
35
+ }[];
36
+ }[];
37
+ slots: {
38
+ name: string;
39
+ type: string;
40
+ description: string;
41
+ }[];
42
+ };
@@ -0,0 +1,50 @@
1
+ export const mapMeta = (meta, render) => {
2
+ const nonGlobalProps = meta.props.filter((prop) => !prop.global);
3
+ const hasAssociatedEvent = (prop) => {
4
+ return meta.events.some((event) => event.name === `update:${prop.name}`);
5
+ };
6
+ const hasAssociatedProp = (event) => {
7
+ return meta.props.some((prop) => `update:${prop.name}` === event.name);
8
+ };
9
+ const getDeprecated = (tags) => {
10
+ const deprecated = getTag(tags, 'deprecated');
11
+ return deprecated ? deprecated.text || true : false;
12
+ };
13
+ const getFilteredTags = (tags) => {
14
+ const filteredTags = tags.filter((t) => !['default', 'deprecated'].includes(t.name));
15
+ return filteredTags.map((tag) => ({
16
+ name: tag.name,
17
+ text: tag.text ? render(tag.text) : undefined,
18
+ }));
19
+ };
20
+ const mapPropertyAndEvent = (prop) => ({
21
+ name: prop.name,
22
+ type: prop.type,
23
+ description: render(prop.description),
24
+ deprecated: getDeprecated(prop.tags),
25
+ tags: getFilteredTags(prop.tags),
26
+ });
27
+ const mapSlotAndExposed = (m) => ({
28
+ name: m.name,
29
+ type: m.type,
30
+ description: render(m.description),
31
+ });
32
+ const mapProperty = (prop) => ({
33
+ ...mapPropertyAndEvent(prop),
34
+ default: prop.default || getTag(prop.tags, 'default')?.text,
35
+ required: prop.required,
36
+ });
37
+ return {
38
+ modelValues: nonGlobalProps
39
+ .filter((prop) => hasAssociatedEvent(prop))
40
+ .map((modelValue) => mapProperty(modelValue)),
41
+ props: nonGlobalProps
42
+ .filter((prop) => !hasAssociatedEvent(prop))
43
+ .map((prop) => mapProperty(prop)),
44
+ events: meta.events
45
+ .filter((event) => !hasAssociatedProp(event))
46
+ .map((event) => mapPropertyAndEvent(event)),
47
+ slots: meta.slots.map((slot) => mapSlotAndExposed(slot)),
48
+ };
49
+ };
50
+ const getTag = (tags, tag) => tags.find(({ name }) => name === tag);
@@ -0,0 +1,7 @@
1
+ import type { Plugin } from 'vite';
2
+ /**
3
+ * Adds `.vue?meta` imports which returns the results of
4
+ * running the component through `vue-component-meta`.
5
+ */
6
+ declare const _default: () => Plugin;
7
+ export default _default;
@@ -0,0 +1,35 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { createChecker } from 'vue-component-meta';
4
+ import markdown from 'markdown-it';
5
+ import { root } from '../../files.js';
6
+ import { mapMeta } from './meta-mapper.js';
7
+ /**
8
+ * Adds `.vue?meta` imports which returns the results of
9
+ * running the component through `vue-component-meta`.
10
+ */
11
+ export default () => {
12
+ let tsconfig = join(root, 'tsconfig.app.json');
13
+ if (!existsSync(tsconfig)) {
14
+ tsconfig = join(root, 'tsconfig.json');
15
+ }
16
+ const checker = createChecker(tsconfig, {
17
+ noDeclarations: true,
18
+ });
19
+ const md = markdown();
20
+ return {
21
+ name: 'do11y:meta',
22
+ transform(_, id) {
23
+ if (id.endsWith('.vue?meta')) {
24
+ const file = id.replace('?meta', '');
25
+ const meta = checker.getComponentMeta(file);
26
+ const code = `export default ${JSON.stringify(mapMeta(meta, (content) => md.render(content)))}`;
27
+ return {
28
+ code,
29
+ map: { mappings: '' },
30
+ moduleType: 'js',
31
+ };
32
+ }
33
+ },
34
+ };
35
+ };
@@ -1,4 +1,5 @@
1
1
  import markdownPlugin from './markdown/markdown.js';
2
+ import metaPlugin from './meta/meta.js';
2
3
  import customBlockPlugin from 'v-custom-block';
3
4
  import sitePlugin from './site/site.js';
4
5
  import routesPlugin from './routes/routes.js';
@@ -8,6 +9,7 @@ import { pluginOptions } from '../plugin-options.js';
8
9
  export const plugins = () => [
9
10
  uiPlugin(),
10
11
  sandboxPlugin(),
12
+ metaPlugin(),
11
13
  markdownPlugin(pluginOptions),
12
14
  customBlockPlugin('docs'),
13
15
  sitePlugin(),
@@ -10,6 +10,10 @@ export interface Site {
10
10
  * Additional setup for the app.
11
11
  */
12
12
  setup?(app: App, router: Router): void | Promise<void>;
13
+ /**
14
+ * Additional setup for the sandbox app.
15
+ */
16
+ setupSandbox?(app: App): void | Promise<void>;
13
17
  }
14
18
  /**
15
19
  * Add ability to access the site options (`docs/site/index.ts`)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@do11y/docs",
3
3
  "description": "A very bare-bones tool to help document Vue components.",
4
- "version": "0.0.11",
4
+ "version": "0.0.13",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -38,9 +38,9 @@
38
38
  "front-matter": "^4.0.2",
39
39
  "markdown-it": "^14.1.0",
40
40
  "markdown-it-attrs": "^4.3.1",
41
- "markdown-it-vue-meta": "^0.0.2",
42
41
  "v-custom-block": "^1.0.67",
43
- "@do11y/ui": "0.0.5"
42
+ "vue-component-meta": "^3.1.8",
43
+ "@do11y/ui": "0.0.6"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@tsconfig/node24": "^24.0.3",
@@ -1,8 +1,5 @@
1
1
  <template>
2
- <div class="iframe-wrapper">
3
- <iframe ref="iframe" :title="`Sandbox for ${title}`" :src="url" />
4
- </div>
5
-
2
+ <iframe ref="iframe" :title="`Sandbox for ${title}`" :src="url" />
6
3
  <a :href="url" target="_blank" rel="noopener noreferrer">Open in a new tab</a>
7
4
  </template>
8
5
 
@@ -10,7 +7,15 @@
10
7
  import { computed, ref } from 'vue';
11
8
 
12
9
  const props = defineProps<{
10
+ /**
11
+ * The iframe title.
12
+ */
13
13
  title: string;
14
+
15
+ /**
16
+ * The `id` of the sandbox - which is the filename
17
+ * without the extension `.sandbox.vue`.
18
+ */
14
19
  id: string;
15
20
  }>();
16
21
 
@@ -19,7 +24,7 @@
19
24
  const url = computed(() => `${window.location.origin}/sandbox?id=${props.id}`);
20
25
  </script>
21
26
 
22
- <style scoped>
27
+ <style>
23
28
  iframe {
24
29
  border: none;
25
30
  width: 100%;
@@ -1,26 +1,20 @@
1
1
  <template>
2
2
  <nav>
3
3
  <ul>
4
- <template v-for="route of routes" :key="route.path">
5
- <li>
6
- <router-link :to="route.path">
7
- {{ route.meta.title }}
8
- </router-link>
9
- </li>
10
- </template>
4
+ <li v-for="route of routes" :key="route.path">
5
+ <router-link :to="route.path">
6
+ {{ route.meta.title }}
7
+ </router-link>
8
+ </li>
11
9
  </ul>
12
10
  </nav>
13
11
 
14
- <RouterView />
12
+ <main>
13
+ <RouterView />
14
+ </main>
15
15
  </template>
16
16
 
17
17
  <script lang="ts" setup>
18
18
  import routes from 'do11y:routes';
19
19
  import './style.css';
20
20
  </script>
21
-
22
- <style>
23
- button {
24
- width: max-content;
25
- }
26
- </style>
@@ -4,7 +4,11 @@ export default {
4
4
  Site: () => import('./Site.vue'),
5
5
 
6
6
  async setup(app) {
7
- const SandboxPlaygroundComponent = (await import('./SandboxPlayground.vue')).default;
8
- app.component('SandboxPlayground', SandboxPlaygroundComponent);
7
+ const SandboxIframe = (await import('./SandboxIframe.vue')).default;
8
+ app.component('SandboxIframe', SandboxIframe);
9
+ },
10
+
11
+ async setupSandbox(app) {
12
+ /** Setup sandbox app */
9
13
  },
10
14
  } satisfies Site;
@@ -1,10 +1,3 @@
1
1
  import type { PluginOptions } from '@do11y/docs';
2
2
 
3
- export default {
4
- metaRenderer(meta, title) {
5
- return `
6
- <h3>${title}</h3>
7
- <pre><code>${JSON.stringify(meta, null, 2)}</code></pre>
8
- `;
9
- },
10
- } satisfies PluginOptions;
3
+ export default {} satisfies PluginOptions;
@@ -15,6 +15,7 @@ html {
15
15
  margin: 0;
16
16
  }
17
17
 
18
+ [id],
18
19
  :target {
19
20
  scroll-margin-block: 2em;
20
21
  }
@@ -24,54 +25,17 @@ button {
24
25
  cursor: pointer;
25
26
  }
26
27
 
27
- button {
28
- color: inherit;
29
- }
30
-
31
28
  button:disabled,
32
29
  button[aria-disabled='true'] {
33
30
  cursor: default;
34
31
  }
35
32
 
36
- button,
37
- input,
38
- textarea,
39
- select {
40
- font: inherit;
41
- letter-spacing: inherit;
42
- word-spacing: inherit;
43
- }
44
-
45
- textarea {
46
- field-sizing: content;
47
- }
48
-
49
- a {
50
- text-underline-position: from-font;
51
- }
52
-
53
33
  pre {
54
34
  white-space: pre-wrap;
35
+ inline-size: 100%;
55
36
  }
56
37
 
57
- code {
58
- word-break: break-all;
59
- }
60
-
61
- pre,
62
- table,
63
- img,
64
- svg,
65
- picture,
66
- video,
67
- canvas,
68
- iframe {
69
- max-inline-size: 100%;
70
- min-inline-size: 0;
71
-
72
- display: block;
73
- }
74
-
75
- [id] {
76
- scroll-margin-top: 2rem;
38
+ body {
39
+ margin-inline: auto;
40
+ inline-size: min(90dvw, 50rem);
77
41
  }