Skip to content

Commit

Permalink
functional providers, format separation, resolve hook prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Sep 3, 2017
1 parent 1adf7cb commit 6e328d0
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 145 deletions.
54 changes: 28 additions & 26 deletions lib/internal/loader/Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {

const ModuleMap = require('internal/loader/ModuleMap');
const ModuleJob = require('internal/loader/ModuleJob');
const resolveRequestUrl = require('internal/loader/resolveRequestUrl');
const { formatProviders, resolve } = require('internal/loader/ModuleRequest');
const errors = require('internal/errors');

function getBase() {
Expand All @@ -34,39 +34,41 @@ class Loader {
this.base = base;
}

async resolve(specifier) {
const request = resolveRequestUrl(this.base, specifier);
if (request.url.protocol !== 'file:') {
setModuleResolver(resolver) {
resolve = resolver;
}

async resolve(specifier, parentUrlOrString = this.base) {
const { url, format } = await resolve(specifier, parentUrlOrString);
if (typeof url === 'string') {
url = new URL(url);
}
else if (!(url instanceof URL)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'URL');
}
if (url.protocol !== 'file:') {
throw new errors.Error('ERR_INVALID_PROTOCOL',
request.url.protocol, 'file:');
}
return request.url;
if (!formatProviders.has(format)) {
throw new errors.Error('ERR_INVALID_FORMAT', format);
}
return { url, format };
}

async getModuleJob(dependentJob, specifier) {
if (!this.moduleMap.has(dependentJob.url)) {
throw new errors.Error('ERR_MISSING_MODULE', dependentJob.url);
}
const request = await resolveRequestUrl(dependentJob.url, specifier);
const url = `${request.url}`;
if (this.moduleMap.has(url)) {
return this.moduleMap.get(url);
async getModuleJob(specifier, parentUrlOrString = this.base) {
const { url, format } = await this.resolve(specifier, parentUrlOrString);
const urlString = `${url}`;
let job = this.moduleMap.get(urlString);
if (job === undefined) {
job = new ModuleJob(this, url, urlString, formatProviders.get(format));
this.moduleMap.set(urlString, job);
}
const dependencyJob = new ModuleJob(this, request);
this.moduleMap.set(url, dependencyJob);
return dependencyJob;
return job;
}

async import(specifier) {
const request = await resolveRequestUrl(this.base, specifier);
const url = `${request.url}`;
let job;
if (this.moduleMap.has(url)) {
job = this.moduleMap.get(url);
} else {
job = new ModuleJob(this, request);
this.moduleMap.set(url, job);
}
async import(specifier, parentUrlOrString = this.base) {
const job = await this.getModuleJob(specifier, parentUrlOrString);
const module = await job.run();
return getNamespaceOfModuleWrap(module);
}
Expand Down
16 changes: 7 additions & 9 deletions lib/internal/loader/ModuleJob.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,28 @@ const resolvedPromise = SafePromise.resolve();
const resolvedArrayPromise = SafePromise.resolve([]);
const { ModuleWrap } = require('internal/loader/ModuleWrap');

const NOOP = () => { /* No-op */ };
class ModuleJob {
/**
* @param {module: ModuleWrap?, compiled: Promise} moduleProvider
*/
constructor(loader, moduleProvider, url) {
this.url = `${moduleProvider.url}`;
this.moduleProvider = moduleProvider;
constructor(loader, url, urlString, moduleProvider) {
this.url = url;
this.loader = loader;
this.error = null;
this.hadError = false;

if (moduleProvider instanceof ModuleWrap !== true) {
// linked == promise for dependency jobs, with module populated,
// module wrapper linked
this.modulePromise = this.moduleProvider.createModule();
this.moduleProvider = moduleProvider;
this.modulePromise = this.moduleProvider(url);
this.module = undefined;
const linked = async () => {
const dependencyJobs = [];
this.module = await this.modulePromise;
this.module.link(async (dependencySpecifier) => {
const dependencyJobPromise =
this.loader.getModuleJob(this, dependencySpecifier);
this.loader.getModuleJob(dependencySpecifier, urlString);
dependencyJobs.push(dependencyJobPromise);
const dependencyJob = await dependencyJobPromise;
return dependencyJob.modulePromise;
Expand All @@ -40,9 +39,8 @@ class ModuleJob {
//module wrapper instantiated
this.instantiated = undefined;
} else {
const getModuleProvider = async () => moduleProvider;
this.modulePromise = getModuleProvider();
this.moduleProvider = { finish: NOOP };
this.moduleProvider = async () => moduleProvider;
this.modulePromise = this.moduleProvider();
this.module = moduleProvider;
this.linked = resolvedArrayPromise;
this.instantiated = this.modulePromise;
Expand Down
121 changes: 121 additions & 0 deletions lib/internal/loader/ModuleRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use strict';

const { URL } = require('url');
const internalCJSModule = require('internal/module');
const internalURLModule = require('internal/url');
const internalFS = require('internal/fs');
const NativeModule = require('native_module');
const { extname } = require('path');
const { realpathSync } = require('fs');
const preserveSymlinks = !!process.binding('config').preserveSymlinks;
const {
ModuleWrap,
createDynamicModule
} = require('internal/loader/ModuleWrap');
const errors = require('internal/errors');

const search = require('internal/loader/search');
const asyncReadFile = require('util').promisify(require('fs').readFile);
const debug = require('util').debuglog('esm');

const realpathCache = new Map();

const formatProviders = new Map();
exports.formatProviders = formatProviders;

// Strategy for loading a standard JavaScript module
formatProviders.set('esm', async (url) => {
const source = `${await asyncReadFile(url)}`;
debug(`Loading StandardModule ${url}`);
return new ModuleWrap(internalCJSModule.stripShebang(source),
`${url}`);
});

// Strategy for loading a node-style CommonJS module
formatProviders.set('cjs', async (url) => {
const ctx = createDynamicModule(['default'], url, (reflect) => {
debug(`Loading CJSModule ${url.pathname}`);
const CJSModule = require('module');
const pathname = internalURLModule.getPathFromURL(url);
ctx.reflect.exports.default.set(CJSModule._load(pathname));
});
return ctx.module;
});

// Strategy for loading a node builtin CommonJS module that isn't
// through normal resolution
formatProviders.set('native', async (url) => {
const ctx = createDynamicModule(['default'], url, (reflect) => {
debug(`Loading CJSModule ${url.pathname}`);
const CJSModule = require('module');
const pathname = internalURLModule.getPathFromURL(url);
ctx.reflect.exports.default.set(CJSModule._load(pathname));
});
return ctx.module;
});

formatProviders.set('json', async (url) => {
const source = `${await asyncReadFile(url)}`;
const ctx = createDynamicModule(['default'], url, (reflect) => {
debug(`Loading JSONModule ${url.pathname}`);
const json = JSON.parse(source);
ctx.reflect.exports.default.set(json);
});
return ctx.module;
});

// TODO: make this native binary handling only
formatProviders.set('binary', async (url) => {
const source = `${await asyncReadFile(url)}`;
const ctx = createDynamicModule(['default'], url, (reflect) => {
debug(`Loading JSONModule ${url.pathname}`);
const json = JSON.parse(source);
ctx.reflect.exports.default.set(json);
});
return ctx.module;
});

function normalizeBaseURL(baseURLOrString) {
if (baseURLOrString instanceof URL) return baseURLOrString;
if (typeof baseURLOrString === 'string') return new URL(baseURLOrString);
return undefined;
}

exports.resolve = resolve;
function resolve(specifier, parentURLOrString) {
if (NativeModule.nonInternalExists(specifier)) {
return {
url: new URL(`node:${specifier}`),
format: 'native'
};
}

const parentURL = normalizeBaseURL(parentURLOrString);
let url = search(specifier, parentURL);

if (url.protocol !== 'file:') {
throw new errors.Error('ERR_INVALID_PROTOCOL', url.protocol, 'file:');
}

if (!preserveSymlinks) {
const real = realpathSync(internalURLModule.getPathFromURL(url), {
[internalFS.realpathCacheKey]: realpathCache
});
const old = url;
url = internalURLModule.getURLFromFilePath(real);
url.search = old.search;
url.hash = old.hash;
}

const ext = extname(url.pathname);
switch (ext) {
case '.mjs':
return { url, format: 'esm' };
case '.json':
return { url, format: 'json' };
case '.node':
return { url, format: 'binary' };
default:
return { url, format: 'cjs' };
}
};
104 changes: 0 additions & 104 deletions lib/internal/loader/resolveRequestUrl.js

This file was deleted.

12 changes: 7 additions & 5 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const errors = require('internal/errors');
const Loader = require('internal/loader/Loader');
const ModuleJob = require('internal/loader/ModuleJob');
const { createDynamicModule } = require('internal/loader/ModuleWrap');
const { resolve: defaultResolver } = require('internal/loader/ModuleRequest');
const ESMLoader = new Loader();

function stat(filename) {
Expand Down Expand Up @@ -78,6 +79,9 @@ Module._extensions = Object.create(null);
var modulePaths = [];
Module.globalPaths = [];

Module.resolve = defaultResolver;
Module.setModuleResolver = ESMLoader.setModuleResolver;

Module.wrapper = NativeModule.wrapper;
Module.wrap = NativeModule.wrap;
Module._debug = util.debuglog('module');
Expand Down Expand Up @@ -547,14 +551,12 @@ Module.prototype.load = function(filename) {

if (experimentalModules) {
const url = getURLFromFilePath(filename);
if (ESMLoader.moduleMap.has(`${url}`) !== true) {
const urlString = `${url}`;
if (ESMLoader.moduleMap.has(urlString) !== true) {
const ctx = createDynamicModule(['default'], url);
ctx.reflect.exports.default.set(this.exports);
ESMLoader.moduleMap.set(`${url}`,
ESMLoader.moduleMap.set(urlString,
new ModuleJob(ESMLoader, ctx.module));
} else {
ESMLoader.moduleMap.get(`${url}`).moduleProvider.finish(
Module._cache[filename]);
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
'lib/internal/loader/ModuleMap.js',
'lib/internal/loader/ModuleJob.js',
'lib/internal/loader/ModuleWrap.js',
'lib/internal/loader/resolveRequestUrl.js',
'lib/internal/loader/ModuleRequest.js',
'lib/internal/loader/search.js',
'lib/internal/safe_globals.js',
'lib/internal/net.js',
Expand Down

0 comments on commit 6e328d0

Please sign in to comment.