Skip to content
This repository was archived by the owner on Apr 16, 2020. It is now read-only.

Commit 71fae1a

Browse files
esm: scoped --type, cpp refactoring
Co-authored-by: Myles Borins <[email protected]>
1 parent c32a863 commit 71fae1a

File tree

9 files changed

+177
-130
lines changed

9 files changed

+177
-130
lines changed

doc/api/esm.md

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,11 @@ of these top-level routines.
168168
169169
_isMain_ is **true** when resolving the Node.js application entry point.
170170
171-
If the top-level `--type` is _"commonjs"_, then the ESM resolver is skipped
172-
entirely for the CommonJS loader.
173-
174-
If the top-level `--type` is _"module"_, then the ESM resolver is used
175-
as described here, with the conditional `--type` check in **ESM_FORMAT**.
171+
When using the `--type` flag, it overrides the ESM_FORMAT result while
172+
providing errors in the case of explicit conflicts.
176173
177174
<details>
178-
<summary>Resolver algorithm psuedocode</summary>
175+
<summary>Resolver algorithm specification</summary>
179176
180177
**ESM_RESOLVE(_specifier_, _parentURL_, _isMain_)**
181178
> 1. Let _resolvedURL_ be **undefined**.
@@ -255,25 +252,20 @@ PACKAGE_MAIN_RESOLVE(_packageURL_, _pjson_)
255252
256253
**ESM_FORMAT(_url_, _isMain_)**
257254
> 1. Assert: _url_ corresponds to an existing file.
258-
> 1. If _isMain_ is **true** and the `--type` flag is _"module"_, then
259-
> 1. If _url_ ends with _".cjs"_, then
260-
> 1. Throw a _Type Mismatch_ error.
261-
> 1. Return _"module"_.
262255
> 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_).
263-
> 1. If _pjson_ is **null** and _isMain_ is **true**, then
264-
> 1. If _url_ ends in _".mjs"_, then
265-
> 1. Return _"module"_.
266-
> 1. Return _"commonjs"_.
267-
> 1. If _pjson.type_ exists and is _"module"_, then
268-
> 1. If _url_ ends in _".cjs"_, then
269-
> 1. Return _"commonjs"_.
256+
> 1. If _url_ ends in _".mjs"_, then
270257
> 1. Return _"module"_.
271-
> 1. Otherwise,
272-
> 1. If _url_ ends in _".mjs"_, then
273-
> 1. Return _"module"_.
274-
> 1. If _url_ does not end in _".js"_, then
275-
> 1. Throw an _Unsupported File Extension_ error.
258+
> 1. If _url_ ends in _".cjs"_, then
276259
> 1. Return _"commonjs"_.
260+
> 1. If _pjson?.type_ exists and is _"module"_, then
261+
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, then
262+
> 1. Return _"module"_.
263+
> 1. Throw an _Unsupported File Extension_ error.
264+
> 1. Otherwise,
265+
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, _".json"_ or
266+
> _".node"_, then
267+
> 1. Return _"commonjs"_.
268+
> 1. Throw an _Unsupported File Extension_ error.
277269
278270
READ_PACKAGE_SCOPE(_url_)
279271
> 1. Let _scopeURL_ be _url_.

