@designtools/next-plugin 0.1.2 → 0.1.3
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/dist/codesurface.js +357 -0
- package/dist/codesurface.mjs +357 -0
- package/dist/index.js +197 -4
- package/dist/index.mjs +197 -4
- package/package.json +1 -1
- package/src/codesurface.tsx +476 -0
- package/src/index.ts +11 -0
- package/src/preview-route.ts +231 -0
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,195 @@
|
|
|
1
1
|
import "./chunk-Y6FXYEAI.mjs";
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import path2 from "path";
|
|
5
|
+
|
|
6
|
+
// src/preview-route.ts
|
|
7
|
+
import fs from "fs";
|
|
4
8
|
import path from "path";
|
|
9
|
+
var PREVIEW_DIR = "designtools-preview";
|
|
10
|
+
function generatePreviewRoute(appDir) {
|
|
11
|
+
const projectRoot = path.dirname(appDir);
|
|
12
|
+
const previewDir = path.join(appDir, PREVIEW_DIR);
|
|
13
|
+
const componentPaths = discoverComponentFiles(projectRoot);
|
|
14
|
+
fs.mkdirSync(previewDir, { recursive: true });
|
|
15
|
+
fs.writeFileSync(
|
|
16
|
+
path.join(previewDir, "layout.tsx"),
|
|
17
|
+
getLayoutTemplate(),
|
|
18
|
+
"utf-8"
|
|
19
|
+
);
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
path.join(previewDir, "page.tsx"),
|
|
22
|
+
getPageTemplate(componentPaths),
|
|
23
|
+
"utf-8"
|
|
24
|
+
);
|
|
25
|
+
ensureGitignore(projectRoot);
|
|
26
|
+
}
|
|
27
|
+
function ensureGitignore(projectRoot) {
|
|
28
|
+
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
29
|
+
const entry = "app/designtools-preview";
|
|
30
|
+
try {
|
|
31
|
+
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
|
|
32
|
+
if (!existing.includes(entry)) {
|
|
33
|
+
fs.appendFileSync(gitignorePath, `
|
|
34
|
+
# Generated by @designtools/next-plugin
|
|
35
|
+
${entry}
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function discoverComponentFiles(projectRoot) {
|
|
42
|
+
const dirs = ["components/ui", "src/components/ui"];
|
|
43
|
+
for (const dir of dirs) {
|
|
44
|
+
const fullDir = path.join(projectRoot, dir);
|
|
45
|
+
if (fs.existsSync(fullDir)) {
|
|
46
|
+
const files = fs.readdirSync(fullDir);
|
|
47
|
+
return files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts") || f.endsWith(".jsx")).map((f) => `${dir}/${f.replace(/\.(tsx|ts|jsx|js)$/, "")}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
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
|
+
|
|
192
|
+
// src/index.ts
|
|
5
193
|
function withDesigntools(nextConfig = {}) {
|
|
6
194
|
return {
|
|
7
195
|
...nextConfig,
|
|
@@ -12,7 +200,7 @@ function withDesigntools(nextConfig = {}) {
|
|
|
12
200
|
exclude: /node_modules/,
|
|
13
201
|
use: [
|
|
14
202
|
{
|
|
15
|
-
loader:
|
|
203
|
+
loader: path2.resolve(__dirname, "loader.js"),
|
|
16
204
|
options: {
|
|
17
205
|
cwd: context.dir
|
|
18
206
|
}
|
|
@@ -22,15 +210,20 @@ function withDesigntools(nextConfig = {}) {
|
|
|
22
210
|
config.module.rules.push({
|
|
23
211
|
test: /layout\.(tsx|jsx)$/,
|
|
24
212
|
include: [
|
|
25
|
-
|
|
26
|
-
|
|
213
|
+
path2.resolve(context.dir, "app"),
|
|
214
|
+
path2.resolve(context.dir, "src/app")
|
|
27
215
|
],
|
|
28
216
|
use: [
|
|
29
217
|
{
|
|
30
|
-
loader:
|
|
218
|
+
loader: path2.resolve(__dirname, "codesurface-mount-loader.js")
|
|
31
219
|
}
|
|
32
220
|
]
|
|
33
221
|
});
|
|
222
|
+
const appDir = path2.resolve(context.dir, "app");
|
|
223
|
+
try {
|
|
224
|
+
generatePreviewRoute(appDir);
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
34
227
|
}
|
|
35
228
|
if (typeof nextConfig.webpack === "function") {
|
|
36
229
|
return nextConfig.webpack(config, context);
|