From c5ccbf9b377665ce44ebfdc4a25a2d0a90cafa26 Mon Sep 17 00:00:00 2001 From: Dilan Date: Wed, 29 Jun 2022 16:38:05 -0500 Subject: [PATCH] support webpack 5 asset modules --- package-lock.json | 12 ++-- package.json | 6 +- src/index.ts | 18 +++++ src/lib/asset-modules.ts | 141 +++++++++++++++++++++++++++++++++++++ src/lib/loaders.ts | 74 +++++++++---------- src/types/asset-modules.ts | 20 ++++++ src/types/loaders.ts | 4 +- 7 files changed, 227 insertions(+), 48 deletions(-) create mode 100644 src/lib/asset-modules.ts create mode 100644 src/types/asset-modules.ts diff --git a/package-lock.json b/package-lock.json index c855a854..545bcd1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@craco/craco", - "version": "7.0.0-alpha4", + "version": "7.0.0-alpha.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@craco/craco", - "version": "7.0.0-alpha4", + "version": "7.0.0-alpha.5", "license": "Apache-2.0", "dependencies": { "autoprefixer": "^10.4.7", @@ -27,10 +27,10 @@ "@types/eslint": "^8.4.3", "@types/lodash": "^4.14.182", "@types/semver": "^7.3.10", - "@types/webpack": "^5.28.0", "eslint-webpack-plugin": "^3.2.0", "react-scripts": "5.*", - "typescript": "^4.7.4" + "typescript": "^4.7.4", + "webpack": "^5.73.0" }, "engines": { "node": ">=6" @@ -4413,6 +4413,8 @@ "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@types/node": "*", "tapable": "^2.2.0", @@ -22558,6 +22560,8 @@ "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", "dev": true, + "optional": true, + "peer": true, "requires": { "@types/node": "*", "tapable": "^2.2.0", diff --git a/package.json b/package.json index c629219b..e5ad285b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@craco/craco", "description": "Create React App Configuration Override, an easy and comprehensible configuration layer for create-react-app.", - "version": "7.0.0-alpha.5", + "version": "7.0.0-alpha.6", "scripts": { "prepublishOnly": "tsc" }, @@ -52,10 +52,10 @@ "@types/eslint": "^8.4.3", "@types/lodash": "^4.14.182", "@types/semver": "^7.3.10", - "@types/webpack": "^5.28.0", "eslint-webpack-plugin": "^3.2.0", "react-scripts": "5.*", - "typescript": "^4.7.4" + "typescript": "^4.7.4", + "webpack": "^5.73.0" }, "dependencies": { "autoprefixer": "^10.4.7", diff --git a/src/index.ts b/src/index.ts index 2c685c78..37231b52 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,13 @@ +import { + addAfterAssetModule, + addAfterAssetModules, + addBeforeAssetModule, + addBeforeAssetModules, + assetModuleByName, + getAssetModule, + getAssetModules, + removeAssetModules, +} from './lib/asset-modules'; import { createDevServerConfigProviderProxy } from './lib/features/dev-server/api'; import { createJestConfig } from './lib/features/jest/api'; import { @@ -32,6 +42,14 @@ export { addAfterLoader, addAfterLoaders, loaderByName, + getAssetModule, + getAssetModules, + removeAssetModules, + addBeforeAssetModule, + addBeforeAssetModules, + addAfterAssetModule, + addAfterAssetModules, + assetModuleByName, getPlugin, pluginByName, addPlugins, diff --git a/src/lib/asset-modules.ts b/src/lib/asset-modules.ts new file mode 100644 index 00000000..1210e354 --- /dev/null +++ b/src/lib/asset-modules.ts @@ -0,0 +1,141 @@ +import type { RuleSetRule, Configuration as WebpackConfig } from 'webpack'; +import type { + AssetModule, + AssetModuleMatcher, + AssetModuleType, +} from '../types/asset-modules'; + +export function assetModuleByName(assetModuleName: AssetModuleType) { + return (rule: RuleSetRule) => rule.type === assetModuleName; +} + +const toMatchingAssetModule = ( + rule: RuleSetRule, + index: number +): AssetModule => ({ + rule, + index, +}); + +export function getAssetModule( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher +) { + let matchingAssetModule: AssetModule | undefined; + (webpackConfig.module?.rules as RuleSetRule[])?.some((rule, index) => { + if (matcher(rule)) { + matchingAssetModule = toMatchingAssetModule(rule, index); + } + return matchingAssetModule !== undefined; + }); + return { + isFound: matchingAssetModule !== undefined, + match: matchingAssetModule, + }; +} + +export function getAssetModules( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher +) { + const matchingAssetModules: AssetModule[] = []; + (webpackConfig.module?.rules as RuleSetRule[])?.forEach((rule, index) => { + if (matcher(rule)) { + matchingAssetModules.push(toMatchingAssetModule(rule, index)); + } + }); + + return { + hasFoundAny: matchingAssetModules.length !== 0, + matches: matchingAssetModules, + }; +} + +export function removeAssetModules( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher +) { + const toRemove: number[] = []; + (webpackConfig.module?.rules as RuleSetRule[])?.forEach((rule, index) => { + if (matcher(rule)) { + toRemove.push(index); + } + }); + + toRemove.forEach((index) => { + webpackConfig.module?.rules?.splice(index, 1); + }); + + return { + rules: webpackConfig.module?.rules, + removedCount: toRemove.length, + }; +} + +function addAssetModule( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newAssetModule: RuleSetRule, + positionAdapter: (index: number) => number +) { + const { match } = getAssetModule(webpackConfig, matcher); + + if (match !== undefined) { + webpackConfig.module?.rules?.splice( + positionAdapter(match.index), + 0, + newAssetModule + ); + + return { isAdded: true }; + } + + return { isAdded: false }; +} + +export const addBeforeAssetModule = ( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newAssetModule: RuleSetRule +) => addAssetModule(webpackConfig, matcher, newAssetModule, (x) => x); + +export const addAfterAssetModule = ( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newAssetModule: RuleSetRule +) => addAssetModule(webpackConfig, matcher, newAssetModule, (x) => x + 1); + +function addAssetModules( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newLoader: RuleSetRule, + positionAdapter: (index: number) => number +) { + const { matches } = getAssetModules(webpackConfig, matcher); + + if (matches.length !== 0) { + matches.forEach((match) => { + webpackConfig.module?.rules?.splice( + positionAdapter(match.index), + 0, + newLoader + ); + }); + + return { isAdded: true, addedCount: matches.length }; + } + + return { isAdded: false, addedCount: 0 }; +} + +export const addBeforeAssetModules = ( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newAssetModule: RuleSetRule +) => addAssetModules(webpackConfig, matcher, newAssetModule, (x) => x); + +export const addAfterAssetModules = ( + webpackConfig: WebpackConfig, + matcher: AssetModuleMatcher, + newAssetModule: RuleSetRule +) => addAssetModules(webpackConfig, matcher, newAssetModule, (x) => x + 1); diff --git a/src/lib/loaders.ts b/src/lib/loaders.ts index 6dfe17cd..f85f8856 100644 --- a/src/lib/loaders.ts +++ b/src/lib/loaders.ts @@ -3,7 +3,7 @@ import type { RuleSetRule, RuleSetUseItem, } from 'webpack'; -import type { Loader, Matcher, Rule, Rules } from '../types/loaders'; +import type { Loader, LoaderMatcher, Rule, Rules } from '../types/loaders'; import path from 'path'; import { isArray, isString } from './utils'; @@ -28,15 +28,13 @@ export function loaderByName(targetLoaderName: string) { }; } -function toMatchingLoader(loader: Rule, parent: Rules, index: number): Loader { - return { - loader, - parent, - index, - }; -} +const toMatchingLoader = ( + loader: Rule, + parent: Rules, + index: number +): Loader => ({ loader, parent, index }); -function getLoaderRecursively(rules: Rules, matcher: Matcher) { +function getLoaderRecursively(rules: Rules, matcher: LoaderMatcher) { let loader: Loader | undefined; rules?.some((rule, index) => { @@ -71,21 +69,21 @@ function getLoaderRecursively(rules: Rules, matcher: Matcher) { return loader; } -export function getLoader(webpackConfig: WebpackConfig, matcher: Matcher) { +export function getLoader( + webpackConfig: WebpackConfig, + matcher: LoaderMatcher +) { const matchingLoader = getLoaderRecursively( webpackConfig.module?.rules as Rules, matcher ); - return { - isFound: matchingLoader !== undefined, - match: matchingLoader, - }; + return { isFound: matchingLoader !== undefined, match: matchingLoader }; } function getLoadersRecursively( rules: Rules, - matcher: Matcher, + matcher: LoaderMatcher, matchingLoaders: Loader[] ) { rules?.forEach((rule, index) => { @@ -119,7 +117,10 @@ function getLoadersRecursively( }); } -export function getLoaders(webpackConfig: WebpackConfig, matcher: Matcher) { +export function getLoaders( + webpackConfig: WebpackConfig, + matcher: LoaderMatcher +) { const matchingLoaders: Loader[] = []; getLoadersRecursively( @@ -136,7 +137,7 @@ export function getLoaders(webpackConfig: WebpackConfig, matcher: Matcher) { function removeLoadersRecursively( rules: Rules, - matcher: Matcher + matcher: LoaderMatcher ): { rules: Rules; removedCount: number; @@ -145,10 +146,7 @@ function removeLoadersRecursively( let removedCount = 0; if (!rules) { - return { - rules, - removedCount: 0, - }; + return { rules, removedCount: 0 }; } for (let i = 0, max = rules.length; i < max; i += 1) { @@ -189,13 +187,13 @@ function removeLoadersRecursively( rules.splice(ruleIndex - i, 1); }); - return { - rules, - removedCount: removedCount + toRemove.length, - }; + return { rules, removedCount: removedCount + toRemove.length }; } -export function removeLoaders(webpackConfig: WebpackConfig, matcher: Matcher) { +export function removeLoaders( + webpackConfig: WebpackConfig, + matcher: LoaderMatcher +) { const result = removeLoadersRecursively( webpackConfig.module?.rules as Rules, matcher @@ -209,7 +207,7 @@ export function removeLoaders(webpackConfig: WebpackConfig, matcher: Matcher) { function addLoader( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule, positionAdapter: (index: number) => number ) { @@ -226,18 +224,19 @@ function addLoader( export const addBeforeLoader = ( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule ) => addLoader(webpackConfig, matcher, newLoader, (x) => x); + export const addAfterLoader = ( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule ) => addLoader(webpackConfig, matcher, newLoader, (x) => x + 1); function addLoaders( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule, positionAdapter: (index: number) => number ) { @@ -248,25 +247,20 @@ function addLoaders( match!.parent?.splice(positionAdapter(match.index), 0, newLoader); }); - return { - isAdded: true, - addedCount: matches.length, - }; + return { isAdded: true, addedCount: matches.length }; } - return { - isAdded: false, - addedCount: 0, - }; + return { isAdded: false, addedCount: 0 }; } export const addBeforeLoaders = ( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule ) => addLoaders(webpackConfig, matcher, newLoader, (x) => x); + export const addAfterLoaders = ( webpackConfig: WebpackConfig, - matcher: Matcher, + matcher: LoaderMatcher, newLoader: Rule ) => addLoaders(webpackConfig, matcher, newLoader, (x) => x + 1); diff --git a/src/types/asset-modules.ts b/src/types/asset-modules.ts new file mode 100644 index 00000000..3ea5a7fe --- /dev/null +++ b/src/types/asset-modules.ts @@ -0,0 +1,20 @@ +import type { RuleSetRule } from 'webpack'; + +export type AssetModuleType = + | 'javascript/auto' + | 'javascript/dynamic' + | 'javascript/esm' + | 'json' + | 'webassembly/sync' + | 'webassembly/async' + | 'asset' + | 'asset/source' + | 'asset/resource' + | 'asset/inline'; + +export type AssetModuleMatcher = (rule: RuleSetRule) => boolean; + +export interface AssetModule { + rule: RuleSetRule; + index: number; +} diff --git a/src/types/loaders.ts b/src/types/loaders.ts index 50ad1cf9..498986b9 100644 --- a/src/types/loaders.ts +++ b/src/types/loaders.ts @@ -1,8 +1,10 @@ import type { RuleSetRule, RuleSetUseItem } from 'webpack'; +// TODO these typings need to be updated I'm pretty sure + export type Rule = RuleSetRule | undefined; export type Rules = Rule[] | undefined; -export type Matcher = (rule: RuleSetRule | RuleSetUseItem) => boolean; +export type LoaderMatcher = (rule: RuleSetRule | RuleSetUseItem) => boolean; export interface Loader { loader: Rule;