Skip to content

Commit e5a254f

Browse files
committed
perf: drop lodash, cache publicPath regex, avoid forEach closure and double html scan
Replace lodash escapeRegExp with an inline implementation to remove the runtime dependency. Cache compiled publicPath RegExp instances per unique path instead of rebuilding on every getCSSStyle call. Switch forEach to for...of in prepare() to avoid closure allocation per asset. Replace the indexOf + replace double-pass in processStyle with a single replace(), detecting a missing target by comparing the result to the original string. Also convert replaceConfig and styleTagFactory from getters to constructor-assigned readonly fields.
1 parent b91844c commit e5a254f

File tree

4 files changed

+33
-33
lines changed

4 files changed

+33
-33
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
"html-webpack-plugin": "^3.0.0 || ^4.0.0 || ^5.0.0"
6363
},
6464
"dependencies": {
65-
"lodash": "^4.17.21",
6665
"tslib": "^2.6.0"
6766
}
6867
}

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/base-plugin.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1-
import type { Compilation } from 'webpack';
1+
import type { Compilation } from 'webpack'
22

33
import {
4-
Config,
5-
StyleTagFactory,
4+
type Config,
5+
type StyleTagFactory,
66
DEFAULT_REPLACE_CONFIG,
7-
FileCache,
7+
type FileCache,
8+
type ReplaceConfig,
89
} from '../types'
910
import { isCSS, escapeRegExp } from '../utils'
1011

1112
export class BasePlugin {
12-
protected cssStyleCache: FileCache = {}
13+
private publicPathRegexMap = new Map<string, RegExp>()
14+
protected readonly replaceConfig: ReplaceConfig
15+
protected readonly styleTagFactory: StyleTagFactory
1316

14-
protected get replaceConfig() {
15-
return this.config.replace || DEFAULT_REPLACE_CONFIG
16-
}
17+
protected cssStyleCache: FileCache = {}
1718

18-
protected get styleTagFactory(): StyleTagFactory {
19-
return (
20-
this.config.styleTagFactory ||
19+
constructor(protected readonly config: Config = {}) {
20+
this.replaceConfig = config.replace || DEFAULT_REPLACE_CONFIG
21+
this.styleTagFactory =
22+
config.styleTagFactory ||
2123
(({ style }) => `<style type="text/css">${style}</style>`)
22-
)
2324
}
2425

25-
constructor(protected readonly config: Config = {}) {}
26-
2726
protected prepare({ assets }: Compilation) {
28-
Object.keys(assets).forEach((fileName) => {
27+
for (const fileName of Object.keys(assets)) {
2928
if (isCSS(fileName) && this.isCurrentFileNeedsToBeInlined(fileName)) {
3029
const source = assets[fileName].source()
31-
this.cssStyleCache[fileName] = typeof source === 'string' ? source : source.toString()
30+
this.cssStyleCache[fileName] =
31+
typeof source === 'string' ? source : source.toString()
3232

3333
if (!this.config.leaveCSSFile) {
3434
delete assets[fileName]
3535
}
3636
}
37-
})
37+
}
3838
}
3939

4040
protected getCSSStyle({
@@ -44,10 +44,13 @@ export class BasePlugin {
4444
cssLink: string
4545
publicPath: string
4646
}): string | undefined {
47+
let publicPathRegex = this.publicPathRegexMap.get(publicPath)
48+
if (publicPathRegex === undefined) {
49+
publicPathRegex = new RegExp(`^${escapeRegExp(publicPath)}`)
50+
this.publicPathRegexMap.set(publicPath, publicPathRegex)
51+
}
4752
// Link pattern: publicPath + fileName + '?' + hash
48-
const fileName = cssLink
49-
.replace(new RegExp(`^${escapeRegExp(publicPath)}`), '')
50-
.replace(/\?.+$/g, '')
53+
const fileName = cssLink.replace(publicPathRegex, '').replace(/\?.+$/g, '')
5154

5255
if (this.isCurrentFileNeedsToBeInlined(fileName)) {
5356
const style = this.cssStyleCache[fileName]
@@ -90,13 +93,14 @@ export class BasePlugin {
9093
replaceValues.reverse()
9194
}
9295

93-
if (html.indexOf(this.replaceConfig.target) === -1) {
94-
throw new Error(
95-
`Can not inject css style into "${htmlFileName}", as there is not replace target "${this.replaceConfig.target}"`,
96-
)
96+
const replaced = html.replace(
97+
this.replaceConfig.target,
98+
replaceValues.join(''),
99+
)
100+
if (replaced === html) {
101+
throw new Error(`Can not inject css style into "${htmlFileName}"...`)
97102
}
98-
99-
return html.replace(this.replaceConfig.target, replaceValues.join(''))
103+
return replaced
100104
}
101105

102106
protected cleanUp(html: string) {

src/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { escapeRegExp } from 'lodash'
2-
3-
export { escapeRegExp }
1+
export function escapeRegExp(str: string): string {
2+
return str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
3+
}
44

55
export function is(filenameExtension: string) {
66
const reg = new RegExp(`\.${filenameExtension}$`)

0 commit comments

Comments
 (0)