Skip to content

Commit dfa4444

Browse files
aduh95targos
authored andcommitted
esm: skip file: URL conversion to path when possible
PR-URL: #46305 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent b8634ee commit dfa4444

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

lib/internal/modules/esm/get_format.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ const {
44
ObjectPrototypeHasOwnProperty,
55
PromisePrototypeThen,
66
PromiseResolve,
7+
StringPrototypeCharCodeAt,
78
StringPrototypeSlice,
89
} = primordials;
9-
const { basename, extname, relative } = require('path');
10+
const { basename, relative } = require('path');
1011
const { getOptionValue } = require('internal/options');
1112
const {
1213
extensionFormatMap,
@@ -44,15 +45,38 @@ function getDataProtocolModuleFormat(parsed) {
4445
return mimeToFormat(mime);
4546
}
4647

48+
const DOT_CODE = 46;
49+
const SLASH_CODE = 47;
50+
51+
/**
52+
* Returns the file extension from a URL. Should give similar result to
53+
* `require('node:path').extname(require('node:url').fileURLToPath(url))`
54+
* when used with a `file:` URL.
55+
* @param {URL} url
56+
* @returns {string}
57+
*/
58+
function extname(url) {
59+
const { pathname } = url;
60+
for (let i = pathname.length - 1; i > 0; i--) {
61+
switch (StringPrototypeCharCodeAt(pathname, i)) {
62+
case SLASH_CODE:
63+
return '';
64+
65+
case DOT_CODE:
66+
return StringPrototypeCharCodeAt(pathname, i - 1) === SLASH_CODE ? '' : StringPrototypeSlice(pathname, i);
67+
}
68+
}
69+
return '';
70+
}
71+
4772
/**
4873
* @param {URL} url
4974
* @param {{parentURL: string}} context
5075
* @param {boolean} ignoreErrors
5176
* @returns {string}
5277
*/
5378
function getFileProtocolModuleFormat(url, context, ignoreErrors) {
54-
const filepath = fileURLToPath(url);
55-
const ext = extname(filepath);
79+
const ext = extname(url);
5680
if (ext === '.js') {
5781
return getPackageType(url) === 'module' ? 'module' : 'commonjs';
5882
}
@@ -63,6 +87,7 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) {
6387
if (experimentalSpecifierResolution !== 'node') {
6488
// Explicit undefined return indicates load hook should rerun format check
6589
if (ignoreErrors) return undefined;
90+
const filepath = fileURLToPath(url);
6691
let suggestion = '';
6792
if (getPackageType(url) === 'module' && ext === '') {
6893
const config = getPackageScopeConfig(url);
@@ -128,4 +153,5 @@ module.exports = {
128153
defaultGetFormat,
129154
defaultGetFormatWithoutErrors,
130155
extensionFormatMap,
156+
extname,
131157
};

lib/internal/modules/esm/translators.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ const {
3535
Module: CJSModule,
3636
cjsParseCache,
3737
} = require('internal/modules/cjs/loader');
38-
const internalURLModule = require('internal/url');
39-
const { fileURLToPath, URL } = require('url');
38+
const { fileURLToPath, URL } = require('internal/url');
4039
let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
4140
debug = fn;
4241
});
@@ -147,9 +146,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
147146
isMain) {
148147
debug(`Translating CJSModule ${url}`);
149148

150-
let filename = internalURLModule.fileURLToPath(new URL(url));
151-
if (isWindows)
152-
filename = StringPrototypeReplaceAll(filename, '/', '\\');
149+
const filename = fileURLToPath(new URL(url));
153150

154151
if (!cjsParse) await initCJSParse();
155152
const { module, exportNames } = cjsPreparseModuleExports(filename);

test/parallel/test-esm-url-extname.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
require('../common');
4+
const assert = require('node:assert');
5+
const path = require('node:path');
6+
const { extname } = require('node:internal/modules/esm/get_format');
7+
const { fileURLToPath } = require('node:url');
8+
9+
[
10+
'file:///c:/path/to/file',
11+
'file:///c:/path/to/file.ext',
12+
'file:///c:/path.to/file.ext',
13+
'file:///c:/path.to/file',
14+
'file:///c:/path.to/.file',
15+
'file:///c:/path.to/.file.ext',
16+
'file:///c:/path/to/f.ext',
17+
'file:///c:/path/to/..ext',
18+
'file:///c:/path/to/..',
19+
'file:///c:/file',
20+
'file:///c:/file.ext',
21+
'file:///c:/.file',
22+
'file:///c:/.file.ext',
23+
].forEach((input) => {
24+
const inputAsURL = new URL(input);
25+
const inputAsPath = fileURLToPath(inputAsURL);
26+
assert.strictEqual(extname(inputAsURL), path.extname(inputAsPath));
27+
});

0 commit comments

Comments
 (0)