@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.
- package/README.md +139 -0
- package/dist/answers.js +332 -0
- package/dist/answers.js.map +1 -0
- package/dist/cli-notionx.js +388 -0
- package/dist/cli-notionx.js.map +1 -0
- package/dist/cli-notionx.test.js +277 -0
- package/dist/cli-notionx.test.js.map +1 -0
- package/dist/diff.js +40 -0
- package/dist/diff.js.map +1 -0
- package/dist/diff.test.js +90 -0
- package/dist/diff.test.js.map +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/locale-add/apply.js +39 -0
- package/dist/locale-add/apply.js.map +1 -0
- package/dist/locale-add/format.js +38 -0
- package/dist/locale-add/format.js.map +1 -0
- package/dist/locale-add/list.js +44 -0
- package/dist/locale-add/list.js.map +1 -0
- package/dist/locale-add/list.test.js +45 -0
- package/dist/locale-add/list.test.js.map +1 -0
- package/dist/locale-add/plan.js +128 -0
- package/dist/locale-add/plan.js.map +1 -0
- package/dist/locale-add/validate.js +46 -0
- package/dist/locale-add/validate.js.map +1 -0
- package/dist/metadata.js +41 -0
- package/dist/metadata.js.map +1 -0
- package/dist/notion-translation-sources/apply.js +61 -0
- package/dist/notion-translation-sources/apply.js.map +1 -0
- package/dist/notion-translation-sources/index.js +3 -0
- package/dist/notion-translation-sources/index.js.map +1 -0
- package/dist/notion-translation-sources/plan.js +33 -0
- package/dist/notion-translation-sources/plan.js.map +1 -0
- package/dist/notionx-source.js +142 -0
- package/dist/notionx-source.js.map +1 -0
- package/dist/notionx-source.test.js +144 -0
- package/dist/notionx-source.test.js.map +1 -0
- package/dist/password.js +18 -0
- package/dist/password.js.map +1 -0
- package/dist/presets.js +83 -0
- package/dist/presets.js.map +1 -0
- package/dist/presets.test.js +50 -0
- package/dist/presets.test.js.map +1 -0
- package/dist/prompt.js +218 -0
- package/dist/prompt.js.map +1 -0
- package/dist/provision/cloudflare.js +236 -0
- package/dist/provision/cloudflare.js.map +1 -0
- package/dist/provision/dependencies.js +219 -0
- package/dist/provision/dependencies.js.map +1 -0
- package/dist/provision/index.js +681 -0
- package/dist/provision/index.js.map +1 -0
- package/dist/provision/index.test.js +54 -0
- package/dist/provision/index.test.js.map +1 -0
- package/dist/provision/inspect.js +109 -0
- package/dist/provision/inspect.js.map +1 -0
- package/dist/provision/inspect.test.js +75 -0
- package/dist/provision/inspect.test.js.map +1 -0
- package/dist/provision/notion.js +1981 -0
- package/dist/provision/notion.js.map +1 -0
- package/dist/provision/notion.test.js +542 -0
- package/dist/provision/notion.test.js.map +1 -0
- package/dist/provision/ntn-credentials.js +198 -0
- package/dist/provision/ntn-credentials.js.map +1 -0
- package/dist/provision/options.js +15 -0
- package/dist/provision/options.js.map +1 -0
- package/dist/provision/password-hash.js +78 -0
- package/dist/provision/password-hash.js.map +1 -0
- package/dist/provision/prompts.js +115 -0
- package/dist/provision/prompts.js.map +1 -0
- package/dist/provision/repair.js +48 -0
- package/dist/provision/repair.js.map +1 -0
- package/dist/provision/repair.test.js +141 -0
- package/dist/provision/repair.test.js.map +1 -0
- package/dist/provision/shell.js +84 -0
- package/dist/provision/shell.js.map +1 -0
- package/dist/provision/wire.js +78 -0
- package/dist/provision/wire.js.map +1 -0
- package/dist/registry/doctor.js +181 -0
- package/dist/registry/doctor.js.map +1 -0
- package/dist/registry/doctor.test.js +180 -0
- package/dist/registry/doctor.test.js.map +1 -0
- package/dist/registry/install.js +217 -0
- package/dist/registry/install.js.map +1 -0
- package/dist/registry/install.test.js +168 -0
- package/dist/registry/install.test.js.map +1 -0
- package/dist/registry/load-registry.js +24 -0
- package/dist/registry/load-registry.js.map +1 -0
- package/dist/registry/load-registry.test.js +59 -0
- package/dist/registry/load-registry.test.js.map +1 -0
- package/dist/registry/migration-planner.js +204 -0
- package/dist/registry/migration-planner.js.map +1 -0
- package/dist/registry/migration-planner.test.js +340 -0
- package/dist/registry/migration-planner.test.js.map +1 -0
- package/dist/registry/migrations-store.js +125 -0
- package/dist/registry/migrations-store.js.map +1 -0
- package/dist/registry/migrations-store.test.js +163 -0
- package/dist/registry/migrations-store.test.js.map +1 -0
- package/dist/registry/migrations-types.js +25 -0
- package/dist/registry/migrations-types.js.map +1 -0
- package/dist/registry/project-meta.js +84 -0
- package/dist/registry/project-meta.js.map +1 -0
- package/dist/registry/registry-items.js +354 -0
- package/dist/registry/registry-items.js.map +1 -0
- package/dist/registry/registry-items.test.js +99 -0
- package/dist/registry/registry-items.test.js.map +1 -0
- package/dist/registry/registry-store.js +232 -0
- package/dist/registry/registry-store.js.map +1 -0
- package/dist/registry/registry-store.test.js +136 -0
- package/dist/registry/registry-store.test.js.map +1 -0
- package/dist/registry/registry-types.js +18 -0
- package/dist/registry/registry-types.js.map +1 -0
- package/dist/registry/registry-types.test.js +146 -0
- package/dist/registry/registry-types.test.js.map +1 -0
- package/dist/registry/render-content-source-files.js +158 -0
- package/dist/registry/render-content-source-files.js.map +1 -0
- package/dist/registry/render-multi-source.js +296 -0
- package/dist/registry/render-multi-source.js.map +1 -0
- package/dist/registry/render-multi-source.test.js +110 -0
- package/dist/registry/render-multi-source.test.js.map +1 -0
- package/dist/registry/text-utils.js +42 -0
- package/dist/registry/text-utils.js.map +1 -0
- package/dist/registry/uninstall.js +250 -0
- package/dist/registry/uninstall.js.map +1 -0
- package/dist/registry/uninstall.test.js +264 -0
- package/dist/registry/uninstall.test.js.map +1 -0
- package/dist/registry/update.js +280 -0
- package/dist/registry/update.js.map +1 -0
- package/dist/registry/update.test.js +229 -0
- package/dist/registry/update.test.js.map +1 -0
- package/dist/render.js +549 -0
- package/dist/render.js.map +1 -0
- package/dist/render.test.js +414 -0
- package/dist/render.test.js.map +1 -0
- package/dist/templates/.dev.vars.example.tmpl +32 -0
- package/dist/templates/.gitignore.tmpl +58 -0
- package/dist/templates/README.md.tmpl +417 -0
- package/dist/templates/app/[slug]/page.tsx.tmpl +55 -0
- package/dist/templates/app/admin/account/page.tsx.tmpl +18 -0
- package/dist/templates/app/admin/content-models/page.tsx.tmpl +6 -0
- package/dist/templates/app/admin/layout.tsx.tmpl +90 -0
- package/dist/templates/app/admin/loading.tsx.tmpl +6 -0
- package/dist/templates/app/admin/page.tsx.tmpl +17 -0
- package/dist/templates/app/api/auth/google/callback/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/google/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/verify-email/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/viewer/route.ts.tmpl +3 -0
- package/dist/templates/app/api/health/route.ts.tmpl +3 -0
- package/dist/templates/app/api/{{contentSourceId}}/[slug]/route.ts.tmpl +27 -0
- package/dist/templates/app/api/{{contentSourceId}}/route.ts.tmpl +18 -0
- package/dist/templates/app/globals.css.tmpl +109 -0
- package/dist/templates/app/layout.tsx.tmpl +56 -0
- package/dist/templates/app/login/page.tsx.tmpl +154 -0
- package/dist/templates/app/page.fallback.tsx.tmpl +31 -0
- package/dist/templates/app/page.tsx.tmpl +42 -0
- package/dist/templates/app/register/page.tsx.tmpl +138 -0
- package/dist/templates/app/{{contentSourceListPath}}/[slug]/page.tsx.tmpl +113 -0
- package/dist/templates/app/{{contentSourceListPath}}/page.tsx.tmpl +74 -0
- package/dist/templates/components/content/post-card.tsx.tmpl +80 -0
- package/dist/templates/components/notion-blocks.tsx.tmpl +668 -0
- package/dist/templates/components/page-blocks/feature-grid-block.tsx.tmpl +68 -0
- package/dist/templates/components/page-blocks/hero-block.tsx.tmpl +73 -0
- package/dist/templates/components/page-blocks/latest-posts-block.tsx.tmpl +59 -0
- package/dist/templates/components/page-blocks/story-block.tsx.tmpl +70 -0
- package/dist/templates/components/page-blocks.fallback.tsx.tmpl +17 -0
- package/dist/templates/components/page-blocks.tsx.tmpl +32 -0
- package/dist/templates/components/search/search-dialog.tsx.tmpl +171 -0
- package/dist/templates/components/site/locale-switcher.tsx.tmpl +65 -0
- package/dist/templates/components/site/site-footer.tsx.tmpl +106 -0
- package/dist/templates/components/site/site-header.tsx.tmpl +80 -0
- package/dist/templates/components/site/site-shell.tsx.tmpl +20 -0
- package/dist/templates/components/site/theme-bootstrap.tsx.tmpl +51 -0
- package/dist/templates/components/theme-provider.tsx.tmpl +14 -0
- package/dist/templates/components/theme-toggle.tsx.tmpl +38 -0
- package/dist/templates/components/ui/accordion.tsx.tmpl +56 -0
- package/dist/templates/components/ui/alert.tsx.tmpl +59 -0
- package/dist/templates/components/ui/aspect-ratio.tsx.tmpl +8 -0
- package/dist/templates/components/ui/avatar.tsx.tmpl +44 -0
- package/dist/templates/components/ui/badge.tsx.tmpl +33 -0
- package/dist/templates/components/ui/button.tsx.tmpl +56 -0
- package/dist/templates/components/ui/card.tsx.tmpl +61 -0
- package/dist/templates/components/ui/checkbox.tsx.tmpl +28 -0
- package/dist/templates/components/ui/dialog.tsx.tmpl +104 -0
- package/dist/templates/components/ui/dropdown-menu.tsx.tmpl +183 -0
- package/dist/templates/components/ui/input.tsx.tmpl +21 -0
- package/dist/templates/components/ui/label.tsx.tmpl +25 -0
- package/dist/templates/components/ui/popover.tsx.tmpl +30 -0
- package/dist/templates/components/ui/radio-group.tsx.tmpl +44 -0
- package/dist/templates/components/ui/select.tsx.tmpl +150 -0
- package/dist/templates/components/ui/separator.tsx.tmpl +30 -0
- package/dist/templates/components/ui/sheet.tsx.tmpl +125 -0
- package/dist/templates/components/ui/skeleton.tsx.tmpl +15 -0
- package/dist/templates/components/ui/sonner.tsx.tmpl +30 -0
- package/dist/templates/components/ui/switch.tsx.tmpl +29 -0
- package/dist/templates/components/ui/table.tsx.tmpl +107 -0
- package/dist/templates/components/ui/tabs.tsx.tmpl +55 -0
- package/dist/templates/components/ui/textarea.tsx.tmpl +24 -0
- package/dist/templates/components/ui/tooltip.tsx.tmpl +30 -0
- package/dist/templates/components.json.tmpl +21 -0
- package/dist/templates/env.d.ts.tmpl +32 -0
- package/dist/templates/lib/admin/actions.ts.tmpl +43 -0
- package/dist/templates/lib/admin/context.tsx.tmpl +209 -0
- package/dist/templates/lib/admin/nav.ts.tmpl +23 -0
- package/dist/templates/lib/auth.config.fallback.ts.tmpl +10 -0
- package/dist/templates/lib/auth.config.ts.tmpl +45 -0
- package/dist/templates/lib/blocks/translations.ts.tmpl +44 -0
- package/dist/templates/lib/blog/translations.ts.tmpl +52 -0
- package/dist/templates/lib/content/models.ts.tmpl +53 -0
- package/dist/templates/lib/i18n/config.ts.tmpl +18 -0
- package/dist/templates/lib/i18n/index.ts.tmpl +1 -0
- package/dist/templates/lib/locale-contract/built-in.ts.tmpl +19 -0
- package/dist/templates/lib/locale-contract/index.ts.tmpl +3 -0
- package/dist/templates/lib/locale-contract/paths.ts.tmpl +29 -0
- package/dist/templates/lib/pages/model.ts.tmpl +16 -0
- package/dist/templates/lib/pages/source.ts.tmpl +566 -0
- package/dist/templates/lib/pages/translations.ts.tmpl +34 -0
- package/dist/templates/lib/search/config.fallback.ts.tmpl +11 -0
- package/dist/templates/lib/search/config.ts.tmpl +25 -0
- package/dist/templates/lib/site/config.ts.tmpl +120 -0
- package/dist/templates/lib/site/request-env.ts.tmpl +71 -0
- package/dist/templates/lib/site/settings.fallback.ts.tmpl +21 -0
- package/dist/templates/lib/site/settings.ts.tmpl +320 -0
- package/dist/templates/lib/site/translations.ts.tmpl +30 -0
- package/dist/templates/lib/utils.ts.tmpl +9 -0
- package/dist/templates/migrations/0001_init.sql.tmpl +57 -0
- package/dist/templates/migrations/0002_admin_seed.sql.tmpl +30 -0
- package/dist/templates/migrations/0003_search_index.sql.tmpl +29 -0
- package/dist/templates/next.config.ts.tmpl +18 -0
- package/dist/templates/package.json.tmpl +40 -0
- package/dist/templates/shims/cloudflare-workers-empty.mjs +4 -0
- package/dist/templates/shims/next-headers-empty.mjs +4 -0
- package/dist/templates/tests/smoke.test.ts.tmpl +83 -0
- package/dist/templates/tsconfig.json.tmpl +31 -0
- package/dist/templates/vite.config.ts.tmpl +53 -0
- package/dist/templates/vitest.config.ts.tmpl +13 -0
- package/dist/templates/worker/index.ts.tmpl +52 -0
- package/dist/templates/wrangler.jsonc.tmpl +44 -0
- package/dist/ui-presets.js +60 -0
- package/dist/ui-presets.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// packages/create-notionx-app/src/registry/render-content-source-files.ts
|
|
2
|
+
//
|
|
3
|
+
// Render all files declared by a `RegistryItem` into the project.
|
|
4
|
+
//
|
|
5
|
+
// Each `RegistryFile` in the item's `files` array may carry a
|
|
6
|
+
// `template` path (relative to the templates directory). The
|
|
7
|
+
// renderer reads that template, substitutes tokens, and writes
|
|
8
|
+
// the result to the file's `path` (relative to the project root).
|
|
9
|
+
// Files without a `template` are skipped.
|
|
10
|
+
//
|
|
11
|
+
// The token set is the same multi-source token map used by
|
|
12
|
+
// `render.ts` at scaffold time, plus per-item overrides so the
|
|
13
|
+
// template sees *this* item's id/path/var-name (not the
|
|
14
|
+
// alphabetically-first installed source).
|
|
15
|
+
import { promises as fs } from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { buildMultiSourceTokenMap } from "./render-multi-source.js";
|
|
18
|
+
import { toCamel, titleCase } from "./text-utils.js";
|
|
19
|
+
/**
|
|
20
|
+
* Render all files declared by `input.item` that have a `template`
|
|
21
|
+
* path. Returns one `RenderedContentSourceFile` per rendered file.
|
|
22
|
+
*
|
|
23
|
+
* For `content-source` items, per-item tokens (contentSourceId,
|
|
24
|
+
* contentSourceListPath, etc.) are computed and injected. For
|
|
25
|
+
* `feature-module` items, only the shared multi-source tokens are
|
|
26
|
+
* injected — feature-module templates don't use content-source
|
|
27
|
+
* tokens.
|
|
28
|
+
*
|
|
29
|
+
* `internalSources` controls which internal singleton sources
|
|
30
|
+
* (siteSettingsSource, blocksSource) appear in the generated
|
|
31
|
+
* `models.ts` block. Callers should pass the manifest's
|
|
32
|
+
* `enableSiteSettings` / `enableBlocks` flags (adjusted for the
|
|
33
|
+
* item being installed if it's a feature-module).
|
|
34
|
+
*/
|
|
35
|
+
export async function renderContentSourceFiles(input) {
|
|
36
|
+
const installedWithNew = dedupeInstalled([
|
|
37
|
+
...input.installed,
|
|
38
|
+
{
|
|
39
|
+
id: input.item.id,
|
|
40
|
+
kind: input.item.kind,
|
|
41
|
+
version: input.item.version,
|
|
42
|
+
source: input.item.source,
|
|
43
|
+
params: input.item.params,
|
|
44
|
+
installedAt: new Date(0).toISOString(),
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
// Multi-source bookkeeping tokens (sources block, env vars, var
|
|
48
|
+
// names) — independent of which item is being rendered.
|
|
49
|
+
const tokens = buildMultiSourceTokenMap({
|
|
50
|
+
project: input.project,
|
|
51
|
+
installed: installedWithNew,
|
|
52
|
+
internalSources: input.internalSources,
|
|
53
|
+
});
|
|
54
|
+
const flat = {
|
|
55
|
+
contentSourceDeclarations: tokens.contentSourceDeclarations,
|
|
56
|
+
contentSourceSourcesVarNames: tokens.contentSourceSourcesVarNames,
|
|
57
|
+
internalSourceDeclarations: tokens.internalSourceDeclarations,
|
|
58
|
+
internalSourceVarNames: tokens.internalSourceVarNames,
|
|
59
|
+
projectName: input.project.projectName,
|
|
60
|
+
projectNameLower: input.project.projectName.toLowerCase(),
|
|
61
|
+
targetDir: input.project.targetDir,
|
|
62
|
+
defaultLocale: input.project.defaultLocale,
|
|
63
|
+
supportedLocales: input.project.supportedLocales.join(", "),
|
|
64
|
+
supportedLocalesJson: JSON.stringify(input.project.supportedLocales),
|
|
65
|
+
notionxSource: input.project.notionxSource,
|
|
66
|
+
};
|
|
67
|
+
// For content-source items, add per-item tokens so the template
|
|
68
|
+
// sees this item's id/path/var-name. Feature-module items don't
|
|
69
|
+
// use these tokens.
|
|
70
|
+
if (input.item.kind === "content-source") {
|
|
71
|
+
const perItem = computePerItemTokens(input.item);
|
|
72
|
+
Object.assign(flat, {
|
|
73
|
+
contentSourceId: perItem.contentSourceId,
|
|
74
|
+
contentSourceTitle: perItem.contentSourceTitle,
|
|
75
|
+
contentSourceListPath: perItem.contentSourceListPath,
|
|
76
|
+
contentSourceVarName: perItem.contentSourceVarName,
|
|
77
|
+
contentSourceConstName: perItem.contentSourceConstName,
|
|
78
|
+
contentSourceNavLabel: perItem.contentSourceNavLabel,
|
|
79
|
+
contentSourceListTitle: perItem.contentSourceListTitle,
|
|
80
|
+
contentSourceListDescription: perItem.contentSourceListDescription,
|
|
81
|
+
contentSourceEmptyState: perItem.contentSourceEmptyState,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const file of input.item.files) {
|
|
86
|
+
if (!file.template)
|
|
87
|
+
continue;
|
|
88
|
+
// The template path is a LITERAL path on disk — the
|
|
89
|
+
// `{{...}}` segments are real directory names (e.g.
|
|
90
|
+
// `app/{{contentSourceListPath}}/page.tsx.tmpl`). Token
|
|
91
|
+
// substitution only applies to the template *content*,
|
|
92
|
+
// not the path.
|
|
93
|
+
const tplAbs = path.join(input.templatesDir, file.template);
|
|
94
|
+
let tpl;
|
|
95
|
+
try {
|
|
96
|
+
tpl = await fs.readFile(tplAbs, "utf8");
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Template file doesn't exist — skip silently. The catalog
|
|
100
|
+
// may declare files whose templates ship in a later version.
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const rendered = renderTemplate(tpl, flat);
|
|
104
|
+
const destAbs = path.join(input.projectDir, file.path);
|
|
105
|
+
const existed = await pathExists(destAbs);
|
|
106
|
+
out.push({
|
|
107
|
+
absolutePath: destAbs,
|
|
108
|
+
projectRelativePath: file.path,
|
|
109
|
+
ownership: file.ownership,
|
|
110
|
+
existed,
|
|
111
|
+
content: rendered,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function dedupeInstalled(items) {
|
|
117
|
+
const seen = new Map();
|
|
118
|
+
for (const item of items) {
|
|
119
|
+
if (!seen.has(item.id))
|
|
120
|
+
seen.set(item.id, item);
|
|
121
|
+
}
|
|
122
|
+
return [...seen.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
123
|
+
}
|
|
124
|
+
function computePerItemTokens(item) {
|
|
125
|
+
const id = item.id;
|
|
126
|
+
const basePath = item.params.basePath;
|
|
127
|
+
const listPath = basePath ?? `/${id}`;
|
|
128
|
+
const camel = toCamel(id);
|
|
129
|
+
return {
|
|
130
|
+
contentSourceId: id,
|
|
131
|
+
contentSourceTitle: titleCase(id),
|
|
132
|
+
contentSourceListPath: listPath,
|
|
133
|
+
contentSourceVarName: `${camel}Source`,
|
|
134
|
+
contentSourceConstName: `${camel}ContentModel`,
|
|
135
|
+
contentSourceNavLabel: titleCase(id),
|
|
136
|
+
contentSourceListTitle: titleCase(id),
|
|
137
|
+
contentSourceListDescription: `${titleCase(id)} entries backed by Notion.`,
|
|
138
|
+
contentSourceEmptyState: `No ${id} entries yet.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function renderTemplate(input, tokens) {
|
|
142
|
+
return input.replace(/\{\{\s*([a-zA-Z][a-zA-Z0-9_]*)\s*\}\}/g, (full, key) => {
|
|
143
|
+
const value = tokens[key];
|
|
144
|
+
if (typeof value === "string")
|
|
145
|
+
return value;
|
|
146
|
+
return full;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function pathExists(p) {
|
|
150
|
+
try {
|
|
151
|
+
await fs.stat(p);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=render-content-source-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-content-source-files.js","sourceRoot":"","sources":["../../src/registry/render-content-source-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// packages/create-notionx-app/src/registry/render-multi-source.ts
|
|
2
|
+
//
|
|
3
|
+
// Multi-source token-map builder. The v1 `render.ts` accepted a
|
|
4
|
+
// single `contentSource` answer and emitted one source's worth of
|
|
5
|
+
// tokens. With v2, a project can have *N* content sources installed
|
|
6
|
+
// (blog + docs + movies + …), and the templates need to render
|
|
7
|
+
// them as a single `contentSources` array.
|
|
8
|
+
//
|
|
9
|
+
// This module is the **only** place where multi-source text is
|
|
10
|
+
// generated. v1 callers go through the legacy single-source path in
|
|
11
|
+
// `render.ts`; v2 callers (`notionx add` / `notionx init`) build
|
|
12
|
+
// their token map here and pass it to a thin multi-source render
|
|
13
|
+
// function. The two paths share a `TokenMap` shape so the on-disk
|
|
14
|
+
// output is identical.
|
|
15
|
+
import { toCamel, titleCase } from "./text-utils.js";
|
|
16
|
+
/**
|
|
17
|
+
* Build a token map from a list of installed items.
|
|
18
|
+
*
|
|
19
|
+
* Sorting rule: content sources are alphabetised by `id` so the
|
|
20
|
+
* generated `models.ts` is deterministic across runs (the order of
|
|
21
|
+
* registration is observable via `getRegisteredSources()` and a
|
|
22
|
+
* non-deterministic order would produce noisy diffs in
|
|
23
|
+
* `notionx update` plans).
|
|
24
|
+
*
|
|
25
|
+
* Internal singleton sources (`siteSettingsSource`, `blocksSource`)
|
|
26
|
+
* are emitted in a fixed order (site-settings first, then blocks)
|
|
27
|
+
* so the generated block is also deterministic.
|
|
28
|
+
*/
|
|
29
|
+
export function buildMultiSourceTokenMap(input) {
|
|
30
|
+
const contentSources = input.installed
|
|
31
|
+
.filter((i) => i.kind === "content-source")
|
|
32
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
33
|
+
if (contentSources.length === 0) {
|
|
34
|
+
throw new Error("Cannot build a multi-source token map without at least one content-source item installed.");
|
|
35
|
+
}
|
|
36
|
+
// Reject duplicate ids — they would shadow each other in the
|
|
37
|
+
// runtime registry and produce a broken `models.ts`.
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
for (const source of contentSources) {
|
|
40
|
+
if (seen.has(source.id)) {
|
|
41
|
+
throw new Error(`Duplicate content-source id "${source.id}" in installed list. ` +
|
|
42
|
+
`Each content source must have a unique id.`);
|
|
43
|
+
}
|
|
44
|
+
seen.add(source.id);
|
|
45
|
+
}
|
|
46
|
+
const primary = contentSources[0];
|
|
47
|
+
const sourcesBlock = renderSourcesBlock(contentSources);
|
|
48
|
+
const internalFlags = {
|
|
49
|
+
siteSettings: input.internalSources?.siteSettings ?? true,
|
|
50
|
+
blocks: input.internalSources?.blocks ?? true,
|
|
51
|
+
};
|
|
52
|
+
const internalDecls = [];
|
|
53
|
+
const internalVars = [];
|
|
54
|
+
if (internalFlags.siteSettings) {
|
|
55
|
+
internalDecls.push(SITE_SETTINGS_SOURCE_DECLARATION);
|
|
56
|
+
internalVars.push("siteSettingsSource");
|
|
57
|
+
}
|
|
58
|
+
if (internalFlags.blocks) {
|
|
59
|
+
internalDecls.push(BLOCKS_SOURCE_DECLARATION);
|
|
60
|
+
internalVars.push("blocksSource");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
contentSourceId: primary.id,
|
|
64
|
+
contentSourceTitle: titleCase(primary.id),
|
|
65
|
+
contentSourceListPath: `/${primary.id}`,
|
|
66
|
+
contentSourceVarName: `${toCamel(primary.id)}Source`,
|
|
67
|
+
contentSourceConstName: `${toCamel(primary.id)}ContentModel`,
|
|
68
|
+
contentSourceNavLabel: titleCase(primary.id),
|
|
69
|
+
contentSourceListTitle: titleCase(primary.id),
|
|
70
|
+
contentSourceListDescription: `${titleCase(primary.id)} entries backed by Notion.`,
|
|
71
|
+
contentSourceEmptyState: `No ${primary.id} entries yet.`,
|
|
72
|
+
contentSourceDeclarations: sourcesBlock,
|
|
73
|
+
contentSourceSourcesVarNames: varNamesForSources(contentSources),
|
|
74
|
+
internalSourceDeclarations: internalDecls.join("\n\n"),
|
|
75
|
+
internalSourceVarNames: internalVars.length
|
|
76
|
+
? "\n " + internalVars.join(",\n ") + ","
|
|
77
|
+
: "",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function varNamesForSources(sources) {
|
|
81
|
+
return sources
|
|
82
|
+
.map((s) => `${toCamel(s.id)}Source`)
|
|
83
|
+
.join(", ");
|
|
84
|
+
}
|
|
85
|
+
// ---- internals ----
|
|
86
|
+
/**
|
|
87
|
+
* Render the `defineContentSource({...})` calls for every installed
|
|
88
|
+
* content source, separated by `\n\n`. The output is meant to be
|
|
89
|
+
* dropped into `models.ts` between the import block and the
|
|
90
|
+
* `contentSources` array literal.
|
|
91
|
+
*/
|
|
92
|
+
function renderSourcesBlock(sources) {
|
|
93
|
+
return sources
|
|
94
|
+
.map((source) => renderSingleSourceDeclaration(source))
|
|
95
|
+
.join("\n\n");
|
|
96
|
+
}
|
|
97
|
+
function renderSingleSourceDeclaration(source) {
|
|
98
|
+
const id = source.id;
|
|
99
|
+
const camelId = toCamel(id);
|
|
100
|
+
const varName = `${camelId}Source`;
|
|
101
|
+
const listPath = pathForSource(source, "list");
|
|
102
|
+
const detailPath = pathForSource(source, "detail");
|
|
103
|
+
const apiPath = pathForSource(source, "api");
|
|
104
|
+
const envVar = toEnvVarForSource(id);
|
|
105
|
+
const kind = id === "blog" ? "article" : "catalog";
|
|
106
|
+
const capabilities = defaultCapabilities(id);
|
|
107
|
+
// Indent every `key: "value"` line so it sits inside `fields: { ... }`
|
|
108
|
+
// of the generated call. Default fields: title + slug, which is the
|
|
109
|
+
// minimum any Notion data source needs. Users edit the file to
|
|
110
|
+
// match their actual property names.
|
|
111
|
+
return [
|
|
112
|
+
`export const ${varName}: ContentSource = defineContentSource({`,
|
|
113
|
+
` id: "${id}",`,
|
|
114
|
+
` kind: ${JSON.stringify(kind)},`,
|
|
115
|
+
` visibility: {`,
|
|
116
|
+
` public: true,`,
|
|
117
|
+
` admin: true,`,
|
|
118
|
+
` },`,
|
|
119
|
+
` source: {`,
|
|
120
|
+
` type: "notion",`,
|
|
121
|
+
` tokenEnv: "NOTION_TOKEN",`,
|
|
122
|
+
` dataSourceEnv: ${JSON.stringify(envVar)},`,
|
|
123
|
+
` fields: {`,
|
|
124
|
+
` title: "Name",`,
|
|
125
|
+
` slug: "Slug",`,
|
|
126
|
+
` },`,
|
|
127
|
+
` query: {`,
|
|
128
|
+
` pageSize: 50,`,
|
|
129
|
+
` },`,
|
|
130
|
+
` },`,
|
|
131
|
+
` routes: {`,
|
|
132
|
+
` listPath: ${JSON.stringify(listPath)},`,
|
|
133
|
+
` detailPath: ${JSON.stringify(detailPath)},`,
|
|
134
|
+
` detailParam: "slug",`,
|
|
135
|
+
` publicApiPath: ${JSON.stringify(apiPath)},`,
|
|
136
|
+
` },`,
|
|
137
|
+
` ui: {`,
|
|
138
|
+
` name: ${JSON.stringify(titleCase(id))},`,
|
|
139
|
+
` pluralName: ${JSON.stringify(`${titleCase(id)}s`)},`,
|
|
140
|
+
` navLabel: ${JSON.stringify(titleCase(id))},`,
|
|
141
|
+
` listTitle: ${JSON.stringify(titleCase(id))},`,
|
|
142
|
+
` listDescription: ${JSON.stringify(`${titleCase(id)} entries backed by Notion.`)},`,
|
|
143
|
+
` emptyState: ${JSON.stringify(`No ${id} entries yet.`)},`,
|
|
144
|
+
` },`,
|
|
145
|
+
` capabilities: ${capabilities},`,
|
|
146
|
+
`});`,
|
|
147
|
+
].join("\n");
|
|
148
|
+
}
|
|
149
|
+
function pathForSource(source, kind) {
|
|
150
|
+
// Sources can override the path via params (e.g. `basePath: "/docs"`).
|
|
151
|
+
// Falls back to the convention `/{id}` and `/api/{id}`.
|
|
152
|
+
const basePath = source.params.basePath;
|
|
153
|
+
if (basePath && kind !== "api") {
|
|
154
|
+
return kind === "list" ? basePath : `${basePath}/[slug]`;
|
|
155
|
+
}
|
|
156
|
+
switch (kind) {
|
|
157
|
+
case "list":
|
|
158
|
+
return `/${source.id}`;
|
|
159
|
+
case "detail":
|
|
160
|
+
return `/${source.id}/[slug]`;
|
|
161
|
+
case "api":
|
|
162
|
+
return `/api/${source.id}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function toEnvVarForSource(id) {
|
|
166
|
+
return `NOTION_${snakeUpper(id)}_DATA_SOURCE_ID`;
|
|
167
|
+
}
|
|
168
|
+
function snakeUpper(id) {
|
|
169
|
+
return id.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase();
|
|
170
|
+
}
|
|
171
|
+
function defaultCapabilities(id) {
|
|
172
|
+
// Blog ships with rich block rendering and cover images because
|
|
173
|
+
// that's what the v1 starter assumed. Other sources default to
|
|
174
|
+
// the leaner shape; users can edit the file to enable features.
|
|
175
|
+
if (id === "blog") {
|
|
176
|
+
return "{\n richBlocks: true,\n coverImages: true,\n gatedAssets: false,\n }";
|
|
177
|
+
}
|
|
178
|
+
return "{\n richBlocks: false,\n coverImages: true,\n gatedAssets: false,\n }";
|
|
179
|
+
}
|
|
180
|
+
// ---- internal singleton source declarations ----
|
|
181
|
+
//
|
|
182
|
+
// These two `defineContentSource({...})` blocks back
|
|
183
|
+
// `lib/site/settings.ts` and `components/page-blocks` respectively.
|
|
184
|
+
// They are emitted verbatim into `models.ts` between the
|
|
185
|
+
// `BEGIN generated-internal-sources` / `END generated-internal-sources`
|
|
186
|
+
// markers when the corresponding flag (`enableSiteSettings` /
|
|
187
|
+
// `enableBlocks`) is true.
|
|
188
|
+
//
|
|
189
|
+
// Keep these in sync with the field maps documented in
|
|
190
|
+
// `templates/README.md.tmpl` — operators edit the Notion database
|
|
191
|
+
// columns to match these names.
|
|
192
|
+
const SITE_SETTINGS_SOURCE_DECLARATION = `export const siteSettingsSource: ContentSource = defineContentSource({
|
|
193
|
+
id: "site-settings",
|
|
194
|
+
kind: "directory",
|
|
195
|
+
visibility: {
|
|
196
|
+
public: false,
|
|
197
|
+
admin: true,
|
|
198
|
+
},
|
|
199
|
+
source: {
|
|
200
|
+
type: "notion",
|
|
201
|
+
tokenEnv: "NOTION_TOKEN",
|
|
202
|
+
dataSourceEnv: "NOTION_SITE_SETTINGS_DATA_SOURCE_ID",
|
|
203
|
+
fields: {
|
|
204
|
+
title: "Site Name",
|
|
205
|
+
description: "Description",
|
|
206
|
+
cover: "Social Image",
|
|
207
|
+
},
|
|
208
|
+
query: {
|
|
209
|
+
pageSize: 1,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
routes: {
|
|
213
|
+
listPath: "/__internal/site-settings",
|
|
214
|
+
detailPath: "/__internal/site-settings/[slug]",
|
|
215
|
+
detailParam: "slug",
|
|
216
|
+
publicApiPath: "/api/__internal/site-settings",
|
|
217
|
+
},
|
|
218
|
+
ui: {
|
|
219
|
+
name: "Site Settings",
|
|
220
|
+
pluralName: "Site Settings",
|
|
221
|
+
navLabel: "Site Settings",
|
|
222
|
+
listTitle: "Site Settings",
|
|
223
|
+
listDescription:
|
|
224
|
+
"Singleton row in Notion that powers the site name, tagline, description, default locale, and social image.",
|
|
225
|
+
emptyState: "No site settings row found — re-run provisioning.",
|
|
226
|
+
},
|
|
227
|
+
capabilities: {
|
|
228
|
+
richBlocks: false,
|
|
229
|
+
coverImages: true,
|
|
230
|
+
gatedAssets: false,
|
|
231
|
+
},
|
|
232
|
+
});`;
|
|
233
|
+
const BLOCKS_SOURCE_DECLARATION = `export const blocksSource: ContentSource = defineContentSource({
|
|
234
|
+
id: "blocks",
|
|
235
|
+
kind: "directory",
|
|
236
|
+
visibility: {
|
|
237
|
+
public: false,
|
|
238
|
+
admin: true,
|
|
239
|
+
},
|
|
240
|
+
source: {
|
|
241
|
+
type: "notion",
|
|
242
|
+
tokenEnv: "NOTION_TOKEN",
|
|
243
|
+
dataSourceEnv: "NOTION_BLOCKS_DATA_SOURCE_ID",
|
|
244
|
+
fields: {
|
|
245
|
+
title: "Name",
|
|
246
|
+
slug: "Slug",
|
|
247
|
+
type: "Type",
|
|
248
|
+
description: "Description",
|
|
249
|
+
pageKeys: "Page Keys",
|
|
250
|
+
order: "Order",
|
|
251
|
+
cover: "Cover",
|
|
252
|
+
published: "Status",
|
|
253
|
+
eyebrow: "Eyebrow",
|
|
254
|
+
headline: "Headline",
|
|
255
|
+
subheadline: "Subheadline",
|
|
256
|
+
primaryCtaLabel: "Primary CTA Label",
|
|
257
|
+
primaryCtaHref: "Primary CTA Href",
|
|
258
|
+
secondaryCtaLabel: "Secondary CTA Label",
|
|
259
|
+
secondaryCtaHref: "Secondary CTA Href",
|
|
260
|
+
alignment: "Alignment",
|
|
261
|
+
theme: "Theme",
|
|
262
|
+
columns: "Columns",
|
|
263
|
+
count: "Count",
|
|
264
|
+
items: "Items",
|
|
265
|
+
body: "Body",
|
|
266
|
+
quote: "Quote",
|
|
267
|
+
quoteAttribution: "Quote Attribution",
|
|
268
|
+
mediaUrl: "Media Url",
|
|
269
|
+
layout: "Layout",
|
|
270
|
+
},
|
|
271
|
+
query: {
|
|
272
|
+
pageSize: 50,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
routes: {
|
|
276
|
+
listPath: "/__internal/blocks",
|
|
277
|
+
detailPath: "/__internal/blocks/[slug]",
|
|
278
|
+
detailParam: "slug",
|
|
279
|
+
publicApiPath: "/api/__internal/blocks",
|
|
280
|
+
},
|
|
281
|
+
ui: {
|
|
282
|
+
name: "Block",
|
|
283
|
+
pluralName: "Blocks",
|
|
284
|
+
navLabel: "Blocks",
|
|
285
|
+
listTitle: "Reusable Blocks",
|
|
286
|
+
listDescription:
|
|
287
|
+
"Reusable structured page sections stored in Notion and referenced from the Pages model.",
|
|
288
|
+
emptyState: "No reusable blocks found — re-run provisioning.",
|
|
289
|
+
},
|
|
290
|
+
capabilities: {
|
|
291
|
+
richBlocks: true,
|
|
292
|
+
coverImages: true,
|
|
293
|
+
gatedAssets: false,
|
|
294
|
+
},
|
|
295
|
+
});`;
|
|
296
|
+
//# sourceMappingURL=render-multi-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-multi-source.js","sourceRoot":"","sources":["../../src/registry/render-multi-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// packages/create-notionx-app/src/registry/render-multi-source.test.ts
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { buildMultiSourceTokenMap } from "./render-multi-source.js";
|
|
4
|
+
const OFFICIAL = {
|
|
5
|
+
kind: "official",
|
|
6
|
+
name: "@notionx/official",
|
|
7
|
+
};
|
|
8
|
+
function item(id, params = {}) {
|
|
9
|
+
return {
|
|
10
|
+
id,
|
|
11
|
+
kind: "content-source",
|
|
12
|
+
version: 1,
|
|
13
|
+
source: OFFICIAL,
|
|
14
|
+
params,
|
|
15
|
+
installedAt: "2026-06-10T00:00:00.000Z",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const DEFAULT_PROJECT = {
|
|
19
|
+
projectName: "demo",
|
|
20
|
+
targetDir: "./demo",
|
|
21
|
+
defaultLocale: "en",
|
|
22
|
+
supportedLocales: ["en"],
|
|
23
|
+
notionxSource: "^2.0.0",
|
|
24
|
+
adminEmail: "admin@example.com",
|
|
25
|
+
adminPassword: "Password123",
|
|
26
|
+
scaffoldVersion: "0.7.2",
|
|
27
|
+
};
|
|
28
|
+
describe("buildMultiSourceTokenMap", () => {
|
|
29
|
+
it("renders a single source (blog) with per-source and block tokens", () => {
|
|
30
|
+
const tokens = buildMultiSourceTokenMap({
|
|
31
|
+
project: DEFAULT_PROJECT,
|
|
32
|
+
installed: [item("blog", { contentSourceId: "blog" })],
|
|
33
|
+
});
|
|
34
|
+
// Per-source tokens (derived from the primary source).
|
|
35
|
+
expect(tokens.contentSourceId).toBe("blog");
|
|
36
|
+
expect(tokens.contentSourceVarName).toBe("blogSource");
|
|
37
|
+
expect(tokens.contentSourceListPath).toBe("/blog");
|
|
38
|
+
// The declarations block contains the same source.
|
|
39
|
+
expect(tokens.contentSourceDeclarations).toContain("blogSource");
|
|
40
|
+
expect(tokens.contentSourceDeclarations).toContain('id: "blog"');
|
|
41
|
+
});
|
|
42
|
+
it("renders two sources (blog + docs) and produces a unified declarations block", () => {
|
|
43
|
+
const tokens = buildMultiSourceTokenMap({
|
|
44
|
+
project: DEFAULT_PROJECT,
|
|
45
|
+
installed: [
|
|
46
|
+
item("blog", { contentSourceId: "blog" }),
|
|
47
|
+
item("docs", { contentSourceId: "docs", basePath: "/docs" }),
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
// Both sources are referenced in the declarations block.
|
|
51
|
+
expect(tokens.contentSourceDeclarations).toContain("blogSource");
|
|
52
|
+
expect(tokens.contentSourceDeclarations).toContain("docsSource");
|
|
53
|
+
expect(tokens.contentSourceDeclarations).toContain('id: "blog"');
|
|
54
|
+
expect(tokens.contentSourceDeclarations).toContain('id: "docs"');
|
|
55
|
+
// The block is **ordered by id** for deterministic diffs
|
|
56
|
+
// (alphabetical when no explicit order is given).
|
|
57
|
+
const blogIdx = tokens.contentSourceDeclarations.indexOf("blogSource");
|
|
58
|
+
const docsIdx = tokens.contentSourceDeclarations.indexOf("docsSource");
|
|
59
|
+
expect(blogIdx).toBeGreaterThan(-1);
|
|
60
|
+
expect(docsIdx).toBeGreaterThan(blogIdx);
|
|
61
|
+
// The var-names token is a comma-joined list of `*Source` names.
|
|
62
|
+
expect(tokens.contentSourceSourcesVarNames).toBe("blogSource, docsSource");
|
|
63
|
+
});
|
|
64
|
+
it("honors a custom basePath param for content sources (docs at /docs)", () => {
|
|
65
|
+
const tokens = buildMultiSourceTokenMap({
|
|
66
|
+
project: DEFAULT_PROJECT,
|
|
67
|
+
installed: [item("docs", { contentSourceId: "docs", basePath: "/docs" })],
|
|
68
|
+
});
|
|
69
|
+
// The declarations block reflects the custom basePath.
|
|
70
|
+
expect(tokens.contentSourceDeclarations).toContain('listPath: "/docs"');
|
|
71
|
+
expect(tokens.contentSourceDeclarations).toContain('detailPath: "/docs/[slug]"');
|
|
72
|
+
});
|
|
73
|
+
it("treats feature-modules (e.g. search) as separate from content-source arrays", () => {
|
|
74
|
+
const tokens = buildMultiSourceTokenMap({
|
|
75
|
+
project: DEFAULT_PROJECT,
|
|
76
|
+
installed: [
|
|
77
|
+
item("docs", { contentSourceId: "docs" }),
|
|
78
|
+
{
|
|
79
|
+
id: "search",
|
|
80
|
+
kind: "feature-module",
|
|
81
|
+
version: 1,
|
|
82
|
+
source: OFFICIAL,
|
|
83
|
+
params: { scope: "docs" },
|
|
84
|
+
installedAt: "2026-06-10T00:00:00.000Z",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
// docs is a content-source → its source block is emitted
|
|
89
|
+
expect(tokens.contentSourceDeclarations).toContain("docsSource");
|
|
90
|
+
expect(tokens.contentSourceDeclarations).toContain('id: "docs"');
|
|
91
|
+
// search is a feature-module → not in the content-source array
|
|
92
|
+
expect(tokens.contentSourceDeclarations).not.toContain("searchSource");
|
|
93
|
+
});
|
|
94
|
+
it("throws if no content-source items are installed (must have at least one)", () => {
|
|
95
|
+
expect(() => buildMultiSourceTokenMap({
|
|
96
|
+
project: DEFAULT_PROJECT,
|
|
97
|
+
installed: [],
|
|
98
|
+
})).toThrow(/at least one content-source/);
|
|
99
|
+
});
|
|
100
|
+
it("throws on duplicate content-source ids", () => {
|
|
101
|
+
expect(() => buildMultiSourceTokenMap({
|
|
102
|
+
project: DEFAULT_PROJECT,
|
|
103
|
+
installed: [
|
|
104
|
+
item("blog", { contentSourceId: "blog" }),
|
|
105
|
+
item("blog", { contentSourceId: "blog" }),
|
|
106
|
+
],
|
|
107
|
+
})).toThrow(/duplicate/i);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=render-multi-source.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-multi-source.test.js","sourceRoot":"","sources":["../../src/registry/render-multi-source.test.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,MAAM,QAAQ,GAA2B;IACvC,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,mBAAmB;CAC1B,CAAC;AAEF,SAAS,IAAI,CACX,EAAU,EACV,SAAiC,EAAE;IAEnC,OAAO;QACL,EAAE;QACF,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,QAAQ;QAChB,MAAM;QACN,WAAW,EAAE,0BAA0B;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,eAAe,GAAG;IACtB,WAAW,EAAE,MAAM;IACnB,SAAS,EAAE,QAAQ;IACnB,aAAa,EAAE,IAAI;IACnB,gBAAgB,EAAE,CAAC,IAAI,CAAC;IACxB,aAAa,EAAE,QAAQ;IACvB,UAAU,EAAE,mBAAmB;IAC/B,aAAa,EAAE,aAAa;IAC5B,eAAe,EAAE,OAAO;CACzB,CAAC;AAEF,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACtC,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC;SACvD,CAAC,CAAC;QAEH,uDAAuD;QACvD,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnD,mDAAmD;QACnD,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACtC,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE;gBACT,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;gBACzC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;aAC7D;SACF,CAAC,CAAC;QAEH,yDAAyD;QACzD,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAEjE,yDAAyD;QACzD,kDAAkD;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,yBAAyB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,MAAM,CAAC,yBAAyB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACvE,MAAM,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAEzC,iEAAiE;QACjE,MAAM,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACtC,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;SAC1E,CAAC,CAAC;QAEH,uDAAuD;QACvD,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAChD,4BAA4B,CAC7B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACtC,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE;gBACT,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;gBACzC;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,CAAC;oBACV,MAAM,EAAE,QAAQ;oBAChB,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;oBACzB,WAAW,EAAE,0BAA0B;iBACxC;aACF;SACF,CAAC,CAAC;QAEH,yDAAyD;QACzD,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAEjE,+DAA+D;QAC/D,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,CAAC,GAAG,EAAE,CACV,wBAAwB,CAAC;YACvB,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE,EAAE;SACd,CAAC,CACH,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,GAAG,EAAE,CACV,wBAAwB,CAAC;YACvB,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE;gBACT,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;gBACzC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;aAC1C;SACF,CAAC,CACH,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// packages/create-notionx-app/src/registry/text-utils.ts
|
|
2
|
+
//
|
|
3
|
+
// Shared string-casing helpers used by the render layer.
|
|
4
|
+
// Previously duplicated in render.ts, render-multi-source.ts, and
|
|
5
|
+
// render-content-source-files.ts.
|
|
6
|
+
/**
|
|
7
|
+
* Convert an arbitrary id (e.g. `blog`, `my-cool-source`) to
|
|
8
|
+
* `kebab-case` (lowercase, non-alphanumeric runs → `-`).
|
|
9
|
+
*/
|
|
10
|
+
export function toKebab(input) {
|
|
11
|
+
return input
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
14
|
+
.replace(/(^-|-$)/g, "");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Convert an arbitrary id to `camelCase` (e.g. `blog` → `blog`,
|
|
18
|
+
* `my-cool-source` → `myCoolSource`).
|
|
19
|
+
*/
|
|
20
|
+
export function toCamel(input) {
|
|
21
|
+
const parts = toKebab(input).split("-").filter(Boolean);
|
|
22
|
+
if (parts.length === 0)
|
|
23
|
+
return "field";
|
|
24
|
+
const [first, ...rest] = parts;
|
|
25
|
+
return first + rest.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert an arbitrary id to `PascalCase` (e.g. `blog` → `Blog`,
|
|
29
|
+
* `my-cool-source` → `MyCoolSource`).
|
|
30
|
+
*/
|
|
31
|
+
export function toPascal(input) {
|
|
32
|
+
const camel = toCamel(input);
|
|
33
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Uppercase the first character, leave the rest untouched.
|
|
37
|
+
* (e.g. `blog` → `Blog`, `docs` → `Docs`).
|
|
38
|
+
*/
|
|
39
|
+
export function titleCase(id) {
|
|
40
|
+
return id.charAt(0).toUpperCase() + id.slice(1);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=text-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-utils.js","sourceRoot":"","sources":["../../src/registry/text-utils.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,yDAAyD;AACzD,kEAAkE;AAClE,kCAAkC;AAElC;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACvC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC;IAC/B,OAAO,KAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC"}
|