@bobfrankston/npmglobalize 1.0.180 → 1.0.181
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/cli.js +9 -7
- package/lib.d.ts +6 -2
- package/lib.js +35 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -427,13 +427,18 @@ Workspace mode is auto-detected when run from a root with `"private": true` and
|
|
|
427
427
|
history instead of creating a fresh repo.
|
|
428
428
|
-adopt Strict adopt: require a reachable git remote in
|
|
429
429
|
package.json.repository. Abort if probe fails. Skips prompt.
|
|
430
|
-
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
430
|
+
-strict-imports, -import-check
|
|
431
|
+
Opt in to scanning .ts/.js source for imports of packages not
|
|
432
|
+
declared in any dependencies bucket. Off by default — the
|
|
433
|
+
always-declare style this enforces doesn't hold in monorepos
|
|
434
|
+
that rely on workspace cross-refs or the -public-deps cascade
|
|
435
|
+
(those produce noisy prompts that get dismissed reflexively,
|
|
436
|
+
which defeats the safety purpose). Use only on packages where
|
|
437
|
+
every import is meant to be a direct package.json declaration.
|
|
438
|
+
When it does fire, it catches silent runtime failures —
|
|
439
|
+
ERR_MODULE_NOT_FOUND on a clean install of a published tarball
|
|
440
|
+
that was resolving via an ambient parent/global node_modules
|
|
441
|
+
at dev time.
|
|
437
442
|
-force Continue despite git errors
|
|
438
443
|
-dry-run Preview what would happen
|
|
439
444
|
-quiet Suppress npm warnings (default)
|
package/cli.js
CHANGED
|
@@ -86,10 +86,12 @@ Other Options:
|
|
|
86
86
|
in package.json.repository if reachable, else fresh init)
|
|
87
87
|
-adopt Strict adopt: require a reachable git remote in
|
|
88
88
|
package.json.repository. Abort if probe fails. Skips prompt.
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
-strict-imports, -import-check
|
|
90
|
+
Scan .ts/.js for imports not declared in any deps bucket and
|
|
91
|
+
warn before publish. Off by default — the always-declare
|
|
92
|
+
style this enforces doesn't hold for monorepos that rely on
|
|
93
|
+
workspace cross-refs / -public-deps cascade. Opt in only
|
|
94
|
+
when the assumption holds in your codebase.
|
|
93
95
|
-force Continue despite git errors
|
|
94
96
|
-dry-run Preview what would happen
|
|
95
97
|
-quiet Suppress npm warnings (default)
|
|
@@ -233,9 +235,9 @@ function parseArgs(args) {
|
|
|
233
235
|
case '-adopt':
|
|
234
236
|
options.adopt = true;
|
|
235
237
|
break;
|
|
236
|
-
case '-
|
|
237
|
-
case '-
|
|
238
|
-
options.
|
|
238
|
+
case '-strict-imports':
|
|
239
|
+
case '-import-check':
|
|
240
|
+
options.importCheck = true;
|
|
239
241
|
break;
|
|
240
242
|
case '-git':
|
|
241
243
|
i++;
|
package/lib.d.ts
CHANGED
|
@@ -119,8 +119,12 @@ export interface GlobalizeOptions {
|
|
|
119
119
|
* package.json.repository. Aborts (rather than creating a fresh repo) if
|
|
120
120
|
* the probe fails. Skips the no-git prompt. */
|
|
121
121
|
adopt?: boolean;
|
|
122
|
-
/**
|
|
123
|
-
|
|
122
|
+
/** Run the undeclared-imports scan (`-strict-imports`). Default false —
|
|
123
|
+
* the scan assumes a style where every import is declared in the
|
|
124
|
+
* consuming package's package.json, which doesn't hold for monorepos
|
|
125
|
+
* that rely on workspace cross-refs / -public-deps cascade. Opt in when
|
|
126
|
+
* the assumption holds. */
|
|
127
|
+
importCheck?: boolean;
|
|
124
128
|
/** Internal: signals this call is from workspace orchestrator */
|
|
125
129
|
/** Skip the upfront dep-graph prescan */
|
|
126
130
|
noPrescan?: boolean;
|
package/lib.js
CHANGED
|
@@ -1346,8 +1346,13 @@ function collectSourceFiles(dir, acc = [], depth = 0) {
|
|
|
1346
1346
|
}
|
|
1347
1347
|
return acc;
|
|
1348
1348
|
}
|
|
1349
|
+
/** npm package name rules (simplified): lowercase letters, digits, `-`, `_`, `.`,
|
|
1350
|
+
* optionally with an `@scope/` prefix. No spaces, no `<>`, no uppercase. */
|
|
1351
|
+
const NPM_NAME_RE = /^(@[a-z0-9_.~-]+\/)?[a-z0-9_.~-]+$/;
|
|
1349
1352
|
/** Extract the package name from an ESM/CJS module specifier.
|
|
1350
|
-
* Returns null for relative paths, absolute paths, URLs, node: builtins
|
|
1353
|
+
* Returns null for relative paths, absolute paths, URLs, node: builtins, and
|
|
1354
|
+
* anything that doesn't look like a valid npm package name (filters out
|
|
1355
|
+
* matches that landed in comments or string literals). */
|
|
1351
1356
|
function specifierToPackageName(spec) {
|
|
1352
1357
|
if (!spec)
|
|
1353
1358
|
return null;
|
|
@@ -1361,11 +1366,22 @@ function specifierToPackageName(spec) {
|
|
|
1361
1366
|
return null; // url/data
|
|
1362
1367
|
if (spec.startsWith('#'))
|
|
1363
1368
|
return null; // subpath import
|
|
1369
|
+
let candidate;
|
|
1364
1370
|
if (spec.startsWith('@')) {
|
|
1365
1371
|
const parts = spec.split('/');
|
|
1366
|
-
|
|
1372
|
+
if (parts.length < 2)
|
|
1373
|
+
return null;
|
|
1374
|
+
candidate = `${parts[0]}/${parts[1]}`;
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
candidate = spec.split('/')[0];
|
|
1367
1378
|
}
|
|
1368
|
-
|
|
1379
|
+
// Reject anything that isn't a plausible npm name — this filters out
|
|
1380
|
+
// matches inside string literals / comments like "Bob <bob@gmail.com>"
|
|
1381
|
+
// or "All Inboxes" that slipped through the regex.
|
|
1382
|
+
if (!NPM_NAME_RE.test(candidate))
|
|
1383
|
+
return null;
|
|
1384
|
+
return candidate;
|
|
1369
1385
|
}
|
|
1370
1386
|
const NODE_BUILTIN_NAMES = new Set(builtinModules);
|
|
1371
1387
|
export function findUndeclaredImports(cwd, pkg) {
|
|
@@ -1377,14 +1393,17 @@ export function findUndeclaredImports(cwd, pkg) {
|
|
|
1377
1393
|
declared.add(n);
|
|
1378
1394
|
}
|
|
1379
1395
|
const selfName = pkg.name;
|
|
1380
|
-
//
|
|
1381
|
-
//
|
|
1382
|
-
//
|
|
1383
|
-
|
|
1384
|
-
|
|
1396
|
+
// Anchored to line start (with optional leading whitespace) so that a
|
|
1397
|
+
// comment like `// imported from "Bob <bob@gmail.com>"` or an inline
|
|
1398
|
+
// string literal `"received from 'x'"` can't trigger the regex. This is
|
|
1399
|
+
// intentionally stricter than full JS parsing — minified one-line imports
|
|
1400
|
+
// are missed, but those are vendor bundles where false negatives are far
|
|
1401
|
+
// better than the false-positive noise.
|
|
1402
|
+
const IMPORT_FROM_RE = /^\s*(?:import|export)\s(?:[^'"`;]+\s)?from\s+['"]([^'"]+)['"]/g;
|
|
1403
|
+
const IMPORT_BARE_RE = /^\s*import\s+['"]([^'"]+)['"]/g;
|
|
1385
1404
|
const DYNAMIC_RE = /(?:^|[^.\w$])import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1386
1405
|
const REQUIRE_RE = /(?:^|[^.\w$])require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1387
|
-
const PATTERNS = [
|
|
1406
|
+
const PATTERNS = [IMPORT_FROM_RE, IMPORT_BARE_RE, DYNAMIC_RE, REQUIRE_RE];
|
|
1388
1407
|
const findings = new Map();
|
|
1389
1408
|
const files = collectSourceFiles(cwd);
|
|
1390
1409
|
for (const f of files) {
|
|
@@ -3536,7 +3555,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3536
3555
|
const { bump = 'patch', noPublish = false, cleanup = false, install = false, link = false, wsl = false, force = false, files = true, dryRun = false, quiet = true, verbose = false, init = false, gitVisibility = 'private', npmVisibility = 'private', message, conform = false, asis = false, updateDeps = false, updateMajor = false, publishDeps = true, // Default to publishing deps for safety
|
|
3537
3556
|
publishDepsYes = false, // -pd: auto-yes to dep-cascade prompts (private only)
|
|
3538
3557
|
publicDeps = false, // -public-deps: cascade public visibility to all deps
|
|
3539
|
-
noPrescan = false, forcePublish = false, fix = true, fixTags = false, rebase = false, show = false, local = false, freeze = false, usePaths = true, allowTs, adopt = false,
|
|
3558
|
+
noPrescan = false, forcePublish = false, fix = true, fixTags = false, rebase = false, show = false, local = false, freeze = false, usePaths = true, allowTs, adopt = false, importCheck = false } = options;
|
|
3540
3559
|
// Show tool version only for recursive dep calls (CLI already prints it at startup)
|
|
3541
3560
|
const toolVersion = getToolVersion();
|
|
3542
3561
|
if (!options._fromWorkspace && !options._fromCli) {
|
|
@@ -3680,10 +3699,12 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3680
3699
|
}
|
|
3681
3700
|
}
|
|
3682
3701
|
// Source-import scan — catches imports of packages not declared in
|
|
3683
|
-
// dependencies/devDependencies/peer/optional.
|
|
3684
|
-
//
|
|
3685
|
-
//
|
|
3686
|
-
|
|
3702
|
+
// dependencies/devDependencies/peer/optional. Opt-in via -strict-imports
|
|
3703
|
+
// because the always-declare style this check enforces doesn't hold in
|
|
3704
|
+
// monorepos that rely on workspace cross-refs / -public-deps. Only at the
|
|
3705
|
+
// top level (deps in the cascade will be scanned when their own
|
|
3706
|
+
// globalize() runs).
|
|
3707
|
+
if (importCheck && !cleanup && !options._fromDep && !options._fromWorkspace) {
|
|
3687
3708
|
const scanPkg = readPackageJson(cwd);
|
|
3688
3709
|
const undeclared = findUndeclaredImports(cwd, scanPkg);
|
|
3689
3710
|
if (undeclared.length > 0) {
|