@bleedingdev/modern-js-create 3.2.0-ultramodern.11 → 3.2.0-ultramodern.111

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 (75) hide show
  1. package/README.md +158 -35
  2. package/bin/run.js +0 -0
  3. package/dist/cjs/index.cjs +1042 -0
  4. package/dist/cjs/locale/en.cjs +97 -0
  5. package/dist/cjs/locale/index.cjs +50 -0
  6. package/dist/cjs/locale/zh.cjs +97 -0
  7. package/dist/cjs/ultramodern-package-source.cjs +135 -0
  8. package/dist/cjs/ultramodern-workspace.cjs +5623 -0
  9. package/dist/esm/index.js +1004 -0
  10. package/dist/esm/locale/en.js +59 -0
  11. package/dist/esm/locale/index.js +9 -0
  12. package/dist/esm/locale/zh.js +59 -0
  13. package/dist/esm/ultramodern-package-source.js +63 -0
  14. package/dist/esm/ultramodern-workspace.js +5561 -0
  15. package/dist/esm-node/index.js +1005 -0
  16. package/dist/esm-node/locale/en.js +60 -0
  17. package/dist/esm-node/locale/index.js +10 -0
  18. package/dist/esm-node/locale/zh.js +60 -0
  19. package/dist/esm-node/ultramodern-package-source.js +64 -0
  20. package/dist/esm-node/ultramodern-workspace.js +5562 -0
  21. package/dist/types/locale/en.d.ts +3 -0
  22. package/dist/types/locale/index.d.ts +117 -2
  23. package/dist/types/locale/zh.d.ts +3 -0
  24. package/dist/types/ultramodern-package-source.d.ts +28 -0
  25. package/dist/types/ultramodern-workspace.d.ts +12 -2
  26. package/package.json +27 -11
  27. package/template/.codex/hooks.json +16 -0
  28. package/template/.github/renovate.json +53 -0
  29. package/template/.github/workflows/ultramodern-gates.yml.handlebars +34 -10
  30. package/template/.mise.toml.handlebars +2 -0
  31. package/template/AGENTS.md +9 -6
  32. package/template/README.md +66 -34
  33. package/template/api/effect/index.ts.handlebars +20 -9
  34. package/template/api/lambda/hello.ts.handlebars +5 -5
  35. package/template/config/favicon.svg +5 -0
  36. package/template/config/public/assets/ultramodern-logo.svg +6 -0
  37. package/template/config/public/locales/cs/translation.json +44 -0
  38. package/template/config/public/locales/en/translation.json +44 -0
  39. package/template/lefthook.yml +10 -0
  40. package/template/modern.config.ts.handlebars +35 -3
  41. package/template/oxfmt.config.ts +8 -1
  42. package/template/oxlint.config.ts +8 -1
  43. package/template/package.json.handlebars +37 -30
  44. package/template/pnpm-workspace.yaml +34 -0
  45. package/template/rstest.config.mts +5 -0
  46. package/template/scripts/bootstrap-agent-skills.mjs +148 -15
  47. package/template/scripts/check-i18n-strings.mjs +3 -0
  48. package/template/scripts/validate-ultramodern.mjs.handlebars +495 -3
  49. package/template/src/modern-app-env.d.ts +2 -0
  50. package/template/src/modern.runtime.ts.handlebars +17 -1
  51. package/template/src/routes/[lang]/page.tsx.handlebars +209 -0
  52. package/template/src/routes/index.css.handlebars +192 -55
  53. package/template/src/routes/layout.tsx.handlebars +2 -1
  54. package/template/tailwind.config.ts.handlebars +1 -1
  55. package/template/tests/tsconfig.json +7 -0
  56. package/template/tests/ultramodern.contract.test.ts.handlebars +163 -0
  57. package/template/tsconfig.json +2 -1
  58. package/template-workspace/.agents/agent-reference-repos.json +24 -0
  59. package/template-workspace/.agents/skills-lock.json +19 -0
  60. package/template-workspace/.codex/hooks.json +16 -0
  61. package/template-workspace/.github/renovate.json +29 -0
  62. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +54 -0
  63. package/template-workspace/.gitignore.handlebars +5 -0
  64. package/template-workspace/.mise.toml.handlebars +2 -0
  65. package/template-workspace/AGENTS.md +36 -5
  66. package/template-workspace/README.md.handlebars +70 -11
  67. package/template-workspace/lefthook.yml +10 -0
  68. package/template-workspace/oxfmt.config.ts +1 -0
  69. package/template-workspace/oxlint.config.ts +1 -0
  70. package/template-workspace/pnpm-workspace.yaml +31 -8
  71. package/template-workspace/scripts/bootstrap-agent-skills.mjs +190 -21
  72. package/template-workspace/scripts/setup-agent-reference-repos.mjs +370 -0
  73. package/dist/index.js +0 -2474
  74. package/template/src/routes/page.tsx.handlebars +0 -136
  75. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -405
