@aaqiljamal/visual-editor-next 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/bin/visual-editor-init.cjs +200 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/route.d.ts +17 -0
- package/dist/route.js +251 -0
- package/dist/route.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const cwd = process.cwd();
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const dryRun = args.includes("--dry-run") || args.includes("-n");
|
|
9
|
+
const force = args.includes("--force") || args.includes("-f");
|
|
10
|
+
|
|
11
|
+
const ok = (s) => process.stdout.write(` ✓ ${s}\n`);
|
|
12
|
+
const note = (s) => process.stdout.write(` · ${s}\n`);
|
|
13
|
+
const warn = (s) => process.stdout.write(` ⚠ ${s}\n`);
|
|
14
|
+
const fail = (s) => {
|
|
15
|
+
process.stderr.write(`\n ✗ ${s}\n\n`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const wouldWrite = (label) =>
|
|
20
|
+
process.stdout.write(` ${dryRun ? "·" : "✓"} ${dryRun ? "would write " : ""}${label}\n`);
|
|
21
|
+
|
|
22
|
+
process.stdout.write(
|
|
23
|
+
`\nInitializing visual-editor in ${cwd}${dryRun ? " (dry run)" : ""}\n\n`,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Pre-flight
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
31
|
+
if (!fs.existsSync(pkgPath)) {
|
|
32
|
+
fail(
|
|
33
|
+
"No package.json in current directory. Run this in your Next.js project root.",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let pkg;
|
|
38
|
+
try {
|
|
39
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
40
|
+
} catch (err) {
|
|
41
|
+
fail(`package.json is not valid JSON: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const allDeps = Object.assign(
|
|
45
|
+
{},
|
|
46
|
+
pkg.dependencies || {},
|
|
47
|
+
pkg.devDependencies || {},
|
|
48
|
+
);
|
|
49
|
+
if (!allDeps.next) {
|
|
50
|
+
fail(
|
|
51
|
+
"This project doesn't declare a Next.js dependency. visual-editor only " +
|
|
52
|
+
"supports Next.js App Router projects (see docs for Vite/Remix flow).",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const appDir = path.join(cwd, "app");
|
|
57
|
+
const srcAppDir = path.join(cwd, "src", "app");
|
|
58
|
+
const pagesDir = path.join(cwd, "pages");
|
|
59
|
+
const srcPagesDir = path.join(cwd, "src", "pages");
|
|
60
|
+
|
|
61
|
+
const hasAppRouter = fs.existsSync(appDir) || fs.existsSync(srcAppDir);
|
|
62
|
+
const hasPagesRouter = fs.existsSync(pagesDir) || fs.existsSync(srcPagesDir);
|
|
63
|
+
|
|
64
|
+
if (!hasAppRouter && !force) {
|
|
65
|
+
fail(
|
|
66
|
+
"No `app/` (or `src/app/`) directory found. visual-editor requires the " +
|
|
67
|
+
"App Router. If this is a Pages Router project, visual-editor doesn't " +
|
|
68
|
+
"support it yet. Use --force to proceed anyway.",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (hasPagesRouter && !force) {
|
|
73
|
+
warn(
|
|
74
|
+
"Detected a `pages/` directory alongside `app/`. visual-editor's Route " +
|
|
75
|
+
"Handler will only fire for App Router routes. Pages Router-rendered " +
|
|
76
|
+
"elements will be visible to the overlay but Apply may behave oddly. " +
|
|
77
|
+
"Use --force to suppress this warning.",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Detect whether `app/` lives at root or under src/
|
|
82
|
+
const appRoot = fs.existsSync(srcAppDir) ? srcAppDir : appDir;
|
|
83
|
+
|
|
84
|
+
process.stdout.write("\nProject looks like a Next.js App Router app. Proceeding.\n\n");
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// 1. app/api/visual-editor/[...path]/route.ts
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
const routeDir = path.join(appRoot, "api", "visual-editor", "[...path]");
|
|
92
|
+
const routeFile = path.join(routeDir, "route.ts");
|
|
93
|
+
if (fs.existsSync(routeFile)) {
|
|
94
|
+
note(`route.ts exists at ${path.relative(cwd, routeFile)} — leaving it alone`);
|
|
95
|
+
} else {
|
|
96
|
+
if (!dryRun) {
|
|
97
|
+
fs.mkdirSync(routeDir, { recursive: true });
|
|
98
|
+
fs.writeFileSync(
|
|
99
|
+
routeFile,
|
|
100
|
+
'export { GET, POST, DELETE } from "@aaqiljamal/visual-editor-next/route";\n',
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
wouldWrite(path.relative(cwd, routeFile));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// 2. babel.config.js — guard SWC users
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
{
|
|
112
|
+
const babelFile = path.join(cwd, "babel.config.js");
|
|
113
|
+
if (fs.existsSync(babelFile)) {
|
|
114
|
+
const current = fs.readFileSync(babelFile, "utf8");
|
|
115
|
+
if (current.includes("@aaqiljamal/visual-editor-babel-plugin")) {
|
|
116
|
+
note("babel.config.js already references the plugin");
|
|
117
|
+
} else {
|
|
118
|
+
warn(
|
|
119
|
+
"babel.config.js exists with other config. Add this to its `plugins` array manually:",
|
|
120
|
+
);
|
|
121
|
+
process.stdout.write(' "@aaqiljamal/visual-editor-babel-plugin"\n');
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
warn(
|
|
125
|
+
"Writing babel.config.js — this opts your project out of SWC for builds. " +
|
|
126
|
+
"Dev builds will be slightly slower. Visual-edit needs this to stamp " +
|
|
127
|
+
"data-oid attributes on JSX. (You can revert by deleting this file.)",
|
|
128
|
+
);
|
|
129
|
+
if (!dryRun) {
|
|
130
|
+
fs.writeFileSync(
|
|
131
|
+
babelFile,
|
|
132
|
+
'module.exports = {\n' +
|
|
133
|
+
' presets: ["next/babel"],\n' +
|
|
134
|
+
' plugins: ["@aaqiljamal/visual-editor-babel-plugin"],\n' +
|
|
135
|
+
'};\n',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
wouldWrite("babel.config.js");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// 3. .gitignore
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
const giFile = path.join(cwd, ".gitignore");
|
|
148
|
+
const entry = "/.visual-editor/";
|
|
149
|
+
if (fs.existsSync(giFile)) {
|
|
150
|
+
const current = fs.readFileSync(giFile, "utf8");
|
|
151
|
+
if (current.split("\n").some((l) => l.trim() === entry)) {
|
|
152
|
+
note(".gitignore already has /.visual-editor/");
|
|
153
|
+
} else {
|
|
154
|
+
if (!dryRun) {
|
|
155
|
+
fs.appendFileSync(
|
|
156
|
+
giFile,
|
|
157
|
+
(current.endsWith("\n") ? "" : "\n") +
|
|
158
|
+
"\n# visual-editor local state\n" +
|
|
159
|
+
entry +
|
|
160
|
+
"\n",
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
wouldWrite("added /.visual-editor/ to .gitignore");
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
fs.writeFileSync(giFile, "# visual-editor local state\n" + entry + "\n");
|
|
168
|
+
}
|
|
169
|
+
wouldWrite(".gitignore");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// 4. Manual step
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
const layoutPath = path.join(appRoot, "layout.tsx");
|
|
178
|
+
const layoutRel = fs.existsSync(layoutPath)
|
|
179
|
+
? path.relative(cwd, layoutPath)
|
|
180
|
+
: "app/layout.tsx";
|
|
181
|
+
|
|
182
|
+
process.stdout.write(
|
|
183
|
+
`\nManual step — mount the overlay in ${layoutRel}:\n\n` +
|
|
184
|
+
' import { VisualEditOverlay } from "@aaqiljamal/visual-editor-next";\n\n' +
|
|
185
|
+
" export default function RootLayout({ children }) {\n" +
|
|
186
|
+
" return (\n" +
|
|
187
|
+
' <html>\n' +
|
|
188
|
+
" <body>\n" +
|
|
189
|
+
" {children}\n" +
|
|
190
|
+
' {process.env.NODE_ENV === "development" && <VisualEditOverlay />}\n' +
|
|
191
|
+
" </body>\n" +
|
|
192
|
+
" </html>\n" +
|
|
193
|
+
" );\n" +
|
|
194
|
+
" }\n\n" +
|
|
195
|
+
"Then: npm run dev → open your browser → click any element.\n\n",
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (dryRun) {
|
|
199
|
+
process.stdout.write("(dry run — no files were written)\n\n");
|
|
200
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { VisualEditOverlay } from '@aaqiljamal/visual-editor-runtime';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["// Public surface of @aaqiljamal/visual-editor-next.\n//\n// For users:\n// import { VisualEditOverlay } from \"@aaqiljamal/visual-editor-next\";\n// export { GET, POST, DELETE } from \"@aaqiljamal/visual-editor-next/route\";\n//\n// One package = overlay + route handler + Babel plugin (transitive).\n// The separate `visual-editor-server` CLI is still available via\n// @aaqiljamal/visual-editor-server but isn't needed in this setup.\n\nexport { VisualEditOverlay } from \"@aaqiljamal/visual-editor-runtime\";\n"],"mappings":";;;AAUA,SAAS,yBAAyB;","names":[]}
|
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare function GET(req: Request, context: {
|
|
2
|
+
params: Promise<{
|
|
3
|
+
path?: string[];
|
|
4
|
+
}>;
|
|
5
|
+
}): Promise<Response>;
|
|
6
|
+
declare function POST(req: Request, context: {
|
|
7
|
+
params: Promise<{
|
|
8
|
+
path?: string[];
|
|
9
|
+
}>;
|
|
10
|
+
}): Promise<Response>;
|
|
11
|
+
declare function DELETE(req: Request, context: {
|
|
12
|
+
params: Promise<{
|
|
13
|
+
path?: string[];
|
|
14
|
+
}>;
|
|
15
|
+
}): Promise<Response>;
|
|
16
|
+
|
|
17
|
+
export { DELETE, GET, POST };
|
package/dist/route.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// src/route.ts
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import {
|
|
4
|
+
applyToFile,
|
|
5
|
+
revertToFile,
|
|
6
|
+
applyCssProperty,
|
|
7
|
+
applyStyledProperty,
|
|
8
|
+
RecentApplies,
|
|
9
|
+
CurrentSelection
|
|
10
|
+
} from "@aaqiljamal/visual-editor-server";
|
|
11
|
+
var recentApplies = new RecentApplies();
|
|
12
|
+
var currentSelection = new CurrentSelection();
|
|
13
|
+
var stateLoaded = false;
|
|
14
|
+
async function ensureStateLoaded() {
|
|
15
|
+
const workspaceRoot = process.cwd();
|
|
16
|
+
if (!stateLoaded) {
|
|
17
|
+
await recentApplies.load(
|
|
18
|
+
path.join(workspaceRoot, ".visual-editor", "history.json")
|
|
19
|
+
);
|
|
20
|
+
stateLoaded = true;
|
|
21
|
+
}
|
|
22
|
+
return workspaceRoot;
|
|
23
|
+
}
|
|
24
|
+
function isDev() {
|
|
25
|
+
return process.env.NODE_ENV !== "production";
|
|
26
|
+
}
|
|
27
|
+
function devOnlyOrJson() {
|
|
28
|
+
if (!isDev()) {
|
|
29
|
+
return new Response("Not available in production", { status: 404 });
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function json(status, body) {
|
|
34
|
+
return new Response(JSON.stringify(body), {
|
|
35
|
+
status,
|
|
36
|
+
headers: { "Content-Type": "application/json" }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function endpointFromPath(pathSegments) {
|
|
40
|
+
return "/" + (pathSegments ?? []).join("/");
|
|
41
|
+
}
|
|
42
|
+
async function GET(req, context) {
|
|
43
|
+
const dev = devOnlyOrJson();
|
|
44
|
+
if (dev) return dev;
|
|
45
|
+
const workspaceRoot = await ensureStateLoaded();
|
|
46
|
+
const { path: parts } = await context.params;
|
|
47
|
+
const endpoint = endpointFromPath(parts);
|
|
48
|
+
if (endpoint === "/health") {
|
|
49
|
+
return json(200, { ok: true, mode: "next-route-handler" });
|
|
50
|
+
}
|
|
51
|
+
if (endpoint === "/token") {
|
|
52
|
+
return json(200, { token: null });
|
|
53
|
+
}
|
|
54
|
+
if (endpoint === "/selection") {
|
|
55
|
+
return json(200, { ok: true, selection: currentSelection.get() });
|
|
56
|
+
}
|
|
57
|
+
if (endpoint === "/recent") {
|
|
58
|
+
return json(200, { ok: true, applies: recentApplies.list() });
|
|
59
|
+
}
|
|
60
|
+
if (endpoint === "/assets") {
|
|
61
|
+
return await handleAssets(workspaceRoot);
|
|
62
|
+
}
|
|
63
|
+
return json(404, {
|
|
64
|
+
ok: false,
|
|
65
|
+
reason: "not-found",
|
|
66
|
+
details: `GET ${endpoint}`
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function POST(req, context) {
|
|
70
|
+
const dev = devOnlyOrJson();
|
|
71
|
+
if (dev) return dev;
|
|
72
|
+
const workspaceRoot = await ensureStateLoaded();
|
|
73
|
+
const { path: parts } = await context.params;
|
|
74
|
+
const endpoint = endpointFromPath(parts);
|
|
75
|
+
let body;
|
|
76
|
+
try {
|
|
77
|
+
body = await req.json();
|
|
78
|
+
} catch (err) {
|
|
79
|
+
return json(400, {
|
|
80
|
+
ok: false,
|
|
81
|
+
reason: "invalid-json",
|
|
82
|
+
details: err.message
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (endpoint === "/apply") {
|
|
86
|
+
return await handleMutation(body, workspaceRoot, false);
|
|
87
|
+
}
|
|
88
|
+
if (endpoint === "/propose") {
|
|
89
|
+
return await handleMutation(body, workspaceRoot, true);
|
|
90
|
+
}
|
|
91
|
+
if (endpoint === "/revert") {
|
|
92
|
+
const outcome = await revertToFile(
|
|
93
|
+
body,
|
|
94
|
+
{ workspaceRoot, dryRun: false },
|
|
95
|
+
recentApplies
|
|
96
|
+
);
|
|
97
|
+
if (outcome.ok) return json(200, { ok: true, diff: outcome.diff });
|
|
98
|
+
return json(outcome.status, {
|
|
99
|
+
ok: false,
|
|
100
|
+
reason: outcome.reason,
|
|
101
|
+
details: outcome.details
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (endpoint === "/apply-css-prop") {
|
|
105
|
+
const outcome = await applyCssProperty(body, {
|
|
106
|
+
workspaceRoot,
|
|
107
|
+
dryRun: false
|
|
108
|
+
});
|
|
109
|
+
if (outcome.ok) {
|
|
110
|
+
return json(200, {
|
|
111
|
+
ok: true,
|
|
112
|
+
diff: outcome.diff,
|
|
113
|
+
selector: outcome.selector,
|
|
114
|
+
previousValue: outcome.previousValue
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return json(outcome.status, {
|
|
118
|
+
ok: false,
|
|
119
|
+
reason: outcome.reason,
|
|
120
|
+
details: outcome.details
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (endpoint === "/apply-styled-prop") {
|
|
124
|
+
const outcome = await applyStyledProperty(
|
|
125
|
+
body,
|
|
126
|
+
{ workspaceRoot, dryRun: false }
|
|
127
|
+
);
|
|
128
|
+
if (outcome.ok) {
|
|
129
|
+
return json(200, {
|
|
130
|
+
ok: true,
|
|
131
|
+
diff: outcome.diff,
|
|
132
|
+
componentName: outcome.componentName,
|
|
133
|
+
previousValue: outcome.previousValue
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return json(outcome.status, {
|
|
137
|
+
ok: false,
|
|
138
|
+
reason: outcome.reason,
|
|
139
|
+
details: outcome.details
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (endpoint === "/selection") {
|
|
143
|
+
if (!isValidSelection(body)) {
|
|
144
|
+
return json(400, {
|
|
145
|
+
ok: false,
|
|
146
|
+
reason: "invalid-input",
|
|
147
|
+
details: "Body must be { file, line, col, oid, className, tagName, componentName, instanceCount }"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
currentSelection.set(body);
|
|
151
|
+
return json(200, { ok: true });
|
|
152
|
+
}
|
|
153
|
+
return json(404, {
|
|
154
|
+
ok: false,
|
|
155
|
+
reason: "not-found",
|
|
156
|
+
details: `POST ${endpoint}`
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async function DELETE(req, context) {
|
|
160
|
+
const dev = devOnlyOrJson();
|
|
161
|
+
if (dev) return dev;
|
|
162
|
+
await ensureStateLoaded();
|
|
163
|
+
const { path: parts } = await context.params;
|
|
164
|
+
const endpoint = endpointFromPath(parts);
|
|
165
|
+
if (endpoint === "/selection") {
|
|
166
|
+
currentSelection.clear();
|
|
167
|
+
return json(200, { ok: true });
|
|
168
|
+
}
|
|
169
|
+
return json(404, {
|
|
170
|
+
ok: false,
|
|
171
|
+
reason: "not-found",
|
|
172
|
+
details: `DELETE ${endpoint}`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function handleMutation(input, workspaceRoot, dryRun) {
|
|
176
|
+
const outcome = await applyToFile(input, { workspaceRoot, dryRun });
|
|
177
|
+
if (outcome.ok) {
|
|
178
|
+
if (!dryRun) {
|
|
179
|
+
const beforeForBuffer = outcome.previousValue ?? input.before ?? "";
|
|
180
|
+
recentApplies.push({
|
|
181
|
+
file: input.file,
|
|
182
|
+
line: input.line,
|
|
183
|
+
col: input.col,
|
|
184
|
+
before: beforeForBuffer,
|
|
185
|
+
after: input.after,
|
|
186
|
+
appliedAt: Date.now()
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return json(200, { ok: true, diff: outcome.diff });
|
|
190
|
+
}
|
|
191
|
+
return json(outcome.status, {
|
|
192
|
+
ok: false,
|
|
193
|
+
reason: outcome.reason,
|
|
194
|
+
details: outcome.details
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function handleAssets(workspaceRoot) {
|
|
198
|
+
const fs = await import("fs/promises");
|
|
199
|
+
const IMAGE_EXTS = /* @__PURE__ */ new Set([
|
|
200
|
+
".png",
|
|
201
|
+
".jpg",
|
|
202
|
+
".jpeg",
|
|
203
|
+
".gif",
|
|
204
|
+
".svg",
|
|
205
|
+
".webp",
|
|
206
|
+
".avif"
|
|
207
|
+
]);
|
|
208
|
+
const publicDir = path.join(workspaceRoot, "public");
|
|
209
|
+
async function walk(dir, rel) {
|
|
210
|
+
let entries = [];
|
|
211
|
+
try {
|
|
212
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
213
|
+
} catch {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
const out = [];
|
|
217
|
+
for (const e of entries) {
|
|
218
|
+
const child = path.join(dir, e.name);
|
|
219
|
+
const childRel = rel ? `${rel}/${e.name}` : e.name;
|
|
220
|
+
if (e.isDirectory()) {
|
|
221
|
+
const sub = await walk(child, childRel);
|
|
222
|
+
out.push(...sub);
|
|
223
|
+
} else if (e.isFile() && IMAGE_EXTS.has(path.extname(e.name).toLowerCase())) {
|
|
224
|
+
out.push(`/${childRel}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const assets = await walk(publicDir, "");
|
|
231
|
+
assets.sort();
|
|
232
|
+
return json(200, { ok: true, assets });
|
|
233
|
+
} catch (err) {
|
|
234
|
+
return json(500, {
|
|
235
|
+
ok: false,
|
|
236
|
+
reason: "assets-list-failed",
|
|
237
|
+
details: err.message
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function isValidSelection(x) {
|
|
242
|
+
if (!x || typeof x !== "object") return false;
|
|
243
|
+
const o = x;
|
|
244
|
+
return typeof o.file === "string" && typeof o.line === "number" && typeof o.col === "number" && typeof o.oid === "string" && typeof o.className === "string" && typeof o.tagName === "string" && (o.componentName === null || typeof o.componentName === "string") && typeof o.instanceCount === "number";
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
DELETE,
|
|
248
|
+
GET,
|
|
249
|
+
POST
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/route.ts"],"sourcesContent":["/**\n * Web Request handler for visual-editor, mounted as a Next.js catchall\n * Route Handler. Users add ONE file:\n *\n * app/api/visual-editor/[...path]/route.ts\n * ──────────────────────────────────────\n * export { GET, POST, DELETE } from \"@aaqiljamal/visual-editor-next/route\";\n *\n * The AST mutation logic runs in-process with the user's Next dev server.\n * No separate port, no separate process, no CORS dance, no bearer token.\n *\n * In production we 404 every request: visual-editor is dev-only by design.\n */\nimport * as path from \"node:path\";\nimport {\n applyToFile,\n type ApplyInput,\n revertToFile,\n type RevertInput,\n applyCssProperty,\n type ApplyCssPropertyInput,\n applyStyledProperty,\n type ApplyStyledPropertyInput,\n RecentApplies,\n CurrentSelection,\n type Selection,\n} from \"@aaqiljamal/visual-editor-server\";\n\n// Module-level state. Survives across Route Handler invocations within\n// the same Next dev server process. RecentApplies persists to disk, so\n// HMR/full restarts don't lose history.\nconst recentApplies = new RecentApplies();\nconst currentSelection = new CurrentSelection();\nlet stateLoaded = false;\n\nasync function ensureStateLoaded(): Promise<string> {\n const workspaceRoot = process.cwd();\n if (!stateLoaded) {\n await recentApplies.load(\n path.join(workspaceRoot, \".visual-editor\", \"history.json\"),\n );\n stateLoaded = true;\n }\n return workspaceRoot;\n}\n\nfunction isDev(): boolean {\n return process.env.NODE_ENV !== \"production\";\n}\n\nfunction devOnlyOrJson(): Response | null {\n if (!isDev()) {\n return new Response(\"Not available in production\", { status: 404 });\n }\n return null;\n}\n\nfunction json(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction endpointFromPath(pathSegments: string[] | undefined): string {\n return \"/\" + (pathSegments ?? []).join(\"/\");\n}\n\n// ---------------------------------------------------------------------------\n// GET\n// ---------------------------------------------------------------------------\n\nexport async function GET(\n req: Request,\n context: { params: Promise<{ path?: string[] }> },\n): Promise<Response> {\n const dev = devOnlyOrJson();\n if (dev) return dev;\n const workspaceRoot = await ensureStateLoaded();\n const { path: parts } = await context.params;\n const endpoint = endpointFromPath(parts);\n\n if (endpoint === \"/health\") {\n return json(200, { ok: true, mode: \"next-route-handler\" });\n }\n if (endpoint === \"/token\") {\n // Route Handler is same-origin — no bearer token needed. We respond\n // 200 with null so the overlay's bootstrap doesn't log a 404.\n return json(200, { token: null });\n }\n if (endpoint === \"/selection\") {\n return json(200, { ok: true, selection: currentSelection.get() });\n }\n if (endpoint === \"/recent\") {\n return json(200, { ok: true, applies: recentApplies.list() });\n }\n if (endpoint === \"/assets\") {\n return await handleAssets(workspaceRoot);\n }\n return json(404, {\n ok: false,\n reason: \"not-found\",\n details: `GET ${endpoint}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// POST\n// ---------------------------------------------------------------------------\n\nexport async function POST(\n req: Request,\n context: { params: Promise<{ path?: string[] }> },\n): Promise<Response> {\n const dev = devOnlyOrJson();\n if (dev) return dev;\n const workspaceRoot = await ensureStateLoaded();\n const { path: parts } = await context.params;\n const endpoint = endpointFromPath(parts);\n\n let body: unknown;\n try {\n body = await req.json();\n } catch (err) {\n return json(400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n }\n\n if (endpoint === \"/apply\") {\n return await handleMutation(body as ApplyInput, workspaceRoot, false);\n }\n if (endpoint === \"/propose\") {\n return await handleMutation(body as ApplyInput, workspaceRoot, true);\n }\n if (endpoint === \"/revert\") {\n const outcome = await revertToFile(\n body as RevertInput,\n { workspaceRoot, dryRun: false },\n recentApplies,\n );\n if (outcome.ok) return json(200, { ok: true, diff: outcome.diff });\n return json(outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n }\n if (endpoint === \"/apply-css-prop\") {\n const outcome = await applyCssProperty(body as ApplyCssPropertyInput, {\n workspaceRoot,\n dryRun: false,\n });\n if (outcome.ok) {\n return json(200, {\n ok: true,\n diff: outcome.diff,\n selector: outcome.selector,\n previousValue: outcome.previousValue,\n });\n }\n return json(outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n }\n if (endpoint === \"/apply-styled-prop\") {\n const outcome = await applyStyledProperty(\n body as ApplyStyledPropertyInput,\n { workspaceRoot, dryRun: false },\n );\n if (outcome.ok) {\n return json(200, {\n ok: true,\n diff: outcome.diff,\n componentName: outcome.componentName,\n previousValue: outcome.previousValue,\n });\n }\n return json(outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n }\n if (endpoint === \"/selection\") {\n if (!isValidSelection(body)) {\n return json(400, {\n ok: false,\n reason: \"invalid-input\",\n details:\n \"Body must be { file, line, col, oid, className, tagName, componentName, instanceCount }\",\n });\n }\n currentSelection.set(body);\n return json(200, { ok: true });\n }\n\n return json(404, {\n ok: false,\n reason: \"not-found\",\n details: `POST ${endpoint}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// DELETE\n// ---------------------------------------------------------------------------\n\nexport async function DELETE(\n req: Request,\n context: { params: Promise<{ path?: string[] }> },\n): Promise<Response> {\n const dev = devOnlyOrJson();\n if (dev) return dev;\n await ensureStateLoaded();\n const { path: parts } = await context.params;\n const endpoint = endpointFromPath(parts);\n\n if (endpoint === \"/selection\") {\n currentSelection.clear();\n return json(200, { ok: true });\n }\n return json(404, {\n ok: false,\n reason: \"not-found\",\n details: `DELETE ${endpoint}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function handleMutation(\n input: ApplyInput,\n workspaceRoot: string,\n dryRun: boolean,\n): Promise<Response> {\n const outcome = await applyToFile(input, { workspaceRoot, dryRun });\n if (outcome.ok) {\n if (!dryRun) {\n const beforeForBuffer =\n outcome.previousValue ?? input.before ?? \"\";\n recentApplies.push({\n file: input.file,\n line: input.line,\n col: input.col,\n before: beforeForBuffer,\n after: input.after,\n appliedAt: Date.now(),\n });\n }\n return json(200, { ok: true, diff: outcome.diff });\n }\n return json(outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n}\n\nasync function handleAssets(workspaceRoot: string): Promise<Response> {\n const fs = await import(\"node:fs/promises\");\n const IMAGE_EXTS = new Set([\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".webp\",\n \".avif\",\n ]);\n const publicDir = path.join(workspaceRoot, \"public\");\n\n async function walk(dir: string, rel: string): Promise<string[]> {\n let entries: import(\"node:fs\").Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const e of entries) {\n const child = path.join(dir, e.name);\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n if (e.isDirectory()) {\n const sub = await walk(child, childRel);\n out.push(...sub);\n } else if (\n e.isFile() &&\n IMAGE_EXTS.has(path.extname(e.name).toLowerCase())\n ) {\n out.push(`/${childRel}`);\n }\n }\n return out;\n }\n\n try {\n const assets = await walk(publicDir, \"\");\n assets.sort();\n return json(200, { ok: true, assets });\n } catch (err) {\n return json(500, {\n ok: false,\n reason: \"assets-list-failed\",\n details: (err as Error).message,\n });\n }\n}\n\nfunction isValidSelection(x: unknown): x is Selection {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n typeof o.oid === \"string\" &&\n typeof o.className === \"string\" &&\n typeof o.tagName === \"string\" &&\n (o.componentName === null || typeof o.componentName === \"string\") &&\n typeof o.instanceCount === \"number\"\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aaqiljamal/visual-editor-next",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Visual-edit for Next.js — one install. Includes the overlay, the Babel plugin, and a Route Handler that does AST writes inside your dev process (no separate server).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./route": {
|
|
15
|
+
"types": "./dist/route.d.ts",
|
|
16
|
+
"import": "./dist/route.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"visual-editor-init": "bin/visual-editor-init.cjs"
|
|
21
|
+
},
|
|
22
|
+
"files": ["dist", "bin", "README.md"],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": ["visual-editor", "next", "tailwind", "css-modules", "styled-components"],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"author": "Aaqil Jamal",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/The-Design-Alchemist/visual-editor.git",
|
|
35
|
+
"directory": "packages/next"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/The-Design-Alchemist/visual-editor#readme",
|
|
38
|
+
"bugs": "https://github.com/The-Design-Alchemist/visual-editor/issues",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"next": ">=15.0.0",
|
|
47
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@aaqiljamal/visual-editor-babel-plugin": "0.2.0",
|
|
51
|
+
"@aaqiljamal/visual-editor-runtime": "0.2.0",
|
|
52
|
+
"@aaqiljamal/visual-editor-server": "0.2.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.10.0",
|
|
56
|
+
"next": "^16.0.0",
|
|
57
|
+
"react": "^19.0.0",
|
|
58
|
+
"tsup": "^8.3.5",
|
|
59
|
+
"typescript": "^5.6.3"
|
|
60
|
+
}
|
|
61
|
+
}
|