oxc_resolver 11.19.1

ESM / CJS module resolution
Documentation
//! https://github.com/webpack/enhanced-resolve/blob/main/test/missing.test.js

use crate::{AliasValue, PathUtil, ResolveContext, ResolveOptions, Resolver};

#[test]
fn test() {
    let f = super::fixture();

    let data = [
        (
            "./missing-file",
            vec![f.join("missing-file"), f.join("missing-file.js"), f.join("missing-file.node")],
        ),
        (
            "missing-module",
            vec![
                f.join("node_modules/missing-module"),
                f.parent().unwrap().join("node_modules"), // enhanced-resolve is "node_modules/missing-module"
            ],
        ),
        (
            "missing-module/missing-file",
            vec![
                f.join("node_modules/missing-module"),
                // f.parent().unwrap().join("node_modules/missing-module"), // we don't report this
            ],
        ),
        (
            "m1/missing-file",
            vec![
                f.join("node_modules/m1/missing-file"),
                f.join("node_modules/m1/missing-file.js"),
                f.join("node_modules/m1/missing-file.node"),
                // f.parent().unwrap().join("node_modules/m1"), // we don't report this
            ],
        ),
        (
            "m1/",
            vec![
                f.join("node_modules/m1/index.js"),
                f.join("node_modules/m1/index.json"),
                f.join("node_modules/m1/index.node"),
            ],
        ),
        ("m1/a", vec![f.join("node_modules/m1/a")]),
    ];

    let resolver = Resolver::default();

    for (specifier, missing_dependencies) in data {
        let mut ctx = ResolveContext::default();
        let _ = resolver.resolve_with_context(&f, specifier, None, &mut ctx);

        for path in ctx.file_dependencies {
            assert_eq!(path, path.normalize(), "{path:?}");
        }

        for path in missing_dependencies {
            assert_eq!(path, path.normalize(), "{path:?}");
            assert!(
                ctx.missing_dependencies.contains(&path),
                "{specifier}: {path:?} not in {:?}",
                &ctx.missing_dependencies
            );
        }
    }
}

#[test]
fn alias_and_extensions() {
    let f = super::fixture();

    let resolver = Resolver::new(ResolveOptions {
        alias: vec![
            (
                "@scope-js/package-name/dir$".into(),
                vec![AliasValue::Path(f.join("foo/index.js").to_string_lossy().to_string())],
            ),
            (
                "react-dom".into(),
                vec![AliasValue::Path(f.join("foo/index.js").to_string_lossy().to_string())],
            ),
        ],
        extensions: vec![".server.ts".into()],

        ..ResolveOptions::default()
    });

    let mut ctx = ResolveContext::default();
    let _ = resolver.resolve_with_context(&f, "@scope-js/package-name/dir/router", None, &mut ctx);
    let _ = resolver.resolve_with_context(&f, "react-dom/client", None, &mut ctx);

    for path in ctx.file_dependencies {
        assert_eq!(path, path.normalize(), "{path:?}");
    }

    for path in ctx.missing_dependencies {
        assert_eq!(path, path.normalize(), "{path:?}");
        if let Some(path) = path.parent() {
            assert!(!path.is_file(), "{path:?} must not be a file");
        }
    }
}