Created
November 4, 2017 07:54
-
-
Save Jessidhia/006ed7ad68e249677fe25662e87f7f5b to your computer and use it in GitHub Desktop.
Hack for testing the ESM hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
presets: ['@babel/react'] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export { resolve } from './loaderImpl.mjs' | |
import register from './loaderImpl.mjs' | |
register({ extensions: ['.jsx'] }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// formatting may be crazy as I didn't use linters | |
import deepClone from "lodash/cloneDeep"; | |
import escapeRegExp from "lodash/escapeRegExp"; | |
import babylon from 'babylon' | |
import babel from "@babel/core"; | |
import fs from "fs"; | |
import path from "path"; | |
import util from 'util'; | |
import url from 'url' | |
const { OptionManager, DEFAULT_EXTENSIONS } = babel; | |
const { URL } = url | |
const asyncReadFile = util.promisify(fs.readFile) | |
const asyncWriteFile = util.promisify(fs.writeFile) | |
const asyncStat = util.promisify(fs.stat) | |
const transformOpts = {}; | |
const extensions = new Set() | |
const FILE_SUFFIX = `.babel.${babel.version}.${babel.getEnv()}.mjs`; | |
/** | |
* Write stringified cache to disk. | |
* | |
* @param {string} originalURL | |
* @param {string} code | |
* @returns {Promise<string>} url of cached file | |
*/ | |
async function writeCache(originalURL, code) { | |
const parsed = new URL(originalURL) | |
const basename = path.basename(parsed.pathname) | |
// the cached result needs to be colocated in order to preserve relative imports 😿 | |
// possible TODO: traverse Imports and rewrite relative imports | |
const destination = new URL(`./.${basename}${FILE_SUFFIX}`, parsed) | |
const { pathname } = destination | |
await asyncWriteFile(pathname, code) | |
return destination.toString() | |
} | |
function compile(code, filename) { | |
// merge in base options and resolve all the plugins and presets relative to this file | |
const opts = new OptionManager().init({ | |
sourceRoot: path.dirname(filename), // sourceRoot can be overwritten | |
...deepClone(transformOpts), | |
filename | |
}); | |
// Bail out ASAP if the file has been ignored. | |
if (opts === null) return null; | |
// const env = babel.getEnv(false); | |
const result = babel.transform( | |
code, { | |
...opts | |
// Do not process config files since has already been done with the OptionManager | |
// calls above and would introduce duplicates. | |
babelrc: false, | |
sourceMaps: "both", | |
ast: false, | |
sourceType: "module" | |
} | |
); | |
return result.code; | |
} | |
export default function register(opts = {}) { | |
// Clone to avoid mutating the arguments object with the 'delete's below. | |
opts = Object.assign({}, opts); | |
if (opts.extensions) { | |
for (const ext of opts.extensions) { | |
extensions.add(ext); | |
} | |
} | |
delete opts.extensions; | |
Object.assign(transformOpts, opts); | |
if (!transformOpts.ignore && !transformOpts.only) { | |
transformOpts.only = [ | |
// Only compile things inside the current working directory. | |
new RegExp("^" + escapeRegExp(process.cwd()), "i"), | |
]; | |
transformOpts.ignore = [ | |
// Ignore any node_modules inside the current working directory. | |
new RegExp( | |
"^" + | |
escapeRegExp(process.cwd()) + | |
"(?:" + | |
path.sep + | |
".*)?" + | |
escapeRegExp(path.sep + "node_modules" + path.sep), | |
"i", | |
), | |
]; | |
} | |
} | |
/** | |
* | |
* @param {string} specifier | |
* @param {string} parentURL | |
* @param {function(string, string): Promise<{ url: string, format: string }>} defaultResolve | |
* @returns {Promise<{ url: string, format: string }>} | |
*/ | |
export async function resolve (specifier, parentURL, defaultResolve) { | |
const resolved = new URL(specifier, parentURL) | |
if (resolved.protocol !== 'file:') { | |
return resolved | |
} | |
let { pathname } = resolved | |
try { | |
await asyncStat(pathname) | |
} catch (e) { | |
pathname = new URL((await defaultResolve(specifier, parentURL)).url).pathname | |
} | |
if (extensions.has(path.extname(pathname))) { | |
const code = compile(await asyncReadFile(pathname), pathname) | |
if (code) { | |
return { url: await writeCache(resolved, code), format: 'esm' } | |
} | |
} | |
return defaultResolve(specifier, parentURL) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment