create-ailk 0.27.0 → 0.28.1
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 +84 -7
- package/dist/add-module-core.js +2 -1
- package/dist/add-module.d.ts +2 -1
- package/dist/add-module.js +9 -4
- package/dist/add-route.js +46 -19
- package/dist/body-heading.d.ts +56 -0
- package/dist/body-heading.js +394 -0
- package/dist/cli.js +28 -1
- package/dist/content-payloads.js +2 -2
- package/dist/derive-architecture.js +8 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/lib/auth-secret-file.d.ts +6 -2
- package/dist/lib/auth-secret-file.js +46 -20
- package/dist/lib/chrome-readers.d.ts +21 -5
- package/dist/lib/chrome-readers.js +128 -20
- package/dist/lib/declared-env.d.ts +6 -0
- package/dist/lib/declared-env.js +2 -0
- package/dist/lib/env-catalog.js +20 -0
- package/dist/lib/import-graph.d.ts +16 -0
- package/dist/lib/import-graph.js +53 -0
- package/dist/lib/site-modules.d.ts +8 -0
- package/dist/lib/site-modules.js +34 -0
- package/dist/module-architecture.js +23 -14
- package/dist/paid-sections.d.ts +17 -0
- package/dist/paid-sections.js +39 -0
- package/dist/programmatic.d.ts +16 -0
- package/dist/programmatic.js +73 -4
- package/dist/remove-module.d.ts +98 -0
- package/dist/remove-module.js +620 -0
- package/dist/seed-flows.d.ts +2 -2
- package/dist/seed-flows.js +16 -9
- package/dist/site-roster.js +4 -1
- package/dist/starter-grid.d.ts +26 -0
- package/dist/starter-grid.js +34 -0
- package/dist/surfaces.d.ts +26 -0
- package/dist/surfaces.js +105 -9
- package/dist/sync-routes.d.ts +7 -0
- package/dist/sync-routes.js +53 -6
- package/dist/template-pages.d.ts +36 -0
- package/dist/template-pages.js +129 -0
- package/dist/template-record.d.ts +13 -4
- package/dist/template-record.js +8 -4
- package/dist/topology.d.ts +2 -1
- package/dist/topology.js +4 -2
- package/dist/update.d.ts +34 -2
- package/dist/update.js +395 -48
- package/package.json +3 -3
- package/release-scope.json +2 -1
- package/site-templates/aliases/hub.TEMPLATE-MANIFEST.json +115 -0
- package/templates/.env.example +5 -2
- package/templates/apps/api/.env.example +9 -5
- package/templates/apps/api/CLAUDE.md +14 -9
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +37 -0
- package/templates/apps/api/src/vercel-handler.ts +8 -1
- package/templates/apps/mcp/CLAUDE.md +18 -10
- package/templates/apps/web/.env.example +25 -11
- package/templates/apps/web/CLAUDE.md +45 -4
- package/templates/apps/web/__tests__/setup.ts +7 -0
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +7 -7
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +61 -2
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +81 -0
- package/templates/apps/web/app/[locale]/(authed)/account/connect/__tests__/page.test.tsx +197 -0
- package/templates/apps/web/app/[locale]/(authed)/account/connect/page.tsx +152 -0
- package/templates/apps/web/app/[locale]/about/__tests__/about-routes.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +76 -21
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +46 -16
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +180 -2
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +122 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/hub-children.test.tsx +65 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +23 -11
- package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/consent.actions.ts +44 -6
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +269 -42
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +88 -30
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/courses/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/courses/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +15 -2
- package/templates/apps/web/app/[locale]/events/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/events/page.tsx +23 -7
- package/templates/apps/web/app/[locale]/features/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +23 -25
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +95 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/save.actions.test.ts +193 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/email-moment.ts +92 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +10 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +513 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +122 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.no-commerce.test.tsx +195 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/landing.test.tsx +293 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +397 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/checkout-seam.ts +74 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +264 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/save.actions.ts +75 -0
- package/templates/apps/web/app/[locale]/for/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +5 -1
- package/templates/apps/web/app/[locale]/for/page.tsx +23 -25
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +25 -4
- package/templates/apps/web/app/[locale]/integrations/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/integrations/page.tsx +23 -11
- package/templates/apps/web/app/[locale]/layout.tsx +157 -139
- package/templates/apps/web/app/[locale]/page.tsx +51 -3
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +16 -3
- package/templates/apps/web/app/[locale]/partners/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/partners/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/portfolio/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +25 -24
- package/templates/apps/web/app/[locale]/pricing/[slug]/__tests__/page.test.tsx +321 -0
- package/templates/apps/web/app/[locale]/pricing/[slug]/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/pricing-pages.test.tsx +215 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +56 -341
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +21 -4
- package/templates/apps/web/app/[locale]/products/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/products/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/projects/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +18 -11
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +25 -4
- package/templates/apps/web/app/[locale]/resources/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/resources/page.tsx +20 -10
- package/templates/apps/web/app/[locale]/services/[slug]/[child]/page.tsx +68 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +64 -9
- package/templates/apps/web/app/[locale]/services/__tests__/hub-children.test.tsx +158 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/services/__tests__/service-detail-sections.test.tsx +224 -0
- package/templates/apps/web/app/[locale]/services/__tests__/service-families.test.tsx +368 -0
- package/templates/apps/web/app/[locale]/services/page.tsx +45 -19
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/team/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/team/page.tsx +25 -24
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +24 -3
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/templates/[slug]/__tests__/page.test.tsx +203 -0
- package/templates/apps/web/app/[locale]/templates/[slug]/page.tsx +81 -0
- package/templates/apps/web/app/[locale]/templates/__tests__/page.test.tsx +237 -0
- package/templates/apps/web/app/[locale]/templates/page.tsx +108 -0
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +24 -3
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +204 -2
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +151 -33
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +233 -7
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +92 -41
- package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +77 -39
- package/templates/apps/web/app/llms.txt/route.ts +41 -3
- package/templates/apps/web/app/robots.txt/route.ts +5 -1
- package/templates/apps/web/app/sitemap.xml/route.ts +45 -3
- package/templates/apps/web/auth.ts +102 -50
- package/templates/apps/web/components/signed-in-nav.tsx +8 -1
- package/templates/apps/web/lib/__tests__/bot-check.test.ts +254 -0
- package/templates/apps/web/lib/__tests__/consent-visitor.test.ts +212 -13
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +13 -1
- package/templates/apps/web/lib/__tests__/docs-index.test.ts +93 -0
- package/templates/apps/web/lib/__tests__/docs-path.test.ts +168 -0
- package/templates/apps/web/lib/__tests__/hub-children.fixtures.ts +279 -0
- package/templates/apps/web/lib/__tests__/nav-label-keys.test.ts +138 -0
- package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +178 -2
- package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +183 -0
- package/templates/apps/web/lib/__tests__/page-chrome.test.ts +412 -0
- package/templates/apps/web/lib/__tests__/pricing-page.test.ts +142 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +202 -10
- package/templates/apps/web/lib/__tests__/route-family.test.ts +57 -0
- package/templates/apps/web/lib/__tests__/service-listing.test.ts +111 -0
- package/templates/apps/web/lib/__tests__/services-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/site-header.test.ts +151 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +148 -6
- package/templates/apps/web/lib/__tests__/telemetry-scrub.test.ts +84 -0
- package/templates/apps/web/lib/__tests__/template-listing.test.ts +158 -0
- package/templates/apps/web/lib/auth-secret.ts +57 -0
- package/templates/apps/web/lib/bot-check.ts +138 -0
- package/templates/apps/web/lib/consent-visitor.ts +81 -7
- package/templates/apps/web/lib/deck-page.ts +6 -3
- package/templates/apps/web/lib/docs-index.ts +100 -0
- package/templates/apps/web/lib/docs-path.ts +94 -0
- package/templates/apps/web/lib/hub-children.tsx +318 -0
- package/templates/apps/web/lib/modules.generated.ts +9 -0
- package/templates/apps/web/lib/nav-label-check.ts +61 -0
- package/templates/apps/web/lib/nav-label-keys.ts +78 -0
- package/templates/apps/web/lib/newsletter.actions.ts +31 -6
- package/templates/apps/web/lib/offerings-pricing.ts +135 -6
- package/templates/apps/web/lib/page-chrome.ts +367 -0
- package/templates/apps/web/lib/posting-title.ts +26 -4
- package/templates/apps/web/lib/pricing-page.ts +430 -0
- package/templates/apps/web/lib/relay.ts +31 -106
- package/templates/apps/web/lib/route-family.ts +69 -0
- package/templates/apps/web/lib/service-listing.ts +89 -0
- package/templates/apps/web/lib/services-path.ts +97 -0
- package/templates/apps/web/lib/site-header.ts +94 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +68 -3
- package/templates/apps/web/lib/template-listing.ts +169 -0
- package/templates/apps/web/messages/en.json +86 -30
- package/templates/apps/web/middleware.ts +171 -11
- package/templates/apps/web/next.config.mjs +211 -2
- package/templates/apps/web/package.json +1 -1
- package/templates/apps/web/scripts/assert-content-bundled.ts +28 -6
- package/templates/apps/web/scripts/check-nav-labels.ts +39 -0
- package/templates/apps/web/sentry.server.config.ts +30 -2
- package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -9
- package/templates/apps/web/src/lib/rate-limit.ts +68 -27
- package/templates/apps/web/src/lib/routes.ts +15 -1
- package/templates/tools/declared-env.ts +7 -0
- package/versions.json +48 -48
package/README.md
CHANGED
|
@@ -60,10 +60,20 @@ pnpm create ailk@latest my-site --shape organization-service --license <key>
|
|
|
60
60
|
`--design minimalism` picks the other look and needs a Pro or Pro Plus key;
|
|
61
61
|
`standard` (the default) needs none of its own (the run itself still needs a
|
|
62
62
|
key while Free is held, above). `--template <alias>` scaffolds one of the
|
|
63
|
-
|
|
64
|
-
each fixes a shape, a design and its own module list — and cannot be
|
|
65
|
-
with `--shape` or `--design`; `marketing-site-free` with no key is
|
|
66
|
-
same way while Free is held.
|
|
63
|
+
four named aliases (`marketing-site-free`, `marketing-site`, `saas-product`,
|
|
64
|
+
`hub`) — each fixes a shape, a design and its own module list — and cannot be
|
|
65
|
+
combined with `--shape` or `--design`; `marketing-site-free` with no key is
|
|
66
|
+
refused the same way while Free is held.
|
|
67
|
+
|
|
68
|
+
`hub` is the starter for a hub (#10040): the `organization-service` shape with
|
|
69
|
+
every public row dropped and no module in its own list, so the scaffold is the
|
|
70
|
+
sign-in pages, the account area, `/privacy`, `/terms` and `/working-agreement`,
|
|
71
|
+
the API and the MCP server — the same tree `remove-module marketing` leaves —
|
|
72
|
+
and the Admin and the rest ride the key's `--modules`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm create ailk@latest my-hub --template hub --modules multi-tenant --license <key>
|
|
76
|
+
```
|
|
67
77
|
|
|
68
78
|
### Add a purchased module to an existing site
|
|
69
79
|
|
|
@@ -110,6 +120,58 @@ result of the roster check (`Page roster check: clean`). Running it again
|
|
|
110
120
|
changes nothing and reports nothing to add.
|
|
111
121
|
Run `pnpm install` afterwards.
|
|
112
122
|
|
|
123
|
+
### Remove a module from an existing site
|
|
124
|
+
|
|
125
|
+
`remove-module` is the inverse of `add-module` for the modules whose
|
|
126
|
+
pages are public: `marketing` and `docs-blog` (#10040). A hub — the site
|
|
127
|
+
every spoke of an organization signs in through — needs sign-in, the account
|
|
128
|
+
area, the API and the Admin, and no second copy of the public site:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# in an already-scaffolded repo (default target: cwd)
|
|
132
|
+
create-ailk remove-module marketing
|
|
133
|
+
# or point at the repo explicitly, and delete the site's own edits too
|
|
134
|
+
create-ailk remove-module marketing ./my-hub --force
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
It deletes the module's route directories under `apps/web/app/[locale]/` —
|
|
138
|
+
everything but the `(auth)` and `(authed)` groups and the `/privacy`,
|
|
139
|
+
`/terms` and `/working-agreement` pages the sign-up form links — and the
|
|
140
|
+
seed content under `content/<locale>/` except those three pages' seeds; for
|
|
141
|
+
`docs-blog`, the module's own files and sample content. Then it:
|
|
142
|
+
|
|
143
|
+
- records every removed app file as **retired** in `.ailk-template-files.json`
|
|
144
|
+
(its row stays, with the template's hash), so `create-ailk update` reads it
|
|
145
|
+
as deleted locally and never writes it back, even across a version bump;
|
|
146
|
+
`sync-routes` and `add-module` stop copying the public tree on a site whose
|
|
147
|
+
`.ailk-site-manifest.json` lists no `marketing`;
|
|
148
|
+
- keeps any file in a removed directory that a kept file imports — the
|
|
149
|
+
locale layout reads `contact/site-contact-lines.ts`, and `lib/in-page-magnet.ts`
|
|
150
|
+
the contact form's action — with its own imports, listed under "Kept — a
|
|
151
|
+
kept file imports them" with the importer; the directory stays with no
|
|
152
|
+
page, so its route still 404s;
|
|
153
|
+
- drops the module from `.ailk-site-manifest.json` (its `modules` and the
|
|
154
|
+
roster rows) and from `architecture.yaml`'s `# Modules:` header, keeping
|
|
155
|
+
the web app's import permissions;
|
|
156
|
+
- regenerates the module flags, the paid-sections registry and — unless you
|
|
157
|
+
have edited it — the nav, which links nothing on a site with no public page.
|
|
158
|
+
|
|
159
|
+
A file the site has edited, or a page the site authored inside a removed
|
|
160
|
+
directory, stops the run before anything is written and is listed; `--force`
|
|
161
|
+
removes those too. Removing `marketing` while `docs-blog` is installed is
|
|
162
|
+
refused — remove `docs-blog` first. A `docs-blog` removal leaves the bundle's
|
|
163
|
+
`package.json`, `next.config.mjs` and `jest.config.cjs` patches in place
|
|
164
|
+
(listed under "Left in place"), so the site still builds; reverse them by
|
|
165
|
+
hand if you want the module's dependencies gone.
|
|
166
|
+
|
|
167
|
+
A site without `marketing` redirects `/` to `/account` (signed out, on to
|
|
168
|
+
sign-in), serves `robots.txt` disallowing every path, sends
|
|
169
|
+
`X-Robots-Tag: noindex` on every response, and answers 404 at
|
|
170
|
+
`/sitemap.xml` and `/llms.txt`; a site that keeps `marketing` or `docs-blog`
|
|
171
|
+
is byte-identical to today. The route code stays and reads the module list
|
|
172
|
+
(`apps/web/lib/modules.generated.ts`), so nothing needs restoring if a module
|
|
173
|
+
comes back.
|
|
174
|
+
|
|
113
175
|
### Apply a design to an existing site
|
|
114
176
|
|
|
115
177
|
A site that already exists can wear a different design with one command. Name
|
|
@@ -362,11 +424,26 @@ it; the merge itself still never loses content.
|
|
|
362
424
|
never a shell. Fetching an old template version (when it is not the running
|
|
363
425
|
package's own) reads the public npm registry at
|
|
364
426
|
`https://registry.npmjs.org`, overridable with `AILK_NPM_REGISTRY_URL` for a
|
|
365
|
-
mirror. `update` never touches content,
|
|
366
|
-
|
|
367
|
-
|
|
427
|
+
mirror. `update` never touches content, except to give an mdx-body page
|
|
428
|
+
with no `# ` heading its title and description (below), nor env files,
|
|
429
|
+
`.claude/`, lockfiles or any `package.json` — those stay `add-module`'s and
|
|
430
|
+
the consumer version bump's job. Commit before running `update`, and review the diff after: a
|
|
368
431
|
clean merge is still worth reading.
|
|
369
432
|
|
|
433
|
+
**Page headings (#7495).** A page your `apps/web` renders in
|
|
434
|
+
`bodyMode: "mdx-body"` takes its `<h1>` and intro from its own body: the
|
|
435
|
+
template prints neither frontmatter `title` nor `description` on it. So every
|
|
436
|
+
`update` run, after the template files, gives each such page under
|
|
437
|
+
`content/<locale>/<Type>/` whose body has no `# ` heading its `title` as
|
|
438
|
+
`# <title>` and its `description` as the paragraph under it, inserted at the
|
|
439
|
+
top of the body (after any leading `import`/`export` lines), and lists it
|
|
440
|
+
under **Page heading added**. Nothing else in the file changes. A page with
|
|
441
|
+
one `# ` heading is left exactly as it is; a page with several, or one whose
|
|
442
|
+
`title` or `description` is written in a form `update` does not read (a
|
|
443
|
+
folded `>` block, say), is listed under **Needs review** for you to fix.
|
|
444
|
+
Which pages are mdx-body is read from your own `apps/web/app` routes, and the
|
|
445
|
+
step runs again harmlessly on every update.
|
|
446
|
+
|
|
370
447
|
### Adopting an existing site
|
|
371
448
|
|
|
372
449
|
A site that create-ailk did not scaffold, or that was scaffolded before
|
package/dist/add-module-core.js
CHANGED
|
@@ -35,7 +35,8 @@ export function readArchitectureHeader(archYaml) {
|
|
|
35
35
|
}
|
|
36
36
|
let modules;
|
|
37
37
|
try {
|
|
38
|
-
|
|
38
|
+
// `none` is the empty set `deriveArchitecture` writes (#10040).
|
|
39
|
+
modules = modulesLine.trim() === "none" ? [] : normalizeModules(modulesLine.split(","));
|
|
39
40
|
}
|
|
40
41
|
catch (err) {
|
|
41
42
|
throw new AddModuleRefusedError(`architecture.yaml's \`# Modules:\` header is not a module list: ${err.message}`);
|
package/dist/add-module.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type InstallReportRow } from "./lib/extract-module-bundle.js";
|
|
|
3
3
|
import { type ModulePatch } from "./module-architecture.js";
|
|
4
4
|
import { type ModuleName } from "./parse-args.js";
|
|
5
5
|
import type { MarkerMap, WorkspaceVersionMap } from "./substitute.js";
|
|
6
|
+
import { type RenderNavConfigOptions } from "./surfaces.js";
|
|
6
7
|
import { type SyncRoutesResult } from "./sync-routes.js";
|
|
7
8
|
import { type Topology } from "./topology.js";
|
|
8
9
|
/**
|
|
@@ -97,7 +98,7 @@ export declare function isUneditedNav(existing: string, before: string[]): boole
|
|
|
97
98
|
* (`isUneditedNav`). An edited nav is the customer's file: left as-is and
|
|
98
99
|
* reported `skipped` with the change, like any other.
|
|
99
100
|
*/
|
|
100
|
-
export declare function syncNavConfig(targetAbs: string, before: string[], after: string[]): InstallReportRow | undefined;
|
|
101
|
+
export declare function syncNavConfig(targetAbs: string, before: string[], after: string[], renderOptions?: Omit<RenderNavConfigOptions, "serviceSlugs">): InstallReportRow | undefined;
|
|
101
102
|
/** The report `add-module` prints. */
|
|
102
103
|
export declare function formatAddModuleReport(result: AddModuleResult, moduleName: string): string;
|
|
103
104
|
export interface AddModuleArgs {
|
package/dist/add-module.js
CHANGED
|
@@ -34,13 +34,14 @@ import { LEGACY_NAV_RENDERERS } from "./legacy-nav-renderers.js";
|
|
|
34
34
|
import { writeAuthSecretFile } from "./lib/auth-secret-file.js";
|
|
35
35
|
import { appendEnvRows } from "./lib/declared-env.js";
|
|
36
36
|
import { diffTwoStrings, extractModuleBundle, } from "./lib/extract-module-bundle.js";
|
|
37
|
+
import { publicTreeRemoved } from "./lib/site-modules.js";
|
|
37
38
|
import { normalizeModules } from "./module-architecture.js";
|
|
38
39
|
import { MODULE_FLAGS_PATH, PAID_MCP_TOOLS_PATH, PAID_SECTIONS_PATH, writePaidSections, } from "./paid-sections.js";
|
|
39
40
|
import { ParseError, parseModules } from "./parse-args.js";
|
|
40
41
|
import { writeSpokeFlowsReadme } from "./seed-flows.js";
|
|
41
42
|
import { SITE_ROSTER_PATH, moduleRosterRows, planRosterAppend, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
42
43
|
import { spokeCheckoutSite, spokeEnvRowOptions, writeSpokeCheckoutSlice, } from "./spoke-checkout.js";
|
|
43
|
-
import { MODULE_SURFACES, NAV_CONFIG_PATH, isUnservedSurfaceContent, readNavConfigStamp, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
44
|
+
import { MODULE_SURFACES, NAV_CONFIG_PATH, isRemovedPublicRoutePath, isUnservedSurfaceContent, readNavConfigStamp, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
44
45
|
import { syncRoutes } from "./sync-routes.js";
|
|
45
46
|
import { TopologyError, isSpokeExcluded, readSiteTopology, readSpokeShape, spokeDestination, spokeModuleRefusal, } from "./topology.js";
|
|
46
47
|
/**
|
|
@@ -159,8 +160,12 @@ export async function applyModuleToScaffold(opts) {
|
|
|
159
160
|
// writes a seed under a gateable surface this site's own
|
|
160
161
|
// `architecture.yaml` header (resolved above, into `surfaces`) does
|
|
161
162
|
// not enable.
|
|
163
|
+
// #10040 D10 — a site that dropped `marketing` carries no public route
|
|
164
|
+
// tree, so a bundle's file inside one (commerce's dev/purchase launcher,
|
|
165
|
+
// a flow checkout action) is not written either.
|
|
162
166
|
skip: (destination) => (spoke !== undefined && isSpokeExcluded(destination, spoke)) ||
|
|
163
|
-
isUnservedSurfaceContent(destination, surfaces)
|
|
167
|
+
isUnservedSurfaceContent(destination, surfaces) ||
|
|
168
|
+
(publicTreeRemoved(targetAbs) && isRemovedPublicRoutePath(destination)),
|
|
164
169
|
...(spoke !== undefined
|
|
165
170
|
? {
|
|
166
171
|
// #7876 D10 — a module flow file lands in flows/ as declared data.
|
|
@@ -313,7 +318,7 @@ export function isUneditedNav(existing, before) {
|
|
|
313
318
|
* (`isUneditedNav`). An edited nav is the customer's file: left as-is and
|
|
314
319
|
* reported `skipped` with the change, like any other.
|
|
315
320
|
*/
|
|
316
|
-
export function syncNavConfig(targetAbs, before, after) {
|
|
321
|
+
export function syncNavConfig(targetAbs, before, after, renderOptions = {}) {
|
|
317
322
|
const navPath = join(targetAbs, NAV_CONFIG_PATH);
|
|
318
323
|
// #6878 D6 — a finished site already has `content/en/Service` (it exists
|
|
319
324
|
// on ANY site that ever enabled the `services` surface), so read the
|
|
@@ -324,7 +329,7 @@ export function syncNavConfig(targetAbs, before, after) {
|
|
|
324
329
|
.filter((f) => f.endsWith(".mdx"))
|
|
325
330
|
.map((f) => f.slice(0, -".mdx".length))
|
|
326
331
|
: [];
|
|
327
|
-
const wanted = renderNavConfig(after, { serviceSlugs });
|
|
332
|
+
const wanted = renderNavConfig(after, { ...renderOptions, serviceSlugs });
|
|
328
333
|
const existing = existsSync(navPath)
|
|
329
334
|
? readFileSync(navPath, "utf8")
|
|
330
335
|
: undefined;
|
package/dist/add-route.js
CHANGED
|
@@ -19,9 +19,12 @@
|
|
|
19
19
|
* never over a file the site already has.
|
|
20
20
|
*
|
|
21
21
|
* The result is the site a fresh scaffold with `--surfaces <route>` would
|
|
22
|
-
* have been
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* have been, with one exception: a surface with generated seeds
|
|
23
|
+
* (`templates`, #10051 D10) gets them only from `add-route`. A scaffold built
|
|
24
|
+
* with `--surfaces templates` has the two routes and no seeds, so
|
|
25
|
+
* `/templates` answers 404 until a hub seed is written. That scaffold adds
|
|
26
|
+
* no row to `.ailk-site-manifest.json` for a surface (the roster names only
|
|
27
|
+
* the template's own pages), so neither does this. A route the site already declares is a no-op: a second run changes
|
|
25
28
|
* nothing, even where the site has since deleted a seed.
|
|
26
29
|
*
|
|
27
30
|
* `create-ailk add-route magnet --path <path> --flow <slug>` (#8840 D12) is
|
|
@@ -46,6 +49,7 @@ import { substituteMarkers } from "./substitute.js";
|
|
|
46
49
|
import { GATEABLE_SURFACES, MODULE_SURFACES, parseSurfaces, resolveSurfaces, } from "./surfaces.js";
|
|
47
50
|
import { syncRoutes } from "./sync-routes.js";
|
|
48
51
|
import { BASE_LAYER_DIR } from "./template-manifest.js";
|
|
52
|
+
import { templatePageSeeds } from "./template-pages.js";
|
|
49
53
|
import { TopologyError, readSiteTopology } from "./topology.js";
|
|
50
54
|
/** A refusal the CLI exits 2 on, thrown before anything is written. */
|
|
51
55
|
export class AddRouteRefusedError extends Error {
|
|
@@ -115,6 +119,7 @@ export function addRouteToScaffold(opts) {
|
|
|
115
119
|
forceSurfaces: [route],
|
|
116
120
|
});
|
|
117
121
|
const report = seedSurfaceContent(targetAbs, route, opts);
|
|
122
|
+
seedGeneratedContent(targetAbs, route, report);
|
|
118
123
|
const navRow = syncNavConfig(targetAbs, current.surfaces, surfaces);
|
|
119
124
|
if (navRow !== undefined)
|
|
120
125
|
report.push(navRow);
|
|
@@ -183,25 +188,47 @@ function seedSurfaceContent(targetAbs, route, opts) {
|
|
|
183
188
|
const wanted = isText
|
|
184
189
|
? Buffer.from(substituteMarkers(readFileSync(src, "utf8"), opts.markers))
|
|
185
190
|
: readFileSync(src);
|
|
186
|
-
|
|
187
|
-
if (existsSync(dest)) {
|
|
188
|
-
const existing = readFileSync(dest);
|
|
189
|
-
if (existing.equals(wanted))
|
|
190
|
-
continue;
|
|
191
|
-
const encoding = isText ? "utf8" : "latin1";
|
|
192
|
-
report.push({
|
|
193
|
-
path,
|
|
194
|
-
action: "skipped",
|
|
195
|
-
wanted: diffTwoStrings(path, existing.toString(encoding), wanted.toString(encoding), isText),
|
|
196
|
-
});
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
200
|
-
writeFileSync(dest, wanted);
|
|
201
|
-
report.push({ path, action: "written" });
|
|
191
|
+
writeSeed(targetAbs, path, wanted, isText, report);
|
|
202
192
|
}
|
|
203
193
|
return report;
|
|
204
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Write one seed file unless the site already has it. Identical bytes are
|
|
197
|
+
* silent; different bytes are reported `skipped` with the change the seed
|
|
198
|
+
* wanted, and the site's file is left as it is.
|
|
199
|
+
*/
|
|
200
|
+
function writeSeed(targetAbs, path, wanted, isText, report) {
|
|
201
|
+
const dest = join(targetAbs, path);
|
|
202
|
+
if (existsSync(dest)) {
|
|
203
|
+
const existing = readFileSync(dest);
|
|
204
|
+
if (existing.equals(wanted))
|
|
205
|
+
return;
|
|
206
|
+
const encoding = isText ? "utf8" : "latin1";
|
|
207
|
+
report.push({
|
|
208
|
+
path,
|
|
209
|
+
action: "skipped",
|
|
210
|
+
wanted: diffTwoStrings(path, existing.toString(encoding), wanted.toString(encoding), isText),
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
215
|
+
writeFileSync(dest, wanted);
|
|
216
|
+
report.push({ path, action: "written" });
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Seeds a surface derives from a registry rather than copies from a layer
|
|
220
|
+
* (#10051 D10): the `templates` family seeds one TemplatePage per AILK
|
|
221
|
+
* template from `TEMPLATE_MANIFESTS`, so a new template never needs a new
|
|
222
|
+
* seed file. Written after the static seeds, never over a site's file.
|
|
223
|
+
*/
|
|
224
|
+
const GENERATED_SEEDS = {
|
|
225
|
+
templates: () => templatePageSeeds(),
|
|
226
|
+
};
|
|
227
|
+
function seedGeneratedContent(targetAbs, route, report) {
|
|
228
|
+
for (const { path, text } of GENERATED_SEEDS[route]?.() ?? []) {
|
|
229
|
+
writeSeed(targetAbs, path, Buffer.from(text), true, report);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
205
232
|
/** Every file under `root/dir`, recorded site-relative → absolute source. */
|
|
206
233
|
function collectFiles(root, dir, out) {
|
|
207
234
|
const abs = join(root, dir);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Per content type: mdx-body always, or only while `sections[]` is empty. */
|
|
2
|
+
export type MdxBodyRule = Map<string, "always" | "unless-sections">;
|
|
3
|
+
/**
|
|
4
|
+
* Scan `<appsRoot>/apps/web/app/**\/page.{ts,tsx}` for `renderPage({…})` calls
|
|
5
|
+
* naming `bodyMode: "mdx-body"`. A call whose `bodyMode` sits in a conditional
|
|
6
|
+
* spread is `"unless-sections"`, any other `"always"`; `"always"` wins when two
|
|
7
|
+
* routes name one type. A content hub's `HubConfig` reads the same way, from
|
|
8
|
+
* its `body`. A missing `apps/web/app` gives an empty map.
|
|
9
|
+
*/
|
|
10
|
+
export declare function mdxBodyRule(appsRoot: string): MdxBodyRule;
|
|
11
|
+
/** The routes' own `composed` test: mdx-body unless `sections` is a non-empty array. */
|
|
12
|
+
export declare function isMdxBodyPage(type: string, frontmatter: Record<string, unknown>, rule: MdxBodyRule): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* ATX level-1 headings (`^ {0,3}# +\S`) outside fenced code blocks and
|
|
15
|
+
* multi-line `{/* … *\/}` comments. `##`, `#text` and Setext headings do not
|
|
16
|
+
* count. A fence closes, as in CommonMark, on a run of its own character at
|
|
17
|
+
* least as long as the opening one, with nothing after it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function countBodyHeadings(body: string): number;
|
|
20
|
+
/**
|
|
21
|
+
* Text for an MDX body line: whitespace runs collapsed, trimmed, and every
|
|
22
|
+
* `\`, `{`, `<`, `` ` ``, `*`, `_` and `[` backslash-escaped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mdxText(text: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* The new file text for an mdx-body page with zero `# ` headings — its
|
|
27
|
+
* `# <title>` and description paragraph inserted at the top of the body —
|
|
28
|
+
* else `undefined`. A page the subset reader refuses, with no frontmatter, or
|
|
29
|
+
* with an empty `title` or `description`, is never written.
|
|
30
|
+
*/
|
|
31
|
+
export declare function addBodyHeading(raw: string, rule: MdxBodyRule, type: string): string | undefined;
|
|
32
|
+
export interface BodyHeadingPlan {
|
|
33
|
+
writes: Array<{
|
|
34
|
+
path: string;
|
|
35
|
+
text: string;
|
|
36
|
+
}>;
|
|
37
|
+
refused: Array<{
|
|
38
|
+
path: string;
|
|
39
|
+
reason: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
/** What `migrateBodyHeadings` would write, and the heading-less pages it cannot. Never throws for a page. */
|
|
43
|
+
export declare function planBodyHeadings(contentDir: string, rule: MdxBodyRule): BodyHeadingPlan;
|
|
44
|
+
/** Every mdx-body page whose body does not have exactly one `# ` heading. */
|
|
45
|
+
export declare function headingViolations(contentDir: string, rule: MdxBodyRule): Array<{
|
|
46
|
+
path: string;
|
|
47
|
+
count: number;
|
|
48
|
+
}>;
|
|
49
|
+
/** Write each planned page; the paths written and the refused rows. */
|
|
50
|
+
export declare function migrateBodyHeadings(contentDir: string, rule: MdxBodyRule): {
|
|
51
|
+
written: string[];
|
|
52
|
+
refused: Array<{
|
|
53
|
+
path: string;
|
|
54
|
+
reason: string;
|
|
55
|
+
}>;
|
|
56
|
+
};
|