@notionx/create-notionx-app 1.0.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.
Files changed (239) hide show
  1. package/README.md +139 -0
  2. package/dist/answers.js +332 -0
  3. package/dist/answers.js.map +1 -0
  4. package/dist/cli-notionx.js +388 -0
  5. package/dist/cli-notionx.js.map +1 -0
  6. package/dist/cli-notionx.test.js +277 -0
  7. package/dist/cli-notionx.test.js.map +1 -0
  8. package/dist/diff.js +40 -0
  9. package/dist/diff.js.map +1 -0
  10. package/dist/diff.test.js +90 -0
  11. package/dist/diff.test.js.map +1 -0
  12. package/dist/index.js +99 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/locale-add/apply.js +39 -0
  15. package/dist/locale-add/apply.js.map +1 -0
  16. package/dist/locale-add/format.js +38 -0
  17. package/dist/locale-add/format.js.map +1 -0
  18. package/dist/locale-add/list.js +44 -0
  19. package/dist/locale-add/list.js.map +1 -0
  20. package/dist/locale-add/list.test.js +45 -0
  21. package/dist/locale-add/list.test.js.map +1 -0
  22. package/dist/locale-add/plan.js +128 -0
  23. package/dist/locale-add/plan.js.map +1 -0
  24. package/dist/locale-add/validate.js +46 -0
  25. package/dist/locale-add/validate.js.map +1 -0
  26. package/dist/metadata.js +41 -0
  27. package/dist/metadata.js.map +1 -0
  28. package/dist/notion-translation-sources/apply.js +61 -0
  29. package/dist/notion-translation-sources/apply.js.map +1 -0
  30. package/dist/notion-translation-sources/index.js +3 -0
  31. package/dist/notion-translation-sources/index.js.map +1 -0
  32. package/dist/notion-translation-sources/plan.js +33 -0
  33. package/dist/notion-translation-sources/plan.js.map +1 -0
  34. package/dist/notionx-source.js +142 -0
  35. package/dist/notionx-source.js.map +1 -0
  36. package/dist/notionx-source.test.js +144 -0
  37. package/dist/notionx-source.test.js.map +1 -0
  38. package/dist/password.js +18 -0
  39. package/dist/password.js.map +1 -0
  40. package/dist/presets.js +83 -0
  41. package/dist/presets.js.map +1 -0
  42. package/dist/presets.test.js +50 -0
  43. package/dist/presets.test.js.map +1 -0
  44. package/dist/prompt.js +218 -0
  45. package/dist/prompt.js.map +1 -0
  46. package/dist/provision/cloudflare.js +236 -0
  47. package/dist/provision/cloudflare.js.map +1 -0
  48. package/dist/provision/dependencies.js +219 -0
  49. package/dist/provision/dependencies.js.map +1 -0
  50. package/dist/provision/index.js +681 -0
  51. package/dist/provision/index.js.map +1 -0
  52. package/dist/provision/index.test.js +54 -0
  53. package/dist/provision/index.test.js.map +1 -0
  54. package/dist/provision/inspect.js +109 -0
  55. package/dist/provision/inspect.js.map +1 -0
  56. package/dist/provision/inspect.test.js +75 -0
  57. package/dist/provision/inspect.test.js.map +1 -0
  58. package/dist/provision/notion.js +1981 -0
  59. package/dist/provision/notion.js.map +1 -0
  60. package/dist/provision/notion.test.js +542 -0
  61. package/dist/provision/notion.test.js.map +1 -0
  62. package/dist/provision/ntn-credentials.js +198 -0
  63. package/dist/provision/ntn-credentials.js.map +1 -0
  64. package/dist/provision/options.js +15 -0
  65. package/dist/provision/options.js.map +1 -0
  66. package/dist/provision/password-hash.js +78 -0
  67. package/dist/provision/password-hash.js.map +1 -0
  68. package/dist/provision/prompts.js +115 -0
  69. package/dist/provision/prompts.js.map +1 -0
  70. package/dist/provision/repair.js +48 -0
  71. package/dist/provision/repair.js.map +1 -0
  72. package/dist/provision/repair.test.js +141 -0
  73. package/dist/provision/repair.test.js.map +1 -0
  74. package/dist/provision/shell.js +84 -0
  75. package/dist/provision/shell.js.map +1 -0
  76. package/dist/provision/wire.js +78 -0
  77. package/dist/provision/wire.js.map +1 -0
  78. package/dist/registry/doctor.js +181 -0
  79. package/dist/registry/doctor.js.map +1 -0
  80. package/dist/registry/doctor.test.js +180 -0
  81. package/dist/registry/doctor.test.js.map +1 -0
  82. package/dist/registry/install.js +217 -0
  83. package/dist/registry/install.js.map +1 -0
  84. package/dist/registry/install.test.js +168 -0
  85. package/dist/registry/install.test.js.map +1 -0
  86. package/dist/registry/load-registry.js +24 -0
  87. package/dist/registry/load-registry.js.map +1 -0
  88. package/dist/registry/load-registry.test.js +59 -0
  89. package/dist/registry/load-registry.test.js.map +1 -0
  90. package/dist/registry/migration-planner.js +204 -0
  91. package/dist/registry/migration-planner.js.map +1 -0
  92. package/dist/registry/migration-planner.test.js +340 -0
  93. package/dist/registry/migration-planner.test.js.map +1 -0
  94. package/dist/registry/migrations-store.js +125 -0
  95. package/dist/registry/migrations-store.js.map +1 -0
  96. package/dist/registry/migrations-store.test.js +163 -0
  97. package/dist/registry/migrations-store.test.js.map +1 -0
  98. package/dist/registry/migrations-types.js +25 -0
  99. package/dist/registry/migrations-types.js.map +1 -0
  100. package/dist/registry/project-meta.js +84 -0
  101. package/dist/registry/project-meta.js.map +1 -0
  102. package/dist/registry/registry-items.js +354 -0
  103. package/dist/registry/registry-items.js.map +1 -0
  104. package/dist/registry/registry-items.test.js +99 -0
  105. package/dist/registry/registry-items.test.js.map +1 -0
  106. package/dist/registry/registry-store.js +232 -0
  107. package/dist/registry/registry-store.js.map +1 -0
  108. package/dist/registry/registry-store.test.js +136 -0
  109. package/dist/registry/registry-store.test.js.map +1 -0
  110. package/dist/registry/registry-types.js +18 -0
  111. package/dist/registry/registry-types.js.map +1 -0
  112. package/dist/registry/registry-types.test.js +146 -0
  113. package/dist/registry/registry-types.test.js.map +1 -0
  114. package/dist/registry/render-content-source-files.js +158 -0
  115. package/dist/registry/render-content-source-files.js.map +1 -0
  116. package/dist/registry/render-multi-source.js +296 -0
  117. package/dist/registry/render-multi-source.js.map +1 -0
  118. package/dist/registry/render-multi-source.test.js +110 -0
  119. package/dist/registry/render-multi-source.test.js.map +1 -0
  120. package/dist/registry/text-utils.js +42 -0
  121. package/dist/registry/text-utils.js.map +1 -0
  122. package/dist/registry/uninstall.js +250 -0
  123. package/dist/registry/uninstall.js.map +1 -0
  124. package/dist/registry/uninstall.test.js +264 -0
  125. package/dist/registry/uninstall.test.js.map +1 -0
  126. package/dist/registry/update.js +280 -0
  127. package/dist/registry/update.js.map +1 -0
  128. package/dist/registry/update.test.js +229 -0
  129. package/dist/registry/update.test.js.map +1 -0
  130. package/dist/render.js +549 -0
  131. package/dist/render.js.map +1 -0
  132. package/dist/render.test.js +414 -0
  133. package/dist/render.test.js.map +1 -0
  134. package/dist/templates/.dev.vars.example.tmpl +32 -0
  135. package/dist/templates/.gitignore.tmpl +58 -0
  136. package/dist/templates/README.md.tmpl +417 -0
  137. package/dist/templates/app/[slug]/page.tsx.tmpl +55 -0
  138. package/dist/templates/app/admin/account/page.tsx.tmpl +18 -0
  139. package/dist/templates/app/admin/content-models/page.tsx.tmpl +6 -0
  140. package/dist/templates/app/admin/layout.tsx.tmpl +90 -0
  141. package/dist/templates/app/admin/loading.tsx.tmpl +6 -0
  142. package/dist/templates/app/admin/page.tsx.tmpl +17 -0
  143. package/dist/templates/app/api/auth/google/callback/route.ts.tmpl +3 -0
  144. package/dist/templates/app/api/auth/google/route.ts.tmpl +3 -0
  145. package/dist/templates/app/api/auth/verify-email/route.ts.tmpl +3 -0
  146. package/dist/templates/app/api/auth/viewer/route.ts.tmpl +3 -0
  147. package/dist/templates/app/api/health/route.ts.tmpl +3 -0
  148. package/dist/templates/app/api/{{contentSourceId}}/[slug]/route.ts.tmpl +27 -0
  149. package/dist/templates/app/api/{{contentSourceId}}/route.ts.tmpl +18 -0
  150. package/dist/templates/app/globals.css.tmpl +109 -0
  151. package/dist/templates/app/layout.tsx.tmpl +56 -0
  152. package/dist/templates/app/login/page.tsx.tmpl +154 -0
  153. package/dist/templates/app/page.fallback.tsx.tmpl +31 -0
  154. package/dist/templates/app/page.tsx.tmpl +42 -0
  155. package/dist/templates/app/register/page.tsx.tmpl +138 -0
  156. package/dist/templates/app/{{contentSourceListPath}}/[slug]/page.tsx.tmpl +113 -0
  157. package/dist/templates/app/{{contentSourceListPath}}/page.tsx.tmpl +74 -0
  158. package/dist/templates/components/content/post-card.tsx.tmpl +80 -0
  159. package/dist/templates/components/notion-blocks.tsx.tmpl +668 -0
  160. package/dist/templates/components/page-blocks/feature-grid-block.tsx.tmpl +68 -0
  161. package/dist/templates/components/page-blocks/hero-block.tsx.tmpl +73 -0
  162. package/dist/templates/components/page-blocks/latest-posts-block.tsx.tmpl +59 -0
  163. package/dist/templates/components/page-blocks/story-block.tsx.tmpl +70 -0
  164. package/dist/templates/components/page-blocks.fallback.tsx.tmpl +17 -0
  165. package/dist/templates/components/page-blocks.tsx.tmpl +32 -0
  166. package/dist/templates/components/search/search-dialog.tsx.tmpl +171 -0
  167. package/dist/templates/components/site/locale-switcher.tsx.tmpl +65 -0
  168. package/dist/templates/components/site/site-footer.tsx.tmpl +106 -0
  169. package/dist/templates/components/site/site-header.tsx.tmpl +80 -0
  170. package/dist/templates/components/site/site-shell.tsx.tmpl +20 -0
  171. package/dist/templates/components/site/theme-bootstrap.tsx.tmpl +51 -0
  172. package/dist/templates/components/theme-provider.tsx.tmpl +14 -0
  173. package/dist/templates/components/theme-toggle.tsx.tmpl +38 -0
  174. package/dist/templates/components/ui/accordion.tsx.tmpl +56 -0
  175. package/dist/templates/components/ui/alert.tsx.tmpl +59 -0
  176. package/dist/templates/components/ui/aspect-ratio.tsx.tmpl +8 -0
  177. package/dist/templates/components/ui/avatar.tsx.tmpl +44 -0
  178. package/dist/templates/components/ui/badge.tsx.tmpl +33 -0
  179. package/dist/templates/components/ui/button.tsx.tmpl +56 -0
  180. package/dist/templates/components/ui/card.tsx.tmpl +61 -0
  181. package/dist/templates/components/ui/checkbox.tsx.tmpl +28 -0
  182. package/dist/templates/components/ui/dialog.tsx.tmpl +104 -0
  183. package/dist/templates/components/ui/dropdown-menu.tsx.tmpl +183 -0
  184. package/dist/templates/components/ui/input.tsx.tmpl +21 -0
  185. package/dist/templates/components/ui/label.tsx.tmpl +25 -0
  186. package/dist/templates/components/ui/popover.tsx.tmpl +30 -0
  187. package/dist/templates/components/ui/radio-group.tsx.tmpl +44 -0
  188. package/dist/templates/components/ui/select.tsx.tmpl +150 -0
  189. package/dist/templates/components/ui/separator.tsx.tmpl +30 -0
  190. package/dist/templates/components/ui/sheet.tsx.tmpl +125 -0
  191. package/dist/templates/components/ui/skeleton.tsx.tmpl +15 -0
  192. package/dist/templates/components/ui/sonner.tsx.tmpl +30 -0
  193. package/dist/templates/components/ui/switch.tsx.tmpl +29 -0
  194. package/dist/templates/components/ui/table.tsx.tmpl +107 -0
  195. package/dist/templates/components/ui/tabs.tsx.tmpl +55 -0
  196. package/dist/templates/components/ui/textarea.tsx.tmpl +24 -0
  197. package/dist/templates/components/ui/tooltip.tsx.tmpl +30 -0
  198. package/dist/templates/components.json.tmpl +21 -0
  199. package/dist/templates/env.d.ts.tmpl +32 -0
  200. package/dist/templates/lib/admin/actions.ts.tmpl +43 -0
  201. package/dist/templates/lib/admin/context.tsx.tmpl +209 -0
  202. package/dist/templates/lib/admin/nav.ts.tmpl +23 -0
  203. package/dist/templates/lib/auth.config.fallback.ts.tmpl +10 -0
  204. package/dist/templates/lib/auth.config.ts.tmpl +45 -0
  205. package/dist/templates/lib/blocks/translations.ts.tmpl +44 -0
  206. package/dist/templates/lib/blog/translations.ts.tmpl +52 -0
  207. package/dist/templates/lib/content/models.ts.tmpl +53 -0
  208. package/dist/templates/lib/i18n/config.ts.tmpl +18 -0
  209. package/dist/templates/lib/i18n/index.ts.tmpl +1 -0
  210. package/dist/templates/lib/locale-contract/built-in.ts.tmpl +19 -0
  211. package/dist/templates/lib/locale-contract/index.ts.tmpl +3 -0
  212. package/dist/templates/lib/locale-contract/paths.ts.tmpl +29 -0
  213. package/dist/templates/lib/pages/model.ts.tmpl +16 -0
  214. package/dist/templates/lib/pages/source.ts.tmpl +566 -0
  215. package/dist/templates/lib/pages/translations.ts.tmpl +34 -0
  216. package/dist/templates/lib/search/config.fallback.ts.tmpl +11 -0
  217. package/dist/templates/lib/search/config.ts.tmpl +25 -0
  218. package/dist/templates/lib/site/config.ts.tmpl +120 -0
  219. package/dist/templates/lib/site/request-env.ts.tmpl +71 -0
  220. package/dist/templates/lib/site/settings.fallback.ts.tmpl +21 -0
  221. package/dist/templates/lib/site/settings.ts.tmpl +320 -0
  222. package/dist/templates/lib/site/translations.ts.tmpl +30 -0
  223. package/dist/templates/lib/utils.ts.tmpl +9 -0
  224. package/dist/templates/migrations/0001_init.sql.tmpl +57 -0
  225. package/dist/templates/migrations/0002_admin_seed.sql.tmpl +30 -0
  226. package/dist/templates/migrations/0003_search_index.sql.tmpl +29 -0
  227. package/dist/templates/next.config.ts.tmpl +18 -0
  228. package/dist/templates/package.json.tmpl +40 -0
  229. package/dist/templates/shims/cloudflare-workers-empty.mjs +4 -0
  230. package/dist/templates/shims/next-headers-empty.mjs +4 -0
  231. package/dist/templates/tests/smoke.test.ts.tmpl +83 -0
  232. package/dist/templates/tsconfig.json.tmpl +31 -0
  233. package/dist/templates/vite.config.ts.tmpl +53 -0
  234. package/dist/templates/vitest.config.ts.tmpl +13 -0
  235. package/dist/templates/worker/index.ts.tmpl +52 -0
  236. package/dist/templates/wrangler.jsonc.tmpl +44 -0
  237. package/dist/ui-presets.js +60 -0
  238. package/dist/ui-presets.js.map +1 -0
  239. package/package.json +60 -0
