@byline/cli 3.21.0 → 4.1.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 (183) hide show
  1. package/README.md +29 -3
  2. package/dist/cli.js +5 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +1 -0
  6. package/dist/commands/doctor.js.map +1 -1
  7. package/dist/commands/init.d.ts +3 -0
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +10 -14
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/setup-checks.d.ts +9 -0
  12. package/dist/commands/setup-checks.d.ts.map +1 -1
  13. package/dist/commands/setup-checks.js +43 -18
  14. package/dist/commands/setup-checks.js.map +1 -1
  15. package/dist/commands/setup.js +1 -1
  16. package/dist/commands/setup.js.map +1 -1
  17. package/dist/context.d.ts +2 -0
  18. package/dist/context.d.ts.map +1 -1
  19. package/dist/context.js +6 -0
  20. package/dist/context.js.map +1 -1
  21. package/dist/lib/dependency-version.d.ts +12 -0
  22. package/dist/lib/dependency-version.d.ts.map +1 -0
  23. package/dist/lib/dependency-version.js +101 -0
  24. package/dist/lib/dependency-version.js.map +1 -0
  25. package/dist/lib/planned-writes.d.ts +8 -0
  26. package/dist/lib/planned-writes.d.ts.map +1 -0
  27. package/dist/lib/planned-writes.js +177 -0
  28. package/dist/lib/planned-writes.js.map +1 -0
  29. package/dist/lib/planned-writes.test.d.ts +2 -0
  30. package/dist/lib/planned-writes.test.d.ts.map +1 -0
  31. package/dist/lib/planned-writes.test.js +113 -0
  32. package/dist/lib/planned-writes.test.js.map +1 -0
  33. package/dist/lib/release-policy.d.ts +12 -0
  34. package/dist/lib/release-policy.d.ts.map +1 -0
  35. package/dist/lib/release-policy.js +20 -0
  36. package/dist/lib/release-policy.js.map +1 -0
  37. package/dist/lib/release-policy.test.d.ts +2 -0
  38. package/dist/lib/release-policy.test.d.ts.map +1 -0
  39. package/dist/lib/release-policy.test.js +36 -0
  40. package/dist/lib/release-policy.test.js.map +1 -0
  41. package/dist/lib/route-config.d.ts +40 -0
  42. package/dist/lib/route-config.d.ts.map +1 -0
  43. package/dist/lib/route-config.js +322 -0
  44. package/dist/lib/route-config.js.map +1 -0
  45. package/dist/lib/static-config.d.ts +20 -0
  46. package/dist/lib/static-config.d.ts.map +1 -0
  47. package/dist/lib/static-config.js +444 -0
  48. package/dist/lib/static-config.js.map +1 -0
  49. package/dist/lib/static-config.test.d.ts +2 -0
  50. package/dist/lib/static-config.test.d.ts.map +1 -0
  51. package/dist/lib/static-config.test.js +105 -0
  52. package/dist/lib/static-config.test.js.map +1 -0
  53. package/dist/lib/template-hook-boundary.test.node.d.ts +2 -0
  54. package/dist/lib/template-hook-boundary.test.node.d.ts.map +1 -0
  55. package/dist/lib/template-hook-boundary.test.node.js +30 -0
  56. package/dist/lib/template-hook-boundary.test.node.js.map +1 -0
  57. package/dist/lib/template-path.d.ts +5 -0
  58. package/dist/lib/template-path.d.ts.map +1 -0
  59. package/dist/lib/template-path.js +9 -0
  60. package/dist/lib/template-path.js.map +1 -0
  61. package/dist/lib/workspace-root.d.ts +28 -0
  62. package/dist/lib/workspace-root.d.ts.map +1 -0
  63. package/dist/lib/workspace-root.js +205 -0
  64. package/dist/lib/workspace-root.js.map +1 -0
  65. package/dist/manifest/deps.d.ts +1 -1
  66. package/dist/manifest/deps.d.ts.map +1 -1
  67. package/dist/manifest/deps.js +24 -8
  68. package/dist/manifest/deps.js.map +1 -1
  69. package/dist/phases/deps.d.ts +14 -1
  70. package/dist/phases/deps.d.ts.map +1 -1
  71. package/dist/phases/deps.js +312 -137
  72. package/dist/phases/deps.js.map +1 -1
  73. package/dist/phases/deps.test.d.ts +2 -0
  74. package/dist/phases/deps.test.d.ts.map +1 -0
  75. package/dist/phases/deps.test.js +343 -0
  76. package/dist/phases/deps.test.js.map +1 -0
  77. package/dist/phases/preflight.d.ts.map +1 -1
  78. package/dist/phases/preflight.js +59 -11
  79. package/dist/phases/preflight.js.map +1 -1
  80. package/dist/phases/prompts.d.ts +2 -0
  81. package/dist/phases/prompts.d.ts.map +1 -1
  82. package/dist/phases/prompts.js +51 -2
  83. package/dist/phases/prompts.js.map +1 -1
  84. package/dist/phases/routes-ui.test.d.ts +2 -0
  85. package/dist/phases/routes-ui.test.d.ts.map +1 -0
  86. package/dist/phases/routes-ui.test.js +766 -0
  87. package/dist/phases/routes-ui.test.js.map +1 -0
  88. package/dist/phases/routes.d.ts +4 -1
  89. package/dist/phases/routes.d.ts.map +1 -1
  90. package/dist/phases/routes.js +610 -122
  91. package/dist/phases/routes.js.map +1 -1
  92. package/dist/phases/scaffold-smoke.test.d.ts +2 -0
  93. package/dist/phases/scaffold-smoke.test.d.ts.map +1 -0
  94. package/dist/phases/scaffold-smoke.test.js +175 -0
  95. package/dist/phases/scaffold-smoke.test.js.map +1 -0
  96. package/dist/phases/scaffold.d.ts +3 -2
  97. package/dist/phases/scaffold.d.ts.map +1 -1
  98. package/dist/phases/scaffold.js +268 -91
  99. package/dist/phases/scaffold.js.map +1 -1
  100. package/dist/phases/scaffold.test.js +171 -22
  101. package/dist/phases/scaffold.test.js.map +1 -1
  102. package/dist/phases/ui.d.ts +4 -1
  103. package/dist/phases/ui.d.ts.map +1 -1
  104. package/dist/phases/ui.js +51 -59
  105. package/dist/phases/ui.js.map +1 -1
  106. package/dist/phases/wire/index.d.ts.map +1 -1
  107. package/dist/phases/wire/index.js +10 -6
  108. package/dist/phases/wire/index.js.map +1 -1
  109. package/dist/phases/wire/shared.d.ts +4 -1
  110. package/dist/phases/wire/shared.d.ts.map +1 -1
  111. package/dist/phases/wire/vite-config.d.ts.map +1 -1
  112. package/dist/phases/wire/vite-config.js +69 -20
  113. package/dist/phases/wire/vite-config.js.map +1 -1
  114. package/dist/phases/wire/vite-config.test.d.ts +2 -0
  115. package/dist/phases/wire/vite-config.test.d.ts.map +1 -0
  116. package/dist/phases/wire/vite-config.test.js +97 -0
  117. package/dist/phases/wire/vite-config.test.js.map +1 -0
  118. package/dist/prompts.d.ts +1 -0
  119. package/dist/prompts.d.ts.map +1 -1
  120. package/dist/prompts.js +7 -2
  121. package/dist/prompts.js.map +1 -1
  122. package/dist/runner.js +1 -1
  123. package/dist/runner.js.map +1 -1
  124. package/dist/templates/byline/collection-types.contract.ts +26 -1
  125. package/dist/templates/byline/collections/index.ts +1 -1
  126. package/dist/templates/byline/collections/server-hooks.ts +7 -0
  127. package/dist/templates/byline/generated/collection-types.ts +14 -6
  128. package/dist/templates/byline/routes.ts +7 -6
  129. package/dist/templates/byline/server.config.ts +10 -1
  130. package/dist/templates/byline-examples/admin.config.ts +7 -12
  131. package/dist/templates/byline-examples/blocks/photo-block.ts +3 -2
  132. package/dist/templates/byline-examples/collection-types.contract.ts +26 -1
  133. package/dist/templates/byline-examples/collections/docs/hooks.ts +23 -13
  134. package/dist/templates/byline-examples/collections/docs/schema.ts +2 -11
  135. package/dist/templates/byline-examples/collections/index.ts +1 -1
  136. package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +11 -6
  137. package/dist/templates/byline-examples/collections/media/schema.ts +3 -0
  138. package/dist/templates/byline-examples/collections/news/schema.ts +4 -3
  139. package/dist/templates/byline-examples/collections/server-hooks.ts +8 -0
  140. package/dist/templates/byline-examples/fields/ai-text.ts +1 -1
  141. package/dist/templates/byline-examples/fields/ai-textarea.ts +1 -1
  142. package/dist/templates/byline-examples/fields/lexical-richtext-ai.tsx +3 -3
  143. package/dist/templates/byline-examples/generated/collection-types.ts +138 -130
  144. package/dist/templates/byline-examples/routes.ts +7 -6
  145. package/dist/templates/byline-examples/scripts/backfill-version-locales.ts +1 -1
  146. package/dist/templates/byline-examples/scripts/import-docs.ts +10 -11
  147. package/dist/templates/byline-examples/scripts/lib/mdast-to-lexical.test.node.ts +93 -233
  148. package/dist/templates/byline-examples/scripts/lib/mdast-to-lexical.ts +2 -2
  149. package/dist/templates/byline-examples/scripts/lib/strip-leading-h1.test.node.ts +0 -4
  150. package/dist/templates/byline-examples/scripts/re-anchor.ts +3 -3
  151. package/dist/templates/byline-examples/scripts/regenerate-media-operation.ts +112 -0
  152. package/dist/templates/byline-examples/scripts/regenerate-media.ts +129 -92
  153. package/dist/templates/byline-examples/server.config.ts +14 -6
  154. package/dist/templates/host/vite.config.ts +2 -0
  155. package/dist/templates/routes/_byline/route.lazy.tsx +1 -2
  156. package/dist/templates/routes/_byline/route.tsx +1 -1
  157. package/dist/templates/ui-byline/blocks/photo-block/index.tsx +18 -22
  158. package/dist/templates/ui-byline/blocks/richtext-block/index.tsx +1 -1
  159. package/dist/templates/ui-byline/components/admonition/index.tsx +11 -8
  160. package/dist/templates/ui-byline/components/code/code.tsx +6 -4
  161. package/dist/templates/ui-byline/components/link/lang-link.tsx +4 -8
  162. package/dist/templates/ui-byline/components/link/link-field.tsx +3 -2
  163. package/dist/templates/ui-byline/components/link/link-lexical.tsx +9 -8
  164. package/dist/templates/ui-byline/render-blocks.tsx +38 -34
  165. package/dist/templates/ui-byline/types/content.ts +25 -0
  166. package/dist/templates/ui-byline/types/i18n.ts +4 -8
  167. package/dist/test-helpers.d.ts +5 -0
  168. package/dist/test-helpers.d.ts.map +1 -0
  169. package/dist/test-helpers.js +52 -0
  170. package/dist/test-helpers.js.map +1 -0
  171. package/dist/types.d.ts +21 -1
  172. package/dist/types.d.ts.map +1 -1
  173. package/dist/ui-template-contract.test.d.ts +2 -0
  174. package/dist/ui-template-contract.test.d.ts.map +1 -0
  175. package/dist/ui-template-contract.test.js +150 -0
  176. package/dist/ui-template-contract.test.js.map +1 -0
  177. package/dist/workspace-root.test.d.ts +2 -0
  178. package/dist/workspace-root.test.d.ts.map +1 -0
  179. package/dist/workspace-root.test.js +311 -0
  180. package/dist/workspace-root.test.js.map +1 -0
  181. package/package.json +9 -1
  182. package/dist/templates/byline/client.server.ts +0 -32
  183. package/dist/templates/byline-examples/client.server.ts +0 -32
