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

Cpp Refactoring Ensuring avoiding duplicate package cache #58

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 14 additions & 22 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,14 +541,11 @@ of these top-level routines.

_isMain_ is **true** when resolving the Node.js application entry point.

If the top-level `--type` is _"commonjs"_, then the ESM resolver is skipped
entirely for the CommonJS loader.

If the top-level `--type` is _"module"_, then the ESM resolver is used
as described here, with the conditional `--type` check in **ESM_FORMAT**.
When using the `--type` flag, it overrides the ESM_FORMAT result while
providing errors in the case of explicit conflicts.

<details>
<summary>Resolver algorithm psuedocode</summary>
<summary>Resolver algorithm specification</summary>

**ESM_RESOLVE(_specifier_, _parentURL_, _isMain_)**
> 1. Let _resolvedURL_ be **undefined**.
Expand Down Expand Up @@ -628,25 +625,20 @@ PACKAGE_MAIN_RESOLVE(_packageURL_, _pjson_)

**ESM_FORMAT(_url_, _isMain_)**
> 1. Assert: _url_ corresponds to an existing file.
> 1. If _isMain_ is **true** and the `--type` flag is _"module"_, then
> 1. If _url_ ends with _".cjs"_, then
> 1. Throw a _Type Mismatch_ error.
> 1. Return _"module"_.
> 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_).
> 1. If _pjson_ is **null** and _isMain_ is **true**, then
> 1. If _url_ ends in _".mjs"_, then
> 1. Return _"module"_.
> 1. Return _"commonjs"_.
> 1. If _pjson.type_ exists and is _"module"_, then
> 1. If _url_ ends in _".cjs"_, then
> 1. Return _"commonjs"_.
> 1. If _url_ ends in _".mjs"_, then
> 1. Return _"module"_.
> 1. Otherwise,
> 1. If _url_ ends in _".mjs"_, then
> 1. Return _"module"_.
> 1. If _url_ does not end in _".js"_, then
> 1. Throw an _Unsupported File Extension_ error.
> 1. If _url_ ends in _".cjs"_, then
> 1. Return _"commonjs"_.
> 1. If _pjson?.type_ exists and is _"module"_, then
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, then
> 1. Return _"module"_.
> 1. Throw an _Unsupported File Extension_ error.
> 1. Otherwise,
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, _".json"_ or
> _".node"_, then
> 1. Return _"commonjs"_.
> 1. Throw an _Unsupported File Extension_ error.

