@bleedingdev/modern-js-create 3.2.0-ultramodern.116 → 3.2.0-ultramodern.118

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 (62) hide show
  1. package/README.md +35 -125
  2. package/dist/cjs/create-package-root.cjs +1 -1
  3. package/dist/cjs/index.cjs +12 -606
  4. package/dist/cjs/locale/en.cjs +13 -20
  5. package/dist/cjs/locale/zh.cjs +13 -20
  6. package/dist/cjs/ultramodern-workspace.cjs +16 -19
  7. package/dist/esm/create-package-root.js +1 -1
  8. package/dist/esm/index.js +14 -607
  9. package/dist/esm/locale/en.js +13 -20
  10. package/dist/esm/locale/zh.js +13 -20
  11. package/dist/esm/ultramodern-workspace.js +17 -17
  12. package/dist/esm-node/create-package-root.js +1 -1
  13. package/dist/esm-node/index.js +14 -607
  14. package/dist/esm-node/locale/en.js +13 -20
  15. package/dist/esm-node/locale/zh.js +13 -20
  16. package/dist/esm-node/ultramodern-workspace.js +17 -17
  17. package/dist/types/locale/en.d.ts +0 -7
  18. package/dist/types/locale/index.d.ts +0 -14
  19. package/dist/types/locale/zh.d.ts +0 -7
  20. package/dist/types/ultramodern-workspace.d.ts +0 -1
  21. package/package.json +4 -5
  22. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +22 -6
  23. package/template-workspace/AGENTS.md +7 -3
  24. package/template-workspace/README.md.handlebars +5 -1
  25. package/template-workspace/lefthook.yml +18 -4
  26. package/template/.agents/skills-lock.json +0 -34
  27. package/template/.browserslistrc +0 -4
  28. package/template/.codex/hooks.json +0 -16
  29. package/template/.github/renovate.json +0 -53
  30. package/template/.github/workflows/ultramodern-gates.yml.handlebars +0 -54
  31. package/template/.gitignore.handlebars +0 -30
  32. package/template/.mise.toml.handlebars +0 -2
  33. package/template/.nvmrc +0 -2
  34. package/template/AGENTS.md +0 -23
  35. package/template/README.md +0 -111
  36. package/template/api/effect/index.ts.handlebars +0 -34
  37. package/template/api/lambda/hello.ts.handlebars +0 -6
  38. package/template/config/favicon.svg +0 -5
  39. package/template/config/public/assets/ultramodern-logo.svg +0 -6
  40. package/template/config/public/locales/cs/translation.json +0 -44
  41. package/template/config/public/locales/en/translation.json +0 -44
  42. package/template/lefthook.yml +0 -10
  43. package/template/modern.config.ts.handlebars +0 -78
  44. package/template/oxfmt.config.ts +0 -15
  45. package/template/oxlint.config.ts +0 -19
  46. package/template/package.json.handlebars +0 -69
  47. package/template/pnpm-workspace.yaml +0 -34
  48. package/template/postcss.config.mjs.handlebars +0 -6
  49. package/template/rstest.config.mts +0 -5
  50. package/template/scripts/bootstrap-agent-skills.mjs +0 -228
  51. package/template/scripts/check-i18n-strings.mjs +0 -3
  52. package/template/scripts/validate-ultramodern.mjs.handlebars +0 -658
  53. package/template/shared/effect/api.ts.handlebars +0 -17
  54. package/template/src/modern-app-env.d.ts +0 -3
  55. package/template/src/modern.runtime.ts.handlebars +0 -23
  56. package/template/src/routes/[lang]/page.tsx.handlebars +0 -209
  57. package/template/src/routes/index.css.handlebars +0 -266
  58. package/template/src/routes/layout.tsx.handlebars +0 -10
  59. package/template/tailwind.config.ts.handlebars +0 -10
  60. package/template/tests/tsconfig.json +0 -7
  61. package/template/tests/ultramodern.contract.test.ts.handlebars +0 -163
  62. package/template/tsconfig.json +0 -121
@@ -1,59 +1,16 @@
1
1
  import "node:module";
2
2
  import { execFileSync } from "node:child_process";
3
- import node_crypto from "node:crypto";
4
3
  import node_fs from "node:fs";
5
4
  import node_path from "node:path";
6
5
  import node_readline from "node:readline";
7
6
  import { fileURLToPath } from "node:url";
8
- import { getLocaleLanguage } from "@modern-js/i18n-utils/language-detector";
9
7
  import { resolveCreatePackageRoot } from "./create-package-root.js";
10
8
  import { i18n, localeKeys } from "./locale/index.js";
11
- 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";
12
- import { ULTRAMODERN_WORKSPACE_FLAG, addUltramodernVertical, generateUltramodernWorkspace } from "./ultramodern-workspace.js";
9
+ import { BLEEDINGDEV_CREATE_PACKAGE, BLEEDINGDEV_FRAMEWORK_VERSION_ENV, BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, WORKSPACE_PACKAGE_VERSION } from "./ultramodern-package-source.js";
10
+ import { addUltramodernVertical, generateUltramodernWorkspace } from "./ultramodern-workspace.js";
13
11
  const src_dirname = node_path.dirname(fileURLToPath(import.meta.url));
14
12
  const createPackageRoot = resolveCreatePackageRoot(src_dirname);
15
- const templateDir = node_path.join(createPackageRoot, 'template');
16
13
  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.-]+)?$/;
