@mpfrontend/nx-plugin 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +89 -2
  3. package/dist/core.d.ts +22 -0
  4. package/dist/core.js +46 -0
  5. package/dist/core.js.map +1 -0
  6. package/dist/feature-generator.d.ts +7 -0
  7. package/dist/feature-generator.js +9 -0
  8. package/dist/feature-generator.js.map +1 -0
  9. package/dist/generator.d.ts +6 -0
  10. package/dist/generator.js +14 -0
  11. package/dist/generator.js.map +1 -0
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal.d.ts +1 -0
  16. package/dist/internal.js +3 -0
  17. package/dist/internal.js.map +1 -0
  18. package/dist/nx-tree.d.ts +5 -0
  19. package/dist/nx-tree.js +12 -0
  20. package/dist/nx-tree.js.map +1 -0
  21. package/dist/package-generator.d.ts +8 -0
  22. package/dist/package-generator.js +9 -0
  23. package/dist/package-generator.js.map +1 -0
  24. package/dist/route-generator.d.ts +8 -0
  25. package/dist/route-generator.js +9 -0
  26. package/dist/route-generator.js.map +1 -0
  27. package/dist/scaffold.d.ts +96 -0
  28. package/dist/scaffold.js +227 -0
  29. package/dist/scaffold.js.map +1 -0
  30. package/dist/templates.d.ts +21 -0
  31. package/dist/templates.js +83 -0
  32. package/dist/templates.js.map +1 -0
  33. package/dist/workspace.d.ts +41 -0
  34. package/dist/workspace.js +213 -0
  35. package/dist/workspace.js.map +1 -0
  36. package/generators.json +24 -0
  37. package/package.json +41 -3
  38. package/schema.json +18 -0
  39. package/schemas/feature-schema.json +25 -0
  40. package/schemas/package-schema.json +28 -0
  41. package/schemas/route-schema.json +31 -0
  42. package/templates/feature/list/api/server/__resource__.ts.template +24 -0
  43. package/templates/feature/list/app/api/__resource__/route.ts.template +24 -0
  44. package/templates/feature/list/entities/__resource__/index.ts.template +12 -0
  45. package/templates/feature/list/entities/__resource__/model/__resource__-list.test.ts.template +23 -0
  46. package/templates/feature/list/entities/__resource__/model/__resource__-list.ts.template +24 -0
  47. package/templates/feature/list/entities/__resource__/model/__resource__.test.ts.template +17 -0
  48. package/templates/feature/list/entities/__resource__/model/__resource__.ts.template +19 -0
  49. package/templates/feature/list/features/__feature__/api/__feature__-read.ts.template +12 -0
  50. package/templates/feature/list/features/__feature__/api/__feature__.ts.template +28 -0
  51. package/templates/feature/list/features/__feature__/hooks/use-__feature__-list.ts.template +22 -0
  52. package/templates/feature/list/features/__feature__/index.ts.template +3 -0
  53. package/templates/feature/list/features/__feature__/model/__feature__-fields.ts.template +4 -0
  54. package/templates/feature/list/features/__feature__/model/overrides/index.ts.template +3 -0
  55. package/templates/feature/list/features/__feature__/ui/__feature__-detail-screen.tsx.template +16 -0
  56. package/templates/feature/list/features/__feature__/ui/__feature__-detail.test.tsx.template +28 -0
  57. package/templates/feature/list/features/__feature__/ui/__feature__-detail.tsx.template +44 -0
  58. package/templates/feature/list/features/__feature__/ui/__feature__-list.tsx.template +75 -0
  59. package/templates/feature/list/features/__feature__/ui/__feature__-screen.tsx.template +31 -0
  60. package/templates/feature/list/features/__feature__/utils/__feature__-href.test.ts.template +7 -0
  61. package/templates/feature/list/features/__feature__/utils/__feature__-href.ts.template +4 -0
  62. package/templates/feature/screen/features/__feature__/api/__feature__.ts.template +13 -0
  63. package/templates/feature/screen/features/__feature__/hooks/use-__feature__.ts.template +27 -0
  64. package/templates/feature/screen/features/__feature__/index.ts.template +3 -0
  65. package/templates/feature/screen/features/__feature__/model/__feature__.test.ts.template +12 -0
  66. package/templates/feature/screen/features/__feature__/model/__feature__.ts.template +12 -0
  67. package/templates/feature/screen/features/__feature__/ui/__feature__-panel.tsx.template +22 -0
  68. package/templates/feature/screen/features/__feature__/ui/__feature__-screen.tsx.template +15 -0
  69. package/templates/feature/screen/features/__feature__/ui/__feature__-view.test.tsx.template +25 -0
  70. package/templates/feature/screen/features/__feature__/ui/__feature__-view.tsx.template +38 -0
  71. package/templates/feature/screen/features/__feature__/utils/__feature__.test.ts.template +9 -0
  72. package/templates/feature/screen/features/__feature__/utils/__feature__.ts.template +6 -0
  73. package/templates/next/README.md.template +10 -0
  74. package/templates/next/docs/api-contracts.md.template +22 -0
  75. package/templates/next/docs/overview.md.template +67 -0
  76. package/templates/next/eslint.config.mjs.template +4 -0
  77. package/templates/next/ftg.config.json.template +11 -0
  78. package/templates/next/next-env.d.ts.template +2 -0
  79. package/templates/next/next.config.mjs.template +9 -0
  80. package/templates/next/package.json.template +27 -0
  81. package/templates/next/postcss.config.mjs.template +2 -0
  82. package/templates/next/prepare-runtime.mjs.template +11 -0
  83. package/templates/next/project.json.template +113 -0
  84. package/templates/next/src/api/server/transfer.test.ts.template +153 -0
  85. package/templates/next/src/api/server/transfer.ts.template +25 -0
  86. package/templates/next/src/app/api/access/route.ts.template +6 -0
  87. package/templates/next/src/app/api/docs/assets/[file]/route.ts.template +23 -0
  88. package/templates/next/src/app/api/docs/route.ts.template +8 -0
  89. package/templates/next/src/app/api/openapi/route.ts.template +12 -0
  90. package/templates/next/src/app/api/transfer/[...path]/route.ts.template +22 -0
  91. package/templates/next/src/app/components/page.tsx.template +32 -0
  92. package/templates/next/src/app/globals.css.template +32 -0
  93. package/templates/next/src/app/layout.tsx.template +53 -0
  94. package/templates/next/src/app/page.tsx.template +6 -0
  95. package/templates/next/src/app/providers.tsx.template +31 -0
  96. package/templates/next/src/config/access.ts.template +21 -0
  97. package/templates/next/src/config/app.ts.template +33 -0
  98. package/templates/next/src/config/server.ts.template +13 -0
  99. package/templates/next/src/features/preferences/api/preference-cookie.ts.template +14 -0
  100. package/templates/next/src/features/preferences/hooks/use-preferences.ts.template +30 -0
  101. package/templates/next/src/features/preferences/index.ts.template +3 -0
  102. package/templates/next/src/features/preferences/model/messages/en.ts.template +10 -0
  103. package/templates/next/src/features/preferences/model/messages.ts.template +13 -0
  104. package/templates/next/src/features/preferences/model/preferences.test.ts.template +32 -0
  105. package/templates/next/src/features/preferences/model/preferences.ts.template +22 -0
  106. package/templates/next/src/features/preferences/ui/preference-controls.tsx.template +50 -0
  107. package/templates/next/src/i18n/index.ts.template +13 -0
  108. package/templates/next/src/i18n/messages/en.ts.template +18 -0
  109. package/templates/next/src/i18n/server.ts.template +8 -0
  110. package/templates/next/src/theme/config.test.ts.template +9 -0
  111. package/templates/next/src/theme/config.ts.template +7 -0
  112. package/templates/next/src/theme/theme.css.template +8 -0
  113. package/templates/next/tsconfig.json.template +11 -0
  114. package/templates/package/README.md.template +10 -0
  115. package/templates/package/eslint.config.mjs.template +4 -0
  116. package/templates/package/package.json.template +15 -0
  117. package/templates/package/project.json.template +51 -0
  118. package/templates/package/src/index.ts.template +2 -0
  119. package/templates/package/src/package-name.test.ts.template +7 -0
  120. package/templates/package/src/package-name.ts.template +2 -0
  121. package/templates/package/tsconfig.json.template +7 -0
  122. package/templates/package/tsconfig.lib.json.template +9 -0