lib/internal/modules/cjs/loader.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -866,17 +866,15 @@ Module.runMain = function() {
866866
// Load the main module--the command line argument.
867867
if (experimentalModules) {
868868
if (asyncESM === undefined) lazyLoadESM();
869-
if (asyncESM.typeFlag !== 'commonjs') {
870-
asyncESM.loaderPromise.then((loader) => {
871-
return loader.import(pathToFileURL(process.argv[1]).pathname);
872-
})
873-
.catch((e) => {
874-
internalBinding('task_queue').triggerFatalException(e);
875-
});
876-
// Handle any nextTicks added in the first tick of the program
877-
process._tickCallback();
878-
return;
879-
}
869+
asyncESM.loaderPromise.then((loader) => {
870+
return loader.import(pathToFileURL(process.argv[1]).pathname);
871+
})
872+
.catch((e) => {
873+
internalBinding('task_queue').triggerFatalException(e);
874+
});
875+
// Handle any nextTicks added in the first tick of the program
876+
process._tickCallback();
877+
return;
880878
}
881879
Module._load(process.argv[1], null, true);
882880
// Handle any nextTicks added in the first tick of the program

lib/internal/modules/esm/default_resolve.js

Lines changed: 44 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,25 @@
33
const internalFS = require('internal/fs/utils');
44
const { NativeModule } = require('internal/bootstrap/loaders');
55
const { extname } = require('path');
6-
const { realpathSync, readFileSync } = require('fs');
6+
const { realpathSync } = require('fs');
77
const { getOptionValue } = require('internal/options');
8+
89
const preserveSymlinks = getOptionValue('--preserve-symlinks');
910
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
10-
const { ERR_INVALID_PACKAGE_CONFIG,
11-
ERR_TYPE_MISMATCH,
12-
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
1311
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
14-
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
15-
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
12+
13+
const { resolve: moduleWrapResolve,
14+
getPackageType } = internalBinding('module_wrap');
15+
const { pathToFileURL, fileURLToPath } = require('internal/url');
1616
const asyncESM = require('internal/process/esm_loader');
17+
const { ERR_TYPE_MISMATCH,
18+
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
1719

1820
const realpathCache = new Map();
19-
// TOOD(@guybedford): Shared cache with C++
20-
const pjsonCache = new Map();
21+
22+
// const TYPE_NONE = 0;
23+
const TYPE_COMMONJS = 1;
24+
const TYPE_MODULE = 2;
2125

2226
const extensionFormatMap = {
2327
'__proto__': null,
@@ -45,75 +49,6 @@ if (experimentalJsonModules) {
4549
});
4650
}
4751

48-
function readPackageConfig(path, parentURL) {
49-
const existing = pjsonCache.get(path);
50-
if (existing !== undefined)
51-
return existing;
52-
try {
53-
return JSON.parse(readFileSync(path).toString());
54-
} catch (e) {
55-
if (e.code === 'ENOENT') {
56-
pjsonCache.set(path, null);
57-
return null;
58-
} else if (e instanceof SyntaxError) {
59-
throw new ERR_INVALID_PACKAGE_CONFIG(path, fileURLToPath(parentURL));
60-
}
61-
throw e;
62-
}
63-
}
64-
65-
function getPackageBoundaryConfig(url, parentURL) {
66-
let pjsonURL = new URL('package.json', url);
67-
while (true) {
68-
const pcfg = readPackageConfig(fileURLToPath(pjsonURL), parentURL);
69-
if (pcfg)
70-
return pcfg;
71-
72-
const lastPjsonURL = pjsonURL;
73-
pjsonURL = new URL('../package.json', pjsonURL);
74-
75-
// Terminates at root where ../package.json equals ../../package.json
76-
// (can't just check "/package.json" for Windows support).
77-
if (pjsonURL.pathname === lastPjsonURL.pathname)
78-
return;
79-
}
80-
}
81-
82-
function getModuleFormat(url, isMain, parentURL) {
83-
const pcfg = getPackageBoundaryConfig(url, parentURL);
84-
85-
const legacy = !pcfg || pcfg.type !== 'module';
86-
87-
const ext = extname(url.pathname);
88-
89-
let format = (legacy ? legacyExtensionFormatMap : extensionFormatMap)[ext];
90-
91-
if (!format) {
92-
if (isMain)
93-
format = legacy ? 'commonjs' : 'module';
94-
else
95-
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
96-
fileURLToPath(parentURL));
97-
}
98-
99-
// Check for mismatch between --type and file extension,
100-
// and between --type and the "type" field in package.json.
101-
if (isMain && format !== 'module' && asyncESM.typeFlag === 'module') {
102-
// Conflict between package scope type and --type
103-
if (ext === '.js') {
104-
if (pcfg && pcfg.type)
105-
throw new ERR_TYPE_MISMATCH(
106-
fileURLToPath(url), ext, asyncESM.typeFlag, 'scope');
107-
// Conflict between explicit extension (.mjs, .cjs) and --type
108-
} else {
109-
throw new ERR_TYPE_MISMATCH(
110-
fileURLToPath(url), ext, asyncESM.typeFlag, 'extension');
111-
}
112-
}
113-
114-
return format;
115-
}
116-
11752
function resolve(specifier, parentURL) {
11853
if (NativeModule.canBeRequiredByUsers(specifier)) {
11954
return {
@@ -138,8 +73,39 @@ function resolve(specifier, parentURL) {
13873
url.hash = old.hash;
13974
}
14075

141-
const format = getModuleFormat(url, isMain, parentURL);
76+
const type = getPackageType(url.href);
77+
78+
const ext = extname(url.pathname);
79+
const extMap =
80+
type !== TYPE_MODULE ? legacyExtensionFormatMap : extensionFormatMap;
81+
let format = extMap[ext];
14282

83+
if (isMain && asyncESM.typeFlag) {
84+
// Conflict between explicit extension (.mjs, .cjs) and --type
85+
if (ext === '.cjs' && asyncESM.typeFlag === 'module' ||
86+
ext === '.mjs' && asyncESM.typeFlag === 'commonjs') {
87+
throw new ERR_TYPE_MISMATCH(
88+
fileURLToPath(url), ext, asyncESM.typeFlag, 'extension');
89+
}
90+
91+
// Conflict between package scope type and --type
92+
if (ext === '.js') {
93+
if (type === TYPE_MODULE && asyncESM.typeFlag === 'commonjs' ||
94+
type === TYPE_COMMONJS && asyncESM.typeFlag === 'module') {
95+
throw new ERR_TYPE_MISMATCH(
96+
fileURLToPath(url), ext, asyncESM.typeFlag, 'scope');
97+
}
98+
}
99+
}
100+
if (!format) {
101+
if (isMain && asyncESM.typeFlag)
102+
format = asyncESM.typeFlag;
103+
else if (isMain)
104+
format = type === TYPE_MODULE ? 'module' : 'commonjs';
105+
else
106+
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
107+
fileURLToPath(parentURL));
108+
}
143109
return { url: `${url}`, format };
144110
}
145111

src/env.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,17 @@ struct PackageConfig {
8282
struct HasMain {
8383
enum Bool { No, Yes };
8484
};
85-
struct IsESM {
85+
struct IsModule {
8686
enum Bool { No, Yes };
8787
};
88+
struct PackageType {
89+
enum Type : uint32_t { None = 0, CommonJS, Module };
90+
};
8891
const Exists::Bool exists;
8992
const IsValid::Bool is_valid;
9093
const HasMain::Bool has_main;
9194
const std::string main;
92-
const IsESM::Bool esm;
95+
const PackageType::Type type;
9396
};
9497
} // namespace loader
9598

@@ -149,6 +152,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
149152
V(channel_string, "channel") \
150153
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
151154
V(code_string, "code") \
155+
V(commonjs_string, "commonjs") \
152156
V(config_string, "config") \
153157
V(constants_string, "constants") \
154158
V(crypto_dsa_string, "dsa") \
@@ -218,7 +222,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
218222
V(kill_signal_string, "killSignal") \
219223
V(kind_string, "kind") \
220224
V(library_string, "library") \
221-
V(legacy_string, "legacy") \
222225
V(mac_string, "mac") \
223226
V(main_string, "main") \
224227
V(max_buffer_string, "maxBuffer") \

0 commit comments

Comments
 (0)