@bleedingdev/modern-js-create 3.2.0-ultramodern.117 → 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 +11 -602
  4. package/dist/cjs/locale/en.cjs +12 -19
  5. package/dist/cjs/locale/zh.cjs +12 -19
  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 +13 -603
  9. package/dist/esm/locale/en.js +12 -19
  10. package/dist/esm/locale/zh.js +12 -19
  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 +13 -603
  14. package/dist/esm-node/locale/en.js +12 -19
  15. package/dist/esm-node/locale/zh.js +12 -19
  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
package/dist/esm/index.js CHANGED
@@ -1,57 +1,15 @@
1
1
  import { execFileSync } from "node:child_process";
2
- import node_crypto from "node:crypto";
3
2
  import node_fs from "node:fs";
4
3
  import node_path from "node:path";
5
4
  import node_readline from "node:readline";
6
5
  import { fileURLToPath } from "node:url";
7
6
  import { resolveCreatePackageRoot } from "./create-package-root.js";
8
7
  import { i18n, localeKeys } from "./locale/index.js";
9
- 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";
10
- import { ULTRAMODERN_WORKSPACE_FLAG, addUltramodernVertical, generateUltramodernWorkspace } from "./ultramodern-workspace.js";
8
+ import { BLEEDINGDEV_CREATE_PACKAGE, BLEEDINGDEV_FRAMEWORK_VERSION_ENV, BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, WORKSPACE_PACKAGE_VERSION } from "./ultramodern-package-source.js";
9
+ import { addUltramodernVertical, generateUltramodernWorkspace } from "./ultramodern-workspace.js";
11
10
  const src_dirname = node_path.dirname(fileURLToPath(import.meta.url));
12
11
  const createPackageRoot = resolveCreatePackageRoot(src_dirname);
13
- const templateDir = node_path.join(createPackageRoot, 'template');
14
12
  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.2';
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
13
  const LEGACY_MODERN_JS_FLAG = '--legacy-modern-js';
56
14
  const LEGACY_MODERN_JS_CONFIRMATION = 'USE LEGACY MODERN.JS';
