@bobfrankston/npmglobalize 1.0.205 → 1.0.206
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.d.ts +10 -0
- package/lib.js +105 -1
- package/package.json +3 -3
package/lib.d.ts
CHANGED
|
@@ -25,6 +25,16 @@ export declare function clearBuildIssues(): void;
|
|
|
25
25
|
/** Extract the first TypeScript error line from build output for the summary.
|
|
26
26
|
* Returns a short string like "file.ts(42,5): error TS2339: Property 'foo' ..." */
|
|
27
27
|
export declare function extractFirstTscError(output: string): string | null;
|
|
28
|
+
/** A TS7016 — "Could not find a declaration file for module 'X'" — that tsc blames
|
|
29
|
+
* on the file being compiled is frequently not that file's fault: the copy of X in
|
|
30
|
+
* node_modules carries no `.d.ts` whatsoever. That happens when X was published at
|
|
31
|
+
* a moment its declaration output was not on disk, so the tarball ships JS only and
|
|
32
|
+
* every consumer resolving X through the registry fails identically. tsc's stock
|
|
33
|
+
* advice — `npm i --save-dev @types/bobfrankston__hlib` — then sends the user after
|
|
34
|
+
* a types package that does not and will never exist, which is worse than no advice.
|
|
35
|
+
* Recognize the shape and say what actually fixes it.
|
|
36
|
+
* Returns one diagnosis line per untyped module; empty when TS7016 has another cause. */
|
|
37
|
+
export declare function diagnoseUntypedDeps(cwd: string, buildOutput: string): string[];
|
|
28
38
|
/** One package that depends on this one, recorded in this package's
|
|
29
39
|
* .globalize.json5 when that package publishes. */
|
|
30
40
|
export interface UpstreamEntry {
|
package/lib.js
CHANGED
|
@@ -79,6 +79,97 @@ export function extractFirstTscError(output) {
|
|
|
79
79
|
}
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
|
+
/** Does `dir` contain any `.d.ts` at all? Bounded, and never descends into a
|
|
83
|
+
* nested `node_modules` — we are asking about this package's own output. */
|
|
84
|
+
function hasDeclarationFiles(dir, depth = 3) {
|
|
85
|
+
let entries;
|
|
86
|
+
try {
|
|
87
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
for (const e of entries) {
|
|
93
|
+
if (e.isFile() && e.name.endsWith('.d.ts'))
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (depth <= 0)
|
|
97
|
+
return false;
|
|
98
|
+
for (const e of entries) {
|
|
99
|
+
if (!e.isDirectory() || e.name === 'node_modules' || e.name.startsWith('.'))
|
|
100
|
+
continue;
|
|
101
|
+
if (hasDeclarationFiles(path.join(dir, e.name), depth - 1))
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
/** The spec a consumer declares for `name`. `.dependencies` is checked first
|
|
107
|
+
* because during a publish the live `file:` paths live there while
|
|
108
|
+
* `dependencies` temporarily holds the npm refs that were swapped in. */
|
|
109
|
+
function declaredDepSpec(pkg, name) {
|
|
110
|
+
for (const bucket of ['.dependencies', 'dependencies', 'devDependencies']) {
|
|
111
|
+
const spec = pkg?.[bucket]?.[name];
|
|
112
|
+
if (typeof spec === 'string')
|
|
113
|
+
return spec;
|
|
114
|
+
}
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
/** A TS7016 — "Could not find a declaration file for module 'X'" — that tsc blames
|
|
118
|
+
* on the file being compiled is frequently not that file's fault: the copy of X in
|
|
119
|
+
* node_modules carries no `.d.ts` whatsoever. That happens when X was published at
|
|
120
|
+
* a moment its declaration output was not on disk, so the tarball ships JS only and
|
|
121
|
+
* every consumer resolving X through the registry fails identically. tsc's stock
|
|
122
|
+
* advice — `npm i --save-dev @types/bobfrankston__hlib` — then sends the user after
|
|
123
|
+
* a types package that does not and will never exist, which is worse than no advice.
|
|
124
|
+
* Recognize the shape and say what actually fixes it.
|
|
125
|
+
* Returns one diagnosis line per untyped module; empty when TS7016 has another cause. */
|
|
126
|
+
export function diagnoseUntypedDeps(cwd, buildOutput) {
|
|
127
|
+
if (!buildOutput)
|
|
128
|
+
return [];
|
|
129
|
+
const named = new Set();
|
|
130
|
+
const re = /error TS7016: Could not find a declaration file for module '([^']+)'/g;
|
|
131
|
+
for (let m = re.exec(buildOutput); m; m = re.exec(buildOutput))
|
|
132
|
+
named.add(m[1]);
|
|
133
|
+
if (!named.size)
|
|
134
|
+
return [];
|
|
135
|
+
let consumer = {};
|
|
136
|
+
try {
|
|
137
|
+
consumer = readPackageJson(cwd);
|
|
138
|
+
}
|
|
139
|
+
catch { /* an unreadable consumer still leaves the dep diagnosable */ }
|
|
140
|
+
const scopeOf = (n) => n.startsWith('@') && n.includes('/') ? n.slice(0, n.indexOf('/')) : '';
|
|
141
|
+
const lines = [];
|
|
142
|
+
for (const name of named) {
|
|
143
|
+
if (name.startsWith('.'))
|
|
144
|
+
continue; // a relative import — a code bug, not a packaging one
|
|
145
|
+
const installed = path.join(cwd, 'node_modules', ...name.split('/'));
|
|
146
|
+
if (!fs.existsSync(path.join(installed, 'package.json')))
|
|
147
|
+
continue;
|
|
148
|
+
if (hasDeclarationFiles(installed))
|
|
149
|
+
continue; // declarations are present; TS7016 came from something else
|
|
150
|
+
let version = '';
|
|
151
|
+
try {
|
|
152
|
+
version = readPackageJson(installed).version || '';
|
|
153
|
+
}
|
|
154
|
+
catch { /* version is decoration here */ }
|
|
155
|
+
const at = version ? `@${version}` : '';
|
|
156
|
+
// A `file:` spec points at live source we can look at directly, which tells
|
|
157
|
+
// us whether the missing declarations are a build problem or a stale install.
|
|
158
|
+
const spec = declaredDepSpec(consumer, name);
|
|
159
|
+
const source = spec.startsWith('file:') ? path.resolve(cwd, spec.slice('file:'.length)) : '';
|
|
160
|
+
if (source && fs.existsSync(source)) {
|
|
161
|
+
lines.push(hasDeclarationFiles(source)
|
|
162
|
+
? `${name} resolves to a copy in node_modules with no .d.ts, but its source at ${source} has them — the install is stale. Run \`npm install\` in ${cwd}.`
|
|
163
|
+
: `${name}'s source at ${source} emits no .d.ts — set "declaration": true in its tsconfig and rebuild it.`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const ownScope = scopeOf(name) && scopeOf(name) === scopeOf(consumer?.name || '');
|
|
167
|
+
lines.push(ownScope
|
|
168
|
+
? `${name}${at} was published with no .d.ts in the tarball, so every consumer that installs it from npm fails this way — it is not a problem in ${consumer?.name || 'this package'}. Rebuild and republish ${name} (\`npmglobalize\` in its source directory), or depend on its source with a file: path. Ignore tsc's @types/… suggestion; no such package exists.`
|
|
169
|
+
: `${name}${at} ships no type declarations. Install its @types package if one exists, or add a .d.ts declaring the module.`);
|
|
170
|
+
}
|
|
171
|
+
return lines;
|
|
172
|
+
}
|
|
82
173
|
/**
|
|
83
174
|
* Remove 'nul' files from a directory tree (Windows reserved name issue).
|
|
84
175
|
* These files break git and npm on Windows. Uses \\?\ prefix to bypass name validation.
|
|
@@ -3218,8 +3309,21 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
3218
3309
|
if (buildOutput)
|
|
3219
3310
|
console.error(buildOutput);
|
|
3220
3311
|
console.error(colors.red(`Build failed in ${pkg.name || cwd}`));
|
|
3312
|
+
const label = pkg.name || path.basename(cwd);
|
|
3313
|
+
// tsc reports a missing-declarations failure against the file that imports the
|
|
3314
|
+
// dep, and suggests an @types package that does not exist for private scopes.
|
|
3315
|
+
// When the real cause is a dep installed without any .d.ts, say so plainly and
|
|
3316
|
+
// put THAT in the summary — the raw tsc line is already echoed above.
|
|
3317
|
+
const untyped = diagnoseUntypedDeps(cwd, buildOutput);
|
|
3318
|
+
if (untyped.length) {
|
|
3319
|
+
for (const line of untyped) {
|
|
3320
|
+
console.error(colors.yellow(` ${line}`));
|
|
3321
|
+
recordBuildIssue(label, 'error', line);
|
|
3322
|
+
}
|
|
3323
|
+
return false;
|
|
3324
|
+
}
|
|
3221
3325
|
const firstErr = extractFirstTscError(buildOutput);
|
|
3222
|
-
recordBuildIssue(
|
|
3326
|
+
recordBuildIssue(label, 'error', firstErr || 'Build failed');
|
|
3223
3327
|
return false;
|
|
3224
3328
|
}
|
|
3225
3329
|
/** Walk `file:` deps depth-first (deps before consumers) and build each one
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/npmglobalize",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.206",
|
|
4
4
|
"description": "Transform file: dependencies to npm versions for publishing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@bobfrankston/freezepak": "^0.1.9",
|
|
35
35
|
"@bobfrankston/importgen": "^0.1.40",
|
|
36
36
|
"@bobfrankston/themecolors": "^0.1.8",
|
|
37
|
-
"@bobfrankston/userconfig": "^1.0.
|
|
37
|
+
"@bobfrankston/userconfig": "^1.0.11",
|
|
38
38
|
"@npmcli/package-json": "^7.0.4",
|
|
39
39
|
"json5": "^2.2.3",
|
|
40
40
|
"libnpmversion": "^8.0.3",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@bobfrankston/freezepak": "^0.1.9",
|
|
63
63
|
"@bobfrankston/importgen": "^0.1.40",
|
|
64
64
|
"@bobfrankston/themecolors": "^0.1.8",
|
|
65
|
-
"@bobfrankston/userconfig": "^1.0.
|
|
65
|
+
"@bobfrankston/userconfig": "^1.0.11",
|
|
66
66
|
"@npmcli/package-json": "^7.0.4",
|
|
67
67
|
"json5": "^2.2.3",
|
|
68
68
|
"libnpmversion": "^8.0.3",
|