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

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
@@ -1,3 +1,4 @@
1
+ import { execFileSync } from "node:child_process";
1
2
  import node_crypto from "node:crypto";
2
3
  import node_fs from "node:fs";
3
4
  import node_path from "node:path";
@@ -1010,7 +1011,7 @@ function createRootPackageJson(scope, packageSource, remotes = []) {
1010
1011
  'ultramodern:assert-mf-types': "node ./scripts/assert-mf-types.mjs",
1011
1012
  'ultramodern:check': "node ./scripts/validate-ultramodern-workspace.mjs",
1012
1013
  'ultramodern:i18n-boundaries': "node ./scripts/check-ultramodern-i18n-boundaries.mjs",
1013
- postinstall: "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs && node ./scripts/setup-agent-reference-repos.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)",
1014
+ postinstall: "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs && node ./scripts/setup-agent-reference-repos.mjs",
1014
1015
  check: 'pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm ultramodern:i18n-boundaries && pnpm ultramodern:check'
1015
1016
  },
1016
1017
  engines: {
@@ -1178,6 +1179,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
1178
1179
  private: true,
1179
1180
  name: ultramodern_workspace_packageName(scope, app.packageSuffix),
1180
1181
  version: '0.1.0',
1182
+ type: 'module',
1181
1183
  scripts: {
1182
1184
  dev: 'modern dev',
1183
1185
  build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : 'ULTRAMODERN_ZEPHYR=false modern build',
@@ -4876,7 +4878,7 @@ assert(rootPackage.scripts?.['cloudflare:deploy'] === expectedCloudflareDeploySc
4876
4878
  assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json', 'Root must expose cloudflare:proof');
4877
4879
  assert(rootPackage.scripts?.['skills:install'] === 'node ./scripts/bootstrap-agent-skills.mjs', 'Root must expose skills:install');
4878
4880
  assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent-skills.mjs --check', 'Root must expose skills:check');
4879
- assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs && node ./scripts/setup-agent-reference-repos.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)", 'Root postinstall must format, bootstrap agent skills, install reference repositories, and enable hooks last');
4881
+ assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs && node ./scripts/setup-agent-reference-repos.mjs", 'Root postinstall must format, bootstrap agent skills, initialize git/hooks, and install reference repositories');
4880
4882
 
4881
4883
  const expectedAppIds = ['shell-super-app', ...fullStackVerticals.map(vertical => vertical.id)];
4882
4884
  const expectedCloudflareCompatibilityFlags = ['nodejs_compat', 'global_fetch_strictly_public'];
@@ -4906,6 +4908,7 @@ assert(
4906
4908
  JSON.stringify(expectedZephyrDependencies),
4907
4909
  'Shell Zephyr dependencies must reference every vertical package',
4908
4910
  );
4911
+ assert(shellPackage.type === 'module', 'Shell package must use ESM module mode');
4909
4912
  const shellContract = generatedContract.apps?.find(app => app.id === 'shell-super-app');
4910
4913
  assert(shellContract?.deploy?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell Cloudflare workerName is incorrect');
4911
4914
  assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'Shell Cloudflare public URL env is incorrect');
@@ -4932,6 +4935,7 @@ assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, no
4932
4935
  for (const vertical of fullStackVerticals) {
4933
4936
  const packageJson = readJson(\`\${vertical.path}/package.json\`);
4934
4937
  assert(packageJson.name === vertical.packageName, \`\${vertical.id} package name is incorrect\`);
4938
+ assert(packageJson.type === 'module', \`\${vertical.id} package must use ESM module mode\`);
4935
4939
  assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', \`\${vertical.id} must expose cloudflare:deploy\`);
4936
4940
  assert(packageJson.scripts?.['cloudflare:proof']?.includes(\`--app \${vertical.id}\`), \`\${vertical.id} must expose cloudflare:proof\`);
4937
4941
  assert(packageJson.dependencies?.['@modern-js/plugin-bff'], \`\${vertical.id} must depend on plugin-bff\`);
@@ -6236,6 +6240,91 @@ function writeSingleAppPackageSourceEvidence(targetDir, packageSource, useWorksp
6236
6240
  });
6237
6241
  node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource, useWorkspaceProtocol), null, 2)}\n`);
6238
6242
  }
6243
+ function runSetupCommand(command, args, options = {}) {
6244
+ return execFileSync(command, args, {
6245
+ cwd: options.cwd,
6246
+ encoding: 'utf-8',
6247
+ stdio: options.stdio ?? [
6248
+ 'ignore',
6249
+ 'pipe',
6250
+ 'pipe'
6251
+ ]
6252
+ });
6253
+ }
6254
+ function commandExists(command) {
6255
+ try {
6256
+ runSetupCommand(command, [
6257
+ '--version'
6258
+ ], {
6259
+ stdio: 'ignore'
6260
+ });
6261
+ return true;
6262
+ } catch {
6263
+ return false;
6264
+ }
6265
+ }
6266
+ function installGitForGeneratedProject() {
6267
+ if (commandExists('git')) return;
6268
+ const runShell = (script)=>runSetupCommand('sh', [
6269
+ '-lc',
6270
+ script
6271
+ ], {
6272
+ stdio: 'inherit'
6273
+ });
6274
+ const sudo = 'function' == typeof process.getuid && 0 === process.getuid() ? '' : 'sudo ';
6275
+ if (commandExists('brew')) runSetupCommand('brew', [
6276
+ 'install',
6277
+ 'git'
6278
+ ], {
6279
+ stdio: 'inherit'
6280
+ });
6281
+ else if ('linux' === process.platform && commandExists('apt-get')) runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
6282
+ else if ('linux' === process.platform && commandExists('dnf')) runShell(`${sudo}dnf install -y git`);
6283
+ else if ('linux' === process.platform && commandExists('yum')) runShell(`${sudo}yum install -y git`);
6284
+ else if ('linux' === process.platform && commandExists('apk')) runShell('apk add --no-cache git');
6285
+ if (!commandExists('git')) throw new Error('Git is required for UltraModern setup. Install git and rerun create, or run pnpm skills:install after installing git.');
6286
+ }
6287
+ function isInsideGitWorkTree(targetDir) {
6288
+ try {
6289
+ return 'true' === runSetupCommand('git', [
6290
+ 'rev-parse',
6291
+ '--is-inside-work-tree'
6292
+ ], {
6293
+ cwd: targetDir
6294
+ }).trim();
6295
+ } catch {
6296
+ return false;
6297
+ }
6298
+ }
6299
+ function initializeGeneratedGitRepository(targetDir) {
6300
+ installGitForGeneratedProject();
6301
+ if (isInsideGitWorkTree(targetDir)) return;
6302
+ try {
6303
+ runSetupCommand('git', [
6304
+ 'init',
6305
+ '-b',
6306
+ 'main'
6307
+ ], {
6308
+ cwd: targetDir,
6309
+ stdio: 'inherit'
6310
+ });
6311
+ } catch {
6312
+ runSetupCommand('git', [
6313
+ 'init'
6314
+ ], {
6315
+ cwd: targetDir,
6316
+ stdio: 'inherit'
6317
+ });
6318
+ runSetupCommand('git', [
6319
+ 'branch',
6320
+ '-M',
6321
+ 'main'
6322
+ ], {
6323
+ cwd: targetDir,
6324
+ stdio: 'inherit'
6325
+ });
6326
+ }
6327
+ }
6239
6328
  function isDirectoryEmpty(dirPath) {
6240
6329
  if (!node_fs.existsSync(dirPath)) return false;
6241
6330
  try {
@@ -6361,6 +6450,7 @@ async function main() {
6361
6450
  enableTailwind: detectTailwindFlag(),
6362
6451
  packageSource: detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage)
6363
6452
  });
6453
+ initializeGeneratedGitRepository(targetDir);
6364
6454
  const dim = '\x1b[2m\x1b[3m';
6365
6455
  const reset = '\x1b[0m';
6366
6456
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
@@ -6445,6 +6535,7 @@ async function main() {
6445
6535
  node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
6446
6536
  writeTemplateManifestEvidence(targetDir, templateManifest);
6447
6537
  writeSingleAppPackageSourceEvidence(targetDir, packageSource, useWorkspaceProtocol);
6538
+ if (!isSubproject) initializeGeneratedGitRepository(targetDir);
6448
6539
  const dim = '\x1b[2m\x1b[3m';
6449
6540
  const reset = '\x1b[0m';
6450
6541
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.85",
24
+ "version": "3.2.0-ultramodern.87",
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.85"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.87"
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.85"
57
+ "frameworkVersion": "3.2.0-ultramodern.87"
58
58
  }
59
59
  }
@@ -4,18 +4,29 @@
4
4
  Layer,
5
5
  defineEffectBff,
6
6
  } from '@modern-js/plugin-bff/effect-server';
