@bobfrankston/npmglobalize 1.0.189 → 1.0.190

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.
Files changed (3) hide show
  1. package/cli.js +13 -1
  2. package/lib.js +81 -8
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -8,6 +8,18 @@ import path from 'path';
8
8
  import { styleText } from 'util';
9
9
  // npmglobalize install directory (for --version)
10
10
  const __dirname = import.meta.dirname;
11
+ /** Own version, read once. Stamped into the startup banner AND the Issues Summary
12
+ * so a failure log always reveals which installed npmglobalize produced it — a
13
+ * build that fails on something the source already fixes usually just means the
14
+ * installed copy predates the fix. */
15
+ const NPMGLOBALIZE_VERSION = (() => {
16
+ try {
17
+ return JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8')).version;
18
+ }
19
+ catch {
20
+ return 'unknown';
21
+ }
22
+ })();
11
23
  function printHelp() {
12
24
  console.log(`
13
25
  npmglobalize - Transform file: dependencies to npm versions for publishing
@@ -417,7 +429,7 @@ function printBuildSummary() {
417
429
  if (issues.length === 0)
418
430
  return;
419
431
  console.log('');
420
- console.log(styleText('yellow', '━━━ Issues Summary ━━━━━━━━━━━━━━━━━━━━'));
432
+ console.log(styleText('yellow', `━━━ Issues Summary (npmglobalize v${NPMGLOBALIZE_VERSION}) ━━━`));
421
433
  for (const issue of issues) {
422
434
  const icon = issue.severity === 'error' ? '✗' : '⚠';
423
435
  console.log(styleText('yellow', ` ${icon} ${issue.module}: ${issue.message}`));
package/lib.js CHANGED
@@ -1947,12 +1947,12 @@ function getTscMajor() {
1947
1947
  catch { /* leave at 0 — can't probe, assume pre-6 (no patch) */ }
1948
1948
  return _tscMajor;
1949
1949
  }
1950
- /** Insert a `"types": [...]` line as the first property of `compilerOptions`,
1950
+ /** Insert `"<key>": <valueLiteral>` as the first property of `compilerOptions`,
1951
1951
  * preserving the file's existing formatting/comments. Returns the patched text,
1952
1952
  * or null if the object couldn't be located safely (caller leaves file alone).
1953
- * Only ever called when `compilerOptions.types` is absent. */
1954
- function insertTypesIntoTsconfig(text, typesArr) {
1955
- const arrLiteral = '[' + typesArr.map(t => JSON.stringify(t)).join(', ') + ']';
1953
+ * Only ever called when the key is known to be absent. */
1954
+ function insertCompilerOption(text, key, valueLiteral) {
1955
+ const prop = `"${key}": ${valueLiteral}`;
1956
1956
  const m = text.match(/"compilerOptions"\s*:\s*\{/);
1957
1957
  if (!m)
1958
1958
  return null;
@@ -1965,15 +1965,20 @@ function insertTypesIntoTsconfig(text, typesArr) {
1965
1965
  const lineStart = text.lastIndexOf('\n', m.index) + 1;
1966
1966
  const baseIndent = text.slice(lineStart, m.index).match(/^\s*/)[0];
1967
1967
  const propIndent = baseIndent + ' ';
1968
- return text.slice(0, braceEnd) + '\n' + propIndent + `"types": ${arrLiteral}\n` + baseIndent + text.slice(braceEnd + ws.length);
1968
+ return text.slice(0, braceEnd) + '\n' + propIndent + prop + '\n' + baseIndent + text.slice(braceEnd + ws.length);
1969
1969
  }
1970
1970
  // Multiline object: derive property indent from the existing first property.
1971
1971
  if (ws.includes('\n')) {
1972
1972
  const propIndent = ws.slice(ws.lastIndexOf('\n') + 1);
1973
- return text.slice(0, braceEnd) + '\n' + propIndent + `"types": ${arrLiteral},` + text.slice(braceEnd);
1973
+ return text.slice(0, braceEnd) + '\n' + propIndent + prop + ',' + text.slice(braceEnd);
1974
1974
  }
1975
1975
  // Inline single-line object: `{ "target": ... }`
1976
- return text.slice(0, braceEnd) + ` "types": ${arrLiteral},` + text.slice(braceEnd);
1976
+ return text.slice(0, braceEnd) + ` ${prop},` + text.slice(braceEnd);
1977
+ }
1978
+ /** Insert a `"types": [...]` line as the first property of `compilerOptions`.
1979
+ * Only ever called when `compilerOptions.types` is absent. */
1980
+ function insertTypesIntoTsconfig(text, typesArr) {
1981
+ return insertCompilerOption(text, 'types', '[' + typesArr.map(t => JSON.stringify(t)).join(', ') + ']');
1977
1982
  }
1978
1983
  /** `@types/*` packages that inject *global* declarations (usable with no
1979
1984
  * `import`). TS6 stopped auto-including these, so under TS6+ they must be named
@@ -2102,6 +2107,55 @@ function ensureTsconfigNodeTypes(cwd) {
2102
2107
  }
2103
2108
  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"`);
2104
2109
  }
2110
+ /** TS6 turned several long-deprecated compiler options (e.g.
2111
+ * `moduleResolution: "node10"`/`"node"`/`"classic"`, old `target`s) into hard
2112
+ * errors (`TS5107`/`TS5101`) that will be *removed* in TS7. TypeScript's own
2113
+ * sanctioned bridge for the 6→7 window is `"ignoreDeprecations": "<version>"`
2114
+ * (the error text names the version, e.g. `"6.0"`). We apply exactly that so the
2115
+ * build proceeds, and record a warning so the real migration (to `node16` /
2116
+ * `nodenext` / `bundler`) still happens before TS7. We do NOT auto-rewrite
2117
+ * `moduleResolution` itself — that changes resolution semantics and must be a
2118
+ * human, per-package decision. Returns true if it patched a file. Mirrors
2119
+ * `ensureTsconfigNodeTypes`: resolves the `extends` chain, respects an existing
2120
+ * `ignoreDeprecations`, and patches the base-most writable config. */
2121
+ function ensureTsconfigIgnoreDeprecations(cwd, version) {
2122
+ const tsconfigPath = path.join(cwd, 'tsconfig.json');
2123
+ if (!fs.existsSync(tsconfigPath))
2124
+ return false;
2125
+ const { chain, truncated } = resolveTsconfigChain(tsconfigPath);
2126
+ if (!chain.length)
2127
+ return false;
2128
+ // Already set somewhere in the chain (possibly with a different version) — leave it.
2129
+ if (chain.some(c => c.parsed?.compilerOptions?.ignoreDeprecations !== undefined))
2130
+ return false;
2131
+ if (truncated)
2132
+ return false;
2133
+ const valueLiteral = JSON.stringify(version);
2134
+ const candidates = chain.filter(c => c.writable).reverse();
2135
+ for (const cand of candidates) {
2136
+ let text;
2137
+ try {
2138
+ text = fs.readFileSync(cand.path, 'utf-8');
2139
+ }
2140
+ catch {
2141
+ continue;
2142
+ }
2143
+ const patched = insertCompilerOption(text, 'ignoreDeprecations', valueLiteral);
2144
+ if (!patched)
2145
+ continue;
2146
+ try {
2147
+ fs.writeFileSync(cand.path, patched);
2148
+ const where = path.relative(cwd, cand.path) || 'tsconfig.json';
2149
+ console.log(colors.cyan(` Patched ${where}: added "ignoreDeprecations": ${valueLiteral} (silences TS6 removed-in-TS7 deprecation errors)`));
2150
+ recordBuildIssue(chain[0].parsed?.name || path.basename(cwd), 'warning', `Deprecated compilerOptions silenced via ignoreDeprecations=${version}. Migrate moduleResolution to "node16"/"nodenext"/"bundler" before TypeScript 7.`);
2151
+ return true;
2152
+ }
2153
+ catch (error) {
2154
+ console.error(colors.yellow(` Could not write tsconfig patch (${cand.path}): ${error.message}`));
2155
+ }
2156
+ }
2157
+ return false;
2158
+ }
2105
2159
  /** Build a single project: detect tsconfig, prompt to add `build: tsc` if a
2106
2160
  * TypeScript project lacks a build script, run `npm run build`, record
2107
2161
  * failures. Returns true if build succeeded (or was skipped because no
@@ -2136,7 +2190,17 @@ export async function buildProject(cwd, opts = {}) {
2136
2190
  }
2137
2191
  ensureTsconfigNodeTypes(cwd);
2138
2192
  console.log(`Building ${pkg.name || cwd}...`);
2139
- const buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
2193
+ let buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
2194
+ if (!buildResult.success) {
2195
+ // TS6 deprecation error (removed-in-TS7): apply the version TS itself names
2196
+ // in `"ignoreDeprecations": "<v>"` and retry once.
2197
+ const out = (buildResult.stderr || '') + (buildResult.output || '');
2198
+ const dep = out.match(/error TS510[17]\b/) &&
2199
+ out.match(/['"]ignoreDeprecations['"]\s*:\s*['"]([\d.]+)['"]/);
2200
+ if (dep && ensureTsconfigIgnoreDeprecations(cwd, dep[1])) {
2201
+ buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
2202
+ }
2203
+ }
2140
2204
  if (buildResult.success) {
2141
2205
  console.log(colors.green(`✓ Build succeeded (${pkg.name || path.basename(cwd)})`));
2142
2206
  return true;
@@ -2497,6 +2561,15 @@ function diagnoseBuildFailure(errorText, cwd) {
2497
2561
  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).'));
2498
2562
  diagnosed = true;
2499
2563
  }
2564
+ // Pattern: TS6 made deprecated options (moduleResolution=node10, ...) hard errors,
2565
+ // removed in TS7. (Safety net for when the auto ignoreDeprecations patch+retry didn't fire.)
2566
+ if (!diagnosed && /error TS510[17]\b/.test(errorText)) {
2567
+ const v = errorText.match(/['"]ignoreDeprecations['"]\s*:\s*['"]([\d.]+)['"]/);
2568
+ console.error(colors.yellow('\n Hint: TypeScript 6 flags long-deprecated options that TS7 will remove.'));
2569
+ console.error(colors.yellow(` Bridge: add "ignoreDeprecations": "${v ? v[1] : '6.0'}" to compilerOptions (shared base in a workspace).`));
2570
+ console.error(colors.yellow(' Real fix before TS7: migrate moduleResolution to "node16"/"nodenext"/"bundler".'));
2571
+ diagnosed = true;
2572
+ }
2500
2573
  // Pattern: TypeScript compilation error
2501
2574
  if (!diagnosed && /error TS\d+/.test(errorText)) {
2502
2575
  console.error(colors.yellow('\n Hint: TypeScript compilation errors. Fix the type errors above before publishing.'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.189",
3
+ "version": "1.0.190",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",