@ultimat3/cli 27.16.1 → 29.0.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/CLAUDE.md +1 -1
- package/package.json +31 -31
- package/src/app-root.ts +1 -1
- package/src/cmd-deploy-compose.ts +10 -1
- package/src/cmd-deploy-start-first.ts +63 -19
- package/src/cmd-deploy.ts +10 -1
- package/src/cmd-i18n.ts +28 -6
- package/src/compile-executable.ts +44 -0
- package/src/compile-externals.ts +4 -3
- package/src/dev-route-table.ts +6 -0
- package/src/document-options.ts +10 -0
- package/src/error-codes.ts +6 -0
- package/src/errors.ts +14 -0
- package/src/i18n-locale-parity.ts +134 -0
- package/src/i18n-registration.ts +6 -0
- package/src/mcp-errors.ts +6 -0
- package/src/messages.ts +4 -0
- package/src/prerender.ts +5 -0
- package/src/runtime-jobs.ts +83 -7
- package/src/runtime-render.ts +57 -55
- package/src/serve-web.ts +5 -0
- package/src/site-config.ts +14 -4
- package/src/templates/scaffold-container.ts +3 -3
- package/src/templates/scaffold-repo.ts +2 -2
- package/src/workspace-graph.ts +56 -9
package/src/workspace-graph.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
// The workspace dependency graph, in the two forms that must agree: the edges every manifest
|
|
2
2
|
// DECLARES, the edges every shipped source file actually IMPORTS, and the gate rule that the
|
|
3
|
-
// second is
|
|
4
|
-
// for `tsc`
|
|
3
|
+
// second is covered by the first, with no unused framework sibling declarations. A tsconfig-only
|
|
4
|
+
// edge resolves for `tsc` but is invisible to bun, `--filter` ordering and change-detection tools.
|
|
5
5
|
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { ERROR_DOCS_URL, maskLiterals } from '@ultimat3/core';
|
|
8
|
+
import { CATALOG_PACKAGES } from './error-catalog';
|
|
8
9
|
import { scanAllImports } from './import-scan';
|
|
9
10
|
import type { Finding } from './output';
|
|
10
|
-
import {
|
|
11
|
+
import { FIXTURE_SUFFIX } from './publish-closure';
|
|
12
|
+
import { quoteArg } from './shell-quote';
|
|
13
|
+
import { eachSourceFile, isGenerated, isTest, isVendored, SOURCE_GLOBS } from './source-files';
|
|
11
14
|
|
|
12
15
|
export interface WorkspaceNode {
|
|
13
16
|
/** package.json "name". */
|
|
@@ -176,7 +179,7 @@ export const undeclaredWorkspaceDepFinding = (
|
|
|
176
179
|
cause: `${at} imports ${to.name}, which ${from.dir}/package.json does not declare`,
|
|
177
180
|
// The exact line to paste, at the version the target really carries: a `workspace:*` range would
|
|
178
181
|
// resolve and then fail `checkLockstep`, which compares a sibling pin against the version.
|
|
179
|
-
fix: `add "${to.name}": "${to.version ?? '0.0.0'}" to "dependencies" in ${from.dir}/package.json`,
|
|
182
|
+
fix: `add "${to.name}": "${to.version ?? '0.0.0'}" to "${at.endsWith(FIXTURE_SUFFIX) ? 'devDependencies' : 'dependencies'}" in ${from.dir}/package.json`,
|
|
180
183
|
docs: ERROR_DOCS_URL,
|
|
181
184
|
at: `${from.dir}/package.json`,
|
|
182
185
|
});
|
|
@@ -186,10 +189,9 @@ export const undeclaredWorkspaceDepFinding = (
|
|
|
186
189
|
* declared none of it — the imports resolved through the root tsconfig's `paths`, so the edges
|
|
187
190
|
* existed only inside `tsc` and every tool that asks what a change affects answered short.
|
|
188
191
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* judging test imports would demand 29 of them to describe a resolution that already works.
|
|
192
|
+
* Tests resolve through the root's hoisted devDependencies and are excluded here. Fixtures still
|
|
193
|
+
* need forward declarations, but their imports justify only development dependencies: they are
|
|
194
|
+
* excluded from tarballs and must not keep unused shipped dependency fields alive.
|
|
193
195
|
*
|
|
194
196
|
* One finding per (workspace, dependency) pair, never one per import site: the fix is one line in
|
|
195
197
|
* one manifest, and repeating it per file is noise an author has to deduplicate by hand.
|
|
@@ -202,6 +204,8 @@ export async function checkWorkspaceDependencies(root: string): Promise<readonly
|
|
|
202
204
|
// Deepest directory first: a nested workspace's files are its own, never its parent's.
|
|
203
205
|
const owners = [...graph].sort((left, right) => right.dir.length - left.dir.length);
|
|
204
206
|
const reported = new Set<string>();
|
|
207
|
+
const used = new Set<string>();
|
|
208
|
+
const fixtureUsed = new Set<string>();
|
|
205
209
|
for await (const path of eachSourceFile(root)) {
|
|
206
210
|
if (isTest(path) || isGenerated(path)) continue;
|
|
207
211
|
const owner = owners.find((node) => path.startsWith(`${node.dir}/`));
|
|
@@ -209,12 +213,55 @@ export async function checkWorkspaceDependencies(root: string): Promise<readonly
|
|
|
209
213
|
for (const name of importedPackages(await Bun.file(join(root, path)).text(), path)) {
|
|
210
214
|
const target = byName.get(name);
|
|
211
215
|
if (target === undefined || target.name === owner.name) continue;
|
|
212
|
-
if (owner.dependencies.includes(target.name)) continue;
|
|
213
216
|
const pair = `${owner.dir} ${target.name}`;
|
|
217
|
+
// Fixtures still owe forward declarations, but never justify a shipped dependency.
|
|
218
|
+
(path.endsWith(FIXTURE_SUFFIX) ? fixtureUsed : used).add(pair);
|
|
219
|
+
if (owner.dependencies.includes(target.name)) continue;
|
|
214
220
|
if (reported.has(pair)) continue;
|
|
215
221
|
reported.add(pair);
|
|
216
222
|
findings.push(undeclaredWorkspaceDepFinding(owner, target, path));
|
|
217
223
|
}
|
|
218
224
|
}
|
|
225
|
+
for (const owner of graph) {
|
|
226
|
+
// Only infer absence where the source contract scans. Match directory prefixes rather than
|
|
227
|
+
// observed files so a covered workspace with no source still owes removal of unused siblings.
|
|
228
|
+
const covered = SOURCE_GLOBS.some((pattern) => {
|
|
229
|
+
const directories = pattern.split('/').slice(0, -1);
|
|
230
|
+
return directories.some((_, index) =>
|
|
231
|
+
new Bun.Glob(directories.slice(0, index + 1).join('/')).match(owner.dir),
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
if (!covered || isVendored(owner.dir)) continue;
|
|
235
|
+
const unused = owner.dependencies.filter(
|
|
236
|
+
(name) =>
|
|
237
|
+
name.startsWith('@ultimat3/') &&
|
|
238
|
+
!used.has(`${owner.dir} ${name}`) &&
|
|
239
|
+
!(owner.name === '@ultimat3/cli' && CATALOG_PACKAGES.some((root) => root === name)),
|
|
240
|
+
);
|
|
241
|
+
if (unused.length === 0) continue;
|
|
242
|
+
const path = `${owner.dir}/package.json`;
|
|
243
|
+
const manifest = await readManifest(join(root, path));
|
|
244
|
+
for (const name of unused) {
|
|
245
|
+
const fields = DEPENDENCY_FIELDS.filter(
|
|
246
|
+
(field) =>
|
|
247
|
+
Object.hasOwn(asRecord(manifest?.[field]) ?? {}, name) &&
|
|
248
|
+
!(field === 'devDependencies' && fixtureUsed.has(`${owner.dir} ${name}`)),
|
|
249
|
+
);
|
|
250
|
+
if (fields.length === 0) continue;
|
|
251
|
+
// A package-wide remove would also delete a valid fixture-only development declaration.
|
|
252
|
+
const script =
|
|
253
|
+
'const [path, name, fields] = Bun.argv.slice(-3); ' +
|
|
254
|
+
'const manifest = await Bun.file(path).json(); ' +
|
|
255
|
+
'for (const field of JSON.parse(fields)) delete manifest[field]?.[name]; ' +
|
|
256
|
+
'await Bun.write(path, JSON.stringify(manifest, null, 2) + "\\n");';
|
|
257
|
+
findings.push({
|
|
258
|
+
code: 'X_PACKAGE_SHAPE',
|
|
259
|
+
cause: `${path} declares ${name} in ${fields.join(', ')}, but no eligible source in ${owner.dir} imports it`,
|
|
260
|
+
fix: `bun -e ${quoteArg(script)} -- ${quoteArg(path)} ${quoteArg(name)} ${quoteArg(JSON.stringify(fields))}`,
|
|
261
|
+
docs: ERROR_DOCS_URL,
|
|
262
|
+
at: path,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
219
266
|
return findings;
|
|
220
267
|
}
|