diff --git a/common/web/base-web/package.json b/common/web/base-web/package.json index a158bdda4..7d71be679 100644 --- a/common/web/base-web/package.json +++ b/common/web/base-web/package.json @@ -1,6 +1,6 @@ { "name": "@curiostack/base-web", - "version": "0.0.50", + "version": "0.0.51", "description": "Curiostack's base dependencies for browser applications.", "repository": "curioswitch/curiostack", "author": "Choko ", @@ -139,6 +139,7 @@ "devDependencies": { "@types/compression-webpack-plugin": "2.0.1", "@types/copy-webpack-plugin": "5.0.0", + "@types/enhanced-resolve": "3.0.6", "@types/enzyme-adapter-react-16": "1.0.5", "@types/eslint": "6.1.2", "@types/favicons-webpack-plugin": "1.0.0", diff --git a/common/web/base-web/src/dev/resolve/index.ts b/common/web/base-web/src/dev/resolve/index.ts new file mode 100644 index 000000000..ec3e43e25 --- /dev/null +++ b/common/web/base-web/src/dev/resolve/index.ts @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2019 Choko (choko@curioswitch.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import path from 'path'; + +import { DISABLER } from '../scripts/common'; + +// eslint-disable-next-line +const packageJson = require(path.resolve(process.cwd(), 'package.json')); + +interface ResolveOverrides { + [key: string]: string[]; +} + +const packageOverrides = packageJson.curiostack + ? packageJson.curiostack.resolveOverrides + : {}; + +export const config: ResolveOverrides = { + 'core-js': ['core-js', 'core-js-2'], + ...packageOverrides, +}; + +export function rewriteResolveRequest(request: string): string { + for (const [lib, overrides] of Object.entries(config)) { + if (request !== lib && !request.startsWith(`${lib}/`)) { + continue; + } + + const remainingRequest = request.substring(request.indexOf('/') + 1); + for (const override of overrides) { + const rewritten = `${override}/${remainingRequest}`; + DISABLER.DISABLE_ALIAS = true; + try { + require.resolve(rewritten); + // Resolved so we found a working override. + return rewritten; + } catch (e) { + // Fall through to try other overrides. + } finally { + DISABLER.DISABLE_ALIAS = false; + } + } + } + return request; +} diff --git a/common/web/base-web/src/dev/scripts/common.ts b/common/web/base-web/src/dev/scripts/common.ts index e483d74ad..95c5709f0 100644 --- a/common/web/base-web/src/dev/scripts/common.ts +++ b/common/web/base-web/src/dev/scripts/common.ts @@ -25,30 +25,24 @@ import 'module-alias/register'; import { addAlias } from 'module-alias'; -let RECURSIVE_RESOLVE = false; +import { config as RESOLVE_CONFIG, rewriteResolveRequest } from '../resolve'; -addAlias('core-js', (_, request) => { - if (RECURSIVE_RESOLVE) { - return 'core-js'; - } - RECURSIVE_RESOLVE = true; - try { - try { - require.resolve(request); - // No error, the module exists - return 'core-js'; - } catch (e) { - // Fall through to try core-js-2 +export const DISABLER = { + DISABLE_ALIAS: false, +}; + +for (const lib of Object.keys(RESOLVE_CONFIG)) { + addAlias(lib, (_, request) => { + if (DISABLER.DISABLE_ALIAS) { + return lib; } - try { - require.resolve(request.replace('core-js/', 'core-js-2/')); - // No error, the module exists in core-js-2 - return 'core-js-2'; - } catch (e) { - // Not in core-js-2, just return the default (which will fail and provide an error message). + + const rewritten = rewriteResolveRequest(request); + + if (rewritten === request) { + return lib; } - } finally { - RECURSIVE_RESOLVE = false; - } - return 'core-js'; -}); + + return rewritten.split('/', 1)[0]; + }); +} diff --git a/common/web/base-web/src/dev/webpack/base.ts b/common/web/base-web/src/dev/webpack/base.ts index c80f21289..8002e63fd 100644 --- a/common/web/base-web/src/dev/webpack/base.ts +++ b/common/web/base-web/src/dev/webpack/base.ts @@ -28,6 +28,8 @@ import CopyWebpackPlugin from 'copy-webpack-plugin'; import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; import { Configuration } from 'webpack'; +import FallbackResolverPlugin from './fallback-resolver'; + // eslint-disable-next-line const packageJson = require(path.resolve(process.cwd(), 'package.json')); @@ -193,6 +195,7 @@ function configure(options: any): Configuration { extensions: ['.js', '.jsx', '.ts', '.tsx'], mainFields: ['browser', 'module', 'jsnext:main', 'main'], plugins: [ + new FallbackResolverPlugin(), new TsconfigPathsPlugin({ extensions: ['.ts', '.tsx', '.js', '.jsx'], mainFields: ['browser', 'module', 'jsnext:main', 'main'], diff --git a/common/web/base-web/src/dev/webpack/fallback-resolver/index.ts b/common/web/base-web/src/dev/webpack/fallback-resolver/index.ts new file mode 100644 index 000000000..dc4d6d05c --- /dev/null +++ b/common/web/base-web/src/dev/webpack/fallback-resolver/index.ts @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2019 Choko (choko@curioswitch.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Resolver from 'enhanced-resolve/lib/Resolver'; + +import { rewriteResolveRequest } from '../../resolve'; + +type ResolverRequest = import('enhanced-resolve/lib/common-types').ResolverRequest; + +export default class FallbackResolverPlugin { + public apply(resolver: Resolver) { + const tapable = resolver as any; + + const target = tapable.ensureHook('resolve'); + + tapable + .ensureHook('described-resolve') + .tapAsync( + 'FallbackResolverPlugin', + ( + request: ResolverRequest, + resolveContext: any, + callback: () => void, + ) => { + const rewritten = rewriteResolveRequest(request.request); + if (rewritten === request.request) { + return callback(); + } + + // Cast to any since types don't match webpack definition + return (resolver as any).doResolve( + target, + { + ...request, + request: rewritten, + }, + `rewriting ${request.request} to ${rewritten}`, + resolveContext, + callback, + ); + }, + ); + } +} diff --git a/yarn.lock b/yarn.lock index be84b99e0..b641a1158 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2170,6 +2170,14 @@ dependencies: "@types/node" "*" +"@types/enhanced-resolve@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/enhanced-resolve/-/enhanced-resolve-3.0.6.tgz#a51eaa24f4458ed13fb42a7048c0b66f92e95a28" + integrity sha512-mAWc6JpDiA6GnPCF5023YSGMa/E7baMvLs+HtT9E6Z52lUds3pthf4APhVQpbmV6sZTrbASgBEDdh70eGWTJFw== + dependencies: + "@types/node" "*" + "@types/tapable" "^0" + "@types/enzyme-adapter-react-16@1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.5.tgz#1bf30a166f49be69eeda4b81e3f24113c8b4e9d5" @@ -2684,6 +2692,11 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== +"@types/tapable@^0": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-0.2.5.tgz#2443fc12da514c81346b1a665675559cee21fa75" + integrity sha512-dEoVvo/I9QFomyhY+4Q6Qk+I+dhG59TYceZgC6Q0mCifVPErx6Y83PNTKGDS5e9h9Eti6q0S2mm16BU6iQK+3w== + "@types/through@*": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93"