Skip to content

Commit 55036ab

Browse files
fix: improve perf (#760)
1 parent f2a5db3 commit 55036ab

File tree

3 files changed

+128
-24
lines changed

3 files changed

+128
-24
lines changed

src/index.js

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,48 @@
11
const path = require("path");
22

33
const { validate } = require("schema-utils");
4-
const serialize = require("serialize-javascript");
5-
const normalizePath = require("normalize-path");
6-
const globParent = require("glob-parent");
7-
const fastGlob = require("fast-glob");
84

95
// @ts-ignore
106
const { version } = require("../package.json");
117

128
const schema = require("./options.json");
13-
const { readFile, stat, throttleAll } = require("./utils");
9+
const {
10+
readFile,
11+
stat,
12+
throttleAll,
13+
memoize,
14+
asyncMemoize,
15+
} = require("./utils");
1416

1517
const template = /\[\\*([\w:]+)\\*\]/i;
1618

19+
const getNormalizePath = memoize(() =>
20+
// eslint-disable-next-line global-require
21+
require("normalize-path"),
22+
);
23+
24+
const getGlobParent = memoize(() =>
25+
// eslint-disable-next-line global-require
26+
require("glob-parent"),
27+
);
28+
29+
const getSerializeJavascript = memoize(() =>
30+
// eslint-disable-next-line global-require
31+
require("serialize-javascript"),
32+
);
33+
34+
const getFastGlob = memoize(() =>
35+
// eslint-disable-next-line global-require
36+
require("fast-glob"),
37+
);
38+
39+
const getGlobby = asyncMemoize(async () => {
40+
// @ts-ignore
41+
const { globby } = await import("globby");
42+
43+
return globby;
44+
});
45+
1746
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
1847
/** @typedef {import("webpack").Compiler} Compiler */
1948
/** @typedef {import("webpack").Compilation} Compilation */
@@ -334,7 +363,9 @@ class CopyPlugin {
334363

335364
pattern.context = absoluteFrom;
336365
glob = path.posix.join(
337-
fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom))),
366+
getFastGlob().escapePath(
367+
getNormalizePath()(path.resolve(absoluteFrom)),
368+
),
338369
"**/*",
339370
);
340371
absoluteFrom = path.join(absoluteFrom, "**/*");
@@ -349,15 +380,19 @@ class CopyPlugin {
349380
logger.debug(`added '${absoluteFrom}' as a file dependency`);
350381

351382
pattern.context = path.dirname(absoluteFrom);
352-
glob = fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom)));
383+
glob = getFastGlob().escapePath(
384+
getNormalizePath()(path.resolve(absoluteFrom)),
385+
);
353386