READ_PACKAGE_SCOPE(_url_)
> 1. Let _scopeURL_ be _url_.
Expand Down
20 changes: 9 additions & 11 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -866,17 +866,15 @@ Module.runMain = function() {
// Load the main module--the command line argument.
if (experimentalModules) {
if (asyncESM === undefined) lazyLoadESM();
if (asyncESM.typeFlag !== 'commonjs') {
asyncESM.loaderPromise.then((loader) => {
return loader.import(pathToFileURL(process.argv[1]).pathname);
})
.catch((e) => {
internalBinding('task_queue').triggerFatalException(e);
});
// Handle any nextTicks added in the first tick of the program
process._tickCallback();
return;
}
asyncESM.loaderPromise.then((loader) => {
return loader.import(pathToFileURL(process.argv[1]).pathname);
})
.catch((e) => {
internalBinding('task_queue').triggerFatalException(e);
});
// Handle any nextTicks added in the first tick of the program
process._tickCallback();
return;
}
Module._load(process.argv[1], null, true);
// Handle any nextTicks added in the first tick of the program
Expand Down
128 changes: 36 additions & 92 deletions lib/internal/modules/esm/default_resolve.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
'use strict';

const internalFS = require('internal/fs/utils');
const { NativeModule } = require('internal/bootstrap/loaders');
const { extname } = require('path');
const { realpathSync, readFileSync } = require('fs');
const { getOptionValue } = require('internal/options');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const { ERR_INVALID_PACKAGE_CONFIG,
ERR_TYPE_MISMATCH,
const { ERR_TYPE_MISMATCH,
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
const { getOptionValue } = require('internal/options');
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
const { pathToFileURL, fileURLToPath } = require('internal/url');
const asyncESM = require('internal/process/esm_loader');

const realpathCache = new Map();
// TOOD(@guybedford): Shared cache with C++
const pjsonCache = new Map();
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const { extname } = require('path');

const extensionFormatMap = {
'__proto__': null,
Expand Down Expand Up @@ -45,75 +38,6 @@ if (experimentalJsonModules) {
});
}

function readPackageConfig(path, parentURL) {
const existing = pjsonCache.get(path);
if (existing !== undefined)
return existing;
try {
return JSON.parse(readFileSync(path).toString());
} catch (e) {
if (e.code === 'ENOENT') {
pjsonCache.set(path, null);
return null;
} else if (e instanceof SyntaxError) {
throw new ERR_INVALID_PACKAGE_CONFIG(path, fileURLToPath(parentURL));
}
throw e;
}
}

function getPackageBoundaryConfig(url, parentURL) {
let pjsonURL = new URL('package.json', url);
while (true) {
const pcfg = readPackageConfig(fileURLToPath(pjsonURL), parentURL);
if (pcfg)
return pcfg;

const lastPjsonURL = pjsonURL;
pjsonURL = new URL('../package.json', pjsonURL);

// Terminates at root where ../package.json equals ../../package.json
// (can't just check "/package.json" for Windows support).
if (pjsonURL.pathname === lastPjsonURL.pathname)
return;
}
}

function getModuleFormat(url, isMain, parentURL) {
const pcfg = getPackageBoundaryConfig(url, parentURL);

const legacy = !pcfg || pcfg.type !== 'module';

const ext = extname(url.pathname);

let format = (legacy ? legacyExtensionFormatMap : extensionFormatMap)[ext];

if (!format) {
if (isMain)
format = legacy ? 'commonjs' : 'module';
else
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
fileURLToPath(parentURL));
}

// Check for mismatch between --type and file extension,
// and between --type and the "type" field in package.json.
if (isMain && format !== 'module' && asyncESM.typeFlag === 'module') {
// Conflict between package scope type and --type
if (ext === '.js') {
if (pcfg && pcfg.type)
throw new ERR_TYPE_MISMATCH(
fileURLToPath(url), ext, asyncESM.typeFlag, 'scope');
// Conflict between explicit extension (.mjs, .cjs) and --type
} else {
throw new ERR_TYPE_MISMATCH(
fileURLToPath(url), ext, asyncESM.typeFlag, 'extension');
}
}

return format;
}

function resolve(specifier, parentURL) {
if (NativeModule.canBeRequiredByUsers(specifier)) {
return {
Expand All @@ -126,19 +50,39 @@ function resolve(specifier, parentURL) {
if (isMain)
parentURL = pathToFileURL(`${process.cwd()}/`).href;

let url = moduleWrapResolve(specifier, parentURL);
const { url, type } =
moduleWrapResolve(specifier, parentURL,
isMain ? !preserveSymlinksMain : !preserveSymlinks);

const ext = extname(url.pathname);
let format =
(type !== 2 ? legacyExtensionFormatMap : extensionFormatMap)[ext];

if (isMain && asyncESM.typeFlag) {
// Conflict between explicit extension (.mjs, .cjs) and --type
if (ext === '.cjs' && asyncESM.typeFlag === 'module' ||
ext === '.mjs' && asyncESM.typeFlag === 'commonjs') {
throw new ERR_TYPE_MISMATCH(
fileURLToPath(url), ext, asyncESM.typeFlag, 'extension');
}

if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
const real = realpathSync(fileURLToPath(url), {
[internalFS.realpathCacheKey]: realpathCache
});
const old = url;
url = pathToFileURL(real);
url.search = old.search;
url.hash = old.hash;
// Conflict between package scope type and --type
if (ext === '.js') {
if (type === 2 && asyncESM.typeFlag === 'commonjs' ||
type === 1 && asyncESM.typeFlag === 'module') {
throw new ERR_TYPE_MISMATCH(
fileURLToPath(url), ext, asyncESM.typeFlag, 'scope');
}
}
}

const format = getModuleFormat(url, isMain, parentURL);
if (!format) {
if (isMain)
format = type === 2 ? 'module' : 'commonjs';
else
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
fileURLToPath(parentURL));
}

return { url: `${url}`, format };
}
Expand Down
8 changes: 4 additions & 4 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ struct PackageConfig {
struct HasMain {
enum Bool { No, Yes };
};
struct IsESM {
enum Bool { No, Yes };
struct PackageType {
enum Type : uint32_t { None, CommonJS, Module };
};
const Exists::Bool exists;
const IsValid::Bool is_valid;
const HasMain::Bool has_main;
const std::string main;
const IsESM::Bool esm;
const PackageType::Type type;
};
} // namespace loader

Expand Down Expand Up @@ -149,6 +149,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(channel_string, "channel") \
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
V(code_string, "code") \
V(commonjs_string, "commonjs") \
V(config_string, "config") \
V(constants_string, "constants") \
V(crypto_dsa_string, "dsa") \
Expand Down Expand Up @@ -218,7 +219,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(kill_signal_string, "killSignal") \
V(kind_string, "kind") \
V(library_string, "library") \
V(legacy_string, "legacy") \
V(mac_string, "mac") \
V(main_string, "main") \
V(max_buffer_string, "maxBuffer") \
Expand Down
Loading