@designtools/next-plugin 0.1.6 → 0.1.8
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 +58 -0
- package/dist/chunk-6DZX6EAA.mjs +37 -0
- package/dist/codesurface-mount-loader.js +6 -4
- package/dist/codesurface-mount-loader.mjs +6 -4
- package/dist/codesurface.d.mts +3 -1
- package/dist/codesurface.d.ts +3 -1
- package/dist/codesurface.js +192 -12
- package/dist/codesurface.mjs +193 -13
- package/dist/index.js +75 -162
- package/dist/index.mjs +75 -162
- package/package.json +2 -2
- package/src/codesurface-mount-loader.ts +10 -6
- package/src/codesurface.tsx +254 -25
- package/src/index.ts +72 -12
- package/src/preview-route.ts +33 -176
package/dist/index.mjs
CHANGED
|
@@ -6,27 +6,30 @@ import path2 from "path";
|
|
|
6
6
|
// src/preview-route.ts
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import path from "path";
|
|
9
|
-
var
|
|
10
|
-
function
|
|
9
|
+
var REGISTRY_FILE = "designtools-registry.ts";
|
|
10
|
+
function generateComponentRegistry(appDir) {
|
|
11
11
|
const projectRoot = path.dirname(appDir);
|
|
12
|
-
const previewDir = path.join(appDir, PREVIEW_DIR);
|
|
13
12
|
const componentPaths = discoverComponentFiles(projectRoot);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
|
|
14
|
+
const content = `"use client";
|
|
15
|
+
// Auto-generated by @designtools/next-plugin \u2014 do not edit
|
|
16
|
+
|
|
17
|
+
const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
|
|
18
|
+
${registryEntries}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Self-register on window so CodeSurface can access it without
|
|
22
|
+
// passing functions through the RSC serialization boundary.
|
|
23
|
+
if (typeof window !== "undefined") {
|
|
24
|
+
(window as any).__DESIGNTOOLS_REGISTRY__ = COMPONENT_REGISTRY;
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
fs.writeFileSync(path.join(appDir, REGISTRY_FILE), content, "utf-8");
|
|
25
28
|
ensureGitignore(projectRoot);
|
|
26
29
|
}
|
|
27
30
|
function ensureGitignore(projectRoot) {
|
|
28
31
|
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
29
|
-
const entry = "app/designtools-
|
|
32
|
+
const entry = "app/designtools-registry.ts";
|
|
30
33
|
try {
|
|
31
34
|
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
|
|
32
35
|
if (!existing.includes(entry)) {
|
|
@@ -38,8 +41,8 @@ ${entry}
|
|
|
38
41
|
} catch {
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
|
-
function discoverComponentFiles(projectRoot) {
|
|
42
|
-
const dirs = ["components/ui", "src/components/ui"];
|
|
44
|
+
function discoverComponentFiles(projectRoot, overrideDir) {
|
|
45
|
+
const dirs = overrideDir ? [overrideDir] : ["components/ui", "src/components/ui"];
|
|
43
46
|
for (const dir of dirs) {
|
|
44
47
|
const fullDir = path.join(projectRoot, dir);
|
|
45
48
|
if (fs.existsSync(fullDir)) {
|
|
@@ -49,149 +52,16 @@ function discoverComponentFiles(projectRoot) {
|
|
|
49
52
|
}
|
|
50
53
|
return [];
|
|
51
54
|
}
|
|
52
|
-
function getLayoutTemplate() {
|
|
53
|
-
return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
|
|
54
|
-
export default function PreviewLayout({ children }: { children: React.ReactNode }) {
|
|
55
|
-
return (
|
|
56
|
-
<div style={{ padding: 32, background: "var(--background, #fff)", minHeight: "100vh" }}>
|
|
57
|
-
{children}
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
62
|
-
}
|
|
63
|
-
function getPageTemplate(componentPaths) {
|
|
64
|
-
const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
|
|
65
|
-
return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
|
|
66
|
-
"use client";
|
|
67
|
-
|
|
68
|
-
import { useState, useEffect, useCallback, createElement } from "react";
|
|
69
|
-
|
|
70
|
-
/* Static import registry \u2014 webpack can analyze these imports */
|
|
71
|
-
const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
|
|
72
|
-
${registryEntries}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
interface Combination {
|
|
76
|
-
label: string;
|
|
77
|
-
props: Record<string, string>;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface RenderMsg {
|
|
81
|
-
type: "tool:renderPreview";
|
|
82
|
-
componentPath: string;
|
|
83
|
-
exportName: string;
|
|
84
|
-
combinations: Combination[];
|
|
85
|
-
defaultChildren: string;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export default function PreviewPage() {
|
|
89
|
-
const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
|
|
90
|
-
const [combinations, setCombinations] = useState<Combination[]>([]);
|
|
91
|
-
const [defaultChildren, setDefaultChildren] = useState("");
|
|
92
|
-
const [error, setError] = useState<string | null>(null);
|
|
93
|
-
|
|
94
|
-
const handleMessage = useCallback(async (e: MessageEvent) => {
|
|
95
|
-
const msg = e.data;
|
|
96
|
-
if (msg?.type !== "tool:renderPreview") return;
|
|
97
|
-
|
|
98
|
-
const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg as RenderMsg;
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
setError(null);
|
|
102
|
-
setCombinations(combos);
|
|
103
|
-
setDefaultChildren(children || exportName);
|
|
104
|
-
|
|
105
|
-
const loader = COMPONENT_REGISTRY[componentPath];
|
|
106
|
-
if (!loader) {
|
|
107
|
-
setError(\`Component "\${componentPath}" not found in registry. Available: \${Object.keys(COMPONENT_REGISTRY).join(", ")}\`);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const mod = await loader();
|
|
112
|
-
const Comp = mod[exportName] || mod.default;
|
|
113
|
-
if (!Comp) {
|
|
114
|
-
setError(\`Export "\${exportName}" not found in \${componentPath}\`);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
setComponent(() => Comp);
|
|
119
|
-
|
|
120
|
-
// Notify editor that preview is ready
|
|
121
|
-
window.parent.postMessage(
|
|
122
|
-
{ type: "tool:previewReady", cellCount: combos.length },
|
|
123
|
-
"*"
|
|
124
|
-
);
|
|
125
|
-
} catch (err: any) {
|
|
126
|
-
setError(\`Failed to load component: \${err.message}\`);
|
|
127
|
-
}
|
|
128
|
-
}, []);
|
|
129
|
-
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
window.addEventListener("message", handleMessage);
|
|
132
|
-
// Signal readiness to the editor
|
|
133
|
-
window.parent.postMessage({ type: "tool:injectedReady" }, "*");
|
|
134
|
-
return () => window.removeEventListener("message", handleMessage);
|
|
135
|
-
}, [handleMessage]);
|
|
136
|
-
|
|
137
|
-
if (error) {
|
|
138
|
-
return (
|
|
139
|
-
<div style={{ padding: 32, color: "#ef4444", fontFamily: "monospace", fontSize: 14 }}>
|
|
140
|
-
{error}
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!Component) {
|
|
146
|
-
return (
|
|
147
|
-
<div style={{ padding: 32, color: "#888", fontFamily: "system-ui", fontSize: 14 }}>
|
|
148
|
-
Waiting for component\u2026
|
|
149
|
-
</div>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<div style={{ fontFamily: "system-ui" }}>
|
|
155
|
-
<div style={{
|
|
156
|
-
display: "grid",
|
|
157
|
-
gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
|
|
158
|
-
gap: 24,
|
|
159
|
-
}}>
|
|
160
|
-
{combinations.map((combo, i) => (
|
|
161
|
-
<div key={i} style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
162
|
-
<div style={{
|
|
163
|
-
fontSize: 11,
|
|
164
|
-
fontWeight: 600,
|
|
165
|
-
color: "#888",
|
|
166
|
-
textTransform: "uppercase",
|
|
167
|
-
letterSpacing: "0.05em",
|
|
168
|
-
}}>
|
|
169
|
-
{combo.label}
|
|
170
|
-
</div>
|
|
171
|
-
<div style={{
|
|
172
|
-
padding: 16,
|
|
173
|
-
border: "1px solid #e5e7eb",
|
|
174
|
-
borderRadius: 8,
|
|
175
|
-
display: "flex",
|
|
176
|
-
alignItems: "center",
|
|
177
|
-
justifyContent: "center",
|
|
178
|
-
minHeight: 64,
|
|
179
|
-
background: "#fff",
|
|
180
|
-
}}>
|
|
181
|
-
{createElement(Component, combo.props, defaultChildren)}
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
))}
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
`;
|
|
190
|
-
}
|
|
191
55
|
|
|
192
56
|
// src/index.ts
|
|
193
57
|
function withDesigntools(nextConfig = {}) {
|
|
194
|
-
|
|
58
|
+
const projectRoot = process.cwd();
|
|
59
|
+
const appDir = path2.resolve(projectRoot, "app");
|
|
60
|
+
try {
|
|
61
|
+
generateComponentRegistry(appDir);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
const result = {
|
|
195
65
|
...nextConfig,
|
|
196
66
|
webpack(config, context) {
|
|
197
67
|
if (context.dev) {
|
|
@@ -219,11 +89,6 @@ function withDesigntools(nextConfig = {}) {
|
|
|
219
89
|
}
|
|
220
90
|
]
|
|
221
91
|
});
|
|
222
|
-
const appDir = path2.resolve(context.dir, "app");
|
|
223
|
-
try {
|
|
224
|
-
generatePreviewRoute(appDir);
|
|
225
|
-
} catch {
|
|
226
|
-
}
|
|
227
92
|
}
|
|
228
93
|
if (typeof nextConfig.webpack === "function") {
|
|
229
94
|
return nextConfig.webpack(config, context);
|
|
@@ -231,6 +96,54 @@ function withDesigntools(nextConfig = {}) {
|
|
|
231
96
|
return config;
|
|
232
97
|
}
|
|
233
98
|
};
|
|
99
|
+
if (process.env.TURBOPACK) {
|
|
100
|
+
const sourceLoader = {
|
|
101
|
+
loader: path2.resolve(__dirname, "loader.js"),
|
|
102
|
+
options: {
|
|
103
|
+
// In webpack, context.dir provides this. In turbopack config time,
|
|
104
|
+
// process.cwd() is equivalent since next.config runs from the project root.
|
|
105
|
+
cwd: projectRoot
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const mountLoader = {
|
|
109
|
+
loader: path2.resolve(__dirname, "codesurface-mount-loader.js")
|
|
110
|
+
};
|
|
111
|
+
const sourceRule = {
|
|
112
|
+
loaders: [sourceLoader],
|
|
113
|
+
as: "*.tsx",
|
|
114
|
+
condition: {
|
|
115
|
+
path: /\.(tsx|jsx)$/
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const wildcardGlob = "{*,designtools-source}";
|
|
119
|
+
let wildcardRule = nextConfig.turbopack?.rules?.[wildcardGlob] ?? [];
|
|
120
|
+
wildcardRule = [
|
|
121
|
+
...Array.isArray(wildcardRule) ? wildcardRule : [wildcardRule],
|
|
122
|
+
sourceRule
|
|
123
|
+
];
|
|
124
|
+
const mountRule = {
|
|
125
|
+
loaders: [mountLoader],
|
|
126
|
+
as: "*.tsx",
|
|
127
|
+
condition: {
|
|
128
|
+
path: /layout\.(tsx|jsx)$/
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const mountGlob = "{*,designtools-mount}";
|
|
132
|
+
let mountGlobRule = nextConfig.turbopack?.rules?.[mountGlob] ?? [];
|
|
133
|
+
mountGlobRule = [
|
|
134
|
+
...Array.isArray(mountGlobRule) ? mountGlobRule : [mountGlobRule],
|
|
135
|
+
mountRule
|
|
136
|
+
];
|
|
137
|
+
result.turbopack = {
|
|
138
|
+
...nextConfig.turbopack,
|
|
139
|
+
rules: {
|
|
140
|
+
...nextConfig.turbopack?.rules,
|
|
141
|
+
[wildcardGlob]: wildcardRule,
|
|
142
|
+
[mountGlob]: mountGlobRule
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
234
147
|
}
|
|
235
148
|
export {
|
|
236
149
|
withDesigntools
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@designtools/next-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsup src/index.ts src/loader.ts src/codesurface.tsx src/codesurface-mount-loader.ts --format cjs,esm --dts --external @babel/core --external react --external react/jsx-runtime"
|
|
16
|
+
"build": "tsup src/index.ts src/loader.ts src/codesurface.tsx src/codesurface-mount-loader.ts --format cjs,esm --dts --external @babel/core --external react --external react/jsx-runtime --external react-dom"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"next": ">=14.0.0",
|
|
@@ -28,17 +28,21 @@ export default function codesurfaceMountLoader(this: LoaderContext, source: stri
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Add
|
|
32
|
-
//
|
|
33
|
-
const
|
|
31
|
+
// Add imports at the top (after "use client" or first import)
|
|
32
|
+
// The registry import is a side-effect — it self-registers on window.
|
|
33
|
+
const importStatements = [
|
|
34
|
+
`import { CodeSurface } from "@designtools/next-plugin/codesurface";`,
|
|
35
|
+
`import "./designtools-registry";`,
|
|
36
|
+
].join("\n") + "\n";
|
|
37
|
+
|
|
34
38
|
let modified = source;
|
|
35
39
|
|
|
36
|
-
// Find a good insertion point for the
|
|
40
|
+
// Find a good insertion point for the imports
|
|
37
41
|
const firstImportIndex = source.indexOf("import ");
|
|
38
42
|
if (firstImportIndex !== -1) {
|
|
39
|
-
modified = source.slice(0, firstImportIndex) +
|
|
43
|
+
modified = source.slice(0, firstImportIndex) + importStatements + source.slice(firstImportIndex);
|
|
40
44
|
} else {
|
|
41
|
-
modified =
|
|
45
|
+
modified = importStatements + source;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
// Add <CodeSurface /> just before {children}
|