17
- const semverTagPattern = /^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$/;
18
- const sha1Pattern = /^[0-9a-f]{40}$/;
19
- const sha256Pattern = /^[0-9a-f]{64}$/;
20
- const templateIdPattern = /^[a-z0-9][a-z0-9._-]*$/;
21
- const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
22
- const TANSTACK_ROUTER_VERSION = '1.170.15';
23
- const TAILWIND_VERSION = '4.3.0';
24
- const TAILWIND_POSTCSS_VERSION = '4.3.0';
25
- const PNPM_VERSION = '11.5.2';
26
- const I18NEXT_VERSION = '26.3.1';
27
- const REACT_VERSION = '^19.2.7';
28
- const REACT_DOM_VERSION = '^19.2.7';
29
- const REACT_I18NEXT_VERSION = '17.0.8';
30
- const EFFECT_TSGO_VERSION = '0.14.0';
31
- const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260606.1';
32
- const HAPPY_DOM_VERSION = '^20.10.1';
33
- const RSTEST_CORE_VERSION = '0.10.3';
34
- const OXFMT_VERSION = '0.53.0';
35
- const OXLINT_VERSION = '1.68.0';
36
- const POSTCSS_VERSION = '^8.5.15';
37
- const ULTRACITE_VERSION = '7.8.1';
38
- const TYPES_REACT_VERSION = '^19.2.17';
39
- const TYPES_REACT_DOM_VERSION = '^19.2.3';
40
- const requiredDeniedPaths = [
41
- '.git/**',
42
- '.npmrc',
43
- '.yarnrc',
44
- '.env',
45
- '.env.*',
46
- 'node_modules/**',
47
- 'dist/**'
48
- ];
49
- const requiredLifecycleDeniedScripts = [
50
- 'preinstall',
51
- 'install',
52
- 'prepare'
53
- ];
54
- const requiredLifecycleAllowedScripts = [
55
- 'postinstall'
56
- ];
57
14
  const LEGACY_MODERN_JS_FLAG = '--legacy-modern-js';
58
15
  const LEGACY_MODERN_JS_CONFIRMATION = 'USE LEGACY MODERN.JS';
59
16
  function getOptionValue(args, names) {
@@ -70,327 +27,12 @@ const detectLanguage = ()=>{
70
27
  '--lang',
71
28
  '-l'
72
29
  ]);
73
- if (lang) return 'zh' === lang ? 'zh' : 'en';
74
- const detectedLang = getLocaleLanguage();
75
- if ('zh' === detectedLang) return 'zh';
30
+ if ('zh' === lang) return 'zh';
76
31
  return 'en';
77
32
  };
78
33
  i18n.changeLanguage({
79
34
  locale: detectLanguage()
80
35
  });
