1// Flags: --expose-internals 2 3/** 4 * This test ensures defaultResolve returns the found module format in the 5 * return object in the form: 6 * { url: <url_value>, format: <'module'|'commonjs'|undefined> }; 7 */ 8 9import * as common from '../common/index.mjs'; 10import tmpdir from '../common/tmpdir.js'; 11import * as fixtures from '../common/fixtures.mjs'; 12import path from 'path'; 13import fs from 'fs'; 14import url from 'url'; 15import process from 'process'; 16 17if (!common.isMainThread) { 18 common.skip( 19 'test-esm-resolve-type.mjs: process.chdir is not available in Workers' 20 ); 21} 22 23import assert from 'assert'; 24import internalResolve from 'node:internal/modules/esm/resolve'; 25const { 26 defaultResolve: resolve 27} = internalResolve; 28 29const rel = (file) => path.join(tmpdir.path, file); 30const previousCwd = process.cwd(); 31const nmDir = rel('node_modules'); 32 33try { 34 tmpdir.refresh(); 35 process.chdir(tmpdir.path); 36 /** 37 * ensure that resolving by full path does not return the format 38 * with the defaultResolver 39 */ 40 [ 41 [ '/es-modules/package-type-module/index.js', 'module' ], 42 [ '/es-modules/package-type-commonjs/index.js', 'commonjs' ], 43 [ '/es-modules/package-without-type/index.js', 'commonjs' ], 44 [ '/es-modules/package-without-pjson/index.js', 'commonjs' ], 45 ].forEach(([ testScript, expectedType ]) => { 46 const resolvedPath = path.resolve(fixtures.path(testScript)); 47 const resolveResult = resolve(url.pathToFileURL(resolvedPath)); 48 assert.strictEqual(resolveResult.format, expectedType); 49 }); 50 51 /** 52 * create a test module and try to resolve it by module name. 53 * check the result is as expected 54 * 55 * for test-module-ne: everything .js that is not 'module' is 'commonjs' 56 */ 57 for (const [ moduleName, moduleExtenstion, moduleType, expectedResolvedType ] of 58 [ [ 'test-module-mainjs', 'js', 'module', 'module'], 59 [ 'test-module-mainmjs', 'mjs', 'module', 'module'], 60 [ 'test-module-cjs', 'js', 'commonjs', 'commonjs'], 61 [ 'test-module-ne', 'js', undefined, 'commonjs'], 62 ]) { 63 process.chdir(previousCwd); 64 tmpdir.refresh(); 65 process.chdir(tmpdir.path); 66 const createDir = (path) => { 67 if (!fs.existsSync(path)) { 68 fs.mkdirSync(path); 69 } 70 }; 71 72 const mDir = rel(`node_modules/${moduleName}`); 73 const subDir = rel(`node_modules/${moduleName}/subdir`); 74 const pkg = rel(`node_modules/${moduleName}/package.json`); 75 const script = rel(`node_modules/${moduleName}/subdir/mainfile.${moduleExtenstion}`); 76 77 createDir(nmDir); 78 createDir(mDir); 79 createDir(subDir); 80 const pkgJsonContent = { 81 ...(moduleType !== undefined) && { type: moduleType }, 82 main: `subdir/mainfile.${moduleExtenstion}` 83 }; 84 fs.writeFileSync(pkg, JSON.stringify(pkgJsonContent)); 85 fs.writeFileSync(script, 86 'export function esm-resolve-tester() {return 42}'); 87 88 const resolveResult = resolve(`${moduleName}`); 89 assert.strictEqual(resolveResult.format, expectedResolvedType); 90 91 fs.rmSync(nmDir, { recursive: true, force: true }); 92 } 93 94 // Helpers 95 const createDir = (path) => { 96 if (!fs.existsSync(path)) { 97 fs.mkdirSync(path); 98 } 99 }; 100 101 { 102 // Create a dummy dual package 103 // 104 /** 105 * this creates the following directory structure: 106 * 107 * ./node_modules: 108 * |-> my-dual-package 109 * |-> es 110 * |-> index.js 111 * |-> package.json [2] 112 * |-> lib 113 * |-> index.js 114 * |->package.json [1] 115 * 116 * in case the package is imported: 117 * import * as my-package from 'my-dual-package' 118 * it will cause the resolve method to return: 119 * { 120 * url: '<base_path>/node_modules/my-dual-package/es/index.js', 121 * format: 'module' 122 * } 123 * 124 * following testcase ensures that resolve works correctly in this case 125 * returning the information as specified above. Source for 'url' value 126 * is [1], source for 'format' value is [2] 127 */ 128 129 const moduleName = 'my-dual-package'; 130 131 const mDir = rel(`node_modules/${moduleName}`); 132 const esSubDir = rel(`node_modules/${moduleName}/es`); 133 const cjsSubDir = rel(`node_modules/${moduleName}/lib`); 134 const pkg = rel(`node_modules/${moduleName}/package.json`); 135 const esmPkg = rel(`node_modules/${moduleName}/es/package.json`); 136 const esScript = rel(`node_modules/${moduleName}/es/index.js`); 137 const cjsScript = rel(`node_modules/${moduleName}/lib/index.js`); 138 139 createDir(nmDir); 140 createDir(mDir); 141 createDir(esSubDir); 142 createDir(cjsSubDir); 143 144 const mainPkgJsonContent = { 145 type: 'commonjs', 146 exports: { 147 '.': { 148 'require': './lib/index.js', 149 'import': './es/index.js', 150 'default': './lib/index.js' 151 }, 152 './package.json': './package.json', 153 } 154 }; 155 const esmPkgJsonContent = { 156 type: 'module' 157 }; 158 159 fs.writeFileSync(pkg, JSON.stringify(mainPkgJsonContent)); 160 fs.writeFileSync(esmPkg, JSON.stringify(esmPkgJsonContent)); 161 fs.writeFileSync( 162 esScript, 163 'export function esm-resolve-tester() {return 42}' 164 ); 165 fs.writeFileSync( 166 cjsScript, 167 'module.exports = {esm-resolve-tester: () => {return 42}}' 168 ); 169 170 // test the resolve 171 const resolveResult = resolve(`${moduleName}`); 172 assert.strictEqual(resolveResult.format, 'module'); 173 assert.ok(resolveResult.url.includes('my-dual-package/es/index.js')); 174 } 175 176 // TestParameters are ModuleName, mainRequireScript, mainImportScript, 177 // mainPackageType, subdirPkgJsonType, expectedResolvedFormat, mainSuffix 178 [ 179 [ 'mjs-mod-mod', 'index.js', 'index.mjs', 'module', 'module', 'module'], 180 [ 'mjs-com-com', 'idx.js', 'idx.mjs', 'commonjs', 'commonjs', 'module'], 181 [ 'mjs-mod-com', 'index.js', 'imp.mjs', 'module', 'commonjs', 'module'], 182 [ 'cjs-mod-mod', 'index.cjs', 'imp.cjs', 'module', 'module', 'commonjs'], 183 [ 'js-com-com', 'index.js', 'imp.js', 'commonjs', 'commonjs', 'commonjs'], 184 [ 'js-com-mod', 'index.js', 'imp.js', 'commonjs', 'module', 'module'], 185 [ 'qmod', 'index.js', 'imp.js', 'commonjs', 'module', 'module', '?k=v'], 186 [ 'hmod', 'index.js', 'imp.js', 'commonjs', 'module', 'module', '#Key'], 187 [ 'qhmod', 'index.js', 'imp.js', 'commonjs', 'module', 'module', '?k=v#h'], 188 [ 'ts-mod-com', 'index.js', 'imp.ts', 'module', 'commonjs', undefined], 189 ].forEach((testVariant) => { 190 const [ 191 moduleName, 192 mainRequireScript, 193 mainImportScript, 194 mainPackageType, 195 subdirPackageType, 196 expectedResolvedFormat, 197 mainSuffix = '' ] = testVariant; 198 199 const mDir = rel(`node_modules/${moduleName}`); 200 const subDir = rel(`node_modules/${moduleName}/subdir`); 201 const pkg = rel(`node_modules/${moduleName}/package.json`); 202 const subdirPkg = rel(`node_modules/${moduleName}/subdir/package.json`); 203 const esScript = rel(`node_modules/${moduleName}/subdir/${mainImportScript}`); 204 const cjsScript = rel(`node_modules/${moduleName}/subdir/${mainRequireScript}`); 205 206 createDir(nmDir); 207 createDir(mDir); 208 createDir(subDir); 209 210 const mainPkgJsonContent = { 211 type: mainPackageType, 212 exports: { 213 '.': { 214 'require': `./subdir/${mainRequireScript}${mainSuffix}`, 215 'import': `./subdir/${mainImportScript}${mainSuffix}`, 216 'default': `./subdir/${mainRequireScript}${mainSuffix}` 217 }, 218 './package.json': './package.json', 219 } 220 }; 221 const subdirPkgJsonContent = { 222 type: `${subdirPackageType}` 223 }; 224 225 fs.writeFileSync(pkg, JSON.stringify(mainPkgJsonContent)); 226 fs.writeFileSync(subdirPkg, JSON.stringify(subdirPkgJsonContent)); 227 fs.writeFileSync( 228 esScript, 229 'export function esm-resolve-tester() {return 42}' 230 ); 231 fs.writeFileSync( 232 cjsScript, 233 'module.exports = {esm-resolve-tester: () => {return 42}}' 234 ); 235 236 // test the resolve 237 const resolveResult = resolve(`${moduleName}`); 238 assert.strictEqual(resolveResult.format, expectedResolvedFormat); 239 assert.ok(resolveResult.url.endsWith(`${moduleName}/subdir/${mainImportScript}${mainSuffix}`)); 240 }); 241 242} finally { 243 process.chdir(previousCwd); 244 fs.rmSync(nmDir, { recursive: true, force: true }); 245} 246