@@ -0,0 +1,250 @@
1
+ // packages/create-notionx-app/src/registry/uninstall.ts
2
+ //
3
+ // Core `notionx remove <id>` logic.
4
+ //
5
+ // Contract (mirrors `install.ts`):
6
+ // 1. Read `.notionx/registry.json`.
7
+ // 2. Reject if the item is not installed.
8
+ // 3. Refuse to remove an item that other items still depend on
9
+ // (e.g. can't remove `docs` if `search` requires it).
10
+ // 4. Re-render `lib/content/models.ts` without the removed item.
11
+ // 5. Update `registry.json` (drop the item; clear its file
12
+ // entries from `managedFiles`).
13
+ // 6. For `content-source` items: by default, do NOT delete the
14
+ // source's files from disk. Pass `purge: true` to delete the
15
+ // files recorded on `InstalledItem.files` at install time.
16
+ // 7. For `feature-module` items: render each file's
17
+ // `fallbackTemplate` to the same `path` (overwriting the
18
+ // Notion-backed version) so downstream importers keep
19
+ // compiling. Files without a `fallbackTemplate` are left on
20
+ // disk unless `purge: true` is passed.
21
+ // 8. Flip the item's `featureFlag` to `false` in the manifest
22
+ // (feature-module items only).
23
+ // 9. Never touch Notion or D1 data — surface cleanup as followup.
24
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
25
+ import path from "node:path";
26
+ import { getOfficialItem } from "./registry-items.js";
27
+ import { loadRegistry } from "./load-registry.js";
28
+ import { writeRegistryManifest } from "./registry-store.js";
29
+ import { readProjectMeta, rerenderModelsFile, } from "./project-meta.js";
30
+ export class UninstallError extends Error {
31
+ code;
32
+ constructor(message, code) {
33
+ super(message);
34
+ this.code = code;
35
+ this.name = "UninstallError";
36
+ }
37
+ }
38
+ /**
39
+ * Uninstall a registry item from the project at `projectDir`.
40
+ *
41
+ * Throws `UninstallError` on the common failure modes.
42
+ */
43
+ export async function uninstallItem(input) {
44
+ const { projectDir, templatesDir, itemId } = input;
45
+ const loaded = await loadRegistry(projectDir);
46
+ const manifest = loaded.manifest;
47
+ const installed = manifest.installed.find((i) => i.id === itemId);
48
+ if (!installed) {
49
+ throw new UninstallError(`"${itemId}" is not installed in this project. ` +
50
+ `Run \`notionx add ${itemId}\` to install it.`, "not-installed");
51
+ }
52
+ // Look up the catalog item to access `featureFlag` and
53
+ // `fallbackTemplate` metadata. The `InstalledItem` snapshot
54
+ // doesn't carry these — they live on the catalog `RegistryItem`.
55
+ const catalogItem = getOfficialItem(itemId);
56
+ // Refuse to remove an item that is `requires`d by something
57
+ // else still installed. The user must remove the dependent
58
+ // items first (e.g. `notionx remove search` before
59
+ // `notionx remove docs`). The `requires` list lives on the
60
+ // catalog's `RegistryItem`, not on `InstalledItem` — we look
61
+ // it up by id at check time.
62
+ const blockers = manifest.installed.filter((i) => {
63
+ const catalog = getOfficialItem(i.id);
64
+ if (!catalog)
65
+ return false;
66
+ return (catalog.requires ?? []).some((req) => req.id === itemId);
67
+ });
68
+ if (blockers.length > 0) {
69
+ const names = blockers.map((b) => b.id).join(", ");
70
+ throw new UninstallError(`Cannot remove "${itemId}" — required by: ${names}. ` +
71
+ `Remove those first.`, "still-required");
72
+ }
73
+ // The exact file list to purge: prefer the snapshot stored on
74
+ // `InstalledItem.files` (accurate). Fall back to an empty list
75
+ // for items installed before the `files` field existed.
76
+ const purgeable = installed.files ?? [];
77
+ // Compute the next manifest: drop the item, drop its file
78
+ // entries from `managedFiles`, and flip the feature flag to
79
+ // `false` if this is a feature-module item.
80
+ const nextManifest = {
81
+ ...manifest,
82
+ installed: manifest.installed.filter((i) => i.id !== itemId),
83
+ managedFiles: clearItemFromManagedFiles(manifest, installed),
84
+ ...applyFeatureFlagForUninstall(catalogItem),
85
+ };
86
+ // Compute the `internalSources` flags to pass to the renderers.
87
+ // For feature-module items, the flag being uninstalled flips to
88
+ // `false` so the generated `models.ts` drops the corresponding
89
+ // internal singleton source. For other kinds, the flags are
90
+ // unchanged from the (already-updated) nextManifest.
91
+ const internalSourcesForRender = {
92
+ siteSettings: nextManifest.enableSiteSettings,
93
+ blocks: nextManifest.enableBlocks,
94
+ };
95
+ // Re-render `lib/content/models.ts` to drop the source from
96
+ // `contentSources` / `managedContentSources` (and, for
97
+ // feature-module items, the internal singleton source).
98
+ const project = await readProjectMeta(projectDir, manifest);
99
+ const modelsResult = await rerenderModelsFile({
100
+ projectDir,
101
+ templatesDir,
102
+ project,
103
+ installed: nextManifest.installed,
104
+ internalSources: internalSourcesForRender,
105
+ });
106
+ // For feature-module items, render the fallback templates to
107
+ // the same paths so downstream importers keep compiling. The
108
+ // fallback list is computed up-front so dry-run can report it.
109
+ const fallbackFiles = catalogItem
110
+ ? collectFallbackFiles(catalogItem)
111
+ : [];
112
+ const fallbackRenderedFiles = [];
113
+ const followup = buildFollowupTasks(installed, blockers, input.purge ?? false, catalogItem);
114
+ if (input.dryRun) {
115
+ return {
116
+ removedItem: installed,
117
+ rerenderedModels: modelsResult.wrote,
118
+ wroteManifest: false,
119
+ deletedFiles: [],
120
+ purgeableFiles: purgeable,
121
+ fallbackRenderedFiles: fallbackFiles.map((f) => f.path),
122
+ followup,
123
+ };
124
+ }
125
+ // Render fallback templates for feature-module items.
126
+ if (fallbackFiles.length > 0) {
127
+ const tokens = buildFallbackTokens(project);
128
+ for (const file of fallbackFiles) {
129
+ const tplAbs = path.join(templatesDir, file.fallbackTemplate);
130
+ let tpl;
131
+ try {
132
+ tpl = await readFile(tplAbs, "utf8");
133
+ }
134
+ catch {
135
+ // Fallback template missing — skip silently (file stays
136
+ // as-is on disk; user can hand-edit if needed).
137
+ continue;
138
+ }
139
+ const rendered = renderTemplate(tpl, tokens);
140
+ const destAbs = path.join(projectDir, file.path);
141
+ await mkdir(path.dirname(destAbs), { recursive: true });
142
+ await writeFile(destAbs, rendered, "utf8");
143
+ fallbackRenderedFiles.push(file.path);
144
+ }
145
+ }
146
+ // Delete files from disk if `--purge` was passed.
147
+ const deletedFiles = [];
148
+ if (input.purge) {
149
+ for (const relPath of purgeable) {
150
+ const absPath = path.join(projectDir, relPath);
151
+ try {
152
+ await rm(absPath, { recursive: true, force: true });
153
+ deletedFiles.push(relPath);
154
+ }
155
+ catch {
156
+ // File may have been manually deleted already — skip.
157
+ }
158
+ }
159
+ }
160
+ await writeRegistryManifest(projectDir, nextManifest);
161
+ return {
162
+ removedItem: installed,
163
+ rerenderedModels: modelsResult.wrote,
164
+ wroteManifest: true,
165
+ deletedFiles,
166
+ purgeableFiles: purgeable,
167
+ fallbackRenderedFiles,
168
+ followup,
169
+ };
170
+ }
171
+ // ---- internals ----
172
+ function clearItemFromManagedFiles(manifest, installed) {
173
+ // Use the authoritative file list recorded on `InstalledItem.files`
174
+ // at install time. The old path-heuristic (`/${itemId}/`) missed
175
+ // files whose paths don't contain the item id as a path segment
176
+ // (e.g. `migrations/0003_search_index.sql` for the `search` item).
177
+ const itemFiles = new Set(installed.files ?? []);
178
+ const filterPath = (p) => !itemFiles.has(p);
179
+ return {
180
+ platform: manifest.managedFiles.platform.filter(filterPath),
181
+ bridge: manifest.managedFiles.bridge.filter(filterPath),
182
+ user: manifest.managedFiles.user.filter(filterPath),
183
+ };
184
+ }
185
+ /**
186
+ * Return a partial manifest patch that flips the feature flag
187
+ * controlled by `item` to `false`. Returns `{}` for items without
188
+ * a `featureFlag` (content-source / platform-extension / unknown).
189
+ */
190
+ function applyFeatureFlagForUninstall(item) {
191
+ if (!item || !item.featureFlag)
192
+ return {};
193
+ return { [item.featureFlag]: false };
194
+ }
195
+ /**
196
+ * Collect the files on `item` that declare a `fallbackTemplate`.
197
+ * These are the files that get overwritten with the fallback
198
+ * content during uninstall (instead of being deleted).
199
+ */
200
+ function collectFallbackFiles(item) {
201
+ const out = [];
202
+ for (const file of item.files) {
203
+ if (file.fallbackTemplate) {
204
+ out.push({ path: file.path, fallbackTemplate: file.fallbackTemplate });
205
+ }
206
+ }
207
+ return out;
208
+ }
209
+ /**
210
+ * Build the minimal token map used by fallback templates. Fallback
211
+ * templates are intentionally simple (they return static content)
212
+ * so they only need the basic project tokens — no content-source
213
+ * or internal-source tokens.
214
+ */
215
+ function buildFallbackTokens(project) {
216
+ return {
217
+ projectName: project.projectName,
218
+ projectNameLower: project.projectName.toLowerCase(),
219
+ defaultLocale: project.defaultLocale,
220
+ supportedLocales: project.supportedLocales.join(", "),
221
+ supportedLocalesJson: JSON.stringify(project.supportedLocales),
222
+ notionxSource: project.notionxSource,
223
+ };
224
+ }
225
+ function renderTemplate(input, tokens) {
226
+ return input.replace(/\{\{\s*([a-zA-Z][a-zA-Z0-9_]*)\s*\}\}/g, (full, key) => {
227
+ const value = tokens[key];
228
+ if (typeof value === "string")
229
+ return value;
230
+ return full;
231
+ });
232
+ }
233
+ function buildFollowupTasks(item, blockers, purged, catalogItem) {
234
+ const out = [];
235
+ const isFeatureModule = catalogItem?.kind === "feature-module";
236
+ if (isFeatureModule) {
237
+ out.push(`The fallback version of "${item.id}" is now active. ` +
238
+ `Re-add with \`notionx add ${item.id}\` to restore the Notion-backed version.`);
239
+ }
240
+ else {
241
+ out.push(`The data source for "${item.id}" in Notion is untouched. ` +
242
+ `Re-add with \`notionx add ${item.id}\` to wire it back, or ` +
243
+ `manually archive the Notion data source.`);
244
+ }
245
+ if (!purged && blockers.length === 0) {
246
+ out.push(`Run \`notionx remove --purge ${item.id}\` to also delete the generated files.`);
247
+ }
248
+ return out;
249
+ }
250
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/registry/uninstall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,264 @@
1
+ // packages/create-notionx-app/src/registry/uninstall.test.ts
2
+ import { promises as fs } from "node:fs";
3
+ import { existsSync } from "node:fs";
4
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
+ import { resolveTemplatesDir } from "../render.js";
9
+ import { installItem } from "./install.js";
10
+ import { REGISTRY_SCHEMA_V2 } from "./registry-types.js";
11
+ import { UninstallError, uninstallItem } from "./uninstall.js";
12
+ async function writePackageJson(projectDir, name) {
13
+ await writeFile(path.join(projectDir, "package.json"), JSON.stringify({
14
+ name,
15
+ version: "0.0.0",
16
+ private: true,
17
+ dependencies: { "@notionx/core": "^2.0.0" },
18
+ }), "utf8");
19
+ }
20
+ async function writeV2ManifestWith(projectDir, installed, managedFiles = {}, overrides = {}) {
21
+ await mkdir(path.join(projectDir, ".notionx"), { recursive: true });
22
+ await writeFile(path.join(projectDir, ".notionx/registry.json"), JSON.stringify({
23
+ $schema: REGISTRY_SCHEMA_V2,
24
+ projectKind: "notionx",
25
+ projectName: "demo",
26
+ scaffoldVersion: "2.0.0",
27
+ notionxCore: "^2.0.0",
28
+ defaultLocale: "en",
29
+ supportedLocales: ["en"],
30
+ enableSiteSettings: true,
31
+ enableBlocks: true,
32
+ enableSearch: true,
33
+ contentSource: {
34
+ id: "blog",
35
+ title: "Blog",
36
+ fields: [],
37
+ },
38
+ compat: { mode: "v2-native" },
39
+ registries: {},
40
+ installed,
41
+ managedFiles: {
42
+ platform: managedFiles.platform ?? ["package.json"],
43
+ bridge: managedFiles.bridge ?? [],
44
+ user: managedFiles.user ?? [],
45
+ },
46
+ ...overrides,
47
+ }), "utf8");
48
+ }
49
+ describe("uninstallItem", () => {
50
+ let dir;
51
+ let templatesDir;
52
+ beforeEach(async () => {
53
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-uninstall-"));
54
+ await mkdir(path.join(dir, ".notionx"), { recursive: true });
55
+ templatesDir = await resolveTemplatesDir();
56
+ });
57
+ afterEach(async () => {
58
+ await rm(dir, { recursive: true, force: true });
59
+ });
60
+ it("removes docs from a v2 project and updates the manifest + models.ts", async () => {
61
+ await writePackageJson(dir, "demo");
62
+ await writeV2ManifestWith(dir, [
63
+ {
64
+ id: "blog",
65
+ kind: "content-source",
66
+ version: 1,
67
+ source: { kind: "official", name: "@notionx/official" },
68
+ params: { contentSourceId: "blog" },
69
+ installedAt: "2026-06-10T00:00:00.000Z",
70
+ },
71
+ {
72
+ id: "docs",
73
+ kind: "content-source",
74
+ version: 1,
75
+ source: { kind: "official", name: "@notionx/official" },
76
+ params: { contentSourceId: "docs", basePath: "/docs" },
77
+ installedAt: "2026-06-10T08:00:00.000Z",
78
+ },
79
+ ]);
80
+ const summary = await uninstallItem({
81
+ projectDir: dir,
82
+ templatesDir,
83
+ itemId: "docs",
84
+ });
85
+ expect(summary.removedItem.id).toBe("docs");
86
+ expect(summary.wroteManifest).toBe(true);
87
+ expect(summary.rerenderedModels).toBe(true);
88
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
89
+ const manifest = JSON.parse(manifestRaw);
90
+ expect(manifest.installed.map((i) => i.id)).toEqual(["blog"]);
91
+ const models = await fs.readFile(path.join(dir, "lib/content/models.ts"), "utf8");
92
+ expect(models).toContain("blogSource");
93
+ expect(models).not.toContain("docsSource");
94
+ });
95
+ it("rejects removing an item that is not installed", async () => {
96
+ await writePackageJson(dir, "demo");
97
+ await writeV2ManifestWith(dir, [
98
+ {
99
+ id: "blog",
100
+ kind: "content-source",
101
+ version: 1,
102
+ source: { kind: "official", name: "@notionx/official" },
103
+ params: { contentSourceId: "blog" },
104
+ installedAt: "2026-06-10T00:00:00.000Z",
105
+ },
106
+ ]);
107
+ try {
108
+ await uninstallItem({ projectDir: dir, templatesDir, itemId: "docs" });
109
+ throw new Error("expected uninstallItem to throw");
110
+ }
111
+ catch (err) {
112
+ expect(err).toBeInstanceOf(UninstallError);
113
+ expect(err.code).toBe("not-installed");
114
+ }
115
+ });
116
+ it("round-trip: install docs, then remove docs, leaves blog intact", async () => {
117
+ await writePackageJson(dir, "demo");
118
+ await writeV2ManifestWith(dir, [
119
+ {
120
+ id: "blog",
121
+ kind: "content-source",
122
+ version: 1,
123
+ source: { kind: "official", name: "@notionx/official" },
124
+ params: { contentSourceId: "blog" },
125
+ installedAt: "2026-06-10T00:00:00.000Z",
126
+ },
127
+ ]);
128
+ await installItem({ projectDir: dir, templatesDir, itemId: "docs" });
129
+ expect(existsSync(path.join(dir, "app/docs/page.tsx"))).toBe(true);
130
+ const summary = await uninstallItem({
131
+ projectDir: dir,
132
+ templatesDir,
133
+ itemId: "docs",
134
+ });
135
+ expect(summary.removedItem.id).toBe("docs");
136
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
137
+ const manifest = JSON.parse(manifestRaw);
138
+ expect(manifest.installed.map((i) => i.id)).toEqual(["blog"]);
139
+ // The Notion data source is *not* touched; the followup
140
+ // mentions how to clean it up manually.
141
+ const joined = summary.followup.join("\n");
142
+ expect(joined).toContain("Notion");
143
+ });
144
+ it("dry-run does not write the manifest", async () => {
145
+ await writePackageJson(dir, "demo");
146
+ await writeV2ManifestWith(dir, [
147
+ {
148
+ id: "blog",
149
+ kind: "content-source",
150
+ version: 1,
151
+ source: { kind: "official", name: "@notionx/official" },
152
+ params: { contentSourceId: "blog" },
153
+ installedAt: "2026-06-10T00:00:00.000Z",
154
+ },
155
+ {
156
+ id: "docs",
157
+ kind: "content-source",
158
+ version: 1,
159
+ source: { kind: "official", name: "@notionx/official" },
160
+ params: { contentSourceId: "docs", basePath: "/docs" },
161
+ installedAt: "2026-06-10T08:00:00.000Z",
162
+ },
163
+ ]);
164
+ const summary = await uninstallItem({
165
+ projectDir: dir,
166
+ templatesDir,
167
+ itemId: "docs",
168
+ dryRun: true,
169
+ });
170
+ expect(summary.wroteManifest).toBe(false);
171
+ // Manifest is unchanged
172
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
173
+ const manifest = JSON.parse(manifestRaw);
174
+ expect(manifest.installed.map((i) => i.id).sort()).toEqual([
175
+ "blog",
176
+ "docs",
177
+ ]);
178
+ });
179
+ it("feature-module: uninstalling site-settings renders fallback + flips flag", async () => {
180
+ await writePackageJson(dir, "demo");
181
+ await writeV2ManifestWith(dir, [
182
+ {
183
+ id: "blog",
184
+ kind: "content-source",
185
+ version: 1,
186
+ source: { kind: "official", name: "@notionx/official" },
187
+ params: { contentSourceId: "blog" },
188
+ installedAt: "2026-06-10T00:00:00.000Z",
189
+ },
190
+ {
191
+ id: "site-settings",
192
+ kind: "feature-module",
193
+ version: 1,
194
+ source: { kind: "official", name: "@notionx/official" },
195
+ params: {},
196
+ installedAt: "2026-06-10T00:00:00.000Z",
197
+ files: ["lib/site/settings.ts"],
198
+ },
199
+ ], {
200
+ bridge: ["lib/site/settings.ts"],
201
+ });
202
+ const summary = await uninstallItem({
203
+ projectDir: dir,
204
+ templatesDir,
205
+ itemId: "site-settings",
206
+ });
207
+ expect(summary.removedItem.id).toBe("site-settings");
208
+ expect(summary.fallbackRenderedFiles).toContain("lib/site/settings.ts");
209
+ expect(summary.deletedFiles).toEqual([]);
210
+ // The manifest flag should be flipped to false.
211
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
212
+ const manifest = JSON.parse(manifestRaw);
213
+ expect(manifest.installed.map((i) => i.id)).toEqual(["blog"]);
214
+ expect(manifest.enableSiteSettings).toBe(false);
215
+ // models.ts should no longer declare siteSettingsSource.
216
+ const models = await fs.readFile(path.join(dir, "lib/content/models.ts"), "utf8");
217
+ expect(models).not.toContain('export const siteSettingsSource');
218
+ expect(models).not.toContain('"site-settings"');
219
+ // The fallback file should be rendered (it returns the
220
+ // fallback site config directly).
221
+ const settings = await fs.readFile(path.join(dir, "lib/site/settings.ts"), "utf8");
222
+ expect(settings).toContain("fallbackSiteConfig");
223
+ // Followup should mention the fallback is active.
224
+ const joined = summary.followup.join("\n");
225
+ expect(joined).toContain("fallback");
226
+ });
227
+ it("feature-module: dry-run reports fallback files without writing", async () => {
228
+ await writePackageJson(dir, "demo");
229
+ await writeV2ManifestWith(dir, [
230
+ {
231
+ id: "blog",
232
+ kind: "content-source",
233
+ version: 1,
234
+ source: { kind: "official", name: "@notionx/official" },
235
+ params: { contentSourceId: "blog" },
236
+ installedAt: "2026-06-10T00:00:00.000Z",
237
+ },
238
+ {
239
+ id: "blocks",
240
+ kind: "feature-module",
241
+ version: 1,
242
+ source: { kind: "official", name: "@notionx/official" },
243
+ params: {},
244
+ installedAt: "2026-06-10T00:00:00.000Z",
245
+ files: ["components/page-blocks.tsx"],
246
+ },
247
+ ], {
248
+ bridge: ["components/page-blocks.tsx"],
249
+ });
250
+ const summary = await uninstallItem({
251
+ projectDir: dir,
252
+ templatesDir,
253
+ itemId: "blocks",
254
+ dryRun: true,
255
+ });
256
+ expect(summary.wroteManifest).toBe(false);
257
+ expect(summary.fallbackRenderedFiles).toContain("components/page-blocks.tsx");
258
+ // Manifest unchanged.
259
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
260
+ const manifest = JSON.parse(manifestRaw);
261
+ expect(manifest.enableBlocks).toBe(true);
262
+ });
263
+ });
264
+ //# sourceMappingURL=uninstall.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.test.js","sourceRoot":"","sources":["../../src/registry/uninstall.test.ts"],"names":[],"mappings":"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"}