@bleedingdev/modern-js-create 3.2.0-ultramodern.98 → 3.4.0-ultramodern.0
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/README.md +281 -106
- package/bin/run.js +1 -0
- package/dist/cjs/create-package-root.cjs +63 -0
- package/dist/cjs/index.cjs +658 -0
- package/dist/cjs/locale/en.cjs +101 -0
- package/dist/cjs/locale/index.cjs +50 -0
- package/dist/cjs/locale/zh.cjs +101 -0
- package/dist/cjs/ultramodern-package-source.cjs +135 -0
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +603 -0
- package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
- package/dist/cjs/ultramodern-workspace/codesmith.cjs +134 -0
- package/dist/cjs/ultramodern-workspace/contracts.cjs +837 -0
- package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
- package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
- package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
- package/dist/cjs/ultramodern-workspace/generation-result.cjs +158 -0
- package/dist/cjs/ultramodern-workspace/index.cjs +51 -0
- package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
- package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
- package/dist/cjs/ultramodern-workspace/overlays.cjs +120 -0
- package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
- package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
- package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
- package/dist/cjs/ultramodern-workspace/public-api.cjs +47 -0
- package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
- package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
- package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
- package/dist/cjs/ultramodern-workspace/versions.cjs +161 -0
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +154 -0
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +197 -0
- package/dist/esm/create-package-root.js +14 -0
- package/dist/esm/index.js +621 -0
- package/dist/esm/locale/en.js +63 -0
- package/dist/esm/locale/index.js +9 -0
- package/dist/esm/locale/zh.js +63 -0
- package/dist/esm/ultramodern-package-source.js +63 -0
- package/dist/esm/ultramodern-workspace/add-vertical.js +511 -0
- package/dist/esm/ultramodern-workspace/app-files.js +149 -0
- package/dist/esm/ultramodern-workspace/codesmith.js +86 -0
- package/dist/esm/ultramodern-workspace/contracts.js +742 -0
- package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
- package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
- package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
- package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
- package/dist/esm/ultramodern-workspace/generation-result.js +102 -0
- package/dist/esm/ultramodern-workspace/index.js +3 -0
- package/dist/esm/ultramodern-workspace/locales.js +122 -0
- package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
- package/dist/esm/ultramodern-workspace/naming.js +71 -0
- package/dist/esm/ultramodern-workspace/overlays.js +79 -0
- package/dist/esm/ultramodern-workspace/package-json.js +338 -0
- package/dist/esm/ultramodern-workspace/package-source.js +21 -0
- package/dist/esm/ultramodern-workspace/policy.js +183 -0
- package/dist/esm/ultramodern-workspace/public-api.js +2 -0
- package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
- package/dist/esm/ultramodern-workspace/routes.js +280 -0
- package/dist/esm/ultramodern-workspace/types.js +16 -0
- package/dist/esm/ultramodern-workspace/versions.js +36 -0
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +92 -0
- package/dist/esm/ultramodern-workspace/write-workspace.js +143 -0
- package/dist/esm-node/create-package-root.js +15 -0
- package/dist/esm-node/index.js +622 -0
- package/dist/esm-node/locale/en.js +64 -0
- package/dist/esm-node/locale/index.js +10 -0
- package/dist/esm-node/locale/zh.js +64 -0
- package/dist/esm-node/ultramodern-package-source.js +64 -0
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +512 -0
- package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
- package/dist/esm-node/ultramodern-workspace/codesmith.js +87 -0
- package/dist/esm-node/ultramodern-workspace/contracts.js +743 -0
- package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
- package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
- package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
- package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
- package/dist/esm-node/ultramodern-workspace/generation-result.js +103 -0
- package/dist/esm-node/ultramodern-workspace/index.js +4 -0
- package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
- package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
- package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
- package/dist/esm-node/ultramodern-workspace/overlays.js +80 -0
- package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
- package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
- package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
- package/dist/esm-node/ultramodern-workspace/public-api.js +3 -0
- package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
- package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
- package/dist/esm-node/ultramodern-workspace/types.js +17 -0
- package/dist/esm-node/ultramodern-workspace/versions.js +37 -0
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +93 -0
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +144 -0
- package/dist/types/create-package-root.d.ts +1 -0
- package/dist/types/locale/en.d.ts +12 -8
- package/dist/types/locale/index.d.ts +125 -2
- package/dist/types/locale/zh.d.ts +12 -8
- package/dist/types/ultramodern-package-source.d.ts +28 -0
- package/dist/types/ultramodern-workspace/add-vertical.d.ts +36 -0
- package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
- package/dist/types/ultramodern-workspace/codesmith.d.ts +53 -0
- package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
- package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
- package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
- package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
- package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
- package/dist/types/ultramodern-workspace/generation-result.d.ts +14 -0
- package/dist/types/ultramodern-workspace/index.d.ts +4 -0
- package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
- package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
- package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
- package/dist/types/ultramodern-workspace/overlays.d.ts +6 -0
- package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
- package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
- package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
- package/dist/types/ultramodern-workspace/public-api.d.ts +3 -0
- package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
- package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
- package/dist/types/ultramodern-workspace/types.d.ts +197 -0
- package/dist/types/ultramodern-workspace/versions.d.ts +40 -0
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
- package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
- package/package.json +58 -15
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
- package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
- package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
- package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
- package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
- package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
- package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
- package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
- package/template-workspace/.agents/skills-lock.json +114 -0
- package/template-workspace/.codex/hooks.json +16 -0
- package/template-workspace/.github/renovate.json +29 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +67 -0
- package/template-workspace/.gitignore.handlebars +5 -0
- package/template-workspace/.mise.toml.handlebars +3 -0
- package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +20 -9
- package/template-workspace/README.md.handlebars +85 -14
- package/template-workspace/lefthook.yml +18 -4
- package/template-workspace/pnpm-workspace.yaml.handlebars +45 -0
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +69 -75
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +8 -6
- package/templates/app/shell-frame.tsx +49 -0
- package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
- package/templates/packages/shared-contracts-index.ts +466 -0
- package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
- package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
- package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +986 -0
- package/dist/index.js +0 -6625
- package/dist/types/ultramodern-workspace.d.ts +0 -31
- package/template/AGENTS.md +0 -23
- package/template/README.md +0 -105
- package/template/api/effect/index.ts.handlebars +0 -34
- package/template/api/lambda/hello.ts.handlebars +0 -6
- package/template/config/public/locales/cs/translation.json +0 -39
- package/template/config/public/locales/en/translation.json +0 -39
- package/template/lefthook.yml +0 -10
- package/template/modern.config.ts.handlebars +0 -71
- package/template/oxfmt.config.ts +0 -15
- package/template/oxlint.config.ts +0 -19
- package/template/package.json.handlebars +0 -66
- package/template/pnpm-workspace.yaml +0 -29
- package/template/postcss.config.mjs.handlebars +0 -6
- package/template/rstest.config.mts +0 -5
- package/template/scripts/bootstrap-agent-skills.mjs +0 -228
- package/template/scripts/check-i18n-strings.mjs +0 -94
- package/template/scripts/validate-ultramodern.mjs.handlebars +0 -538
- package/template/shared/effect/api.ts.handlebars +0 -17
- package/template/src/modern-app-env.d.ts +0 -3
- package/template/src/modern.runtime.ts.handlebars +0 -23
- package/template/src/routes/[lang]/page.tsx.handlebars +0 -211
- package/template/src/routes/index.css.handlebars +0 -129
- package/template/src/routes/layout.tsx.handlebars +0 -10
- package/template/tailwind.config.ts.handlebars +0 -10
- package/template/tests/tsconfig.json +0 -7
- package/template/tests/ultramodern.contract.test.ts.handlebars +0 -78
- package/template/tsconfig.json +0 -120
- package/template-workspace/pnpm-workspace.yaml +0 -35
|
@@ -7,6 +7,16 @@ const root = process.cwd();
|
|
|
7
7
|
const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
8
8
|
const checkOnly = process.argv.includes('--check');
|
|
9
9
|
const force = process.argv.includes('--force');
|
|
10
|
+
const postinstall = process.argv.includes('--postinstall');
|
|
11
|
+
const truthy = value => /^(1|true|yes|on)$/i.test(String(value ?? ''));
|
|
12
|
+
const falsy = value => /^(0|false|no|off)$/i.test(String(value ?? ''));
|
|
13
|
+
const skipRequested =
|
|
14
|
+
truthy(process.env.ULTRAMODERN_SKIP_AGENT_SKILLS) ||
|
|
15
|
+
falsy(process.env.ULTRAMODERN_AGENT_SKILLS);
|
|
16
|
+
const cloneTimeoutMs = Number.parseInt(
|
|
17
|
+
process.env.ULTRAMODERN_AGENT_SKILLS_CLONE_TIMEOUT_MS ?? '60000',
|
|
18
|
+
10,
|
|
19
|
+
);
|
|
10
20
|
|
|
11
21
|
const readJson = filePath => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
12
22
|
|
|
@@ -15,6 +25,7 @@ const run = (command, args, options = {}) =>
|
|
|
15
25
|
cwd: options.cwd ?? root,
|
|
16
26
|
encoding: 'utf-8',
|
|
17
27
|
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
28
|
+
timeout: options.timeout,
|
|
18
29
|
});
|
|
19
30
|
|
|
20
31
|
const commandExists = command => {
|
|
@@ -26,45 +37,14 @@ const commandExists = command => {
|
|
|
26
37
|
}
|
|
27
38
|
};
|
|
28
39
|
|
|
29
|
-
const
|
|
30
|
-
run('sh', ['-lc', script], {
|
|
31
|
-
stdio: 'inherit',
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const installGit = () => {
|
|
40
|
+
const requireGit = () => {
|
|
35
41
|
if (commandExists('git')) {
|
|
36
42
|
return;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
}
|
|
45
|
+
throw new Error(
|
|
46
|
+
'Git is required to install agent skills. Install git yourself (for example "brew install git" or "sudo apt-get install git") and run pnpm skills:install again. This script never installs system packages on your behalf.',
|
|
47
|
+
);
|
|
68
48
|
};
|
|
69
49
|
|
|
70
50
|
const isInsideGitWorkTree = () => {
|
|
@@ -106,12 +86,14 @@ const removeTree = dir =>
|
|
|
106
86
|
|
|
107
87
|
const cloneSource = (source, targetDir) => {
|
|
108
88
|
if (source.commit) {
|
|
109
|
-
run('git', ['init', targetDir]);
|
|
89
|
+
run('git', ['init', targetDir], { timeout: 30000 });
|
|
110
90
|
run('git', ['remote', 'add', 'origin', source.repository], {
|
|
111
91
|
cwd: targetDir,
|
|
92
|
+
timeout: 30000,
|
|
112
93
|
});
|
|
113
94
|
run('git', ['fetch', '--depth', '1', '--quiet', 'origin', source.commit], {
|
|
114
95
|
cwd: targetDir,
|
|
96
|
+
timeout: cloneTimeoutMs,
|
|
115
97
|
});
|
|
116
98
|
run(
|
|
117
99
|
'git',
|
|
@@ -123,32 +105,24 @@ const cloneSource = (source, targetDir) => {
|
|
|
123
105
|
'--quiet',
|
|
124
106
|
'FETCH_HEAD',
|
|
125
107
|
],
|
|
126
|
-
{ cwd: targetDir },
|
|
108
|
+
{ cwd: targetDir, timeout: 30000 },
|
|
127
109
|
);
|
|
128
110
|
return;
|
|
129
111
|
}
|
|
130
112
|
|
|
131
113
|
const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
|
|
132
114
|
try {
|
|
133
|
-
run(
|
|
134
|
-
'
|
|
135
|
-
'clone',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
'--',
|
|
139
|
-
'--depth',
|
|
140
|
-
'1',
|
|
141
|
-
'--quiet',
|
|
142
|
-
]);
|
|
115
|
+
run(
|
|
116
|
+
'gh',
|
|
117
|
+
['repo', 'clone', repo, targetDir, '--', '--depth', '1', '--quiet'],
|
|
118
|
+
{ timeout: cloneTimeoutMs },
|
|
119
|
+
);
|
|
143
120
|
} catch {
|
|
144
|
-
run(
|
|
145
|
-
'
|
|
146
|
-
'--depth',
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
source.repository,
|
|
150
|
-
targetDir,
|
|
151
|
-
]);
|
|
121
|
+
run(
|
|
122
|
+
'git',
|
|
123
|
+
['clone', '--depth', '1', '--quiet', source.repository, targetDir],
|
|
124
|
+
{ timeout: cloneTimeoutMs },
|
|
125
|
+
);
|
|
152
126
|
}
|
|
153
127
|
};
|
|
154
128
|
|
|
@@ -178,21 +152,37 @@ const requiredCloneSources = sources.filter(
|
|
|
178
152
|
const optionalCloneSources = sources.filter(
|
|
179
153
|
source => source.install === 'clone-if-authorized',
|
|
180
154
|
);
|
|
181
|
-
const
|
|
182
|
-
...(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
(skill, index, skills) =>
|
|
186
|
-
skills.findIndex(candidate => candidate.name === skill.name) === index,
|
|
155
|
+
const cloneSourceSkillNames = new Set(
|
|
156
|
+
[...requiredCloneSources, ...optionalCloneSources].flatMap(source =>
|
|
157
|
+
(source.baseline ?? []).map(skill => skill.name),
|
|
158
|
+
),
|
|
187
159
|
);
|
|
160
|
+
const vendoredRequiredSkills = (lock.baseline ?? []).filter(
|
|
161
|
+
skill => !cloneSourceSkillNames.has(skill.name),
|
|
162
|
+
);
|
|
163
|
+
const cloneOptIn = truthy(process.env.ULTRAMODERN_AGENT_SKILLS);
|
|
164
|
+
|
|
165
|
+
if (skipRequested) {
|
|
166
|
+
const reason = 'agent skills bootstrap skipped by environment';
|
|
167
|
+
if (checkOnly) {
|
|
168
|
+
console.log(reason);
|
|
169
|
+
process.exit(0);
|
|
170
|
+
}
|
|
171
|
+
console.log(reason);
|
|
172
|
+
installLefthook();
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
188
175
|
|
|
189
176
|
if (checkOnly) {
|
|
190
|
-
const
|
|
177
|
+
const missingVendored = vendoredRequiredSkills
|
|
191
178
|
.map(skill => skill.name)
|
|
192
179
|
.filter(
|
|
193
180
|
skillName => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
194
181
|
);
|
|
195
|
-
const
|
|
182
|
+
const missingCloneInstalled = [
|
|
183
|
+
...requiredCloneSources,
|
|
184
|
+
...optionalCloneSources,
|
|
185
|
+
].flatMap(source =>
|
|
196
186
|
(source.baseline ?? [])
|
|
197
187
|
.map(skill => skill.name)
|
|
198
188
|
.filter(
|
|
@@ -201,27 +191,33 @@ if (checkOnly) {
|
|
|
201
191
|
),
|
|
202
192
|
);
|
|
203
193
|
|
|
204
|
-
if (
|
|
194
|
+
if (missingVendored.length > 0) {
|
|
205
195
|
console.error(
|
|
206
|
-
`Required agent skills not installed: ${
|
|
196
|
+
`Required agent skills not installed: ${missingVendored.join(', ')}. Run pnpm skills:install.`,
|
|
207
197
|
);
|
|
208
198
|
process.exit(1);
|
|
209
199
|
}
|
|
210
200
|
|
|
211
|
-
if (
|
|
201
|
+
if (missingCloneInstalled.length > 0) {
|
|
212
202
|
console.warn(
|
|
213
|
-
`
|
|
203
|
+
`Clone-installed agent skills not present: ${missingCloneInstalled.join(', ')}. Run pnpm skills:install to fetch them.`,
|
|
214
204
|
);
|
|
215
205
|
} else {
|
|
216
|
-
console.log('
|
|
217
|
-
process.exit(0);
|
|
206
|
+
console.log('All pinned agent skills are installed.');
|
|
218
207
|
}
|
|
219
|
-
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (postinstall && !cloneOptIn) {
|
|
212
|
+
console.log(
|
|
213
|
+
'Skipping agent skill repository clones during postinstall. Run pnpm skills:install (or set ULTRAMODERN_AGENT_SKILLS=1 before installing) to fetch them.',
|
|
214
|
+
);
|
|
215
|
+
installLefthook();
|
|
220
216
|
process.exit(0);
|
|
221
217
|
}
|
|
222
218
|
|
|
223
219
|
fs.mkdirSync(installDir, { recursive: true });
|
|
224
|
-
|
|
220
|
+
requireGit();
|
|
225
221
|
initializeGitRepository();
|
|
226
222
|
|
|
227
223
|
for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
|
|
@@ -230,10 +226,8 @@ for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
|
|
|
230
226
|
try {
|
|
231
227
|
cloneSource(source, tempDir);
|
|
232
228
|
} catch (error) {
|
|
233
|
-
if (source.install === 'clone-if-authorized') {
|
|
234
|
-
console.warn(
|
|
235
|
-
`Skipping ${source.repository}; current developer may not have access.`,
|
|
236
|
-
);
|
|
229
|
+
if (source.install === 'clone-if-authorized' || postinstall) {
|
|
230
|
+
console.warn(`Skipping ${source.repository}; ${error.message}`);
|
|
237
231
|
continue;
|
|
238
232
|
}
|
|
239
233
|
throw error;
|
|
@@ -122,6 +122,12 @@ function porcelainStatus() {
|
|
|
122
122
|
return run('git', ['status', '--porcelain'], { timeout: 30000 });
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function commitInstallerChanges(message) {
|
|
126
|
+
run('git', ['commit', '--no-verify', '-m', message], {
|
|
127
|
+
timeout: 120000,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
function ensureGitRepository() {
|
|
126
132
|
if (!isGitWorkTree()) {
|
|
127
133
|
if (checkOnly) {
|
|
@@ -139,9 +145,7 @@ function ensureGitRepository() {
|
|
|
139
145
|
}
|
|
140
146
|
log('creating initial workspace commit before adding reference subtrees');
|
|
141
147
|
run('git', ['add', '-A'], { timeout: 30000 });
|
|
142
|
-
|
|
143
|
-
timeout: 120000,
|
|
144
|
-
});
|
|
148
|
+
commitInstallerChanges('Initialize UltraModern workspace');
|
|
145
149
|
return true;
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -311,9 +315,7 @@ function commitManifestIfChanged() {
|
|
|
311
315
|
return;
|
|
312
316
|
}
|
|
313
317
|
run('git', ['add', manifestPath], { timeout: 30000 });
|
|
314
|
-
|
|
315
|
-
timeout: 120000,
|
|
316
|
-
});
|
|
318
|
+
commitInstallerChanges('Record agent reference repo manifest');
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
function main() {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { Header, StatusBadge } from './vertical-components';
|
|
4
|
+
|
|
5
|
+
interface ShellFrameProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function ShellFrame({ children }: ShellFrameProps) {
|
|
10
|
+
const { i18nInstance, language } = useModernI18n();
|
|
11
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
12
|
+
const { alternates } = useLocalizedLocation();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<main className="shell:min-h-screen shell:bg-um-canvas shell:px-4 shell:py-5 shell:text-um-foreground shell:sm:px-6 shell:lg:px-12">
|
|
16
|
+
<div className="shell:mx-auto shell:flex shell:min-h-20 shell:max-w-7xl shell:flex-col shell:items-start shell:gap-3 shell:bg-white/90 shell:px-4 shell:py-3 shell:shadow-xl shell:shadow-stone-900/10 shell:sm:px-6 shell:md:flex-row shell:md:flex-wrap shell:md:items-center shell:md:justify-between">
|
|
17
|
+
<Header />
|
|
18
|
+
<div className="shell:flex shell:min-w-0 shell:flex-wrap shell:items-center shell:gap-2 shell:md:ml-auto">
|
|
19
|
+
<label className="shell:sr-only" htmlFor="ultramodern-language">
|
|
20
|
+
{t('shell.language.switcher')}
|
|
21
|
+
</label>
|
|
22
|
+
<select
|
|
23
|
+
aria-label={t('shell.language.switcher')}
|
|
24
|
+
className="shell:h-10 shell:w-10 shell:cursor-pointer shell:appearance-none shell:border-0 shell:bg-transparent shell:p-0 shell:text-center shell:text-3xl shell:font-black shell:leading-none shell:text-stone-950 shell:shadow-none shell:[appearance:none] shell:[text-align-last:center] shell:focus-visible:rounded-md shell:focus-visible:outline-3 shell:focus-visible:outline-offset-2 shell:focus-visible:outline-emerald-700/40 shell:[&::-ms-expand]:hidden shell:[&::picker-icon]:hidden shell:[&_option]:text-xl"
|
|
25
|
+
id="ultramodern-language"
|
|
26
|
+
name="language"
|
|
27
|
+
onChange={event => {
|
|
28
|
+
const nextLanguage = event.currentTarget.value;
|
|
29
|
+
const targetHref = alternates[nextLanguage];
|
|
30
|
+
if (targetHref !== undefined) {
|
|
31
|
+
window.location.assign(targetHref);
|
|
32
|
+
}
|
|
33
|
+
}}
|
|
34
|
+
value={language}
|
|
35
|
+
>
|
|
36
|
+
<option aria-label={t('shell.language.en')} value="en">
|
|
37
|
+
🇬🇧
|
|
38
|
+
</option>
|
|
39
|
+
<option aria-label={t('shell.language.cs')} value="cs">
|
|
40
|
+
🇨🇿
|
|
41
|
+
</option>
|
|
42
|
+
</select>
|
|
43
|
+
<StatusBadge />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
{children}
|
|
47
|
+
</main>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2
|
+
import { Helmet } from '@modern-js/runtime/head';
|
|
3
|
+
import {
|
|
4
|
+
ultramodernRouteMetadata,
|
|
5
|
+
} from './ultramodern-route-metadata';
|
|
6
|
+
import type { RouteJsonLd } from './ultramodern-jsonld';
|
|
7
|
+
|
|
8
|
+
const appName = {{appDisplayNameJson}};
|
|
9
|
+
const fallbackLanguage = 'en';
|
|
10
|
+
const supportedLanguages = ['en', 'cs'] as const;
|
|
11
|
+
type SupportedLanguage = (typeof supportedLanguages)[number];
|
|
12
|
+
type RouteMetadata = (typeof ultramodernRouteMetadata)[number] & {
|
|
13
|
+
readonly jsonLd?: RouteJsonLd;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const routeMetadata = ultramodernRouteMetadata as readonly RouteMetadata[];
|
|
17
|
+
|
|
18
|
+
const isSupportedLanguage = (value: string): value is SupportedLanguage =>
|
|
19
|
+
supportedLanguages.includes(value as SupportedLanguage);
|
|
20
|
+
|
|
21
|
+
const normalisePath = (pathname: string) => {
|
|
22
|
+
const normalised = pathname.replaceAll(/\/+/gu, '/').replace(/\/+$/u, '');
|
|
23
|
+
return normalised.length > 0 ? normalised : '/';
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const stripLanguagePrefix = (pathname: string) => {
|
|
27
|
+
const segments = normalisePath(pathname).split('/').filter(Boolean);
|
|
28
|
+
if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
|
|
29
|
+
segments.shift();
|
|
30
|
+
}
|
|
31
|
+
return `/${segments.join('/')}`;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const escapeRegExp = (value: string) =>
|
|
35
|
+
value.replaceAll(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
36
|
+
|
|
37
|
+
const paramName = (segment: string) => segment.slice(1).replace(/\?$/u, '');
|
|
38
|
+
|
|
39
|
+
const matchPattern = (pathname: string, pattern: string) => {
|
|
40
|
+
const names: string[] = [];
|
|
41
|
+
const source = normalisePath(pattern)
|
|
42
|
+
.split('/')
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.map(segment => {
|
|
45
|
+
if (segment.startsWith(':')) {
|
|
46
|
+
names.push(paramName(segment));
|
|
47
|
+
return segment.endsWith('?') ? '(?:/([^/]+))?' : '/([^/]+)';
|
|
48
|
+
}
|
|
49
|
+
return `/${escapeRegExp(segment)}`;
|
|
50
|
+
})
|
|
51
|
+
.join('');
|
|
52
|
+
const match = new RegExp(`^${source || '/'}$`, 'u').exec(
|
|
53
|
+
normalisePath(pathname),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (match === null) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const params: Record<string, string> = {};
|
|
61
|
+
for (const [index, name] of names.entries()) {
|
|
62
|
+
params[name] = decodeURIComponent(match[index + 1] ?? '');
|
|
63
|
+
}
|
|
64
|
+
return params;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const resolveRouteMetadata = (pathname: string) => {
|
|
68
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname);
|
|
69
|
+
|
|
70
|
+
for (const route of routeMetadata) {
|
|
71
|
+
const canonicalParams = matchPattern(pathWithoutLanguage, route.canonicalPath);
|
|
72
|
+
if (canonicalParams !== undefined) {
|
|
73
|
+
return route;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const language of supportedLanguages) {
|
|
77
|
+
const params = matchPattern(pathWithoutLanguage, route.localisedPaths[language]);
|
|
78
|
+
if (params !== undefined) {
|
|
79
|
+
return route;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return routeMetadata[0];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const absoluteUrl = (pathname: string) => {
|
|
88
|
+
const origin = ULTRAMODERN_SITE_URL.replace(/\/+$/u, '');
|
|
89
|
+
return `${origin}${pathname}`;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const sanitiseJsonLd = (value: RouteJsonLd) =>
|
|
93
|
+
JSON.stringify(value).replaceAll('<', '\\u003c');
|
|
94
|
+
|
|
95
|
+
export const UltramodernRouteHead = () => {
|
|
96
|
+
const { i18nInstance } = useModernI18n();
|
|
97
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
98
|
+
const { canonical, alternates } = useLocalizedLocation();
|
|
99
|
+
const route = resolveRouteMetadata(canonical);
|
|
100
|
+
const title = route ? t(route.titleKey) : appName;
|
|
101
|
+
const description = route ? t(route.descriptionKey) : appName;
|
|
102
|
+
const canonicalUrl = absoluteUrl(alternates[fallbackLanguage] ?? `/${fallbackLanguage}`);
|
|
103
|
+
const indexable = route?.public === true && route?.indexable === true;
|
|
104
|
+
const jsonLd = indexable ? route?.jsonLd : undefined;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<Helmet htmlAttributes={{ lang: i18nInstance.language ?? fallbackLanguage }}>
|
|
108
|
+
<title>{title}</title>
|
|
109
|
+
<meta content={description} name="description" />
|
|
110
|
+
<meta content={indexable ? 'index, follow' : 'noindex, nofollow'} name="robots" />
|
|
111
|
+
{indexable && (
|
|
112
|
+
<>
|
|
113
|
+
<link rel="canonical" href={canonicalUrl} />
|
|
114
|
+
{supportedLanguages.map(code => (
|
|
115
|
+
<link
|
|
116
|
+
href={absoluteUrl(alternates[code] ?? `/${code}`)}
|
|
117
|
+
hrefLang={code}
|
|
118
|
+
key={code}
|
|
119
|
+
rel="alternate"
|
|
120
|
+
/>
|
|
121
|
+
))}
|
|
122
|
+
<link
|
|
123
|
+
href={absoluteUrl(alternates[fallbackLanguage] ?? `/${fallbackLanguage}`)}
|
|
124
|
+
hrefLang="x-default"
|
|
125
|
+
rel="alternate"
|
|
126
|
+
/>
|
|
127
|
+
<meta content={title} property="og:title" />
|
|
128
|
+
<meta content={description} property="og:description" />
|
|
129
|
+
<meta content={canonicalUrl} property="og:url" />
|
|
130
|
+
<meta content="website" property="og:type" />
|
|
131
|
+
<meta content={i18nInstance.language ?? fallbackLanguage} property="og:locale" />
|
|
132
|
+
<meta content="summary_large_image" name="twitter:card" />
|
|
133
|
+
<meta content={title} name="twitter:title" />
|
|
134
|
+
<meta content={description} name="twitter:description" />
|
|
135
|
+
{jsonLd && (
|
|
136
|
+
<script type="application/ld+json">{sanitiseJsonLd(jsonLd)}</script>
|
|
137
|
+
)}
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
</Helmet>
|
|
141
|
+
);
|
|
142
|
+
};
|