use crate::tests::parse_ts as parse_source;
fn relative_references(source: &str) -> Vec<String> {
parse_source(source)
.dynamic_imports
.iter()
.map(|import| import.source.clone())
.collect()
}
fn package_references(source: &str) -> Vec<String> {
parse_source(source).package_path_references.to_vec()
}
#[test]
fn global_require_resolve_references_the_file_and_the_package() {
let source = "require.resolve('./shim.js');\nrequire.resolve('some-pkg/package.json');\n";
assert_eq!(relative_references(source), ["./shim.js"]);
assert_eq!(package_references(source), ["some-pkg"]);
}
#[test]
fn module_level_create_require_still_references_the_file() {
let source = "import { createRequire } from 'node:module';\n\
const require = createRequire(import.meta.url);\n\
export const shim = require.resolve('./shim.js');\n\
export const pkg = require.resolve('some-pkg');\n";
assert_eq!(relative_references(source), ["./shim.js"]);
assert_eq!(package_references(source), ["some-pkg"]);
}
#[test]
fn a_require_parameter_shadows_the_module_require() {
let source = "export function f(require) {\n\
require.resolve('./shim.js');\n\
return require.resolve('some-pkg');\n\
}\n\
export const g = (require) => require.resolve('./arrow.js');\n\
export const h = function ({ require }) { return require.resolve('other-pkg'); };\n";
assert!(
relative_references(source).is_empty(),
"a parameter named require is not the CommonJS require"
);
assert!(
package_references(source).is_empty(),
"a parameter named require is not the CommonJS require"
);
}
#[test]
fn a_nested_declaration_shadows_the_module_require() {
let source = "export function f() {\n\
const require = makeLoader();\n\
return [require.resolve('./shim.js'), require.resolve('some-pkg')];\n\
}\n\
if (globalThis.ready) {\n\
let require = makeLoader();\n\
require.resolve('./block.js');\n\
}\n\
require.resolve('./outside.js');\n";
assert_eq!(
relative_references(source),
["./outside.js"],
"only the call outside the shadowing scopes references a file"
);
assert!(package_references(source).is_empty());
}
#[test]
fn a_package_helper_with_a_require_parameter_is_not_a_resolution_helper() {
let source = "function resolveWith(require, name) {\n\
return require.resolve(name);\n\
}\n\
resolveWith(localRequire, 'some-pkg');\n\
function resolveNested(name) {\n\
return [loader].map((require) => require.resolve(name));\n\
}\n\
resolveNested('nested-pkg');\n\
function resolvePkg(name) {\n\
return require.resolve(name);\n\
}\n\
resolvePkg('other-pkg');\n";
assert_eq!(
package_references(source),
["other-pkg"],
"a helper that calls resolve on its own require parameter resolves nothing"
);
}
#[test]
fn import_meta_resolve_references_the_package() {
let source = "export const a = import.meta.resolve('open-pkg');\n\
export const b = import.meta.resolve('@scope/data/package.json');\n\
export const c = import.meta.resolve(`tpl-pkg`);\n";
assert_eq!(
package_references(source),
["open-pkg", "@scope/data", "tpl-pkg"]
);
}
#[test]
fn a_deep_subpath_credits_the_package() {
let source = "import { createRequire } from 'node:module';\n\
const require = createRequire(import.meta.url);\n\
export const a = require.resolve('bridge-pkg/lib/tsc');\n\
export const b = require.resolve('@scope/tool/dist/cli.js');\n\
export const c = import.meta.resolve('deep-pkg/dist/x.js');\n\
export const d = import.meta.resolve('@scope/meta/sub');\n";
assert_eq!(
package_references(source),
["bridge-pkg", "@scope/tool", "deep-pkg", "@scope/meta"],
"a subpath credits the package that owns it"
);
}
#[test]
fn a_protocol_or_bare_scope_specifier_credits_nothing() {
let source = "export const a = import.meta.resolve('node:fs');\n\
export const b = require.resolve('@scope');\n\
export const c = require.resolve('@scope/');\n\
export const d = import.meta.resolve('#internal/x');\n";
assert!(
package_references(source).is_empty(),
"a protocol, a bare scope or a subpath import credits no package"
);
}
#[test]
fn a_deep_subpath_gets_no_unlisted_site() {
let source = "require.resolve('deep-pkg/lib/tsc');\n\
import.meta.resolve('@scope/meta/sub');\n";
assert!(
package_resolve_sites(source).is_empty(),
"a path alias can look like a package subpath, so only the package root is a site"
);
}
#[test]
fn a_local_meta_binding_is_not_import_meta() {
let source = "const meta = { resolve: (s) => s };\n\
export const a = meta.resolve('local-pkg');\n";
assert!(package_references(source).is_empty());
}
fn package_resolve_sites(source: &str) -> Vec<(String, u32)> {
parse_source(source).package_resolve_sites.to_vec()
}
#[test]
fn only_a_literal_resolve_call_gets_a_site() {
let source = "function packageRoot(name) {\n\
return require.resolve(`${name}/package.json`);\n\
}\n\
packageRoot('helper-pkg');\n\
const PACKAGES = ['table-pkg'];\n\
for (const name of PACKAGES) require.resolve(`${name}/package.json`);\n\
require.resolve('searched-pkg', { paths: [dir] });\n\
require.resolve('./local.js');\n\
require.resolve('direct-pkg/package.json');\n\
require.resolve(`template-pkg`);\n";
let mut credited = package_references(source);
credited.sort();
assert_eq!(
credited,
[
"direct-pkg",
"helper-pkg",
"searched-pkg",
"table-pkg",
"template-pkg"
],
"every package name credits the dependency"
);
let direct_start = u32::try_from(source.find("require.resolve('direct-pkg").unwrap()).unwrap();
let template_start =
u32::try_from(source.find("require.resolve(`template-pkg").unwrap()).unwrap();
assert_eq!(
package_resolve_sites(source),
[
("direct-pkg".to_string(), direct_start),
("template-pkg".to_string(), template_start),
],
"only a direct call with one static argument has a source site"
);
}
#[test]
fn a_shadowed_require_resolve_gets_no_site() {
let source = "function load(require) {\n\
return require.resolve('shadowed-pkg');\n\
}\n";
assert!(package_resolve_sites(source).is_empty());
assert_eq!(
package_resolve_sites("require.resolve('module-pkg');\n"),
[("module-pkg".to_string(), 0)],
"the module require still gets a site"
);
}
#[test]
fn a_const_with_conditional_literals_credits_every_branch() {
let source = "const compiler = native ? 'native-pkg' : '@scope/wasm-pkg';\n\
export const url = import.meta.resolve(`${compiler}/package.json`);\n\
const fallback = process.env.X || 'or-pkg';\n\
const literalOr = 'left-pkg' || 'right-pkg';\n\
const nullish = 'first-pkg' ?? ('second-pkg');\n\
const nested = a ? 'outer-pkg' : b ? 'inner-pkg' : 'last-pkg';\n\
require.resolve(literalOr);\n\
require.resolve(`${nullish}/package.json`);\n\
require.resolve(nested);\n\
require.resolve(fallback);\n";
let mut credited = package_references(source);
credited.sort();
assert_eq!(
credited,
[
"@scope/wasm-pkg",
"first-pkg",
"inner-pkg",
"last-pkg",
"left-pkg",
"native-pkg",
"outer-pkg",
"right-pkg",
"second-pkg",
],
"a branch that is not a string literal makes the value unknown"
);
}
#[test]
fn an_inline_conditional_specifier_credits_every_branch() {
let source = "export const a = import.meta.resolve(`${native ? 'native-pkg' : 'wasm-pkg'}/package.json`);\n\
export const b = require.resolve(native ? 'cjs-a' : 'cjs-b');\n";
let mut credited = package_references(source);
credited.sort();
assert_eq!(credited, ["cjs-a", "cjs-b", "native-pkg", "wasm-pkg"]);
}
#[test]
fn a_conditional_specifier_gets_no_site() {
let source = "const compiler = native ? 'native-pkg' : 'wasm-pkg';\n\
export const url = import.meta.resolve(`${compiler}/package.json`);\n\
export const b = require.resolve(native ? 'cjs-a' : 'cjs-b');\n";
assert!(package_resolve_sites(source).is_empty());
}
#[test]
fn function_scoped_create_require_is_the_module_require() {
let source = "import { createRequire } from 'node:module';\n\
export async function build() {\n\
const require = createRequire(import.meta.url);\n\
await run({ shim: require.resolve('./shim.js'), exe: require.resolve('some-pkg') });\n\
}\n";
assert_eq!(relative_references(source), ["./shim.js"]);
assert_eq!(package_references(source), ["some-pkg"]);
assert_eq!(package_resolve_sites(source).len(), 1);
}
#[test]
fn function_scoped_create_require_from_filename_counts() {
let source = "const { createRequire } = require('node:module');\n\
function load() {\n\
const require = createRequire(__filename);\n\
return require.resolve('some-pkg');\n\
}\n\
load();\n";
assert_eq!(package_references(source), ["some-pkg"]);
}
#[test]
fn member_create_require_counts() {
let source = "import module from 'node:module';\n\
export function load() {\n\
const require = module.createRequire(import.meta.url);\n\
return require.resolve('some-pkg');\n\
}\n";
assert_eq!(package_references(source), ["some-pkg"]);
}
#[test]
fn create_require_with_another_base_still_shadows() {
let source = "import { createRequire } from 'node:module';\n\
import { join } from 'node:path';\n\
export function load(dir) {\n\
const require = createRequire(join(dir, 'package.json'));\n\
return [require.resolve('./shim.js'), require.resolve('some-pkg')];\n\
}\n";
assert!(relative_references(source).is_empty());
assert!(package_references(source).is_empty());
}
#[test]
fn inner_rebinding_shadows_a_create_require_scope() {
let source = "import { createRequire } from 'node:module';\n\
export function load() {\n\
const require = createRequire(import.meta.url);\n\
{\n\
const require = makeLoader();\n\
require.resolve('inner-pkg');\n\
}\n\
return require.resolve('outer-pkg');\n\
}\n";
assert_eq!(package_references(source), ["outer-pkg"]);
}
#[test]
fn sibling_scope_after_create_require_scope_is_not_affected() {
let source = "import { createRequire } from 'node:module';\n\
export function f() {\n\
const require = createRequire(import.meta.url);\n\
return require.resolve('a-pkg');\n\
}\n\
export function g(require) {\n\
return require.resolve('x');\n\
}\n";
assert_eq!(package_references(source), ["a-pkg"]);
}