7
+ import type { HttpApi, HttpApiGroup } from '@modern-js/plugin-bff/effect-server';
7
8
  import { bffEffectApi } from '../../shared/effect/api';
8
9
 
9
- const greetingsLayer = HttpApiBuilder.group(bffEffectApi, 'greetings', (handlers) =>
10
- handlers.handle('hello', () =>
11
- Effect.succeed({
12
- message: 'Hello from Effect HttpApi',
13
- runtime: 'effect' as const,
14
- }),
15
- ),
10
+ type ApiGroups<TApi> = TApi extends HttpApi.HttpApi<string, infer TGroups> ? TGroups : never;
11
+ type GreetingsHandlers = HttpApiBuilder.Handlers.FromGroup<
12
+ HttpApiGroup.WithName<ApiGroups<typeof bffEffectApi>, 'greetings'>
13
+ >;
14
+
15
+ const greetingsLayer = HttpApiBuilder.group(
16
+ bffEffectApi,
17
+ 'greetings',
18
+ (handlers: GreetingsHandlers) =>
19
+ handlers.handle('hello', () =>
20
+ Effect.succeed({
21
+ message: 'Hello from Effect HttpApi',
22
+ runtime: 'effect' as const,
23
+ }),
24
+ ),
16
25
  );
17
26
 
18
- const layer = HttpApiBuilder.layer(bffEffectApi).pipe(Layer.provide(greetingsLayer));
27
+ const layer = HttpApiBuilder.layer(bffEffectApi).pipe(
28
+ Layer.provide(greetingsLayer),
29
+ );
19
30
 
20
31
  export default defineEffectBff({
21
32
  api: bffEffectApi,
@@ -96,15 +96,34 @@ const removeTree = (dir) =>
96
96
  });
97
97
 
98
98
  const cloneSource = (source, targetDir) => {
99
+ if (source.commit) {
100
+ run('git', ['init', targetDir]);
101
+ run('git', ['remote', 'add', 'origin', source.repository], {
102
+ cwd: targetDir,
103
+ });
104
+ run('git', ['fetch', '--depth', '1', '--quiet', 'origin', source.commit], {
105
+ cwd: targetDir,
106
+ });
107
+ run(
108
+ 'git',
109
+ [
110
+ '-c',
111
+ 'advice.detachedHead=false',
112
+ 'checkout',
113
+ '--detach',
114
+ '--quiet',
115
+ 'FETCH_HEAD',
116
+ ],
117
+ { cwd: targetDir },
118
+ );
119
+ return;
120
+ }
121
+
99
122
  const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
100
123
  try {
101
- run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
102
- stdio: 'inherit',
103
- });
124
+ run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1', '--quiet']);
104
125
  } catch {
105
- run('git', ['clone', '--depth', '1', source.repository, targetDir], {
106
- stdio: 'inherit',
107
- });
126
+ run('git', ['clone', '--depth', '1', '--quiet', source.repository, targetDir]);
108
127
  }
