Skip to content

Commit

Permalink
chore: Auto-generate the supported hooks & extensions in CI (#86)
Browse files Browse the repository at this point in the history
chore: Rewrite API docs
chore: Autogenerate supported extensions & hooks
chore: Update CI to generate README before prettier
chore: Update readme & run prettier
  • Loading branch information
phated committed Apr 8, 2022
1 parent 2cf15eb commit a1e9151
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 88 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,24 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Set Node.js version
uses: actions/setup-node@v2
with:
node-version: 16

- run: node --version
- run: npm --version

- name: Install npm dependencies
run: npm install

- name: Update README
run: npm run readme

- name: Prettier
uses: gulpjs/prettier_action@v3.0
with:
commit_message: 'chore: Run prettier'
commit_message: 'chore: Update readme & run prettier'
prettier_options: '--write .'

test:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ test.xunit

# Created in fixtures directories by test
package-lock.json

# Generated by our README generator
scripts/extensions.yaml
scripts/jsVariants.yaml
259 changes: 172 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,69 +14,32 @@ A dictionary of file extensions and associated module loaders.

This is used by [Liftoff] to automatically require dependencies for configuration files, and by [rechoir] for registering module loaders.

## How to use it

Consumers should use the exported `extensions` or `jsVariants` object to determine which module should be loaded for a given extension. If a matching extension is found, consumers should do the following:

1. If the value is null, do nothing.
2. If the value is a string, try to require it.
3. If the value is an object, try to require the `module` property. If successful, the `register` property (a function) should be called with the module passed as the first argument.
4. If the value is an array, iterate over it, attempting step #2 or #3 until one of the attempts does not throw.

## API

### extensions
This module provides two top-level properties: `extensions` and `jsVariants`.

**Note:** This module does not depend on any of the loaders it recommends; instead, end-users are expected to install the hooks they want to use for the file types they want to use. See supported extensions and their hooks in the sections below.

### `extensions`

Map file types to modules which provide a [require.extensions] loader.
A mapping of file extensions to modules which provide a [require.extensions] loader.

File extension keys are all in the format of `'.foo'` or `'.foo.bar'` and module loader values are either `null` if the loader should fallthrough to node's loader,
or a string representing the module to be required, an object of `{ module: 'foobar', register: function }`, or an array containing those strings and/or objects.

A sample of an entry containing multiple hooks would look like:

```js
{
'.babel.js': {
module: '@babel/register',
register: function(hook) {
hook({
extensions: '.js',
rootMode: 'upward-optional',
ignore: [ignoreNonBabelAndNodeModules],
});
},
},
'.babel.ts': [
{
module: '@babel/register',
register: function(hook) {
hook({
extensions: '.ts',
rootMode: 'upward-optional',
ignore: [ignoreNonBabelAndNodeModules],
});
},
},
],
'.coffee': 'coffeescript/register',
'.coffee.md': 'coffeescript/register',
'.esm.js': {
module: 'esm',
register: function(hook) {
// register on .js extension due to https://github.com/joyent/node/blob/v0.12.0/lib/module.js#L353
// which only captures the final extension (.babel.js -> .js)
var esmLoader = hook(module);
require.extensions['.js'] = esmLoader('module')._extensions['.js'];
},
},
'.js': null,
'.json': null,
'.json5': 'json5/lib/register',
'.jsx': {
module: '@babel/register',
register: function(hook) {
hook({
extensions: '.jsx',
rootMode: 'upward-optional',
ignore: [ignoreNonBabelAndNodeModules],
});
},
},
'.litcoffee': 'coffeescript/register',
'.mjs': '/absolute/path/to/interpret/mjs-stub.js',
'.node': null,
'.toml': {
module: 'toml-require',
register: function(hook) {
hook.install();
},
},
'.ts': [
'ts-node/register',
'sucrase/register/ts',
Expand All @@ -91,59 +54,181 @@ Map file types to modules which provide a [require.extensions] loader.
},
},
],
'.tsx': [
'ts-node/register',
'sucrase/register',
{
module: '@babel/register',
register: function(hook) {
hook({
extensions: '.tsx',
rootMode: 'upward-optional',
ignore: [ignoreNonBabelAndNodeModules],
});
},
},
],
'.yaml': 'yaml-hook/register',
'.yml': 'yaml-hook/register',
}
```

### jsVariants

Same as above, but only include the extensions which are javascript variants.

## How to use it

Consumers should use the exported `extensions` or `jsVariants` object to determine which module should be loaded for a given extension. If a matching extension is found, consumers should do the following:

1. If the value is null, do nothing.

2. If the value is a string, try to require it.

3. If the value is an object, try to require the `module` property. If successful, the `register` property (a function) should be called with the module passed as the first argument.
**Supported extensions and their hooks**

```yaml file=scripts/extensions.yaml
.babel.js:
- '@babel/register'
.babel.jsx:
- '@babel/register'
.babel.ts:
- '@babel/register'
.babel.tsx:
- '@babel/register'
.coffee:
- coffeescript/register
.coffee.md:
- coffeescript/register
.esbuild.js:
- esbuild-register/dist/node
.esbuild.jsx:
- esbuild-register/dist/node
.esbuild.ts:
- esbuild-register/dist/node
.esbuild.tsx:
- esbuild-register/dist/node
.esm.js:
- esm
.js:
- built-in node.js loader
.json:
- built-in node.js loader
.json5:
- json5/lib/register
.jsx:
- '@babel/register'
- sucrase/register/jsx
.litcoffee:
- coffeescript/register
.mjs:
- interpret/mjs-stub
.node:
- built-in node.js loader
.sucrase.js:
- sucrase/dist/register
.sucrase.jsx:
- sucrase/dist/register
.sucrase.ts:
- sucrase/dist/register
.sucrase.tsx:
- sucrase/dist/register
.swc.js:
- '@swc/register'
.swc.jsx:
- '@swc/register'
.swc.ts:
- '@swc/register'
.swc.tsx:
- '@swc/register'
.toml:
- toml-require
.ts:
- ts-node/register
- sucrase/register/ts
- '@babel/register'
- esbuild-register/dist/node
- '@swc/register'
.tsx:
- ts-node/register
- sucrase/register/tsx
- '@babel/register'
- esbuild-register/dist/node
- '@swc/register'
.yaml:
- yaml-hook/register
.yml:
- yaml-hook/register
```

4. If the value is an array, iterate over it, attempting step #2 or #3 until one of the attempts does not throw.
### `jsVariants`

The `jsVariants` is the same mapping as above, but only include the extensions which are variants of JavaScript.

**Supported extensions and their hooks**

```yaml file=scripts/jsVariants.yaml
.babel.js:
- '@babel/register'
.babel.jsx:
- '@babel/register'
.babel.ts:
- '@babel/register'
.babel.tsx:
- '@babel/register'
.coffee:
- coffeescript/register
.coffee.md:
- coffeescript/register
.esbuild.js:
- esbuild-register/dist/node
.esbuild.jsx:
- esbuild-register/dist/node
.esbuild.ts:
- esbuild-register/dist/node
.esbuild.tsx:
- esbuild-register/dist/node
.esm.js:
- esm
.js:
- built-in node.js loader
.jsx:
- '@babel/register'
- sucrase/register/jsx
.litcoffee:
- coffeescript/register
.mjs:
- interpret/mjs-stub
.sucrase.js:
- sucrase/dist/register
.sucrase.jsx:
- sucrase/dist/register
.sucrase.ts:
- sucrase/dist/register
.sucrase.tsx:
- sucrase/dist/register
.swc.js:
- '@swc/register'
.swc.jsx:
- '@swc/register'
.swc.ts:
- '@swc/register'
.swc.tsx:
- '@swc/register'
.ts:
- ts-node/register
- sucrase/register/ts
- '@babel/register'
- esbuild-register/dist/node
- '@swc/register'
.tsx:
- ts-node/register
- sucrase/register/tsx
- '@babel/register'
- esbuild-register/dist/node
- '@swc/register'
```

## License

MIT

<!-- prettier-ignore-start -->

[downloads-image]: https://img.shields.io/npm/dm/interpret.svg?style=flat-square

[npm-url]: https://www.npmjs.com/package/interpret

[npm-image]: https://img.shields.io/npm/v/interpret.svg?style=flat-square

[ci-url]: https://github.com/gulpjs/interpret/actions?query=workflow:dev

[ci-image]: https://img.shields.io/github/workflow/status/gulpjs/interpret/dev?style=flat-square

[coveralls-url]: https://coveralls.io/r/gulpjs/interpret

[coveralls-image]: https://img.shields.io/coveralls/gulpjs/interpret/master.svg?style=flat-square

<!-- prettier-ignore-end -->

<!-- prettier-ignore-start -->

[Liftoff]: http://github.com/gulpjs/liftoff

[rechoir]: http://github.com/gulpjs/rechoir

[require.extensions]: https://nodejs.org/api/modules.html#requireextensions

<!-- prettier-ignore-end -->
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"mjs-stub.js"
],
"scripts": {
"readme": "remark README.md --use ./scripts/plugin.mjs --output",
"lint": "eslint .",
"pretest": "npm run lint",
"test": "nyc mocha --async-only"
Expand All @@ -28,10 +29,13 @@
"eslint-config-gulp": "^5.0.0",
"eslint-plugin-node": "^11.1.0",
"expect": "^27.0.0",
"js-yaml": "^4.1.0",
"mocha": "^8.0.0",
"nyc": "^15.0.0",
"parse-node-version": "^2.0.0",
"rechoir": "^0.8.0",
"remark-cli": "^10.0.1",
"remark-code-import": "^1.1.0",
"shelljs": "0.8.5"
},
"nyc": {
Expand Down

0 comments on commit a1e9151

Please sign in to comment.