You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

create-esm-loader

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

create-esm-loader - npm Package Compare versions

Comparing version

to
0.0.1

123

lib/create-loader.js
// # create-loader.js
import semver from 'semver';
export default function createLoader(...args) {

@@ -49,39 +50,97 @@ return new Loader(...args).hooks();

// This function returns an object containing all Node.js loader hooks as
// properties so that the loader entry file can re-export them. It's here
// that we can do some checks of the Node version in the future if we want.
// properties so that the loader entry file can re-export them. See #1.
// Given that the api changed in v16.12.0, we'll inspect the current
// process version and adapt accordingly.
hooks() {
// For backwards compatibility purposes, we will manually compose
// `format()`, `fetch()` and `transform()` into a `load()` function.
const hook = id => (...args) => this.handleStack(id, ...args);
return {
resolve: hook('resolve'),
getFormat: hook('format'),
getSource: hook('fetch'),
const resolve = hook('resolve');
const getFormat = hook('format');
const getSource = hook('fetch');
// Handling the transform is fundamentally different as we need to
// chain results here!
transformSource: async (source, ctx, node) => {
let stack = await this.stack;
let fns = stack.transform || [];
let baseOptions = { ...this.options };
let mem = source;
let flag = true;
for (let { fn, options } of fns) {
let finalOptions = {
...baseOptions,
...options,
...ctx,
};
let result = await fn(mem, finalOptions);
if (result) {
flag = false;
// Handling transformation is fundamentally different as we have to
// chain results here.
const transformSource = async (source, ctx, node) => {
let stack = await this.stack;
let fns = stack.transform || [];
let baseOptions = { ...this.options };
let mem = source;
let flag = true;
for (let { fn, options } of fns) {
let finalOptions = {
...baseOptions,
...options,
...ctx,
};
let result = await fn(mem, finalOptions);
if (result || typeof result === 'string') {
flag = false;
if (typeof result === 'string') {
mem = result;
} else {
mem = result.source;
}
}
if (flag) {
return node(source, ctx, node);
} else {
return { source: mem };
}
if (flag) {
return node(source, ctx, node);
} else {
return { source: mem };
}
};
// Now compose the correct hooks based on the Node version we're
// running.
if (semver.satisfies(process.version, '<16.12.0')) {
return {
resolve,
getFormat,
getSource,
transformSource,
};
}
// If we reach this point, it means we're running on Node v16.12.0 or
// higher, which uses the new approach. We only have to export a
// `resolve` and `load` function here, but the difficulty is that the
// `load()` function has to be composed manually!
const load = async function(url, ctx, defaultLoad) {
// If the format was already specified by the resolve hook, we
// won't try to fetch it again. Note that this functionality is
// specific to v16.12.
const grab = (obj = {}) => obj.format;
let {
format = await getFormat(url, ctx, noop).then(grab),
} = ctx;
// Mock the default `getSource` function. What's important here is
// that if we the default getSource is used, we'll also set it as
// default format!
const defaultGetSource = async (url, ctx) => {
let result = await defaultLoad(url, { format });
if (!format) {
format = result.format;
}
},
return result;
};
let { source } = await getSource(url, ctx, defaultGetSource);
// At last transform.
const defaultTransform = source => ({ source });
let transform = await transformSource(
source,
{ url, format },
defaultTransform,
);
return {
format,
source: transform.source,
};
};
return { resolve, load };
}

@@ -121,3 +180,6 @@

let module = await import(def.loader);
let normalized = normalize(module.default);
let normalized = normalize({
hooks: module.default,
options: def.options,
});
this.fill(normalized, dummy);

@@ -214,1 +276,4 @@ })());

}
// # noop()
function noop() {}
{
"name": "create-esm-loader",
"version": "0.0.1-alpha.0",
"version": "0.0.1",
"description": "A utility library for creating Node loader hooks",

@@ -32,6 +32,10 @@ "type": "module",

"devDependencies": {
"chai": "^4.2.0",
"chai": "^4.3.4",
"chai-spies": "^1.0.0",
"mocha": "^8.2.1"
"mocha": "^9.1.3",
"typescript": "^4.4.4"
},
"dependencies": {
"semver": "^7.3.5"
}
}

@@ -9,3 +9,3 @@ # create-esm-loader

Node 14 provides full support for native ES Modules without the need for transpilation.
While CommonJS is likely not to go anywhere soon, it is good practice to at least start thinking about migrating your codebase from CommonJS to ESM.
While CommonJS is likely not to go anywhere soon, it is good practice to [at least start thinking about migrating your codebase from CommonJS to ESM](https://blog.sindresorhus.com/get-ready-for-esm-aa53530b3f77).
In the `require`-world, we had [require.extensions](https://nodejs.org/api/modules.html#modules_require_extensions) if we wanted to load non-JS files into Node.

@@ -29,17 +29,9 @@ You could use this, for example, to load TypeScript files and compile them just-in-time.

ESM loaders must be written in ESM format.
This means that Node needs to interpret it as an ES Module as well, which means you either need to use the `.mjs` extension, or make sure that the nearest `package.json` contains a `{ "type": "module" }` field.
For more info, see https://nodejs.org/api/esm.html#esm_enabling.
`create-esm-loader` is inspired by Webpack.
You can start to create loaders like this:
You can pass it a configuration object and it will return a set of [loader hooks](https://nodejs.org/api/esm.html#hooks) which you then have to export manually.
This typically looks like
```js
// loader.mjs
// loader.js
import createLoader from 'create-esm-loader';
const loader = createLoader(config);
// In order for a loader to work on Node, you must export the appropriate hooks:
const { resolve, getFormat, getSource, transformSource } = loader;
export { resolve, getFormat, getSource, transformSource };
export const { resolve, load } = createLoader(config);
```

@@ -52,2 +44,22 @@

Note that in Node 16.12, the loader hooks [have changed](https://nodejs.org/docs/v16.12.0/api/esm.html#esm_loaders).
In previous versions, **including `16.11`**, you had to export `resolve()`, `getFormat()`, `getSource()` and `transformSource()`.
In Node `>=16.12.0`, you have to export `resolve()` and `load()` instead.
`create-esm-loader` is backwards compatible and is able to handle both.
This means that if you're writing a loader that needs to support `<16.12`, you have to export
```js
export const {
resolve,
getFormat,
getSource,
transformSource,
load,
} = createLoader(config);
```
ESM loaders must be written in ESM format.
This means that Node needs to interpret it as an ES Module as well, which means you either need to use the `.mjs` extension, or make sure that the nearest `package.json` contains a `{ "type": "module" }` field.
For more info, see https://nodejs.org/api/esm.html#esm_enabling.
### Basic configuration

@@ -72,5 +84,4 @@

```
Those methods respectively correspond to the `resolve()`, `getFormat()`, `getSource()` and `transform()` [loader hooks](https://nodejs.org/api/esm.html#esm_hooks) from Node.
The reason why the names don't match is to make abstraction of the underlying Node mechanism, which might still change in the future.
The hope is that if this happens, only this module will need to be updated and not the way you've written your loader configurations.
Those methods used to correspond respectively to the `resolve()`, `getFormat()`, `getSource()` and `transform()` [loader hooks](https://nodejs.org/docs/latest-v14.x/api/esm.html#esm_loaders) from Node, but as mentioned above the `getFormat()`, `getSource()` and `transform()` hooks have now been merged into a single `load()` hook.
The api of this module has not changed as it's explicit goal is to hide how Node handles loaders internally.

@@ -80,2 +91,20 @@ Every hook is optional and can be an async function, which is useful if you need to do some async logic within it.

### Node `^16.12`
If you only target node 16.12 and above, you can simplify your life a bit by specifying the format in the `resolve()` hook, omitting the need for a separate `format()` hook.
```js
// Will not work in Node < 16.12!!
const loader = createLoader({
resolve(specifier, opts) {
let url = new URL(specifier, opts.parentURL);
if (url.pathname.endsWith('.vue')) {
return {
format: 'module',
url: url.href,
};
}
},
});
```
### Advanced configurations

@@ -168,4 +197,2 @@

```js
import { URL } from 'url';
const tsLoader = {

@@ -186,8 +213,11 @@ resolve(specifier, opts) {

return {
source: ts.compile(String(source)),
source: ts.transpileModule(String(source), {
compilerOptions: {
module: ts.ModuleKind.ES2020,
},
}),
};
},
};
const { resolve, getFormat, getSource, transformSource } = createLoader(tsLoader);
export { resolve, getFormat, getSource, transformSource };
export const { resolve, load } = createLoader(tsLoader);

@@ -215,4 +245,3 @@ // Usage:

};
const { resolve, getFormat, getSource, transformSource } = createLoader(directoryLoader);
export { resolve, getFormat, getSource, transformSource };
export const { resolve, load } = createLoader(directoryLoader);

@@ -219,0 +248,0 @@ // Usage:

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet