@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,18 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ // Add project-specific headers, redirects, or rewrites here.
5
+
6
+ // Allow `next/image` to load cover art hosted by Notion and the
7
+ // common placeholder hosts the scaffolder seeds into sample posts.
8
+ // Adjust this list if you self-host covers on R2 or another CDN.
9
+ images: {
10
+ remotePatterns: [
11
+ { protocol: "https", hostname: "prod-files-secure.s3.us-west-2.amazonaws.com" },
12
+ { protocol: "https", hostname: "www.notion.so" },
13
+ { protocol: "https", hostname: "images.unsplash.com" },
14
+ ],
15
+ },
16
+ };
17
+
18
+ export default nextConfig;
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "{{projectNameLower}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "scripts": {
10
+ "dev": "vinext dev --port 3001",
11
+ "build": "vinext build",
12
+ "start": "vinext start",
13
+ "test": "vitest run",
14
+ "typecheck": "tsc --noEmit",
15
+ "lint": "eslint .",
16
+ "deploy": "vinext deploy",
17
+ "migrate:remote": "wrangler d1 migrations apply {{projectNameLower}}-db --remote"
18
+ },
19
+ "dependencies": {
20
+ {{dependenciesBlock}}
21
+ },
22
+ "devDependencies": {
23
+ "@cloudflare/vite-plugin": "^1.40.0",
24
+ "@cloudflare/workers-types": "^4",
25
+ "@notionx/create-notionx-app": "{{createNotionxAppVersion}}",
26
+ "@tailwindcss/vite": "^4.0.0",
27
+ "@types/node": "^22",
28
+ "@types/react": "^19",
29
+ "@types/react-dom": "^19",
30
+ "@vinext/cloudflare": "^0.1.2",
31
+ "@vitejs/plugin-rsc": "^0.5.0",
32
+ "ntn": "^0.16.0",
33
+ "tailwindcss": "^4.0.0",
34
+ "typescript": "^5.5",
35
+ "vinext": "^0.1.4",
36
+ "vite": "^8",
37
+ "vitest": "^2",
38
+ "wrangler": "^4"
39
+ }
40
+ }
@@ -0,0 +1,4 @@
1
+ // Minimal shim for `cloudflare:workers` in the client environment.
2
+ // The real module is resolved by the @cloudflare/vite-plugin in the
3
+ // rsc/ssr environments; the client bundle scan never executes this.
4
+ export {};
@@ -0,0 +1,4 @@
1
+ // Minimal shim for `next/headers` in the client environment.
2
+ // The real module is resolved by vinext/next in the rsc/ssr
3
+ // environments; the client bundle scan never executes this.
4
+ export {};
@@ -0,0 +1,83 @@
1
+ // Sanity test for the generated project. Verifies that
2
+ // `defineContentSource` registers the content source picked in the
3
+ // scaffolder prompt under the expected id and that the
4
+ // `@notionx/core` API surface is reachable from the generated
5
+ // project.
6
+
7
+ import { describe, it, expect } from "vitest";
8
+ import { {{contentSourceVarName}} } from "../lib/content/models.ts";
9
+ import { pageFields, pagesDataSourceEnv } from "../lib/pages/model.ts";
10
+ import {
11
+ defineContentSource,
12
+ getRegisteredSource,
13
+ getRegisteredSources,
14
+ type ContentSource,
15
+ } from "@notionx/core/content";
16
+
17
+ describe("content source registration", () => {
18
+ it("uses the id chosen in the scaffolder prompt", () => {
19
+ expect({{contentSourceVarName}}.id).toBe("{{contentSourceId}}");
20
+ });
21
+
22
+ it("is visible in notionx's registry", () => {
23
+ const ids = getRegisteredSources().map((s: ContentSource) => s.id);
24
+ expect(ids).toContain("{{contentSourceId}}");
25
+ });
26
+
27
+ it("returns the same instance from getRegisteredSource", () => {
28
+ expect(getRegisteredSource("{{contentSourceId}}")).toBe({{contentSourceVarName}});
29
+ });
30
+ });
31
+
32
+ describe("pages model", () => {
33
+ it("declares the Notion fields used for navigation and page rendering", () => {
34
+ expect(pagesDataSourceEnv).toBe("NOTION_PAGES_DATA_SOURCE_ID");
35
+ expect(pageFields.key).toBe("Key");
36
+ expect(pageFields.showInNav).toBe("Show in Nav");
37
+ expect(pageFields.footerOrder).toBe("Footer Order");
38
+ expect(pageFields.contentSource).toBe("Content Source");
39
+ });
40
+ });
41
+
42
+ describe("defineContentSource", () => {
43
+ it("is a re-registration-safe factory", () => {
44
+ const id = "smoke-{{contentSourceId}}";
45
+ const first = defineContentSource({
46
+ id,
47
+ kind: "article",
48
+ visibility: { public: true, admin: false },
49
+ source: {
50
+ type: "notion",
51
+ tokenEnv: "NOTION_TOKEN",
52
+ dataSourceEnv: "NOTION_DATA_SOURCE_ID",
53
+ fields: { title: "Title" },
54
+ query: { pageSize: 10 },
55
+ },
56
+ routes: {
57
+ listPath: "/smoke",
58
+ detailPath: "/smoke/[slug]",
59
+ detailParam: "slug",
60
+ },
61
+ ui: {
62
+ name: "Smoke",
63
+ pluralName: "Smokes",
64
+ navLabel: "Smoke",
65
+ listTitle: "Smoke",
66
+ listDescription: "Smoke",
67
+ emptyState: "empty",
68
+ },
69
+ capabilities: {
70
+ richBlocks: false,
71
+ coverImages: false,
72
+ gatedAssets: false,
73
+ },
74
+ });
75
+ expect(getRegisteredSource(id)).toBe(first);
76
+ const second = defineContentSource({
77
+ ...first,
78
+ ui: { ...first.ui, listTitle: "Updated" },
79
+ });
80
+ expect(getRegisteredSource(id)).toBe(second);
81
+ expect(getRegisteredSource(id)?.ui.listTitle).toBe("Updated");
82
+ });
83
+ });
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "plugins": [{ "name": "next" }],
18
+ "paths": {
19
+ "@/*": ["./*"]
20
+ }
21
+ },
22
+ "include": [
23
+ "next-env.d.ts",
24
+ "env.d.ts",
25
+ "**/*.ts",
26
+ "**/*.tsx",
27
+ ".next/types/**/*.ts",
28
+ ".next/dev/types/**/*.ts"
29
+ ],
30
+ "exclude": ["node_modules", "dist", ".wrangler"]
31
+ }
@@ -0,0 +1,53 @@
1
+ import { defineConfig, type Plugin } from "vite";
2
+ import vinext from "vinext";
3
+ import { cloudflare } from "@cloudflare/vite-plugin";
4
+ import tailwindcss from "@tailwindcss/vite";
5
+ import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ /**
9
+ * Provide empty shims for `cloudflare:workers` and `next/headers` in the
10
+ * client environment only. The cloudflare vite plugin resolves these
11
+ * virtual modules in the rsc/ssr environments, and workerd provides
12
+ * them at runtime. The client pre-bundle scan never executes the code;
13
+ * it just needs the import to resolve.
14
+ */
15
+ function serverOnlyShimsForClient(): Plugin {
16
+ return {
17
+ name: "vinext:server-only-shims-for-client",
18
+ enforce: "pre",
19
+ resolveId(id) {
20
+ if (this.environment?.name !== "client") return null;
21
+ if (id === "cloudflare:workers") {
22
+ return fileURLToPath(
23
+ new URL("./shims/cloudflare-workers-empty.mjs", import.meta.url),
24
+ );
25
+ }
26
+ if (id === "next/headers") {
27
+ return fileURLToPath(
28
+ new URL("./shims/next-headers-empty.mjs", import.meta.url),
29
+ );
30
+ }
31
+ return null;
32
+ },
33
+ };
34
+ }
35
+
36
+ export default defineConfig({
37
+ plugins: [
38
+ // `kvDataAdapter()` is a config-time builder: it just returns a
39
+ // serializable descriptor pointing at the runtime KV cache factory.
40
+ // The actual KV handler is instantiated lazily on the first request
41
+ // (it reads the `VINEXT_KV_CACHE` binding from `env`), so this is
42
+ // safe to call from `vite.config.ts` — it never touches workerd.
43
+ vinext({ cache: { data: kvDataAdapter() } }),
44
+ cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] } }),
45
+ tailwindcss(),
46
+ serverOnlyShimsForClient(),
47
+ ],
48
+ resolve: {
49
+ alias: {
50
+ "@": fileURLToPath(new URL(".", import.meta.url)),
51
+ },
52
+ },
53
+ });
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["tests/**/*.test.{ts,mjs}"],
7
+ },
8
+ resolve: {
9
+ alias: {
10
+ "@": new URL(".", import.meta.url).pathname,
11
+ },
12
+ },
13
+ });
@@ -0,0 +1,52 @@
1
+ // Cloudflare Worker entry point. Notionx-routed requests (health,
2
+ // storage, notion media) are handled by `createNotionxWorker`
3
+ // before falling through to the vinext app router.
4
+
5
+ import handler from "vinext/server/app-router-entry";
6
+ import { createNotionxWorker } from "@notionx/core/worker";
7
+ import { authConfig } from "../lib/auth.config";
8
+ import { searchAdapter } from "../lib/search/config";
9
+ import { adminNav } from "../lib/admin/nav";
10
+ import { managedContentSources } from "../lib/content/models";
11
+ import { siteConfig } from "../lib/site/config";
12
+ import { runWithRequestEnv } from "../lib/site/request-env";
13
+
14
+ const notionx = createNotionxWorker({
15
+ sources: [...managedContentSources],
16
+ adminNav,
17
+ // `authConfig` is `undefined` when auth is disabled (scaffolded
18
+ // with `--no-auth` or after `notionx remove auth`). The worker
19
+ // bootstrap handles `undefined` by skipping session/admin gates.
20
+ ...(authConfig ? { authConfig } : {}),
21
+ // `searchAdapter` is `undefined` when search is disabled
22
+ // (scaffolded with `--no-search` or after `notionx remove search`).
23
+ // The worker bootstrap handles `undefined` by not registering
24
+ // the `/api/search` route.
25
+ ...(searchAdapter ? { searchAdapter } : {}),
26
+ siteConfig: {
27
+ name: siteConfig.name,
28
+ description: siteConfig.description,
29
+ defaultLocale: siteConfig.defaultLocale,
30
+ locales: [...siteConfig.locales],
31
+ navigation: siteConfig.navigation.main as unknown as unknown[],
32
+ },
33
+ });
34
+
35
+ export default {
36
+ async fetch(
37
+ request: Request,
38
+ env: Env,
39
+ ctx: ExecutionContext
40
+ ): Promise<Response> {
41
+ // Make `env` reachable from any async code path (RSC server
42
+ // components, server actions, Notion content loaders, etc.) via
43
+ // `getRequestEnv()`. See `lib/site/request-env.ts` for the
44
+ // rationale — we cannot use `cloudflare:workers.getRequestContext`
45
+ // because that module does not export it.
46
+ return runWithRequestEnv(env, async () => {
47
+ const notionxResponse = await notionx.fetch(request, env, ctx);
48
+ if (notionxResponse) return notionxResponse;
49
+ return handler.fetch(request, env, ctx);
50
+ });
51
+ },
52
+ };
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "node_modules/wrangler/config-schema.json",
3
+ "name": "{{projectNameLower}}",
4
+ "compatibility_date": "2026-06-05",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "main": "./worker/index.ts",
7
+ "assets": {
8
+ "directory": "dist/client",
9
+ "not_found_handling": "none",
10
+ "binding": "ASSETS"
11
+ },
12
+ "images": {
13
+ "binding": "IMAGES"
14
+ },
15
+ "d1_databases": [
16
+ {
17
+ "binding": "DB",
18
+ "database_name": "{{projectNameLower}}-db",
19
+ "database_id": "REPLACE_WITH_D1_DATABASE_ID",
20
+ "migrations_dir": "migrations"
21
+ }
22
+ ],
23
+ "kv_namespaces": [
24
+ {
25
+ "binding": "CONTENT_CACHE",
26
+ "id": "REPLACE_WITH_KV_NAMESPACE_ID"
27
+ },
28
+ {
29
+ "binding": "VINEXT_KV_CACHE",
30
+ "id": "REPLACE_WITH_VINEXT_KV_NAMESPACE_ID"
31
+ }
32
+ ],
33
+ "r2_buckets": [
34
+ {
35
+ "binding": "ASSETS_BUCKET",
36
+ "bucket_name": "{{projectNameLower}}-assets"
37
+ }
38
+ ],
39
+ "vars": {
40
+ "SITE_URL": "https://{{projectNameLower}}.example.com",
41
+ "NOTION_BLOCKS_DATA_SOURCE_ID": "REPLACE_WITH_NOTION_BLOCKS_DATA_SOURCE_ID",
42
+ "NOTION_SITE_SETTINGS_DATA_SOURCE_ID": "REPLACE_WITH_NOTION_SITE_SETTINGS_DATA_SOURCE_ID"
43
+ }
44
+ }
@@ -0,0 +1,60 @@
1
+ // packages/create-notionx-app/src/ui-presets.ts
2
+ //
3
+ // Mirrors the component list from `presets.ts` for callers that
4
+ // need the raw array (e.g. the README's "What's included" section)
5
+ // plus dependency rendering helpers. The scaffolder only ships the
6
+ // "site" set, so there is no preset-selection layer here.
7
+ const SITE_PRESET_COMPONENTS = [
8
+ "accordion",
9
+ "alert",
10
+ "aspect-ratio",
11
+ "badge",
12
+ "button",
13
+ "card",
14
+ "dialog",
15
+ "dropdown-menu",
16
+ "input",
17
+ "label",
18
+ "separator",
19
+ "sheet",
20
+ "skeleton",
21
+ "table",
22
+ "tabs",
23
+ "tooltip",
24
+ ];
25
+ const BASE_DEPENDENCIES = {
26
+ "@radix-ui/react-slot": "^1.2.0",
27
+ "class-variance-authority": "^0.7.1",
28
+ clsx: "^2.1.1",
29
+ "lucide-react": "^0.460.0",
30
+ "next-themes": "^0.4.4",
31
+ "tailwind-merge": "^2.5.5",
32
+ "tailwindcss-animate": "^1.0.7",
33
+ };
34
+ const COMPONENT_DEPENDENCIES = {
35
+ accordion: { "@radix-ui/react-accordion": "^1.2.1" },
36
+ "aspect-ratio": { "@radix-ui/react-aspect-ratio": "^1.1.0" },
37
+ dialog: { "@radix-ui/react-dialog": "^1.1.2" },
38
+ "dropdown-menu": { "@radix-ui/react-dropdown-menu": "^2.1.2" },
39
+ label: { "@radix-ui/react-label": "^2.1.0" },
40
+ separator: { "@radix-ui/react-separator": "^1.1.0" },
41
+ sheet: { "@radix-ui/react-dialog": "^1.1.2" },
42
+ tabs: { "@radix-ui/react-tabs": "^1.1.1" },
43
+ tooltip: { "@radix-ui/react-tooltip": "^1.1.4" },
44
+ };
45
+ export function uiComponents() {
46
+ return SITE_PRESET_COMPONENTS;
47
+ }
48
+ export function uiDependencies() {
49
+ const deps = { ...BASE_DEPENDENCIES };
50
+ for (const component of SITE_PRESET_COMPONENTS) {
51
+ Object.assign(deps, COMPONENT_DEPENDENCIES[component]);
52
+ }
53
+ return Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a.localeCompare(b)));
54
+ }
55
+ export function renderDependencyLines(deps, indent = " ") {
56
+ return Object.entries(deps)
57
+ .map(([name, version]) => `${indent}${JSON.stringify(name)}: ${JSON.stringify(version)},`)
58
+ .join("\n");
59
+ }
60
+ //# sourceMappingURL=ui-presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-presets.js","sourceRoot":"","sources":["../src/ui-presets.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,gEAAgE;AAChE,mEAAmE;AACnE,mEAAmE;AACnE,0DAA0D;AAE1D,MAAM,sBAAsB,GAAG;IAC7B,WAAW;IACX,OAAO;IACP,cAAc;IACd,OAAO;IACP,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,eAAe;IACf,OAAO;IACP,OAAO;IACP,WAAW;IACX,OAAO;IACP,UAAU;IACV,OAAO;IACP,MAAM;IACN,SAAS;CACD,CAAC;AAEX,MAAM,iBAAiB,GAA2B;IAChD,sBAAsB,EAAE,QAAQ;IAChC,0BAA0B,EAAE,QAAQ;IACpC,IAAI,EAAE,QAAQ;IACd,cAAc,EAAE,UAAU;IAC1B,aAAa,EAAE,QAAQ;IACvB,gBAAgB,EAAE,QAAQ;IAC1B,qBAAqB,EAAE,QAAQ;CAChC,CAAC;AAEF,MAAM,sBAAsB,GAA2C;IACrE,SAAS,EAAE,EAAE,2BAA2B,EAAE,QAAQ,EAAE;IACpD,cAAc,EAAE,EAAE,8BAA8B,EAAE,QAAQ,EAAE;IAC5D,MAAM,EAAE,EAAE,wBAAwB,EAAE,QAAQ,EAAE;IAC9C,eAAe,EAAE,EAAE,+BAA+B,EAAE,QAAQ,EAAE;IAC9D,KAAK,EAAE,EAAE,uBAAuB,EAAE,QAAQ,EAAE;IAC5C,SAAS,EAAE,EAAE,2BAA2B,EAAE,QAAQ,EAAE;IACpD,KAAK,EAAE,EAAE,wBAAwB,EAAE,QAAQ,EAAE;IAC7C,IAAI,EAAE,EAAE,sBAAsB,EAAE,QAAQ,EAAE;IAC1C,OAAO,EAAE,EAAE,yBAAyB,EAAE,QAAQ,EAAE;CACjD,CAAC;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAA2B,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC9D,KAAK,MAAM,SAAS,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAC5D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,IAA4B,EAC5B,MAAM,GAAG,MAAM;IAEf,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACxB,GAAG,CACF,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAClB,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAClE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@notionx/create-notionx-app",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a new vinext (Next.js on Cloudflare Workers + D1 + R2) project backed by @notionx/core, with one-step provisioning of D1 / KV / R2 / Turnstile / Notion / Resend / Google OAuth.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-notionx-app": "./dist/index.js",
8
+ "notionx": "./dist/cli-notionx.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "keywords": [
15
+ "vinext",
16
+ "notionx",
17
+ "notionx",
18
+ "nextjs",
19
+ "cloudflare",
20
+ "workers",
21
+ "d1",
22
+ "r2",
23
+ "turnstile",
24
+ "notion",
25
+ "scaffold",
26
+ "create-notionx-app"
27
+ ],
28
+ "license": "MIT",
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "dependencies": {
33
+ "@clack/prompts": "^0.7"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22",
37
+ "tsx": "^4",
38
+ "typescript": "^5.5",
39
+ "vitest": "^3.2.4"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org/"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/digwis/nextion.git",
48
+ "directory": "packages/create-notionx-app"
49
+ },
50
+ "author": "zhaofilms",
51
+ "homepage": "https://github.com/digwis/nextion#readme",
52
+ "bugs": {
53
+ "url": "https://github.com/digwis/nextion/issues"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc -p tsconfig.json && node scripts/copy-templates.mjs",
57
+ "dev": "tsx src/index.ts",
58
+ "test": "vitest run"
59
+ }
60
+ }