do11y 0.0.0
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 +8 -0
- package/dist/docs/cli.d.ts +1 -0
- package/dist/docs/cli.js +23 -0
- package/dist/docs/files.d.ts +5 -0
- package/dist/docs/files.js +8 -0
- package/dist/docs/html/index.html +13 -0
- package/dist/docs/html/plugin.d.ts +2 -0
- package/dist/docs/html/plugin.js +25 -0
- package/dist/docs/html/render.d.ts +1 -0
- package/dist/docs/html/render.js +19 -0
- package/dist/docs/index.d.ts +31 -0
- package/dist/docs/index.js +2 -0
- package/dist/docs/options.d.ts +5 -0
- package/dist/docs/options.js +5 -0
- package/dist/docs/plugins/markdown.d.ts +31 -0
- package/dist/docs/plugins/markdown.js +49 -0
- package/dist/docs/plugins/meta/meta-mapper.d.ts +3 -0
- package/dist/docs/plugins/meta/meta-mapper.js +39 -0
- package/dist/docs/plugins/meta/meta-types.d.ts +56 -0
- package/dist/docs/plugins/meta/meta-types.js +1 -0
- package/dist/docs/plugins/meta/meta.d.ts +7 -0
- package/dist/docs/plugins/meta/meta.js +52 -0
- package/dist/docs/plugins/options.d.ts +36 -0
- package/dist/docs/plugins/options.js +13 -0
- package/dist/docs/plugins/plugins.d.ts +2 -0
- package/dist/docs/plugins/plugins.js +17 -0
- package/dist/docs/plugins/routes.d.ts +7 -0
- package/dist/docs/plugins/routes.js +56 -0
- package/dist/docs/plugins/sandbox.d.ts +7 -0
- package/dist/docs/plugins/sandbox.js +88 -0
- package/dist/docs/plugins/ui.d.ts +6 -0
- package/dist/docs/plugins/ui.js +6 -0
- package/dist/docs/vite-config.d.ts +27 -0
- package/dist/docs/vite-config.js +62 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/sandbox-iframe.js +1 -0
- package/dist/ui/sandbox.js +1 -0
- package/package.json +67 -0
- package/src/types.d.ts +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Do11y
|
|
2
|
+
|
|
3
|
+
A bare-bones tool to document Vue components.
|
|
4
|
+
|
|
5
|
+
- Write documentation in markdown files that turn into single file components.
|
|
6
|
+
- Import markdown files as routes through `do11y:routes`.
|
|
7
|
+
- Create sandbox components - e.g. `Button.sandbox.vue` will be available under the url `/sandbox?id=button`, and if importing the component it will be wrapped inside an iframe component that has access to the source code with or without compiled CSS.
|
|
8
|
+
- Easily document components through meta imports which give a simplified result from [vue-component-meta](https://www.npmjs.com/package/vue-component-meta) - e.g. `Button.vue?meta`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/docs/cli.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createServer, build, preview, mergeConfig } from "vite";
|
|
2
|
+
import { getUserViteConfig, getUserVuePlugin, viteConfig } from "./vite-config.js";
|
|
3
|
+
const [_, __, command = "dev"] = process.argv;
|
|
4
|
+
if (command !== "dev" && command !== "build" && command !== "preview") {
|
|
5
|
+
throw new Error();
|
|
6
|
+
}
|
|
7
|
+
const userViteConfig = await getUserViteConfig(command);
|
|
8
|
+
const vuePlugin = await getUserVuePlugin(userViteConfig);
|
|
9
|
+
const mergedViteConfig = mergeConfig({ plugins: [vuePlugin] }, viteConfig);
|
|
10
|
+
if (command === "dev") {
|
|
11
|
+
const server = await createServer(mergedViteConfig);
|
|
12
|
+
await server.listen();
|
|
13
|
+
server.printUrls();
|
|
14
|
+
server.bindCLIShortcuts({ print: true });
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
await build(mergedViteConfig);
|
|
18
|
+
if (command === "preview") {
|
|
19
|
+
const previewServer = await preview(mergedViteConfig);
|
|
20
|
+
previewServer.printUrls();
|
|
21
|
+
previewServer.bindCLIShortcuts({ print: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { searchForWorkspaceRoot } from "vite";
|
|
3
|
+
export const ui = join(import.meta.dirname, "../ui");
|
|
4
|
+
export const root = searchForWorkspaceRoot(process.cwd());
|
|
5
|
+
// export const root = join(searchForWorkspaceRoot(process.cwd()), "example");
|
|
6
|
+
export const docs = join(root, "docs");
|
|
7
|
+
export const output = join(docs, "dist");
|
|
8
|
+
export const do11y = join(docs, "do11y", "do11y.ts");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
|
|
8
|
+
<title>Do11y</title>
|
|
9
|
+
<link rel="icon" href="/favicon.ico" />
|
|
10
|
+
</head>
|
|
11
|
+
|
|
12
|
+
<body id="app"></body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { output } from "../files.js";
|
|
4
|
+
import { render } from "./render.js";
|
|
5
|
+
export const indexHtml = (folder, key) => ({
|
|
6
|
+
writeBundle() {
|
|
7
|
+
const html = render(`/assets/${key}.js`, `/assets/${key}.css`);
|
|
8
|
+
const indexHtmlFile = join(output, `${key}.html`);
|
|
9
|
+
writeFileSync(indexHtmlFile, html);
|
|
10
|
+
},
|
|
11
|
+
configureServer(server) {
|
|
12
|
+
const bundle = join(folder, `${key}.js`);
|
|
13
|
+
const sandboxBundle = join(folder, "sandbox.js");
|
|
14
|
+
server.middlewares.use(async (req, res, next) => {
|
|
15
|
+
if (!req.url || req.method !== "GET" || !req.headers.accept?.includes("text/html")) {
|
|
16
|
+
return next();
|
|
17
|
+
}
|
|
18
|
+
const html = render(`/@fs/${req.url?.startsWith("/sandbox") ? sandboxBundle : bundle}`);
|
|
19
|
+
const transformedHtml = await server.transformIndexHtml(req.url, html);
|
|
20
|
+
res.statusCode = 200;
|
|
21
|
+
res.setHeader("content-type", "text/html; charset=UTF-8");
|
|
22
|
+
res.end(transformedHtml);
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const render: (script: string, stylesheet?: string) => string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { JSDOM } from "jsdom";
|
|
4
|
+
const template = readFileSync(join(import.meta.dirname, "index.html"), "utf-8");
|
|
5
|
+
export const render = (script, stylesheet) => {
|
|
6
|
+
const jsdom = new JSDOM(template);
|
|
7
|
+
const scriptElement = jsdom.window.document.createElement("script");
|
|
8
|
+
scriptElement.setAttribute("type", "module");
|
|
9
|
+
scriptElement.setAttribute("src", script);
|
|
10
|
+
jsdom.window.document.documentElement.appendChild(scriptElement);
|
|
11
|
+
if (stylesheet) {
|
|
12
|
+
const linkElement = jsdom.window.document.createElement("link");
|
|
13
|
+
linkElement.setAttribute("as", "style");
|
|
14
|
+
linkElement.setAttribute("rel", "preload stylesheet");
|
|
15
|
+
linkElement.setAttribute("href", stylesheet);
|
|
16
|
+
jsdom.window.document.head.appendChild(linkElement);
|
|
17
|
+
}
|
|
18
|
+
return jsdom.serialize();
|
|
19
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Options } from "./plugins/options.js";
|
|
2
|
+
import type { Meta } from "./plugins/meta/meta-types.js";
|
|
3
|
+
export type { Options, Meta };
|
|
4
|
+
export interface SandboxIframeProps {
|
|
5
|
+
/**
|
|
6
|
+
* The `id` of the sandbox - which is the filename
|
|
7
|
+
* in lower-case without the extension.
|
|
8
|
+
*
|
|
9
|
+
* @do11y Automatically passed.
|
|
10
|
+
*/
|
|
11
|
+
id: string;
|
|
12
|
+
/**
|
|
13
|
+
* The sandbox url.
|
|
14
|
+
*
|
|
15
|
+
* @do11y Automatically passed.
|
|
16
|
+
*/
|
|
17
|
+
url: string;
|
|
18
|
+
/**
|
|
19
|
+
* The source code.
|
|
20
|
+
*
|
|
21
|
+
* @do11y Automatically passed.
|
|
22
|
+
*/
|
|
23
|
+
source?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The source code with compiled CSS.
|
|
26
|
+
* Only included if the style tag has lang `scss` or `sass`.
|
|
27
|
+
*
|
|
28
|
+
* @do11y Automatically passed.
|
|
29
|
+
*/
|
|
30
|
+
sourceWithCompiledCss?: string;
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type MarkdownSfcBlocks } from "@mdit-vue/plugin-sfc";
|
|
2
|
+
import type { PluginSimple } from "markdown-it";
|
|
3
|
+
import type { Plugin } from "vite";
|
|
4
|
+
import type MarkdownIt from "markdown-it";
|
|
5
|
+
export interface MarkdownPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Additional markdown-it setup.
|
|
8
|
+
*/
|
|
9
|
+
markdownSetup?: PluginSimple;
|
|
10
|
+
/**
|
|
11
|
+
* The highlight option for `markdown-it`.
|
|
12
|
+
*/
|
|
13
|
+
markdownHighlight?: (md: MarkdownIt, code: string, lang: string, attrs: string) => string;
|
|
14
|
+
}
|
|
15
|
+
export interface MarkdownItEnv {
|
|
16
|
+
/**
|
|
17
|
+
* Blocks extracted by `@mdit-vue/plugin-sfc`.
|
|
18
|
+
*/
|
|
19
|
+
sfcBlocks?: MarkdownSfcBlocks;
|
|
20
|
+
/**
|
|
21
|
+
* Blocks extracted by `@mdit-vue/plugin-frontmatter`.
|
|
22
|
+
*/
|
|
23
|
+
frontmatter?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Processes blocks with the lang set to `md` into HTML,
|
|
27
|
+
* and turns `.md` files into single file vue components
|
|
28
|
+
* using `markdown-it` and `@mdit-vue`.
|
|
29
|
+
*/
|
|
30
|
+
declare const _default: (options?: MarkdownPluginOptions) => Plugin;
|
|
31
|
+
export default _default;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { componentPlugin } from "@mdit-vue/plugin-component";
|
|
2
|
+
import { sfcPlugin } from "@mdit-vue/plugin-sfc";
|
|
3
|
+
import { frontmatterPlugin } from "@mdit-vue/plugin-frontmatter";
|
|
4
|
+
import attrsPlugin from "markdown-it-attrs";
|
|
5
|
+
import markdown from "markdown-it";
|
|
6
|
+
/**
|
|
7
|
+
* Processes blocks with the lang set to `md` into HTML,
|
|
8
|
+
* and turns `.md` files into single file vue components
|
|
9
|
+
* using `markdown-it` and `@mdit-vue`.
|
|
10
|
+
*/
|
|
11
|
+
export default (options) => {
|
|
12
|
+
const md = markdown({
|
|
13
|
+
html: true,
|
|
14
|
+
highlight(code, lang, attrs) {
|
|
15
|
+
return options?.markdownHighlight?.(md, code, lang, attrs) ?? "";
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
md.use(frontmatterPlugin);
|
|
19
|
+
md.use(sfcPlugin);
|
|
20
|
+
md.use(componentPlugin);
|
|
21
|
+
md.use(attrsPlugin);
|
|
22
|
+
if (options?.markdownSetup) {
|
|
23
|
+
md.use(options.markdownSetup);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
name: "do11y:markdown",
|
|
27
|
+
enforce: "pre",
|
|
28
|
+
transform(code, id) {
|
|
29
|
+
if (id.endsWith(".md")) {
|
|
30
|
+
const env = {};
|
|
31
|
+
const html = md.render(code, env);
|
|
32
|
+
/**
|
|
33
|
+
* If it's a markdown block, return the
|
|
34
|
+
* html directly - otherwise create
|
|
35
|
+
* a single file component.
|
|
36
|
+
*/
|
|
37
|
+
if (id.endsWith("lang.md")) {
|
|
38
|
+
return html;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const template = env.sfcBlocks?.template?.content ?? "";
|
|
42
|
+
const script = env.sfcBlocks?.scriptSetup?.contentStripped ?? "";
|
|
43
|
+
const styles = env.sfcBlocks?.styles.map((s) => s.content).join("\n\n") ?? "";
|
|
44
|
+
return `${template}\n\n<script setup>${script}</script>\n\n${styles}`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const mapMeta = (meta, render) => {
|
|
2
|
+
const nonGlobalProps = meta.props.filter((prop) => !prop.global);
|
|
3
|
+
const getDeprecated = (tags) => {
|
|
4
|
+
const deprecated = getTag(tags, "deprecated");
|
|
5
|
+
return deprecated ? deprecated.text || true : false;
|
|
6
|
+
};
|
|
7
|
+
const getFilteredTags = (tags) => {
|
|
8
|
+
const filteredTags = tags.filter((t) => !["default", "deprecated"].includes(t.name));
|
|
9
|
+
return filteredTags.map((tag) => ({
|
|
10
|
+
name: tag.name,
|
|
11
|
+
text: tag.text ? render(tag.text) : undefined,
|
|
12
|
+
}));
|
|
13
|
+
};
|
|
14
|
+
const mapEvent = (prop) => ({
|
|
15
|
+
name: prop.name,
|
|
16
|
+
type: prop.type,
|
|
17
|
+
description: prop.description ? render(prop.description) : undefined,
|
|
18
|
+
deprecated: getDeprecated(prop.tags),
|
|
19
|
+
tags: getFilteredTags(prop.tags),
|
|
20
|
+
});
|
|
21
|
+
const mapSlotAndExposed = (se) => ({
|
|
22
|
+
name: se.name,
|
|
23
|
+
type: se.type,
|
|
24
|
+
description: se.description ? render(se.description) : undefined,
|
|
25
|
+
});
|
|
26
|
+
const mapProperty = (prop) => ({
|
|
27
|
+
...mapEvent(prop),
|
|
28
|
+
default: prop.default || getTag(prop.tags, "default")?.text,
|
|
29
|
+
required: prop.required,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
description: meta.description ? render(meta.description) : "",
|
|
33
|
+
props: nonGlobalProps.map((prop) => mapProperty(prop)),
|
|
34
|
+
events: meta.events.map((event) => mapEvent(event)),
|
|
35
|
+
slots: meta.slots.map((slot) => mapSlotAndExposed(slot)),
|
|
36
|
+
exposed: meta.exposed.map((exposed) => mapSlotAndExposed(exposed)),
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const getTag = (tags, tag) => tags.find(({ name }) => name === tag);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PropertyMeta } from "vue-component-meta";
|
|
2
|
+
interface Property {
|
|
3
|
+
/**
|
|
4
|
+
* The name of the prop.
|
|
5
|
+
*/
|
|
6
|
+
name: string;
|
|
7
|
+
/**
|
|
8
|
+
* The type expected for the prop.
|
|
9
|
+
*/
|
|
10
|
+
type: string;
|
|
11
|
+
/**
|
|
12
|
+
* The HTML description.
|
|
13
|
+
*/
|
|
14
|
+
description?: string;
|
|
15
|
+
/**
|
|
16
|
+
* If the prop has been marked with `@deprecated`.
|
|
17
|
+
*/
|
|
18
|
+
deprecated: string | boolean;
|
|
19
|
+
/**
|
|
20
|
+
* JSDOC tags - filtered so that `@deprecated` and
|
|
21
|
+
* `@default` are not included.
|
|
22
|
+
*/
|
|
23
|
+
tags: PropertyMeta["tags"][number][];
|
|
24
|
+
/**
|
|
25
|
+
* The default.
|
|
26
|
+
* If one can't be calculated - falls back to the `@default` tag.
|
|
27
|
+
*/
|
|
28
|
+
default?: string;
|
|
29
|
+
/**
|
|
30
|
+
* If the prop is required.
|
|
31
|
+
*/
|
|
32
|
+
required: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface Meta {
|
|
35
|
+
/**
|
|
36
|
+
* The HTML description for the component.
|
|
37
|
+
*/
|
|
38
|
+
description?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The props.
|
|
41
|
+
*/
|
|
42
|
+
props: Property[];
|
|
43
|
+
/**
|
|
44
|
+
* The emits.
|
|
45
|
+
*/
|
|
46
|
+
events: Omit<Property, "default" | "required">[];
|
|
47
|
+
/**
|
|
48
|
+
* The slots.
|
|
49
|
+
*/
|
|
50
|
+
slots: Pick<Property, "name" | "type" | "description">[];
|
|
51
|
+
/**
|
|
52
|
+
* The exposed properties.
|
|
53
|
+
*/
|
|
54
|
+
exposed: Pick<Property, "name" | "type" | "description">[];
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createChecker } from "vue-component-meta";
|
|
4
|
+
import { parse as parseVue } from "vue/compiler-sfc";
|
|
5
|
+
import markdown from "markdown-it";
|
|
6
|
+
import { root } from "../../files.js";
|
|
7
|
+
import { mapMeta } from "./meta-mapper.js";
|
|
8
|
+
const getFirstExistingFile = (...paths) => {
|
|
9
|
+
for (const path of paths) {
|
|
10
|
+
if (existsSync(path)) {
|
|
11
|
+
return path;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Adds `.vue?meta` imports which returns a simplified result
|
|
17
|
+
* of running the component through `vue-component-meta`.
|
|
18
|
+
*/
|
|
19
|
+
export default () => {
|
|
20
|
+
const tsconfig = getFirstExistingFile(join(root, "tsconfig.app.json"), join(root, "tsconfig.json"));
|
|
21
|
+
if (!tsconfig) {
|
|
22
|
+
return {
|
|
23
|
+
name: "do11y:meta",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const checker = createChecker(tsconfig, {
|
|
27
|
+
noDeclarations: true,
|
|
28
|
+
});
|
|
29
|
+
const md = markdown();
|
|
30
|
+
return {
|
|
31
|
+
name: "do11y:meta",
|
|
32
|
+
transform(_, id) {
|
|
33
|
+
if (id.endsWith(".vue?meta")) {
|
|
34
|
+
const file = id.replace("?meta", "");
|
|
35
|
+
const content = readFileSync(file, "utf-8");
|
|
36
|
+
const { descriptor } = parseVue(content, { ignoreEmpty: true });
|
|
37
|
+
const block = descriptor.customBlocks.find((b) => b.type === "docs" && b.lang === "md");
|
|
38
|
+
const description = block ? md.render(block.content) || undefined : undefined;
|
|
39
|
+
const meta = checker.getComponentMeta(file);
|
|
40
|
+
const mappedMeta = {
|
|
41
|
+
...mapMeta(meta, (content) => md.render(content)),
|
|
42
|
+
description: description || meta.description,
|
|
43
|
+
};
|
|
44
|
+
const code = `export default ${JSON.stringify(mappedMeta)}`;
|
|
45
|
+
return {
|
|
46
|
+
code,
|
|
47
|
+
moduleType: "js",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import type { App, Component } from "vue";
|
|
3
|
+
import type { Router } from "vue-router";
|
|
4
|
+
import type { MarkdownPluginOptions } from "./markdown.js";
|
|
5
|
+
export interface Options extends MarkdownPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The home page component.
|
|
8
|
+
*/
|
|
9
|
+
Home: () => Promise<Component>;
|
|
10
|
+
/**
|
|
11
|
+
* The layout component for the documents.
|
|
12
|
+
*/
|
|
13
|
+
Layout?: () => Promise<Component>;
|
|
14
|
+
/**
|
|
15
|
+
* Additional setup for the app.
|
|
16
|
+
*/
|
|
17
|
+
setup?(app: App, router: Router): void | Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* The wrapper component for the sandbox.
|
|
20
|
+
*/
|
|
21
|
+
Sandbox?: () => Promise<Component>;
|
|
22
|
+
/**
|
|
23
|
+
* Additional setup for the sandbox app.
|
|
24
|
+
*/
|
|
25
|
+
setupSandbox?(app: App): void | Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Custom wrapper component for `?iframe` imports.
|
|
28
|
+
*/
|
|
29
|
+
SandboxIframe?: () => Promise<Component>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add ability to access options (`docs/do11y/do11y.ts`)
|
|
33
|
+
* through `do11y:options`.
|
|
34
|
+
*/
|
|
35
|
+
declare const _default: () => Plugin;
|
|
36
|
+
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { do11y } from "../files.js";
|
|
2
|
+
/**
|
|
3
|
+
* Add ability to access options (`docs/do11y/do11y.ts`)
|
|
4
|
+
* through `do11y:options`.
|
|
5
|
+
*/
|
|
6
|
+
export default () => ({
|
|
7
|
+
name: "do11y:options",
|
|
8
|
+
async resolveId(id, importer) {
|
|
9
|
+
if (id === "do11y:options") {
|
|
10
|
+
return this.resolve(do11y.replace(".ts", ".js"), importer);
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ui from "./ui.js";
|
|
2
|
+
import sandbox from "./sandbox.js";
|
|
3
|
+
import meta from "./meta/meta.js";
|
|
4
|
+
import markdown from "./markdown.js";
|
|
5
|
+
import block from "v-custom-block";
|
|
6
|
+
import options from "./options.js";
|
|
7
|
+
import routes from "./routes.js";
|
|
8
|
+
import { do11yOptions } from "../options.js";
|
|
9
|
+
export const plugins = () => [
|
|
10
|
+
ui(),
|
|
11
|
+
sandbox(),
|
|
12
|
+
meta(),
|
|
13
|
+
markdown(do11yOptions),
|
|
14
|
+
block("docs"),
|
|
15
|
+
options(),
|
|
16
|
+
routes(),
|
|
17
|
+
];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { globSync } from "tinyglobby";
|
|
3
|
+
import { root } from "../files.js";
|
|
4
|
+
import fm from "front-matter";
|
|
5
|
+
/**
|
|
6
|
+
* Adds the ability to import routes (`*.md` files with
|
|
7
|
+
* a `title` and `slug`) through `do11y:routes`.
|
|
8
|
+
*/
|
|
9
|
+
export default () => {
|
|
10
|
+
const moduleId = "do11y:routes";
|
|
11
|
+
const resolvedModuleId = "\0" + moduleId;
|
|
12
|
+
const markdownFiles = globSync(["(docs|src|packages)/**/*.md"], {
|
|
13
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
14
|
+
expandDirectories: false,
|
|
15
|
+
absolute: true,
|
|
16
|
+
cwd: root,
|
|
17
|
+
});
|
|
18
|
+
/* prettier-ignore */
|
|
19
|
+
const pages = markdownFiles
|
|
20
|
+
.map((path) => ({ path, frontmatter: fm(readFileSync(path, "utf-8")).attributes }))
|
|
21
|
+
.filter((file) => typeof file.frontmatter.title === "string" && typeof file.frontmatter.slug === "string");
|
|
22
|
+
return {
|
|
23
|
+
name: "do11y:routes",
|
|
24
|
+
resolveId(id) {
|
|
25
|
+
if (id === moduleId) {
|
|
26
|
+
return resolvedModuleId;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
load(id) {
|
|
30
|
+
if (id === resolvedModuleId) {
|
|
31
|
+
const stringifiedRoutes = pages.map((route) => {
|
|
32
|
+
return `{
|
|
33
|
+
path: "${route.frontmatter.slug}",
|
|
34
|
+
meta: ${JSON.stringify(route.frontmatter)},
|
|
35
|
+
component: async () => (await import("${route.path}")).default
|
|
36
|
+
}`.trim();
|
|
37
|
+
});
|
|
38
|
+
const homeMeta = {
|
|
39
|
+
title: "Home",
|
|
40
|
+
slug: "/",
|
|
41
|
+
};
|
|
42
|
+
return `
|
|
43
|
+
import options from 'do11y:options';
|
|
44
|
+
|
|
45
|
+
const home = {
|
|
46
|
+
path: "/",
|
|
47
|
+
meta: ${JSON.stringify(homeMeta)},
|
|
48
|
+
component: options.Home
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default [home, ${stringifiedRoutes.join(",\n")}];
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { join, parse } from "node:path";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { compileString } from "sass";
|
|
5
|
+
import { globSync } from "tinyglobby";
|
|
6
|
+
import { parse as parseVue } from "vue/compiler-sfc";
|
|
7
|
+
import { root, ui } from "../files.js";
|
|
8
|
+
import { indexHtml } from "../html/plugin.js";
|
|
9
|
+
const toParamId = (path) => parse(path).name.toLowerCase().replace(".sandbox", "");
|
|
10
|
+
/**
|
|
11
|
+
* Creates a seprate sandbox app, and
|
|
12
|
+
* adds access to sandbox components through `do11y:sandbox`.
|
|
13
|
+
*/
|
|
14
|
+
export default () => {
|
|
15
|
+
const moduleId = "do11y:sandbox";
|
|
16
|
+
const resolvedModuleId = "\0" + moduleId;
|
|
17
|
+
const sandboxFiles = globSync(["(docs|src|packages)/**/*.sandbox.vue"], {
|
|
18
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
19
|
+
expandDirectories: false,
|
|
20
|
+
absolute: true,
|
|
21
|
+
cwd: root,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
name: "do11y:sandbox",
|
|
25
|
+
...indexHtml(ui, "sandbox"),
|
|
26
|
+
resolveId(id) {
|
|
27
|
+
if (id === moduleId) {
|
|
28
|
+
return resolvedModuleId;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
load(id) {
|
|
32
|
+
if (id === resolvedModuleId) {
|
|
33
|
+
const stringifiedSandboxes = sandboxFiles.map((path) => {
|
|
34
|
+
return `{
|
|
35
|
+
url: "${toParamId(path)}",
|
|
36
|
+
component: async () => (await import("${path}?default")).default
|
|
37
|
+
}`.trim();
|
|
38
|
+
});
|
|
39
|
+
return `export default [${stringifiedSandboxes.join(",\n")}];`;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
transform(_, id) {
|
|
43
|
+
if (id.endsWith(".sandbox.vue")) {
|
|
44
|
+
let source = readFileSync(id, "utf-8");
|
|
45
|
+
const { descriptor } = parseVue(source, {
|
|
46
|
+
filename: id,
|
|
47
|
+
ignoreEmpty: true,
|
|
48
|
+
});
|
|
49
|
+
const usesSass = descriptor.styles.some((s) => ["sass", "scss"].includes(s.lang));
|
|
50
|
+
let sourceWithCompiledCss = source.replace(/\n<style\b[^>]*>[\s\S]*?<\/style>/gi, "");
|
|
51
|
+
if (usesSass) {
|
|
52
|
+
for (const style of descriptor.styles) {
|
|
53
|
+
const compiledStyle = compileString(style.content, {
|
|
54
|
+
url: pathToFileURL(id),
|
|
55
|
+
});
|
|
56
|
+
const attributes = Object.entries(style.attrs)
|
|
57
|
+
.filter(([key]) => key !== "lang")
|
|
58
|
+
.map(([key, value]) => `${key}="${value}"`);
|
|
59
|
+
const stringifiedAttributes = attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
60
|
+
sourceWithCompiledCss += `<style${stringifiedAttributes}>\n${compiledStyle.css}\n</style>`;
|
|
61
|
+
}
|
|
62
|
+
/* Fix lack of new lines between style tags */
|
|
63
|
+
sourceWithCompiledCss = sourceWithCompiledCss
|
|
64
|
+
.replace(/\n\n\n<style/gi, "\n\n<style")
|
|
65
|
+
.replace("</style><style", "</style>\n\n<style");
|
|
66
|
+
}
|
|
67
|
+
const code = `
|
|
68
|
+
import { defineComponent, h } from "vue";
|
|
69
|
+
|
|
70
|
+
import SandboxIframe from "${join(ui, "sandbox-iframe.js")}";
|
|
71
|
+
|
|
72
|
+
export default defineComponent((props) => {
|
|
73
|
+
return () => h(SandboxIframe, {
|
|
74
|
+
id: "${toParamId(id)}",
|
|
75
|
+
source: ${JSON.stringify(source)},
|
|
76
|
+
sourceWithCompiledCss: ${usesSass ? JSON.stringify(sourceWithCompiledCss) : "undefined"},
|
|
77
|
+
passedProps: props,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
`.trim();
|
|
81
|
+
return {
|
|
82
|
+
code,
|
|
83
|
+
moduleType: "js",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Plugin, type UserConfig } from "vite";
|
|
2
|
+
import type { Api } from "@vitejs/plugin-vue";
|
|
3
|
+
export declare const getUserViteConfig: (command: "dev" | "build" | "preview") => Promise<UserConfig>;
|
|
4
|
+
export declare const getUserVuePlugin: (userViteConfig: UserConfig) => Promise<Plugin<Api>>;
|
|
5
|
+
export declare const viteConfig: {
|
|
6
|
+
root: string;
|
|
7
|
+
plugins: Plugin<any>[];
|
|
8
|
+
server: {
|
|
9
|
+
port: number;
|
|
10
|
+
};
|
|
11
|
+
preview: {
|
|
12
|
+
port: number;
|
|
13
|
+
};
|
|
14
|
+
build: {
|
|
15
|
+
manifest: true;
|
|
16
|
+
outDir: string;
|
|
17
|
+
rollupOptions: {
|
|
18
|
+
input: {
|
|
19
|
+
index: string;
|
|
20
|
+
sandbox: string;
|
|
21
|
+
};
|
|
22
|
+
output: {
|
|
23
|
+
entryFileNames: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { loadConfigFromFile } from "vite";
|
|
3
|
+
import { docs, ui, output } from "./files.js";
|
|
4
|
+
import { plugins } from "./plugins/plugins.js";
|
|
5
|
+
export const getUserViteConfig = async (command) => {
|
|
6
|
+
const userConfigFile = await loadConfigFromFile({
|
|
7
|
+
command: command === "dev" ? "serve" : "build",
|
|
8
|
+
mode: command === "dev" ? "dev" : "build",
|
|
9
|
+
});
|
|
10
|
+
return userConfigFile?.config ?? {};
|
|
11
|
+
};
|
|
12
|
+
const getPlugin = async (plugin) => {
|
|
13
|
+
if (!plugin) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
if (typeof plugin === "function") {
|
|
17
|
+
return [await plugin()];
|
|
18
|
+
}
|
|
19
|
+
else if ("name" in plugin) {
|
|
20
|
+
return [plugin];
|
|
21
|
+
}
|
|
22
|
+
else if (Array.isArray(plugin)) {
|
|
23
|
+
return (await Promise.all(plugin.map(async (p) => await getPlugin(p)))).flat();
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return (await getPlugin(await plugin)).flat();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export const getUserVuePlugin = async (userViteConfig) => {
|
|
30
|
+
const userPlugins = userViteConfig.plugins?.map(async (p) => await getPlugin(p)) ?? [];
|
|
31
|
+
const resolvedUserPlugins = (await Promise.all(userPlugins)).flat();
|
|
32
|
+
const vuePluginApi = resolvedUserPlugins.find((p) => p.name === "vite:vue").api;
|
|
33
|
+
const VuePlugin = (await import("@vitejs/plugin-vue")).default;
|
|
34
|
+
return VuePlugin({
|
|
35
|
+
...vuePluginApi.options,
|
|
36
|
+
include: [/\.vue$/, /\.md$/],
|
|
37
|
+
exclude: [/\.vue\?meta$/],
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
export const viteConfig = {
|
|
41
|
+
root: docs,
|
|
42
|
+
plugins: plugins(),
|
|
43
|
+
server: {
|
|
44
|
+
port: 1998,
|
|
45
|
+
},
|
|
46
|
+
preview: {
|
|
47
|
+
port: 1996,
|
|
48
|
+
},
|
|
49
|
+
build: {
|
|
50
|
+
manifest: true,
|
|
51
|
+
outDir: output,
|
|
52
|
+
rollupOptions: {
|
|
53
|
+
input: {
|
|
54
|
+
index: join(ui, "index.js"),
|
|
55
|
+
sandbox: join(ui, "sandbox.js"),
|
|
56
|
+
},
|
|
57
|
+
output: {
|
|
58
|
+
entryFileNames: "assets/[name].js",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createApp as e,createBlock as t,createCommentVNode as n,defineComponent as r,onBeforeMount as i,openBlock as a,renderSlot as o,shallowRef as s,unref as c,withCtx as l}from"vue";import u from"do11y:options";import{createRouter as d,createWebHistory as f}from"vue-router";import p from"do11y:routes";var m=r({__name:`Page`,setup(e){let n=s();return i(async()=>{n.value=(await u.Layout?.())?.default}),(e,r)=>n.value?(a(),t(c(n),{key:0},{default:l(()=>[o(e.$slots,`default`)]),_:3})):o(e.$slots,`default`,{key:1})}});(async()=>{let t=d({history:f(`/`),routes:p}),n=e(m);await u.setup?.(n,t),n.use(t),n.mount(`#app`)})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{computed as e,createBlock as t,createCommentVNode as n,createElementBlock as r,defineComponent as i,mergeProps as a,onBeforeMount as o,openBlock as s,shallowRef as c,unref as l}from"vue";import u from"do11y:options";var d=[`src`],f=i({__name:`SandboxIframe`,props:{id:{},url:{},source:{},sourceWithCompiledCss:{},passedProps:{}},setup(n){let i=n,f=e(()=>`/sandbox?id=${i.id}`),p=c();return o(async()=>{p.value=(await u.SandboxIframe?.())?.default}),(e,i)=>p.value?(s(),t(l(p),a({key:0,id:n.id,url:f.value,source:n.source,"source-with-compiled-css":n.sourceWithCompiledCss},n.passedProps),null,16,[`id`,`url`,`source`,`source-with-compiled-css`])):(s(),r(`iframe`,{key:1,src:f.value},null,8,d))}});export{f as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createApp as e,createBlock as t,createCommentVNode as n,defineComponent as r,onBeforeMount as i,openBlock as a,shallowRef as o,unref as s,withCtx as c}from"vue";import l from"do11y:options";import{parseQuery as u}from"vue-router";import d from"do11y:sandbox";e(r({__name:`Sandbox`,setup(e){let r=u(window.location.search),f=o(),p=o();return i(async()=>{let{component:e}=d.find(({url:e})=>e===r.id)??{};f.value=(await l.Sandbox?.())?.default,p.value=await e?.()}),(e,r)=>f.value?(a(),t(s(f),{key:0},{default:c(()=>[p.value?(a(),t(s(p),{key:0})):n(``,!0)]),_:1})):n(``,!0)}})).mount(`#app`);
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "do11y",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "A bare-bones tool to document Vue components.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "github:sq11y/do11y",
|
|
7
|
+
"bin": "./dist/docs/cli.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"src/types.d.ts"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/docs/index.d.ts",
|
|
16
|
+
"import": "./dist/docs/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./types": "./src/types.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@mdit-vue/plugin-component": "^3.0.2",
|
|
22
|
+
"@mdit-vue/plugin-frontmatter": "^3.0.2",
|
|
23
|
+
"@mdit-vue/plugin-sfc": "^3.0.2",
|
|
24
|
+
"front-matter": "^4.0.2",
|
|
25
|
+
"jsdom": "^28.1.0",
|
|
26
|
+
"markdown-it": "^14.1.1",
|
|
27
|
+
"markdown-it-attrs": "^4.3.1",
|
|
28
|
+
"sass": "^1.97.3",
|
|
29
|
+
"tinyglobby": "^0.2.15",
|
|
30
|
+
"v-custom-block": "^1.0.67",
|
|
31
|
+
"vue-component-meta": "^3.2.4"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@tsconfig/node24": "^24.0.4",
|
|
35
|
+
"@types/jsdom": "^27.0.0",
|
|
36
|
+
"@types/markdown-it": "^14.1.2",
|
|
37
|
+
"@types/markdown-it-attrs": "^4.1.3",
|
|
38
|
+
"@types/node": "24.10.3",
|
|
39
|
+
"@vue/tsconfig": "^0.8.1",
|
|
40
|
+
"copyfiles": "^2.4.1",
|
|
41
|
+
"jiti": "^2.6.1",
|
|
42
|
+
"npm-run-all2": "^8.0.4",
|
|
43
|
+
"oxfmt": "^0.32.0",
|
|
44
|
+
"oxlint": "^1.47.0",
|
|
45
|
+
"typescript": "~5.9.3"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
49
|
+
"vite": "8.0.0-beta.14",
|
|
50
|
+
"vue": "^3.5.28",
|
|
51
|
+
"vue-router": "^5.0.2",
|
|
52
|
+
"vue-tsc": "^3.2.4"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=24"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"dev": "pnpm run -r dev",
|
|
59
|
+
"preview": "pnpm run -r preview",
|
|
60
|
+
"build:node": "tsc --p tsconfig.node.json && copyfiles -f src/docs/html/index.html dist/docs/html",
|
|
61
|
+
"build:client": "vue-tsc --build && vite build",
|
|
62
|
+
"build:example": "pnpm run -r build",
|
|
63
|
+
"build": "pnpm '/^build:.*/'",
|
|
64
|
+
"lint": "oxlint . --fix",
|
|
65
|
+
"fmt": "oxfmt ."
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
declare module "do11y:routes" {
|
|
2
|
+
import type { RouteRecordRaw as VueRouteRecordRaw, RouterOptions } from "vue-router";
|
|
3
|
+
|
|
4
|
+
type RouteRecordRaw = VueRouteRecordRaw & {
|
|
5
|
+
meta: {
|
|
6
|
+
title: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const routes: RouteRecordRaw[];
|
|
12
|
+
|
|
13
|
+
export default routes;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module "do11y:options" {
|
|
17
|
+
import type { Options } from "do11y";
|
|
18
|
+
|
|
19
|
+
const options: Omit<Options, "Layout" | "Sandbox" | "SandboxIframe"> & {
|
|
20
|
+
Layout: () => Promise<{ default: Component }>;
|
|
21
|
+
Sandbox?: () => Promise<{ default: Component }>;
|
|
22
|
+
SandboxIframe?: () => Promise<{ default: Component }>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default options;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module "do11y:sandbox" {
|
|
29
|
+
import type { Component } from "vue";
|
|
30
|
+
|
|
31
|
+
const sandboxes: {
|
|
32
|
+
url: string;
|
|
33
|
+
component: () => Promise<Component>;
|
|
34
|
+
}[];
|
|
35
|
+
|
|
36
|
+
export default sandboxes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare module "*.vue?meta" {
|
|
40
|
+
import type { Meta } from "do11y";
|
|
41
|
+
|
|
42
|
+
const meta: Meta;
|
|
43
|
+
|
|
44
|
+
export default meta;
|
|
45
|
+
}
|