@bleedingdev/modern-js-create 3.2.0-ultramodern.83 → 3.2.0-ultramodern.85

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.
package/dist/index.js CHANGED
@@ -619,6 +619,19 @@ function sortJsonValue(value) {
619
619
  return value;
620
620
  }
621
621
  const ULTRAMODERN_WORKSPACE_FLAG = '--ultramodern-workspace';
622
+ const FIRST_VERTICAL_PORT = 4101;
623
+ const TAILWIND_PREFIX_DIGIT_WORDS = [
624
+ 'zero',
625
+ 'one',
626
+ 'two',
627
+ 'three',
628
+ 'four',
629
+ 'five',
630
+ 'six',
631
+ 'seven',
632
+ 'eight',
633
+ 'nine'
634
+ ];
622
635
  function isRecord(value) {
623
636
  return null !== value && 'object' == typeof value && !Array.isArray(value);
624
637
  }
@@ -1115,6 +1128,7 @@ function createTsConfigBase() {
1115
1128
  verbatimModuleSyntax: true,
1116
1129
  strict: true,
1117
1130
  noEmit: true,
1131
+ allowImportingTsExtensions: true,
1118
1132
  allowJs: true,
1119
1133
  esModuleInterop: true,
1120
1134
  noUncheckedIndexedAccess: true,
@@ -1228,6 +1242,7 @@ function createAppModernConfig(scope, app) {
1228
1242
  const bffImport = appHasEffectApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
1229
1243
  const bffConfig = appHasEffectApi(app) ? ` bff: {
1230
1244
  effect: {
1245
+ entry: './api/effect/index',
1231
1246
  openapi: {
1232
1247
  path: '/openapi.json',
1233
1248
  },
@@ -1916,9 +1931,9 @@ function createCssTokenImport(scope) {
1916
1931
  return `@import '${ultramodern_workspace_packageName(scope, 'shared-design-tokens')}/tokens.css';\n`;
1917
1932
  }
1918
1933
  function createTailwindPrefix(raw) {
1919
- const prefix = raw.toLowerCase().replace(/[^a-z]/gu, '');
1920
- if (!prefix) throw new Error(`Cannot derive a Tailwind prefix from ${raw}`);
1921
- return prefix;
1934
+ const normalized = raw.toLowerCase().replace(/[^a-z0-9]/gu, '');
1935
+ if (!normalized) throw new Error(`Cannot derive a Tailwind prefix from ${raw}`);
1936
+ return normalized.replace(/[0-9]/gu, (digit)=>TAILWIND_PREFIX_DIGIT_WORDS[Number(digit)]);
1922
1937
  }
1923
1938
  function tailwindPrefixForApp(app) {
1924
1939
  if ('shell' === app.kind) return 'shell';
@@ -3450,7 +3465,7 @@ function createEffectServiceEntry(scope, service, contractImportPath = ultramode
3450
3465
  HttpApiBuilder,
3451
3466
  Layer,
3452
3467
  } from '@modern-js/plugin-bff/effect-edge';
3453
- import { ultramodernApiMarker } from '../../src/ultramodern-build';
3468
+ import { ultramodernApiMarker } from '../../src/ultramodern-build.ts';
3454
3469
  import {
3455
3470
  ${apiExport},
3456
3471
  ${groupName}OperationContexts,
@@ -4693,6 +4708,7 @@ function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4693
4708
  mfName: remote.mfName,
4694
4709
  apiPrefix: remote.effectApi.prefix,
4695
4710
  tailwindPrefix: tailwindPrefixForApp(remote),
4711
+ zephyrAlias: remoteDependencyAlias(remote),
4696
4712
  packageName: ultramodern_workspace_packageName(scope, remote.packageSuffix),
4697
4713
  exposes: Object.keys(remote.exposes ?? {}),
4698
4714
  componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
@@ -4881,7 +4897,7 @@ assert(generatedContract.cssFederation?.sharedDesignTokens?.ssr?.firstPaintRequi
4881
4897
  const shellPackage = readJson('apps/shell-super-app/package.json');
4882
4898
  const expectedZephyrDependencies = Object.fromEntries(
4883
4899
  fullStackVerticals.map(vertical => [
4884
- vertical.domain,
4900
+ vertical.zephyrAlias,
4885
4901
  \`\${vertical.packageName}@workspace:*\`,
4886
4902
  ]),
4887
4903
  );
@@ -4925,7 +4941,7 @@ for (const vertical of fullStackVerticals) {
4925
4941
  fullStackVerticals
4926
4942
  .filter(candidate => vertical.verticalRefs.includes(candidate.id))
4927
4943
  .map(candidate => [
4928
- candidate.domain,
4944
+ candidate.zephyrAlias,
4929
4945
  \`\${candidate.packageName}@workspace:*\`,
4930
4946
  ]),
4931
4947
  );
@@ -4968,7 +4984,7 @@ for (const vertical of fullStackVerticals) {
4968
4984
  assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, \`\${vertical.id} CSS federation owner is missing\`);
4969
4985
  assert(contractEntry?.styling?.federation?.role === 'vertical-css', \`\${vertical.id} must own only vertical CSS\`);
4970
4986
  assert(contractEntry?.styling?.federation?.rootSelector === \`[data-app-id="\${vertical.id}"]\`, \`\${vertical.id} CSS root selector is incorrect\`);
4971
- assert(contractEntry?.styling?.federation?.classPrefix === \`\${vertical.domain}:\`, \`\${vertical.id} CSS class prefix is incorrect\`);
4987
+ assert(contractEntry?.styling?.federation?.classPrefix === \`\${vertical.tailwindPrefix}:\`, \`\${vertical.id} CSS class prefix is incorrect\`);
4972
4988
  assert(contractEntry?.styling?.federation?.layers?.owned?.includes(\`ultramodern-vertical-\${vertical.domain}\`), \`\${vertical.id} vertical CSS layer is missing\`);
4973
4989
  assert(!contractEntry?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-base'), \`\${vertical.id} must not own shell base CSS\`);
4974
4990
  assert(contractEntry?.styling?.federation?.entrypoints?.federationEntry === 'src/federation-entry.tsx', \`\${vertical.id} CSS contract must include federation entry\`);
@@ -5372,7 +5388,7 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
5372
5388
  }
5373
5389
  if (appHasEffectApi(resolvedApp)) {
5374
5390
  writeFile(targetDir, `${resolvedApp.directory}/shared/effect/api.ts`, createEffectSharedApi(resolvedApp));
5375
- writeFile(targetDir, `${resolvedApp.directory}/api/effect/index.ts`, createEffectServiceEntry(scope, resolvedApp, '../../shared/effect/api'));
5391
+ writeFile(targetDir, `${resolvedApp.directory}/api/effect/index.ts`, createEffectServiceEntry(scope, resolvedApp, '../../shared/effect/api.ts'));
5376
5392
  writeFile(targetDir, `${resolvedApp.directory}/src/effect/${resolvedApp.effectApi.stem}-client.ts`, createEffectClient(resolvedApp, '../../shared/effect/api'));
5377
5393
  }
5378
5394
  if ('vertical' === resolvedApp.kind) {
@@ -5462,7 +5478,7 @@ function assertValidVerticalName(name) {
5462
5478
  }
5463
5479
  function nextAvailablePort(ports) {
5464
5480
  const numericPorts = Object.values(ports).filter((value)=>'number' == typeof value && Number.isFinite(value));
5465
- return Math.max(3030, ...numericPorts) + 1;
5481
+ return Math.max(FIRST_VERTICAL_PORT - 1, ...numericPorts) + 1;
5466
5482
  }
5467
5483
  function assertCanCreate(workspaceRoot, relativePath) {
5468
5484
  if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.83",
24
+ "version": "3.2.0-ultramodern.85",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "@types/node": "^25.9.1",
42
42
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
43
43
  "tsx": "^4.22.3",
44
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.83"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.85"
45
45
  },
46
46
  "publishConfig": {
47
47
  "registry": "https://registry.npmjs.org/",
@@ -54,6 +54,6 @@
54
54
  "start": "node ./dist/index.js"
55
55
  },
56
56
  "ultramodern": {
57
- "frameworkVersion": "3.2.0-ultramodern.83"
57
+ "frameworkVersion": "3.2.0-ultramodern.85"
58
58
  }
59
59
  }
@@ -10,7 +10,7 @@ This project is generated for Codex-first UltraModern.js work.
10
10
  - `pnpm i18n:check` rejects hardcoded user-visible JSX text.
11
11
  - `pnpm ultramodern:check` verifies the generated contract.
12
12
  - Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm ultramodern:check`.
13
- - `postinstall` installs `lefthook` when the app is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format && pnpm lint:fix && pnpm ultramodern:check` on pre-commit; pre-push runs `pnpm ultramodern:check`.
13
+ - `postinstall` formats the generated tree, installs private orchestration skills when available, initializes a Git repository when needed, and installs `lefthook`. Generated `lefthook.yml` runs `pnpm format && pnpm lint:fix && pnpm ultramodern:check` on pre-commit; pre-push runs `pnpm ultramodern:check`.
14
14
 
15
15
  ## Internationalization
16
16
 
@@ -3,6 +3,13 @@ import ultracite from 'ultracite/oxfmt';
3
3
 
4
4
  export default defineConfig({
5
5
  extends: [ultracite],
6
- ignorePatterns: ['dist', 'node_modules', '.modern', '.modernjs', '**/routeTree.gen.ts'],
6
+ ignorePatterns: [
7
+ '.agents',
8
+ 'dist',
9
+ 'node_modules',
10
+ '.modern',
11
+ '.modernjs',
12
+ '**/routeTree.gen.ts',
13
+ ],
7
14
  singleQuote: true,
8
15
  });
@@ -8,5 +8,12 @@ export default defineConfig({
8
8
  node: true,
9
9
  },
10
10
  extends: [core, react],
11
- ignorePatterns: ['dist', 'node_modules', '.modern', '.modernjs', '**/routeTree.gen.ts'],
11
+ ignorePatterns: [
12
+ '.agents',
13
+ 'dist',
14
+ 'node_modules',
15
+ '.modern',
16
+ '.modernjs',
17
+ '**/routeTree.gen.ts',
18
+ ],
12
19
  });
@@ -15,7 +15,7 @@
15
15
  {{#unless isSubproject}}
16
16
  "skills:install": "node ./scripts/bootstrap-agent-skills.mjs",
17
17
  "skills:check": "node ./scripts/bootstrap-agent-skills.mjs --check",
18
- "postinstall": "node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)",
18
+ "postinstall": "oxfmt . && node ./scripts/bootstrap-agent-skills.mjs",
19
19
  {{/unless}}
20
20
  "ultramodern:check": "{{#unless isSubproject}}pnpm format:check && pnpm lint && {{/unless}}pnpm typecheck && pnpm i18n:check && pnpm test{{#unless isSubproject}} && pnpm skills:check{{/unless}} && node ./scripts/validate-ultramodern.mjs"{{#unless isSubproject}},
21
21
  "format": "oxfmt .",
@@ -41,7 +41,7 @@
41
41
  "@modern-js/app-tools": "{{appToolsVersion}}",
42
42
  {{#if enableBff}} "@modern-js/plugin-bff": "{{pluginBffVersion}}",
43
43
  {{/if}} "@modern-js/tsconfig": "{{tsconfigVersion}}",
44
- "@rstest/core": "0.10.2",
44
+ "@rstest/core": "0.10.3",
45
45
  {{#if enableTailwind}}
46
46
  "@tailwindcss/postcss": "^{{tailwindPostcssVersion}}",
47
47
  {{/if}}
@@ -19,3 +19,11 @@ allowBuilds:
19
19
  msgpackr-extract: true
20
20
  sharp: true
21
21
  workerd: true
22
+ onlyBuiltDependencies:
23
+ - '@swc/core'
24
+ - core-js
25
+ - esbuild
26
+ - lefthook
27
+ - msgpackr-extract
28
+ - sharp
29
+ - workerd
@@ -1,7 +1,5 @@
1
- import { withModernConfig } from '@modern-js/adapter-rstest';
2
1
  import { defineConfig } from '@rstest/core';
3
2
 
4
3
  export default defineConfig({
5
- extends: withModernConfig(),
6
- testEnvironment: 'happy-dom',
4
+ testEnvironment: 'node',
7
5
  });
@@ -17,6 +17,76 @@ const run = (command, args, options = {}) =>
17
17
  stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
18
18
  });
19
19
 
20
+ const commandExists = (command) => {
21
+ try {
22
+ run(command, ['--version'], { stdio: 'ignore' });
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ };
28
+
29
+ const runShell = (script) =>
30
+ run('sh', ['-lc', script], {
31
+ stdio: 'inherit',
32
+ });
33
+
34
+ const installGit = () => {
35
+ if (commandExists('git')) {
36
+ return;
37
+ }
38
+
39
+ if (commandExists('brew')) {
40
+ run('brew', ['install', 'git'], { stdio: 'inherit' });
41
+ } else if (process.platform === 'linux' && commandExists('apt-get')) {
42
+ const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
43
+ runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
44
+ } else if (process.platform === 'linux' && commandExists('dnf')) {
45
+ const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
46
+ runShell(`${sudo}dnf install -y git`);
47
+ } else if (process.platform === 'linux' && commandExists('yum')) {
48
+ const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
49
+ runShell(`${sudo}yum install -y git`);
50
+ } else if (process.platform === 'linux' && commandExists('apk')) {
51
+ runShell('apk add --no-cache git');
52
+ }
53
+
54
+ if (!commandExists('git')) {
55
+ throw new Error(
56
+ 'Git is required for UltraModern setup. Install git and run pnpm skills:install again.',
57
+ );
58
+ }
59
+ };
60
+
61
+ const isInsideGitWorkTree = () => {
62
+ try {
63
+ return run('git', ['rev-parse', '--is-inside-work-tree']).trim() === 'true';
64
+ } catch {
65
+ return false;
66
+ }
67
+ };
68
+
69
+ const initializeGitRepository = () => {
70
+ if (isInsideGitWorkTree()) {
71
+ return;
72
+ }
73
+
74
+ try {
75
+ run('git', ['init', '-b', 'main'], { stdio: 'inherit' });
76
+ } catch {
77
+ run('git', ['init'], { stdio: 'inherit' });
78
+ run('git', ['branch', '-M', 'main'], { stdio: 'inherit' });
79
+ }
80
+ };
81
+
82
+ const installLefthook = () => {
83
+ try {
84
+ run('lefthook', ['install'], { stdio: 'inherit' });
85
+ } catch (error) {
86
+ console.warn(`Unable to install lefthook hooks: ${error.message}`);
87
+ }
88
+ };
89
+
20
90
  const removeTree = (dir) =>
21
91
  fs.rmSync(dir, {
22
92
  force: true,
@@ -98,6 +168,7 @@ if (checkOnly) {
98
168
  }
99
169
 
100
170
  fs.mkdirSync(installDir, { recursive: true });
171
+ installGit();
101
172
 
102
173
  for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
103
174
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
@@ -133,3 +204,6 @@ for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
133
204
  removeTree(tempDir);
134
205
  }
135
206
  }
207
+
208
+ initializeGitRepository();
209
+ installLefthook();
@@ -30,6 +30,13 @@ const collectFiles = (directory) => {
30
30
  };
31
31
 
32
32
  const lineNumberForIndex = (content, index) => content.slice(0, index).split('\n').length;
33
+ const isCodeElementText = (content, index) => {
34
+ const tagStart = content.lastIndexOf('<', index);
35
+ if (tagStart === -1) {
36
+ return false;
37
+ }
38
+ return /^<code(?:\s|>)/u.test(content.slice(tagStart, index));
39
+ };
33
40
  const isIgnoredLine = (content, index) => {
34
41
  const lineStart = content.lastIndexOf('\n', index) + 1;
35
42
  const lineEnd = content.indexOf('\n', index);
@@ -58,7 +65,11 @@ for (const filePath of scanRoots.flatMap(collectFiles)) {
58
65
 
59
66
  for (const match of content.matchAll(jsxTextPattern)) {
60
67
  const text = match[1].replaceAll(/\s+/gu, ' ').trim();
61
- if (text && !isIgnoredLine(content, match.index ?? 0)) {
68
+ if (
69
+ text &&
70
+ !isIgnoredLine(content, match.index ?? 0) &&
71
+ !isCodeElementText(content, match.index ?? 0)
72
+ ) {
62
73
  violations.push({
63
74
  filePath,
64
75
  line: lineNumberForIndex(content, match.index ?? 0),
@@ -9,21 +9,19 @@ const packageSourcePath = path.resolve(
9
9
  '.modernjs/ultramodern-package-source.json',
10
10
  );
11
11
  const readPnpmConfig = (key) => {
12
- const env = { ...process.env };
13
- for (const envKey of Object.keys(env)) {
14
- if (/^(?:npm|pnpm)_config_/i.test(envKey)) {
15
- delete env[envKey];
16
- }
17
- }
12
+ const env = Object.fromEntries(
13
+ Object.entries(process.env).filter(
14
+ ([envKey]) => !/^(?:npm|pnpm)_config_/iu.test(envKey),
15
+ ),
16
+ );
18
17
  const output = execFileSync('pnpm', ['config', 'get', key, '--json'], {
19
18
  cwd: process.cwd(),
20
- env,
21
19
  encoding: 'utf-8',
20
+ env,
22
21
  stdio: ['ignore', 'pipe', 'pipe'],
23
22
  }).trim();
24
23
  return output ? JSON.parse(output) : undefined;
25
24
  };
26
- const isSubproject = {{isSubproject}};
27
25
  const enableTailwind = {{enableTailwind}};
28
26
  const expectedPnpmVersion = '{{pnpmVersion}}';
29
27
  const activePnpmVersion = execFileSync('pnpm', ['--version'], {
@@ -131,14 +129,13 @@ if (fs.existsSync(path.resolve(process.cwd(), 'src/routes/page.tsx'))) {
131
129
  process.exit(1);
132
130
  }
133
131
 
134
- if (!enableTailwind) {
135
- if (
136
- fs.existsSync(path.resolve(process.cwd(), 'postcss.config.mjs')) ||
137
- fs.existsSync(path.resolve(process.cwd(), 'tailwind.config.ts'))
138
- ) {
139
- console.error('Tailwind config files must not be written when Tailwind is disabled');
140
- process.exit(1);
141
- }
132
+ if (
133
+ !enableTailwind &&
134
+ (fs.existsSync(path.resolve(process.cwd(), 'postcss.config.mjs')) ||
135
+ fs.existsSync(path.resolve(process.cwd(), 'tailwind.config.ts')))
136
+ ) {
137
+ console.error('Tailwind config files must not be written when Tailwind is disabled');
138
+ process.exit(1);
142
139
  }
143
140
 
144
141
  {{#unless isSubproject}}
@@ -259,17 +256,19 @@ const skillsLock = JSON.parse(
259
256
  );
260
257
  {{/unless}}
261
258
  const requiredScripts = {
262
- 'i18n:check': 'node ./scripts/check-i18n-strings.mjs',
263
- test: 'rstest run',
264
259
  {{#unless isSubproject}}
265
260
  format: 'oxfmt .',
266
261
  'format:check': 'oxfmt --check .',
262
+ {{/unless}}
263
+ 'i18n:check': 'node ./scripts/check-i18n-strings.mjs',
264
+ {{#unless isSubproject}}
267
265
  lint: 'oxlint .',
268
266
  'lint:fix': 'oxlint . --fix',
267
+ postinstall: 'oxfmt . && node ./scripts/bootstrap-agent-skills.mjs',
269
268
  'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
270
269
  'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
271
- postinstall: 'node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)',
272
270
  {{/unless}}
271
+ test: 'rstest run',
273
272
  };
274
273
 
275
274
  for (const [scriptName, scriptCommand] of Object.entries(requiredScripts)) {
@@ -369,8 +368,11 @@ if (
369
368
  console.error('pnpm allowBuilds must approve only the generated app build dependencies');
370
369
  process.exit(1);
371
370
  }
372
- if (readPnpmConfig('onlyBuiltDependencies') !== undefined) {
373
- console.error('pnpm onlyBuiltDependencies must not be set');
371
+ if (
372
+ JSON.stringify(readPnpmConfig('onlyBuiltDependencies')) !==
373
+ JSON.stringify(['@swc/core', 'core-js', 'esbuild', 'lefthook', 'msgpackr-extract', 'sharp', 'workerd'])
374
+ ) {
375
+ console.error('pnpm onlyBuiltDependencies must approve only the generated app build dependencies');
374
376
  process.exit(1);
375
377
  }
376
378
 
@@ -121,16 +121,12 @@ const Index = () => {
121
121
  <p className="name">{t('home.name')}</p>
122
122
  </div>
123
123
  <p className="description{{#if enableTailwind}} text-emerald-700 font-semibold{{/if}}">
124
- {t('home.description.intro')} <code className="code">presetUltramodern(...)</code>{' '}
125
- {/* i18n-ignore technical token */}
124
+ {t('home.description.intro')}{' '}
125
+ <code className="code">presetUltramodern(...)</code>{' '}
126
126
  {t('home.description.afterPreset')}{' '}
127
- <code className="code">modern.config.ts</code>
128
- {/* i18n-ignore technical token */}
129
- {' '}
127
+ <code className="code">modern.config.ts</code>{' '}
130
128
  {t('home.description.afterConfig')}{' '}
131
- <code className="code">pnpm run ultramodern:check</code>
132
- {/* i18n-ignore technical token */}
133
- {' '}
129
+ <code className="code">pnpm run ultramodern:check</code>{' '}
134
130
  {t('home.description.end')}
135
131
  </p>
136
132
  {{#if useEffectBff}}
@@ -2,9 +2,9 @@
2
2
 
3
3
  export default {
4
4
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
5
+ plugins: [],
5
6
  theme: {
6
7
  extend: {},
7
8
  },
8
- plugins: [],
9
9
  } satisfies Config;
10
10
  {{/if}}