@bobfrankston/npmglobalize 1.0.187 → 1.0.189
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/lib.js +115 -53
- package/package.json +9 -9
package/lib.js
CHANGED
|
@@ -1975,71 +1975,132 @@ function insertTypesIntoTsconfig(text, typesArr) {
|
|
|
1975
1975
|
// Inline single-line object: `{ "target": ... }`
|
|
1976
1976
|
return text.slice(0, braceEnd) + ` "types": ${arrLiteral},` + text.slice(braceEnd);
|
|
1977
1977
|
}
|
|
1978
|
+
/** `@types/*` packages that inject *global* declarations (usable with no
|
|
1979
|
+
* `import`). TS6 stopped auto-including these, so under TS6+ they must be named
|
|
1980
|
+
* in `compilerOptions.types`. Module-only types (`express`, `ws`, `nodemailer`,
|
|
1981
|
+
* ...) resolve through ordinary import resolution and never need listing — so we
|
|
1982
|
+
* deliberately do NOT enumerate every installed `@types/*`. `node` is always
|
|
1983
|
+
* added; the rest below are added only when actually installed. */
|
|
1984
|
+
const GLOBAL_AUGMENTING_TYPES = ['jest', 'mocha', 'jasmine', 'bun'];
|
|
1985
|
+
/** True if `@types/<name>` is resolvable from `start` or any ancestor's
|
|
1986
|
+
* `node_modules` — handles npm-workspace hoisting where `@types` live at the
|
|
1987
|
+
* monorepo root rather than the leaf package. */
|
|
1988
|
+
function atTypesInstalled(start, name) {
|
|
1989
|
+
let dir = path.resolve(start);
|
|
1990
|
+
for (;;) {
|
|
1991
|
+
if (fs.existsSync(path.join(dir, 'node_modules', '@types', name)))
|
|
1992
|
+
return true;
|
|
1993
|
+
const parent = path.dirname(dir);
|
|
1994
|
+
if (parent === dir)
|
|
1995
|
+
return false;
|
|
1996
|
+
dir = parent;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
/** Resolve a tsconfig's `extends` chain into an ordered list (the file itself
|
|
2000
|
+
* first, then each base it extends). Local (non-`node_modules`) entries are
|
|
2001
|
+
* marked `writable`. `truncated` is true when an `extends` could not be read or
|
|
2002
|
+
* resolved (e.g. a package specifier or an array) — the caller then can't be
|
|
2003
|
+
* sure an inherited `types` doesn't exist, so it stays its hand. Cycle- and
|
|
2004
|
+
* depth-guarded. */
|
|
2005
|
+
function resolveTsconfigChain(tsconfigPath) {
|
|
2006
|
+
const chain = [];
|
|
2007
|
+
const seen = new Set();
|
|
2008
|
+
let current = path.resolve(tsconfigPath);
|
|
2009
|
+
let truncated = false;
|
|
2010
|
+
for (let depth = 0; current && !seen.has(current) && depth < 16; depth++) {
|
|
2011
|
+
seen.add(current);
|
|
2012
|
+
let parsed;
|
|
2013
|
+
try {
|
|
2014
|
+
parsed = JSON5.parse(fs.readFileSync(current, 'utf-8'));
|
|
2015
|
+
}
|
|
2016
|
+
catch {
|
|
2017
|
+
truncated = true;
|
|
2018
|
+
break;
|
|
2019
|
+
}
|
|
2020
|
+
chain.push({ path: current, parsed, writable: !/[\\/]node_modules[\\/]/.test(current) });
|
|
2021
|
+
const ext = parsed?.extends;
|
|
2022
|
+
if (ext === undefined)
|
|
2023
|
+
break; // natural end of chain
|
|
2024
|
+
if (typeof ext !== 'string') {
|
|
2025
|
+
truncated = true;
|
|
2026
|
+
break;
|
|
2027
|
+
} // array/object — not followed
|
|
2028
|
+
// Resolve relative to the current file's directory. A bare/extensionless
|
|
2029
|
+
// value may name a directory (→ tsconfig.json) or an extensionless file.
|
|
2030
|
+
const baseDir = path.dirname(current);
|
|
2031
|
+
let next;
|
|
2032
|
+
if (ext.endsWith('.json'))
|
|
2033
|
+
next = path.resolve(baseDir, ext);
|
|
2034
|
+
else {
|
|
2035
|
+
const asDir = path.resolve(baseDir, ext, 'tsconfig.json');
|
|
2036
|
+
next = fs.existsSync(asDir) ? asDir : path.resolve(baseDir, ext + '.json');
|
|
2037
|
+
}
|
|
2038
|
+
if (!fs.existsSync(next)) {
|
|
2039
|
+
truncated = true;
|
|
2040
|
+
break;
|
|
2041
|
+
} // e.g. a node_modules package specifier
|
|
2042
|
+
current = next;
|
|
2043
|
+
}
|
|
2044
|
+
return { chain, truncated };
|
|
2045
|
+
}
|
|
1978
2046
|
/** TypeScript 6 dropped the legacy behavior of auto-including every installed
|
|
1979
|
-
* `@types/*` package. A tsconfig with no explicit `compilerOptions.types`
|
|
1980
|
-
* loses the Node globals (`process`, `Buffer`, ...), breaking the build with
|
|
1981
|
-
* `TS2591`. When building under TS6+, add an explicit `types` list
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
2047
|
+
* `@types/*` package globally. A tsconfig with no explicit `compilerOptions.types`
|
|
2048
|
+
* then loses the Node globals (`process`, `Buffer`, ...), breaking the build with
|
|
2049
|
+
* `TS2591`. When building under TS6+, add an explicit, *minimal* `types` list
|
|
2050
|
+
* (Node plus any installed global-augmenting test types) to restore them.
|
|
2051
|
+
*
|
|
2052
|
+
* Generalized for monorepos: it resolves the full `extends` chain, respects a
|
|
2053
|
+
* `types` set anywhere in that chain, finds `@types/node` even when hoisted to a
|
|
2054
|
+
* workspace-root `node_modules`, and writes the patch to the *base-most writable*
|
|
2055
|
+
* config so one edit fixes every package that shares the base (falling back to
|
|
2056
|
+
* the leaf config when the base lives in `node_modules`). Idempotent and
|
|
2057
|
+
* conservative — bails when the chain can't be fully resolved (an inherited
|
|
2058
|
+
* `types` might be hiding) or when `@types/node` isn't installed. */
|
|
1985
2059
|
function ensureTsconfigNodeTypes(cwd) {
|
|
1986
2060
|
if (getTscMajor() < 6)
|
|
1987
2061
|
return;
|
|
1988
2062
|
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
1989
|
-
|
|
1990
|
-
try {
|
|
1991
|
-
text = fs.readFileSync(tsconfigPath, 'utf-8');
|
|
1992
|
-
}
|
|
1993
|
-
catch {
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
let parsed;
|
|
1997
|
-
try {
|
|
1998
|
-
parsed = JSON5.parse(text);
|
|
1999
|
-
}
|
|
2000
|
-
catch {
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
const co = parsed?.compilerOptions;
|
|
2004
|
-
if (!co || typeof co !== 'object')
|
|
2005
|
-
return;
|
|
2006
|
-
// Respect any explicit choice; never override a user-set `types`.
|
|
2007
|
-
if (co.types !== undefined)
|
|
2063
|
+
if (!fs.existsSync(tsconfigPath))
|
|
2008
2064
|
return;
|
|
2009
|
-
|
|
2010
|
-
if (
|
|
2065
|
+
const { chain, truncated } = resolveTsconfigChain(tsconfigPath);
|
|
2066
|
+
if (!chain.length)
|
|
2011
2067
|
return;
|
|
2012
|
-
//
|
|
2013
|
-
|
|
2014
|
-
const typesDir = path.join(cwd, 'node_modules', '@types');
|
|
2015
|
-
if (!fs.existsSync(path.join(typesDir, 'node')))
|
|
2068
|
+
// Respect an explicit `types` anywhere in the resolved chain.
|
|
2069
|
+
if (chain.some(c => c.parsed?.compilerOptions?.types !== undefined))
|
|
2016
2070
|
return;
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
installed = fs.readdirSync(typesDir, { withFileTypes: true })
|
|
2020
|
-
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
|
2021
|
-
.map(e => e.name);
|
|
2022
|
-
}
|
|
2023
|
-
catch {
|
|
2071
|
+
// Couldn't fully resolve the chain → a hidden base may set `types`; don't risk it.
|
|
2072
|
+
if (truncated)
|
|
2024
2073
|
return;
|
|
2025
|
-
|
|
2026
|
-
if (!
|
|
2074
|
+
// Only genuine Node projects, incl. workspace-hoisted @types at the repo root.
|
|
2075
|
+
if (!atTypesInstalled(cwd, 'node'))
|
|
2027
2076
|
return;
|
|
2028
|
-
//
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2077
|
+
// Minimal correct list: Node globals + any installed global-augmenting types.
|
|
2078
|
+
const typesArr = ['node', ...GLOBAL_AUGMENTING_TYPES.filter(n => atTypesInstalled(cwd, n))];
|
|
2079
|
+
// Prefer the base-most writable config (DRY: one edit fixes the whole workspace),
|
|
2080
|
+
// then walk toward the leaf, patching the first whose compilerOptions we can edit.
|
|
2081
|
+
const candidates = chain.filter(c => c.writable).reverse();
|
|
2082
|
+
for (const cand of candidates) {
|
|
2083
|
+
let text;
|
|
2084
|
+
try {
|
|
2085
|
+
text = fs.readFileSync(cand.path, 'utf-8');
|
|
2086
|
+
}
|
|
2087
|
+
catch {
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2090
|
+
const patched = insertTypesIntoTsconfig(text, typesArr);
|
|
2091
|
+
if (!patched)
|
|
2092
|
+
continue;
|
|
2093
|
+
try {
|
|
2094
|
+
fs.writeFileSync(cand.path, patched);
|
|
2095
|
+
const where = path.relative(cwd, cand.path) || 'tsconfig.json';
|
|
2096
|
+
console.log(colors.cyan(` Patched ${where}: added "types": [${typesArr.join(', ')}] (TypeScript ${_tscMajor}+ no longer auto-includes @types/*)`));
|
|
2097
|
+
}
|
|
2098
|
+
catch (error) {
|
|
2099
|
+
console.error(colors.yellow(` Could not write tsconfig patch (${cand.path}): ${error.message}`));
|
|
2100
|
+
}
|
|
2034
2101
|
return;
|
|
2035
2102
|
}
|
|
2036
|
-
|
|
2037
|
-
fs.writeFileSync(tsconfigPath, patched);
|
|
2038
|
-
console.log(colors.cyan(` Patched tsconfig.json: added "types": [${typesArr.join(', ')}] (TypeScript ${_tscMajor}+ no longer auto-includes @types/*)`));
|
|
2039
|
-
}
|
|
2040
|
-
catch (error) {
|
|
2041
|
-
console.error(colors.yellow(` Could not write tsconfig.json patch: ${error.message}`));
|
|
2042
|
-
}
|
|
2103
|
+
recordBuildIssue(chain[0].parsed?.name || path.basename(cwd), 'warning', `TS6+ build may fail: no writable compilerOptions block in the tsconfig chain of ${cwd} to add "types"`);
|
|
2043
2104
|
}
|
|
2044
2105
|
/** Build a single project: detect tsconfig, prompt to add `build: tsc` if a
|
|
2045
2106
|
* TypeScript project lacks a build script, run `npm run build`, record
|
|
@@ -2433,6 +2494,7 @@ function diagnoseBuildFailure(errorText, cwd) {
|
|
|
2433
2494
|
if (!diagnosed && /error TS2591|Do you need to install type definitions for node/.test(errorText)) {
|
|
2434
2495
|
console.error(colors.yellow('\n Hint: TypeScript 6 no longer auto-includes @types/*, so Node globals (process, Buffer) are unresolved.'));
|
|
2435
2496
|
console.error(colors.yellow(` Fix: add "types": ["node"] to compilerOptions in ${path.join(cwd, 'tsconfig.json')}`));
|
|
2497
|
+
console.error(colors.yellow(' In a workspace this belongs in the shared base tsconfig the package extends (only global @types like node/jest need listing; imported types resolve on their own).'));
|
|
2436
2498
|
diagnosed = true;
|
|
2437
2499
|
}
|
|
2438
2500
|
// Pattern: TypeScript compilation error
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/npmglobalize",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.189",
|
|
4
4
|
"description": "Transform file: dependencies to npm versions for publishing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"url": "https://github.com/BobFrankston/npmglobalize.git"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bobfrankston/freezepak": "^0.1.
|
|
35
|
-
"@bobfrankston/importgen": "^0.1.
|
|
36
|
-
"@bobfrankston/themecolors": "^0.1.
|
|
37
|
-
"@bobfrankston/userconfig": "^1.0.
|
|
34
|
+
"@bobfrankston/freezepak": "^0.1.9",
|
|
35
|
+
"@bobfrankston/importgen": "^0.1.38",
|
|
36
|
+
"@bobfrankston/themecolors": "^0.1.8",
|
|
37
|
+
"@bobfrankston/userconfig": "^1.0.10",
|
|
38
38
|
"@npmcli/package-json": "^7.0.4",
|
|
39
39
|
"json5": "^2.2.3",
|
|
40
40
|
"libnpmversion": "^8.0.3",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
},
|
|
60
60
|
".transformedSnapshot": {
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@bobfrankston/freezepak": "^0.1.
|
|
63
|
-
"@bobfrankston/importgen": "^0.1.
|
|
64
|
-
"@bobfrankston/themecolors": "^0.1.
|
|
65
|
-
"@bobfrankston/userconfig": "^1.0.
|
|
62
|
+
"@bobfrankston/freezepak": "^0.1.9",
|
|
63
|
+
"@bobfrankston/importgen": "^0.1.38",
|
|
64
|
+
"@bobfrankston/themecolors": "^0.1.8",
|
|
65
|
+
"@bobfrankston/userconfig": "^1.0.10",
|
|
66
66
|
"@npmcli/package-json": "^7.0.4",
|
|
67
67
|
"json5": "^2.2.3",
|
|
68
68
|
"libnpmversion": "^8.0.3",
|