81
- function detectRouterFramework() {
82
- const args = process.argv.slice(2);
83
- if (args.includes('--tanstack')) return 'tanstack';
84
- const routerValue = getOptionValue(args, [
85
- '--router',
86
- '-r'
87
- ]);
88
- if (!routerValue || 'tanstack' === routerValue) return 'tanstack';
89
- if ('react-router' === routerValue) return 'react-router';
90
- console.error(i18n.t(localeKeys.error.invalidRouter, {
91
- router: routerValue
92
- }));
93
- process.exit(1);
94
- }
95
- function detectBffRuntime() {
96
- const args = process.argv.slice(2);
97
- const runtimeValue = getOptionValue(args, [
98
- '--bff-runtime'
99
- ]);
100
- if (!runtimeValue) return 'effect';
101
- if ('hono' === runtimeValue || 'effect' === runtimeValue) return runtimeValue;
102
- console.error(i18n.t(localeKeys.error.invalidBffRuntime, {
103
- runtime: runtimeValue
104
- }));
105
- process.exit(1);
106
- }
107
- function renderTemplate(template, data) {
108
- const tagRegex = /\{\{(~?)(#if|#unless|\/if|\/unless)(?:\s+(\w+))?(~?)\}\}/g;
109
- function renderConditionals(startIndex, expectedClose) {
110
- let rendered = '';
111
- let cursor = startIndex;
112
- tagRegex.lastIndex = startIndex;
113
- while(true){
114
- const match = tagRegex.exec(template);
115
- if (!match) return {
116
- rendered: rendered + template.slice(cursor),
117
- nextIndex: template.length
118
- };
119
- const [raw, , tag, condition, rightTrim] = match;
120
- const tagIndex = match.index;
121
- rendered += template.slice(cursor, tagIndex);
122
- cursor = tagIndex + raw.length;
123
- if ('#if' === tag || '#unless' === tag) {
124
- const kind = '#if' === tag ? 'if' : 'unless';
125
- const innerResult = renderConditionals(cursor, kind);
126
- cursor = innerResult.nextIndex;
127
- tagRegex.lastIndex = cursor;
128
- const conditionValue = Boolean(data[condition ?? '']);
129
- const shouldInclude = 'if' === kind ? conditionValue : !conditionValue;
130
- if (shouldInclude) rendered += innerResult.rendered;
131
- continue;
132
- }
133
- if ('/if' === tag || '/unless' === tag) {
134
- const kind = '/if' === tag ? 'if' : 'unless';
135
- if (expectedClose === kind) {
136
- let nextIndex = cursor;
137
- if ('~' === rightTrim) {
138
- const trailingWhitespace = /^\s*/u.exec(template.slice(nextIndex));
139
- nextIndex += trailingWhitespace?.[0].length ?? 0;
140
- }
141
- return {
142
- rendered,
143
- nextIndex
144
- };
145
- }
146
- rendered += raw;
147
- }
148
- }
149
- }
150
- let result = renderConditionals(0).rendered;
151
- const varRegex = /\{\{(\w+)\}\}/g;
152
- result = result.replace(varRegex, (match, key)=>{
153
- const value = data[key];
154
- return null != value ? String(value) : match;
155
- });
156
- return result;
157
- }
158
- function normalizePathForManifest(filePath) {
159
- return filePath.split(node_path.sep).join('/');
160
- }
161
- function isUnsafeRelativePath(filePath) {
162
- return 0 === filePath.length || node_path.isAbsolute(filePath) || filePath.startsWith('/') || /^[A-Za-z]:[\\/]/.test(filePath) || filePath.split(/[\\/]+/).includes('..');
163
- }
164
- function hashFile(filePath) {
165
- return node_crypto.createHash('sha256').update(node_fs.readFileSync(filePath)).digest('hex');
166
- }
167
- function getTemplateFiles(dir) {
168
- const files = [];
169
- function collect(currentDir) {
170
- const entries = node_fs.readdirSync(currentDir, {
171
- withFileTypes: true
172
- }).sort((a, b)=>a.name.localeCompare(b.name));
173
- for (const entry of entries){
174
- const entryPath = node_path.join(currentDir, entry.name);
175
- if (entry.isDirectory()) collect(entryPath);
176
- else if (entry.isFile()) files.push(normalizePathForManifest(node_path.relative(dir, entryPath)));
177
- }
178
- }
179
- collect(dir);
180
- return files;
181
- }
182
- function hashTemplateTree(dir) {
183
- const hash = node_crypto.createHash('sha256');
184
- for (const relativePath of getTemplateFiles(dir)){
185
- const fileHash = hashFile(node_path.join(dir, relativePath));
186
- hash.update(relativePath);
187
- hash.update('\0');
188
- hash.update(fileHash);
189
- hash.update('\0');
190
- }
191
- return hash.digest('hex');
192
- }
193
- function createBuiltinTemplateManifest(version) {
194
- return {
195
- schemaVersion: 1,
196
- template: {
197
- id: 'modernjs-ultramodern-app',
198
- version,
199
- displayName: 'Modern.js Ultramodern App',
200
- description: 'Repository-owned Modern.js application scaffold with UltraModern preset defaults.',
201
- compatibilityLane: 'ultramodern-mv',
202
- minimumModernVersion: version
203
- },
204
- source: {
205
- type: 'builtin',
206
- name: 'modernjs-ultramodern-app',
207
- repositoryPath: 'packages/toolkit/create/template'
208
- },
209
- integrity: {
210
- checksums: [
211
- {
212
- algorithm: 'sha256',
213
- value: hashTemplateTree(templateDir),
214
- scope: 'source-tree'
215
- }
216
- ],
217
- provenance: {
218
- kind: 'repo-local',
219
- issuer: '@modern-js/create',
220
- subject: 'packages/toolkit/create/template'
221
- }
222
- },
223
- materialization: {
224
- targetRoot: 'generated-project-root',
225
- allowedPaths: [
226
- '.agents/**',
227
- '.browserslistrc',
228
- '.codex/**',
229
- '.github/**',
230
- '.gitignore',
231
- '.mise.toml',
232
- '.modernjs/**',
233
- '.nvmrc',
234
- 'AGENTS.md',
235
- 'README.md',
236
- 'api/**',
237
- 'config/**',
238
- 'lefthook.yml',
239
- 'modern.config.ts',
240
- 'oxfmt.config.ts',
241
- 'oxlint.config.ts',
242
- 'package.json',
243
- 'pnpm-workspace.yaml',
244
- 'postcss.config.mjs',
245
- 'rstest.config.mts',
246
- "scripts/**",
247
- 'shared/**',
248
- 'src/**',
249
- 'tailwind.config.ts',
250
- 'tests/**',
251
- 'tsconfig.json'
252
- ],
253
- deniedPaths: requiredDeniedPaths,
254
- overwritePolicy: 'deny-existing'
255
- },
256
- lifecyclePolicy: {
257
- denyByDefault: true,
258
- deniedScripts: requiredLifecycleDeniedScripts,
259
- allowedScripts: requiredLifecycleAllowedScripts,
260
- requiresExplicitOptIn: true
261
- },
262
- validation: {
263
- schemaValidation: true,
264
- sourceValidation: [
265
- 'source-type-supported',
266
- 'checksum-verified',
267
- 'provenance-present'
268
- ],
269
- materializationValidation: [
270
- 'path-boundary-allowlist',
271
- 'path-boundary-denylist',
272
- 'no-path-traversal',
273
- 'no-absolute-paths',
274
- 'overwrite-policy-enforced'
275
- ],
276
- postMaterializationValidation: [
277
- 'ultramodern-contract-check',
278
- 'agent-skill-postinstall-allowed',
279
- 'github-workflow-security-enforced',
280
- 'package-source-retained',
281
- 'pnpm-11-policy-enforced',
282
- 'rstest-smoke-tests',
283
- 'template-manifest-retained'
284
- ],
285
- expectedCommands: [
286
- 'mise install',
287
- 'pnpm install',
288
- 'pnpm test',
289
- 'pnpm run ultramodern:check'
290
- ]
291
- }
292
- };
293
- }
294
- function assertTemplateManifest(condition, message) {
295
- if (!condition) throw new Error(`Template manifest validation failed: ${message}`);
296
- }
297
- function assertSafeManifestPath(filePath, label) {
298
- assertTemplateManifest(!isUnsafeRelativePath(filePath), `${label} is unsafe`);
299
- }
300
- function validateTemplateSource(source) {
301
- const sourceType = source.type;
302
- assertTemplateManifest('builtin' === sourceType || 'npm' === sourceType || 'git' === sourceType || 'local' === sourceType, `unsupported source type "${source.type}"`);
303
- if ('builtin' === source.type) {
304
- assertTemplateManifest(templateIdPattern.test(source.name), 'builtin source name must be a template id');
305
- if (source.repositoryPath) assertSafeManifestPath(source.repositoryPath, 'builtin repositoryPath');
306
- }
307
- if ('npm' === source.type) {
308
- assertTemplateManifest(packageNamePattern.test(source.packageName), 'npm packageName must be exact package metadata');
309
- assertTemplateManifest(semverPattern.test(source.version), 'npm source version must be an exact semver');
310
- assertTemplateManifest(sha256Pattern.test(source.tarballSha256), 'npm source tarballSha256 must be sha256 hex');
311
- }
312
- if ('git' === source.type) {
313
- assertTemplateManifest(sha1Pattern.test(source.checkoutSha), 'git checkoutSha must pin a commit');
314
- if ('sha' === source.ref.kind) assertTemplateManifest(sha1Pattern.test(source.ref.sha), 'git sha ref must be pinned to a commit');
315
- else {
316
- assertTemplateManifest(semverTagPattern.test(source.ref.tag), 'git tag ref must be a semver tag');
317
- assertTemplateManifest(sha1Pattern.test(source.ref.tagSha), 'git tag ref must include the resolved tag sha');
318
- }
319
- if (source.subdirectory) assertSafeManifestPath(source.subdirectory, 'git subdirectory');
320
- }
321
- if ('local' === source.type) {
322
- assertSafeManifestPath(source.path, 'local source path');
323
- assertTemplateManifest(true !== source.allowOutsideWorkspace, 'local source cannot allow outside workspace materialization');
324
- }
325
- }
326
- function validateTemplateManifest(manifest) {
327
- assertTemplateManifest(1 === manifest.schemaVersion, 'schemaVersion must be 1');
328
- assertTemplateManifest(templateIdPattern.test(manifest.template.id), 'template.id must be a template id');
329
- assertTemplateManifest(semverPattern.test(manifest.template.version), 'template.version must be exact semver');
330
- assertTemplateManifest('ultramodern-mv' === manifest.template.compatibilityLane || 'ultramodern-shell' === manifest.template.compatibilityLane || 'ultramodern-remote' === manifest.template.compatibilityLane, 'template.compatibilityLane is unsupported');
331
- if (manifest.template.minimumModernVersion) assertTemplateManifest(semverPattern.test(manifest.template.minimumModernVersion), 'template.minimumModernVersion must be exact semver');
332
- validateTemplateSource(manifest.source);
333
- assertTemplateManifest(manifest.integrity.checksums.length > 0, 'integrity.checksums must not be empty');
334
- for (const checksum of manifest.integrity.checksums){
335
- assertTemplateManifest('sha256' === checksum.algorithm, 'checksum algorithm must be sha256');
336
- assertTemplateManifest(sha256Pattern.test(checksum.value), 'checksum value must be sha256 hex');
337
- assertTemplateManifest('manifest' === checksum.scope || 'source-archive' === checksum.scope || 'source-tree' === checksum.scope || 'lockfile' === checksum.scope, 'checksum scope is unsupported');
338
- }
339
- assertTemplateManifest(manifest.integrity.provenance.kind && manifest.integrity.provenance.issuer && manifest.integrity.provenance.subject, 'provenance kind, issuer, and subject are required');
340
- if (manifest.integrity.lockfile) {
341
- assertSafeManifestPath(manifest.integrity.lockfile.path, 'lockfile path');
342
- assertTemplateManifest(sha256Pattern.test(manifest.integrity.lockfile.sha256), 'lockfile sha256 must be sha256 hex');
343
- }
344
- assertTemplateManifest('generated-project-root' === manifest.materialization.targetRoot || 'workspace-package-root' === manifest.materialization.targetRoot, 'materialization.targetRoot is unsupported');
345
- assertTemplateManifest(manifest.materialization.allowedPaths.length > 0, 'materialization.allowedPaths must not be empty');
346
- for (const allowedPath of manifest.materialization.allowedPaths)assertSafeManifestPath(allowedPath.replace(/\/\*\*$/, '/placeholder'), 'allowed path');
347
- for (const deniedPath of manifest.materialization.deniedPaths)assertSafeManifestPath(deniedPath.replace(/\/\*\*$/, '/placeholder'), 'denied path');
348
- for (const deniedPath of requiredDeniedPaths)assertTemplateManifest(manifest.materialization.deniedPaths.includes(deniedPath), `materialization.deniedPaths must include ${deniedPath}`);
349
- assertTemplateManifest(!manifest.materialization.overwritePolicy || 'deny-existing' === manifest.materialization.overwritePolicy || 'allow-generated-only' === manifest.materialization.overwritePolicy, 'materialization.overwritePolicy is unsupported');
350
- assertTemplateManifest(true === manifest.lifecyclePolicy.denyByDefault, 'lifecyclePolicy.denyByDefault must be true');
351
- for (const scriptName of requiredLifecycleDeniedScripts)assertTemplateManifest(manifest.lifecyclePolicy.deniedScripts.includes(scriptName), `lifecyclePolicy.deniedScripts must include ${scriptName}`);
352
- assertTemplateManifest(JSON.stringify(manifest.lifecyclePolicy.allowedScripts) === JSON.stringify(requiredLifecycleAllowedScripts), 'lifecyclePolicy.allowedScripts must only allow generated postinstall');
353
- assertTemplateManifest(true === manifest.validation.schemaValidation, 'validation.schemaValidation must be true');
354
- for (const token of [
355
- 'source-type-supported',
356
- 'checksum-verified',
357
- 'provenance-present'
358
- ])assertTemplateManifest(manifest.validation.sourceValidation.includes(token), `validation.sourceValidation must include ${token}`);
359
- for (const token of [
360
- 'path-boundary-allowlist',
361
- 'path-boundary-denylist',
362
- 'no-path-traversal',
363
- 'no-absolute-paths',
364
- 'overwrite-policy-enforced'
365
- ])assertTemplateManifest(manifest.validation.materializationValidation.includes(token), `validation.materializationValidation must include ${token}`);
366
- assertTemplateManifest(manifest.validation.postMaterializationValidation.includes('template-manifest-retained'), 'validation.postMaterializationValidation must retain manifest evidence');
367
- }
368
- function matchesManifestPattern(pattern, relativePath) {
369
- if (pattern.endsWith('/**')) {
370
- const prefix = pattern.slice(0, -3);
371
- return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
372
- }
373
- if (pattern.endsWith('.*')) {
374
- const prefix = pattern.slice(0, -1);
375
- return relativePath.startsWith(prefix);
376
- }
377
- return relativePath === pattern;
378
- }
379
- function canMaterializePath(manifest, relativePath) {
380
- if (isUnsafeRelativePath(relativePath)) throw new Error(`Unsafe template path rejected: ${relativePath}`);
381
- if (manifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) return false;
382
- if (!manifest.materialization.allowedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) throw new Error(`Template path is not allowed by manifest: ${relativePath}`);
383
- return true;
384
- }
385
- function writeTemplateManifestEvidence(targetDir, manifest) {
386
- const evidencePath = node_path.join(targetDir, '.modernjs', 'mv-template-manifest.json');
387
- const evidenceRelativePath = normalizePathForManifest(node_path.relative(targetDir, evidencePath));
388
- if (!canMaterializePath(manifest, evidenceRelativePath)) throw new Error('Template manifest evidence path is denied by manifest');
389
- node_fs.mkdirSync(node_path.dirname(evidencePath), {
390
- recursive: true
391
- });
392
- node_fs.writeFileSync(evidencePath, `${JSON.stringify(manifest, null, 2)}\n`);
393
- }
394
36
  function readCreatePackageJson() {
395
37
  const createPackageJson = node_path.join(createPackageRoot, 'package.json');
396
38
  return JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
@@ -421,31 +63,19 @@ function showHelp() {
421
63
  console.log(i18n.t(localeKeys.help.optionHelp));
422
64
  console.log(i18n.t(localeKeys.help.optionVersion));
423
65
  console.log(i18n.t(localeKeys.help.optionLang));
424
- console.log(i18n.t(localeKeys.help.optionRouter));
425
- if (localeKeys.help.optionBff) console.log(i18n.t(localeKeys.help.optionBff));
426
- if (localeKeys.help.optionBffRuntime) console.log(i18n.t(localeKeys.help.optionBffRuntime));
427
66
  if (localeKeys.help.optionTailwind) console.log(i18n.t(localeKeys.help.optionTailwind));
428
- if (localeKeys.help.optionWorkspace) console.log(i18n.t(localeKeys.help.optionWorkspace));
429
- if (localeKeys.help.optionUltramodernWorkspace) console.log(i18n.t(localeKeys.help.optionUltramodernWorkspace));
430
67
  if (localeKeys.help.optionUltramodernPackageSource) console.log(i18n.t(localeKeys.help.optionUltramodernPackageSource));
431
68
  if (localeKeys.help.optionUltramodernPackageScope) console.log(i18n.t(localeKeys.help.optionUltramodernPackageScope));
432
69
  if (localeKeys.help.optionUltramodernPackageNamePrefix) console.log(i18n.t(localeKeys.help.optionUltramodernPackageNamePrefix));
433
70
  if (localeKeys.help.optionVertical) console.log(i18n.t(localeKeys.help.optionVertical));
434
71
  if (localeKeys.help.optionLegacyModernJs) console.log(i18n.t(localeKeys.help.optionLegacyModernJs));
435
- console.log(i18n.t(localeKeys.help.optionSub));
436
72
  console.log('');
437
73
  console.log(i18n.t(localeKeys.help.examples));
438
74
  console.log(i18n.t(localeKeys.help.example1));
439
75
  console.log(i18n.t(localeKeys.help.example2));
440
- console.log(i18n.t(localeKeys.help.example3));
441
76
  if (localeKeys.help.example4) console.log(i18n.t(localeKeys.help.example4));
442
77
  if (localeKeys.help.example5) console.log(i18n.t(localeKeys.help.example5));
443
78
  if (localeKeys.help.example6) console.log(i18n.t(localeKeys.help.example6));
444
- if (localeKeys.help.example7) console.log(i18n.t(localeKeys.help.example7));
445
- if (localeKeys.help.example8) console.log(i18n.t(localeKeys.help.example8));
446
- if (localeKeys.help.example9) console.log(i18n.t(localeKeys.help.example9));
447
- if (localeKeys.help.example10) console.log(i18n.t(localeKeys.help.example10));
448
- if (localeKeys.help.example11) console.log(i18n.t(localeKeys.help.example11));
449
79
  if (localeKeys.help.example12) console.log(i18n.t(localeKeys.help.example12));
450
80
  console.log('');
451
81
  console.log(i18n.t(localeKeys.help.moreInfo));
@@ -503,12 +133,6 @@ function delegateLegacyModernJsSetup(args) {
503
133
  });
504
134
  throw new Error('Legacy Modern.js setup requires pnpm or npx to run @modern-js/create.');
505
135
  }
506
- function detectSubprojectFlag() {
507
- const args = process.argv.slice(2);
508
- if (args.includes('--sub') || args.includes('-s')) return true;
509
- if (args.includes('--no-sub')) return false;
510
- return null;
511
- }
512
136
  function detectTailwindFlag() {
513
137
  const args = process.argv.slice(2);
514
138
  return !args.includes('--no-tailwind');
@@ -518,10 +142,6 @@ function detectExplicitTailwindFlag() {
518
142
  if (args.includes('--no-tailwind')) return false;
519
143
  if (args.includes('--tailwind')) return true;
520
144
  }
521
- function detectWorkspaceProtocolFlag() {
522
- const args = process.argv.slice(2);
523
- return args.includes('--workspace');
524
- }
525
145
  function detectVerticalFlag() {
526
146
  const args = process.argv.slice(2);
527
147
  if (args.some((arg)=>arg.startsWith('--vertical='))) {
@@ -530,10 +150,6 @@ function detectVerticalFlag() {
530
150
  }
531
151
  return args.includes('--vertical');
532
152
  }
533
- function detectUltramodernWorkspaceFlag() {
534
- const args = process.argv.slice(2);
535
- return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
536
- }
537
153
  function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
538
154
  const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
539
155
  const strategy = getOptionValue(args, [
@@ -614,14 +230,6 @@ function resolveInstallBackedPackageSource(args, createPackage, packageSource) {
614
230
  ]) ?? packageSource.aliasPackageNamePrefix ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
615
231
  };
616
232
  }
617
- function resolveSingleAppPackageSource(args, createPackage, packageSource, useWorkspaceProtocol) {
618
- if (useWorkspaceProtocol) return {
619
- ...packageSource,
620
- strategy: 'workspace',
621
- modernPackageVersion: WORKSPACE_PACKAGE_VERSION
622
- };
623
- return resolveInstallBackedPackageSource(args, createPackage, packageSource);
624
- }
625
233
  function resolveWorkspacePackageSource(args, createPackage, packageSource) {
626
234
  if (hasExplicitUltramodernPackageSource(args, 'workspace')) return {
627
235
  ...packageSource,
@@ -630,23 +238,6 @@ function resolveWorkspacePackageSource(args, createPackage, packageSource) {
630
238
  };
631
239
  return resolveInstallBackedPackageSource(args, createPackage, packageSource);
632
240
  }
633
- function createSingleAppPackageSourceEvidence(packageSource) {
634
- return {
635
- schemaVersion: 1,
636
- preset: 'presetUltramodern',
637
- strategy: packageSource.strategy,
638
- modernPackages: createModernPackagesMetadata(ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, packageSource, {
639
- includeAliases: 'install' === packageSource.strategy
640
- })
641
- };
642
- }
643
- function writeSingleAppPackageSourceEvidence(targetDir, packageSource) {
644
- const evidencePath = node_path.join(targetDir, '.modernjs', 'ultramodern-package-source.json');
645
- node_fs.mkdirSync(node_path.dirname(evidencePath), {
646
- recursive: true
647
- });
648
- node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource), null, 2)}\n`);
649
- }
650
241
  function runSetupCommand(command, args, options = {}) {
651
242
  return execFileSync(command, args, {
652
243
  cwd: options.cwd,
@@ -746,9 +337,6 @@ async function getProjectName() {
746
337
  const optionWithValue = new Set([
747
338
  '--lang',
748
339
  '-l',
749
- '--router',
750
- '-r',
751
- '--bff-runtime',
752
340
  '--ultramodern-package-source',
753
341
  '--ultramodern-package-version',
754
342
  '--ultramodern-package-registry',
@@ -760,17 +348,11 @@ async function getProjectName() {
760
348
  '-h',
761
349
  '--version',
762
350
  '-v',
763
- '--sub',
764
- '-s',
765
- '--no-sub',
766
- '--tanstack',
767
- '--bff',
768
351
  '--tailwind',
769
352
  '--no-tailwind',
770
353
  '--workspace',
771
354
  '--vertical',
772
- LEGACY_MODERN_JS_FLAG,
773
- ULTRAMODERN_WORKSPACE_FLAG
355
+ LEGACY_MODERN_JS_FLAG
774
356
  ]);
775
357
  const positionalArgs = [];
776
358
  for(let i = 0; i < args.length; i++){
@@ -780,7 +362,7 @@ async function getProjectName() {
780
362
  i += 1;
781
363
  continue;
782
364
  }
783
- 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);
365
+ if (!(arg.startsWith('--lang=') || 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);
784
366
  }
785
367
  }
786
368
  if (positionalArgs.length > 1) {
@@ -842,7 +424,7 @@ async function main() {
842
424
  const dim = '\x1b[2m\x1b[3m';
843
425
  const reset = '\x1b[0m';
844
426
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
845
- console.log(`${dim} pnpm ultramodern:check${reset}\n`);
427
+ console.log(`${dim} pnpm check${reset}\n`);
846
428
  return;
847
429
  }
848
430
  if (node_fs.existsSync(targetDir)) {
@@ -854,111 +436,15 @@ async function main() {
854
436
  process.exit(1);
855
437
  }
856
438
  }
857
- const generateWorkspace = detectUltramodernWorkspaceFlag();
858
- if (generateWorkspace) {
859
- const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
860
- generateUltramodernWorkspace({
861
- targetDir,
862
- packageName: generatedPackageName,
863
- modernVersion: version,
864
- enableTailwind: detectTailwindFlag(),
865
- packageSource
866
- });
867
- initializeGeneratedGitRepository(targetDir);
868
- const dim = '\x1b[2m\x1b[3m';
869
- const reset = '\x1b[0m';
870
- console.log(`${i18n.t(localeKeys.message.success)}\n`);
871
- console.log(i18n.t(localeKeys.message.nextSteps));
872
- if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
873
- projectName
874
- })}${reset}`);
875
- console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
876
- console.log(`${dim} pnpm ultramodern:check${reset}`);
877
- console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
878
- return;
879
- }
880
- const subprojectFlag = detectSubprojectFlag();
881
- const isSubproject = true === subprojectFlag;
882
- const routerFramework = detectRouterFramework();
883
- const bffRuntime = detectBffRuntime();
884
- const enableTailwind = detectTailwindFlag();
885
- const useWorkspaceProtocol = detectWorkspaceProtocolFlag();
886
- const packageSource = resolveSingleAppPackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage), useWorkspaceProtocol);
887
- const templateManifest = createBuiltinTemplateManifest('install' === packageSource.strategy ? packageSource.modernPackageVersion : version);
888
- validateTemplateManifest(templateManifest);
889
- copyTemplate(templateDir, targetDir, {
439
+ const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
440
+ generateUltramodernWorkspace({
441
+ targetDir,
890
442
  packageName: generatedPackageName,
891
- version: 'workspace' === packageSource.strategy ? WORKSPACE_PACKAGE_VERSION : packageSource.modernPackageVersion,
892
- runtimeVersion: modernPackageSpecifier('@modern-js/runtime', packageSource),
893
- appToolsVersion: modernPackageSpecifier('@modern-js/app-tools', packageSource),
894
- adapterRstestVersion: modernPackageSpecifier('@modern-js/adapter-rstest', packageSource),
895
- codeToolsVersion: modernPackageSpecifier('@modern-js/code-tools', packageSource),
896
- createVersion: modernPackageSpecifier('@modern-js/create', packageSource),
897
- tsconfigVersion: modernPackageSpecifier('@modern-js/tsconfig', packageSource),
898
- pluginTanstackVersion: modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
899
- pluginBffVersion: modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
900
- pluginI18nVersion: modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
901
- tanstackRouterVersion: TANSTACK_ROUTER_VERSION,
902
- i18nextVersion: I18NEXT_VERSION,
903
- reactVersion: REACT_VERSION,
904
- reactDomVersion: REACT_DOM_VERSION,
905
- reactI18nextVersion: REACT_I18NEXT_VERSION,
906
- effectTsgoVersion: EFFECT_TSGO_VERSION,
907
- typescriptNativePreviewVersion: TYPESCRIPT_NATIVE_PREVIEW_VERSION,
908
- rstestCoreVersion: RSTEST_CORE_VERSION,
909
- happyDomVersion: HAPPY_DOM_VERSION,
910
- oxfmtVersion: OXFMT_VERSION,
911
- oxlintVersion: OXLINT_VERSION,
912
- postcssVersion: POSTCSS_VERSION,
913
- ultraciteVersion: ULTRACITE_VERSION,
914
- typesReactVersion: TYPES_REACT_VERSION,
915
- typesReactDomVersion: TYPES_REACT_DOM_VERSION,
916
- tailwindVersion: TAILWIND_VERSION,
917
- tailwindPostcssVersion: TAILWIND_POSTCSS_VERSION,
918
- pnpmVersion: PNPM_VERSION,
919
- isSubproject,
920
- routerFramework,
921
- bffRuntime,
922
- enableTailwind,
923
- templateManifest
443
+ modernVersion: version,
444
+ enableTailwind: detectTailwindFlag(),
445
+ packageSource
924
446
  });
925
- const targetPackageJson = node_path.join(targetDir, 'package.json');
926
- const packageJson = JSON.parse(node_fs.readFileSync(targetPackageJson, 'utf-8'));
927
- packageJson.name = generatedPackageName;
928
- packageJson.modernjs = {
929
- ...packageJson.modernjs ?? {},
930
- preset: 'presetUltramodern',
931
- packageSource: {
932
- strategy: packageSource.strategy,
933
- config: './.modernjs/ultramodern-package-source.json'
934
- }
935
- };
936
- if (isSubproject) {
937
- delete packageJson['lint-staged'];
938
- delete packageJson['simple-git-hooks'];
939
- if (packageJson.scripts) {
940
- delete packageJson.scripts.prepare;
941
- delete packageJson.scripts['skills:install'];
942
- delete packageJson.scripts['skills:check'];
943
- delete packageJson.scripts.postinstall;
944
- }
945
- if (packageJson.devDependencies) {
946
- delete packageJson.devDependencies['lint-staged'];
947
- delete packageJson.devDependencies.lefthook;
948
- delete packageJson.devDependencies['simple-git-hooks'];
949
- }
950
- node_fs.rmSync(node_path.join(targetDir, '.codex'), {
951
- recursive: true,
952
- force: true
953
- });
954
- node_fs.rmSync(node_path.join(targetDir, 'lefthook.yml'), {
955
- force: true
956
- });
957
- }
958
- node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
959
- writeTemplateManifestEvidence(targetDir, templateManifest);
960
- writeSingleAppPackageSourceEvidence(targetDir, packageSource);
961
- if (!isSubproject) initializeGeneratedGitRepository(targetDir);
447
+ initializeGeneratedGitRepository(targetDir);
962
448
  const dim = '\x1b[2m\x1b[3m';
963
449
  const reset = '\x1b[0m';
964
450
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
@@ -967,88 +453,9 @@ async function main() {
967
453
  projectName
968
454
  })}${reset}`);
969
455
  console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
456
+ console.log(`${dim} pnpm check${reset}`);
970
457
  console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
971
458
  }
972
- function copyTemplate(src, dest, options) {
973
- node_fs.mkdirSync(dest, {
974
- recursive: true
975
- });
976
- const excludeInSubproject = [
977
- '.agents',
978
- '.github',
979
- '.gitignore.handlebars',
980
- 'AGENTS.md',
981
- '.npmrc',
982
- '.nvmrc'
983
- ];
984
- function copyRecursive(srcDir, destDir) {
985
- const entries = node_fs.readdirSync(srcDir, {
986
- withFileTypes: true
987
- });
988
- for (const entry of entries){
989
- if (options.isSubproject && excludeInSubproject.includes(entry.name)) continue;
990
- const srcPath = node_path.join(srcDir, entry.name);
991
- let destPath = node_path.join(destDir, entry.name);
992
- const sourceRelativePath = normalizePathForManifest(node_path.relative(src, srcPath));
993
- const finalRelativePath = normalizePathForManifest(sourceRelativePath.replace(/\.handlebars$/, ''));
994
- if (!(!canMaterializePath(options.templateManifest, finalRelativePath) || entry.isDirectory() && options.templateManifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, finalRelativePath)))) if (entry.isDirectory()) {
995
- node_fs.mkdirSync(destPath, {
996
- recursive: true
997
- });
998
- copyRecursive(srcPath, destPath);
999
- } else if (entry.name.endsWith('.handlebars')) {
1000
- const templateContent = node_fs.readFileSync(srcPath, 'utf-8');
1001
- const rendered = renderTemplate(templateContent, {
1002
- packageName: options.packageName,
1003
- version: options.version,
1004
- runtimeVersion: options.runtimeVersion,
1005
- appToolsVersion: options.appToolsVersion,
1006
- adapterRstestVersion: options.adapterRstestVersion,
1007
- codeToolsVersion: options.codeToolsVersion,
1008
- createVersion: options.createVersion,
1009
- tsconfigVersion: options.tsconfigVersion,
1010
- pluginTanstackVersion: options.pluginTanstackVersion,
1011
- pluginBffVersion: options.pluginBffVersion,
1012
- pluginI18nVersion: options.pluginI18nVersion,
1013
- tanstackRouterVersion: options.tanstackRouterVersion,
1014
- i18nextVersion: options.i18nextVersion,
1015
- reactVersion: options.reactVersion,
1016
- reactDomVersion: options.reactDomVersion,
1017
- reactI18nextVersion: options.reactI18nextVersion,
1018
- effectTsgoVersion: options.effectTsgoVersion,
1019
- typescriptNativePreviewVersion: options.typescriptNativePreviewVersion,
1020
- rstestCoreVersion: options.rstestCoreVersion,
1021
- happyDomVersion: options.happyDomVersion,
1022
- oxfmtVersion: options.oxfmtVersion,
1023
- oxlintVersion: options.oxlintVersion,
1024
- postcssVersion: options.postcssVersion,
1025
- ultraciteVersion: options.ultraciteVersion,
1026
- typesReactVersion: options.typesReactVersion,
1027
- typesReactDomVersion: options.typesReactDomVersion,
1028
- tailwindVersion: options.tailwindVersion,
1029
- tailwindPostcssVersion: options.tailwindPostcssVersion,
1030
- pnpmVersion: options.pnpmVersion,
1031
- isSubproject: options.isSubproject,
1032
- isTanstackRouter: 'tanstack' === options.routerFramework,
1033
- enableBff: 'none' !== options.bffRuntime,
1034
- useEffectBff: 'effect' === options.bffRuntime,
1035
- useHonoBff: 'hono' === options.bffRuntime,
1036
- bffRuntime: options.bffRuntime,
1037
- enableTailwind: options.enableTailwind,
1038
- routerRuntimeImport: 'tanstack' === options.routerFramework ? '@modern-js/plugin-tanstack/runtime' : '@modern-js/runtime/router'
1039
- });
1040
- if (0 === rendered.trim().length) continue;
1041
- destPath = destPath.replace(/\.handlebars$/, '');
1042
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
1043
- node_fs.writeFileSync(destPath, rendered, 'utf-8');
1044
- } else {
1045
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
1046
- node_fs.copyFileSync(srcPath, destPath);
1047
- }
1048
- }
1049
- }
1050
- copyRecursive(src, dest);
1051
- }
1052
459
  main().catch((error)=>{
1053
460
  console.error(i18n.t(localeKeys.error.createFailed), error);
1054
461
  process.exit(1);