@@ -1,5 +1,8 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, relative } from 'node:path';
1
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+ import { applyPlannedWrites } from '../lib/planned-writes.js';
4
+ import { recognizeGeneratedRoutesSource, routesSourceAligned, validateRoutePaths, } from '../lib/route-config.js';
5
+ import { normalizeTemplateSource, toPosixTemplatePath } from '../lib/template-path.js';
3
6
  const TARGET_DIR = 'byline';
4
7
  const BASE_TEMPLATE = 'byline';
5
8
  const EXAMPLES_TEMPLATE = 'byline-examples';
@@ -12,124 +15,298 @@ export const scaffoldPhase = {
12
15
  title: 'Scaffold — copy byline/ config tree into the app root',
13
16
  defaultMode: 'auto',
14
17
  async detect(ctx) {
15
- if (ctx.state.isComplete('scaffold'))
16
- return 'done';
17
- return 'pending';
18
+ const inspection = inspectScaffold(ctx);
19
+ return inspection.complete ? 'done' : 'pending';
18
20
  },
19
21
  async plan(ctx) {
20
- const { examples, importDocs } = ctx.state.get().answers;
21
- const entries = collectEntries(ctx, examples ?? true, importDocs ?? false);
22
- const fromBase = entries.filter((e) => e.source === 'base' && e.status === 'create').length;
23
- const fromExamples = entries.filter((e) => e.source === 'examples' && e.status === 'create').length;
24
- const skipped = entries.filter((e) => e.status === 'skip-already-exists').length;
25
- const notes = [
26
- `target: ${ctx.resolve(TARGET_DIR)}/`,
27
- `examples overlay: ${examples === undefined ? 'unanswered (assuming yes)' : examples ? 'yes' : 'no'}`,
28
- `import-docs script: ${importDocs ? 'yes' : 'no'}`,
29
- `${fromBase} base file(s), ${fromExamples} examples file(s), ${skipped} already-existing skipped`,
30
- ];
31
- return { writes: [], commands: [], notes };
22
+ return buildScaffoldPlan(ctx);
32
23
  },
33
- async apply(_plan, ctx) {
34
- const { examples = true, importDocs = false } = ctx.state.get().answers;
35
- const entries = collectEntries(ctx, examples, importDocs);
36
- if (entries.length === 0) {
37
- ctx.logger.error('no template files found — was the cli built with templates copied to dist?');
24
+ async apply(plan, ctx) {
25
+ const answers = ctx.state.get().answers;
26
+ const routes = validateRoutePaths(ctx, answers.adminPath, answers.signInPath);
27
+ if (!routes.ok) {
28
+ ctx.logger.error(routes.error);
38
29
  return { state: 'blocked' };
39
30
  }
40
- let written = 0;
41
- let skipped = 0;
42
- for (const entry of entries) {
43
- if (entry.status === 'skip-already-exists') {
44
- skipped++;
45
- continue;
46
- }
47
- mkdirSync(dirname(entry.toAbs), { recursive: true });
48
- writeFileSync(entry.toAbs, readFileSync(entry.fromAbs, 'utf8'), 'utf8');
49
- written++;
31
+ ctx.state.patchAnswers({
32
+ adminPath: routes.value.adminPath,
33
+ signInPath: routes.value.signInPath,
34
+ });
35
+ const entries = collectTemplateEntries(ctx, answers.examples ?? true, (answers.examples ?? true) && (answers.importDocs ?? false));
36
+ if (entries.length === 0) {
37
+ ctx.logger.error('no scaffold templates found — was the CLI built with templates?');
38
+ return { state: 'blocked' };
50
39
  }
51
- ctx.logger.success(`wrote ${written} file(s) under ${ctx.resolve(TARGET_DIR)}/`);
52
- if (skipped > 0) {
53
- ctx.logger.info(`${skipped} file(s) already existed and were left untouched — re-running scaffold is non-destructive`);
40
+ const result = applyPlannedWrites(plan.writes);
41
+ if (result.written.length > 0) {
42
+ ctx.logger.success(`wrote ${result.written.length} planned scaffold file(s)`);
54
43
  }
55
- const addedScripts = ensureGenerationScripts(ctx);
56
- if (addedScripts.length > 0) {
57
- ctx.logger.success(`added package.json scripts: ${addedScripts.join(', ')}`);
44
+ if (result.conflicts.length > 0) {
45
+ ctx.logger.warn('files changed after preview and were left untouched:');
46
+ for (const path of result.conflicts)
47
+ ctx.logger.raw(` - ${path}`);
48
+ return { state: 'partial' };
58
49
  }
59
- return { state: 'done' };
50
+ const inspection = inspectScaffold(ctx);
51
+ for (const note of inspection.manualNotes)
52
+ ctx.logger.warn(note);
53
+ return { state: inspection.complete ? 'done' : 'partial' };
60
54
  },
61
55
  };
62
- export function ensureGenerationScripts(ctx) {
63
- const packagePath = ctx.resolve('package.json');
64
- if (!existsSync(packagePath))
65
- return [];
66
- const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
67
- const scripts = { ...(pkg.scripts ?? {}) };
68
- const added = [];
69
- for (const [name, command] of Object.entries(GENERATION_SCRIPTS)) {
70
- if (scripts[name] !== undefined)
56
+ export function buildScaffoldPlan(ctx) {
57
+ const answers = ctx.state.get().answers;
58
+ const routes = validateRoutePaths(ctx, answers.adminPath, answers.signInPath);
59
+ if (!routes.ok)
60
+ return { writes: [], commands: [], notes: [routes.error] };
61
+ const examples = answers.examples ?? true;
62
+ const importDocs = examples && (answers.importDocs ?? false);
63
+ const entries = collectTemplateEntries(ctx, examples, importDocs);
64
+ const writes = [];
65
+ const notes = [
66
+ `target: ${ctx.resolve(TARGET_DIR)}/`,
67
+ `examples overlay: ${examples ? 'yes' : 'no'}`,
68
+ `import-docs script: ${importDocs ? 'yes' : 'no'}`,
69
+ ];
70
+ let skipped = 0;
71
+ for (const entry of entries) {
72
+ const path = ctx.resolve(TARGET_DIR, entry.rel);
73
+ const expected = transformTemplate(entry.rel, entry.contents, routes.value.adminPath, routes.value.signInPath);
74
+ if (!existsSync(path)) {
75
+ writes.push({ path, contents: expected, mode: 'create' });
71
76
  continue;
72
- scripts[name] = command;
73
- added.push(name);
77
+ }
78
+ const current = readFileSync(path, 'utf8');
79
+ if (entry.rel === 'routes.ts' &&
80
+ normalizeTemplateSource(current) !== normalizeTemplateSource(expected)) {
81
+ if (!routesSourceAligned(current, ctx, routes.value)) {
82
+ if (recognizeGeneratedRoutesSource(current, entry.contents)) {
83
+ notes.push(`${path}: generated route migration deferred atomically to the routes phase`);
84
+ }
85
+ else {
86
+ notes.push(`${path}: manual — existing routes.ts is user-owned and does not match a generated predecessor`);
87
+ }
88
+ }
89
+ }
90
+ skipped++;
91
+ }
92
+ const packageScripts = planGenerationScripts(ctx);
93
+ if (packageScripts.write)
94
+ writes.push(packageScripts.write);
95
+ notes.push(...packageScripts.notes);
96
+ const turbo = planTurboGenerationCheck(ctx);
97
+ if (turbo.write)
98
+ writes.push(turbo.write);
99
+ if (turbo.note)
100
+ notes.push(turbo.note);
101
+ const ci = inspectCiGenerationCheck(ctx);
102
+ if (ci.note)
103
+ notes.push(ci.note);
104
+ notes.push(`${writes.length} planned write(s), ${skipped} existing scaffold file(s) preserved`);
105
+ if (entries.length === 0)
106
+ notes.push('no template files found — was the CLI built with templates?');
107
+ return { writes, commands: [], notes };
108
+ }
109
+ function inspectScaffold(ctx) {
110
+ const answers = ctx.state.get().answers;
111
+ const routes = validateRoutePaths(ctx, answers.adminPath, answers.signInPath);
112
+ if (!routes.ok)
113
+ return { complete: false, manualNotes: [routes.error] };
114
+ const entries = collectTemplateEntries(ctx, answers.examples ?? true, (answers.examples ?? true) && (answers.importDocs ?? false));
115
+ if (entries.length === 0)
116
+ return { complete: false, manualNotes: [] };
117
+ const missing = entries.filter((entry) => !existsSync(ctx.resolve(TARGET_DIR, entry.rel)));
118
+ const manualNotes = [];
119
+ const routeEntry = entries.find((entry) => entry.rel === 'routes.ts');
120
+ if (routeEntry) {
121
+ const path = ctx.resolve(TARGET_DIR, routeEntry.rel);
122
+ if (existsSync(path)) {
123
+ const expected = transformTemplate(routeEntry.rel, routeEntry.contents, routes.value.adminPath, routes.value.signInPath);
124
+ if (normalizeTemplateSource(readFileSync(path, 'utf8')) !== normalizeTemplateSource(expected)) {
125
+ const current = readFileSync(path, 'utf8');
126
+ if (!routesSourceAligned(current, ctx, routes.value) &&
127
+ !recognizeGeneratedRoutesSource(current, routeEntry.contents)) {
128
+ manualNotes.push(`${path}: manual update required so routes.admin and routes.signIn match ${routes.value.adminPath} and ${routes.value.signInPath}`);
129
+ }
130
+ }
131
+ }
74
132
  }
75
- if (added.length === 0)
76
- return added;
77
- writeFileSync(packagePath, `${JSON.stringify({ ...pkg, scripts }, null, 2)}\n`, 'utf8');
78
- return added;
133
+ const scriptsComplete = generationScriptsComplete(ctx);
134
+ const packageNotes = planGenerationScripts(ctx).notes;
135
+ const turbo = inspectTurboGenerationCheck(ctx);
136
+ const ci = inspectCiGenerationCheck(ctx);
137
+ if (turbo.note)
138
+ manualNotes.push(turbo.note);
139
+ if (ci.note)
140
+ manualNotes.push(ci.note);
141
+ manualNotes.push(...packageNotes);
142
+ return {
143
+ complete: missing.length === 0 &&
144
+ scriptsComplete &&
145
+ turbo.complete &&
146
+ ci.complete &&
147
+ manualNotes.length === 0,
148
+ manualNotes,
149
+ };
79
150
  }
80
- function collectEntries(ctx, examples, importDocs) {
81
- const targetRoot = ctx.resolve(TARGET_DIR);
151
+ function collectTemplateEntries(ctx, examples, importDocs) {
82
152
  const baseRoot = join(ctx.templatesDir(), BASE_TEMPLATE);
83
153
  const examplesRoot = join(ctx.templatesDir(), EXAMPLES_TEMPLATE);
84
154
  const allExamples = examples && existsSync(examplesRoot) ? walkFiles(examplesRoot) : [];
85
- // The markdown → Byline import example script lives at
86
- // `scripts/import-docs.ts` plus everything under `scripts/lib/`. Those
87
- // files (and their devDependency stack) are opt-in via the `prompts`
88
- // phase; everything else under `scripts/` (currently
89
- // `regenerate-media.ts`) is part of the standard examples overlay.
90
- const examplesFiles = importDocs
91
- ? allExamples
92
- : allExamples.filter((abs) => {
93
- const rel = relative(examplesRoot, abs);
94
- return rel !== 'scripts/import-docs.ts' && !rel.startsWith('scripts/lib/');
95
- });
96
- const examplesRels = new Set(examplesFiles.map((abs) => relative(examplesRoot, abs)));
155
+ const exampleFiles = allExamples.filter((abs) => {
156
+ return shouldIncludeExampleTemplate(relative(examplesRoot, abs), importDocs);
157
+ });
158
+ const exampleRels = new Set(exampleFiles.map((abs) => toPosixTemplatePath(relative(examplesRoot, abs))));
97
159
  const entries = [];
98
160
  if (existsSync(baseRoot)) {
99
161
  for (const abs of walkFiles(baseRoot)) {
100
- const rel = relative(baseRoot, abs);
101
- // The examples tree supersedes the base for any path the two share
102
- // (e.g. server.config.ts, admin.config.ts, seed.ts). Skip the base
103
- // entry so we don't queue two writes for the same target file.
104
- if (examplesRels.has(rel))
162
+ const rel = toPosixTemplatePath(relative(baseRoot, abs));
163
+ if (exampleRels.has(rel))
105
164
  continue;
106
- const toAbs = join(targetRoot, rel);
107
- entries.push({
108
- fromAbs: abs,
109
- rel,
110
- toAbs,
111
- source: 'base',
112
- status: existsSync(toAbs) ? 'skip-already-exists' : 'create',
113
- });
165
+ entries.push({ rel, contents: readFileSync(abs, 'utf8') });
114
166
  }
115
167
  }
116
- for (const abs of examplesFiles) {
117
- const rel = relative(examplesRoot, abs);
118
- const toAbs = join(targetRoot, rel);
168
+ for (const abs of exampleFiles) {
119
169
  entries.push({
120
- fromAbs: abs,
121
- rel,
122
- toAbs,
123
- source: 'examples',
124
- status: existsSync(toAbs) ? 'skip-already-exists' : 'create',
170
+ rel: toPosixTemplatePath(relative(examplesRoot, abs)),
171
+ contents: readFileSync(abs, 'utf8'),
125
172
  });
126
173
  }
127
- return entries;
174
+ return entries.sort((a, b) => a.rel.localeCompare(b.rel));
175
+ }
176
+ export function shouldIncludeExampleTemplate(relativePath, importDocs) {
177
+ const rel = toPosixTemplatePath(relativePath);
178
+ if (rel.endsWith('.test.node.ts'))
179
+ return false;
180
+ if (importDocs)
181
+ return true;
182
+ return rel !== 'scripts/import-docs.ts' && !rel.startsWith('scripts/lib/');
183
+ }
184
+ function transformTemplate(rel, contents, adminPath, signInPath) {
185
+ if (toPosixTemplatePath(rel) !== 'routes.ts')
186
+ return contents;
187
+ return contents
188
+ .replace(/(admin\s*:\s*['"])[^'"]+(['"])/, `$1${adminPath}$2`)
189
+ .replace(/(signIn\s*:\s*['"])[^'"]+(['"])/, `$1${signInPath}$2`);
190
+ }
191
+ function planGenerationScripts(ctx) {
192
+ const path = ctx.resolve('package.json');
193
+ if (!existsSync(path))
194
+ return { notes: [`${path}: manual — package.json is missing`] };
195
+ const before = readFileSync(path, 'utf8');
196
+ let pkg;
197
+ try {
198
+ pkg = JSON.parse(before);
199
+ }
200
+ catch {
201
+ return { notes: [`${path}: manual — package.json is not valid JSON`] };
202
+ }
203
+ const scripts = { ...(pkg.scripts ?? {}) };
204
+ let changed = false;
205
+ const notes = [];
206
+ for (const [name, command] of Object.entries(GENERATION_SCRIPTS)) {
207
+ if (scripts[name] !== undefined) {
208
+ if (scripts[name] !== command) {
209
+ notes.push(`${path}: manual — existing ${name} script differs from the canonical command`);
210
+ }
211
+ continue;
212
+ }
213
+ scripts[name] = command;
214
+ changed = true;
215
+ }
216
+ if (!changed)
217
+ return { notes };
218
+ return {
219
+ write: {
220
+ path,
221
+ contents: `${JSON.stringify({ ...pkg, scripts }, null, 2)}\n`,
222
+ mode: 'patch',
223
+ before,
224
+ },
225
+ notes,
226
+ };
227
+ }
228
+ function generationScriptsComplete(ctx) {
229
+ try {
230
+ const pkg = JSON.parse(readFileSync(ctx.resolve('package.json'), 'utf8'));
231
+ return Object.entries(GENERATION_SCRIPTS).every(([name, command]) => pkg.scripts?.[name] === command);
232
+ }
233
+ catch {
234
+ return false;
235
+ }
236
+ }
237
+ function planTurboGenerationCheck(ctx) {
238
+ const path = ctx.resolveWorkspace('turbo.json');
239
+ if (!existsSync(path))
240
+ return {};
241
+ const before = readFileSync(path, 'utf8');
242
+ let config;
243
+ try {
244
+ config = JSON.parse(before);
245
+ }
246
+ catch {
247
+ return { note: `${path}: manual — add a byline:generate:check task; config is not plain JSON` };
248
+ }
249
+ if (!config.tasks || typeof config.tasks !== 'object' || Array.isArray(config.tasks)) {
250
+ return { note: `${path}: manual — add a byline:generate:check task under tasks` };
251
+ }
252
+ if (config.tasks['byline:generate:check'] !== undefined)
253
+ return {};
254
+ const tasks = { ...config.tasks, 'byline:generate:check': { outputs: [] } };
255
+ return {
256
+ write: {
257
+ path,
258
+ contents: `${JSON.stringify({ ...config, tasks }, null, 2)}\n`,
259
+ mode: 'patch',
260
+ before,
261
+ },
262
+ };
263
+ }
264
+ function inspectTurboGenerationCheck(ctx) {
265
+ const path = ctx.resolveWorkspace('turbo.json');
266
+ if (!existsSync(path))
267
+ return { complete: true };
268
+ let config;
269
+ try {
270
+ config = JSON.parse(readFileSync(path, 'utf8'));
271
+ }
272
+ catch {
273
+ return {
274
+ complete: false,
275
+ note: `${path}: manual — add a byline:generate:check task; config is not plain JSON`,
276
+ };
277
+ }
278
+ if (!config.tasks || typeof config.tasks !== 'object' || Array.isArray(config.tasks)) {
279
+ return {
280
+ complete: false,
281
+ note: `${path}: manual — add a byline:generate:check task under tasks`,
282
+ };
283
+ }
284
+ return { complete: config.tasks['byline:generate:check'] !== undefined };
285
+ }
286
+ function inspectCiGenerationCheck(ctx) {
287
+ const path = ctx.resolveWorkspace('.github/workflows/ci.yml');
288
+ if (!existsSync(path))
289
+ return { complete: true };
290
+ if (ciIncludesGenerationCheck(readFileSync(path, 'utf8')))
291
+ return { complete: true };
292
+ return {
293
+ complete: false,
294
+ note: `${path}: manual — add byline:generate:check to the existing CI workflow; arbitrary CI YAML is never rewritten`,
295
+ };
296
+ }
297
+ function ciIncludesGenerationCheck(source) {
298
+ return source.split(/\r?\n/).some((line) => {
299
+ const trimmed = line.trim();
300
+ return (trimmed.length > 0 &&
301
+ !trimmed.startsWith('#') &&
302
+ !trimmed.startsWith('name:') &&
303
+ trimmed.includes('byline:generate:check'));
304
+ });
128
305
  }
129
306
  function walkFiles(root) {
130
307
  const out = [];
131
308
  function walk(dir) {
132
- for (const name of readdirSync(dir)) {
309
+ for (const name of readdirSync(dir).sort()) {
133
310
  const abs = join(dir, name);
134
311
  const st = statSync(abs);
135
312
  if (st.isDirectory())
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/phases/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/phases/scaffold.ts"],"names":[],"mappings":"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"}
@@ -1,29 +1,178 @@
1
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join, resolve } from 'node:path';
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
2
  import { afterEach, describe, expect, it } from 'vitest';
5
- import { ensureGenerationScripts } from './scaffold.js';
6
- const temporaryDirectories = [];
3
+ import { createTestContext } from '../test-helpers.js';
4
+ import { buildRoutesPlan, routesPhase } from './routes.js';
5
+ import { buildScaffoldPlan, scaffoldPhase, shouldIncludeExampleTemplate } from './scaffold.js';
6
+ const contexts = [];
7
7
  afterEach(() => {
8
- for (const directory of temporaryDirectories.splice(0)) {
9
- rmSync(directory, { recursive: true, force: true });
10
- }
8
+ for (const ctx of contexts.splice(0))
9
+ rmSync(ctx.cwd, { recursive: true, force: true });
11
10
  });
12
- describe('ensureGenerationScripts', () => {
13
- it('adds generation commands without replacing an existing script', () => {
14
- const directory = mkdtempSync(join(tmpdir(), 'byline-scaffold-'));
15
- temporaryDirectories.push(directory);
16
- writeFileSync(join(directory, 'package.json'), `${JSON.stringify({ scripts: { 'byline:generate': 'custom-generator' } }, null, 2)}\n`, 'utf8');
17
- const ctx = {
18
- resolve: (...parts) => resolve(directory, ...parts),
19
- };
20
- expect(ensureGenerationScripts(ctx)).toEqual(['byline:generate:check']);
21
- const pkg = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'));
22
- expect(pkg.scripts).toEqual({
23
- 'byline:generate': 'custom-generator',
24
- 'byline:generate:check': 'tsx byline/scripts/generate-types.ts --check',
11
+ function fixture(answers = {}) {
12
+ const ctx = createTestContext(answers);
13
+ contexts.push(ctx);
14
+ writeFileSync(ctx.resolve('package.json'), `${JSON.stringify({ name: 'fixture', scripts: { test: 'vitest' } }, null, 2)}\n`);
15
+ return ctx;
16
+ }
17
+ describe('scaffold planning', () => {
18
+ it('normalizes backslash paths before inventory filtering', () => {
19
+ expect(shouldIncludeExampleTemplate('scripts\\import-docs.ts', false)).toBe(false);
20
+ expect(shouldIncludeExampleTemplate('scripts\\lib\\helper.ts', false)).toBe(false);
21
+ expect(shouldIncludeExampleTemplate('scripts\\lib\\helper.ts', true)).toBe(true);
22
+ expect(shouldIncludeExampleTemplate('scripts\\lib\\helper.test.node.ts', true)).toBe(false);
23
+ });
24
+ it('plans concrete default-example writes and applies exactly their previewed contents', async () => {
25
+ const ctx = fixture({ examples: true, importDocs: false, adminPath: '/admin' });
26
+ const plan = buildScaffoldPlan(ctx);
27
+ expect(plan.writes.some((write) => write.path.endsWith('byline/routes.ts'))).toBe(true);
28
+ expect(plan.writes.some((write) => write.path.includes('import-docs'))).toBe(false);
29
+ expect((await scaffoldPhase.apply(plan, ctx)).state).toBe('done');
30
+ for (const write of plan.writes)
31
+ expect(readFileSync(write.path, 'utf8')).toBe(write.contents);
32
+ });
33
+ it('creates custom runtime routes and never overwrites a divergent routes.ts', async () => {
34
+ const ctx = fixture({ examples: false, adminPath: '/cms', signInPath: '/staff/login' });
35
+ const initial = buildScaffoldPlan(ctx);
36
+ const routesWrite = initial.writes.find((write) => write.path.endsWith('byline/routes.ts'));
37
+ expect(routesWrite?.contents).toContain("admin: '/cms'");
38
+ expect(routesWrite?.contents).toContain("signIn: '/staff/login'");
39
+ await scaffoldPhase.apply(initial, ctx);
40
+ writeFileSync(ctx.resolve('byline/routes.ts'), "export const routes = { admin: '/mine' }\n");
41
+ const rerun = buildScaffoldPlan(ctx);
42
+ expect(rerun.writes.some((write) => write.path.endsWith('byline/routes.ts'))).toBe(false);
43
+ expect(rerun.notes.join('\n')).toContain('manual');
44
+ expect((await scaffoldPhase.apply(rerun, ctx)).state).toBe('partial');
45
+ expect(readFileSync(ctx.resolve('byline/routes.ts'), 'utf8')).toContain("'/mine'");
46
+ });
47
+ it('counts matching user-owned routes as aligned without claiming write ownership', async () => {
48
+ const ctx = fixture({ examples: false, adminPath: '/cms', signInPath: '/staff/login' });
49
+ const path = ctx.resolve('byline/routes.ts');
50
+ const source = `
51
+ const values = { admin: '/cms', signIn: '/staff/login' } as const
52
+ export const routes = { api: '/rpc', ...values } satisfies Record<string, string>
53
+ // user-owned
54
+ `;
55
+ mkdirSync(ctx.resolve('byline'), { recursive: true });
56
+ writeFileSync(path, source);
57
+ const plan = buildScaffoldPlan(ctx);
58
+ expect(plan.writes.some((write) => write.path === path)).toBe(false);
59
+ expect(plan.notes.join('\n')).not.toContain('manual — existing routes.ts');
60
+ expect((await scaffoldPhase.apply(plan, ctx)).state).toBe('done');
61
+ expect(readFileSync(path, 'utf8')).toBe(source);
62
+ });
63
+ it('defers the literal previous-release config migration and completes it in routes', async () => {
64
+ const ctx = fixture({
65
+ examples: false,
66
+ importDocs: false,
67
+ adminPath: '/admin',
68
+ signInPath: '/staff/login',
25
69
  });
26
- expect(ensureGenerationScripts(ctx)).toEqual([]);
70
+ const configPath = ctx.resolve('byline/routes.ts');
71
+ mkdirSync(ctx.resolve('byline'), { recursive: true });
72
+ writeFileSync(configPath, previousReleaseRoutesSource());
73
+ const scaffold = buildScaffoldPlan(ctx);
74
+ expect(scaffold.writes.some((write) => write.path === configPath)).toBe(false);
75
+ expect(scaffold.notes.join('\n')).toContain('deferred atomically to the routes phase');
76
+ expect((await scaffoldPhase.apply(scaffold, ctx)).state).toBe('done');
77
+ expect(await scaffoldPhase.detect(ctx)).toBe('done');
78
+ expect(readFileSync(configPath, 'utf8')).not.toContain('signIn:');
79
+ expect((await routesPhase.apply(buildRoutesPlan(ctx), ctx)).state).toBe('done');
80
+ expect(readFileSync(configPath, 'utf8')).toContain("signIn: '/staff/login'");
81
+ expect(await routesPhase.detect(ctx)).toBe('done');
82
+ expect(await scaffoldPhase.detect(ctx)).toBe('done');
83
+ });
84
+ it('excludes import helper tests while including the optional helper implementation', () => {
85
+ const ctx = fixture({ examples: true, importDocs: true });
86
+ const plan = buildScaffoldPlan(ctx);
87
+ expect(plan.writes.some((write) => write.path.endsWith('scripts/import-docs.ts'))).toBe(true);
88
+ expect(plan.writes.some((write) => write.path.includes('/scripts/') && write.path.endsWith('.test.node.ts'))).toBe(false);
89
+ });
90
+ it('does not scaffold package-owned boundary tests or script helper tests', () => {
91
+ const ctx = fixture({ examples: true, importDocs: true });
92
+ const plan = buildScaffoldPlan(ctx);
93
+ expect(plan.writes.some((write) => write.path.endsWith('client-hook-boundary.test.node.ts') ||
94
+ (write.path.includes('/scripts/') && write.path.endsWith('.test.node.ts')))).toBe(false);
95
+ });
96
+ it('patches a recognized Turbo task map and leaves unknown config manual', () => {
97
+ const ctx = fixture({ examples: false });
98
+ writeFileSync(ctx.resolve('turbo.json'), '{"tasks":{"typecheck":{"outputs":[]}}}\n');
99
+ let plan = buildScaffoldPlan(ctx);
100
+ const turboWrite = plan.writes.find((write) => write.path.endsWith('turbo.json'));
101
+ expect(turboWrite?.contents).toContain('byline:generate:check');
102
+ writeFileSync(ctx.resolve('turbo.json'), '{ // jsonc\n"tasks": {}\n}\n');
103
+ plan = buildScaffoldPlan(ctx);
104
+ expect(plan.writes.some((write) => write.path.endsWith('turbo.json'))).toBe(false);
105
+ expect(plan.notes.join('\n')).toContain('manual');
106
+ });
107
+ it('keeps known CI without generation checking partial across reruns', async () => {
108
+ const ctx = fixture({ examples: false });
109
+ mkdirSync(ctx.resolve('.github/workflows'), { recursive: true });
110
+ writeFileSync(ctx.resolve('.github/workflows/ci.yml'), 'steps:\n # TODO: add byline:generate:check\n - run: pnpm typecheck\n');
111
+ const plan = buildScaffoldPlan(ctx);
112
+ expect(plan.notes.join('\n')).toContain('manual');
113
+ expect((await scaffoldPhase.apply(plan, ctx)).state).toBe('partial');
114
+ expect(await scaffoldPhase.detect(ctx)).toBe('pending');
115
+ writeFileSync(ctx.resolve('.github/workflows/ci.yml'), 'steps:\n - run: pnpm byline:generate:check\n');
116
+ expect(await scaffoldPhase.detect(ctx)).toBe('done');
117
+ });
118
+ it('applies a recognized missing Turbo task but keeps unknown Turbo config partial', async () => {
119
+ const recognized = fixture({ examples: false });
120
+ writeFileSync(recognized.resolve('turbo.json'), '{"tasks":{"typecheck":{}}}\n');
121
+ expect((await scaffoldPhase.apply(buildScaffoldPlan(recognized), recognized)).state).toBe('done');
122
+ expect(await scaffoldPhase.detect(recognized)).toBe('done');
123
+ const unknown = fixture({ examples: false });
124
+ writeFileSync(unknown.resolve('turbo.json'), '{ // jsonc\n"tasks": {}\n}\n');
125
+ expect((await scaffoldPhase.apply(buildScaffoldPlan(unknown), unknown)).state).toBe('partial');
126
+ expect(await scaffoldPhase.detect(unknown)).toBe('pending');
127
+ });
128
+ it('preserves a divergent generation script and reports it as manual', () => {
129
+ const ctx = fixture({ examples: false });
130
+ writeFileSync(ctx.resolve('package.json'), `${JSON.stringify({ scripts: { 'byline:generate': 'custom-generator' } }, null, 2)}\n`);
131
+ const plan = buildScaffoldPlan(ctx);
132
+ const packageWrite = plan.writes.find((write) => write.path.endsWith('package.json'));
133
+ expect(packageWrite?.contents).toContain('custom-generator');
134
+ expect(packageWrite?.contents).toContain('byline:generate:check');
135
+ expect(plan.notes.join('\n')).toContain('manual');
136
+ });
137
+ it('structurally detects missing v3.21 artifacts despite a completed phase flag', async () => {
138
+ const ctx = fixture({ examples: false });
139
+ ctx.state.markPhaseComplete('scaffold');
140
+ expect(await scaffoldPhase.detect(ctx)).toBe('pending');
141
+ const plan = buildScaffoldPlan(ctx);
142
+ await scaffoldPhase.apply(plan, ctx);
143
+ expect(existsSync(ctx.resolve('byline/generated/collection-types.ts'))).toBe(true);
144
+ expect(await scaffoldPhase.detect(ctx)).toBe('done');
27
145
  });
28
146
  });
147
+ function previousReleaseRoutesSource() {
148
+ return `/**
149
+ * This Source Code is subject to the terms of the Mozilla Public
150
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
151
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
152
+ *
153
+ * Copyright (c) Infonomic Company Limited
154
+ */
155
+
156
+ /**
157
+ * URL segments for admin and (future) public API routes. Defaults of
158
+ * \`/admin\` and \`/api\` are applied automatically by \`resolveRoutes()\` —
159
+ * keys only need to be set here when overriding either default.
160
+ */
161
+
162
+ import type { RoutesConfig } from '@byline/core'
163
+
164
+ export const routes: Partial<RoutesConfig> = {
165
+ admin: '/admin',
166
+ api: '/api',
167
+ }
168
+
169
+ /**
170
+ * Fallback used by both server and admin entry points when no
171
+ * \`VITE_SERVER_URL\` env var is set. Each entry resolves the env var
172
+ * itself (Vite's \`import.meta.env\` on the client, Node's \`process.env\`
173
+ * on the server) and falls back to this literal.
174
+ */
175
+ export const DEFAULT_SERVER_URL = 'http://localhost:5173/'
176
+ `;
177
+ }
29
178
  //# sourceMappingURL=scaffold.test.js.map