@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 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
- -no-import-check, -no-imports
431
- Skip the undeclared-imports scan. By default, npmglobalize
432
- scans .ts/.js source for imports of packages not declared in
433
- any dependencies bucket and warns before publishing. The scan
434
- catches silent runtime failures (ERR_MODULE_NOT_FOUND on a
435
- clean install) where the import was resolving via a global or
436
- parent node_modules at dev time but won't on the target host.
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
- -no-import-check
90
- Skip the undeclared-imports scan. Default: scan runs and
91
- warns about packages imported in .ts/.js but missing from
92
- any dependencies bucket (catches silent runtime failures).
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 '-no-import-check':
237
- case '-no-imports':
238
- options.noImportCheck = true;
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
- /** Skip the undeclared-imports scan. Default false (scan runs). */
123
- noImportCheck?: boolean;
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
- return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
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
- return spec.split('/')[0];
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
- // One specifier per line is the common case; a few patterns cover the rest.
1381
- // We process line-by-line so we can report file:line and avoid pathological
1382
- // backtracking on huge files.
1383
- const FROM_RE = /\bfrom\s+['"]([^'"]+)['"]/g;
1384
- const BARE_IMPORT_RE = /(?:^|;|\s)import\s+['"]([^'"]+)['"]/g;
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 = [FROM_RE, BARE_IMPORT_RE, DYNAMIC_RE, REQUIRE_RE];
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, noImportCheck = false } = options;
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. Runs by default; skip with
3684
- // -no-import-check. Only at the top level (deps in the cascade will be
3685
- // scanned when their own globalize() runs).
3686
- if (!noImportCheck && !cleanup && !options._fromDep && !options._fromWorkspace) {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.180",
3
+ "version": "1.0.181",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",