354387
if (typeof globOptions.dot === "undefined") {
355388
globOptions.dot = true;
356389
}
357390
break;
358391
case "glob":
359392
default: {
360-
const contextDependencies = path.normalize(globParent(absoluteFrom));
393+
const contextDependencies = path.normalize(
394+
getGlobParent()(absoluteFrom),
395+
);
361396

362397
compilation.contextDependencies.add(contextDependencies);
363398

@@ -366,7 +401,9 @@ class CopyPlugin {
366401
glob = path.isAbsolute(originalFrom)
367402
? originalFrom
368403
: path.posix.join(
369-
fastGlob.escapePath(normalizePath(path.resolve(pattern.context))),
404+
getFastGlob().escapePath(
405+
getNormalizePath()(path.resolve(pattern.context)),
406+
),
370407
originalFrom,
371408
);
372409
}
@@ -481,7 +518,7 @@ class CopyPlugin {
481518
`determined that '${from}' should write to '${filename}'`,
482519
);
483520

484-
const sourceFilename = normalizePath(
521+
const sourceFilename = getNormalizePath()(
485522
path.relative(compiler.context, absoluteFilename),
486523
);
487524

@@ -628,7 +665,7 @@ class CopyPlugin {
628665
contentHash: hasher.update(buffer).digest("hex"),
629666
index,
630667
};
631-
const cacheKeys = `transform|${serialize(
668+
const cacheKeys = `transform|${getSerializeJavascript()(
632669
typeof transformObj.cache === "boolean"
633670
? defaultCacheKeys
634671
: typeof transformObj.cache.keys === "function"
@@ -708,7 +745,7 @@ class CopyPlugin {
708745
const base = path.basename(sourceFilename);
709746
const name = base.slice(0, base.length - ext.length);
710747
const data = {
711-
filename: normalizePath(
748+
filename: getNormalizePath()(
712749
path.relative(pattern.context, absoluteFilename),
713750
),
714751
contentHash,
@@ -719,7 +756,7 @@ class CopyPlugin {
719756
},
720757
};
721758
const { path: interpolatedFilename, info: assetInfo } =
722-
compilation.getPathWithInfo(normalizePath(filename), data);
759+
compilation.getPathWithInfo(getNormalizePath()(filename), data);
723760

724761
info = { ...info, ...assetInfo };
725762
filename = interpolatedFilename;
@@ -728,7 +765,7 @@ class CopyPlugin {
728765
`interpolated template '${filename}' for '${sourceFilename}'`,
729766
);
730767
} else {
731-
filename = normalizePath(filename);
768+
filename = getNormalizePath()(filename);
732769
}
733770

734771
// eslint-disable-next-line consistent-return
@@ -798,8 +835,7 @@ class CopyPlugin {
798835
async (unusedAssets, callback) => {
799836
if (typeof globby === "undefined") {
800837
try {
801-
// @ts-ignore
802-
({ globby } = await import("globby"));
838+
globby = await getGlobby();
803839
} catch (error) {
804840
callback(/** @type {Error} */ (error));
805841

@@ -925,7 +961,7 @@ class CopyPlugin {
925961
);
926962

927963
const cacheItem = cache.getItemCache(
928-
`transformAll|${serialize({
964+
`transformAll|${getSerializeJavascript()({
929965
version,
930966
from: normalizedPattern.from,
931967
to: normalizedPattern.to,
@@ -970,13 +1006,16 @@ class CopyPlugin {
9701006
);
9711007

9721008
const { path: interpolatedFilename, info: assetInfo } =
973-
compilation.getPathWithInfo(normalizePath(filename), {
974-
contentHash,
975-
chunk: {
976-
id: "unknown-copied-asset",
977-
hash: contentHash,
1009+
compilation.getPathWithInfo(
1010+
getNormalizePath()(filename),
1011+
{
1012+
contentHash,
1013+
chunk: {
1014+
id: "unknown-copied-asset",
1015+
hash: contentHash,
1016+
},
9781017
},
979-
});
1018+
);
9801019

9811020
transformedAsset.filename = interpolatedFilename;
9821021
transformedAsset.info = assetInfo;

src/utils.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,57 @@ function throttleAll(limit, tasks) {
119119
});
120120
}
121121

122-
module.exports = { stat, readFile, throttleAll };
122+
/**
123+
* @template T
124+
* @param fn {(function(): any) | undefined}
125+
* @returns {function(): T}
126+
*/
127+
function memoize(fn) {
128+
let cache = false;
129+
/** @type {T} */
130+
let result;
131+
132+
return () => {
133+
if (cache) {
134+
return result;
135+
}
136+
137+
result = /** @type {function(): any} */ (fn)();
138+
cache = true;
139+
// Allow to clean up memory for fn
140+
// and all dependent resources
141+
// eslint-disable-next-line no-undefined, no-param-reassign
142+
fn = undefined;
143+
144+
return result;
145+
};
146+
}
147+
148+
/**
149+
* @template T
150+
* @param fn {(function(): any) | undefined}
151+
* @returns {function(): Promise<T>}
152+
*/
153+
function asyncMemoize(fn) {
154+
let cache = false;
155+
/** @type {T} */
156+
let result;
157+
158+
return async () => {
159+
if (cache) {
160+
return result;
161+
}
162+
163+
result = await /** @type {function(): any} */ (fn)();
164+
cache = true;
165+
166+
// Allow to clean up memory for fn
167+
// and all dependent resources
168+
// eslint-disable-next-line no-undefined, no-param-reassign
169+
fn = undefined;
170+
171+
return result;
172+
};
173+
}
174+
175+
module.exports = { stat, readFile, throttleAll, memoize, asyncMemoize };

types/utils.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@ export function readFile(
3333
* @returns {Promise<T[]>} A promise that fulfills to an array of the results
3434
*/
3535
export function throttleAll<T>(limit: number, tasks: Task<T>[]): Promise<T[]>;
36+
/**
37+
* @template T
38+
* @param fn {(function(): any) | undefined}
39+
* @returns {function(): T}
40+
*/
41+
export function memoize<T>(fn: (() => any) | undefined): () => T;
42+
/**
43+
* @template T
44+
* @param fn {(function(): any) | undefined}
45+
* @returns {function(): Promise<T>}
46+
*/
47+
export function asyncMemoize<T>(fn: (() => any) | undefined): () => Promise<T>;

0 commit comments

Comments
 (0)