@bobfrankston/npmglobalize 1.0.217 → 1.0.218
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 +12 -7
- package/lib.d.ts +21 -13
- package/lib.js +110 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -736,7 +736,7 @@ Browser projects that use [`importgen`](https://www.npmjs.com/package/@bobfranks
|
|
|
736
736
|
|
|
737
737
|
Before building each project, it checks whether the project is an importgen project, in this order:
|
|
738
738
|
|
|
739
|
-
1. `.vscode/tasks.json` has a task whose `command` is `importgen` (the HTML file, if the task names one, is reused).
|
|
739
|
+
1. `.vscode/tasks.json` has a task whose `command` is `importgen` (the HTML file, if the task names one, is reused). The task's `"options": { "cwd": ... }` is honored too: a plain sub-directory (`www/`) yields `importgen www/index.html`, while a sub-directory that is a package of its own (`client/package.json`) is not a root importgen project at all — its own build script owns the import map and the root build delegates to it (see Sub-projects below).
|
|
740
740
|
2. A root-level `.htm`/`.html` file already contains a `<script type="importmap">` block (`index.html`, `default.html`, `default.htm` are checked first, so a stray `temp.htm` doesn't win).
|
|
741
741
|
3. `importgen` is in `dependencies`/`devDependencies` **and** the project has one of those HTML files — the HTML requirement keeps packages that merely *use* importgen as a library from matching.
|
|
742
742
|
|
|
@@ -744,20 +744,25 @@ If a signal matches and the `build` script doesn't already run `importgen`, you'
|
|
|
744
744
|
|
|
745
745
|
Once wired, the freshness check gains a second condition: a project whose build runs importgen is only considered up to date if the generated HTML is at least as new as `package.json`. Adding a dependency changes the import map without touching any `.ts` file, which the source-vs-output comparison alone would miss.
|
|
746
746
|
|
|
747
|
-
#### Sub-projects (a
|
|
747
|
+
#### Sub-projects (a sub-directory with its own build script or `tsconfig.json`)
|
|
748
748
|
|
|
749
|
-
A package can hold more than one
|
|
749
|
+
A package can hold more than one project. Two shapes, treated differently:
|
|
750
|
+
|
|
751
|
+
- **A sub-package** — `client/` with its own `package.json` whose `build` script (`importgen index.html && tsc`) already says how the client builds. That script is the single source of truth, so the root build **delegates** to it: `npm run build --prefix client`. The root never re-implements the client's recipe (no `importgen client/index.html && tsc -p client` in the root script), and the sub-package's own build script gets the same importgen check the root does.
|
|
752
|
+
- **A plain tsconfig sub-directory** — a service worker in `Sw/` with its own `tsconfig.json` (`lib: ["WebWorker"]`, its own `outDir`) that the root `tsconfig.json` lists under `exclude`, so a bare `"build": "tsc"` compiles everything *except* the service worker and the stale `sw2.js` ships. It has no build of its own, so the root runs `tsc -p Sw`.
|
|
753
|
+
|
|
754
|
+
As with import maps, these have historically been built only by a second `.vscode/tasks.json` watcher, which runs on folder open and nowhere else.
|
|
750
755
|
|
|
751
756
|
Before building, `npmglobalize` looks for sub-projects, in this order:
|
|
752
757
|
|
|
753
|
-
1. `.vscode/tasks.json` has a task that runs `tsc` with `"options": { "cwd": "${workspaceFolder}/
|
|
754
|
-
2. An immediate sub-directory containing its own `tsconfig.json`. Build output and vendored trees (`node_modules`, `prev`, `dist`, `built`, `out`, `wwwroot`, `coverage`, `temp`, `preflight`, dot-directories) are never scanned.
|
|
758
|
+
1. `.vscode/tasks.json` has a task that runs `tsc` or `importgen` with `"options": { "cwd": "${workspaceFolder}/client" }`, or runs `tsc` with `-p`/`--project` naming a sub-directory (authoritative — it's how the project is actually built today; the task's `label` is quoted back in the prompt).
|
|
759
|
+
2. An immediate sub-directory containing its own `package.json` build script or `tsconfig.json`. Build output and vendored trees (`node_modules`, `prev`, `dist`, `built`, `out`, `wwwroot`, `coverage`, `temp`, `preflight`, dot-directories) are never scanned.
|
|
755
760
|
|
|
756
761
|
Nothing is detected when the root `tsconfig.json` uses project `references` — that build graph belongs to `tsc -b` and isn't second-guessed.
|
|
757
762
|
|
|
758
|
-
When the `build` script is `tsc`-driven and doesn't already
|
|
763
|
+
When the `build` script is `tsc`-driven and doesn't already build a detected sub-project, you're prompted to append it — `"build": "tsc"` → `"build": "tsc && npm run build --prefix client"` or `"build": "tsc && tsc -p Sw"`, or for an importgen project `"build": "importgen default.htm && tsc && tsc -p Sw"`. Already-wired scripts are recognized in any of their spellings (`tsc -p Sw`, `tsc --project ./Sw/tsconfig.json`, `cd Sw && tsc`, `npm run build --prefix client`, `npm -C client run build`), and the directory is emitted with its real on-disk casing so the script still works on WSL and CI. Decline and `"subProjects": false` is written to `.globalize.json5`, suppressing the prompt for good; a `"subProjects": ["Sw"]` array pins the list instead of detecting it.
|
|
759
764
|
|
|
760
|
-
Sub-projects the build actually
|
|
765
|
+
Sub-projects the build actually runs are also folded into the freshness check — the package rebuilds when a sub-project's sources are newer than its output (for a sub-package, also when its import map is older than its `package.json`). A sub-project that emits *outside* its own directory (`"outDir": ".."`, the usual service-worker case) always reports stale: comparing its sources against the whole package would prove nothing.
|
|
761
766
|
|
|
762
767
|
#### TypeScript 6 `types` auto-fix
|
|
763
768
|
|
package/lib.d.ts
CHANGED
|
@@ -427,22 +427,30 @@ export declare function detectImportgen(cwd: string): {
|
|
|
427
427
|
* start its watcher on folder open. Declining records `importgen: false` in
|
|
428
428
|
* .globalize.json5 so it stops asking. */
|
|
429
429
|
export declare function ensureImportgenInBuild(cwd: string): Promise<void>;
|
|
430
|
-
/**
|
|
431
|
-
|
|
432
|
-
|
|
430
|
+
/** A sub-directory the root build must build, and how. */
|
|
431
|
+
interface SubProject {
|
|
432
|
+
dir: string; /** Relative directory with its on-disk casing */
|
|
433
|
+
kind: 'package' | 'tsconfig'; /** package: delegate to its build script; tsconfig: `tsc -p` */
|
|
434
|
+
reason: string; /** The signal that found it, quoted in prompts */
|
|
435
|
+
}
|
|
436
|
+
/** Sub-directories the root build must build itself. A package can hold more
|
|
437
|
+
* than one project — a service worker compiled with `lib: WebWorker` that the
|
|
438
|
+
* root tsconfig `exclude`s, or a browser client that is a package of its own
|
|
439
|
+
* with its own build script — and the root `tsc` silently skips them.
|
|
433
440
|
* Signals, in order:
|
|
434
|
-
* 1. `.vscode/tasks.json` runs tsc with `options.cwd` or `-p`
|
|
435
|
-
* sub-dir (authoritative: it's how the developer actually
|
|
436
|
-
*
|
|
441
|
+
* 1. `.vscode/tasks.json` runs tsc or importgen with `options.cwd` or `-p`
|
|
442
|
+
* pointing at a sub-dir (authoritative: it's how the developer actually
|
|
443
|
+
* builds today),
|
|
444
|
+
* 2. an immediate sub-directory holding its own package.json build script
|
|
445
|
+
* or tsconfig.json.
|
|
437
446
|
* Returns nothing when the root tsconfig uses project `references` — `tsc -b`
|
|
438
447
|
* owns that build graph and shouldn't be second-guessed. */
|
|
439
|
-
export declare function detectSubProjects(cwd: string):
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
*
|
|
445
|
-
* what to compile. Declining records `subProjects: false` in .globalize.json5. */
|
|
448
|
+
export declare function detectSubProjects(cwd: string): SubProject[];
|
|
449
|
+
/** Append a build step for every sub-project the existing build script misses:
|
|
450
|
+
* `npm run build --prefix <dir>` for a sub-package, `tsc -p <dir>` for a plain
|
|
451
|
+
* tsconfig. Only touches tsc-driven builds — a bundler-driven `build` has its
|
|
452
|
+
* own idea of what to compile. Declining records `subProjects: false` in
|
|
453
|
+
* .globalize.json5. */
|
|
446
454
|
export declare function ensureSubProjectsInBuild(cwd: string): Promise<void>;
|
|
447
455
|
/** Build a single project: detect tsconfig, prompt to add `build: tsc` if a
|
|
448
456
|
* TypeScript project lacks a build script, run `npm run build`, record
|
package/lib.js
CHANGED
|
@@ -344,7 +344,7 @@ const VALUE_COMMENTS = {
|
|
|
344
344
|
gitVisibility: 'private (default) or public',
|
|
345
345
|
npmVisibility: 'private (default) or public',
|
|
346
346
|
bump: 'patch (default), minor, or major',
|
|
347
|
-
subProjects: 'Sub-dirs with their own tsconfig that build must
|
|
347
|
+
subProjects: 'Sub-dirs with their own build script or tsconfig that build must run (false = never ask)'
|
|
348
348
|
};
|
|
349
349
|
/** Write .globalize.json5 config file */
|
|
350
350
|
export function writeConfig(dir, config, explicitKeys) {
|
|
@@ -450,7 +450,7 @@ export function writeConfig(dir, config, explicitKeys) {
|
|
|
450
450
|
['"usePaths": true', 'true = resolve file: deps from siblings; false = use latest npm version (standalone)'],
|
|
451
451
|
['"allowTs": false', 'true = include .ts source in npm tarball (auto-true for noEmit projects)'],
|
|
452
452
|
['"importgen": auto', 'Detected from .vscode/tasks.json / import map in HTML; false = never ask'],
|
|
453
|
-
['"subProjects": auto', 'Sub-dirs with their own tsconfig (e.g. ["Sw"]); detected from .vscode/tasks.json; false = never ask']
|
|
453
|
+
['"subProjects": auto', 'Sub-dirs with their own build script or tsconfig (e.g. ["client", "Sw"]); detected from .vscode/tasks.json; false = never ask']
|
|
454
454
|
];
|
|
455
455
|
const refWidth = Math.max(...reference.map(([decl]) => decl.length));
|
|
456
456
|
for (const [decl, note] of reference) {
|
|
@@ -3019,7 +3019,27 @@ export function detectImportgen(cwd) {
|
|
|
3019
3019
|
if (!scriptRunsImportgen(command))
|
|
3020
3020
|
continue;
|
|
3021
3021
|
const named = args.find(a => /\.html?$/i.test(a));
|
|
3022
|
-
|
|
3022
|
+
// 2026-09-06 — Claude Code (Fable 5.1), at Bob's direction.
|
|
3023
|
+
// Honor the task's `options.cwd`. itemw runs importgen with cwd
|
|
3024
|
+
// `${workspaceFolder}/client` because its HTML is client/index.html;
|
|
3025
|
+
// ignoring that made us search the root, find nothing, and wire a bare
|
|
3026
|
+
// `importgen` that failed with "No HTML file found".
|
|
3027
|
+
// - cwd is a sub-PACKAGE (has its own package.json): its own build
|
|
3028
|
+
// script owns the import map, and the root build delegates to it
|
|
3029
|
+
// (see detectSubProjects). Not a root importgen project.
|
|
3030
|
+
// - cwd is a plain sub-directory (www/ with just the HTML): run
|
|
3031
|
+
// `importgen www/index.html` from the root — importgen resolves
|
|
3032
|
+
// positional paths against cwd and finds package.json by walking up.
|
|
3033
|
+
const taskCwd = typeof task?.options?.cwd === 'string' ? task.options.cwd : '';
|
|
3034
|
+
let subdir = normalizeProjectPath(taskCwd);
|
|
3035
|
+
if (path.isAbsolute(subdir))
|
|
3036
|
+
subdir = ''; // some other machine's absolute path — nothing we can build from
|
|
3037
|
+
if (subdir && fs.existsSync(path.join(cwd, subdir, 'package.json')))
|
|
3038
|
+
return null;
|
|
3039
|
+
const searchDir = subdir ? path.join(cwd, subdir) : cwd;
|
|
3040
|
+
const found = findImportgenHtml(searchDir, named);
|
|
3041
|
+
const htmlFile = found && subdir ? `${subdir}/${found}` : found;
|
|
3042
|
+
return { htmlFile, reason: '.vscode/tasks.json runs importgen' };
|
|
3023
3043
|
}
|
|
3024
3044
|
}
|
|
3025
3045
|
catch {
|
|
@@ -3178,13 +3198,34 @@ function resolveOnDiskCase(base, rel) {
|
|
|
3178
3198
|
}
|
|
3179
3199
|
return out.length ? out.join('/') : null;
|
|
3180
3200
|
}
|
|
3181
|
-
/**
|
|
3182
|
-
*
|
|
3201
|
+
/** Classify `rel` under `cwd`: a package with its own build script, a plain
|
|
3202
|
+
* tsconfig sub-project, or null when it's neither. */
|
|
3203
|
+
function subProjectKind(cwd, rel) {
|
|
3204
|
+
try {
|
|
3205
|
+
const pkg = readPackageJson(path.join(cwd, rel));
|
|
3206
|
+
if (typeof pkg?.scripts?.build === 'string' && pkg.scripts.build.trim())
|
|
3207
|
+
return 'package';
|
|
3208
|
+
}
|
|
3209
|
+
catch {
|
|
3210
|
+
// No package.json there — fall through to the tsconfig test
|
|
3211
|
+
}
|
|
3212
|
+
return fs.existsSync(path.join(cwd, rel, 'tsconfig.json')) ? 'tsconfig' : null;
|
|
3213
|
+
}
|
|
3214
|
+
/** The command the root build script runs for a sub-project. */
|
|
3215
|
+
function subProjectBuildCommand(s) {
|
|
3216
|
+
return s.kind === 'package'
|
|
3217
|
+
? `npm run build --prefix ${quoteScriptPath(s.dir)}`
|
|
3218
|
+
: `tsc -p ${quoteScriptPath(s.dir)}`;
|
|
3219
|
+
}
|
|
3220
|
+
/** True when `script` already builds `dir` — `tsc -p Sw`,
|
|
3221
|
+
* `tsc --project ./Sw/tsconfig.json`, `cd Sw && tsc`, or for a package
|
|
3222
|
+
* `npm run build --prefix client` / `npm -C client run build`. */
|
|
3183
3223
|
function scriptBuildsSubProject(script, dir) {
|
|
3184
3224
|
const target = normalizeProjectPath(dir).toLowerCase();
|
|
3185
3225
|
if (!target)
|
|
3186
3226
|
return false;
|
|
3187
3227
|
for (const re of [/(?:^|\s)(?:-p|--project)[\s=]+("[^"]+"|'[^']+'|\S+)/g,
|
|
3228
|
+
/(?:^|\s)(?:-C|--prefix)[\s=]+("[^"]+"|'[^']+'|\S+)/g,
|
|
3188
3229
|
/(?:^|[\s&|(])cd\s+("[^"]+"|'[^']+'|\S+)/g]) {
|
|
3189
3230
|
for (const m of script.matchAll(re)) {
|
|
3190
3231
|
if (normalizeProjectPath(m[1]).toLowerCase() === target)
|
|
@@ -3193,13 +3234,16 @@ function scriptBuildsSubProject(script, dir) {
|
|
|
3193
3234
|
}
|
|
3194
3235
|
return false;
|
|
3195
3236
|
}
|
|
3196
|
-
/** Sub-directories
|
|
3197
|
-
*
|
|
3198
|
-
*
|
|
3237
|
+
/** Sub-directories the root build must build itself. A package can hold more
|
|
3238
|
+
* than one project — a service worker compiled with `lib: WebWorker` that the
|
|
3239
|
+
* root tsconfig `exclude`s, or a browser client that is a package of its own
|
|
3240
|
+
* with its own build script — and the root `tsc` silently skips them.
|
|
3199
3241
|
* Signals, in order:
|
|
3200
|
-
* 1. `.vscode/tasks.json` runs tsc with `options.cwd` or `-p`
|
|
3201
|
-
* sub-dir (authoritative: it's how the developer actually
|
|
3202
|
-
*
|
|
3242
|
+
* 1. `.vscode/tasks.json` runs tsc or importgen with `options.cwd` or `-p`
|
|
3243
|
+
* pointing at a sub-dir (authoritative: it's how the developer actually
|
|
3244
|
+
* builds today),
|
|
3245
|
+
* 2. an immediate sub-directory holding its own package.json build script
|
|
3246
|
+
* or tsconfig.json.
|
|
3203
3247
|
* Returns nothing when the root tsconfig uses project `references` — `tsc -b`
|
|
3204
3248
|
* owns that build graph and shouldn't be second-guessed. */
|
|
3205
3249
|
export function detectSubProjects(cwd) {
|
|
@@ -3211,7 +3255,7 @@ export function detectSubProjects(cwd) {
|
|
|
3211
3255
|
catch {
|
|
3212
3256
|
// No root tsconfig (or unparsable) — nested ones still count
|
|
3213
3257
|
}
|
|
3214
|
-
const found = new Map(); // on-disk relative dir →
|
|
3258
|
+
const found = new Map(); // on-disk relative dir → entry
|
|
3215
3259
|
const add = (raw, reason) => {
|
|
3216
3260
|
const rel = normalizeProjectPath(raw);
|
|
3217
3261
|
if (!rel || rel.startsWith('..') || path.isAbsolute(rel))
|
|
@@ -3221,10 +3265,11 @@ export function detectSubProjects(cwd) {
|
|
|
3221
3265
|
const onDisk = resolveOnDiskCase(cwd, rel);
|
|
3222
3266
|
if (!onDisk)
|
|
3223
3267
|
return;
|
|
3224
|
-
|
|
3268
|
+
const kind = subProjectKind(cwd, onDisk);
|
|
3269
|
+
if (!kind)
|
|
3225
3270
|
return;
|
|
3226
3271
|
if (!found.has(onDisk))
|
|
3227
|
-
found.set(onDisk, reason);
|
|
3272
|
+
found.set(onDisk, { dir: onDisk, kind, reason });
|
|
3228
3273
|
};
|
|
3229
3274
|
// 1. .vscode/tasks.json — JSON5 because VS Code allows comments/trailing commas
|
|
3230
3275
|
try {
|
|
@@ -3232,37 +3277,39 @@ export function detectSubProjects(cwd) {
|
|
|
3232
3277
|
for (const task of Array.isArray(tasks?.tasks) ? tasks.tasks : []) {
|
|
3233
3278
|
const command = typeof task?.command === 'string' ? task.command : '';
|
|
3234
3279
|
const args = Array.isArray(task?.args) ? task.args.filter((a) => typeof a === 'string') : [];
|
|
3235
|
-
|
|
3280
|
+
const line = [command, ...args].join(' ');
|
|
3281
|
+
const tool = scriptRunsTsc(line) ? 'tsc' : scriptRunsImportgen(line) ? 'importgen' : null;
|
|
3282
|
+
if (!tool)
|
|
3236
3283
|
continue;
|
|
3237
|
-
const label = typeof task?.label === 'string' ? task.label :
|
|
3284
|
+
const label = typeof task?.label === 'string' ? task.label : `a ${tool} task`;
|
|
3238
3285
|
const projIdx = args.findIndex(a => a === '-p' || a === '--project');
|
|
3239
|
-
if (projIdx !== -1 && args[projIdx + 1]) {
|
|
3286
|
+
if (tool === 'tsc' && projIdx !== -1 && args[projIdx + 1]) {
|
|
3240
3287
|
add(args[projIdx + 1], `.vscode/tasks.json "${label}" builds it`);
|
|
3241
3288
|
}
|
|
3242
3289
|
else if (typeof task?.options?.cwd === 'string') {
|
|
3243
|
-
add(task.options.cwd, `.vscode/tasks.json "${label}" runs
|
|
3290
|
+
add(task.options.cwd, `.vscode/tasks.json "${label}" runs ${tool} there`);
|
|
3244
3291
|
}
|
|
3245
3292
|
}
|
|
3246
3293
|
}
|
|
3247
3294
|
catch {
|
|
3248
3295
|
// No tasks.json, or unparsable — fall through to the filesystem scan
|
|
3249
3296
|
}
|
|
3250
|
-
// 2. Immediate sub-directories with their own tsconfig.json
|
|
3297
|
+
// 2. Immediate sub-directories with their own build script or tsconfig.json
|
|
3251
3298
|
try {
|
|
3252
3299
|
for (const e of fs.readdirSync(cwd, { withFileTypes: true })) {
|
|
3253
3300
|
if (!e.isDirectory() || e.isSymbolicLink())
|
|
3254
3301
|
continue;
|
|
3255
3302
|
if (e.name.startsWith('.') || SUBPROJECT_SKIP_DIRS.has(e.name.toLowerCase()))
|
|
3256
3303
|
continue;
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3304
|
+
const kind = subProjectKind(cwd, e.name);
|
|
3305
|
+
if (kind)
|
|
3306
|
+
add(e.name, kind === 'package' ? `${e.name}/package.json has a build script` : `${e.name}/tsconfig.json`);
|
|
3260
3307
|
}
|
|
3261
3308
|
}
|
|
3262
3309
|
catch {
|
|
3263
3310
|
// Unreadable directory — nothing more to detect
|
|
3264
3311
|
}
|
|
3265
|
-
return [...found
|
|
3312
|
+
return [...found.values()];
|
|
3266
3313
|
}
|
|
3267
3314
|
/** The sub-projects to wire, honoring `.globalize.json5`: `false` disables the
|
|
3268
3315
|
* whole check, an array pins the list instead of detecting it. */
|
|
@@ -3270,9 +3317,14 @@ function configuredSubProjects(cwd, config) {
|
|
|
3270
3317
|
if (config.subProjects === false)
|
|
3271
3318
|
return [];
|
|
3272
3319
|
if (Array.isArray(config.subProjects)) {
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3320
|
+
const listed = [];
|
|
3321
|
+
for (const d of config.subProjects) {
|
|
3322
|
+
const dir = resolveOnDiskCase(cwd, normalizeProjectPath(d)) || d;
|
|
3323
|
+
const kind = subProjectKind(cwd, dir);
|
|
3324
|
+
if (kind)
|
|
3325
|
+
listed.push({ dir, kind, reason: 'listed in .globalize.json5' });
|
|
3326
|
+
}
|
|
3327
|
+
return listed;
|
|
3276
3328
|
}
|
|
3277
3329
|
return detectSubProjects(cwd);
|
|
3278
3330
|
}
|
|
@@ -3280,9 +3332,11 @@ function configuredSubProjects(cwd, config) {
|
|
|
3280
3332
|
function quoteScriptPath(p) {
|
|
3281
3333
|
return /[\s&|<>]/.test(p) ? `"${p}"` : p;
|
|
3282
3334
|
}
|
|
3283
|
-
/** Append
|
|
3284
|
-
*
|
|
3285
|
-
*
|
|
3335
|
+
/** Append a build step for every sub-project the existing build script misses:
|
|
3336
|
+
* `npm run build --prefix <dir>` for a sub-package, `tsc -p <dir>` for a plain
|
|
3337
|
+
* tsconfig. Only touches tsc-driven builds — a bundler-driven `build` has its
|
|
3338
|
+
* own idea of what to compile. Declining records `subProjects: false` in
|
|
3339
|
+
* .globalize.json5. */
|
|
3286
3340
|
export async function ensureSubProjectsInBuild(cwd) {
|
|
3287
3341
|
const config = readConfig(cwd);
|
|
3288
3342
|
if (config.subProjects === false)
|
|
@@ -3297,12 +3351,18 @@ export async function ensureSubProjectsInBuild(cwd) {
|
|
|
3297
3351
|
const buildScript = typeof pkg.scripts?.build === 'string' ? pkg.scripts.build.trim() : '';
|
|
3298
3352
|
if (!buildScript || !scriptRunsTsc(buildScript))
|
|
3299
3353
|
return;
|
|
3300
|
-
const
|
|
3301
|
-
|
|
3354
|
+
const subs = configuredSubProjects(cwd, config);
|
|
3355
|
+
// A sub-package owns its own import map: give its build script the same
|
|
3356
|
+
// importgen check the root gets, so delegating to it is enough.
|
|
3357
|
+
for (const s of subs) {
|
|
3358
|
+
if (s.kind === 'package')
|
|
3359
|
+
await ensureImportgenInBuild(path.join(cwd, s.dir));
|
|
3360
|
+
}
|
|
3361
|
+
const missing = subs.filter(s => !scriptBuildsSubProject(buildScript, s.dir));
|
|
3302
3362
|
if (missing.length === 0)
|
|
3303
3363
|
return;
|
|
3304
3364
|
const name = pkg.name || path.basename(cwd);
|
|
3305
|
-
const newScript = [buildScript, ...missing.map(
|
|
3365
|
+
const newScript = [buildScript, ...missing.map(subProjectBuildCommand)].join(' && ');
|
|
3306
3366
|
// Default to yes only when tasks.json says the developer already builds these
|
|
3307
3367
|
// — a bare nested tsconfig (tests, examples) is a weaker signal, and this
|
|
3308
3368
|
// prompt rewrites a build script that currently works.
|
|
@@ -3318,7 +3378,7 @@ export async function ensureSubProjectsInBuild(cwd) {
|
|
|
3318
3378
|
}
|
|
3319
3379
|
pkg.scripts.build = newScript;
|
|
3320
3380
|
writePackageJson(cwd, pkg);
|
|
3321
|
-
console.log(colors.green(`✓ Added ${missing.map(
|
|
3381
|
+
console.log(colors.green(`✓ Added ${missing.map(subProjectBuildCommand).join(' && ')} to ${name}'s build script`));
|
|
3322
3382
|
}
|
|
3323
3383
|
/** Freshness for the sub-projects the build script actually compiles. A
|
|
3324
3384
|
* sub-project that emits outside its own directory (a service worker with
|
|
@@ -3327,6 +3387,22 @@ export async function ensureSubProjectsInBuild(cwd) {
|
|
|
3327
3387
|
function areSubProjectsUpToDate(cwd, subs) {
|
|
3328
3388
|
for (const s of subs) {
|
|
3329
3389
|
const dir = path.join(cwd, s.dir);
|
|
3390
|
+
if (s.kind === 'package') {
|
|
3391
|
+
// Its own build script decides what it emits; judge it the way the
|
|
3392
|
+
// root is judged — sources vs output, plus the import map if it has one.
|
|
3393
|
+
if (!isBuildUpToDate(dir))
|
|
3394
|
+
return false;
|
|
3395
|
+
let subBuild = '';
|
|
3396
|
+
try {
|
|
3397
|
+
subBuild = readPackageJson(dir).scripts?.build ?? '';
|
|
3398
|
+
}
|
|
3399
|
+
catch {
|
|
3400
|
+
return false;
|
|
3401
|
+
}
|
|
3402
|
+
if (scriptRunsImportgen(subBuild) && !isImportMapUpToDate(dir))
|
|
3403
|
+
return false;
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3330
3406
|
let co;
|
|
3331
3407
|
try {
|
|
3332
3408
|
co = JSON5.parse(fs.readFileSync(path.join(dir, 'tsconfig.json'), 'utf-8')).compilerOptions || {};
|
|
@@ -3395,7 +3471,7 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
3395
3471
|
// Compile the sub-projects (service worker, …) in the same breath: the
|
|
3396
3472
|
// root tsc excludes them, so a bare "tsc" would leave them unbuilt.
|
|
3397
3473
|
const subs = configuredSubProjects(cwd, readConfig(cwd));
|
|
3398
|
-
const tscParts = ['tsc', ...subs.map(
|
|
3474
|
+
const tscParts = ['tsc', ...subs.map(subProjectBuildCommand)];
|
|
3399
3475
|
const newScript = [...(existingBuild ? [existingBuild] : []), ...tscParts].join(' && ');
|
|
3400
3476
|
console.log(colors.yellow(`TypeScript project has no "build" script in ${pkg.name || cwd}`));
|
|
3401
3477
|
for (const s of subs)
|