@@ -0,0 +1,227 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { posix, resolve } from 'node:path';
3
+ import { NAME_PATTERN, constant, exists, importPath, pascal, safeRelative, templateFiles, workspaceFormatter, writeNew } from './templates.js';
4
+ export function diskReader(root) {
5
+ return {
6
+ exists: path => exists(resolve(root, path)),
7
+ read: async (path) => { try {
8
+ return await readFile(resolve(root, path), 'utf8');
9
+ }
10
+ catch (error) {
11
+ if (error.code === 'ENOENT')
12
+ return undefined;
13
+ throw error;
14
+ } },
15
+ };
16
+ }
17
+ const prefix = (directory, files) => Object.fromEntries(Object.entries(files).map(([path, content]) => [directory + '/' + path, content]));
18
+ /** The application template ships English only; a product adds its own locales to its registry. */
19
+ export const TEMPLATE_LOCALES = { defaultLocale: 'en', locales: ['en'] };
20
+ const localeCode = /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/;
21
+ /** Reads the registry of `src/config/app.ts` as the application template writes it. */
22
+ export function readAppLocales(source) {
23
+ const body = /createLocaleRegistry\(\{[\s\S]*?locales:\s*\{([\s\S]*?)\}\s*,\s*defaultLocale:\s*'([^']+)'/.exec(source);
24
+ if (!body)
25
+ return undefined;
26
+ const locales = [...body[1].matchAll(/(?:^|[,{\s])'?([a-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3})'?\s*:\s*\{/g)].map(match => match[1]);
27
+ if (!locales.length || !locales.includes(body[2]) || locales.some(code => !localeCode.test(code)))
28
+ return undefined;
29
+ return { defaultLocale: body[2], locales };
30
+ }
31
+ const humanize = (name) => name.charAt(0).toUpperCase() + name.slice(1).replaceAll('-', ' ');
32
+ const quote = (value) => "'" + value.replaceAll('\\', '\\\\').replaceAll("'", "\\'") + "'";
33
+ const identifier = (code) => code.replaceAll('-', '_');
34
+ /**
35
+ * The catalog set of a feature: one module per app locale under `model/messages/`, and `model/messages.ts`.
36
+ * The base locale holds the starter text; every other locale starts with the same text, to be translated.
37
+ */
38
+ export function messageFiles(directory, base, locales, depth) {
39
+ const entries = Object.entries(base).map(([key, value]) => ' ' + key + ': ' + quote(value) + ',').join('\n');
40
+ const files = {};
41
+ files[directory + '/messages/' + locales.defaultLocale + '.ts'] = `import { defineMessages } from '@mpfrontend/i18n';\n\nexport default defineMessages({\n${entries}\n});\n`;
42
+ for (const code of locales.locales.filter(code => code !== locales.defaultLocale)) {
43
+ files[directory + '/messages/' + code + '.ts'] = `import type { Translation } from '@mpfrontend/i18n';\nimport type base from './${locales.defaultLocale}';\n\n// Starts with the base text: translate each message.\nexport default {\n${entries}\n} satisfies Translation<typeof base>;\n`;
44
+ }
45
+ const others = locales.locales.filter(code => code !== locales.defaultLocale);
46
+ files[directory + '/messages.ts'] = `import { coreMessages, createMessages } from '@mpfrontend/i18n';
47
+ import { localeRegistry } from '${'../'.repeat(depth)}config/app';
48
+ import base from './messages/${locales.defaultLocale}';
49
+ ${others.map(code => `import ${identifier(code)} from './messages/${code}';`).join('\n')}${others.length ? '\n' : ''}
50
+ /** The messages of this unit. The base catalog defines the keys and their parameters. */
51
+ export const messages = createMessages({
52
+ defaultLocale: ${quote(locales.defaultLocale)},
53
+ base,
54
+ // Each other locale of the registry: import its catalog and list it here.
55
+ translations: {${others.length ? ' ' + others.map(code => code.includes('-') ? quote(code) + ': ' + identifier(code) : code).join(', ') + ' ' : ''}},
56
+ core: coreMessages,
57
+ numberingSystem: localeRegistry.numberingSystem,
58
+ });
59
+ `;
60
+ return files;
61
+ }
62
+ const listMessages = (name) => ({
63
+ title: humanize(name), search: 'Search', apply: 'Apply', unavailable: 'The data is not available. Try again.', empty: 'No records found.', loading: 'Loading.',
64
+ detail: 'Details', back: 'Back', pagination: 'Pages', pageOf: 'Page {page, number} of {count, number}', previous: 'Previous', next: 'Next',
65
+ yes: 'Yes', no: 'No',
66
+ });
67
+ const screenMessages = (name) => ({
68
+ title: humanize(name), run: 'Run', running: 'Running', done: 'Done', failed: 'Not completed. Try again.', unbound: 'Not available yet.',
69
+ });
70
+ /**
71
+ * Files of a feature, relative to the app directory. Without a resource it is a `screen` feature: a view, a
72
+ * hook, a model, a request boundary and a helper. With a resource of the app's `ftg.config.json` it is a
73
+ * `list` feature: a URL-driven list and detail over that generated read, its entity and its server boundary.
74
+ */
75
+ export async function featureFiles(name, options = {}) {
76
+ if (!NAME_PATTERN.test(name))
77
+ throw new Error('INVALID_FEATURE_NAME');
78
+ const locales = options.locales ?? TEMPLATE_LOCALES;
79
+ const model = 'features/' + name + '/model';
80
+ if (options.resource === undefined)
81
+ return prefix('src', { ...await templateFiles('feature/screen', { __feature__: name, __Feature__: pascal(name) }), ...messageFiles(model, screenMessages(name), locales, 3) });
82
+ if (!NAME_PATTERN.test(options.resource))
83
+ throw new Error('INVALID_RESOURCE_NAME');
84
+ const output = safeRelative(options.generatedOutput ?? '', 'INVALID_GENERATED_OUTPUT');
85
+ // The list protocol names the operation of the resource; it is checked against the captured contract.
86
+ if (!options.operationId || !/^[A-Za-z_][A-Za-z0-9_.-]{0,99}$/.test(options.operationId))
87
+ throw new Error('INVALID_OPERATION_ID');
88
+ return prefix('src', { ...await templateFiles('feature/list', {
89
+ __FEATURE_TO_OUTPUT__: importPath('src/features/' + name + '/model', output),
90
+ __ENTITY_TO_OUTPUT__: importPath('src/entities/' + options.resource + '/model', output),
91
+ __SERVER_TO_OUTPUT__: importPath('src/api/server', output),
92
+ __RESOURCE_CONSTANT__: constant(options.resource), __OPERATION_ID__: options.operationId,
93
+ __feature__: name, __Feature__: pascal(name), __resource__: options.resource, __Resource__: pascal(options.resource),
94
+ }), ...messageFiles(model, listMessages(name), locales, 3) });
95
+ }
96
+ export async function planFeature(reader, request) {
97
+ const app = safeRelative(request.app, 'INVALID_APP_DIRECTORY');
98
+ if (!NAME_PATTERN.test(request.name))
99
+ throw new Error('INVALID_FEATURE_NAME');
100
+ if (!await reader.exists(app + '/project.json'))
101
+ throw new Error('APP_NOT_FOUND');
102
+ if (await reader.exists(app + '/src/features/' + request.name))
103
+ throw new Error('DESTINATION_EXISTS');
104
+ const locales = readAppLocales(await reader.read(app + '/src/config/app.ts') ?? '');
105
+ if (!locales)
106
+ throw new Error('APP_PREREQUISITE_MISSING:src/config/app.ts locale registry');
107
+ // Feature screens render inside the shared page frame.
108
+ const manifest = JSON.parse(await reader.read(app + '/package.json') ?? '{}');
109
+ if (!manifest.dependencies?.['@mpfrontend/app-layout'])
110
+ throw new Error('APP_PREREQUISITE_MISSING:@mpfrontend/app-layout');
111
+ let generatedOutput, operationId;
112
+ if (request.resource !== undefined) {
113
+ for (const required of ['src/config/app.ts', 'src/config/server.ts'])
114
+ if (!await reader.exists(app + '/' + required))
115
+ throw new Error('APP_PREREQUISITE_MISSING:' + required);
116
+ const config = JSON.parse(await reader.read(app + '/ftg.config.json') ?? 'null');
117
+ if (!config || typeof config.output !== 'string' || !Array.isArray(config.resources))
118
+ throw new Error('APP_PREREQUISITE_MISSING:ftg.config.json');
119
+ const selected = config.resources.find(resource => resource.name === request.resource);
120
+ if (!selected)
121
+ throw new Error('UNKNOWN_RESOURCE');
122
+ if (typeof selected.operationId !== 'string')
123
+ throw new Error('APP_PREREQUISITE_MISSING:ftg.config.json operationId');
124
+ operationId = selected.operationId;
125
+ generatedOutput = posix.normalize(config.output);
126
+ }
127
+ const all = prefix(app, await featureFiles(request.name, { locales, ...(request.resource !== undefined ? { resource: request.resource } : {}), ...(operationId ? { operationId } : {}), ...(generatedOutput ? { generatedOutput } : {}) }));
128
+ const files = {}, kept = [];
129
+ for (const [path, content] of Object.entries(all)) {
130
+ // The entity and the server boundary of a resource are shared; an existing one is authored and kept.
131
+ if (!path.startsWith(app + '/src/features/') && await reader.exists(path)) {
132
+ kept.push(path);
133
+ continue;
134
+ }
135
+ files[path] = content;
136
+ }
137
+ const untranslated = Object.keys(files).filter(path => /\/model\/messages\/[^/]+\.ts$/.test(path) && !path.endsWith('/' + locales.defaultLocale + '.ts')).sort();
138
+ return { files, kept, untranslated };
139
+ }
140
+ // ------------------------------------------------------------------------------------------------------ routes
141
+ const segmentPattern = /^(?:[a-z0-9][a-z0-9-]*|\[[a-z][a-zA-Z0-9]*\])$/;
142
+ /** One Next.js route per screen: a thin page that renders a feature screen with the route's base path. */
143
+ export function routeFiles(path, feature, screen) {
144
+ const segments = path.split('/');
145
+ if (!path || segments.some(segment => !segmentPattern.test(segment)) || segments[0] === 'api')
146
+ throw new Error('INVALID_ROUTE_PATH');
147
+ if (!NAME_PATTERN.test(feature))
148
+ throw new Error('INVALID_FEATURE_NAME');
149
+ if (!/^[A-Z][A-Za-z0-9]{0,79}$/.test(screen))
150
+ throw new Error('INVALID_SCREEN_NAME');
151
+ const dynamic = segments.filter(segment => segment.startsWith('[')).map(segment => segment.slice(1, -1));
152
+ if (new Set(dynamic).size !== dynamic.length)
153
+ throw new Error('INVALID_ROUTE_PATH');
154
+ const staticPart = segments.slice(0, segments.findIndex(segment => segment.startsWith('[')) >= 0 ? segments.findIndex(segment => segment.startsWith('[')) : segments.length);
155
+ const basePath = '/' + staticPart.join('/');
156
+ const entry = '../'.repeat(segments.length + 1) + 'features/' + feature;
157
+ const params = dynamic.length ? `type Params = Promise<{ ${dynamic.map(name => name + ': string').join('; ')} }>;\n` : '';
158
+ const props = dynamic.length ? '{ params: Params; searchParams: SearchParams }' : '{ searchParams: SearchParams }';
159
+ return { ['src/app/' + path + '/page.tsx']: `import { ${screen} } from '${entry}';
160
+
161
+ ${params}type SearchParams = Promise<Record<string, string | string[] | undefined>>;
162
+
163
+ export default function Page(props: ${props}) {
164
+ return <${screen} {...props} basePath="${basePath}" />;
165
+ }
166
+ ` };
167
+ }
168
+ export async function planRoute(reader, request) {
169
+ const app = safeRelative(request.app, 'INVALID_APP_DIRECTORY');
170
+ const screen = request.screen ?? pascal(request.feature) + 'Screen';
171
+ const files = prefix(app, routeFiles(request.path, request.feature, screen));
172
+ if (!await reader.exists(app + '/project.json'))
173
+ throw new Error('APP_NOT_FOUND');
174
+ const entry = await reader.read(app + '/src/features/' + request.feature + '/index.ts');
175
+ if (entry === undefined)
176
+ throw new Error('UNKNOWN_FEATURE');
177
+ if (!new RegExp('\\b' + screen + '\\b').test(entry))
178
+ throw new Error('UNKNOWN_FEATURE_SCREEN');
179
+ const directory = app + '/src/app/' + request.path;
180
+ for (const file of ['page.tsx', 'page.ts', 'page.jsx', 'page.js', 'route.ts', 'route.js'])
181
+ if (await reader.exists(directory + '/' + file))
182
+ throw new Error('DESTINATION_EXISTS');
183
+ return { files, kept: [] };
184
+ }
185
+ /** A shared package: tags, a public entry, a build to dist and the quality targets of the workspace. */
186
+ export async function packageFiles(name, scope, directory, runtime = 'universal') {
187
+ if (!NAME_PATTERN.test(name))
188
+ throw new Error('INVALID_PACKAGE_NAME');
189
+ if (!/^[a-z0-9][a-z0-9._-]{0,99}$/.test(scope))
190
+ throw new Error('INVALID_PACKAGE_SCOPE');
191
+ if (!['universal', 'client', 'server'].includes(runtime))
192
+ throw new Error('INVALID_PACKAGE_RUNTIME');
193
+ if (!/^packages\/[a-z][a-z0-9-]{1,48}$/.test(directory))
194
+ throw new Error('INVALID_PACKAGE_DIRECTORY');
195
+ return prefix(directory, await templateFiles('package', { __SCOPE__: scope, __name__: name, __runtime__: runtime, __DIRECTORY__: directory }));
196
+ }
197
+ export async function planPackage(reader, request) {
198
+ const directory = request.directory ?? 'packages/' + request.name;
199
+ const manifest = JSON.parse(await reader.read('package.json') ?? 'null');
200
+ if (!manifest || typeof manifest.name !== 'string')
201
+ throw new Error('WORKSPACE_ROOT_REQUIRED');
202
+ const scope = manifest.name.startsWith('@') ? manifest.name.slice(1).split('/')[0] : manifest.name;
203
+ const files = await packageFiles(request.name, scope, directory, request.runtime ?? 'universal');
204
+ if (await reader.exists(directory))
205
+ throw new Error('DESTINATION_EXISTS');
206
+ return { files, kept: [] };
207
+ }
208
+ async function apply(plan, options) {
209
+ const root = resolve(options.root ?? process.cwd());
210
+ let formatted = false;
211
+ if (!options.dryRun)
212
+ formatted = await writeNew(root, plan.files, options.formatter ?? await workspaceFormatter(root));
213
+ return { files: Object.keys(plan.files).sort(), kept: plan.kept.sort(), ...(plan.untranslated ? { untranslated: plan.untranslated } : {}), dryRun: options.dryRun === true, formatted };
214
+ }
215
+ export async function createFeature(request) {
216
+ const plan = await planFeature(diskReader(resolve(request.root ?? process.cwd())), request);
217
+ return { ok: true, kind: (request.resource === undefined ? 'screen' : 'list'), name: request.name, ...await apply(plan, request) };
218
+ }
219
+ export async function createRoute(request) {
220
+ const plan = await planRoute(diskReader(resolve(request.root ?? process.cwd())), request);
221
+ return { ok: true, path: request.path, ...await apply(plan, request) };
222
+ }
223
+ export async function createPackage(request) {
224
+ const plan = await planPackage(diskReader(resolve(request.root ?? process.cwd())), request);
225
+ return { ok: true, name: request.name, ...await apply(plan, request) };
226
+ }
227
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /** Lower-case kebab names: apps, features, resources and packages. */
2
+ export declare const NAME_PATTERN: RegExp;
3
+ export declare const pascal: (name: string) => string;
4
+ export declare const constant: (name: string) => string;
5
+ export type Replacements = Readonly<Record<string, string>>;
6
+ /** Reads a template folder; placeholders are replaced in file paths and contents, `.template` is dropped. */
7
+ export declare function templateFiles(folder: string, replacements: Replacements): Promise<Record<string, string>>;
8
+ /** A POSIX import specifier from one source directory to another. */
9
+ export declare function importPath(fromDirectory: string, toPath: string): string;
10
+ /** A workspace-relative directory without traversal, absolute paths or backslashes. */
11
+ export declare function safeRelative(path: string, error: string): string;
12
+ export declare function exists(path: string): Promise<boolean>;
13
+ /** Formats one file's content; `path` is the absolute path the file will be written to. */
14
+ export type Formatter = (path: string, content: string) => Promise<string>;
15
+ /**
16
+ * The workspace formatter, when the workspace has one: Prettier resolved from the workspace root with the
17
+ * workspace's own configuration. Without it, files are written exactly as the templates hold them.
18
+ */
19
+ export declare function workspaceFormatter(root: string): Promise<Formatter | undefined>;
20
+ /** Formats the files and writes them with exclusive creation; an existing file is never replaced. */
21
+ export declare function writeNew(root: string, files: Record<string, string>, formatter?: Formatter): Promise<boolean>;
@@ -0,0 +1,83 @@
1
+ import { readFile, readdir, lstat, mkdir, writeFile } from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, join, resolve, relative, isAbsolute, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /** Lower-case kebab names: apps, features, resources and packages. */
6
+ export const NAME_PATTERN = /^[a-z][a-z0-9-]{1,48}$/;
7
+ export const pascal = (name) => name.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');
8
+ export const constant = (name) => name.replaceAll('-', '_').toUpperCase();
9
+ /** Reads a template folder; placeholders are replaced in file paths and contents, `.template` is dropped. */
10
+ export async function templateFiles(folder, replacements) {
11
+ const root = fileURLToPath(new URL('../templates/' + folder + '/', import.meta.url));
12
+ const apply = (text) => Object.entries(replacements).reduce((value, [key, replacement]) => value.replaceAll(key, replacement), text);
13
+ const result = {};
14
+ async function visit(dir, prefix) {
15
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
16
+ const path = join(dir, entry.name), key = prefix + entry.name;
17
+ if (entry.isDirectory())
18
+ await visit(path, key + '/');
19
+ else
20
+ result[apply(key.replace(/\.template$/, ''))] = apply(await readFile(path, 'utf8'));
21
+ }
22
+ }
23
+ await visit(root, '');
24
+ return result;
25
+ }
26
+ /** A POSIX import specifier from one source directory to another. */
27
+ export function importPath(fromDirectory, toPath) {
28
+ const path = relative(fromDirectory, toPath).split(sep).join('/');
29
+ return path.startsWith('.') ? path : './' + path;
30
+ }
31
+ /** A workspace-relative directory without traversal, absolute paths or backslashes. */
32
+ export function safeRelative(path, error) {
33
+ const parts = path.split('/');
34
+ if (!path || isAbsolute(path) || path.includes('\\') || parts.some(part => part === '' || part === '.' || part === '..'))
35
+ throw new Error(error);
36
+ return parts.join('/');
37
+ }
38
+ export async function exists(path) {
39
+ try {
40
+ await lstat(path);
41
+ return true;
42
+ }
43
+ catch (error) {
44
+ if (error.code === 'ENOENT')
45
+ return false;
46
+ throw error;
47
+ }
48
+ }
49
+ /**
50
+ * The workspace formatter, when the workspace has one: Prettier resolved from the workspace root with the
51
+ * workspace's own configuration. Without it, files are written exactly as the templates hold them.
52
+ */
53
+ export async function workspaceFormatter(root) {
54
+ let entry;
55
+ try {
56
+ entry = createRequire(join(root, 'package.json')).resolve('prettier');
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ const loaded = await import(entry);
62
+ const prettier = (loaded.default ?? loaded);
63
+ return async (path, content) => {
64
+ // The parser comes from the file name; ignore files do not apply to output the generator is writing.
65
+ if (!(await prettier.getFileInfo(path, { withNodeModules: true })).inferredParser)
66
+ return content;
67
+ return prettier.format(content, { ...(await prettier.resolveConfig(path)), filepath: path });
68
+ };
69
+ }
70
+ /** Formats the files and writes them with exclusive creation; an existing file is never replaced. */
71
+ export async function writeNew(root, files, formatter) {
72
+ const output = [];
73
+ for (const [path, content] of Object.entries(files)) {
74
+ const target = resolve(root, path);
75
+ output.push([target, formatter ? await formatter(target, content) : content]);
76
+ }
77
+ for (const [target, content] of output) {
78
+ await mkdir(dirname(target), { recursive: true });
79
+ await writeFile(target, content, { flag: 'wx' });
80
+ }
81
+ return formatter !== undefined;
82
+ }
83
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export type DesignSource = 'none' | 'existing';
2
+ export type WorkspaceOptions = {
3
+ name: string;
4
+ directory: string;
5
+ designSource?: DesignSource;
6
+ dryRun?: boolean;
7
+ };
8
+ /** The cohort a new workspace pins; the quality profile is upgraded with it, never copied into the workspace. */
9
+ export declare const COHORT: {
10
+ readonly cli: "1.0.0";
11
+ readonly nxPlugin: "1.0.0";
12
+ readonly workspaceConfig: "1.0.0";
13
+ };
14
+ /** The scope of the framework packages, bound to the registry that publishes them. */
15
+ export declare const NPMRC = "@mpfrontend:registry=https://registry.npmjs.org/\nsave-exact=true\n";
16
+ /** Tool versions the quality profile is verified with. */
17
+ export declare const QUALITY_TOOLS: {
18
+ readonly eslint: "10.12.0";
19
+ readonly prettier: "3.9.9";
20
+ readonly tsx: "4.23.15";
21
+ };
22
+ /** Workspace scripts. `check` is the gate that CI and the CI-neutral script run. */
23
+ export declare const WORKSPACE_SCRIPTS: {
24
+ readonly build: "nx run-many -t build";
25
+ readonly format: "prettier --write .";
26
+ readonly 'format:check': "prettier --check .";
27
+ readonly lint: "nx run-many -t lint";
28
+ readonly test: "nx run-many -t test";
29
+ readonly check: "prettier --check . && nx run-many -t lint typecheck test build generated-check catalog-check";
30
+ };
31
+ /** A neutral Nx/pnpm shell only. Backend contracts, secrets and deployment decisions are not invented. */
32
+ export declare function workspaceFiles(name: string, designSource?: DesignSource): Record<string, string>;
33
+ export declare function createWorkspace(options: WorkspaceOptions): Promise<{
34
+ ok: boolean;
35
+ name: string;
36
+ directory: string;
37
+ files: string[];
38
+ dryRun: boolean;
39
+ runtimeProfile: string;
40
+ designSource: DesignSource;
41
+ }>;
@@ -0,0 +1,213 @@
1
+ import { lstat, mkdir, writeFile } from 'node:fs/promises';
2
+ import { resolve, join, dirname } from 'node:path';
3
+ const json = (value) => JSON.stringify(value, null, 2) + '\n';
4
+ /** The cohort a new workspace pins; the quality profile is upgraded with it, never copied into the workspace. */
5
+ export const COHORT = { cli: '1.0.0', nxPlugin: '1.0.0', workspaceConfig: '1.0.0' };
6
+ /** The scope of the framework packages, bound to the registry that publishes them. */
7
+ export const NPMRC = '@mpfrontend:registry=https://registry.npmjs.org/\nsave-exact=true\n';
8
+ /** Tool versions the quality profile is verified with. */
9
+ export const QUALITY_TOOLS = { eslint: '10.12.0', prettier: '3.9.9', tsx: '4.23.15' };
10
+ /** Workspace scripts. `check` is the gate that CI and the CI-neutral script run. */
11
+ export const WORKSPACE_SCRIPTS = {
12
+ build: 'nx run-many -t build',
13
+ format: 'prettier --write .',
14
+ 'format:check': 'prettier --check .',
15
+ lint: 'nx run-many -t lint',
16
+ test: 'nx run-many -t test',
17
+ check: 'prettier --check . && nx run-many -t lint typecheck test build generated-check catalog-check',
18
+ };
19
+ // Written in the formatter's own layout so that a new workspace passes its format check.
20
+ const nxJson = `{
21
+ "namedInputs": {
22
+ "default": [
23
+ "{projectRoot}/**/*",
24
+ "!{projectRoot}/.next/**/*",
25
+ "!{projectRoot}/node_modules/**/*",
26
+ "!{projectRoot}/runtime-assets/**/*"
27
+ ],
28
+ "sharedGlobals": [
29
+ "{workspaceRoot}/pnpm-lock.yaml",
30
+ "{workspaceRoot}/package.json",
31
+ "{workspaceRoot}/pnpm-workspace.yaml",
32
+ "{workspaceRoot}/eslint.config.mjs",
33
+ "{workspaceRoot}/prettier.config.mjs",
34
+ "{workspaceRoot}/.prettierignore"
35
+ ]
36
+ },
37
+ "targetDefaults": {
38
+ "build": { "inputs": ["default", "sharedGlobals"], "cache": true },
39
+ "typecheck": { "inputs": ["default", "sharedGlobals"], "cache": true },
40
+ "lint": { "inputs": ["default", "sharedGlobals"], "cache": true },
41
+ "test": { "inputs": ["default", "sharedGlobals"], "cache": true },
42
+ "format:check": { "inputs": ["default", "sharedGlobals"], "cache": true },
43
+ "catalog-check": { "inputs": ["default", "sharedGlobals"], "cache": true },
44
+ "format": { "cache": false }
45
+ }
46
+ }
47
+ `;
48
+ const prettierIgnore = `# Tool-owned files keep the layout of the tool that writes them.
49
+ pnpm-lock.yaml
50
+ pnpm-workspace.yaml
51
+ **/.mpfrontend/
52
+ .agents/skills/mpfrontend-*/
53
+ .claude/skills/mpfrontend-*/
54
+ artifacts/
55
+ # Hash-bound inputs: a reformatted binding, design export or captured contract reads as drift.
56
+ # List the folder of your normalized design export here when you attach one.
57
+ **/design.binding.json
58
+ contracts/openapi/
59
+ # Generated output is checked by generated-check, not by the formatter.
60
+ **/generated/
61
+ **/*.gen.ts
62
+ **/next-env.d.ts
63
+ `;
64
+ const eslintConfig = `import { workspaceConfig } from '@mpfrontend/workspace-config/eslint';
65
+
66
+ // The shared MP Frontend profile. Workspace-wide additions go after it; an app or a package
67
+ // spreads this array in its own eslint.config.mjs and appends only its own additions.
68
+ export default [...workspaceConfig()];
69
+ `;
70
+ const prettierConfig = `import profile from '@mpfrontend/workspace-config/prettier';
71
+
72
+ // The shared MP Frontend formatter profile. Add workspace-wide overrides after the spread.
73
+ export default { ...profile };
74
+ `;
75
+ const ciWorkflow = `# Verification only: a frozen install and the uncached workspace check. No deployment or publication.
76
+ name: check
77
+ on:
78
+ pull_request:
79
+ push:
80
+ branches: [main]
81
+ workflow_dispatch:
82
+ permissions:
83
+ contents: read
84
+ concurrency:
85
+ group: check-\${{ github.ref }}
86
+ cancel-in-progress: true
87
+ jobs:
88
+ check:
89
+ runs-on: ubuntu-latest
90
+ timeout-minutes: 30
91
+ steps:
92
+ - uses: actions/checkout@v7
93
+ - uses: actions/setup-node@v7
94
+ with:
95
+ node-version: '24.19.0'
96
+ package-manager-cache: false
97
+ - name: Install the selected package manager
98
+ run: npm install --global pnpm@11.25.0
99
+ - name: Frozen install and uncached check
100
+ run: sh tools/ci/check.sh
101
+ `;
102
+ const ciScript = `#!/bin/sh
103
+ # CI-neutral workspace gate. Any CI runs this file from the workspace root with Node.js 24.19.0 and
104
+ # pnpm 11.25.0 on the path: a frozen install, then the uncached check. It deploys and publishes nothing.
105
+ set -eu
106
+ export NX_DAEMON=false
107
+ export NEXT_TELEMETRY_DISABLED=1
108
+ pnpm install --frozen-lockfile
109
+ pnpm check --skip-nx-cache
110
+ `;
111
+ const codeowners = `# Code owners. Replace each placeholder with the owning team before you require code-owner reviews.
112
+ * @owner-placeholder/workspace
113
+ /apps/ @owner-placeholder/apps
114
+ /packages/ @owner-placeholder/shared-packages
115
+ /.agents/skills/ @owner-placeholder/workspace
116
+ /.claude/skills/ @owner-placeholder/workspace
117
+ `;
118
+ const skillsGuide = `# Project and domain skills
119
+
120
+ Coding agents discover repository skills in this folder. \`mpfrontend skills install\` writes only the
121
+ \`mpfrontend-*\` folders here and in \`.claude/skills/\`, records them in \`.mpfrontend/skills-lock.json\`, and
122
+ never reads, changes or removes another folder.
123
+
124
+ Add the workspace's own skills beside them:
125
+
126
+ - \`project-<topic>/SKILL.md\` for a repository workflow, for example adding an app or reviewing a change.
127
+ - \`domain-<topic>/SKILL.md\` for a business procedure that this product owns.
128
+
129
+ Each \`SKILL.md\` starts with \`name\` and \`description\` metadata, and \`name\` equals the folder name. Copy
130
+ each skill to \`.claude/skills/<name>/SKILL.md\` as well, so that both agents find it. The \`mpfrontend-\`
131
+ prefix is reserved: \`mpfrontend skills check\` refuses a folder with that prefix that the installer does not
132
+ own. A skill calls the workspace generators and \`pnpm check\` instead of repeating their steps.
133
+ `;
134
+ const agents = `# Consumer workspace
135
+
136
+ Use pnpm and Nx. Product behavior, DLS bindings and assets stay in this repository; MP Frontend core
137
+ remains product-neutral. Read the README and approved API/design contracts before changing behavior.
138
+ Preserve authored overrides. Never commit secrets or claim production readiness from a scaffold/build
139
+ alone. Repository skills are installed explicitly with mpfrontend skills install; no global
140
+ configuration is changed.
141
+
142
+ Run \`pnpm check\` before a change is reviewed. It runs the format check, then lint, typecheck, test, build,
143
+ generated-check and catalog-check of every project. Lint fails on an import across app boundaries, on a raw
144
+ colour outside the theme files, on hand-written CSS outside the theme layer and the global style entry, on a
145
+ physical left or right where a logical start or end belongs, and on literal user-visible text or translated
146
+ text joined to other text. Every visible text is a catalog
147
+ message; the catalog check fails on a key missing in a locale, an unused key, a key that code uses and the
148
+ base does not define, or differing parameters.
149
+ Fix the code; do not weaken a rule to pass the check.
150
+
151
+ Create code with the generators, not by copying: \`mpfrontend create app\`, \`create feature\`,
152
+ \`create route\` and \`create package\` (or the matching \`nx g @mpfrontend/nx-plugin:<generator>\`).
153
+ A feature lives in \`src/features/<feature>/\` with \`ui/\`, \`model/\`, \`hooks/\`, \`api/\` and \`utils/\`, and
154
+ code outside it imports only its \`index.ts\`. Every screen is its own route. Code that two apps need is a
155
+ package under \`packages/\`, never a copy. The workspace's own skills follow \`.agents/skills/README.md\`.
156
+ `;
157
+ /** A neutral Nx/pnpm shell only. Backend contracts, secrets and deployment decisions are not invented. */
158
+ export function workspaceFiles(name, designSource = 'none') {
159
+ if (!/^[a-z][a-z0-9-]{1,48}$/.test(name))
160
+ throw new Error('INVALID_WORKSPACE_NAME');
161
+ if (!['none', 'existing'].includes(designSource))
162
+ throw new Error('INVALID_DESIGN_SOURCE');
163
+ const designStatus = designSource === 'none' ? 'disabled' : 'pending';
164
+ return {
165
+ 'package.json': json({ name, private: true, type: 'module', packageManager: 'pnpm@11.25.0', engines: { node: '>=24 <25' },
166
+ scripts: WORKSPACE_SCRIPTS,
167
+ devDependencies: { '@mpfrontend/ftg-cli': COHORT.cli, '@mpfrontend/nx-plugin': COHORT.nxPlugin, '@mpfrontend/workspace-config': COHORT.workspaceConfig, eslint: QUALITY_TOOLS.eslint, nx: '23.2.1', prettier: QUALITY_TOOLS.prettier, tsx: QUALITY_TOOLS.tsx, typescript: '5.9.3', '@types/node': '24.10.1', '@types/react': '19.3.0', '@types/react-dom': '19.2.3' } }),
168
+ 'pnpm-workspace.yaml': 'packages:\n - apps/*\n - packages/*\n - themes/*\noverrides:\n axios@1.18.1: 1.20.0\n brace-expansion@5.0.9: 5.0.12\n smol-toml@1.6.1: 1.9.0\nallowBuilds:\n "@scarf/scarf": false\n esbuild: true\n nx: true\n sharp: true\n unrs-resolver: true\n',
169
+ 'nx.json': nxJson,
170
+ // The @mpfrontend scope is bound to the public npm registry, so that no other registry can serve it; dependencies
171
+ // are saved at exact versions.
172
+ '.npmrc': NPMRC,
173
+ '.gitignore': 'node_modules/\n.next/\n.nx/\ndist/\nruntime-assets/\n*.tsbuildinfo\n.env\n.env.*\n!.env.example\n.mpfrontend/skills-install.lock\n.ftg-write.lock\n',
174
+ '.prettierignore': prettierIgnore,
175
+ 'eslint.config.mjs': eslintConfig,
176
+ // The module boundary rule reads the root tsconfig.base.json for path aliases; it extends the shared profile.
177
+ 'tsconfig.base.json': json({ extends: '@mpfrontend/workspace-config/tsconfig/base.json' }),
178
+ 'prettier.config.mjs': prettierConfig,
179
+ '.github/workflows/check.yml': ciWorkflow,
180
+ 'tools/ci/check.sh': ciScript,
181
+ 'CODEOWNERS': codeowners,
182
+ '.agents/skills/README.md': skillsGuide,
183
+ '.mpfrontend/workspace.json': json({ schemaVersion: 1, name, orchestrator: 'nx', packageManager: 'pnpm', runtimeProfile: 'unconfigured', designSource, cliVersion: COHORT.cli }),
184
+ '.mpfrontend/design-source.json': json({ schemaVersion: 1, generator: 'MPFrontendDesignSource', cliVersion: COHORT.cli, source: designSource, status: designStatus, binding: null, bindingHash: null, sourceIdentity: null, release: null }),
185
+ 'apps/.gitkeep': '', 'packages/.gitkeep': '', 'themes/.gitkeep': '',
186
+ 'contracts/README.md': '# API ownership\n\nSupply an approved, immutable OpenAPI bundle under openapi/presentation/<app>/openapi.json.\nThe generated catalog example expects listCatalog; configure FTG explicitly for your actual contract.\nNo backend API, permissions or production runtime is generated by workspace initialization.\n',
187
+ 'AGENTS.md': agents,
188
+ 'CLAUDE.md': '@AGENTS.md\n',
189
+ 'README.md': `# ${name}\n\nNeutral MP Frontend Nx/pnpm workspace, initialized with design source \`${designSource}\` without installing dependencies or creating Git history.\n\n1. Install the pinned dependencies with pnpm install. \`.npmrc\` binds the @mpfrontend scope to the public npm registry, and every framework package is pinned to one exact cohort version. Offline, install the archives of a release through pnpm overrides and lock their SHA-256 from the release record.\n2. ${designSource === 'none' ? 'Continue code-first, or attach an approved consumer-owned design source later with mpfrontend design attach.' : 'Create an approved root design.binding.json for the consumer-owned DLS, then complete the pending source with mpfrontend design attach.'}\n3. Run pnpm exec mpfrontend create app --name customer --directory apps/customer. Add features, routes and shared packages with mpfrontend create feature, create route and create package; replace the placeholder owners in CODEOWNERS.\n4. Supply your approved catalog OpenAPI contract as explained in contracts/README.md.\n5. Install application dependencies, then run pnpm check. Review pnpm-lock.yaml and use frozen installs in CI.\n6. Run pnpm exec mpfrontend skills install --for both --profile base only if repository-scoped agent workflows are wanted. The catalog provides eighteen finite workflows; the base profile installs fourteen.\n\n## Quality profile\n\nThe formatter, lint and TypeScript configuration come from \`@mpfrontend/workspace-config\` and are\nupgraded with the MP Frontend cohort. \`eslint.config.mjs\` and \`prettier.config.mjs\` re-export the shared\nprofile; an app adds only its own additions in its own \`eslint.config.mjs\`.\n\n- \`pnpm format\` formats the workspace; \`pnpm format:check\` only checks it.\n- \`pnpm check\` runs the format check, then lint, typecheck, test, build, generated-check and\n catalog-check of every project, with Nx caching. Pass \`--skip-nx-cache\` to prove the current source.\n- Every project has \`format\`, \`format:check\`, \`lint\` and \`test\` targets for focused feedback.\n- \`tools/ci/check.sh\` is the CI-neutral gate (a frozen install, then the uncached check);\n \`.github/workflows/check.yml\` runs it on GitHub Actions.\n\nDesign source modes are explicit: \`existing\` attaches a consumer-owned DLS binding and \`none\` remains code-first. MP Frontend does not ship or require a public Community DLS in this release. Initialization never reads, captures or changes Figma.\n\nThe catalog template is not a protected production application. Configure the security BFF, identity, gateway and realtime boundaries with acceptance tests before protected product adoption.\nProduct DLS, fonts, themes, locale policy, exports, mappings and asset rights are consumer-owned. No Figma access/write, watcher, Git operation, deployment, package publication or global installation is performed.\n\nExisting destinations are refused, including an empty directory or symlink. A filesystem failure may leave a partial new destination for inspection; it is never automatically deleted or retried over.\n`,
190
+ };
191
+ }
192
+ export async function createWorkspace(options) {
193
+ const designSource = options.designSource ?? 'none', files = workspaceFiles(options.name, designSource), directory = resolve(options.directory);
194
+ try {
195
+ await lstat(directory);
196
+ throw new Error('DESTINATION_EXISTS');
197
+ }
198
+ catch (error) {
199
+ if (error.code !== 'ENOENT')
200
+ throw error;
201
+ }
202
+ if (!options.dryRun) {
203
+ // Exclusive directory creation refuses a competing creator; writes never replace an existing file.
204
+ await mkdir(directory, { recursive: false });
205
+ for (const [path, content] of Object.entries(files)) {
206
+ const target = join(directory, path);
207
+ await mkdir(dirname(target), { recursive: true });
208
+ await writeFile(target, content, { flag: 'wx' });
209
+ }
210
+ }
211
+ return { ok: true, name: options.name, directory, files: Object.keys(files).sort(), dryRun: options.dryRun === true, runtimeProfile: 'unconfigured', designSource };
212
+ }
213
+ //# sourceMappingURL=workspace.js.map