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

Commit 842ce61

Browse files
committed
esm: add experimental .json support to loader
With the new flag `--experimental-json-modules` it is now possible to import .json files. It piggy backs on the current cjs loader implementation, so it only exports a default. This is a bit of a hack, and it should potentially have it's own loader, especially if we change the cjs loader at all. The behavior for .json in the cjs loader matches the current planned behavior if json modules were to be standardized, specifically that a .json module only exports a default. Refs: nodejs/modules#255 Refs: whatwg/html#4315 Refs: WICG/webcomponents#770
1 parent 49a38bb commit 842ce61

File tree

6 files changed

+54
-3
lines changed

6 files changed

+54
-3
lines changed

lib/internal/modules/esm/default_resolve.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
1010
const { ERR_INVALID_PACKAGE_CONFIG,
1111
ERR_TYPE_MISMATCH,
1212
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
13+
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
1314
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
1415
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
1516
const asyncESM = require('internal/process/esm_loader');
@@ -29,11 +30,20 @@ const legacyExtensionFormatMap = {
2930
'__proto__': null,
3031
'.cjs': 'commonjs',
3132
'.js': 'commonjs',
32-
'.json': 'commonjs',
3333
'.mjs': 'module',
3434
'.node': 'commonjs'
3535
};
3636

37+
if (experimentalJsonModules) {
38+
// This is a total hack
39+
Object.assign(extensionFormatMap, {
40+
'.json': 'json'
41+
});
42+
Object.assign(legacyExtensionFormatMap, {
43+
'.json': 'json'
44+
});
45+
}
46+
3747
function readPackageConfig(path, parentURL) {
3848
const existing = pjsonCache.get(path);
3949
if (existing !== undefined)
@@ -103,6 +113,7 @@ function getModuleFormat(url, isMain, parentURL) {
103113
return format;
104114
}
105115

116+
106117
function resolve(specifier, parentURL) {
107118
if (NativeModule.canBeRequiredByUsers(specifier)) {
108119
return {

lib/internal/modules/esm/translators.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
const { NativeModule } = require('internal/bootstrap/loaders');
44
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
55
const {
6-
stripShebang
6+
stripShebang,
7+
stripBOM
78
} = require('internal/modules/cjs/helpers');
89
const CJSModule = require('internal/modules/cjs/loader');
910
const internalURLModule = require('internal/url');
@@ -13,12 +14,13 @@ const fs = require('fs');
1314
const {
1415
SafeMap,
1516
} = primordials;
16-
const { URL } = require('url');
17+
const { fileURLToPath, URL } = require('url');
1718
const { debuglog, promisify } = require('util');
1819
const esmLoader = require('internal/process/esm_loader');
1920

2021
const readFileAsync = promisify(fs.readFile);
2122
const StringReplace = Function.call.bind(String.prototype.replace);
23+
const JsonParse = JSON.parse;
2224

2325
const debug = debuglog('esm');
2426

@@ -94,3 +96,21 @@ translators.set('builtin', async function(url) {
9496
reflect.exports.default.set(module.exports);
9597
});
9698
});
99+
100+
// Strategy for loading a JSON file
101+
translators.set('json', async (url) => {
102+
debug(`Translating JSONModule ${url}`);
103+
debug(`Loading JSONModule ${url}`);
104+
const pathname = fileURLToPath(url);
105+
const content = await readFileAsync(pathname, 'utf-8');
106+
return createDynamicModule(['default'], url, (reflect) => {
107+
debug(`Parsing JSONModule ${url}`);
108+
try {
109+
const exports = JsonParse(stripBOM(content));
110+
reflect.exports.default.set(exports);
111+
} catch (err) {
112+
err.message = pathname + ': ' + err.message;
113+
throw err;
114+
}
115+
});
116+
});

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ DebugOptionsParser::DebugOptionsParser() {
202202
}
203203

204204
EnvironmentOptionsParser::EnvironmentOptionsParser() {
205+
AddOption("--experimental-json-modules",
206+
"experimental JSON interop support for the ES Module loader",
207+
&EnvironmentOptions::experimental_json_modules,
208+
kAllowedInEnvironment);
205209
AddOption("--experimental-modules",
206210
"experimental ES Module support and caching modules",
207211
&EnvironmentOptions::experimental_modules,

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class DebugOptions : public Options {
8585
class EnvironmentOptions : public Options {
8686
public:
8787
bool abort_on_uncaught_exception = false;
88+
bool experimental_json_modules = false;
8889
bool experimental_modules = false;
8990
std::string module_type;
9091
std::string experimental_policy;

test/es-module/test-esm-json.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Flags: --experimental-modules --experimental-json-modules
2+
/* eslint-disable node-core/required-modules */
3+
4+
import '../common/index.mjs';
5+
import { strictEqual } from 'assert';
6+
7+
import secret from '../fixtures/experimental.json';
8+
import * as namespace from '../fixtures/experimental.json';
9+
10+
strictEqual(secret.ofLife, 42);
11+
strictEqual(namespace.ofLife, undefined);
12+
strictEqual(namespace.default.ofLife, 42);

test/fixtures/experimental.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"ofLife": 42
3+
}

0 commit comments

Comments
 (0)