109
128
  };
110
129
 
@@ -7,6 +7,9 @@ import { useEffect, useState } from 'react';
7
7
  {{/if}}
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
+ {{#if useEffectBff}}type GreetingResponse = Awaited<ReturnType<typeof effectBff.client.greetings.hello>>;
11
+
12
+ {{/if}}
10
13
  const fallbackLanguage = 'en';
11
14
  const supportedLanguages = ['en', 'cs'] as const;
12
15
  type SupportedLanguage = (typeof supportedLanguages)[number];
@@ -62,7 +65,7 @@ const Index = () => {
62
65
  let mounted = true;
63
66
  Effect.runFork(
64
67
  Effect.promise(() => effectBff.client.greetings.hello({})).pipe(
65
- Effect.tap((data) =>
68
+ Effect.tap((data: GreetingResponse) =>
66
69
  Effect.sync(() => {
67
70
  if (mounted) {
68
71
  setEffectMessage(data.message);
@@ -11,7 +11,7 @@ instructions, not optional reading.
11
11
  - `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
12
12
  - `pnpm check` runs formatting, linting, effect-tsgo, private-skill availability checks, and the generated workspace contract.
13
13
  - Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm check`.
14
- - `postinstall` formats the generated tree, installs agent skills and reference repos, then installs `lefthook` when the workspace is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format && pnpm lint:fix && pnpm check` on pre-commit; pre-push runs `pnpm check`.
14
+ - `postinstall` formats the generated tree, initializes Git when needed, installs agent skills and reference repos, then installs `lefthook`. Generated `lefthook.yml` runs `pnpm format && pnpm lint:fix && pnpm check` on pre-commit; pre-push runs `pnpm check`.
15
15
 
16
16
  ## Localized Routes
17
17
 
@@ -17,6 +17,85 @@ 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 =
43
+ typeof process.getuid === 'function' && process.getuid() === 0
44
+ ? ''
45
+ : 'sudo ';
46
+ runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
47
+ } else if (process.platform === 'linux' && commandExists('dnf')) {
48
+ const sudo =
49
+ typeof process.getuid === 'function' && process.getuid() === 0
50
+ ? ''
51
+ : 'sudo ';
52
+ runShell(`${sudo}dnf install -y git`);
53
+ } else if (process.platform === 'linux' && commandExists('yum')) {
54
+ const sudo =
55
+ typeof process.getuid === 'function' && process.getuid() === 0
56
+ ? ''
57
+ : 'sudo ';
58
+ runShell(`${sudo}yum install -y git`);
59
+ } else if (process.platform === 'linux' && commandExists('apk')) {
60
+ runShell('apk add --no-cache git');
61
+ }
62
+
63
+ if (!commandExists('git')) {
64
+ throw new Error(
65
+ 'Git is required for UltraModern setup. Install git and run pnpm skills:install again.',
66
+ );
67
+ }
68
+ };
69
+
70
+ const isInsideGitWorkTree = () => {
71
+ try {
72
+ return run('git', ['rev-parse', '--is-inside-work-tree']).trim() === 'true';
73
+ } catch {
74
+ return false;
75
+ }
76
+ };
77
+
78
+ const initializeGitRepository = () => {
79
+ if (isInsideGitWorkTree()) {
80
+ return;
81
+ }
82
+
83
+ try {
84
+ run('git', ['init', '-b', 'main'], { stdio: 'inherit' });
85
+ } catch {
86
+ run('git', ['init'], { stdio: 'inherit' });
87
+ run('git', ['branch', '-M', 'main'], { stdio: 'inherit' });
88
+ }
89
+ };
90
+
91
+ const installLefthook = () => {
92
+ try {
93
+ run('lefthook', ['install'], { stdio: 'inherit' });
94
+ } catch (error) {
95
+ console.warn(`Unable to install lefthook hooks: ${error.message}`);
96
+ }
97
+ };
98
+
20
99
  const removeTree = dir =>
21
100
  fs.rmSync(dir, {
22
101
  force: true,
@@ -26,32 +105,50 @@ const removeTree = dir =>
26
105
  });
27
106
 
28
107
  const cloneSource = (source, targetDir) => {
29
- const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
30
- try {
31
- run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
32
- stdio: 'inherit',
108
+ if (source.commit) {
109
+ run('git', ['init', targetDir]);
110
+ run('git', ['remote', 'add', 'origin', source.repository], {
111
+ cwd: targetDir,
33
112
  });
34
- } catch {
35
- run('git', ['clone', '--depth', '1', source.repository, targetDir], {
36
- stdio: 'inherit',
113
+ run('git', ['fetch', '--depth', '1', '--quiet', 'origin', source.commit], {
114
+ cwd: targetDir,
37
115
  });
116
+ run(
117
+ 'git',
118
+ [
119
+ '-c',
120
+ 'advice.detachedHead=false',
121
+ 'checkout',
122
+ '--detach',
123
+ '--quiet',
124
+ 'FETCH_HEAD',
125
+ ],
126
+ { cwd: targetDir },
127
+ );
128
+ return;
38
129
  }
39
- if (source.commit) {
40
- try {
41
- run('git', ['checkout', source.commit], {
42
- cwd: targetDir,
43
- stdio: 'inherit',
44
- });
45
- } catch {
46
- run('git', ['fetch', '--depth', '1', 'origin', source.commit], {
47
- cwd: targetDir,
48
- stdio: 'inherit',
49
- });
50
- run('git', ['checkout', source.commit], {
51
- cwd: targetDir,
52
- stdio: 'inherit',
53
- });
54
- }
130
+
131
+ const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
132
+ try {
133
+ run('gh', [
134
+ 'repo',
135
+ 'clone',
136
+ repo,
137
+ targetDir,
138
+ '--',
139
+ '--depth',
140
+ '1',
141
+ '--quiet',
142
+ ]);
143
+ } catch {
144
+ run('git', [
145
+ 'clone',
146
+ '--depth',
147
+ '1',
148
+ '--quiet',
149
+ source.repository,
150
+ targetDir,
151
+ ]);
55
152
  }
56
153
  };
57
154
 
@@ -124,6 +221,8 @@ if (checkOnly) {
124
221
  }
125
222
 
126
223
  fs.mkdirSync(installDir, { recursive: true });
224
+ installGit();
225
+ initializeGitRepository();
127
226
 
128
227
  for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
129
228
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
@@ -161,3 +260,5 @@ for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
161
260
  removeTree(tempDir);
162
261
  }
163
262
  }
263
+
264
+ installLefthook();