// @flow strict-local import NodeResolver from '../src/NodeResolver'; import path from 'path'; import assert from 'assert'; import nullthrows from 'nullthrows'; import {ncp, overlayFS, outputFS} from '@parcel/test-utils'; import {loadConfig as configCache} from '@parcel/utils'; import {createEnvironment} from '@parcel/core/src/Environment'; import Environment from '@parcel/core/src/public/Environment'; import {DEFAULT_OPTIONS} from '@parcel/core/test/test-utils'; const rootDir = path.join(__dirname, 'fixture'); const NODE_ENV = new Environment( createEnvironment({ context: 'node', includeNodeModules: false, }), DEFAULT_OPTIONS, ); const NODE_INCLUDE_ENV = new Environment( createEnvironment({ context: 'node', includeNodeModules: true, }), DEFAULT_OPTIONS, ); const BROWSER_ENV = new Environment( createEnvironment({ context: 'browser', includeNodeModules: true, }), DEFAULT_OPTIONS, ); describe('resolver', function() { let resolver; beforeEach(async function() { await overlayFS.mkdirp(rootDir); await ncp(rootDir, rootDir); // Create the symlinks here to prevent cross platform and git issues await outputFS.symlink( path.join(rootDir, 'packages/source'), path.join(rootDir, 'node_modules/source'), ); await outputFS.symlink( path.join(rootDir, 'packages/source-alias'), path.join(rootDir, 'node_modules/source-alias'), ); await outputFS.symlink( path.join(rootDir, 'packages/source-alias-glob'), path.join(rootDir, 'node_modules/source-alias-glob'), ); await outputFS.symlink( path.join(rootDir, 'bar.js'), path.join(rootDir, 'baz.js'), ); await outputFS.symlink( path.join(rootDir, 'nested'), path.join(rootDir, 'symlinked-nested'), ); resolver = new NodeResolver({ fs: overlayFS, projectRoot: rootDir, mainFields: ['browser', 'source', 'module', 'main'], extensions: ['.js', '.json'], }); configCache.clear(); }); describe('file paths', function() { it('should resolve a relative path with an extension', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './bar.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve a relative path without an extension', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './bar', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve an absolute path from the root module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '/bar', specifierType: 'esm', parent: path.join(rootDir, 'nested', 'test.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve an absolute path from a node_modules folder', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '/bar', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'foo', 'index.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve a tilde path from the root module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '~/bar', specifierType: 'esm', parent: path.join(rootDir, 'nested', 'test.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve a tilde path from the root module without a slash', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '~bar', specifierType: 'esm', parent: path.join(rootDir, 'nested', 'test.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve a tilde path from a node_modules folder', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '~/bar', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'foo', 'nested', 'baz.js'), }); assert.equal( nullthrows(resolved).filePath, path.join(rootDir, 'node_modules', 'foo', 'bar.js'), ); }); it('should resolve an index file in a directory', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './nested', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(resolved).filePath, path.join(rootDir, 'nested', 'index.js'), ); }); it('should not resolve an index file in a directory for URL specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './nested', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ {message: "Cannot load file './nested' in './'.", hints: []}, ]); }); it('should resolve a file with a question mark with CommonJS specifiers', async function() { // Windows filenames cannot contain question marks. if (process.platform === 'win32') { return; } await overlayFS.writeFile(path.join(rootDir, 'a?b.js'), ''); let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './a?b.js', specifierType: 'commonjs', parent: path.join(rootDir, 'foo.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'a?b.js')); }); it('should not resolve a file with a question mark with ESM specifiers', async function() { // Windows filenames cannot contain question marks. if (process.platform === 'win32') { return; } await overlayFS.writeFile(path.join(rootDir, 'a?b.js'), ''); let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './a?b.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ {message: "Cannot load file './a' in './'.", hints: []}, ]); }); it('should resolve a file with an encoded question mark with ESM specifiers', async function() { // Windows filenames cannot contain question marks. if (process.platform === 'win32') { return; } await overlayFS.writeFile(path.join(rootDir, 'a?b.js'), ''); let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './a%3Fb.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'a?b.js')); }); it('should not support percent encoding in CommonJS specifiers', async function() { // Windows filenames cannot contain question marks. if (process.platform === 'win32') { return; } await overlayFS.writeFile(path.join(rootDir, 'a?b.js'), ''); let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './a%3Fb.js', specifierType: 'commonjs', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ { message: "Cannot load file './a%3Fb.js' in './'.", hints: ["Did you mean '__./a?b.js__'?"], }, ]); }); it('should support query params for ESM specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './nested?foo=bar', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(resolved).filePath, path.join(rootDir, 'nested', 'index.js'), ); assert.deepEqual(nullthrows(resolved).query?.toString(), 'foo=bar'); }); it('should not support query params for CommonJS specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './nested?foo=bar', specifierType: 'commonjs', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ {message: "Cannot load file './nested?foo=bar' in './'.", hints: []}, ]); }); }); describe('builtins', function() { it('should resolve node builtin modules', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'zlib', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: require.resolve('browserify-zlib'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: require.resolve('browserify-zlib'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), require.resolve('browserify-zlib/package.json'), ], }); }); it('Should be able to handle node: prefixes', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'node:zlib', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: require.resolve('browserify-zlib'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: require.resolve('browserify-zlib'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), require.resolve('browserify-zlib/package.json'), ], }); }); it('should resolve unimplemented node builtin modules to an empty file', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'fs', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(__dirname, '..', 'src', '_empty.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(__dirname, '..', 'src', '_empty.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(__dirname, '..', 'package.json'), ], }); }); it('should error when resolving node builtin modules with --target=node', async function() { let resolved = await resolver.resolve({ env: NODE_ENV, filename: 'zlib', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, {isExcluded: true}); }); it('should exclude the electron module in electron environments', async function() { let resolved = await resolver.resolve({ env: new Environment( createEnvironment({ context: 'electron-main', isLibrary: true, }), DEFAULT_OPTIONS, ), filename: 'electron', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), sourcePath: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, {isExcluded: true}); }); }); describe('node_modules', function() { it('should resolve a node_modules index.js', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'foo', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'foo', 'index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/foo', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'foo', 'package.json'), ], }); }); it('should resolve a node_modules package.main', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-main', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'package-main', 'main.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-main', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-main', 'package.json'), ], }); }); it('should resolve a node_modules package.module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-module', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-module', 'module.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-module', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-module', 'package.json'), ], }); }); it('should resolve a node_modules package.browser main field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-browser', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser', 'browser.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-browser', 'package.json'), ], }); }); it('should not resolve a node_modules package.browser main field with --target=node', async function() { let resolved = await resolver.resolve({ env: NODE_INCLUDE_ENV, filename: 'package-browser', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser', 'main.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-browser', 'package.json'), ], }); }); it('should fall back to index.js when it cannot find package.main', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-fallback', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-fallback', 'index.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-fallback', aboveFilePath: path.join(rootDir, 'foo.js'), }, { filePath: path.join( rootDir, 'node_modules', 'package-fallback', 'main.js', ), }, { filePath: path.join( rootDir, 'node_modules', 'package-fallback', 'main.js.js', ), }, { filePath: path.join( rootDir, 'node_modules', 'package-fallback', 'main.js.json', ), }, { filePath: path.join( rootDir, 'node_modules', 'package-fallback', 'main.js/package.json', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-fallback', 'package.json', ), ], }); }); it('should resolve a node_module package.main pointing to a directory', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-main-directory', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-main-directory', 'nested', 'index.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-main-directory', aboveFilePath: path.join(rootDir, 'foo.js'), }, { filePath: path.join( rootDir, 'node_modules', 'package-main-directory', 'nested.js', ), }, { filePath: path.join( rootDir, 'node_modules', 'package-main-directory', 'nested.json', ), }, { filePath: path.join( rootDir, 'node_modules', 'package-main-directory', 'nested', 'package.json', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-main-directory', 'package.json', ), ], }); }); it('should resolve a file inside a node_modules folder', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'foo/nested/baz', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'foo', 'nested', 'baz.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/foo', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'foo', 'package.json'), ], }); }); it('should resolve a scoped module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '@scope/pkg', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve(rootDir, 'node_modules/@scope/pkg/index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/@scope/pkg', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', '@scope', 'pkg', 'package.json'), ], }); }); it('should resolve a file inside a scoped module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '@scope/pkg/foo/bar', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve(rootDir, 'node_modules/@scope/pkg/foo/bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/@scope/pkg', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', '@scope', 'pkg', 'package.json'), ], }); }); describe('sideEffects: false', function() { it('should determine sideEffects correctly (file)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'side-effects-false/src/index.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve( rootDir, 'node_modules/side-effects-false/src/index.js', ), sideEffects: false, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/side-effects-false', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'side-effects-false', 'package.json', ), ], }); }); it('should determine sideEffects correctly (extensionless file)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'side-effects-false/src/index', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve( rootDir, 'node_modules/side-effects-false/src/index.js', ), sideEffects: false, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/side-effects-false', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'side-effects-false', 'package.json', ), ], }); }); it('should determine sideEffects correctly (sub folder)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'side-effects-false/src/', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve( rootDir, 'node_modules/side-effects-false/src/index.js', ), sideEffects: false, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/side-effects-false', aboveFilePath: path.join(rootDir, 'foo.js'), }, { filePath: path.join( rootDir, 'node_modules', 'side-effects-false', 'src', 'package.json', ), }, { aboveFilePath: path.join( rootDir, 'node_modules', 'side-effects-false', 'src', 'index', ), fileName: 'package.json', }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'side-effects-false', 'package.json', ), ], }); }); it('should determine sideEffects correctly (main field)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'side-effects-false/src/', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.resolve( rootDir, 'node_modules/side-effects-false/src/index.js', ), sideEffects: false, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/side-effects-false', aboveFilePath: path.join(rootDir, 'foo.js'), }, { filePath: path.join( rootDir, 'node_modules', 'side-effects-false', 'src', 'package.json', ), }, { aboveFilePath: path.join( rootDir, 'node_modules', 'side-effects-false', 'src', 'index', ), fileName: 'package.json', }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'side-effects-false', 'package.json', ), ], }); }); }); it('should not resolve a node module for URL dependencies', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '@scope/pkg', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ {message: "Cannot load file './@scope/pkg' in './'.", hints: []}, ]); }); it('should resolve a node module for URL dependencies with the npm: prefix', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'npm:@scope/pkg', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual( nullthrows(resolved).filePath, path.join(rootDir, 'node_modules', '@scope', 'pkg', 'index.js'), ); }); it('should support query params for bare ESM specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '@scope/pkg?foo=2', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(resolved).filePath, path.resolve(rootDir, 'node_modules/@scope/pkg/index.js'), ); assert.deepEqual(nullthrows(resolved).query?.toString(), 'foo=2'); }); it('should not support query params for bare CommonJS specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '@scope/pkg?foo=2', specifierType: 'commonjs', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(nullthrows(resolved).diagnostics, [ { message: 'Cannot find module @scope/pkg?foo=2', hints: ["Did you mean '__@scope/pkg__'?"], }, ]); }); it('should support query params for npm: specifiers', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'npm:@scope/pkg?foo=2', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(resolved).filePath, path.resolve(rootDir, 'node_modules/@scope/pkg/index.js'), ); assert.deepEqual(nullthrows(resolved).query?.toString(), 'foo=2'); }); }); describe('aliases', function() { it('should alias the main file using the package.browser field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-browser-alias', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'browser.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser-alias', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-alias', 'package.json', ), ], }); }); it('should alias a sub-file using the package.browser field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-browser-alias/foo', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'bar.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser-alias', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-alias', 'package.json', ), ], }); }); it('should alias a relative file using the package.browser field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './foo', specifierType: 'esm', parent: path.join( rootDir, 'node_modules', 'package-browser-alias', 'browser.js', ), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'bar.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'browser.js', ), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'bar', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-alias', 'package.json', ), ], }); }); it('should not alias using the package.browser field with --target=node', async function() { let resolved = await resolver.resolve({ env: NODE_INCLUDE_ENV, filename: 'package-browser-alias/foo', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'foo.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser-alias', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-alias', 'package.json', ), ], }); }); it('should alias a deep nested relative file using the package.browser field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './nested', specifierType: 'esm', parent: path.join( rootDir, 'node_modules', 'package-browser-alias', 'browser.js', ), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'subfolder1/subfolder2/subfile.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'browser.js', ), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-browser-alias', 'nested', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-alias', 'package.json', ), ], }); }); it('should alias a sub-file using the package.alias field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-alias/foo', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'package-alias', 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-alias', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), ], }); }); it('should alias a relative file using the package.alias field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './foo', specifierType: 'esm', parent: path.join( rootDir, 'node_modules', 'package-alias', 'browser.js', ), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'package-alias', 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'browser.js', ), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'bar', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), ], }); }); it('should alias a glob using the package.alias field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './lib/test', specifierType: 'esm', parent: path.join( rootDir, 'node_modules', 'package-alias-glob', 'index.js', ), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'package-alias-glob', 'src', 'test.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias-glob', 'index.js', ), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias-glob', 'src', 'test', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-alias-glob', 'package.json', ), ], }); }); it('should apply a module alias using the package.alias field in the root package', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliased', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'foo', 'index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/foo', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'foo', 'package.json'), ], }); }); it('should apply a global module alias using the package.alias field in the root package', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliased', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'package-alias', 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'foo', 'index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, { fileName: 'node_modules/foo', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), path.join(rootDir, 'node_modules', 'foo', 'package.json'), ], }); }); it('should apply a global module alias to a sub-file in a package', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliased/bar', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'package-alias', 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'node_modules', 'foo', 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, { fileName: 'node_modules/foo', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), path.join(rootDir, 'node_modules', 'foo', 'package.json'), ], }); }); it('should apply a module alias pointing to a file using the package.alias field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliased-file', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'bar.js'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply a global module alias pointing to a file using the package.alias field', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliased-file', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'package-alias', 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'bar.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), ], }); }); it('should apply an alias for a virtual module folder (relative to project dir)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliasedfolder/test.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'test.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested', 'test.js'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module folder only (relative to project dir)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliasedfolder', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested'), }, { filePath: path.join(rootDir, 'nested.js'), }, { filePath: path.join(rootDir, 'nested.json'), }, { filePath: path.join(rootDir, 'nested', 'package.json'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module folder (relative to root dir)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliasedabsolute/test.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'test.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested', 'test.js'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module folder only (relative to root dir)', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'aliasedabsolute', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'index.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested'), }, { filePath: path.join(rootDir, 'nested.js'), }, { filePath: path.join(rootDir, 'nested.json'), }, { filePath: path.join(rootDir, 'nested', 'package.json'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module folder sub-path', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'foo/bar', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'bar.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'bar.js'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module folder glob sub-path', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'glob/bar/test', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'test.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested', 'test'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply an alias for a virtual module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'something', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'test.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested', 'test.js'), }, ], invalidateOnFileChange: [path.join(rootDir, 'package.json')], }); }); it('should apply a global alias for a virtual module', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'something', specifierType: 'esm', parent: path.join(rootDir, 'node_modules', 'package-alias', 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'nested', 'test.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join( rootDir, 'node_modules', 'package-alias', 'foo.js', ), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'nested', 'test.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'package-alias', 'package.json'), ], }); }); it('should resolve to an empty file when package.browser resolves to false', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-browser-exclude', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(__dirname, '..', 'src', '_empty.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-browser-exclude', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-browser-exclude', 'package.json', ), ], }); }); it('should resolve to an empty file when package.alias resolves to false', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-alias-exclude', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(__dirname, '..', 'src', '_empty.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/package-alias-exclude', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'package-alias-exclude', 'package.json', ), ], }); }); }); describe('source field', function() { it('should use the source field when symlinked', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'source', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'packages', 'source', 'source.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/source', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'source', 'package.json'), ], }); }); it('should not use the source field when not symlinked', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'source-not-symlinked', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'node_modules', 'source-not-symlinked', 'dist.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/source-not-symlinked', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'source-not-symlinked', 'package.json', ), ], }); }); it('should use the source field as an alias when symlinked', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'source-alias/dist', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join(rootDir, 'packages', 'source-alias', 'source.js'), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/source-alias', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join(rootDir, 'node_modules', 'source-alias', 'package.json'), ], }); }); it('should use the source field as a glob alias when symlinked', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'source-alias-glob', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, { filePath: path.join( rootDir, 'packages', 'source-alias-glob', 'src', 'test.js', ), sideEffects: undefined, query: undefined, invalidateOnFileCreate: [ { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'index'), }, { fileName: 'package.json', aboveFilePath: path.join(rootDir, 'foo.js'), }, { fileName: 'node_modules/source-alias-glob', aboveFilePath: path.join(rootDir, 'foo.js'), }, ], invalidateOnFileChange: [ path.join(rootDir, 'package.json'), path.join( rootDir, 'node_modules', 'source-alias-glob', 'package.json', ), ], }); }); }); describe('symlinks', function() { it('should resolve symlinked files to their realpath', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './baz.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal(nullthrows(resolved).filePath, path.join(rootDir, 'bar.js')); }); it('should resolve symlinked directories to their realpath', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: './symlinked-nested', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(resolved).filePath, path.join(rootDir, 'nested', 'index.js'), ); }); }); describe('error handling', function() { it('should return diagnostics when package.module does not exist', async function() { let result = await resolver.resolve({ env: BROWSER_ENV, filename: 'package-module-fallback', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(nullthrows(result).diagnostics)[0].message, `Could not load './module.js' from module 'package-module-fallback' found in package.json#module`, ); }); it('should throw when a relative path cannot be resolved', async function() { let result = await resolver.resolve({ env: BROWSER_ENV, filename: './xyz.js', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }); assert.equal( nullthrows(nullthrows(result).diagnostics)[0].message, `Cannot load file './xyz.js' in './'.`, ); }); it('should throw when a node_module cannot be resolved', async function() { assert.strictEqual( null, await resolver.resolve({ env: BROWSER_ENV, filename: 'xyz', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }), ); }); it('should throw when a subfile of a node_module cannot be resolved', async function() { assert.strictEqual( null, await resolver.resolve({ env: BROWSER_ENV, filename: 'xyz/test/file', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), }), ); }); it('should error when a library is missing an external dependency', async function() { let result = await resolver.resolve({ env: new Environment( createEnvironment({ context: 'browser', isLibrary: true, includeNodeModules: false, }), DEFAULT_OPTIONS, ), filename: 'test', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), sourcePath: path.join(rootDir, 'foo.js'), }); assert.equal( result?.diagnostics?.[0].message, 'External dependency "test" is not declared in package.json.', ); }); it('should not error when external dependencies are declared', async function() { let result = await resolver.resolve({ env: new Environment( createEnvironment({ context: 'browser', isLibrary: true, includeNodeModules: false, }), DEFAULT_OPTIONS, ), filename: 'foo', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), sourcePath: path.join(rootDir, 'foo.js'), }); assert.deepEqual(result, {isExcluded: true}); }); it('should not error when external dependencies are declared in peerDependencies', async function() { let result = await resolver.resolve({ env: new Environment( createEnvironment({ context: 'browser', isLibrary: true, includeNodeModules: false, }), DEFAULT_OPTIONS, ), filename: 'bar', specifierType: 'esm', parent: path.join(rootDir, 'foo.js'), sourcePath: path.join(rootDir, 'foo.js'), }); assert.deepEqual(result, {isExcluded: true}); }); it('should not error on missing dependencies for environment builtins', async function() { let result = await resolver.resolve({ env: new Environment( createEnvironment({ context: 'browser', isLibrary: true, includeNodeModules: false, }), DEFAULT_OPTIONS, ), filename: 'atom', specifierType: 'esm', parent: path.join(rootDir, 'env-dep/foo.js'), sourcePath: path.join(rootDir, 'env-dep/foo.js'), }); assert.deepEqual(result, {isExcluded: true}); }); }); describe('urls', function() { it('should ignore protocol relative urls', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '//example.com/foo.png', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, {isExcluded: true}); }); it('should ignore hash urls', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: '#hash', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, {isExcluded: true}); }); it('should ignore http: urls', async function() { let resolved = await resolver.resolve({ env: BROWSER_ENV, filename: 'http://example.com/foo.png', specifierType: 'url', parent: path.join(rootDir, 'foo.js'), }); assert.deepEqual(resolved, {isExcluded: true}); }); }); });