mdx-artifacts 0.1.1 → 0.2.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 +224 -60
- package/README.zh-CN.md +299 -42
- package/agents/AGENTS.snippet.md +21 -10
- package/artifact-docs/examples/commentable-feedback.mdx +76 -70
- package/artifact-docs/examples/decision-matrix.mdx +122 -50
- package/artifact-docs/examples/layout-composition.mdx +106 -128
- package/artifact-docs/examples/streamlit-style-mixed.mdx +100 -85
- package/dist/lib/cli/{build.js → commands/build.js} +2 -2
- package/dist/lib/cli/{components.js → commands/components.js} +19 -3
- package/dist/lib/cli/{dev.js → commands/dev.js} +2 -2
- package/dist/lib/cli/commands/interactions.d.ts +2 -0
- package/dist/lib/cli/commands/interactions.js +280 -0
- package/dist/lib/cli/commands/review.d.ts +1 -0
- package/dist/lib/cli/commands/review.js +171 -0
- package/dist/lib/cli/commands/scaffold.d.ts +16 -0
- package/dist/lib/cli/commands/scaffold.js +440 -0
- package/dist/lib/cli/commands/validate.d.ts +18 -0
- package/dist/lib/cli/commands/validate.js +311 -0
- package/dist/lib/cli/config/config.d.ts +2 -0
- package/dist/lib/cli/{config.js → config/config.js} +3 -2
- package/dist/lib/cli/{types.d.ts → config/types.d.ts} +2 -1
- package/dist/lib/cli/{vite-artifact.d.ts → dev-server/vite-artifact.d.ts} +3 -3
- package/dist/lib/cli/dev-server/vite-artifact.js +415 -0
- package/dist/lib/cli/diagnostics/diagnostics.d.ts +11 -0
- package/dist/lib/cli/diagnostics/diagnostics.js +1 -0
- package/dist/lib/cli/index.js +39 -18
- package/dist/lib/cli/mdx/sortable-list.d.ts +14 -0
- package/dist/lib/cli/mdx/sortable-list.js +520 -0
- package/dist/lib/cli/resources/resource-policy.d.ts +15 -0
- package/dist/lib/cli/resources/resource-policy.js +46 -0
- package/dist/lib/cli/resources/safe-path.d.ts +13 -0
- package/dist/lib/cli/resources/safe-path.js +55 -0
- package/dist/lib/cli/services/interaction-service.d.ts +40 -0
- package/dist/lib/cli/services/interaction-service.js +226 -0
- package/dist/lib/cli/services/review.d.ts +43 -0
- package/dist/lib/cli/{review.js → services/review.js} +34 -172
- package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.d.ts +1 -1
- package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.js +3 -3
- package/dist/lib/react/composites/comparison-set/index.d.ts +2 -0
- package/dist/lib/react/composites/comparison-set/index.js +1 -0
- package/dist/lib/react/composites/content-set/ContentItem.d.ts +37 -0
- package/dist/lib/react/composites/content-set/ContentItem.js +49 -0
- package/dist/lib/react/composites/content-set/index.d.ts +2 -0
- package/dist/lib/react/composites/content-set/index.js +1 -0
- package/dist/lib/react/{components → composites/export-panel}/ExportPanel.js +2 -2
- package/dist/lib/react/composites/export-panel/index.d.ts +2 -0
- package/dist/lib/react/composites/export-panel/index.js +1 -0
- package/dist/lib/react/{components → composites/section}/Section.js +1 -1
- package/dist/lib/react/composites/section/index.d.ts +2 -0
- package/dist/lib/react/composites/section/index.js +1 -0
- package/dist/lib/react/index.d.ts +36 -31
- package/dist/lib/react/index.js +18 -15
- package/dist/lib/react/interactions/artifact-state/index.d.ts +1 -0
- package/dist/lib/react/interactions/artifact-state/index.js +1 -0
- package/dist/lib/react/{components → interactions/comments}/Comments.d.ts +2 -2
- package/dist/lib/react/{components → interactions/comments}/Comments.js +3 -3
- package/dist/lib/react/interactions/comments/index.d.ts +1 -0
- package/dist/lib/react/interactions/comments/index.js +1 -0
- package/dist/lib/react/interactions/sortable-list/SortableList.d.ts +29 -0
- package/dist/lib/react/interactions/sortable-list/SortableList.js +282 -0
- package/dist/lib/react/interactions/sortable-list/index.d.ts +1 -0
- package/dist/lib/react/interactions/sortable-list/index.js +1 -0
- package/dist/lib/react/layout/layout-primitives/index.d.ts +2 -0
- package/dist/lib/react/layout/layout-primitives/index.js +1 -0
- package/dist/lib/react/legacy/LegacyContentComponents.d.ts +65 -0
- package/dist/lib/react/legacy/LegacyContentComponents.js +26 -0
- package/dist/lib/react/mdx-components.d.ts +5 -0
- package/dist/lib/react/mdx-components.js +38 -0
- package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.d.ts +1 -1
- package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.js +5 -5
- package/dist/lib/react/primitives/annotated-code/index.d.ts +2 -0
- package/dist/lib/react/primitives/annotated-code/index.js +1 -0
- package/dist/lib/react/primitives/callout/Callout.d.ts +11 -0
- package/dist/lib/react/{components → primitives/callout}/Callout.js +9 -6
- package/dist/lib/react/primitives/callout/index.d.ts +2 -0
- package/dist/lib/react/primitives/callout/index.js +1 -0
- package/dist/lib/react/primitives/code-block/CodeBlock.d.ts +20 -0
- package/dist/lib/react/primitives/code-block/CodeBlock.js +32 -0
- package/dist/lib/react/primitives/code-block/index.d.ts +2 -0
- package/dist/lib/react/primitives/code-block/index.js +1 -0
- package/dist/lib/react/primitives/code-surface/CodeSurface.d.ts +11 -0
- package/dist/lib/react/primitives/code-surface/CodeSurface.js +34 -0
- package/dist/lib/react/primitives/code-surface/index.d.ts +2 -0
- package/dist/lib/react/primitives/code-surface/index.js +1 -0
- package/dist/lib/react/primitives/diff-block/DiffBlock.js +25 -0
- package/dist/lib/react/primitives/diff-block/index.d.ts +2 -0
- package/dist/lib/react/primitives/diff-block/index.js +1 -0
- package/dist/lib/react/{components → primitives/inline-text}/InlineText.d.ts +4 -2
- package/dist/lib/react/primitives/inline-text/InlineText.js +28 -0
- package/dist/lib/react/primitives/inline-text/index.d.ts +2 -0
- package/dist/lib/react/primitives/inline-text/index.js +1 -0
- package/dist/lib/react/primitives/markdown-body/MarkdownBody.d.ts +9 -0
- package/dist/lib/react/primitives/markdown-body/MarkdownBody.js +49 -0
- package/dist/lib/react/primitives/markdown-body/index.d.ts +2 -0
- package/dist/lib/react/primitives/markdown-body/index.js +1 -0
- package/dist/lib/react/primitives/severity-badge/index.d.ts +2 -0
- package/dist/lib/react/primitives/severity-badge/index.js +1 -0
- package/dist/lib/react/registry.d.ts +10 -0
- package/dist/lib/react/registry.js +505 -210
- package/dist/lib/react/styles.css +490 -38
- package/docs/cli-structure.md +141 -0
- package/docs/component-protocol.md +199 -33
- package/docs/component-taxonomy.md +40 -4
- package/docs/design.md +42 -21
- package/docs/design.zh-CN.md +41 -21
- package/docs/naming.md +17 -7
- package/docs/releasing.md +132 -0
- package/docs/testing.md +35 -10
- package/package.json +9 -7
- package/dist/lib/cli/config.d.ts +0 -2
- package/dist/lib/cli/review.d.ts +0 -33
- package/dist/lib/cli/scaffold.d.ts +0 -1
- package/dist/lib/cli/scaffold.js +0 -56
- package/dist/lib/cli/validate.d.ts +0 -6
- package/dist/lib/cli/validate.js +0 -79
- package/dist/lib/cli/vite-artifact.js +0 -237
- package/dist/lib/react/components/Callout.d.ts +0 -9
- package/dist/lib/react/components/CodeBlock.d.ts +0 -10
- package/dist/lib/react/components/CodeBlock.js +0 -28
- package/dist/lib/react/components/DecisionMatrix.d.ts +0 -16
- package/dist/lib/react/components/DecisionMatrix.js +0 -27
- package/dist/lib/react/components/DiffBlock.js +0 -24
- package/dist/lib/react/components/InlineText.js +0 -18
- package/dist/lib/react/components/MarkdownBody.d.ts +0 -7
- package/dist/lib/react/components/MarkdownBody.js +0 -36
- package/dist/lib/react/components/OptionGrid.d.ts +0 -13
- package/dist/lib/react/components/OptionGrid.js +0 -21
- /package/dist/lib/cli/{build.d.ts → commands/build.d.ts} +0 -0
- /package/dist/lib/cli/{components.d.ts → commands/components.d.ts} +0 -0
- /package/dist/lib/cli/{dev.d.ts → commands/dev.d.ts} +0 -0
- /package/dist/lib/cli/{types.js → config/types.js} +0 -0
- /package/dist/lib/cli/{artifact-state.d.ts → state/artifact-state.d.ts} +0 -0
- /package/dist/lib/cli/{artifact-state.js → state/artifact-state.js} +0 -0
- /package/dist/lib/react/{components → composites/export-panel}/ExportPanel.d.ts +0 -0
- /package/dist/lib/react/{components → composites/section}/Section.d.ts +0 -0
- /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.d.ts +0 -0
- /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.js +0 -0
- /package/dist/lib/react/{components → layout/layout-primitives}/Layout.d.ts +0 -0
- /package/dist/lib/react/{components → layout/layout-primitives}/Layout.js +0 -0
- /package/dist/lib/react/{components → primitives/diff-block}/DiffBlock.d.ts +0 -0
- /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.d.ts +0 -0
- /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.js +0 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import mdx from "@mdx-js/rollup";
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { createServer, build as viteBuild } from "vite";
|
|
10
|
+
import { createArtifactMeta, createArtifactRoute, readArtifactState, writeArtifactState } from "../state/artifact-state.js";
|
|
11
|
+
import { addInteractionItemService, promoteInteractionService, removeInteractionItemService, resetInteractionService, setInteractionOrderService, updateInteractionItemService } from "../services/interaction-service.js";
|
|
12
|
+
const packageCliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const defaultStylesPath = path.resolve(packageCliDir, "../../react/styles.css");
|
|
14
|
+
export async function createArtifactProject(projectRoot, mdxPath, config) {
|
|
15
|
+
const tmpDir = path.join(projectRoot, ".mdx-artifacts", "tmp", randomUUID());
|
|
16
|
+
const artifact = createArtifactRoute(projectRoot, mdxPath, config.docsDir);
|
|
17
|
+
const srcDir = path.join(tmpDir, "src");
|
|
18
|
+
const distDir = path.join(tmpDir, "dist");
|
|
19
|
+
const entryPath = path.join(srcDir, "entry.tsx");
|
|
20
|
+
const tailwindSourcePath = path.join(srcDir, "artifact-tailwind-sources.css");
|
|
21
|
+
const mdxImport = toRelativeImport(entryPath, mdxPath);
|
|
22
|
+
const styleImports = createStyleImports(projectRoot, entryPath, config, tailwindSourcePath);
|
|
23
|
+
const reactEntryPath = await resolveReactEntryPath();
|
|
24
|
+
const reactEntryImport = toRelativeImport(entryPath, reactEntryPath);
|
|
25
|
+
const reactAliases = resolveReactAliases(projectRoot);
|
|
26
|
+
await mkdir(srcDir, { recursive: true });
|
|
27
|
+
await writeFile(tailwindSourcePath, createTailwindSourceCss(projectRoot, tailwindSourcePath, mdxPath, config));
|
|
28
|
+
await writeFile(path.join(tmpDir, "index.html"), `<!doctype html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="UTF-8" />
|
|
32
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
33
|
+
<title>MDX Artifact</title>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<div id="root"></div>
|
|
37
|
+
<script type="module" src="/src/entry.tsx"></script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
40
|
+
`);
|
|
41
|
+
await writeFile(entryPath, `import React from "react";
|
|
42
|
+
import { createRoot } from "react-dom/client";
|
|
43
|
+
import { ArtifactStateProvider, CommentLayer, artifactMdxComponents } from "${reactEntryImport}";
|
|
44
|
+
import Doc from "${mdxImport}";
|
|
45
|
+
${styleImports}
|
|
46
|
+
|
|
47
|
+
function App() {
|
|
48
|
+
return (
|
|
49
|
+
<main className="ak-shell">
|
|
50
|
+
<article className="ak-document">
|
|
51
|
+
<ArtifactStateProvider>
|
|
52
|
+
<CommentLayer>
|
|
53
|
+
<Doc components={artifactMdxComponents} />
|
|
54
|
+
</CommentLayer>
|
|
55
|
+
</ArtifactStateProvider>
|
|
56
|
+
</article>
|
|
57
|
+
</main>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
createRoot(document.getElementById("root")!).render(<App />);
|
|
62
|
+
`);
|
|
63
|
+
const viteConfig = {
|
|
64
|
+
root: tmpDir,
|
|
65
|
+
logLevel: "warn",
|
|
66
|
+
plugins: [artifactStatePlugin(projectRoot, artifact), react(), mdx(), tailwindcss()],
|
|
67
|
+
resolve: {
|
|
68
|
+
alias: [
|
|
69
|
+
...reactAliases,
|
|
70
|
+
{ find: /^mdx-artifacts\/react$/, replacement: reactEntryPath },
|
|
71
|
+
{ find: /^mdx-artifacts$/, replacement: reactEntryPath }
|
|
72
|
+
],
|
|
73
|
+
dedupe: ["react", "react-dom"]
|
|
74
|
+
},
|
|
75
|
+
optimizeDeps: {
|
|
76
|
+
exclude: ["mdx-artifacts", "mdx-artifacts/react"]
|
|
77
|
+
},
|
|
78
|
+
server: {
|
|
79
|
+
port: config.port,
|
|
80
|
+
fs: {
|
|
81
|
+
allow: [projectRoot]
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
build: {
|
|
85
|
+
outDir: distDir,
|
|
86
|
+
emptyOutDir: true,
|
|
87
|
+
cssCodeSplit: false,
|
|
88
|
+
assetsInlineLimit: Number.MAX_SAFE_INTEGER,
|
|
89
|
+
rollupOptions: {
|
|
90
|
+
input: path.join(tmpDir, "index.html")
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
artifact,
|
|
96
|
+
tmpDir,
|
|
97
|
+
distDir,
|
|
98
|
+
config: viteConfig,
|
|
99
|
+
cleanup: () => rm(tmpDir, { recursive: true, force: true })
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function artifactStatePlugin(projectRoot, artifact) {
|
|
103
|
+
return {
|
|
104
|
+
name: "mdx-artifacts-state",
|
|
105
|
+
configureServer(server) {
|
|
106
|
+
server.middlewares.use(async (request, response, next) => {
|
|
107
|
+
const requestUrl = new URL(request.url ?? "/", "http://localhost");
|
|
108
|
+
try {
|
|
109
|
+
if (requestUrl.pathname === "/__artifact/meta") {
|
|
110
|
+
await handleArtifactMeta(projectRoot, artifact, request, response);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (requestUrl.pathname === "/__artifact/state") {
|
|
114
|
+
await handleArtifactState(projectRoot, artifact, request, response);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (requestUrl.pathname.startsWith("/__artifact/interactions/")) {
|
|
118
|
+
await handleArtifactInteraction(projectRoot, artifact, requestUrl.pathname, request, response);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
sendJson(response, 500, {
|
|
124
|
+
error: error instanceof Error ? error.message : String(error)
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
next();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async function handleArtifactMeta(projectRoot, artifact, request, response) {
|
|
134
|
+
if (request.method !== "GET") {
|
|
135
|
+
sendJson(response, 405, { error: "Method not allowed." });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
sendJson(response, 200, await createArtifactMeta(projectRoot, artifact));
|
|
139
|
+
}
|
|
140
|
+
async function handleArtifactState(projectRoot, artifact, request, response) {
|
|
141
|
+
if (request.method === "GET") {
|
|
142
|
+
sendJson(response, 200, await readArtifactState(artifact));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (request.method === "POST") {
|
|
146
|
+
let value;
|
|
147
|
+
try {
|
|
148
|
+
value = JSON.parse(await readRequestBody(request));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
sendJson(response, 400, { error: "Request body must be valid JSON." });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const state = await writeArtifactState(projectRoot, artifact, value);
|
|
155
|
+
sendJson(response, 200, { ok: true, state });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
sendJson(response, 405, { error: "Method not allowed." });
|
|
159
|
+
}
|
|
160
|
+
async function handleArtifactInteraction(projectRoot, artifact, pathname, request, response) {
|
|
161
|
+
if (request.method !== "POST") {
|
|
162
|
+
sendJson(response, 405, { error: "Method not allowed." });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let value;
|
|
166
|
+
try {
|
|
167
|
+
value = JSON.parse(await readRequestBody(request));
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
sendJson(response, 400, { error: "Request body must be valid JSON." });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
if (pathname === "/__artifact/interactions/set-order") {
|
|
175
|
+
const body = parseSetOrderRequest(value);
|
|
176
|
+
const result = await setInteractionOrderService(projectRoot, artifact.sourceRelativePath, body.id, body.orderedIds);
|
|
177
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (pathname === "/__artifact/interactions/reset") {
|
|
181
|
+
const body = parseInteractionIdRequest(value, "reset");
|
|
182
|
+
const result = await resetInteractionService(projectRoot, artifact.sourceRelativePath, body.id);
|
|
183
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (pathname === "/__artifact/interactions/promote") {
|
|
187
|
+
const body = parseInteractionIdRequest(value, "promote");
|
|
188
|
+
const result = await promoteInteractionService(projectRoot, artifact.sourceRelativePath, body.id);
|
|
189
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (pathname === "/__artifact/interactions/add-item") {
|
|
193
|
+
const body = parseAddItemRequest(value);
|
|
194
|
+
const result = await addInteractionItemService(projectRoot, artifact.sourceRelativePath, body.id, body.item, {
|
|
195
|
+
afterId: body.afterId
|
|
196
|
+
});
|
|
197
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (pathname === "/__artifact/interactions/remove-item") {
|
|
201
|
+
const body = parseItemIdRequest(value, "remove-item");
|
|
202
|
+
const result = await removeInteractionItemService(projectRoot, artifact.sourceRelativePath, body.id, body.itemId);
|
|
203
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (pathname === "/__artifact/interactions/update-item") {
|
|
207
|
+
const body = parseUpdateItemRequest(value);
|
|
208
|
+
const result = await updateInteractionItemService(projectRoot, artifact.sourceRelativePath, body.id, body.itemId, body.patch);
|
|
209
|
+
sendJson(response, 200, { ok: true, result, state: result.state });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
sendJson(response, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
sendJson(response, 404, { error: "Interaction endpoint not found." });
|
|
218
|
+
}
|
|
219
|
+
function parseAddItemRequest(value) {
|
|
220
|
+
const body = parseInteractionIdRequest(value, "add-item");
|
|
221
|
+
if (!isRecord(value) || !isRecord(value.item)) {
|
|
222
|
+
throw new Error("interactions add-item requires item.");
|
|
223
|
+
}
|
|
224
|
+
if (typeof value.item.id !== "string" || !value.item.id) {
|
|
225
|
+
throw new Error("interactions add-item requires item.id.");
|
|
226
|
+
}
|
|
227
|
+
if (typeof value.item.title !== "string" || !value.item.title) {
|
|
228
|
+
throw new Error("interactions add-item requires item.title.");
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
id: body.id,
|
|
232
|
+
item: normalizeItem(value.item),
|
|
233
|
+
afterId: typeof value.afterId === "string" && value.afterId ? value.afterId : undefined
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function parseUpdateItemRequest(value) {
|
|
237
|
+
const body = parseItemIdRequest(value, "update-item");
|
|
238
|
+
if (!isRecord(value) || !isRecord(value.patch)) {
|
|
239
|
+
throw new Error("interactions update-item requires patch.");
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
id: body.id,
|
|
243
|
+
itemId: body.itemId,
|
|
244
|
+
patch: normalizeItemPatch(value.patch)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function parseItemIdRequest(value, action) {
|
|
248
|
+
const body = parseInteractionIdRequest(value, action);
|
|
249
|
+
if (!isRecord(value) || typeof value.itemId !== "string" || !value.itemId) {
|
|
250
|
+
throw new Error(`interactions ${action} requires itemId.`);
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
id: body.id,
|
|
254
|
+
itemId: value.itemId
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function parseSetOrderRequest(value) {
|
|
258
|
+
const body = parseInteractionIdRequest(value, "set-order");
|
|
259
|
+
if (!isRecord(value) || !Array.isArray(value.orderedIds)) {
|
|
260
|
+
throw new Error("interactions set-order requires orderedIds.");
|
|
261
|
+
}
|
|
262
|
+
const orderedIds = value.orderedIds.filter((itemId) => typeof itemId === "string");
|
|
263
|
+
if (orderedIds.length !== value.orderedIds.length || orderedIds.length === 0) {
|
|
264
|
+
throw new Error("interactions set-order requires non-empty string orderedIds.");
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
id: body.id,
|
|
268
|
+
orderedIds
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function normalizeItem(value) {
|
|
272
|
+
return {
|
|
273
|
+
id: value.id,
|
|
274
|
+
title: value.title,
|
|
275
|
+
...normalizeItemPatch(value)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function normalizeItemPatch(value) {
|
|
279
|
+
return {
|
|
280
|
+
...(typeof value.title === "string" ? { title: value.title } : {}),
|
|
281
|
+
...("summary" in value ? { summary: typeof value.summary === "string" ? value.summary : undefined } : {}),
|
|
282
|
+
...("badge" in value ? { badge: typeof value.badge === "string" ? value.badge : undefined } : {}),
|
|
283
|
+
...("tags" in value
|
|
284
|
+
? { tags: Array.isArray(value.tags) ? value.tags.filter((tag) => typeof tag === "string") : undefined }
|
|
285
|
+
: {}),
|
|
286
|
+
...(typeof value.disabled === "boolean" ? { disabled: value.disabled } : {})
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function parseInteractionIdRequest(value, action) {
|
|
290
|
+
if (!isRecord(value) || typeof value.id !== "string" || !value.id) {
|
|
291
|
+
throw new Error(`interactions ${action} requires id.`);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
id: value.id
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function readRequestBody(request) {
|
|
298
|
+
return new Promise((resolve, reject) => {
|
|
299
|
+
let body = "";
|
|
300
|
+
request.setEncoding("utf8");
|
|
301
|
+
request.on("data", (chunk) => {
|
|
302
|
+
body += chunk;
|
|
303
|
+
});
|
|
304
|
+
request.on("end", () => resolve(body));
|
|
305
|
+
request.on("error", reject);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function sendJson(response, statusCode, value) {
|
|
309
|
+
response.statusCode = statusCode;
|
|
310
|
+
response.setHeader("content-type", "application/json; charset=utf-8");
|
|
311
|
+
response.end(JSON.stringify(value, null, 2));
|
|
312
|
+
}
|
|
313
|
+
function isRecord(value) {
|
|
314
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
315
|
+
}
|
|
316
|
+
async function resolveReactEntryPath() {
|
|
317
|
+
const builtEntryPath = path.resolve(packageCliDir, "../../react/index.js");
|
|
318
|
+
const sourceEntryPath = path.resolve(packageCliDir, "../../react/index.ts");
|
|
319
|
+
try {
|
|
320
|
+
await access(builtEntryPath);
|
|
321
|
+
return builtEntryPath;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return sourceEntryPath;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function resolveReactAliases(projectRoot) {
|
|
328
|
+
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
329
|
+
const packageRequire = createRequire(import.meta.url);
|
|
330
|
+
return [
|
|
331
|
+
{ find: /^react$/, replacement: resolveFromProjectOrPackage(projectRequire, packageRequire, "react") },
|
|
332
|
+
{
|
|
333
|
+
find: /^react\/jsx-runtime$/,
|
|
334
|
+
replacement: resolveFromProjectOrPackage(projectRequire, packageRequire, "react/jsx-runtime")
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
find: /^react\/jsx-dev-runtime$/,
|
|
338
|
+
replacement: resolveFromProjectOrPackage(projectRequire, packageRequire, "react/jsx-dev-runtime")
|
|
339
|
+
},
|
|
340
|
+
{ find: /^react-dom$/, replacement: resolveFromProjectOrPackage(projectRequire, packageRequire, "react-dom") },
|
|
341
|
+
{
|
|
342
|
+
find: /^react-dom\/client$/,
|
|
343
|
+
replacement: resolveFromProjectOrPackage(projectRequire, packageRequire, "react-dom/client")
|
|
344
|
+
}
|
|
345
|
+
];
|
|
346
|
+
}
|
|
347
|
+
function resolveFromProjectOrPackage(projectRequire, packageRequire, specifier) {
|
|
348
|
+
try {
|
|
349
|
+
return projectRequire.resolve(specifier);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return packageRequire.resolve(specifier);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
export async function startDevServer(project) {
|
|
356
|
+
const server = await createServer(project.config);
|
|
357
|
+
await server.listen();
|
|
358
|
+
return server;
|
|
359
|
+
}
|
|
360
|
+
export async function buildArtifact(project) {
|
|
361
|
+
await viteBuild(project.config);
|
|
362
|
+
return inlineBuildAssets(project.distDir);
|
|
363
|
+
}
|
|
364
|
+
async function inlineBuildAssets(distDir) {
|
|
365
|
+
const htmlPath = path.join(distDir, "index.html");
|
|
366
|
+
let html = await readFile(htmlPath, "utf8");
|
|
367
|
+
html = await replaceAsync(html, /<link rel="stylesheet" crossorigin href="([^"]+)">/g, async (_match, href) => {
|
|
368
|
+
const css = await readFile(assetPath(distDir, href), "utf8");
|
|
369
|
+
return `<style>${css}</style>`;
|
|
370
|
+
});
|
|
371
|
+
html = await replaceAsync(html, /<script type="module" crossorigin src="([^"]+)"><\/script>/g, async (_match, src) => {
|
|
372
|
+
const js = await readFile(assetPath(distDir, src), "utf8");
|
|
373
|
+
return `<script type="module">${escapeScriptContent(js)}</script>`;
|
|
374
|
+
});
|
|
375
|
+
return html;
|
|
376
|
+
}
|
|
377
|
+
function assetPath(distDir, value) {
|
|
378
|
+
return path.join(distDir, value.replace(/^\//, ""));
|
|
379
|
+
}
|
|
380
|
+
function toRelativeImport(fromFile, targetFile) {
|
|
381
|
+
const relative = path.relative(path.dirname(fromFile), targetFile).replaceAll(path.sep, "/");
|
|
382
|
+
return relative.startsWith(".") ? relative : `./${relative}`;
|
|
383
|
+
}
|
|
384
|
+
function createStyleImports(projectRoot, entryPath, config, tailwindSourcePath) {
|
|
385
|
+
const styles = [
|
|
386
|
+
tailwindSourcePath,
|
|
387
|
+
...(config.includeDefaultStyles ? [defaultStylesPath] : []),
|
|
388
|
+
...config.styles.map((stylePath) => path.resolve(projectRoot, stylePath))
|
|
389
|
+
];
|
|
390
|
+
return styles.map((stylePath) => `import "${toRelativeImport(entryPath, stylePath)}";`).join("\n");
|
|
391
|
+
}
|
|
392
|
+
function createTailwindSourceCss(projectRoot, sourceStylesPath, mdxPath, config) {
|
|
393
|
+
const sources = [
|
|
394
|
+
mdxPath,
|
|
395
|
+
...config.tailwindSources.map((sourcePath) => path.resolve(projectRoot, sourcePath))
|
|
396
|
+
];
|
|
397
|
+
return `@import "tailwindcss";\n${sources
|
|
398
|
+
.map((sourcePath) => `@source "${escapeCssString(toRelativeImport(sourceStylesPath, sourcePath))}";`)
|
|
399
|
+
.join("\n")}\n`;
|
|
400
|
+
}
|
|
401
|
+
function escapeCssString(value) {
|
|
402
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
403
|
+
}
|
|
404
|
+
async function replaceAsync(source, pattern, replacer) {
|
|
405
|
+
const matches = Array.from(source.matchAll(pattern));
|
|
406
|
+
let output = source;
|
|
407
|
+
for (const match of matches) {
|
|
408
|
+
const replacement = await replacer(match[0], ...match.slice(1));
|
|
409
|
+
output = output.replace(match[0], () => replacement);
|
|
410
|
+
}
|
|
411
|
+
return output;
|
|
412
|
+
}
|
|
413
|
+
function escapeScriptContent(source) {
|
|
414
|
+
return source.replace(/<\/script/gi, "<\\/script");
|
|
415
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/lib/cli/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { buildCommand } from "./build.js";
|
|
4
|
-
import { componentsCommand } from "./components.js";
|
|
5
|
-
import { devCommand } from "./dev.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { buildCommand } from "./commands/build.js";
|
|
4
|
+
import { componentsCommand } from "./commands/components.js";
|
|
5
|
+
import { devCommand } from "./commands/dev.js";
|
|
6
|
+
import { interactionsCommand } from "./commands/interactions.js";
|
|
7
|
+
import { reviewCommand } from "./commands/review.js";
|
|
8
|
+
import { initProject, parseInitOptions } from "./commands/scaffold.js";
|
|
9
|
+
import { formatValidationJson, printValidationResult, validateMdx } from "./commands/validate.js";
|
|
10
|
+
import { loadConfig } from "./config/config.js";
|
|
9
11
|
const projectRoot = process.cwd();
|
|
10
12
|
const [command, input] = process.argv.slice(2);
|
|
11
13
|
async function main() {
|
|
@@ -14,7 +16,7 @@ async function main() {
|
|
|
14
16
|
return;
|
|
15
17
|
}
|
|
16
18
|
if (command === "init") {
|
|
17
|
-
await initProject(projectRoot);
|
|
19
|
+
await initProject(projectRoot, await parseInitOptions(process.argv.slice(3)));
|
|
18
20
|
return;
|
|
19
21
|
}
|
|
20
22
|
if (command === "components") {
|
|
@@ -28,12 +30,24 @@ async function main() {
|
|
|
28
30
|
await reviewCommand(projectRoot, process.argv.slice(3));
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
33
|
+
if (command === "interactions") {
|
|
34
|
+
await interactionsCommand(projectRoot, process.argv.slice(3));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
31
37
|
if (!input) {
|
|
32
38
|
throw new Error(`${command} requires a .mdx file path.`);
|
|
33
39
|
}
|
|
34
40
|
if (command === "validate") {
|
|
35
|
-
const
|
|
36
|
-
|
|
41
|
+
const args = process.argv.slice(3);
|
|
42
|
+
const json = args.includes("--json");
|
|
43
|
+
const config = await loadConfig(projectRoot);
|
|
44
|
+
const result = await validateMdx(path.resolve(projectRoot, input), { projectRoot, config });
|
|
45
|
+
if (json) {
|
|
46
|
+
console.log(JSON.stringify(formatValidationJson(result), null, 2));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
printValidationResult(result);
|
|
50
|
+
}
|
|
37
51
|
if (result.errors.length > 0) {
|
|
38
52
|
process.exitCode = 1;
|
|
39
53
|
}
|
|
@@ -50,17 +64,24 @@ async function main() {
|
|
|
50
64
|
throw new Error(`Unknown command: ${command}`);
|
|
51
65
|
}
|
|
52
66
|
function printHelp() {
|
|
53
|
-
console.log(`
|
|
67
|
+
console.log(`mdx-artifacts
|
|
54
68
|
|
|
55
69
|
Usage:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
mdx-artifacts init [--yes] [--docs-dir <dir>] [--components-dir <dir>] [--agent <generic|codex|claude-code|cursor|all>]
|
|
71
|
+
mdx-artifacts components [ComponentName] [--json]
|
|
72
|
+
mdx-artifacts validate <file.mdx> [--json]
|
|
73
|
+
mdx-artifacts interactions inspect <file.mdx> <id> [--json]
|
|
74
|
+
mdx-artifacts interactions set-order <file.mdx> <id> --ordered-ids <id> [...id]
|
|
75
|
+
mdx-artifacts interactions reset <file.mdx> <id>
|
|
76
|
+
mdx-artifacts interactions promote <file.mdx> <id>
|
|
77
|
+
mdx-artifacts interactions add-item <file.mdx> <id> --item-id <id> --title <title> [--summary <text>] [--badge <text>] [--tags <tag,tag>] [--disabled true|false] [--after <itemId>]
|
|
78
|
+
mdx-artifacts interactions remove-item <file.mdx> <id> --item-id <id>
|
|
79
|
+
mdx-artifacts interactions update-item <file.mdx> <id> --item-id <id> [--title <title>] [--summary <text>] [--badge <text>] [--tags <tag,tag>] [--disabled true|false]
|
|
80
|
+
mdx-artifacts review add <file.mdx> --anchor <anchorId> --body <message> [--title <title>]
|
|
81
|
+
mdx-artifacts review reply <file.mdx> --thread <threadId> --body <message> [...repeat] [--status <status>]
|
|
82
|
+
mdx-artifacts review validate <file.mdx>
|
|
83
|
+
mdx-artifacts dev <file.mdx>
|
|
84
|
+
mdx-artifacts build <file.mdx>
|
|
64
85
|
`);
|
|
65
86
|
}
|
|
66
87
|
main().catch((error) => {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SortableListItem } from "../../react";
|
|
2
|
+
export type SortableListSeed = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
summary?: string;
|
|
6
|
+
items: SortableListItem[];
|
|
7
|
+
};
|
|
8
|
+
export declare function extractSortableListSeeds(source: string): SortableListSeed[];
|
|
9
|
+
export declare function promoteSortableListOrder(source: string, listId: string, orderedIds: string[]): string;
|
|
10
|
+
export declare function addSortableListItem(source: string, listId: string, item: SortableListItem, options?: {
|
|
11
|
+
afterId?: string;
|
|
12
|
+
}): string;
|
|
13
|
+
export declare function removeSortableListItem(source: string, listId: string, itemId: string): string;
|
|
14
|
+
export declare function updateSortableListItem(source: string, listId: string, itemId: string, patch: Partial<Omit<SortableListItem, "id">>): string;
|