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 CHANGED
@@ -1,12 +1,12 @@
1
1
  # Apostil
2
2
 
3
- Figma-like commenting tool for React & Next.js. Leave comments directly on the Web App UI, and never miss a feedback.
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
- This will:
33
- - Create `app/api/apostil/route.ts` — API route for comment storage
34
- - Create `components/apostil-wrapper.tsx`pre-configured wrapper with env guard
35
- - Create `.apostil/` directory and add it to `.gitignore`
36
- - **Automatically wrap `{children}` in your root layout** with `<ApostilWrapper>`
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
 
@@ -47,10 +47,10 @@ function parseMode(flags) {
47
47
  }
48
48
  function printHelp() {
49
49
  console.log(`
50
- apostil \u2014 Pin-and-comment feedback for React & Next.js
50
+ apostil \u2014 Lightweight, Figma-like commenting for React
51
51
 
52
52
  Usage:
53
- npx apostil init [mode] Set up apostil in your Next.js project
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 running this from your project root.\n");
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, getWrapperComponent(mode), "utf-8");
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
- const gitignorePath = import_path.default.join(cwd, ".gitignore");
99
- let gitignore = "";
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 remove() {
126
- const cwd = process.cwd();
127
- const appDir = await findAppDir(cwd);
128
- const useSrc = appDir?.includes("src/app") ?? false;
129
- console.log("\n Removing apostil...\n");
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
- if (await fileExists(wrapperFile)) {
140
- await import_promises.default.rm(wrapperFile);
141
- console.log(" \u2713 Removed wrapper component");
142
- }
143
- if (appDir) {
144
- const unwrapped = await removeFromLayout(appDir);
145
- if (unwrapped) {
146
- console.log(" \u2713 Removed <ApostilWrapper> from root layout");
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 getWrapperComponent(mode) {
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 injectIntoLayout(appDir, useSrc) {
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 removeFromLayout(appDir) {
256
- const layoutPath = await findLayout(appDir);
257
- if (!layoutPath) return false;
258
- let content = await import_promises.default.readFile(layoutPath, "utf-8");
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(layoutPath, content, "utf-8");
442
+ await import_promises.default.writeFile(filePath, content, "utf-8");
263
443
  return true;
264
444
  }
265
445
  async function findAppDir(cwd) {
@@ -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":"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","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":"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","names":["path","fs"]}