57
15
  function getOptionValue(args, names) {
@@ -74,319 +32,6 @@ const detectLanguage = ()=>{
74
32
  i18n.changeLanguage({
75
33
  locale: detectLanguage()
76
34
  });
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
35
  function readCreatePackageJson() {
391
36
  const createPackageJson = node_path.join(createPackageRoot, 'package.json');
392
37
  return JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
@@ -417,31 +62,19 @@ function showHelp() {
417
62
  console.log(i18n.t(localeKeys.help.optionHelp));
418
63
  console.log(i18n.t(localeKeys.help.optionVersion));
419
64
  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
65
  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
66
  if (localeKeys.help.optionUltramodernPackageSource) console.log(i18n.t(localeKeys.help.optionUltramodernPackageSource));
427
67
  if (localeKeys.help.optionUltramodernPackageScope) console.log(i18n.t(localeKeys.help.optionUltramodernPackageScope));
428
68
  if (localeKeys.help.optionUltramodernPackageNamePrefix) console.log(i18n.t(localeKeys.help.optionUltramodernPackageNamePrefix));
429
69
  if (localeKeys.help.optionVertical) console.log(i18n.t(localeKeys.help.optionVertical));
430
70
  if (localeKeys.help.optionLegacyModernJs) console.log(i18n.t(localeKeys.help.optionLegacyModernJs));
431
- console.log(i18n.t(localeKeys.help.optionSub));
432
71
  console.log('');
433
72
  console.log(i18n.t(localeKeys.help.examples));
434
73
  console.log(i18n.t(localeKeys.help.example1));
435
74
  console.log(i18n.t(localeKeys.help.example2));
436
- console.log(i18n.t(localeKeys.help.example3));
437
75
  if (localeKeys.help.example4) console.log(i18n.t(localeKeys.help.example4));
438
76
  if (localeKeys.help.example5) console.log(i18n.t(localeKeys.help.example5));
439
77
  if (localeKeys.help.example6) console.log(i18n.t(localeKeys.help.example6));
440
- if (localeKeys.help.example7) console.log(i18n.t(localeKeys.help.example7));
441
- if (localeKeys.help.example8) console.log(i18n.t(localeKeys.help.example8));
442
- if (localeKeys.help.example9) console.log(i18n.t(localeKeys.help.example9));
443
- if (localeKeys.help.example10) console.log(i18n.t(localeKeys.help.example10));
444
- if (localeKeys.help.example11) console.log(i18n.t(localeKeys.help.example11));
445
78
  if (localeKeys.help.example12) console.log(i18n.t(localeKeys.help.example12));
446
79
  console.log('');
447
80
  console.log(i18n.t(localeKeys.help.moreInfo));
@@ -499,12 +132,6 @@ function delegateLegacyModernJsSetup(args) {
499
132
  });
500
133
  throw new Error('Legacy Modern.js setup requires pnpm or npx to run @modern-js/create.');
501
134
  }
502
- function detectSubprojectFlag() {
503
- const args = process.argv.slice(2);
504
- if (args.includes('--sub') || args.includes('-s')) return true;
505
- if (args.includes('--no-sub')) return false;
506
- return null;
507
- }
508
135
  function detectTailwindFlag() {
509
136
  const args = process.argv.slice(2);
510
137
  return !args.includes('--no-tailwind');
@@ -514,10 +141,6 @@ function detectExplicitTailwindFlag() {
514
141
  if (args.includes('--no-tailwind')) return false;
515
142
  if (args.includes('--tailwind')) return true;
516
143
  }
517
- function detectWorkspaceProtocolFlag() {
518
- const args = process.argv.slice(2);
519
- return args.includes('--workspace');
520
- }
521
144
  function detectVerticalFlag() {
522
145
  const args = process.argv.slice(2);
523
146
  if (args.some((arg)=>arg.startsWith('--vertical='))) {
@@ -526,10 +149,6 @@ function detectVerticalFlag() {
526
149
  }
527
150
  return args.includes('--vertical');
528
151
  }
529
- function detectUltramodernWorkspaceFlag() {
530
- const args = process.argv.slice(2);
531
- return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
532
- }
533
152
  function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
534
153
  const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
535
154
  const strategy = getOptionValue(args, [
@@ -610,14 +229,6 @@ function resolveInstallBackedPackageSource(args, createPackage, packageSource) {
610
229
  ]) ?? packageSource.aliasPackageNamePrefix ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
611
230
  };
612
231
  }
613
- function resolveSingleAppPackageSource(args, createPackage, packageSource, useWorkspaceProtocol) {
614
- if (useWorkspaceProtocol) return {
615
- ...packageSource,
616
- strategy: 'workspace',
617
- modernPackageVersion: WORKSPACE_PACKAGE_VERSION
618
- };
619
- return resolveInstallBackedPackageSource(args, createPackage, packageSource);
620
- }
621
232
  function resolveWorkspacePackageSource(args, createPackage, packageSource) {
622
233
  if (hasExplicitUltramodernPackageSource(args, 'workspace')) return {
623
234
  ...packageSource,
@@ -626,23 +237,6 @@ function resolveWorkspacePackageSource(args, createPackage, packageSource) {
626
237
  };
627
238
  return resolveInstallBackedPackageSource(args, createPackage, packageSource);
628
239
  }
629
- function createSingleAppPackageSourceEvidence(packageSource) {
630
- return {
631
- schemaVersion: 1,
632
- preset: 'presetUltramodern',
633
- strategy: packageSource.strategy,
634
- modernPackages: createModernPackagesMetadata(ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, packageSource, {
635
- includeAliases: 'install' === packageSource.strategy
636
- })
637
- };
638
- }
639
- function writeSingleAppPackageSourceEvidence(targetDir, packageSource) {
640
- const evidencePath = node_path.join(targetDir, '.modernjs', 'ultramodern-package-source.json');
641
- node_fs.mkdirSync(node_path.dirname(evidencePath), {
642
- recursive: true
643
- });
644
- node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource), null, 2)}\n`);
645
- }
646
240
  function runSetupCommand(command, args, options = {}) {
647
241
  return execFileSync(command, args, {
648
242
  cwd: options.cwd,
@@ -742,9 +336,6 @@ async function getProjectName() {
742
336
  const optionWithValue = new Set([
743
337
  '--lang',
744
338
  '-l',
745
- '--router',
746
- '-r',
747
- '--bff-runtime',
748
339
  '--ultramodern-package-source',
749
340
  '--ultramodern-package-version',
750
341
  '--ultramodern-package-registry',
@@ -756,17 +347,11 @@ async function getProjectName() {
756
347
  '-h',
757
348
  '--version',
758
349
  '-v',
759
- '--sub',
760
- '-s',
761
- '--no-sub',
762
- '--tanstack',
763
- '--bff',
764
350
  '--tailwind',
765
351
  '--no-tailwind',
766
352
  '--workspace',
767
353
  '--vertical',
768
- LEGACY_MODERN_JS_FLAG,
769
- ULTRAMODERN_WORKSPACE_FLAG
354
+ LEGACY_MODERN_JS_FLAG
770
355
  ]);
771
356
  const positionalArgs = [];
772
357
  for(let i = 0; i < args.length; i++){
@@ -776,7 +361,7 @@ async function getProjectName() {
776
361
  i += 1;
777
362
  continue;
778
363
  }
779
- 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);
364
+ 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);
780
365
  }
781
366
  }
782
367
  if (positionalArgs.length > 1) {
@@ -838,7 +423,7 @@ async function main() {
838
423
  const dim = '\x1b[2m\x1b[3m';
839
424
  const reset = '\x1b[0m';
840
425
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
841
- console.log(`${dim} pnpm ultramodern:check${reset}\n`);
426
+ console.log(`${dim} pnpm check${reset}\n`);
842
427
  return;
843
428
  }
844
429
  if (node_fs.existsSync(targetDir)) {
@@ -850,111 +435,15 @@ async function main() {
850
435
  process.exit(1);
851
436
  }
852
437
  }
853
- const generateWorkspace = detectUltramodernWorkspaceFlag();
854
- if (generateWorkspace) {
855
- const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
856
- generateUltramodernWorkspace({
857
- targetDir,
858
- packageName: generatedPackageName,
859
- modernVersion: version,
860
- enableTailwind: detectTailwindFlag(),
861
- packageSource
862
- });
863
- initializeGeneratedGitRepository(targetDir);
864
- const dim = '\x1b[2m\x1b[3m';
865
- const reset = '\x1b[0m';
866
- console.log(`${i18n.t(localeKeys.message.success)}\n`);
867
- console.log(i18n.t(localeKeys.message.nextSteps));
868
- if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
869
- projectName
870
- })}${reset}`);
871
- console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
872
- console.log(`${dim} pnpm ultramodern:check${reset}`);
873
- console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
874
- return;
875
- }
876
- const subprojectFlag = detectSubprojectFlag();
877
- const isSubproject = true === subprojectFlag;
878
- const routerFramework = detectRouterFramework();
879
- const bffRuntime = detectBffRuntime();
880
- const enableTailwind = detectTailwindFlag();
881
- const useWorkspaceProtocol = detectWorkspaceProtocolFlag();
882
- const packageSource = resolveSingleAppPackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage), useWorkspaceProtocol);
883
- const templateManifest = createBuiltinTemplateManifest('install' === packageSource.strategy ? packageSource.modernPackageVersion : version);
884
- validateTemplateManifest(templateManifest);
885
- copyTemplate(templateDir, targetDir, {
438
+ const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
439
+ generateUltramodernWorkspace({
440
+ targetDir,
886
441
  packageName: generatedPackageName,
887
- version: 'workspace' === packageSource.strategy ? WORKSPACE_PACKAGE_VERSION : packageSource.modernPackageVersion,
888
- runtimeVersion: modernPackageSpecifier('@modern-js/runtime', packageSource),
889
- appToolsVersion: modernPackageSpecifier('@modern-js/app-tools', packageSource),
890
- adapterRstestVersion: modernPackageSpecifier('@modern-js/adapter-rstest', packageSource),
891
- codeToolsVersion: modernPackageSpecifier('@modern-js/code-tools', packageSource),
892
- createVersion: modernPackageSpecifier('@modern-js/create', packageSource),
893
- tsconfigVersion: modernPackageSpecifier('@modern-js/tsconfig', packageSource),
894
- pluginTanstackVersion: modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
895
- pluginBffVersion: modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
896
- pluginI18nVersion: modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
897
- tanstackRouterVersion: TANSTACK_ROUTER_VERSION,
898
- i18nextVersion: I18NEXT_VERSION,
899
- reactVersion: REACT_VERSION,
900
- reactDomVersion: REACT_DOM_VERSION,
901
- reactI18nextVersion: REACT_I18NEXT_VERSION,
902
- effectTsgoVersion: EFFECT_TSGO_VERSION,
903
- typescriptNativePreviewVersion: TYPESCRIPT_NATIVE_PREVIEW_VERSION,
904
- rstestCoreVersion: RSTEST_CORE_VERSION,
905
- happyDomVersion: HAPPY_DOM_VERSION,
906
- oxfmtVersion: OXFMT_VERSION,
907
- oxlintVersion: OXLINT_VERSION,
908
- postcssVersion: POSTCSS_VERSION,
909
- ultraciteVersion: ULTRACITE_VERSION,
910
- typesReactVersion: TYPES_REACT_VERSION,
911
- typesReactDomVersion: TYPES_REACT_DOM_VERSION,
912
- tailwindVersion: TAILWIND_VERSION,
913
- tailwindPostcssVersion: TAILWIND_POSTCSS_VERSION,
914
- pnpmVersion: PNPM_VERSION,
915
- isSubproject,
916
- routerFramework,
917
- bffRuntime,
918
- enableTailwind,
919
- templateManifest
442
+ modernVersion: version,
443
+ enableTailwind: detectTailwindFlag(),
444
+ packageSource
920
445
  });
921
- const targetPackageJson = node_path.join(targetDir, 'package.json');
922
- const packageJson = JSON.parse(node_fs.readFileSync(targetPackageJson, 'utf-8'));
923
- packageJson.name = generatedPackageName;
924
- packageJson.modernjs = {
925
- ...packageJson.modernjs ?? {},
926
- preset: 'presetUltramodern',
927
- packageSource: {
928
- strategy: packageSource.strategy,
929
- config: './.modernjs/ultramodern-package-source.json'
930
- }
931
- };
932
- if (isSubproject) {
933
- delete packageJson['lint-staged'];
934
- delete packageJson['simple-git-hooks'];
935
- if (packageJson.scripts) {
936
- delete packageJson.scripts.prepare;
937
- delete packageJson.scripts['skills:install'];
938
- delete packageJson.scripts['skills:check'];
939
- delete packageJson.scripts.postinstall;
940
- }
941
- if (packageJson.devDependencies) {
942
- delete packageJson.devDependencies['lint-staged'];
943
- delete packageJson.devDependencies.lefthook;
944
- delete packageJson.devDependencies['simple-git-hooks'];
945
- }
946
- node_fs.rmSync(node_path.join(targetDir, '.codex'), {
947
- recursive: true,
948
- force: true
949
- });
950
- node_fs.rmSync(node_path.join(targetDir, 'lefthook.yml'), {
951
- force: true
952
- });
953
- }
954
- node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
955
- writeTemplateManifestEvidence(targetDir, templateManifest);
956
- writeSingleAppPackageSourceEvidence(targetDir, packageSource);
957
- if (!isSubproject) initializeGeneratedGitRepository(targetDir);
446
+ initializeGeneratedGitRepository(targetDir);
958
447
  const dim = '\x1b[2m\x1b[3m';
959
448
  const reset = '\x1b[0m';
960
449
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
@@ -963,88 +452,9 @@ async function main() {
963
452
  projectName
964
453
  })}${reset}`);
965
454
  console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
455
+ console.log(`${dim} pnpm check${reset}`);
966
456
  console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
967
457
  }
968
- function copyTemplate(src, dest, options) {
969
- node_fs.mkdirSync(dest, {
970
- recursive: true
971
- });
972
- const excludeInSubproject = [
973
- '.agents',
974
- '.github',
975
- '.gitignore.handlebars',
976
- 'AGENTS.md',
977
- '.npmrc',
978
- '.nvmrc'
979
- ];
980
- function copyRecursive(srcDir, destDir) {
981
- const entries = node_fs.readdirSync(srcDir, {
982
- withFileTypes: true
983
- });
984
- for (const entry of entries){
985
- if (options.isSubproject && excludeInSubproject.includes(entry.name)) continue;
986
- const srcPath = node_path.join(srcDir, entry.name);
987
- let destPath = node_path.join(destDir, entry.name);
988
- const sourceRelativePath = normalizePathForManifest(node_path.relative(src, srcPath));
989
- const finalRelativePath = normalizePathForManifest(sourceRelativePath.replace(/\.handlebars$/, ''));
990
- if (!(!canMaterializePath(options.templateManifest, finalRelativePath) || entry.isDirectory() && options.templateManifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, finalRelativePath)))) if (entry.isDirectory()) {
991
- node_fs.mkdirSync(destPath, {
992
- recursive: true
993
- });
994
- copyRecursive(srcPath, destPath);
995
- } else if (entry.name.endsWith('.handlebars')) {
996
- const templateContent = node_fs.readFileSync(srcPath, 'utf-8');
997
- const rendered = renderTemplate(templateContent, {
998
- packageName: options.packageName,
999
- version: options.version,
1000
- runtimeVersion: options.runtimeVersion,
1001
- appToolsVersion: options.appToolsVersion,
1002
- adapterRstestVersion: options.adapterRstestVersion,
1003
- codeToolsVersion: options.codeToolsVersion,
1004
- createVersion: options.createVersion,
1005
- tsconfigVersion: options.tsconfigVersion,
1006
- pluginTanstackVersion: options.pluginTanstackVersion,
1007
- pluginBffVersion: options.pluginBffVersion,
1008
- pluginI18nVersion: options.pluginI18nVersion,
1009
- tanstackRouterVersion: options.tanstackRouterVersion,
1010
- i18nextVersion: options.i18nextVersion,
1011
- reactVersion: options.reactVersion,
1012
- reactDomVersion: options.reactDomVersion,
1013
- reactI18nextVersion: options.reactI18nextVersion,
1014
- effectTsgoVersion: options.effectTsgoVersion,
1015
- typescriptNativePreviewVersion: options.typescriptNativePreviewVersion,
1016
- rstestCoreVersion: options.rstestCoreVersion,
1017
- happyDomVersion: options.happyDomVersion,
1018
- oxfmtVersion: options.oxfmtVersion,
1019
- oxlintVersion: options.oxlintVersion,
1020
- postcssVersion: options.postcssVersion,
1021
- ultraciteVersion: options.ultraciteVersion,
1022
- typesReactVersion: options.typesReactVersion,
1023
- typesReactDomVersion: options.typesReactDomVersion,
1024
- tailwindVersion: options.tailwindVersion,
1025
- tailwindPostcssVersion: options.tailwindPostcssVersion,
1026
- pnpmVersion: options.pnpmVersion,
1027
- isSubproject: options.isSubproject,
1028
- isTanstackRouter: 'tanstack' === options.routerFramework,
1029
- enableBff: 'none' !== options.bffRuntime,
1030
- useEffectBff: 'effect' === options.bffRuntime,
1031
- useHonoBff: 'hono' === options.bffRuntime,
1032
- bffRuntime: options.bffRuntime,
1033
- enableTailwind: options.enableTailwind,
1034
- routerRuntimeImport: 'tanstack' === options.routerFramework ? '@modern-js/plugin-tanstack/runtime' : '@modern-js/runtime/router'
1035
- });
1036
- if (0 === rendered.trim().length) continue;
1037
- destPath = destPath.replace(/\.handlebars$/, '');
1038
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
1039
- node_fs.writeFileSync(destPath, rendered, 'utf-8');
1040
- } else {
1041
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
1042
- node_fs.copyFileSync(srcPath, destPath);
1043
- }
1044
- }
1045
- }
1046
- copyRecursive(src, dest);
1047
- }
1048
458
  main().catch((error)=>{
1049
459
  console.error(i18n.t(localeKeys.error.createFailed), error);
1050
460
  process.exit(1);