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

Commit c894000

Browse files
guybedfordMylesBorins
authored andcommitted
esm: scoped --type, cpp refactoring
1 parent 0a47098 commit c894000

File tree

9 files changed

+188
-146
lines changed

9 files changed

+188
-146
lines changed

doc/api/esm.md

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -541,14 +541,11 @@ of these top-level routines.
541541
542542
_isMain_ is **true** when resolving the Node.js application entry point.
543543
544-
If the top-level `--type` is _"commonjs"_, then the ESM resolver is skipped
545-
entirely for the CommonJS loader.
546-
547-
If the top-level `--type` is _"module"_, then the ESM resolver is used
548-
as described here, with the conditional `--type` check in **ESM_FORMAT**.
544+
When using the `--type` flag, it overrides the ESM_FORMAT result while
545+
providing errors in the case of explicit conflicts.
549546
550547
<details>
551-
<summary>Resolver algorithm psuedocode</summary>
548+
<summary>Resolver algorithm specification</summary>
552549
553550
**ESM_RESOLVE(_specifier_, _parentURL_, _isMain_)**
554551
> 1. Let _resolvedURL_ be **undefined**.
@@ -628,25 +625,20 @@ PACKAGE_MAIN_RESOLVE(_packageURL_, _pjson_)
628625
629626
**ESM_FORMAT(_url_, _isMain_)**
630627
> 1. Assert: _url_ corresponds to an existing file.
631-
> 1. If _isMain_ is **true** and the `--type` flag is _"module"_, then
632-
> 1. If _url_ ends with _".cjs"_, then
633-
> 1. Throw a _Type Mismatch_ error.
634-
> 1. Return _"module"_.
635628
> 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_).
636-
> 1. If _pjson_ is **null** and _isMain_ is **true**, then
637-
> 1. If _url_ ends in _".mjs"_, then
638-
> 1. Return _"module"_.
639-
> 1. Return _"commonjs"_.
640-
> 1. If _pjson.type_ exists and is _"module"_, then
641-
> 1. If _url_ ends in _".cjs"_, then
642-
> 1. Return _"commonjs"_.
629+
> 1. If _url_ ends in _".mjs"_, then
643630
> 1. Return _"module"_.
644-
> 1. Otherwise,
645-
> 1. If _url_ ends in _".mjs"_, then
646-
> 1. Return _"module"_.
647-
> 1. If _url_ does not end in _".js"_, then
648-
> 1. Throw an _Unsupported File Extension_ error.
631+
> 1. If _url_ ends in _".cjs"_, then
649632
> 1. Return _"commonjs"_.
633+
> 1. If _pjson?.type_ exists and is _"module"_, then
634+
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, then
635+
> 1. Return _"module"_.
636+
> 1. Throw an _Unsupported File Extension_ error.
637+
> 1. Otherwise,
638+
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, _".json"_ or
639+
> _".node"_, then
640+
> 1. Return _"commonjs"_.
641+
> 1. Throw an _Unsupported File Extension_ error.
650642
651643
READ_PACKAGE_SCOPE(_url_)
652644
> 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: 36 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
'use strict';
22

3-
const internalFS = require('internal/fs/utils');
43
const { NativeModule } = require('internal/bootstrap/loaders');
5-
const { extname } = require('path');
6-
const { realpathSync, readFileSync } = require('fs');
7-
const { getOptionValue } = require('internal/options');
8-
const preserveSymlinks = getOptionValue('--preserve-symlinks');
9-
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
10-
const { ERR_INVALID_PACKAGE_CONFIG,
11-
ERR_TYPE_MISMATCH,
4+
const { ERR_TYPE_MISMATCH,
125
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
6+
const { getOptionValue } = require('internal/options');
137
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
148
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
15-
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
9+
const { pathToFileURL, fileURLToPath } = require('internal/url');
1610
const asyncESM = require('internal/process/esm_loader');
17-
18-
const realpathCache = new Map();
19-
// TOOD(@guybedford): Shared cache with C++
20-
const pjsonCache = new Map();
11+
const preserveSymlinks = getOptionValue('--preserve-symlinks');
12+
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
13+
const { extname } = require('path');
2114

2215
const extensionFormatMap = {
2316
'__proto__': null,
@@ -45,75 +38,6 @@ if (experimentalJsonModules) {
4538
});
4639
}
4740

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-
11741
function resolve(specifier, parentURL) {
11842
if (NativeModule.canBeRequiredByUsers(specifier)) {
11943
return {
@@ -126,19 +50,39 @@ function resolve(specifier, parentURL) {
12650
if (isMain)
12751
parentURL = pathToFileURL(`${process.cwd()}/`).href;
12852

129-
let url = moduleWrapResolve(specifier, parentURL);
53+
const { url, type } =
54+
moduleWrapResolve(specifier, parentURL,
55+
isMain ? !preserveSymlinksMain : !preserveSymlinks);
56+
57+
const ext = extname(url.pathname);
58+
let format =
59+
(type !== 2 ? legacyExtensionFormatMap : extensionFormatMap)[ext];
60+
61+
if (isMain && asyncESM.typeFlag) {
62+
// Conflict between explicit extension (.mjs, .cjs) and --type
63+
if (ext === '.cjs' && asyncESM.typeFlag === 'module' ||
64+
ext === '.mjs' && asyncESM.typeFlag === 'commonjs') {
65+
throw new ERR_TYPE_MISMATCH(
66+
fileURLToPath(url), ext, asyncESM.typeFlag, 'extension');
67+
}
13068

131-
if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
132-
const real = realpathSync(fileURLToPath(url), {
133-
[internalFS.realpathCacheKey]: realpathCache
134-
});
135-
const old = url;
136-
url = pathToFileURL(real);
137-
url.search = old.search;
138-
url.hash = old.hash;
69+
// Conflict between package scope type and --type
70+
if (ext === '.js') {
71+
if (type === 2 && asyncESM.typeFlag === 'commonjs' ||
72+
type === 1 && asyncESM.typeFlag === 'module') {
73+
throw new ERR_TYPE_MISMATCH(
74+
fileURLToPath(url), ext, asyncESM.typeFlag, 'scope');
75+
}
76+
}
13977
}
14078

141-
const format = getModuleFormat(url, isMain, parentURL);
79+
if (!format) {
80+
if (isMain)
81+
format = type === 2 ? 'module' : 'commonjs';
82+
else
83+
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
84+
fileURLToPath(parentURL));
85+
}
14286

14387
return { url: `${url}`, format };
14488
}

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, 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)