@@ -0,0 +1,1005 @@
1
+ import "node:module";
2
+ import { execFileSync } from "node:child_process";
3
+ import node_crypto from "node:crypto";
4
+ import node_fs from "node:fs";
5
+ import node_path from "node:path";
6
+ import node_readline from "node:readline";
7
+ import { fileURLToPath } from "node:url";
8
+ import { getLocaleLanguage } from "@modern-js/i18n-utils/language-detector";
9
+ import { i18n, localeKeys } from "./locale/index.js";
10
+ import { BLEEDINGDEV_CREATE_PACKAGE, BLEEDINGDEV_FRAMEWORK_VERSION_ENV, BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, createModernPackagesMetadata, modernPackageSpecifier } from "./ultramodern-package-source.js";
11
+ import { ULTRAMODERN_WORKSPACE_FLAG, addUltramodernVertical, generateUltramodernWorkspace } from "./ultramodern-workspace.js";
12
+ const src_dirname = node_path.dirname(fileURLToPath(import.meta.url));
13
+ const templateDir = node_path.resolve(src_dirname, '..', 'template');
14
+ const semverPattern = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
15
+ const semverTagPattern = /^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$/;
16
+ const sha1Pattern = /^[0-9a-f]{40}$/;
17
+ const sha256Pattern = /^[0-9a-f]{64}$/;
18
+ const templateIdPattern = /^[a-z0-9][a-z0-9._-]*$/;
19
+ const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
20
+ const TANSTACK_ROUTER_VERSION = '1.170.15';
21
+ const TAILWIND_VERSION = '4.3.0';
22
+ const TAILWIND_POSTCSS_VERSION = '4.3.0';
23
+ const PNPM_VERSION = '11.5.0';
24
+ const I18NEXT_VERSION = '26.3.1';
25
+ const REACT_VERSION = '^19.2.7';
26
+ const REACT_DOM_VERSION = '^19.2.7';
27
+ const REACT_I18NEXT_VERSION = '17.0.8';
28
+ const EFFECT_TSGO_VERSION = '0.14.0';
29
+ const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260606.1';
30
+ const HAPPY_DOM_VERSION = '^20.10.1';
31
+ const RSTEST_CORE_VERSION = '0.10.3';
32
+ const OXFMT_VERSION = '0.53.0';
33
+ const OXLINT_VERSION = '1.68.0';
34
+ const POSTCSS_VERSION = '^8.5.15';
35
+ const ULTRACITE_VERSION = '7.8.1';
36
+ const TYPES_REACT_VERSION = '^19.2.17';
37
+ const TYPES_REACT_DOM_VERSION = '^19.2.3';
38
+ const requiredDeniedPaths = [
39
+ '.git/**',
40
+ '.npmrc',
41
+ '.yarnrc',
42
+ '.env',
43
+ '.env.*',
44
+ 'node_modules/**',
45
+ 'dist/**'
46
+ ];
47
+ const requiredLifecycleDeniedScripts = [
48
+ 'preinstall',
49
+ 'install',
50
+ 'prepare'
51
+ ];
52
+ const requiredLifecycleAllowedScripts = [
53
+ 'postinstall'
54
+ ];
55
+ function getOptionValue(args, names) {
56
+ for (const name of names){
57
+ const prefix = `${name}=`;
58
+ const byEquals = args.find((arg)=>arg.startsWith(prefix));
59
+ if (byEquals) return byEquals.slice(prefix.length);
60
+ const index = args.findIndex((arg)=>arg === name);
61
+ if (-1 !== index && args[index + 1] && !args[index + 1].startsWith('-')) return args[index + 1];
62
+ }
63
+ }
64
+ const detectLanguage = ()=>{
65
+ const lang = getOptionValue(process.argv.slice(2), [
66
+ '--lang',
67
+ '-l'
68
+ ]);
69
+ if (lang) return 'zh' === lang ? 'zh' : 'en';
70
+ const detectedLang = getLocaleLanguage();
71
+ if ('zh' === detectedLang) return 'zh';
72
+ return 'en';
73
+ };
74
+ i18n.changeLanguage({
75
+ locale: detectLanguage()
76
+ });
77
+ function detectRouterFramework() {
78
+ const args = process.argv.slice(2);
79
+ if (args.includes('--tanstack')) return 'tanstack';
80
+ const routerValue = getOptionValue(args, [
81
+ '--router',
82
+ '-r'
83
+ ]);
84
+ if (!routerValue || 'tanstack' === routerValue) return 'tanstack';
85
+ if ('react-router' === routerValue) return 'react-router';
86
+ console.error(i18n.t(localeKeys.error.invalidRouter, {
87
+ router: routerValue
88
+ }));
89
+ process.exit(1);
90
+ }
91
+ function detectBffRuntime() {
92
+ const args = process.argv.slice(2);
93
+ const runtimeValue = getOptionValue(args, [
94
+ '--bff-runtime'
95
+ ]);
96
+ if (!runtimeValue) return 'effect';
97
+ if ('hono' === runtimeValue || 'effect' === runtimeValue) return runtimeValue;
98
+ console.error(i18n.t(localeKeys.error.invalidBffRuntime, {
99
+ runtime: runtimeValue
100
+ }));
101
+ process.exit(1);
102
+ }
103
+ function renderTemplate(template, data) {
104
+ const tagRegex = /\{\{(~?)(#if|#unless|\/if|\/unless)(?:\s+(\w+))?(~?)\}\}/g;
105
+ function renderConditionals(startIndex, expectedClose) {
106
+ let rendered = '';
107
+ let cursor = startIndex;
108
+ tagRegex.lastIndex = startIndex;
109
+ while(true){
110
+ const match = tagRegex.exec(template);
111
+ if (!match) return {
112
+ rendered: rendered + template.slice(cursor),
113
+ nextIndex: template.length
114
+ };
115
+ const [raw, , tag, condition, rightTrim] = match;
116
+ const tagIndex = match.index;
117
+ rendered += template.slice(cursor, tagIndex);
118
+ cursor = tagIndex + raw.length;
119
+ if ('#if' === tag || '#unless' === tag) {
120
+ const kind = '#if' === tag ? 'if' : 'unless';
121
+ const innerResult = renderConditionals(cursor, kind);
122
+ cursor = innerResult.nextIndex;
123
+ tagRegex.lastIndex = cursor;
124
+ const conditionValue = Boolean(data[condition ?? '']);
125
+ const shouldInclude = 'if' === kind ? conditionValue : !conditionValue;
126
+ if (shouldInclude) rendered += innerResult.rendered;
127
+ continue;
128
+ }
129
+ if ('/if' === tag || '/unless' === tag) {
130
+ const kind = '/if' === tag ? 'if' : 'unless';
131
+ if (expectedClose === kind) {
132
+ let nextIndex = cursor;
133
+ if ('~' === rightTrim) {
134
+ const trailingWhitespace = /^\s*/u.exec(template.slice(nextIndex));
135
+ nextIndex += trailingWhitespace?.[0].length ?? 0;
136
+ }
137
+ return {
138
+ rendered,
139
+ nextIndex
140
+ };
141
+ }
142
+ rendered += raw;
143
+ }
144
+ }
145
+ }
146
+ let result = renderConditionals(0).rendered;
147
+ const varRegex = /\{\{(\w+)\}\}/g;
148
+ result = result.replace(varRegex, (match, key)=>{
149
+ const value = data[key];
150
+ return null != value ? String(value) : match;
151
+ });
152
+ return result;
153
+ }
154
+ function normalizePathForManifest(filePath) {
155
+ return filePath.split(node_path.sep).join('/');
156
+ }
157
+ function isUnsafeRelativePath(filePath) {
158
+ return 0 === filePath.length || node_path.isAbsolute(filePath) || filePath.startsWith('/') || /^[A-Za-z]:[\\/]/.test(filePath) || filePath.split(/[\\/]+/).includes('..');
159
+ }
160
+ function hashFile(filePath) {
161
+ return node_crypto.createHash('sha256').update(node_fs.readFileSync(filePath)).digest('hex');
162
+ }
163
+ function getTemplateFiles(dir) {
164
+ const files = [];
165
+ function collect(currentDir) {
166
+ const entries = node_fs.readdirSync(currentDir, {
167
+ withFileTypes: true
168
+ }).sort((a, b)=>a.name.localeCompare(b.name));
169
+ for (const entry of entries){
170
+ const entryPath = node_path.join(currentDir, entry.name);
171
+ if (entry.isDirectory()) collect(entryPath);
172
+ else if (entry.isFile()) files.push(normalizePathForManifest(node_path.relative(dir, entryPath)));
173
+ }
174
+ }
175
+ collect(dir);
176
+ return files;
177
+ }
178
+ function hashTemplateTree(dir) {
179
+ const hash = node_crypto.createHash('sha256');
180
+ for (const relativePath of getTemplateFiles(dir)){
181
+ const fileHash = hashFile(node_path.join(dir, relativePath));
182
+ hash.update(relativePath);
183
+ hash.update('\0');
184
+ hash.update(fileHash);
185
+ hash.update('\0');
186
+ }
187
+ return hash.digest('hex');
188
+ }
189
+ function createBuiltinTemplateManifest(version) {
190
+ return {
191
+ schemaVersion: 1,
192
+ template: {
193
+ id: 'modernjs-ultramodern-app',
194
+ version,
195
+ displayName: 'Modern.js Ultramodern App',
196
+ description: 'Repository-owned Modern.js application scaffold with UltraModern preset defaults.',
197
+ compatibilityLane: 'ultramodern-mv',
198
+ minimumModernVersion: version
199
+ },
200
+ source: {
201
+ type: 'builtin',
202
+ name: 'modernjs-ultramodern-app',
203
+ repositoryPath: 'packages/toolkit/create/template'
204
+ },
205
+ integrity: {
206
+ checksums: [
207
+ {
208
+ algorithm: 'sha256',
209
+ value: hashTemplateTree(templateDir),
210
+ scope: 'source-tree'
211
+ }
212
+ ],
213
+ provenance: {
214
+ kind: 'repo-local',
215
+ issuer: '@modern-js/create',
216
+ subject: 'packages/toolkit/create/template'
217
+ }
218
+ },
219
+ materialization: {
220
+ targetRoot: 'generated-project-root',
221
+ allowedPaths: [
222
+ '.agents/**',
223
+ '.browserslistrc',
224
+ '.codex/**',
225
+ '.github/**',
226
+ '.gitignore',
227
+ '.mise.toml',
228
+ '.modernjs/**',
229
+ '.nvmrc',
230
+ 'AGENTS.md',
231
+ 'README.md',
232
+ 'api/**',
233
+ 'config/**',
234
+ 'lefthook.yml',
235
+ 'modern.config.ts',
236
+ 'oxfmt.config.ts',
237
+ 'oxlint.config.ts',
238
+ 'package.json',
239
+ 'pnpm-workspace.yaml',
240
+ 'postcss.config.mjs',
241
+ 'rstest.config.mts',
242
+ "scripts/**",
243
+ 'shared/**',
244
+ 'src/**',
245
+ 'tailwind.config.ts',
246
+ 'tests/**',
247
+ 'tsconfig.json'
248
+ ],
249
+ deniedPaths: requiredDeniedPaths,
250
+ overwritePolicy: 'deny-existing'
251
+ },
252
+ lifecyclePolicy: {
253
+ denyByDefault: true,
254
+ deniedScripts: requiredLifecycleDeniedScripts,
255
+ allowedScripts: requiredLifecycleAllowedScripts,
256
+ requiresExplicitOptIn: true
257
+ },
258
+ validation: {
259
+ schemaValidation: true,
260
+ sourceValidation: [
261
+ 'source-type-supported',
262
+ 'checksum-verified',
263
+ 'provenance-present'
264
+ ],
265
+ materializationValidation: [
266
+ 'path-boundary-allowlist',
267
+ 'path-boundary-denylist',
268
+ 'no-path-traversal',
269
+ 'no-absolute-paths',
270
+ 'overwrite-policy-enforced'
271
+ ],
272
+ postMaterializationValidation: [
273
+ 'ultramodern-contract-check',
274
+ 'agent-skill-postinstall-allowed',
275
+ 'github-workflow-security-enforced',
276
+ 'package-source-retained',
277
+ 'pnpm-11-policy-enforced',
278
+ 'rstest-smoke-tests',
279
+ 'template-manifest-retained'
280
+ ],
281
+ expectedCommands: [
282
+ 'mise install',
283
+ 'pnpm install',
284
+ 'pnpm test',
285
+ 'pnpm run ultramodern:check'
286
+ ]
287
+ }
288
+ };
289
+ }
290
+ function assertTemplateManifest(condition, message) {
291
+ if (!condition) throw new Error(`Template manifest validation failed: ${message}`);
292
+ }
293
+ function assertSafeManifestPath(filePath, label) {
294
+ assertTemplateManifest(!isUnsafeRelativePath(filePath), `${label} is unsafe`);
295
+ }
296
+ function validateTemplateSource(source) {
297
+ const sourceType = source.type;
298
+ assertTemplateManifest('builtin' === sourceType || 'npm' === sourceType || 'git' === sourceType || 'local' === sourceType, `unsupported source type "${source.type}"`);
299
+ if ('builtin' === source.type) {
300
+ assertTemplateManifest(templateIdPattern.test(source.name), 'builtin source name must be a template id');
301
+ if (source.repositoryPath) assertSafeManifestPath(source.repositoryPath, 'builtin repositoryPath');
302
+ }
303
+ if ('npm' === source.type) {
304
+ assertTemplateManifest(packageNamePattern.test(source.packageName), 'npm packageName must be exact package metadata');
305
+ assertTemplateManifest(semverPattern.test(source.version), 'npm source version must be an exact semver');
306
+ assertTemplateManifest(sha256Pattern.test(source.tarballSha256), 'npm source tarballSha256 must be sha256 hex');
307
+ }
308
+ if ('git' === source.type) {
309
+ assertTemplateManifest(sha1Pattern.test(source.checkoutSha), 'git checkoutSha must pin a commit');
310
+ if ('sha' === source.ref.kind) assertTemplateManifest(sha1Pattern.test(source.ref.sha), 'git sha ref must be pinned to a commit');
311
+ else {
312
+ assertTemplateManifest(semverTagPattern.test(source.ref.tag), 'git tag ref must be a semver tag');
313
+ assertTemplateManifest(sha1Pattern.test(source.ref.tagSha), 'git tag ref must include the resolved tag sha');
314
+ }
315
+ if (source.subdirectory) assertSafeManifestPath(source.subdirectory, 'git subdirectory');
316
+ }
317
+ if ('local' === source.type) {
318
+ assertSafeManifestPath(source.path, 'local source path');
319
+ assertTemplateManifest(true !== source.allowOutsideWorkspace, 'local source cannot allow outside workspace materialization');
320
+ }
321
+ }
322
+ function validateTemplateManifest(manifest) {
323
+ assertTemplateManifest(1 === manifest.schemaVersion, 'schemaVersion must be 1');
324
+ assertTemplateManifest(templateIdPattern.test(manifest.template.id), 'template.id must be a template id');
325
+ assertTemplateManifest(semverPattern.test(manifest.template.version), 'template.version must be exact semver');
326
+ assertTemplateManifest('ultramodern-mv' === manifest.template.compatibilityLane || 'ultramodern-shell' === manifest.template.compatibilityLane || 'ultramodern-remote' === manifest.template.compatibilityLane, 'template.compatibilityLane is unsupported');
327
+ if (manifest.template.minimumModernVersion) assertTemplateManifest(semverPattern.test(manifest.template.minimumModernVersion), 'template.minimumModernVersion must be exact semver');
328
+ validateTemplateSource(manifest.source);
329
+ assertTemplateManifest(manifest.integrity.checksums.length > 0, 'integrity.checksums must not be empty');
330
+ for (const checksum of manifest.integrity.checksums){
331
+ assertTemplateManifest('sha256' === checksum.algorithm, 'checksum algorithm must be sha256');
332
+ assertTemplateManifest(sha256Pattern.test(checksum.value), 'checksum value must be sha256 hex');
333
+ assertTemplateManifest('manifest' === checksum.scope || 'source-archive' === checksum.scope || 'source-tree' === checksum.scope || 'lockfile' === checksum.scope, 'checksum scope is unsupported');
334
+ }
335
+ assertTemplateManifest(manifest.integrity.provenance.kind && manifest.integrity.provenance.issuer && manifest.integrity.provenance.subject, 'provenance kind, issuer, and subject are required');
336
+ if (manifest.integrity.lockfile) {
337
+ assertSafeManifestPath(manifest.integrity.lockfile.path, 'lockfile path');
338
+ assertTemplateManifest(sha256Pattern.test(manifest.integrity.lockfile.sha256), 'lockfile sha256 must be sha256 hex');
339
+ }
340
+ assertTemplateManifest('generated-project-root' === manifest.materialization.targetRoot || 'workspace-package-root' === manifest.materialization.targetRoot, 'materialization.targetRoot is unsupported');
341
+ assertTemplateManifest(manifest.materialization.allowedPaths.length > 0, 'materialization.allowedPaths must not be empty');
342
+ for (const allowedPath of manifest.materialization.allowedPaths)assertSafeManifestPath(allowedPath.replace(/\/\*\*$/, '/placeholder'), 'allowed path');
343
+ for (const deniedPath of manifest.materialization.deniedPaths)assertSafeManifestPath(deniedPath.replace(/\/\*\*$/, '/placeholder'), 'denied path');
344
+ for (const deniedPath of requiredDeniedPaths)assertTemplateManifest(manifest.materialization.deniedPaths.includes(deniedPath), `materialization.deniedPaths must include ${deniedPath}`);
345
+ assertTemplateManifest(!manifest.materialization.overwritePolicy || 'deny-existing' === manifest.materialization.overwritePolicy || 'allow-generated-only' === manifest.materialization.overwritePolicy, 'materialization.overwritePolicy is unsupported');
346
+ assertTemplateManifest(true === manifest.lifecyclePolicy.denyByDefault, 'lifecyclePolicy.denyByDefault must be true');
347
+ for (const scriptName of requiredLifecycleDeniedScripts)assertTemplateManifest(manifest.lifecyclePolicy.deniedScripts.includes(scriptName), `lifecyclePolicy.deniedScripts must include ${scriptName}`);
348
+ assertTemplateManifest(JSON.stringify(manifest.lifecyclePolicy.allowedScripts) === JSON.stringify(requiredLifecycleAllowedScripts), 'lifecyclePolicy.allowedScripts must only allow generated postinstall');
349
+ assertTemplateManifest(true === manifest.validation.schemaValidation, 'validation.schemaValidation must be true');
350
+ for (const token of [
351
+ 'source-type-supported',
352
+ 'checksum-verified',
353
+ 'provenance-present'
354
+ ])assertTemplateManifest(manifest.validation.sourceValidation.includes(token), `validation.sourceValidation must include ${token}`);
355
+ for (const token of [
356
+ 'path-boundary-allowlist',
357
+ 'path-boundary-denylist',
358
+ 'no-path-traversal',
359
+ 'no-absolute-paths',
360
+ 'overwrite-policy-enforced'
361
+ ])assertTemplateManifest(manifest.validation.materializationValidation.includes(token), `validation.materializationValidation must include ${token}`);
362
+ assertTemplateManifest(manifest.validation.postMaterializationValidation.includes('template-manifest-retained'), 'validation.postMaterializationValidation must retain manifest evidence');
363
+ }
364
+ function matchesManifestPattern(pattern, relativePath) {
365
+ if (pattern.endsWith('/**')) {
366
+ const prefix = pattern.slice(0, -3);
367
+ return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
368
+ }
369
+ if (pattern.endsWith('.*')) {
370
+ const prefix = pattern.slice(0, -1);
371
+ return relativePath.startsWith(prefix);
372
+ }
373
+ return relativePath === pattern;
374
+ }
375
+ function canMaterializePath(manifest, relativePath) {
376
+ if (isUnsafeRelativePath(relativePath)) throw new Error(`Unsafe template path rejected: ${relativePath}`);
377
+ if (manifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) return false;
378
+ if (!manifest.materialization.allowedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) throw new Error(`Template path is not allowed by manifest: ${relativePath}`);
379
+ return true;
380
+ }
381
+ function writeTemplateManifestEvidence(targetDir, manifest) {
382
+ const evidencePath = node_path.join(targetDir, '.modernjs', 'mv-template-manifest.json');
383
+ const evidenceRelativePath = normalizePathForManifest(node_path.relative(targetDir, evidencePath));
384
+ if (!canMaterializePath(manifest, evidenceRelativePath)) throw new Error('Template manifest evidence path is denied by manifest');
385
+ node_fs.mkdirSync(node_path.dirname(evidencePath), {
386
+ recursive: true
387
+ });
388
+ node_fs.writeFileSync(evidencePath, `${JSON.stringify(manifest, null, 2)}\n`);
389
+ }
390
+ function readCreatePackageJson() {
391
+ const createPackageJson = node_path.resolve(src_dirname, '..', 'package.json');
392
+ return JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
393
+ }
394
+ function isBleedingDevCreatePackage(createPackage) {
395
+ return createPackage.name === BLEEDINGDEV_CREATE_PACKAGE;
396
+ }
397
+ function getBleedingDevFrameworkVersion(createPackage, fallbackVersion) {
398
+ const frameworkVersion = createPackage.ultramodern?.frameworkVersion;
399
+ return 'string' == typeof frameworkVersion && frameworkVersion.length > 0 ? frameworkVersion : fallbackVersion;
400
+ }
401
+ function showVersion() {
402
+ const createPackage = readCreatePackageJson();
403
+ const version = createPackage.version || 'unknown';
404
+ console.log(i18n.t(localeKeys.version.message, {
405
+ version
406
+ }));
407
+ process.exit(0);
408
+ }
409
+ function showHelp() {
410
+ console.log(i18n.t(localeKeys.help.title));
411
+ console.log(i18n.t(localeKeys.help.description));
412
+ console.log('');
413
+ console.log(i18n.t(localeKeys.help.usage));
414
+ console.log(i18n.t(localeKeys.help.usageExample));
415
+ console.log('');
416
+ console.log(i18n.t(localeKeys.help.options));
417
+ console.log(i18n.t(localeKeys.help.optionHelp));
418
+ console.log(i18n.t(localeKeys.help.optionVersion));
419
+ console.log(i18n.t(localeKeys.help.optionLang));
420
+ console.log(i18n.t(localeKeys.help.optionRouter));
421
+ if (localeKeys.help.optionBff) console.log(i18n.t(localeKeys.help.optionBff));
422
+ if (localeKeys.help.optionBffRuntime) console.log(i18n.t(localeKeys.help.optionBffRuntime));
423
+ if (localeKeys.help.optionTailwind) console.log(i18n.t(localeKeys.help.optionTailwind));
424
+ if (localeKeys.help.optionWorkspace) console.log(i18n.t(localeKeys.help.optionWorkspace));
425
+ if (localeKeys.help.optionUltramodernWorkspace) console.log(i18n.t(localeKeys.help.optionUltramodernWorkspace));
426
+ if (localeKeys.help.optionUltramodernPackageSource) console.log(i18n.t(localeKeys.help.optionUltramodernPackageSource));
427
+ if (localeKeys.help.optionUltramodernPackageScope) console.log(i18n.t(localeKeys.help.optionUltramodernPackageScope));
428
+ if (localeKeys.help.optionUltramodernPackageNamePrefix) console.log(i18n.t(localeKeys.help.optionUltramodernPackageNamePrefix));
429
+ if (localeKeys.help.optionVertical) console.log(i18n.t(localeKeys.help.optionVertical));
430
+ console.log(i18n.t(localeKeys.help.optionSub));
431
+ console.log('');
432
+ console.log(i18n.t(localeKeys.help.examples));
433
+ console.log(i18n.t(localeKeys.help.example1));
434
+ console.log(i18n.t(localeKeys.help.example2));
435
+ console.log(i18n.t(localeKeys.help.example3));
436
+ if (localeKeys.help.example4) console.log(i18n.t(localeKeys.help.example4));
437
+ if (localeKeys.help.example5) console.log(i18n.t(localeKeys.help.example5));
438
+ if (localeKeys.help.example6) console.log(i18n.t(localeKeys.help.example6));
439
+ if (localeKeys.help.example7) console.log(i18n.t(localeKeys.help.example7));
440
+ if (localeKeys.help.example8) console.log(i18n.t(localeKeys.help.example8));
441
+ if (localeKeys.help.example9) console.log(i18n.t(localeKeys.help.example9));
442
+ if (localeKeys.help.example10) console.log(i18n.t(localeKeys.help.example10));
443
+ if (localeKeys.help.example11) console.log(i18n.t(localeKeys.help.example11));
444
+ if (localeKeys.help.example12) console.log(i18n.t(localeKeys.help.example12));
445
+ console.log('');
446
+ console.log(i18n.t(localeKeys.help.moreInfo));
447
+ console.log('');
448
+ process.exit(0);
449
+ }
450
+ function promptInput(question) {
451
+ const rl = node_readline.createInterface({
452
+ input: process.stdin,
453
+ output: process.stdout
454
+ });
455
+ return new Promise((resolve)=>{
456
+ rl.question(question, (answer)=>{
457
+ rl.close();
458
+ resolve(answer.trim());
459
+ });
460
+ });
461
+ }
462
+ function detectSubprojectFlag() {
463
+ const args = process.argv.slice(2);
464
+ if (args.includes('--sub') || args.includes('-s')) return true;
465
+ if (args.includes('--no-sub')) return false;
466
+ return null;
467
+ }
468
+ function detectTailwindFlag() {
469
+ const args = process.argv.slice(2);
470
+ return !args.includes('--no-tailwind');
471
+ }
472
+ function detectExplicitTailwindFlag() {
473
+ const args = process.argv.slice(2);
474
+ if (args.includes('--no-tailwind')) return false;
475
+ if (args.includes('--tailwind')) return true;
476
+ }
477
+ function detectWorkspaceProtocolFlag() {
478
+ const args = process.argv.slice(2);
479
+ return args.includes('--workspace');
480
+ }
481
+ function detectVerticalFlag() {
482
+ const args = process.argv.slice(2);
483
+ if (args.some((arg)=>arg.startsWith('--vertical='))) {
484
+ console.error('--vertical does not accept a value. Use: create <name> --vertical');
485
+ process.exit(1);
486
+ }
487
+ return args.includes('--vertical');
488
+ }
489
+ function detectUltramodernWorkspaceFlag() {
490
+ const args = process.argv.slice(2);
491
+ return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
492
+ }
493
+ function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
494
+ const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
495
+ const strategy = getOptionValue(args, [
496
+ '--ultramodern-package-source'
497
+ ]) ?? (bleedingDevDefaults ? 'install' : 'workspace');
498
+ if ('workspace' !== strategy && 'install' !== strategy) {
499
+ console.error('--ultramodern-package-source must be "workspace" or "install"');
500
+ process.exit(1);
501
+ }
502
+ const packageSourceStrategy = strategy;
503
+ const explicitRegistry = getOptionValue(args, [
504
+ '--ultramodern-package-registry'
505
+ ]);
506
+ const aliasScope = getOptionValue(args, [
507
+ '--ultramodern-package-scope'
508
+ ]) ?? (bleedingDevDefaults && 'install' === packageSourceStrategy && !explicitRegistry ? BLEEDINGDEV_PACKAGE_SCOPE : void 0);
509
+ return {
510
+ strategy: packageSourceStrategy,
511
+ modernPackageVersion: getOptionValue(args, [
512
+ '--ultramodern-package-version'
513
+ ]) ?? defaultPackageVersion,
514
+ registry: explicitRegistry,
515
+ aliasScope,
516
+ aliasPackageNamePrefix: getOptionValue(args, [
517
+ '--ultramodern-package-name-prefix'
518
+ ]) ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
519
+ };
520
+ }
521
+ function hasExplicitUltramodernPackageSource(args, value) {
522
+ const configuredValue = getOptionValue(args, [
523
+ '--ultramodern-package-source'
524
+ ]);
525
+ return value ? configuredValue === value : void 0 !== configuredValue;
526
+ }
527
+ function readBleedingDevFrameworkVersionFromRegistry() {
528
+ const envVersion = process.env[BLEEDINGDEV_FRAMEWORK_VERSION_ENV]?.trim();
529
+ if (envVersion) {
530
+ if (!semverPattern.test(envVersion)) {
531
+ console.error(`${BLEEDINGDEV_FRAMEWORK_VERSION_ENV} must be a valid semver version`);
532
+ process.exit(1);
533
+ }
534
+ return envVersion;
535
+ }
536
+ try {
537
+ const rawVersion = runSetupCommand('npm', [
538
+ 'view',
539
+ `${BLEEDINGDEV_CREATE_PACKAGE}@latest`,
540
+ 'ultramodern.frameworkVersion',
541
+ '--json'
542
+ ]).trim();
543
+ const version = JSON.parse(rawVersion);
544
+ if ('string' == typeof version && semverPattern.test(version)) return version;
545
+ } catch {}
546
+ console.error([
547
+ `Could not resolve ${BLEEDINGDEV_CREATE_PACKAGE}@latest ultramodern.frameworkVersion.`,
548
+ 'Pass --workspace to use local workspace protocol dependencies,',
549
+ 'or pass --ultramodern-package-version with the exact BleedingDev framework cohort.'
550
+ ].join(' '));
551
+ process.exit(1);
552
+ }
553
+ function resolveInstallBackedPackageSource(args, createPackage, packageSource) {
554
+ const explicitVersion = getOptionValue(args, [
555
+ '--ultramodern-package-version'
556
+ ]);
557
+ const explicitRegistry = getOptionValue(args, [
558
+ '--ultramodern-package-registry'
559
+ ]);
560
+ const aliasScope = getOptionValue(args, [
561
+ '--ultramodern-package-scope'
562
+ ]) ?? packageSource.aliasScope ?? (explicitRegistry ? void 0 : BLEEDINGDEV_PACKAGE_SCOPE);
563
+ return {
564
+ ...packageSource,
565
+ strategy: 'install',
566
+ modernPackageVersion: explicitVersion ?? (isBleedingDevCreatePackage(createPackage) ? packageSource.modernPackageVersion : readBleedingDevFrameworkVersionFromRegistry()),
567
+ aliasScope,
568
+ aliasPackageNamePrefix: getOptionValue(args, [
569
+ '--ultramodern-package-name-prefix'
570
+ ]) ?? packageSource.aliasPackageNamePrefix ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
571
+ };
572
+ }
573
+ function resolveSingleAppPackageSource(args, createPackage, packageSource, useWorkspaceProtocol) {
574
+ if (useWorkspaceProtocol) return {
575
+ ...packageSource,
576
+ strategy: 'workspace',
577
+ modernPackageVersion: WORKSPACE_PACKAGE_VERSION
578
+ };
579
+ return resolveInstallBackedPackageSource(args, createPackage, packageSource);
580
+ }
581
+ function resolveWorkspacePackageSource(args, createPackage, packageSource) {
582
+ if (hasExplicitUltramodernPackageSource(args, 'workspace')) return {
583
+ ...packageSource,
584
+ strategy: 'workspace',
585
+ modernPackageVersion: WORKSPACE_PACKAGE_VERSION
586
+ };
587
+ return resolveInstallBackedPackageSource(args, createPackage, packageSource);
588
+ }
589
+ function createSingleAppPackageSourceEvidence(packageSource) {
590
+ return {
591
+ schemaVersion: 1,
592
+ preset: 'presetUltramodern',
593
+ strategy: packageSource.strategy,
594
+ modernPackages: createModernPackagesMetadata(ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, packageSource, {
595
+ includeAliases: 'install' === packageSource.strategy
596
+ })
597
+ };
598
+ }
599
+ function writeSingleAppPackageSourceEvidence(targetDir, packageSource) {
600
+ const evidencePath = node_path.join(targetDir, '.modernjs', 'ultramodern-package-source.json');
601
+ node_fs.mkdirSync(node_path.dirname(evidencePath), {
602
+ recursive: true
603
+ });
604
+ node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource), null, 2)}\n`);
605
+ }
606
+ function runSetupCommand(command, args, options = {}) {
607
+ return execFileSync(command, args, {
608
+ cwd: options.cwd,
609
+ encoding: 'utf-8',
610
+ stdio: options.stdio ?? [
611
+ 'ignore',
612
+ 'pipe',
613
+ 'pipe'
614
+ ]
615
+ });
616
+ }
617
+ function commandExists(command) {
618
+ try {
619
+ runSetupCommand(command, [
620
+ '--version'
621
+ ], {
622
+ stdio: 'ignore'
623
+ });
624
+ return true;
625
+ } catch {
626
+ return false;
627
+ }
628
+ }
629
+ function installGitForGeneratedProject() {
630
+ if (commandExists('git')) return;
631
+ const runShell = (script)=>runSetupCommand('sh', [
632
+ '-lc',
633
+ script
634
+ ], {
635
+ stdio: 'inherit'
636
+ });
637
+ const sudo = 'function' == typeof process.getuid && 0 === process.getuid() ? '' : 'sudo ';
638
+ if (commandExists('brew')) runSetupCommand('brew', [
639
+ 'install',
640
+ 'git'
641
+ ], {
642
+ stdio: 'inherit'
643
+ });
644
+ else if ('linux' === process.platform && commandExists('apt-get')) runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
645
+ else if ('linux' === process.platform && commandExists('dnf')) runShell(`${sudo}dnf install -y git`);
646
+ else if ('linux' === process.platform && commandExists('yum')) runShell(`${sudo}yum install -y git`);
647
+ else if ('linux' === process.platform && commandExists('apk')) runShell('apk add --no-cache git');
648
+ if (!commandExists('git')) throw new Error('Git is required for UltraModern setup. Install git and rerun create, or run pnpm skills:install after installing git.');
649
+ }
650
+ function isInsideGitWorkTree(targetDir) {
651
+ try {
652
+ return 'true' === runSetupCommand('git', [
653
+ 'rev-parse',
654
+ '--is-inside-work-tree'
655
+ ], {
656
+ cwd: targetDir
657
+ }).trim();
658
+ } catch {
659
+ return false;
660
+ }
661
+ }
662
+ function initializeGeneratedGitRepository(targetDir) {
663
+ installGitForGeneratedProject();
664
+ if (isInsideGitWorkTree(targetDir)) return;
665
+ try {
666
+ runSetupCommand('git', [
667
+ 'init',
668
+ '-b',
669
+ 'main'
670
+ ], {
671
+ cwd: targetDir,
672
+ stdio: 'inherit'
673
+ });
674
+ } catch {
675
+ runSetupCommand('git', [
676
+ 'init'
677
+ ], {
678
+ cwd: targetDir,
679
+ stdio: 'inherit'
680
+ });
681
+ runSetupCommand('git', [
682
+ 'branch',
683
+ '-M',
684
+ 'main'
685
+ ], {
686
+ cwd: targetDir,
687
+ stdio: 'inherit'
688
+ });
689
+ }
690
+ }
691
+ function isDirectoryEmpty(dirPath) {
692
+ if (!node_fs.existsSync(dirPath)) return false;
693
+ try {
694
+ const files = node_fs.readdirSync(dirPath);
695
+ return 0 === files.length;
696
+ } catch {
697
+ return false;
698
+ }
699
+ }
700
+ async function getProjectName() {
701
+ const args = process.argv.slice(2);
702
+ const optionWithValue = new Set([
703
+ '--lang',
704
+ '-l',
705
+ '--router',
706
+ '-r',
707
+ '--bff-runtime',
708
+ '--ultramodern-package-source',
709
+ '--ultramodern-package-version',
710
+ '--ultramodern-package-registry',
711
+ '--ultramodern-package-scope',
712
+ '--ultramodern-package-name-prefix'
713
+ ]);
714
+ const optionWithoutValue = new Set([
715
+ '--help',
716
+ '-h',
717
+ '--version',
718
+ '-v',
719
+ '--sub',
720
+ '-s',
721
+ '--no-sub',
722
+ '--tanstack',
723
+ '--bff',
724
+ '--tailwind',
725
+ '--no-tailwind',
726
+ '--workspace',
727
+ '--vertical',
728
+ ULTRAMODERN_WORKSPACE_FLAG
729
+ ]);
730
+ const positionalArgs = [];
731
+ for(let i = 0; i < args.length; i++){
732
+ const arg = args[i];
733
+ if (!optionWithoutValue.has(arg)) {
734
+ if (optionWithValue.has(arg)) {
735
+ i += 1;
736
+ continue;
737
+ }
738
+ if (!(arg.startsWith('--lang=') || arg.startsWith('--router=') || arg.startsWith('--bff-runtime=') || arg.startsWith('--ultramodern-package-source=') || arg.startsWith('--ultramodern-package-version=') || arg.startsWith('--ultramodern-package-registry=') || arg.startsWith('--ultramodern-package-scope=') || arg.startsWith('--ultramodern-package-name-prefix='))) positionalArgs.push(arg);
739
+ }
740
+ }
741
+ if (positionalArgs.length > 1) {
742
+ console.error(`Unexpected positional argument: ${positionalArgs[1]}`);
743
+ process.exit(1);
744
+ }
745
+ const projectNameArg = positionalArgs[0];
746
+ if (projectNameArg) {
747
+ if ('.' === projectNameArg) return {
748
+ name: node_path.basename(process.cwd()),
749
+ useCurrentDir: true
750
+ };
751
+ return {
752
+ name: projectNameArg,
753
+ useCurrentDir: false
754
+ };
755
+ }
756
+ const currentDir = process.cwd();
757
+ if (isDirectoryEmpty(currentDir)) return {
758
+ name: node_path.basename(currentDir),
759
+ useCurrentDir: true
760
+ };
761
+ const projectName = await promptInput(i18n.t(localeKeys.prompt.projectName));
762
+ if (!projectName) {
763
+ console.error(i18n.t(localeKeys.error.projectNameEmpty));
764
+ process.exit(1);
765
+ }
766
+ return {
767
+ name: projectName,
768
+ useCurrentDir: false
769
+ };
770
+ }
771
+ async function main() {
772
+ const args = process.argv.slice(2);
773
+ if (args.includes('--help') || args.includes('-h')) return void showHelp();
774
+ if (args.includes('--version') || args.includes('-v')) return void showVersion();
775
+ console.log(`\n${i18n.t(localeKeys.message.welcome)}\n`);
776
+ const { name: projectName, useCurrentDir } = await getProjectName();
777
+ const targetDir = useCurrentDir ? process.cwd() : node_path.isAbsolute(projectName) ? projectName : node_path.resolve(process.cwd(), projectName);
778
+ const generatedPackageName = useCurrentDir || node_path.isAbsolute(projectName) ? node_path.basename(targetDir) : projectName;
779
+ const createPackage = readCreatePackageJson();
780
+ const version = createPackage.version || 'latest';
781
+ const ultramodernPackageVersion = isBleedingDevCreatePackage(createPackage) ? getBleedingDevFrameworkVersion(createPackage, version) : version;
782
+ const addVertical = detectVerticalFlag();
783
+ if (addVertical) {
784
+ const overridePackageSource = args.some((arg)=>arg.startsWith('--ultramodern-package-')) ? detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage) : void 0;
785
+ addUltramodernVertical({
786
+ workspaceRoot: process.cwd(),
787
+ name: generatedPackageName,
788
+ modernVersion: version,
789
+ enableTailwind: detectExplicitTailwindFlag(),
790
+ packageSource: overridePackageSource
791
+ });
792
+ const dim = '\x1b[2m\x1b[3m';
793
+ const reset = '\x1b[0m';
794
+ console.log(`${i18n.t(localeKeys.message.success)}\n`);
795
+ console.log(`${dim} pnpm ultramodern:check${reset}\n`);
796
+ return;
797
+ }
798
+ if (node_fs.existsSync(targetDir)) {
799
+ const files = node_fs.readdirSync(targetDir);
800
+ if (files.length > 0) {
801
+ console.error(i18n.t(localeKeys.error.directoryExists, {
802
+ projectName
803
+ }));
804
+ process.exit(1);
805
+ }
806
+ }
807
+ const generateWorkspace = detectUltramodernWorkspaceFlag();
808
+ if (generateWorkspace) {
809
+ const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
810
+ generateUltramodernWorkspace({
811
+ targetDir,
812
+ packageName: generatedPackageName,
813
+ modernVersion: version,
814
+ enableTailwind: detectTailwindFlag(),
815
+ packageSource
816
+ });
817
+ initializeGeneratedGitRepository(targetDir);
818
+ const dim = '\x1b[2m\x1b[3m';
819
+ const reset = '\x1b[0m';
820
+ console.log(`${i18n.t(localeKeys.message.success)}\n`);
821
+ console.log(i18n.t(localeKeys.message.nextSteps));
822
+ if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
823
+ projectName
824
+ })}${reset}`);
825
+ console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
826
+ console.log(`${dim} pnpm ultramodern:check${reset}`);
827
+ console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
828
+ return;
829
+ }
830
+ const subprojectFlag = detectSubprojectFlag();
831
+ const isSubproject = true === subprojectFlag;
832
+ const routerFramework = detectRouterFramework();
833
+ const bffRuntime = detectBffRuntime();
834
+ const enableTailwind = detectTailwindFlag();
835
+ const useWorkspaceProtocol = detectWorkspaceProtocolFlag();
836
+ const packageSource = resolveSingleAppPackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage), useWorkspaceProtocol);
837
+ const templateManifest = createBuiltinTemplateManifest('install' === packageSource.strategy ? packageSource.modernPackageVersion : version);
838
+ validateTemplateManifest(templateManifest);
839
+ copyTemplate(templateDir, targetDir, {
840
+ packageName: generatedPackageName,
841
+ version: 'workspace' === packageSource.strategy ? WORKSPACE_PACKAGE_VERSION : packageSource.modernPackageVersion,
842
+ runtimeVersion: modernPackageSpecifier('@modern-js/runtime', packageSource),
843
+ appToolsVersion: modernPackageSpecifier('@modern-js/app-tools', packageSource),
844
+ adapterRstestVersion: modernPackageSpecifier('@modern-js/adapter-rstest', packageSource),
845
+ codeToolsVersion: modernPackageSpecifier('@modern-js/code-tools', packageSource),
846
+ createVersion: modernPackageSpecifier('@modern-js/create', packageSource),
847
+ tsconfigVersion: modernPackageSpecifier('@modern-js/tsconfig', packageSource),
848
+ pluginTanstackVersion: modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
849
+ pluginBffVersion: modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
850
+ pluginI18nVersion: modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
851
+ tanstackRouterVersion: TANSTACK_ROUTER_VERSION,
852
+ i18nextVersion: I18NEXT_VERSION,
853
+ reactVersion: REACT_VERSION,
854
+ reactDomVersion: REACT_DOM_VERSION,
855
+ reactI18nextVersion: REACT_I18NEXT_VERSION,
856
+ effectTsgoVersion: EFFECT_TSGO_VERSION,
857
+ typescriptNativePreviewVersion: TYPESCRIPT_NATIVE_PREVIEW_VERSION,
858
+ rstestCoreVersion: RSTEST_CORE_VERSION,
859
+ happyDomVersion: HAPPY_DOM_VERSION,
860
+ oxfmtVersion: OXFMT_VERSION,
861
+ oxlintVersion: OXLINT_VERSION,
862
+ postcssVersion: POSTCSS_VERSION,
863
+ ultraciteVersion: ULTRACITE_VERSION,
864
+ typesReactVersion: TYPES_REACT_VERSION,
865
+ typesReactDomVersion: TYPES_REACT_DOM_VERSION,
866
+ tailwindVersion: TAILWIND_VERSION,
867
+ tailwindPostcssVersion: TAILWIND_POSTCSS_VERSION,
868
+ pnpmVersion: PNPM_VERSION,
869
+ isSubproject,
870
+ routerFramework,
871
+ bffRuntime,
872
+ enableTailwind,
873
+ templateManifest
874
+ });
875
+ const targetPackageJson = node_path.join(targetDir, 'package.json');
876
+ const packageJson = JSON.parse(node_fs.readFileSync(targetPackageJson, 'utf-8'));
877
+ packageJson.name = generatedPackageName;
878
+ packageJson.modernjs = {
879
+ ...packageJson.modernjs ?? {},
880
+ preset: 'presetUltramodern',
881
+ packageSource: {
882
+ strategy: packageSource.strategy,
883
+ config: './.modernjs/ultramodern-package-source.json'
884
+ }
885
+ };
886
+ if (isSubproject) {
887
+ delete packageJson['lint-staged'];
888
+ delete packageJson['simple-git-hooks'];
889
+ if (packageJson.scripts) {
890
+ delete packageJson.scripts.prepare;
891
+ delete packageJson.scripts['skills:install'];
892
+ delete packageJson.scripts['skills:check'];
893
+ delete packageJson.scripts.postinstall;
894
+ }
895
+ if (packageJson.devDependencies) {
896
+ delete packageJson.devDependencies['lint-staged'];
897
+ delete packageJson.devDependencies.lefthook;
898
+ delete packageJson.devDependencies['simple-git-hooks'];
899
+ }
900
+ node_fs.rmSync(node_path.join(targetDir, '.codex'), {
901
+ recursive: true,
902
+ force: true
903
+ });
904
+ node_fs.rmSync(node_path.join(targetDir, 'lefthook.yml'), {
905
+ force: true
906
+ });
907
+ }
908
+ node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
909
+ writeTemplateManifestEvidence(targetDir, templateManifest);
910
+ writeSingleAppPackageSourceEvidence(targetDir, packageSource);
911
+ if (!isSubproject) initializeGeneratedGitRepository(targetDir);
912
+ const dim = '\x1b[2m\x1b[3m';
913
+ const reset = '\x1b[0m';
914
+ console.log(`${i18n.t(localeKeys.message.success)}\n`);
915
+ console.log(i18n.t(localeKeys.message.nextSteps));
916
+ if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
917
+ projectName
918
+ })}${reset}`);
919
+ console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
920
+ console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
921
+ }
922
+ function copyTemplate(src, dest, options) {
923
+ node_fs.mkdirSync(dest, {
924
+ recursive: true
925
+ });
926
+ const excludeInSubproject = [
927
+ '.agents',
928
+ '.github',
929
+ '.gitignore.handlebars',
930
+ 'AGENTS.md',
931
+ '.npmrc',
932
+ '.nvmrc'
933
+ ];
934
+ function copyRecursive(srcDir, destDir) {
935
+ const entries = node_fs.readdirSync(srcDir, {
936
+ withFileTypes: true
937
+ });
938
+ for (const entry of entries){
939
+ if (options.isSubproject && excludeInSubproject.includes(entry.name)) continue;
940
+ const srcPath = node_path.join(srcDir, entry.name);
941
+ let destPath = node_path.join(destDir, entry.name);
942
+ const sourceRelativePath = normalizePathForManifest(node_path.relative(src, srcPath));
943
+ const finalRelativePath = normalizePathForManifest(sourceRelativePath.replace(/\.handlebars$/, ''));
944
+ if (!(!canMaterializePath(options.templateManifest, finalRelativePath) || entry.isDirectory() && options.templateManifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, finalRelativePath)))) if (entry.isDirectory()) {
945
+ node_fs.mkdirSync(destPath, {
946
+ recursive: true
947
+ });
948
+ copyRecursive(srcPath, destPath);
949
+ } else if (entry.name.endsWith('.handlebars')) {
950
+ const templateContent = node_fs.readFileSync(srcPath, 'utf-8');
951
+ const rendered = renderTemplate(templateContent, {
952
+ packageName: options.packageName,
953
+ version: options.version,
954
+ runtimeVersion: options.runtimeVersion,
955
+ appToolsVersion: options.appToolsVersion,
956
+ adapterRstestVersion: options.adapterRstestVersion,
957
+ codeToolsVersion: options.codeToolsVersion,
958
+ createVersion: options.createVersion,
959
+ tsconfigVersion: options.tsconfigVersion,
960
+ pluginTanstackVersion: options.pluginTanstackVersion,
961
+ pluginBffVersion: options.pluginBffVersion,
962
+ pluginI18nVersion: options.pluginI18nVersion,
963
+ tanstackRouterVersion: options.tanstackRouterVersion,
964
+ i18nextVersion: options.i18nextVersion,
965
+ reactVersion: options.reactVersion,
966
+ reactDomVersion: options.reactDomVersion,
967
+ reactI18nextVersion: options.reactI18nextVersion,
968
+ effectTsgoVersion: options.effectTsgoVersion,
969
+ typescriptNativePreviewVersion: options.typescriptNativePreviewVersion,
970
+ rstestCoreVersion: options.rstestCoreVersion,
971
+ happyDomVersion: options.happyDomVersion,
972
+ oxfmtVersion: options.oxfmtVersion,
973
+ oxlintVersion: options.oxlintVersion,
974
+ postcssVersion: options.postcssVersion,
975
+ ultraciteVersion: options.ultraciteVersion,
976
+ typesReactVersion: options.typesReactVersion,
977
+ typesReactDomVersion: options.typesReactDomVersion,
978
+ tailwindVersion: options.tailwindVersion,
979
+ tailwindPostcssVersion: options.tailwindPostcssVersion,
980
+ pnpmVersion: options.pnpmVersion,
981
+ isSubproject: options.isSubproject,
982
+ isTanstackRouter: 'tanstack' === options.routerFramework,
983
+ enableBff: 'none' !== options.bffRuntime,
984
+ useEffectBff: 'effect' === options.bffRuntime,
985
+ useHonoBff: 'hono' === options.bffRuntime,
986
+ bffRuntime: options.bffRuntime,
987
+ enableTailwind: options.enableTailwind,
988
+ routerRuntimeImport: 'tanstack' === options.routerFramework ? '@modern-js/plugin-tanstack/runtime' : '@modern-js/runtime/router'
989
+ });
990
+ if (0 === rendered.trim().length) continue;
991
+ destPath = destPath.replace(/\.handlebars$/, '');
992
+ if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
993
+ node_fs.writeFileSync(destPath, rendered, 'utf-8');
994
+ } else {
995
+ if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
996
+ node_fs.copyFileSync(srcPath, destPath);
997
+ }
998
+ }
999
+ }
1000
+ copyRecursive(src, dest);
1001
+ }
1002
+ main().catch((error)=>{
1003
+ console.error(i18n.t(localeKeys.error.createFailed), error);
1004
+ process.exit(1);
1005
+ });