From e671b628c47694ab2e8c279f361c24eaf2a38aa7 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 25 Jul 2021 08:46:30 +0800 Subject: [PATCH] feat(webpack): `load` hook initial implementation --- package.json | 27 ++++++- src/types.ts | 4 + src/webpack/index.ts | 142 +++++++++++++++++++----------------- src/webpack/loaders/load.ts | 9 +-- yarn.lock | 55 +++++++++----- 5 files changed, 141 insertions(+), 96 deletions(-) diff --git a/package.json b/package.json index 90568c43..ad29c5c3 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,9 @@ "profile": "0x -o -D .profile -P 'autocannon -c 100 -p 10 -d 40 http://localhost:$PORT' ./hello.js", "test": "yarn lint && jest" }, + "dependencies": { + "webpack-virtual-modules": "^0.4.3" + }, "devDependencies": { "0x": "latest", "@nuxtjs/eslint-config-typescript": "latest", @@ -38,21 +41,37 @@ "connect": "latest", "cookie": "latest", "destr": "latest", - "enhanced-resolve": "^5.8.2", + "enhanced-resolve": "latest", "eslint": "latest", "express": "latest", "get-port": "latest", "jest": "latest", "jiti": "latest", "listhen": "latest", - "rollup": "latest", + "rollup": "^2.53.3", "siroc": "latest", "standard-version": "latest", "supertest": "latest", "ts-jest": "latest", "typescript": "latest", "ufo": "latest", - "vite": "latest", - "webpack": "latest" + "vite": "^2.4.3", + "webpack": "^5.46.0" + }, + "peerDependencies": { + "rollup": "^2.50.0", + "vite": "^2.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } } } diff --git a/src/types.ts b/src/types.ts index 859aaad7..e17e708f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ import type { Plugin as RollupPlugin } from 'rollup' import type { Compiler as WebpackCompiler } from 'webpack' import type { Plugin as VitePlugin } from 'vite' +import VirtualModulesPlugin from 'webpack-virtual-modules' export { RollupPlugin, @@ -22,6 +23,9 @@ export interface UnpluginOptions { rollup?: Partial webpack?: (compiler: WebpackCompiler) => void vite?: Partial + + // injected internal objects + __vfs?: VirtualModulesPlugin } export type UnpluginFactory = (options?: UserOptions) => UnpluginOptions diff --git a/src/webpack/index.ts b/src/webpack/index.ts index 6d521482..06f0de20 100644 --- a/src/webpack/index.ts +++ b/src/webpack/index.ts @@ -1,88 +1,94 @@ +import fs from 'fs' import { join, resolve } from 'path' import type { Resolver } from 'enhanced-resolve' +import VirtualModulesPlugin from 'webpack-virtual-modules' import type { UnpluginInstance, UnpluginFactory, WebpackCompiler } from '../types' export function getWebpackPlugin ( factory: UnpluginFactory ): UnpluginInstance['webpack'] { - class UnpluginWebpackPlugin { - // eslint-disable-next-line no-useless-constructor - constructor (public userOptions?: UserOptions) {} + return (userOptions?: UserOptions) => { + return { + apply (compiler: WebpackCompiler) { + const rawPlugin = factory(userOptions) + const loaderPath = resolve(__dirname, 'webpack/loaders') - apply (compiler: WebpackCompiler) { - const rawPlugin = factory(this.userOptions) - const loaderPath = resolve(__dirname, 'webpack/loaders') + if (!compiler.$unpluginContext) { + compiler.$unpluginContext = {} + } + compiler.$unpluginContext[rawPlugin.name] = rawPlugin - if (!compiler.$unpluginContext) { - compiler.$unpluginContext = {} - } - compiler.$unpluginContext[rawPlugin.name] = rawPlugin + if (rawPlugin.transform) { + compiler.options.module.rules.push({ + include (id: string) { + if (rawPlugin.transformInclude) { + return rawPlugin.transformInclude(id) + } else { + return true + } + }, + enforce: rawPlugin.enforce, + use: [{ + loader: join(loaderPath, 'transform.cjs'), + options: { + unpluginName: rawPlugin.name + } + }] + }) + } - if (rawPlugin.transform) { - compiler.options.module.rules.push({ - include (id: string) { - if (rawPlugin.transformInclude) { - return rawPlugin.transformInclude(id) - } else { - return true - } - }, - enforce: rawPlugin.enforce, - use: [{ - loader: join(loaderPath, 'transform.cjs'), - options: { - unpluginName: rawPlugin.name - } - }] - }) - } + if (rawPlugin.resolveId) { + const virtualModule = new VirtualModulesPlugin() + rawPlugin.__vfs = virtualModule + compiler.options.plugins.push(virtualModule) - if (rawPlugin.resolveId) { - const resolver = { - apply (resolver: Resolver) { - const target = resolver.ensureHook('resolve') - resolver - .getHook('described-resolve') - .tapAsync('unplugin', async (request: any, resolveContext: any, callback: any) => { - const resolved = await rawPlugin.resolveId!(request.request) - if (resolved != null) { - const newRequest = { - ...request, - request: resolved + const resolver = { + apply (resolver: Resolver) { + const target = resolver.ensureHook('resolve') + resolver + .getHook('described-resolve') + .tapAsync('unplugin', async (request: any, resolveContext: any, callback: any) => { + const resolved = await rawPlugin.resolveId!(request.request) + if (resolved != null) { + const newRequest = { + ...request, + request: resolved + } + if (!fs.existsSync(resolved)) { + virtualModule.writeModule(resolved, '') + } + resolver.doResolve(target, newRequest, null, resolveContext, callback) + } else { + callback() } - resolver.doResolve(target, newRequest, null, resolveContext, callback) - } else { - callback() - } - }) + }) + } } - } - compiler.options.resolve.plugins = compiler.options.resolve.plugins || [] - compiler.options.resolve.plugins.push(resolver) - } + compiler.options.resolve.plugins = compiler.options.resolve.plugins || [] + compiler.options.resolve.plugins.push(resolver) + } - // TODO: not working for virtual module - if (rawPlugin.load) { - compiler.options.module.rules.push({ - include () { - return true - }, - enforce: rawPlugin.enforce, - use: [{ - loader: join(loaderPath, 'load.cjs'), - options: { - unpluginName: rawPlugin.name - } - }] - }) - } + // TODO: not working for virtual module + if (rawPlugin.load) { + compiler.options.module.rules.push({ + include () { + return true + }, + enforce: rawPlugin.enforce, + use: [{ + loader: join(loaderPath, 'load.cjs'), + options: { + unpluginName: rawPlugin.name + } + }] + }) + } - if (rawPlugin.webpack) { - rawPlugin.webpack(compiler) + if (rawPlugin.webpack) { + rawPlugin.webpack(compiler) + } } } } - - return UserOptions => new UnpluginWebpackPlugin(UserOptions) } diff --git a/src/webpack/loaders/load.ts b/src/webpack/loaders/load.ts index f57e4dc7..e56c7519 100644 --- a/src/webpack/loaders/load.ts +++ b/src/webpack/loaders/load.ts @@ -6,16 +6,11 @@ export default async function load (this: LoaderContext, source: string) { const { unpluginName } = this.query const plugin = this._compiler?.$unpluginContext[unpluginName] - if (!plugin?.resolveId || !plugin?.load) { + if (!plugin?.load) { return callback(null, source) } - const id = await plugin.resolveId(this.resource) - if (id == null) { - return callback(null, source) - } - - const res = await plugin.load(id) + const res = await plugin.load(this.resource) if (res == null) { callback(null, source) diff --git a/yarn.lock b/yarn.lock index 2b00afb7..74aa2077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -845,7 +845,7 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/json-schema@*": +"@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.8" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== @@ -2806,7 +2806,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.8.0, enhanced-resolve@^5.8.2: +enhanced-resolve@^5.8.0, enhanced-resolve@latest: version "5.8.2" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== @@ -6299,7 +6299,7 @@ rollup-plugin-esbuild@4.5.0: joycon "^3.0.1" jsonc-parser "^3.0.0" -rollup@^2.38.5, rollup@latest: +rollup@^2.38.5: version "2.53.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.1.tgz#b60439efd1eb41bdb56630509bd99aae78b575d3" integrity sha512-yiTCvcYXZEulNWNlEONOQVlhXA/hgxjelFSjNcrwAAIfYx/xqjSHwqg/cCaWOyFRKr+IQBaXwt723m8tCaIUiw== @@ -6313,6 +6313,13 @@ rollup@^2.52.2: optionalDependencies: fsevents "~2.3.2" +rollup@^2.53.3: + version "2.53.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.3.tgz#14b0e57f0874d4ad23bdbb13050cf70bcd1eabf7" + integrity sha512-79QIGP5DXz5ZHYnCPi3tLz+elOQi6gudp9YINdaJdjG0Yddubo6JRFUM//qCZ0Bap/GJrsUoEBVdSOc4AkMlRA== + optionalDependencies: + fsevents "~2.3.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -6358,6 +6365,15 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + selfsigned@^1.10.8: version "1.10.11" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9" @@ -7388,10 +7404,10 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vite@latest: - version "2.4.2" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.4.2.tgz#07d00615775c808530bc9f65641062b349b67929" - integrity sha512-2MifxD2I9fjyDmmEzbULOo3kOUoqX90A58cT6mECxoVQlMYFuijZsPQBuA14mqSwvV3ydUsqnq+BRWXyO9Qa+w== +vite@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.4.3.tgz#fe4aa78e9dd7d36bcb12eccbd52313b26cfadf77" + integrity sha512-iT6NPeiUUZ2FkzC3eazytOEMRaM4J+xgRQcNcpRcbmfYjakCFP4WKPJpeEz1U5JEKHAtwv3ZBQketQUFhFU3ng== dependencies: esbuild "^0.12.8" postcss "^8.3.5" @@ -7465,18 +7481,23 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-sources@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa" - integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ== +webpack-sources@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== dependencies: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@latest: - version "5.44.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.44.0.tgz#97b13a02bd79fb71ac6301ce697920660fa214a1" - integrity sha512-I1S1w4QLoKmH19pX6YhYN0NiSXaWY8Ou00oA+aMcr9IUGeF5azns+IKBkfoAAG9Bu5zOIzZt/mN35OffBya8AQ== +webpack-virtual-modules@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz#cd597c6d51d5a5ecb473eea1983a58fa8a17ded9" + integrity sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw== + +webpack@^5.46.0: + version "5.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.46.0.tgz#105d20d96f79db59b316b0ae54316f0f630314b5" + integrity sha512-qxD0t/KTedJbpcXUmvMxY5PUvXDbF8LsThCzqomeGaDlCA6k998D8yYVwZMvO8sSM3BTEOaD4uzFniwpHaTIJw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" @@ -7496,11 +7517,11 @@ webpack@latest: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.0.0" + schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" watchpack "^2.2.0" - webpack-sources "^2.3.0" + webpack-sources "^2.3.1" whatwg-encoding@^1.0.5: version "1.0.5"