Created
December 12, 2021 10:34
-
-
Save hyrious/5546487637ac86ffb0ec14f754f87d15 to your computer and use it in GitHub Desktop.
possible plugin api through esm loaders
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
// plugin.mjs | |
import path from "path" | |
export default function test() { | |
return { | |
name: 'test', | |
setup({ onStart, onResolve, onLoad }) { | |
onStart(() => { | |
console.log('started --', import.meta.url) | |
}) | |
onResolve({ filter: /()/ }, args => { | |
if (args.path.includes('--')) { | |
return { | |
path: path.join( | |
args.resolveDir, | |
args.path.slice(0, args.path.indexOf('-')) + ".ts" | |
) | |
} | |
} | |
}) | |
onLoad({ filter: /()/ }, args => { | |
if (args.path.endsWith('b.ts')) { | |
return { | |
contents: `console.log( | |
import.meta.url, | |
'has been hooked by', | |
${JSON.stringify(import.meta.url)} | |
)` | |
} | |
} | |
}) | |
} | |
} | |
} |
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
import "./b-----?" // will be edited from plugin's onResolve | |
const message = "Hello, world!" | |
throw new Error(message as string) |
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
// loader.mjs | |
const plugins = [] | |
for (const arg of JSON.parse(process.env.__esbuild_plugins__)) { | |
let plugin | |
if (arg.startsWith('{')) { | |
plugin = new Function('return ' + arg) | |
} else { | |
plugin = await import(arg) | |
} | |
if (plugin.default) { | |
plugin = plugin.default | |
} else { | |
let key = Object.keys(plugin).filter(e => e.startsWith('_'))[0] | |
if (key) { | |
plugin = plugin[key] | |
} else { | |
throw new Error(`can not load plugin ${JSON.stringify(arg)}`) | |
} | |
} | |
if (typeof plugin === 'function') { | |
plugin = plugin() | |
} | |
plugins.push(plugin) | |
} | |
const onStart = fn => fn() | |
const onEnd = fn => undefined // never end, because never build | |
const resolvers = [] | |
const onResolve = (options, callback) => { | |
resolvers.push([options, callback]) | |
} | |
const loaders = [] | |
const onLoad = (options, callback) => { | |
loaders.push([options, callback]) | |
} | |
for (const plugin of plugins) { | |
plugin.setup({ onStart, onEnd, onResolve, onLoad, esbuild }) | |
} | |
import path from "path" | |
import { fileURLToPath, pathToFileURL } from "url" | |
export async function resolve(id, context, defaultResolve) { | |
for (const [{ filter }, callback] of resolvers) { | |
if (filter.test(id)) { | |
const result = await callback({ | |
path: id, | |
importer: context.parentURL, | |
namespace: 'file', | |
resolveDir: context.parentURL ? path.dirname(fileURLToPath(context.parentURL)) : process.cwd(), | |
kind: 'import-statement' | |
}) | |
if (result?.path) { | |
return { url: result.path, format: 'module' } | |
} | |
} | |
} | |
let url | |
try { | |
url = new URL(id) | |
} catch (e) { | |
if (e instanceof TypeError) { | |
if (id[0] === '.') { | |
let result | |
await esbuild.build({ | |
stdin: { | |
contents: `import ${JSON.stringify(id)}`, | |
resolveDir: path.dirname(fileURLToPath(context.parentURL)) | |
}, | |
write: false, | |
bundle: true, | |
platform: 'node', | |
plugins: [{ | |
name: 'resolve', | |
setup({ onLoad }) { | |
onLoad({ filter: /.*/ }, (args) => { | |
result = args.path | |
return { contents: '' } | |
}) | |
}, | |
}], | |
}) | |
if (result) { | |
url = pathToFileURL(result) | |
} | |
} | |
} else { | |
throw e | |
} | |
} | |
if (url) { | |
return { url: url.href, format: 'module' } | |
} | |
return defaultResolve(id, context, defaultResolve) | |
} | |
import fs from "fs" | |
import esbuild from "esbuild" | |
// node16.12 | |
export async function load(url, context, defaultLoad) { | |
for (const [{ filter }, callback] of loaders) { | |
if (filter.test(url)) { | |
const result = await callback({ | |
path: url, | |
}) | |
if (result?.contents) { | |
// todo: transform contents with result.loader | |
return { format: 'module', source: result.contents } | |
} | |
} | |
} | |
if (!url.startsWith('file://')) { | |
url = pathToFileURL(url) | |
} else { | |
url = new URL(url) | |
} | |
const source = await fs.promises.readFile(url, 'utf-8') | |
const { code, warnings } = await esbuild.transform(source, { | |
sourcefile: url.pathname, | |
sourcemap: 'inline', | |
loader: path.extname(new URL(url).pathname).slice(1), | |
target: `node${process.versions.node}`, | |
format: context.format === 'module' ? 'esm' : 'cjs', | |
}) | |
// todo: print warnings | |
return { format: 'module', source: code } | |
return defaultLoad(url, context, defaultLoad) | |
} |
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
console.log("b --", import.meta.url) |
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
// esmo.mjs | |
import { spawnSync } from 'child_process' | |
process.exit(spawnSync( | |
process.argv0, | |
[ | |
'--no-warnings', | |
'--experimental-loader', | |
new URL('./b.mjs', import.meta.url).toString(), | |
process.argv.slice(2) | |
], | |
{ | |
stdio: 'inherit', | |
env: { | |
...process.env, | |
// todo: make id more unique, like appending '_' if already exist | |
'__esbuild_plugins__': JSON.stringify([ | |
'./a.mjs' | |
]), | |
} | |
} | |
).status) |
The realworld implementation should refer to https://github.com/evanw/esbuild/blob/master/lib/shared/common.ts#L630
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage:
More todo:
support
namespace
andpluginData
. This shouldn't be too hard.