apostil 0.1.6 → 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 +8 -9
- package/dist/cli/index.cjs +252 -72
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +252 -72
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +38 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +38 -33
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Apostil
|
|
2
2
|
|
|
3
|
-
Figma-like commenting tool for React
|
|
3
|
+
Figma-like commenting tool for React. Leave comments directly on the Web App UI, and never miss a feedback. Works with Next.js and Vite.
|
|
4
4
|
|
|
5
5
|
## What can it do?
|
|
6
6
|
|
|
7
7
|
- **Smart target detection** — auto-anchors to nearest meaningful element
|
|
8
8
|
- **Project level view** — see every comment across your project in one sidebar, like Figma.
|
|
9
|
-
- **SSR-safe** — works with Next.js App Router
|
|
9
|
+
- **SSR-safe** — works with Next.js App Router and Vite + React
|
|
10
10
|
- **Ships its own CSS** — no Tailwind config needed in your project
|
|
11
11
|
|
|
12
12
|
## Quick Start Guide
|
|
@@ -29,11 +29,11 @@ npx apostil init --dev # dev + staging environments
|
|
|
29
29
|
npx apostil init --public # all environments including production
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
The CLI auto-detects your framework (Next.js or Vite) and sets up accordingly:
|
|
33
|
+
|
|
34
|
+
**Next.js** — creates API route for file-based storage, wrapper component, injects into root layout.
|
|
35
|
+
|
|
36
|
+
**Vite + React** — creates wrapper component with localStorage adapter, injects into `App.tsx` or `main.tsx`. Uses `react-router-dom` for page detection if available.
|
|
37
37
|
|
|
38
38
|
**3. Start your project and start commenting**
|
|
39
39
|
|
|
@@ -186,8 +186,7 @@ __apostil_debug.disable()
|
|
|
186
186
|
## Requirements
|
|
187
187
|
|
|
188
188
|
- React 18+
|
|
189
|
-
- Next.js (App Router)
|
|
190
|
-
- Tailwind CSS
|
|
189
|
+
- Next.js (App Router) or Vite + React
|
|
191
190
|
|
|
192
191
|
## License
|
|
193
192
|
|
package/dist/cli/index.cjs
CHANGED
|
@@ -47,10 +47,10 @@ function parseMode(flags) {
|
|
|
47
47
|
}
|
|
48
48
|
function printHelp() {
|
|
49
49
|
console.log(`
|
|
50
|
-
apostil \u2014
|
|
50
|
+
apostil \u2014 Lightweight, Figma-like commenting for React
|
|
51
51
|
|
|
52
52
|
Usage:
|
|
53
|
-
npx apostil init [mode] Set up apostil in your
|
|
53
|
+
npx apostil init [mode] Set up apostil in your project
|
|
54
54
|
npx apostil remove Remove apostil from your project
|
|
55
55
|
npx apostil help Show this help
|
|
56
56
|
|
|
@@ -58,20 +58,54 @@ function printHelp() {
|
|
|
58
58
|
(default) Personal \u2014 local dev only, comments gitignored
|
|
59
59
|
--dev Dev + staging \u2014 comments gitignored, env-controlled
|
|
60
60
|
--public All environments \u2014 comments committed to git
|
|
61
|
+
|
|
62
|
+
Supported frameworks: Next.js (App Router), Vite + React
|
|
63
|
+
Framework is auto-detected from your project.
|
|
61
64
|
`);
|
|
62
65
|
}
|
|
66
|
+
async function detectFramework(cwd) {
|
|
67
|
+
for (const name of ["next.config.js", "next.config.ts", "next.config.mjs"]) {
|
|
68
|
+
if (await fileExists(import_path.default.join(cwd, name))) return "nextjs";
|
|
69
|
+
}
|
|
70
|
+
for (const name of ["vite.config.js", "vite.config.ts", "vite.config.mjs"]) {
|
|
71
|
+
if (await fileExists(import_path.default.join(cwd, name))) return "vite";
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const pkg = JSON.parse(await import_promises.default.readFile(import_path.default.join(cwd, "package.json"), "utf-8"));
|
|
75
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
76
|
+
if (deps["next"]) return "nextjs";
|
|
77
|
+
if (deps["vite"]) return "vite";
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
63
82
|
async function init(mode) {
|
|
64
83
|
const cwd = process.cwd();
|
|
84
|
+
const framework = await detectFramework(cwd);
|
|
85
|
+
if (!framework) {
|
|
86
|
+
console.log(" Could not detect your framework.");
|
|
87
|
+
console.log(" Apostil supports Next.js (App Router) and Vite + React.");
|
|
88
|
+
console.log(" Make sure you're running this from your project root.\n");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
console.log(`
|
|
92
|
+
Detected: ${framework === "nextjs" ? "Next.js" : "Vite + React"}`);
|
|
93
|
+
if (framework === "nextjs") {
|
|
94
|
+
await initNextjs(cwd, mode);
|
|
95
|
+
} else {
|
|
96
|
+
await initVite(cwd, mode);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function initNextjs(cwd, mode) {
|
|
65
100
|
const appDir = await findAppDir(cwd);
|
|
66
101
|
if (!appDir) {
|
|
67
102
|
console.log(" Could not find a Next.js app/ directory.");
|
|
68
|
-
console.log(" Make sure you're
|
|
103
|
+
console.log(" Make sure you're using the App Router.\n");
|
|
69
104
|
process.exit(1);
|
|
70
105
|
}
|
|
71
106
|
const useSrc = appDir.includes("src/app");
|
|
72
107
|
const modeLabel = mode === "personal" ? "personal" : mode === "dev" ? "dev" : "public";
|
|
73
|
-
console.log(`
|
|
74
|
-
Setting up apostil (${modeLabel} mode)...
|
|
108
|
+
console.log(` Setting up apostil (${modeLabel} mode)...
|
|
75
109
|
`);
|
|
76
110
|
const apiDir = import_path.default.join(appDir, "api", "apostil");
|
|
77
111
|
const apiFile = import_path.default.join(apiDir, "route.ts");
|
|
@@ -90,80 +124,37 @@ async function init(mode) {
|
|
|
90
124
|
const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
|
|
91
125
|
const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
|
|
92
126
|
await import_promises.default.mkdir(componentsDir, { recursive: true });
|
|
93
|
-
await import_promises.default.writeFile(wrapperFile,
|
|
127
|
+
await import_promises.default.writeFile(wrapperFile, getNextjsWrapper(mode), "utf-8");
|
|
94
128
|
console.log(` \u2713 Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);
|
|
95
129
|
const commentsDir = import_path.default.join(cwd, ".apostil");
|
|
96
130
|
await import_promises.default.mkdir(commentsDir, { recursive: true });
|
|
97
131
|
console.log(" \u2713 Created .apostil/ directory");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
if (mode === "public") {
|
|
105
|
-
if (gitignore.includes(".apostil")) {
|
|
106
|
-
console.log(" \u2713 .gitignore unchanged (public mode \u2014 comments will be committed)");
|
|
107
|
-
} else {
|
|
108
|
-
console.log(" \u2713 Comments will be committed to git (public mode)");
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
if (!gitignore.includes(".apostil")) {
|
|
112
|
-
const entry = "\n# Apostil comments\n.apostil/\n";
|
|
113
|
-
await import_promises.default.appendFile(gitignorePath, entry, "utf-8");
|
|
114
|
-
console.log(" \u2713 Added .apostil/ to .gitignore");
|
|
115
|
-
} else {
|
|
116
|
-
console.log(" \u2713 .gitignore already configured");
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const layoutInjected = await injectIntoLayout(appDir, useSrc);
|
|
132
|
+
await handleGitignore(cwd, mode);
|
|
133
|
+
const layoutInjected = await injectIntoNextjsLayout(appDir, useSrc);
|
|
120
134
|
if (layoutInjected) {
|
|
121
135
|
console.log(" \u2713 Added <ApostilWrapper> to root layout");
|
|
122
136
|
}
|
|
123
137
|
console.log("\n Done! Run your dev server and press C to start commenting.\n");
|
|
124
138
|
}
|
|
125
|
-
async function
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (appDir) {
|
|
131
|
-
const apiDir = import_path.default.join(appDir, "api", "apostil");
|
|
132
|
-
if (await fileExists(import_path.default.join(apiDir, "route.ts"))) {
|
|
133
|
-
await import_promises.default.rm(apiDir, { recursive: true });
|
|
134
|
-
console.log(" \u2713 Removed API route");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
139
|
+
async function initVite(cwd, mode) {
|
|
140
|
+
const useSrc = await fileExists(import_path.default.join(cwd, "src"));
|
|
141
|
+
const modeLabel = mode === "personal" ? "personal" : mode === "dev" ? "dev" : "public";
|
|
142
|
+
console.log(` Setting up apostil (${modeLabel} mode)...
|
|
143
|
+
`);
|
|
137
144
|
const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
|
|
138
145
|
const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const commentsDir = import_path.default.join(cwd, ".apostil");
|
|
150
|
-
if (await fileExists(commentsDir)) {
|
|
151
|
-
await import_promises.default.rm(commentsDir, { recursive: true });
|
|
152
|
-
console.log(" \u2713 Removed .apostil/ directory");
|
|
153
|
-
}
|
|
154
|
-
const gitignorePath = import_path.default.join(cwd, ".gitignore");
|
|
155
|
-
try {
|
|
156
|
-
let gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
|
|
157
|
-
gitignore = gitignore.replace(/\n?# Apostil comments\n\.apostil\/\n?/g, "");
|
|
158
|
-
await import_promises.default.writeFile(gitignorePath, gitignore, "utf-8");
|
|
159
|
-
console.log(" \u2713 Cleaned .gitignore");
|
|
160
|
-
} catch {
|
|
146
|
+
await import_promises.default.mkdir(componentsDir, { recursive: true });
|
|
147
|
+
const hasRouter = await hasReactRouter(cwd);
|
|
148
|
+
await import_promises.default.writeFile(wrapperFile, getViteWrapper(mode, hasRouter), "utf-8");
|
|
149
|
+
console.log(` \u2713 Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);
|
|
150
|
+
console.log(" \u2713 Using localStorage adapter (comments stored in browser)");
|
|
151
|
+
const entryInjected = await injectIntoViteEntry(cwd, useSrc);
|
|
152
|
+
if (entryInjected) {
|
|
153
|
+
console.log(" \u2713 Added <ApostilWrapper> to app entry");
|
|
161
154
|
}
|
|
162
|
-
console.log(
|
|
163
|
-
Done! Now run: npm uninstall apostil
|
|
164
|
-
`);
|
|
155
|
+
console.log("\n Done! Run your dev server and press C to start commenting.\n");
|
|
165
156
|
}
|
|
166
|
-
function
|
|
157
|
+
function getNextjsWrapper(mode) {
|
|
167
158
|
const envGuard = mode === "personal" ? `
|
|
168
159
|
// Personal mode \u2014 only active in local development
|
|
169
160
|
if (process.env.NODE_ENV !== "development") {
|
|
@@ -209,7 +200,86 @@ export function ApostilWrapper({ children }: { children: React.ReactNode }) {${e
|
|
|
209
200
|
}
|
|
210
201
|
`;
|
|
211
202
|
}
|
|
212
|
-
async function
|
|
203
|
+
async function hasReactRouter(cwd) {
|
|
204
|
+
try {
|
|
205
|
+
const pkg = JSON.parse(await import_promises.default.readFile(import_path.default.join(cwd, "package.json"), "utf-8"));
|
|
206
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
207
|
+
return !!deps["react-router-dom"] || !!deps["react-router"];
|
|
208
|
+
} catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function getViteWrapper(mode, hasRouter) {
|
|
213
|
+
const envGuard = mode === "personal" ? `
|
|
214
|
+
// Personal mode \u2014 only active in local development
|
|
215
|
+
if (import.meta.env.PROD) {
|
|
216
|
+
return <>{children}</>;
|
|
217
|
+
}
|
|
218
|
+
` : mode === "dev" ? `
|
|
219
|
+
// Dev mode \u2014 active in dev + staging, disabled in production
|
|
220
|
+
// Set VITE_APOSTIL=true to force on in any environment
|
|
221
|
+
const forceOn = import.meta.env.VITE_APOSTIL === "true";
|
|
222
|
+
if (import.meta.env.PROD && !forceOn) {
|
|
223
|
+
return <>{children}</>;
|
|
224
|
+
}
|
|
225
|
+
` : `
|
|
226
|
+
// Public mode \u2014 active in all environments
|
|
227
|
+
// Set VITE_APOSTIL=false to disable
|
|
228
|
+
if (import.meta.env.VITE_APOSTIL === "false") {
|
|
229
|
+
return <>{children}</>;
|
|
230
|
+
}
|
|
231
|
+
`;
|
|
232
|
+
const routerImport = hasRouter ? `import { useLocation } from "react-router-dom";
|
|
233
|
+
` : "";
|
|
234
|
+
const pageIdLogic = hasRouter ? ` const location = useLocation();
|
|
235
|
+
const pageId = location.pathname.replace(/\\//g, "--").replace(/^--/, "") || "home";` : ` const pageId = window.location.pathname.replace(/\\//g, "--").replace(/^--/, "") || "home";`;
|
|
236
|
+
return `${routerImport}import {
|
|
237
|
+
ApostilProvider,
|
|
238
|
+
CommentOverlay,
|
|
239
|
+
CommentToggle,
|
|
240
|
+
CommentSidebar,
|
|
241
|
+
} from "apostil";
|
|
242
|
+
import { localStorageAdapter } from "apostil/adapters/localStorage";
|
|
243
|
+
import "apostil/styles.css";
|
|
244
|
+
|
|
245
|
+
export function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}
|
|
246
|
+
${pageIdLogic}
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<ApostilProvider pageId={pageId} storage={localStorageAdapter}>
|
|
250
|
+
{children}
|
|
251
|
+
<CommentOverlay />
|
|
252
|
+
<CommentSidebar />
|
|
253
|
+
<CommentToggle />
|
|
254
|
+
</ApostilProvider>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
}
|
|
259
|
+
async function handleGitignore(cwd, mode) {
|
|
260
|
+
const gitignorePath = import_path.default.join(cwd, ".gitignore");
|
|
261
|
+
let gitignore = "";
|
|
262
|
+
try {
|
|
263
|
+
gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
|
|
264
|
+
} catch {
|
|
265
|
+
}
|
|
266
|
+
if (mode === "public") {
|
|
267
|
+
if (gitignore.includes(".apostil")) {
|
|
268
|
+
console.log(" \u2713 .gitignore unchanged (public mode \u2014 comments will be committed)");
|
|
269
|
+
} else {
|
|
270
|
+
console.log(" \u2713 Comments will be committed to git (public mode)");
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
if (!gitignore.includes(".apostil")) {
|
|
274
|
+
const entry = "\n# Apostil comments\n.apostil/\n";
|
|
275
|
+
await import_promises.default.appendFile(gitignorePath, entry, "utf-8");
|
|
276
|
+
console.log(" \u2713 Added .apostil/ to .gitignore");
|
|
277
|
+
} else {
|
|
278
|
+
console.log(" \u2713 .gitignore already configured");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function injectIntoNextjsLayout(appDir, useSrc) {
|
|
213
283
|
const layoutPath = await findLayout(appDir);
|
|
214
284
|
if (!layoutPath) return false;
|
|
215
285
|
let content = await import_promises.default.readFile(layoutPath, "utf-8");
|
|
@@ -252,14 +322,124 @@ async function injectIntoLayout(appDir, useSrc) {
|
|
|
252
322
|
await import_promises.default.writeFile(layoutPath, content, "utf-8");
|
|
253
323
|
return true;
|
|
254
324
|
}
|
|
255
|
-
async function
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
325
|
+
async function injectIntoViteEntry(cwd, useSrc) {
|
|
326
|
+
const baseDir = useSrc ? import_path.default.join(cwd, "src") : cwd;
|
|
327
|
+
for (const ext of ["tsx", "jsx"]) {
|
|
328
|
+
const mainFile = import_path.default.join(baseDir, `main.${ext}`);
|
|
329
|
+
if (await fileExists(mainFile)) {
|
|
330
|
+
return await injectIntoViteMain(mainFile);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
console.log(" \u26A0 Could not find main.tsx or main.jsx \u2014 add <ApostilWrapper> manually");
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
async function injectIntoViteMain(mainFile, wrapperFile) {
|
|
337
|
+
let content = await import_promises.default.readFile(mainFile, "utf-8");
|
|
338
|
+
if (content.includes("ApostilWrapper")) {
|
|
339
|
+
console.log(" \u2713 Entry already has <ApostilWrapper>");
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
const mainDir = import_path.default.dirname(mainFile);
|
|
343
|
+
const wrapperPath = wrapperFile ?? import_path.default.join(mainDir, "components", "apostil-wrapper.tsx");
|
|
344
|
+
let relativePath = import_path.default.relative(mainDir, wrapperPath).replace(/\.tsx$/, "");
|
|
345
|
+
if (!relativePath.startsWith(".")) relativePath = "./" + relativePath;
|
|
346
|
+
const importLine = `import { ApostilWrapper } from "${relativePath}";
|
|
347
|
+
`;
|
|
348
|
+
const importRegex = /^import\s.+$/gm;
|
|
349
|
+
let lastImportIndex = 0;
|
|
350
|
+
let match;
|
|
351
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
352
|
+
lastImportIndex = match.index + match[0].length;
|
|
353
|
+
}
|
|
354
|
+
if (lastImportIndex > 0) {
|
|
355
|
+
content = content.slice(0, lastImportIndex) + "\n" + importLine + content.slice(lastImportIndex);
|
|
356
|
+
} else {
|
|
357
|
+
content = importLine + content;
|
|
358
|
+
}
|
|
359
|
+
content = content.replace(/<App\s*\/>/, `<ApostilWrapper><App /></ApostilWrapper>`);
|
|
360
|
+
await import_promises.default.writeFile(mainFile, content, "utf-8");
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
async function remove() {
|
|
364
|
+
const cwd = process.cwd();
|
|
365
|
+
const framework = await detectFramework(cwd);
|
|
366
|
+
console.log("\n Removing apostil...\n");
|
|
367
|
+
if (framework === "nextjs") {
|
|
368
|
+
await removeNextjs(cwd);
|
|
369
|
+
} else if (framework === "vite") {
|
|
370
|
+
await removeVite(cwd);
|
|
371
|
+
} else {
|
|
372
|
+
await removeNextjs(cwd);
|
|
373
|
+
await removeVite(cwd);
|
|
374
|
+
}
|
|
375
|
+
const commentsDir = import_path.default.join(cwd, ".apostil");
|
|
376
|
+
if (await fileExists(commentsDir)) {
|
|
377
|
+
await import_promises.default.rm(commentsDir, { recursive: true });
|
|
378
|
+
console.log(" \u2713 Removed .apostil/ directory");
|
|
379
|
+
}
|
|
380
|
+
const gitignorePath = import_path.default.join(cwd, ".gitignore");
|
|
381
|
+
try {
|
|
382
|
+
let gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
|
|
383
|
+
gitignore = gitignore.replace(/\n?# Apostil comments\n\.apostil\/\n?/g, "");
|
|
384
|
+
await import_promises.default.writeFile(gitignorePath, gitignore, "utf-8");
|
|
385
|
+
console.log(" \u2713 Cleaned .gitignore");
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
console.log(`
|
|
389
|
+
Done! Now run: npm uninstall apostil
|
|
390
|
+
`);
|
|
391
|
+
}
|
|
392
|
+
async function removeNextjs(cwd) {
|
|
393
|
+
const appDir = await findAppDir(cwd);
|
|
394
|
+
const useSrc = appDir?.includes("src/app") ?? false;
|
|
395
|
+
if (appDir) {
|
|
396
|
+
const apiDir = import_path.default.join(appDir, "api", "apostil");
|
|
397
|
+
if (await fileExists(import_path.default.join(apiDir, "route.ts"))) {
|
|
398
|
+
await import_promises.default.rm(apiDir, { recursive: true });
|
|
399
|
+
console.log(" \u2713 Removed API route");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
|
|
403
|
+
const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
|
|
404
|
+
if (await fileExists(wrapperFile)) {
|
|
405
|
+
await import_promises.default.rm(wrapperFile);
|
|
406
|
+
console.log(" \u2713 Removed wrapper component");
|
|
407
|
+
}
|
|
408
|
+
if (appDir) {
|
|
409
|
+
const layoutPath = await findLayout(appDir);
|
|
410
|
+
if (layoutPath) {
|
|
411
|
+
const unwrapped = await removeWrapperFromFile(layoutPath);
|
|
412
|
+
if (unwrapped) {
|
|
413
|
+
console.log(" \u2713 Removed <ApostilWrapper> from root layout");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async function removeVite(cwd) {
|
|
419
|
+
const useSrc = await fileExists(import_path.default.join(cwd, "src"));
|
|
420
|
+
const baseDir = useSrc ? import_path.default.join(cwd, "src") : cwd;
|
|
421
|
+
const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
|
|
422
|
+
const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
|
|
423
|
+
if (await fileExists(wrapperFile)) {
|
|
424
|
+
await import_promises.default.rm(wrapperFile);
|
|
425
|
+
console.log(" \u2713 Removed wrapper component");
|
|
426
|
+
}
|
|
427
|
+
for (const name of ["App.tsx", "App.jsx", "main.tsx", "main.jsx"]) {
|
|
428
|
+
const file = import_path.default.join(baseDir, name);
|
|
429
|
+
if (await fileExists(file)) {
|
|
430
|
+
const unwrapped = await removeWrapperFromFile(file);
|
|
431
|
+
if (unwrapped) {
|
|
432
|
+
console.log(` \u2713 Removed <ApostilWrapper> from ${name}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function removeWrapperFromFile(filePath) {
|
|
438
|
+
let content = await import_promises.default.readFile(filePath, "utf-8");
|
|
259
439
|
if (!content.includes("ApostilWrapper")) return false;
|
|
260
440
|
content = content.replace(/import\s*\{[^}]*ApostilWrapper[^}]*\}\s*from\s*["'][^"']+["'];?\n?/g, "");
|
|
261
441
|
content = content.replace(/<ApostilWrapper>([\s\S]*?)<\/ApostilWrapper>/g, "$1");
|
|
262
|
-
await import_promises.default.writeFile(
|
|
442
|
+
await import_promises.default.writeFile(filePath, content, "utf-8");
|
|
263
443
|
return true;
|
|
264
444
|
}
|
|
265
445
|
async function findAppDir(cwd) {
|
package/dist/cli/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\ntype Mode = \"personal\" | \"dev\" | \"public\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === \"init\") {\n const mode = parseMode(args.slice(1));\n init(mode);\n} else if (command === \"remove\") {\n remove();\n} else if (command === \"help\" || command === \"--help\" || command === \"-h\" || !command) {\n printHelp();\n} else {\n console.log(` Unknown command: ${command}\\n`);\n printHelp();\n}\n\nfunction parseMode(flags: string[]): Mode {\n if (flags.includes(\"--dev\")) return \"dev\";\n if (flags.includes(\"--public\")) return \"public\";\n return \"personal\";\n}\n\nfunction printHelp() {\n console.log(`\n apostil — Pin-and-comment feedback for React & Next.js\n\n Usage:\n npx apostil init [mode] Set up apostil in your Next.js project\n npx apostil remove Remove apostil from your project\n npx apostil help Show this help\n\n Modes:\n (default) Personal — local dev only, comments gitignored\n --dev Dev + staging — comments gitignored, env-controlled\n --public All environments — comments committed to git\n`);\n}\n\nasync function init(mode: Mode) {\n const cwd = process.cwd();\n\n // Detect Next.js app directory\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.log(\" Could not find a Next.js app/ directory.\");\n console.log(\" Make sure you're running this from your project root.\\n\");\n process.exit(1);\n }\n\n // Detect src/ prefix\n const useSrc = appDir.includes(\"src/app\");\n\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(`\\n Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create API route\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n const apiFile = path.join(apiDir, \"route.ts\");\n if (await fileExists(apiFile)) {\n console.log(\" ✓ API route already exists\");\n } else {\n await fs.mkdir(apiDir, { recursive: true });\n await fs.writeFile(\n apiFile,\n `export { GET, POST } from \"apostil/adapters/nextjs\";\\n`,\n \"utf-8\"\n );\n console.log(` ✓ Created ${rel(cwd, apiFile)}`);\n }\n\n // 2. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n await fs.writeFile(wrapperFile, getWrapperComponent(mode), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n // 3. Create .apostil/ directory for comment storage\n const commentsDir = path.join(cwd, \".apostil\");\n await fs.mkdir(commentsDir, { recursive: true });\n console.log(\" ✓ Created .apostil/ directory\");\n\n // 4. Handle .gitignore based on mode\n const gitignorePath = path.join(cwd, \".gitignore\");\n let gitignore = \"\";\n try {\n gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {}\n\n if (mode === \"public\") {\n // Public mode: don't gitignore comments — they get committed\n if (gitignore.includes(\".apostil\")) {\n console.log(\" ✓ .gitignore unchanged (public mode — comments will be committed)\");\n } else {\n console.log(\" ✓ Comments will be committed to git (public mode)\");\n }\n } else {\n // Personal & dev: gitignore comments\n if (!gitignore.includes(\".apostil\")) {\n const entry = \"\\n# Apostil comments\\n.apostil/\\n\";\n await fs.appendFile(gitignorePath, entry, \"utf-8\");\n console.log(\" ✓ Added .apostil/ to .gitignore\");\n } else {\n console.log(\" ✓ .gitignore already configured\");\n }\n }\n\n // 5. Inject wrapper into root layout\n const layoutInjected = await injectIntoLayout(appDir, useSrc);\n if (layoutInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to root layout\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\nasync function remove() {\n const cwd = process.cwd();\n const appDir = await findAppDir(cwd);\n const useSrc = appDir?.includes(\"src/app\") ?? false;\n\n console.log(\"\\n Removing apostil...\\n\");\n\n // 1. Remove API route\n if (appDir) {\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n if (await fileExists(path.join(apiDir, \"route.ts\"))) {\n await fs.rm(apiDir, { recursive: true });\n console.log(\" ✓ Removed API route\");\n }\n }\n\n // 2. Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // 3. Remove wrapper from layout\n if (appDir) {\n const unwrapped = await removeFromLayout(appDir);\n if (unwrapped) {\n console.log(\" ✓ Removed <ApostilWrapper> from root layout\");\n }\n }\n\n // 4. Remove .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n if (await fileExists(commentsDir)) {\n await fs.rm(commentsDir, { recursive: true });\n console.log(\" ✓ Removed .apostil/ directory\");\n }\n\n // 5. Remove from .gitignore\n const gitignorePath = path.join(cwd, \".gitignore\");\n try {\n let gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n gitignore = gitignore.replace(/\\n?# Apostil comments\\n\\.apostil\\/\\n?/g, \"\");\n await fs.writeFile(gitignorePath, gitignore, \"utf-8\");\n console.log(\" ✓ Cleaned .gitignore\");\n } catch {}\n\n console.log(`\n Done! Now run: npm uninstall apostil\n`);\n}\n\n// --- Wrapper component template ---\n\nfunction getWrapperComponent(mode: Mode): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (process.env.NODE_ENV !== \"development\") {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment\n const forceOn = process.env.NEXT_PUBLIC_APOSTIL === \"true\";\n if (process.env.NODE_ENV === \"production\" && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set NEXT_PUBLIC_APOSTIL=false to disable\n if (process.env.NEXT_PUBLIC_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n return `\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n const pathname = usePathname();\n const pageId = pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";\n\n return (\n <ApostilProvider pageId={pageId}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\n// --- Layout injection ---\n\nasync function injectIntoLayout(appDir: string, useSrc: boolean): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n // Skip if already injected\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Layout already has <ApostilWrapper>\");\n return false;\n }\n\n // Add import at the top (after existing imports or \"use\" directives)\n const importPath = useSrc ? \"@/components/apostil-wrapper\" : \"../components/apostil-wrapper\";\n const importLine = `import { ApostilWrapper } from \"${importPath}\";\\n`;\n\n // Find the last import statement and insert after it\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n // No imports found, add at the top (after \"use client\" or \"use server\" if present)\n const useDirective = content.match(/^[\"']use (client|server)[\"'];?\\n/);\n const insertAt = useDirective ? useDirective[0].length : 0;\n content = content.slice(0, insertAt) + importLine + content.slice(insertAt);\n }\n\n // Wrap {children} with <ApostilWrapper>\n // Handle both {children} and { children } patterns inside <body>\n const bodyChildrenRegex = /(<body[^>]*>)([\\s\\S]*?)(\\{[\\s]*children[\\s]*\\})([\\s\\S]*?)(<\\/body>)/;\n const bodyMatch = content.match(bodyChildrenRegex);\n\n if (bodyMatch) {\n content = content.replace(\n bodyChildrenRegex,\n `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`\n );\n } else {\n // Fallback: try to find {children} anywhere and wrap it\n const childrenRegex = /(\\{[\\s]*children[\\s]*\\})/;\n if (childrenRegex.test(content)) {\n content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);\n } else {\n console.log(\" ⚠ Could not find {children} in layout — add <ApostilWrapper> manually\");\n return false;\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\nasync function removeFromLayout(appDir: string): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n if (!content.includes(\"ApostilWrapper\")) return false;\n\n // Remove import line\n content = content.replace(/import\\s*\\{[^}]*ApostilWrapper[^}]*\\}\\s*from\\s*[\"'][^\"']+[\"'];?\\n?/g, \"\");\n\n // Unwrap <ApostilWrapper>{children}</ApostilWrapper>\n content = content.replace(/<ApostilWrapper>([\\s\\S]*?)<\\/ApostilWrapper>/g, \"$1\");\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\n// --- Helpers ---\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n for (const candidate of [\"src/app\", \"app\"]) {\n const dir = path.join(cwd, candidate);\n try {\n const stat = await fs.stat(dir);\n if (stat.isDirectory()) return dir;\n } catch {}\n }\n return null;\n}\n\nasync function findLayout(appDir: string): Promise<string | null> {\n for (const ext of [\"tsx\", \"jsx\", \"ts\", \"js\"]) {\n const file = path.join(appDir, `layout.${ext}`);\n if (await fileExists(file)) return file;\n }\n return null;\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction rel(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,sBAAe;AACf,kBAAiB;AAIjB,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,IAAI,YAAY,QAAQ;AACtB,QAAM,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC;AACpC,OAAK,IAAI;AACX,WAAW,YAAY,UAAU;AAC/B,SAAO;AACT,WAAW,YAAY,UAAU,YAAY,YAAY,YAAY,QAAQ,CAAC,SAAS;AACrF,YAAU;AACZ,OAAO;AACL,UAAQ,IAAI,sBAAsB,OAAO;AAAA,CAAI;AAC7C,YAAU;AACZ;AAEA,SAAS,UAAU,OAAuB;AACxC,MAAI,MAAM,SAAS,OAAO,EAAG,QAAO;AACpC,MAAI,MAAM,SAAS,UAAU,EAAG,QAAO;AACvC,SAAO;AACT;AAEA,SAAS,YAAY;AACnB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAYb;AACD;AAEA,eAAe,KAAK,MAAY;AAC9B,QAAM,MAAM,QAAQ,IAAI;AAGxB,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,4CAA4C;AACxD,YAAQ,IAAI,2DAA2D;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SAAS,OAAO,SAAS,SAAS;AAExC,QAAM,YAAY,SAAS,aAAa,aAAa,SAAS,QAAQ,QAAQ;AAC9E,UAAQ,IAAI;AAAA,wBAA2B,SAAS;AAAA,CAAa;AAG7D,QAAM,SAAS,YAAAA,QAAK,KAAK,QAAQ,OAAO,SAAS;AACjD,QAAM,UAAU,YAAAA,QAAK,KAAK,QAAQ,UAAU;AAC5C,MAAI,MAAM,WAAW,OAAO,GAAG;AAC7B,YAAQ,IAAI,mCAA8B;AAAA,EAC5C,OAAO;AACL,UAAM,gBAAAC,QAAG,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,gBAAAA,QAAG;AAAA,MACP;AAAA,MACA;AAAA;AAAA,MACA;AAAA,IACF;AACA,YAAQ,IAAI,oBAAe,IAAI,KAAK,OAAO,CAAC,EAAE;AAAA,EAChD;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,QAAM,gBAAAC,QAAG,MAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AACjD,QAAM,gBAAAA,QAAG,UAAU,aAAa,oBAAoB,IAAI,GAAG,OAAO;AAClE,UAAQ,IAAI,oBAAe,IAAI,KAAK,WAAW,CAAC,KAAK,SAAS,QAAQ;AAGtE,QAAM,cAAc,YAAAD,QAAK,KAAK,KAAK,UAAU;AAC7C,QAAM,gBAAAC,QAAG,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC/C,UAAQ,IAAI,sCAAiC;AAG7C,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,YAAY;AACjD,MAAI,YAAY;AAChB,MAAI;AACF,gBAAY,MAAM,gBAAAC,QAAG,SAAS,eAAe,OAAO;AAAA,EACtD,QAAQ;AAAA,EAAC;AAET,MAAI,SAAS,UAAU;AAErB,QAAI,UAAU,SAAS,UAAU,GAAG;AAClC,cAAQ,IAAI,+EAAqE;AAAA,IACnF,OAAO;AACL,cAAQ,IAAI,0DAAqD;AAAA,IACnE;AAAA,EACF,OAAO;AAEL,QAAI,CAAC,UAAU,SAAS,UAAU,GAAG;AACnC,YAAM,QAAQ;AACd,YAAM,gBAAAA,QAAG,WAAW,eAAe,OAAO,OAAO;AACjD,cAAQ,IAAI,wCAAmC;AAAA,IACjD,OAAO;AACL,cAAQ,IAAI,wCAAmC;AAAA,IACjD;AAAA,EACF;AAGA,QAAM,iBAAiB,MAAM,iBAAiB,QAAQ,MAAM;AAC5D,MAAI,gBAAgB;AAClB,YAAQ,IAAI,gDAA2C;AAAA,EACzD;AAEA,UAAQ,IAAI,kEAAkE;AAChF;AAEA,eAAe,SAAS;AACtB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,SAAS,QAAQ,SAAS,SAAS,KAAK;AAE9C,UAAQ,IAAI,2BAA2B;AAGvC,MAAI,QAAQ;AACV,UAAM,SAAS,YAAAD,QAAK,KAAK,QAAQ,OAAO,SAAS;AACjD,QAAI,MAAM,WAAW,YAAAA,QAAK,KAAK,QAAQ,UAAU,CAAC,GAAG;AACnD,YAAM,gBAAAC,QAAG,GAAG,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,cAAQ,IAAI,4BAAuB;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,gBAAAC,QAAG,GAAG,WAAW;AACvB,YAAQ,IAAI,oCAA+B;AAAA,EAC7C;AAGA,MAAI,QAAQ;AACV,UAAM,YAAY,MAAM,iBAAiB,MAAM;AAC/C,QAAI,WAAW;AACb,cAAQ,IAAI,oDAA+C;AAAA,IAC7D;AAAA,EACF;AAGA,QAAM,cAAc,YAAAD,QAAK,KAAK,KAAK,UAAU;AAC7C,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,gBAAAC,QAAG,GAAG,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,YAAQ,IAAI,sCAAiC;AAAA,EAC/C;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,YAAY;AACjD,MAAI;AACF,QAAI,YAAY,MAAM,gBAAAC,QAAG,SAAS,eAAe,OAAO;AACxD,gBAAY,UAAU,QAAQ,0CAA0C,EAAE;AAC1E,UAAM,gBAAAA,QAAG,UAAU,eAAe,WAAW,OAAO;AACpD,YAAQ,IAAI,6BAAwB;AAAA,EACtC,QAAQ;AAAA,EAAC;AAET,UAAQ,IAAI;AAAA;AAAA,CAEb;AACD;AAIA,SAAS,oBAAoB,MAAoB;AAC/C,QAAM,WAAW,SAAS,aACtB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,SAAS,QACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQJ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+EAWsE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcvF;AAIA,eAAe,iBAAiB,QAAgB,QAAmC;AACjF,QAAM,aAAa,MAAM,WAAW,MAAM;AAC1C,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,OAAO;AAGnD,MAAI,QAAQ,SAAS,gBAAgB,GAAG;AACtC,YAAQ,IAAI,8CAAyC;AACrD,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,SAAS,iCAAiC;AAC7D,QAAM,aAAa,mCAAmC,UAAU;AAAA;AAGhE,QAAM,cAAc;AACpB,MAAI,kBAAkB;AACtB,MAAI;AACJ,UAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,sBAAkB,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,EAC3C;AAEA,MAAI,kBAAkB,GAAG;AACvB,cAAU,QAAQ,MAAM,GAAG,eAAe,IAAI,OAAO,aAAa,QAAQ,MAAM,eAAe;AAAA,EACjG,OAAO;AAEL,UAAM,eAAe,QAAQ,MAAM,kCAAkC;AACrE,UAAM,WAAW,eAAe,aAAa,CAAC,EAAE,SAAS;AACzD,cAAU,QAAQ,MAAM,GAAG,QAAQ,IAAI,aAAa,QAAQ,MAAM,QAAQ;AAAA,EAC5E;AAIA,QAAM,oBAAoB;AAC1B,QAAM,YAAY,QAAQ,MAAM,iBAAiB;AAEjD,MAAI,WAAW;AACb,cAAU,QAAQ;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,gBAAgB;AACtB,QAAI,cAAc,KAAK,OAAO,GAAG;AAC/B,gBAAU,QAAQ,QAAQ,eAAe,qCAAqC;AAAA,IAChF,OAAO;AACL,cAAQ,IAAI,mFAAyE;AACrF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,gBAAAA,QAAG,UAAU,YAAY,SAAS,OAAO;AAC/C,SAAO;AACT;AAEA,eAAe,iBAAiB,QAAkC;AAChE,QAAM,aAAa,MAAM,WAAW,MAAM;AAC1C,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,OAAO;AAEnD,MAAI,CAAC,QAAQ,SAAS,gBAAgB,EAAG,QAAO;AAGhD,YAAU,QAAQ,QAAQ,uEAAuE,EAAE;AAGnG,YAAU,QAAQ,QAAQ,iDAAiD,IAAI;AAE/E,QAAM,gBAAAA,QAAG,UAAU,YAAY,SAAS,OAAO;AAC/C,SAAO;AACT;AAIA,eAAe,WAAW,KAAqC;AAC7D,aAAW,aAAa,CAAC,WAAW,KAAK,GAAG;AAC1C,UAAM,MAAM,YAAAD,QAAK,KAAK,KAAK,SAAS;AACpC,QAAI;AACF,YAAM,OAAO,MAAM,gBAAAC,QAAG,KAAK,GAAG;AAC9B,UAAI,KAAK,YAAY,EAAG,QAAO;AAAA,IACjC,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEA,eAAe,WAAW,QAAwC;AAChE,aAAW,OAAO,CAAC,OAAO,OAAO,MAAM,IAAI,GAAG;AAC5C,UAAM,OAAO,YAAAD,QAAK,KAAK,QAAQ,UAAU,GAAG,EAAE;AAC9C,QAAI,MAAM,WAAW,IAAI,EAAG,QAAO;AAAA,EACrC;AACA,SAAO;AACT;AAEA,eAAe,WAAW,GAA6B;AACrD,MAAI;AACF,UAAM,gBAAAC,QAAG,KAAK,CAAC;AACf,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,IAAI,KAAa,UAA0B;AAClD,SAAO,YAAAD,QAAK,SAAS,KAAK,QAAQ;AACpC;","names":["path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\ntype Mode = \"personal\" | \"dev\" | \"public\";\ntype Framework = \"nextjs\" | \"vite\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === \"init\") {\n const mode = parseMode(args.slice(1));\n init(mode);\n} else if (command === \"remove\") {\n remove();\n} else if (command === \"help\" || command === \"--help\" || command === \"-h\" || !command) {\n printHelp();\n} else {\n console.log(` Unknown command: ${command}\\n`);\n printHelp();\n}\n\nfunction parseMode(flags: string[]): Mode {\n if (flags.includes(\"--dev\")) return \"dev\";\n if (flags.includes(\"--public\")) return \"public\";\n return \"personal\";\n}\n\nfunction printHelp() {\n console.log(`\n apostil — Lightweight, Figma-like commenting for React\n\n Usage:\n npx apostil init [mode] Set up apostil in your project\n npx apostil remove Remove apostil from your project\n npx apostil help Show this help\n\n Modes:\n (default) Personal — local dev only, comments gitignored\n --dev Dev + staging — comments gitignored, env-controlled\n --public All environments — comments committed to git\n\n Supported frameworks: Next.js (App Router), Vite + React\n Framework is auto-detected from your project.\n`);\n}\n\n// --- Framework detection ---\n\nasync function detectFramework(cwd: string): Promise<Framework | null> {\n // Check config files first\n for (const name of [\"next.config.js\", \"next.config.ts\", \"next.config.mjs\"]) {\n if (await fileExists(path.join(cwd, name))) return \"nextjs\";\n }\n for (const name of [\"vite.config.js\", \"vite.config.ts\", \"vite.config.mjs\"]) {\n if (await fileExists(path.join(cwd, name))) return \"vite\";\n }\n\n // Fallback: check package.json dependencies\n try {\n const pkg = JSON.parse(await fs.readFile(path.join(cwd, \"package.json\"), \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps[\"next\"]) return \"nextjs\";\n if (deps[\"vite\"]) return \"vite\";\n } catch {}\n\n return null;\n}\n\n// --- Init ---\n\nasync function init(mode: Mode) {\n const cwd = process.cwd();\n const framework = await detectFramework(cwd);\n\n if (!framework) {\n console.log(\" Could not detect your framework.\");\n console.log(\" Apostil supports Next.js (App Router) and Vite + React.\");\n console.log(\" Make sure you're running this from your project root.\\n\");\n process.exit(1);\n }\n\n console.log(`\\n Detected: ${framework === \"nextjs\" ? \"Next.js\" : \"Vite + React\"}`);\n\n if (framework === \"nextjs\") {\n await initNextjs(cwd, mode);\n } else {\n await initVite(cwd, mode);\n }\n}\n\n// --- Next.js init ---\n\nasync function initNextjs(cwd: string, mode: Mode) {\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.log(\" Could not find a Next.js app/ directory.\");\n console.log(\" Make sure you're using the App Router.\\n\");\n process.exit(1);\n }\n\n const useSrc = appDir.includes(\"src/app\");\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(` Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create API route\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n const apiFile = path.join(apiDir, \"route.ts\");\n if (await fileExists(apiFile)) {\n console.log(\" ✓ API route already exists\");\n } else {\n await fs.mkdir(apiDir, { recursive: true });\n await fs.writeFile(\n apiFile,\n `export { GET, POST } from \"apostil/adapters/nextjs\";\\n`,\n \"utf-8\"\n );\n console.log(` ✓ Created ${rel(cwd, apiFile)}`);\n }\n\n // 2. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n await fs.writeFile(wrapperFile, getNextjsWrapper(mode), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n // 3. Create .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n await fs.mkdir(commentsDir, { recursive: true });\n console.log(\" ✓ Created .apostil/ directory\");\n\n // 4. Handle .gitignore\n await handleGitignore(cwd, mode);\n\n // 5. Inject wrapper into root layout\n const layoutInjected = await injectIntoNextjsLayout(appDir, useSrc);\n if (layoutInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to root layout\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\n// --- Vite init ---\n\nasync function initVite(cwd: string, mode: Mode) {\n const useSrc = await fileExists(path.join(cwd, \"src\"));\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(` Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n const hasRouter = await hasReactRouter(cwd);\n await fs.writeFile(wrapperFile, getViteWrapper(mode, hasRouter), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n console.log(\" ✓ Using localStorage adapter (comments stored in browser)\");\n\n // 3. Inject wrapper into entry point\n const entryInjected = await injectIntoViteEntry(cwd, useSrc);\n if (entryInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to app entry\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\n// --- Wrapper templates ---\n\nfunction getNextjsWrapper(mode: Mode): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (process.env.NODE_ENV !== \"development\") {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment\n const forceOn = process.env.NEXT_PUBLIC_APOSTIL === \"true\";\n if (process.env.NODE_ENV === \"production\" && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set NEXT_PUBLIC_APOSTIL=false to disable\n if (process.env.NEXT_PUBLIC_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n return `\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n const pathname = usePathname();\n const pageId = pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";\n\n return (\n <ApostilProvider pageId={pageId}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\nasync function hasReactRouter(cwd: string): Promise<boolean> {\n try {\n const pkg = JSON.parse(await fs.readFile(path.join(cwd, \"package.json\"), \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n return !!deps[\"react-router-dom\"] || !!deps[\"react-router\"];\n } catch {\n return false;\n }\n}\n\n// No \"use client\" directive — Vite doesn't use React Server Components\nfunction getViteWrapper(mode: Mode, hasRouter: boolean): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (import.meta.env.PROD) {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set VITE_APOSTIL=true to force on in any environment\n const forceOn = import.meta.env.VITE_APOSTIL === \"true\";\n if (import.meta.env.PROD && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set VITE_APOSTIL=false to disable\n if (import.meta.env.VITE_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n const routerImport = hasRouter\n ? `import { useLocation } from \"react-router-dom\";\\n`\n : \"\";\n\n const pageIdLogic = hasRouter\n ? ` const location = useLocation();\n const pageId = location.pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";`\n : ` const pageId = window.location.pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";`;\n\n return `${routerImport}import {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport { localStorageAdapter } from \"apostil/adapters/localStorage\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n${pageIdLogic}\n\n return (\n <ApostilProvider pageId={pageId} storage={localStorageAdapter}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\n// --- Gitignore ---\n\nasync function handleGitignore(cwd: string, mode: Mode) {\n const gitignorePath = path.join(cwd, \".gitignore\");\n let gitignore = \"\";\n try {\n gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {}\n\n if (mode === \"public\") {\n if (gitignore.includes(\".apostil\")) {\n console.log(\" ✓ .gitignore unchanged (public mode — comments will be committed)\");\n } else {\n console.log(\" ✓ Comments will be committed to git (public mode)\");\n }\n } else {\n if (!gitignore.includes(\".apostil\")) {\n const entry = \"\\n# Apostil comments\\n.apostil/\\n\";\n await fs.appendFile(gitignorePath, entry, \"utf-8\");\n console.log(\" ✓ Added .apostil/ to .gitignore\");\n } else {\n console.log(\" ✓ .gitignore already configured\");\n }\n }\n}\n\n// --- Next.js layout injection ---\n\nasync function injectIntoNextjsLayout(appDir: string, useSrc: boolean): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Layout already has <ApostilWrapper>\");\n return false;\n }\n\n const importPath = useSrc ? \"@/components/apostil-wrapper\" : \"../components/apostil-wrapper\";\n const importLine = `import { ApostilWrapper } from \"${importPath}\";\\n`;\n\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n const useDirective = content.match(/^[\"']use (client|server)[\"'];?\\n/);\n const insertAt = useDirective ? useDirective[0].length : 0;\n content = content.slice(0, insertAt) + importLine + content.slice(insertAt);\n }\n\n const bodyChildrenRegex = /(<body[^>]*>)([\\s\\S]*?)(\\{[\\s]*children[\\s]*\\})([\\s\\S]*?)(<\\/body>)/;\n const bodyMatch = content.match(bodyChildrenRegex);\n\n if (bodyMatch) {\n content = content.replace(\n bodyChildrenRegex,\n `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`\n );\n } else {\n const childrenRegex = /(\\{[\\s]*children[\\s]*\\})/;\n if (childrenRegex.test(content)) {\n content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);\n } else {\n console.log(\" ⚠ Could not find {children} in layout — add <ApostilWrapper> manually\");\n return false;\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\n// --- Vite entry injection ---\n\nasync function injectIntoViteEntry(cwd: string, useSrc: boolean): Promise<boolean> {\n const baseDir = useSrc ? path.join(cwd, \"src\") : cwd;\n\n // Inject into main.tsx/jsx — wrap <App /> which is a reliable, unique target\n for (const ext of [\"tsx\", \"jsx\"]) {\n const mainFile = path.join(baseDir, `main.${ext}`);\n if (await fileExists(mainFile)) {\n return await injectIntoViteMain(mainFile);\n }\n }\n\n console.log(\" ⚠ Could not find main.tsx or main.jsx — add <ApostilWrapper> manually\");\n return false;\n}\n\nasync function injectIntoViteMain(mainFile: string, wrapperFile?: string): Promise<boolean> {\n let content = await fs.readFile(mainFile, \"utf-8\");\n\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Entry already has <ApostilWrapper>\");\n return false;\n }\n\n // Compute relative import path from main file to wrapper\n const mainDir = path.dirname(mainFile);\n const wrapperPath = wrapperFile ?? path.join(mainDir, \"components\", \"apostil-wrapper.tsx\");\n let relativePath = path.relative(mainDir, wrapperPath).replace(/\\.tsx$/, \"\");\n if (!relativePath.startsWith(\".\")) relativePath = \"./\" + relativePath;\n const importLine = `import { ApostilWrapper } from \"${relativePath}\";\\n`;\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n content = importLine + content;\n }\n\n // Wrap <App /> with <ApostilWrapper>\n content = content.replace(/<App\\s*\\/>/, `<ApostilWrapper><App /></ApostilWrapper>`);\n\n await fs.writeFile(mainFile, content, \"utf-8\");\n return true;\n}\n\n// --- Remove ---\n\nasync function remove() {\n const cwd = process.cwd();\n const framework = await detectFramework(cwd);\n\n console.log(\"\\n Removing apostil...\\n\");\n\n if (framework === \"nextjs\") {\n await removeNextjs(cwd);\n } else if (framework === \"vite\") {\n await removeVite(cwd);\n } else {\n // Try both\n await removeNextjs(cwd);\n await removeVite(cwd);\n }\n\n // Remove .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n if (await fileExists(commentsDir)) {\n await fs.rm(commentsDir, { recursive: true });\n console.log(\" ✓ Removed .apostil/ directory\");\n }\n\n // Remove from .gitignore\n const gitignorePath = path.join(cwd, \".gitignore\");\n try {\n let gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n gitignore = gitignore.replace(/\\n?# Apostil comments\\n\\.apostil\\/\\n?/g, \"\");\n await fs.writeFile(gitignorePath, gitignore, \"utf-8\");\n console.log(\" ✓ Cleaned .gitignore\");\n } catch {}\n\n console.log(`\n Done! Now run: npm uninstall apostil\n`);\n}\n\nasync function removeNextjs(cwd: string) {\n const appDir = await findAppDir(cwd);\n const useSrc = appDir?.includes(\"src/app\") ?? false;\n\n // Remove API route\n if (appDir) {\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n if (await fileExists(path.join(apiDir, \"route.ts\"))) {\n await fs.rm(apiDir, { recursive: true });\n console.log(\" ✓ Removed API route\");\n }\n }\n\n // Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // Remove wrapper from layout\n if (appDir) {\n const layoutPath = await findLayout(appDir);\n if (layoutPath) {\n const unwrapped = await removeWrapperFromFile(layoutPath);\n if (unwrapped) {\n console.log(\" ✓ Removed <ApostilWrapper> from root layout\");\n }\n }\n }\n}\n\nasync function removeVite(cwd: string) {\n const useSrc = await fileExists(path.join(cwd, \"src\"));\n const baseDir = useSrc ? path.join(cwd, \"src\") : cwd;\n\n // Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // Remove wrapper from App.tsx or main.tsx\n for (const name of [\"App.tsx\", \"App.jsx\", \"main.tsx\", \"main.jsx\"]) {\n const file = path.join(baseDir, name);\n if (await fileExists(file)) {\n const unwrapped = await removeWrapperFromFile(file);\n if (unwrapped) {\n console.log(` ✓ Removed <ApostilWrapper> from ${name}`);\n }\n }\n }\n}\n\nasync function removeWrapperFromFile(filePath: string): Promise<boolean> {\n let content = await fs.readFile(filePath, \"utf-8\");\n if (!content.includes(\"ApostilWrapper\")) return false;\n\n // Remove import line\n content = content.replace(/import\\s*\\{[^}]*ApostilWrapper[^}]*\\}\\s*from\\s*[\"'][^\"']+[\"'];?\\n?/g, \"\");\n\n // Unwrap <ApostilWrapper>...</ApostilWrapper>\n content = content.replace(/<ApostilWrapper>([\\s\\S]*?)<\\/ApostilWrapper>/g, \"$1\");\n\n await fs.writeFile(filePath, content, \"utf-8\");\n return true;\n}\n\n// --- Helpers ---\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n for (const candidate of [\"src/app\", \"app\"]) {\n const dir = path.join(cwd, candidate);\n try {\n const stat = await fs.stat(dir);\n if (stat.isDirectory()) return dir;\n } catch {}\n }\n return null;\n}\n\nasync function findLayout(appDir: string): Promise<string | null> {\n for (const ext of [\"tsx\", \"jsx\", \"ts\", \"js\"]) {\n const file = path.join(appDir, `layout.${ext}`);\n if (await fileExists(file)) return file;\n }\n return null;\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction rel(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,sBAAe;AACf,kBAAiB;AAKjB,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,IAAI,YAAY,QAAQ;AACtB,QAAM,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC;AACpC,OAAK,IAAI;AACX,WAAW,YAAY,UAAU;AAC/B,SAAO;AACT,WAAW,YAAY,UAAU,YAAY,YAAY,YAAY,QAAQ,CAAC,SAAS;AACrF,YAAU;AACZ,OAAO;AACL,UAAQ,IAAI,sBAAsB,OAAO;AAAA,CAAI;AAC7C,YAAU;AACZ;AAEA,SAAS,UAAU,OAAuB;AACxC,MAAI,MAAM,SAAS,OAAO,EAAG,QAAO;AACpC,MAAI,MAAM,SAAS,UAAU,EAAG,QAAO;AACvC,SAAO;AACT;AAEA,SAAS,YAAY;AACnB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAeb;AACD;AAIA,eAAe,gBAAgB,KAAwC;AAErE,aAAW,QAAQ,CAAC,kBAAkB,kBAAkB,iBAAiB,GAAG;AAC1E,QAAI,MAAM,WAAW,YAAAA,QAAK,KAAK,KAAK,IAAI,CAAC,EAAG,QAAO;AAAA,EACrD;AACA,aAAW,QAAQ,CAAC,kBAAkB,kBAAkB,iBAAiB,GAAG;AAC1E,QAAI,MAAM,WAAW,YAAAA,QAAK,KAAK,KAAK,IAAI,CAAC,EAAG,QAAO;AAAA,EACrD;AAGA,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,MAAM,gBAAAC,QAAG,SAAS,YAAAD,QAAK,KAAK,KAAK,cAAc,GAAG,OAAO,CAAC;AACjF,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,QAAI,KAAK,MAAM,EAAG,QAAO;AACzB,QAAI,KAAK,MAAM,EAAG,QAAO;AAAA,EAC3B,QAAQ;AAAA,EAAC;AAET,SAAO;AACT;AAIA,eAAe,KAAK,MAAY;AAC9B,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,YAAY,MAAM,gBAAgB,GAAG;AAE3C,MAAI,CAAC,WAAW;AACd,YAAQ,IAAI,oCAAoC;AAChD,YAAQ,IAAI,2DAA2D;AACvE,YAAQ,IAAI,2DAA2D;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI;AAAA,cAAiB,cAAc,WAAW,YAAY,cAAc,EAAE;AAElF,MAAI,cAAc,UAAU;AAC1B,UAAM,WAAW,KAAK,IAAI;AAAA,EAC5B,OAAO;AACL,UAAM,SAAS,KAAK,IAAI;AAAA,EAC1B;AACF;AAIA,eAAe,WAAW,KAAa,MAAY;AACjD,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,4CAA4C;AACxD,YAAQ,IAAI,4CAA4C;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,OAAO,SAAS,SAAS;AACxC,QAAM,YAAY,SAAS,aAAa,aAAa,SAAS,QAAQ,QAAQ;AAC9E,UAAQ,IAAI,yBAAyB,SAAS;AAAA,CAAa;AAG3D,QAAM,SAAS,YAAAA,QAAK,KAAK,QAAQ,OAAO,SAAS;AACjD,QAAM,UAAU,YAAAA,QAAK,KAAK,QAAQ,UAAU;AAC5C,MAAI,MAAM,WAAW,OAAO,GAAG;AAC7B,YAAQ,IAAI,mCAA8B;AAAA,EAC5C,OAAO;AACL,UAAM,gBAAAC,QAAG,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,gBAAAA,QAAG;AAAA,MACP;AAAA,MACA;AAAA;AAAA,MACA;AAAA,IACF;AACA,YAAQ,IAAI,oBAAe,IAAI,KAAK,OAAO,CAAC,EAAE;AAAA,EAChD;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,QAAM,gBAAAC,QAAG,MAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AACjD,QAAM,gBAAAA,QAAG,UAAU,aAAa,iBAAiB,IAAI,GAAG,OAAO;AAC/D,UAAQ,IAAI,oBAAe,IAAI,KAAK,WAAW,CAAC,KAAK,SAAS,QAAQ;AAGtE,QAAM,cAAc,YAAAD,QAAK,KAAK,KAAK,UAAU;AAC7C,QAAM,gBAAAC,QAAG,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC/C,UAAQ,IAAI,sCAAiC;AAG7C,QAAM,gBAAgB,KAAK,IAAI;AAG/B,QAAM,iBAAiB,MAAM,uBAAuB,QAAQ,MAAM;AAClE,MAAI,gBAAgB;AAClB,YAAQ,IAAI,gDAA2C;AAAA,EACzD;AAEA,UAAQ,IAAI,kEAAkE;AAChF;AAIA,eAAe,SAAS,KAAa,MAAY;AAC/C,QAAM,SAAS,MAAM,WAAW,YAAAD,QAAK,KAAK,KAAK,KAAK,CAAC;AACrD,QAAM,YAAY,SAAS,aAAa,aAAa,SAAS,QAAQ,QAAQ;AAC9E,UAAQ,IAAI,yBAAyB,SAAS;AAAA,CAAa;AAG3D,QAAM,gBAAgB,YAAAA,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,QAAM,gBAAAC,QAAG,MAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AACjD,QAAM,YAAY,MAAM,eAAe,GAAG;AAC1C,QAAM,gBAAAA,QAAG,UAAU,aAAa,eAAe,MAAM,SAAS,GAAG,OAAO;AACxE,UAAQ,IAAI,oBAAe,IAAI,KAAK,WAAW,CAAC,KAAK,SAAS,QAAQ;AAEtE,UAAQ,IAAI,kEAA6D;AAGzE,QAAM,gBAAgB,MAAM,oBAAoB,KAAK,MAAM;AAC3D,MAAI,eAAe;AACjB,YAAQ,IAAI,8CAAyC;AAAA,EACvD;AAEA,UAAQ,IAAI,kEAAkE;AAChF;AAIA,SAAS,iBAAiB,MAAoB;AAC5C,QAAM,WAAW,SAAS,aACtB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,SAAS,QACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQJ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+EAWsE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcvF;AAEA,eAAe,eAAe,KAA+B;AAC3D,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,MAAM,gBAAAA,QAAG,SAAS,YAAAD,QAAK,KAAK,KAAK,cAAc,GAAG,OAAO,CAAC;AACjF,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,WAAO,CAAC,CAAC,KAAK,kBAAkB,KAAK,CAAC,CAAC,KAAK,cAAc;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,eAAe,MAAY,WAA4B;AAC9D,QAAM,WAAW,SAAS,aACtB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,SAAS,QACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQJ,QAAM,eAAe,YACjB;AAAA,IACA;AAEJ,QAAM,cAAc,YAChB;AAAA,0FAEA;AAEJ,SAAO,GAAG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+EASuD,QAAQ;AAAA,EACrF,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYb;AAIA,eAAe,gBAAgB,KAAa,MAAY;AACtD,QAAM,gBAAgB,YAAAA,QAAK,KAAK,KAAK,YAAY;AACjD,MAAI,YAAY;AAChB,MAAI;AACF,gBAAY,MAAM,gBAAAC,QAAG,SAAS,eAAe,OAAO;AAAA,EACtD,QAAQ;AAAA,EAAC;AAET,MAAI,SAAS,UAAU;AACrB,QAAI,UAAU,SAAS,UAAU,GAAG;AAClC,cAAQ,IAAI,+EAAqE;AAAA,IACnF,OAAO;AACL,cAAQ,IAAI,0DAAqD;AAAA,IACnE;AAAA,EACF,OAAO;AACL,QAAI,CAAC,UAAU,SAAS,UAAU,GAAG;AACnC,YAAM,QAAQ;AACd,YAAM,gBAAAA,QAAG,WAAW,eAAe,OAAO,OAAO;AACjD,cAAQ,IAAI,wCAAmC;AAAA,IACjD,OAAO;AACL,cAAQ,IAAI,wCAAmC;AAAA,IACjD;AAAA,EACF;AACF;AAIA,eAAe,uBAAuB,QAAgB,QAAmC;AACvF,QAAM,aAAa,MAAM,WAAW,MAAM;AAC1C,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,UAAU,MAAM,gBAAAA,QAAG,SAAS,YAAY,OAAO;AAEnD,MAAI,QAAQ,SAAS,gBAAgB,GAAG;AACtC,YAAQ,IAAI,8CAAyC;AACrD,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,SAAS,iCAAiC;AAC7D,QAAM,aAAa,mCAAmC,UAAU;AAAA;AAEhE,QAAM,cAAc;AACpB,MAAI,kBAAkB;AACtB,MAAI;AACJ,UAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,sBAAkB,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,EAC3C;AAEA,MAAI,kBAAkB,GAAG;AACvB,cAAU,QAAQ,MAAM,GAAG,eAAe,IAAI,OAAO,aAAa,QAAQ,MAAM,eAAe;AAAA,EACjG,OAAO;AACL,UAAM,eAAe,QAAQ,MAAM,kCAAkC;AACrE,UAAM,WAAW,eAAe,aAAa,CAAC,EAAE,SAAS;AACzD,cAAU,QAAQ,MAAM,GAAG,QAAQ,IAAI,aAAa,QAAQ,MAAM,QAAQ;AAAA,EAC5E;AAEA,QAAM,oBAAoB;AAC1B,QAAM,YAAY,QAAQ,MAAM,iBAAiB;AAEjD,MAAI,WAAW;AACb,cAAU,QAAQ;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM,gBAAgB;AACtB,QAAI,cAAc,KAAK,OAAO,GAAG;AAC/B,gBAAU,QAAQ,QAAQ,eAAe,qCAAqC;AAAA,IAChF,OAAO;AACL,cAAQ,IAAI,mFAAyE;AACrF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,gBAAAA,QAAG,UAAU,YAAY,SAAS,OAAO;AAC/C,SAAO;AACT;AAIA,eAAe,oBAAoB,KAAa,QAAmC;AACjF,QAAM,UAAU,SAAS,YAAAD,QAAK,KAAK,KAAK,KAAK,IAAI;AAGjD,aAAW,OAAO,CAAC,OAAO,KAAK,GAAG;AAChC,UAAM,WAAW,YAAAA,QAAK,KAAK,SAAS,QAAQ,GAAG,EAAE;AACjD,QAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,aAAO,MAAM,mBAAmB,QAAQ;AAAA,IAC1C;AAAA,EACF;AAEA,UAAQ,IAAI,mFAAyE;AACrF,SAAO;AACT;AAEA,eAAe,mBAAmB,UAAkB,aAAwC;AAC1F,MAAI,UAAU,MAAM,gBAAAC,QAAG,SAAS,UAAU,OAAO;AAEjD,MAAI,QAAQ,SAAS,gBAAgB,GAAG;AACtC,YAAQ,IAAI,6CAAwC;AACpD,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,YAAAD,QAAK,QAAQ,QAAQ;AACrC,QAAM,cAAc,eAAe,YAAAA,QAAK,KAAK,SAAS,cAAc,qBAAqB;AACzF,MAAI,eAAe,YAAAA,QAAK,SAAS,SAAS,WAAW,EAAE,QAAQ,UAAU,EAAE;AAC3E,MAAI,CAAC,aAAa,WAAW,GAAG,EAAG,gBAAe,OAAO;AACzD,QAAM,aAAa,mCAAmC,YAAY;AAAA;AAClE,QAAM,cAAc;AACpB,MAAI,kBAAkB;AACtB,MAAI;AACJ,UAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,sBAAkB,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,EAC3C;AAEA,MAAI,kBAAkB,GAAG;AACvB,cAAU,QAAQ,MAAM,GAAG,eAAe,IAAI,OAAO,aAAa,QAAQ,MAAM,eAAe;AAAA,EACjG,OAAO;AACL,cAAU,aAAa;AAAA,EACzB;AAGA,YAAU,QAAQ,QAAQ,cAAc,0CAA0C;AAElF,QAAM,gBAAAC,QAAG,UAAU,UAAU,SAAS,OAAO;AAC7C,SAAO;AACT;AAIA,eAAe,SAAS;AACtB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,YAAY,MAAM,gBAAgB,GAAG;AAE3C,UAAQ,IAAI,2BAA2B;AAEvC,MAAI,cAAc,UAAU;AAC1B,UAAM,aAAa,GAAG;AAAA,EACxB,WAAW,cAAc,QAAQ;AAC/B,UAAM,WAAW,GAAG;AAAA,EACtB,OAAO;AAEL,UAAM,aAAa,GAAG;AACtB,UAAM,WAAW,GAAG;AAAA,EACtB;AAGA,QAAM,cAAc,YAAAD,QAAK,KAAK,KAAK,UAAU;AAC7C,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,gBAAAC,QAAG,GAAG,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,YAAQ,IAAI,sCAAiC;AAAA,EAC/C;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,YAAY;AACjD,MAAI;AACF,QAAI,YAAY,MAAM,gBAAAC,QAAG,SAAS,eAAe,OAAO;AACxD,gBAAY,UAAU,QAAQ,0CAA0C,EAAE;AAC1E,UAAM,gBAAAA,QAAG,UAAU,eAAe,WAAW,OAAO;AACpD,YAAQ,IAAI,6BAAwB;AAAA,EACtC,QAAQ;AAAA,EAAC;AAET,UAAQ,IAAI;AAAA;AAAA,CAEb;AACD;AAEA,eAAe,aAAa,KAAa;AACvC,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,SAAS,QAAQ,SAAS,SAAS,KAAK;AAG9C,MAAI,QAAQ;AACV,UAAM,SAAS,YAAAD,QAAK,KAAK,QAAQ,OAAO,SAAS;AACjD,QAAI,MAAM,WAAW,YAAAA,QAAK,KAAK,QAAQ,UAAU,CAAC,GAAG;AACnD,YAAM,gBAAAC,QAAG,GAAG,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,cAAQ,IAAI,4BAAuB;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,gBAAgB,YAAAD,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,gBAAAC,QAAG,GAAG,WAAW;AACvB,YAAQ,IAAI,oCAA+B;AAAA,EAC7C;AAGA,MAAI,QAAQ;AACV,UAAM,aAAa,MAAM,WAAW,MAAM;AAC1C,QAAI,YAAY;AACd,YAAM,YAAY,MAAM,sBAAsB,UAAU;AACxD,UAAI,WAAW;AACb,gBAAQ,IAAI,oDAA+C;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,WAAW,KAAa;AACrC,QAAM,SAAS,MAAM,WAAW,YAAAD,QAAK,KAAK,KAAK,KAAK,CAAC;AACrD,QAAM,UAAU,SAAS,YAAAA,QAAK,KAAK,KAAK,KAAK,IAAI;AAGjD,QAAM,gBAAgB,YAAAA,QAAK,KAAK,KAAK,SAAS,mBAAmB,YAAY;AAC7E,QAAM,cAAc,YAAAA,QAAK,KAAK,eAAe,qBAAqB;AAClE,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,gBAAAC,QAAG,GAAG,WAAW;AACvB,YAAQ,IAAI,oCAA+B;AAAA,EAC7C;AAGA,aAAW,QAAQ,CAAC,WAAW,WAAW,YAAY,UAAU,GAAG;AACjE,UAAM,OAAO,YAAAD,QAAK,KAAK,SAAS,IAAI;AACpC,QAAI,MAAM,WAAW,IAAI,GAAG;AAC1B,YAAM,YAAY,MAAM,sBAAsB,IAAI;AAClD,UAAI,WAAW;AACb,gBAAQ,IAAI,0CAAqC,IAAI,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,sBAAsB,UAAoC;AACvE,MAAI,UAAU,MAAM,gBAAAC,QAAG,SAAS,UAAU,OAAO;AACjD,MAAI,CAAC,QAAQ,SAAS,gBAAgB,EAAG,QAAO;AAGhD,YAAU,QAAQ,QAAQ,uEAAuE,EAAE;AAGnG,YAAU,QAAQ,QAAQ,iDAAiD,IAAI;AAE/E,QAAM,gBAAAA,QAAG,UAAU,UAAU,SAAS,OAAO;AAC7C,SAAO;AACT;AAIA,eAAe,WAAW,KAAqC;AAC7D,aAAW,aAAa,CAAC,WAAW,KAAK,GAAG;AAC1C,UAAM,MAAM,YAAAD,QAAK,KAAK,KAAK,SAAS;AACpC,QAAI;AACF,YAAM,OAAO,MAAM,gBAAAC,QAAG,KAAK,GAAG;AAC9B,UAAI,KAAK,YAAY,EAAG,QAAO;AAAA,IACjC,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEA,eAAe,WAAW,QAAwC;AAChE,aAAW,OAAO,CAAC,OAAO,OAAO,MAAM,IAAI,GAAG;AAC5C,UAAM,OAAO,YAAAD,QAAK,KAAK,QAAQ,UAAU,GAAG,EAAE;AAC9C,QAAI,MAAM,WAAW,IAAI,EAAG,QAAO;AAAA,EACrC;AACA,SAAO;AACT;AAEA,eAAe,WAAW,GAA6B;AACrD,MAAI;AACF,UAAM,gBAAAC,QAAG,KAAK,CAAC;AACf,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,IAAI,KAAa,UAA0B;AAClD,SAAO,YAAAD,QAAK,SAAS,KAAK,QAAQ;AACpC;","names":["path","fs"]}
|