Skip to content

Commit 9eaa398

Browse files
committedDec 21, 2022
perf: move stylesheet processing into a worker pool
Stylesheets will now be processed using a worker pool This allows up to four stylesheets to be processed in parallel and keeps the main thread available for other build tasks. `NG_BUILD_MAX_WORKERS` environment variable can be used to comfigure the limit of workers used.
1 parent caf0fee commit 9eaa398

File tree

7 files changed

+325
-214
lines changed

7 files changed

+325
-214
lines changed
 

Diff for: ‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"jsonc-parser": "^3.2.0",
5151
"less": "^4.1.3",
5252
"ora": "^5.1.0",
53+
"piscina": "^3.2.0",
5354
"postcss": "^8.4.16",
5455
"postcss-url": "^10.1.3",
5556
"rollup": "^3.0.0",

Diff for: ‎src/lib/ng-package/entry-point/compile-ngc.transform.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ export const compileNgcTransformFactory = (
2020
discardStdin: false,
2121
});
2222

23-
try {
24-
const entryPoints: EntryPointNode[] = graph.filter(isEntryPoint);
25-
const entryPoint: EntryPointNode = entryPoints.find(isEntryPointInProgress());
26-
const ngPackageNode: PackageNode = graph.find(isPackage);
27-
const projectBasePath = ngPackageNode.data.primary.basePath;
23+
const entryPoints: EntryPointNode[] = graph.filter(isEntryPoint);
24+
const entryPoint: EntryPointNode = entryPoints.find(isEntryPointInProgress());
25+
const ngPackageNode: PackageNode = graph.find(isPackage);
26+
const projectBasePath = ngPackageNode.data.primary.basePath;
2827

28+
try {
2929
// Add paths mappings for dependencies
3030
const tsConfig = setDependenciesTsConfigPaths(entryPoint.data.tsConfig, entryPoints);
3131

@@ -74,6 +74,10 @@ export const compileNgcTransformFactory = (
7474
} catch (error) {
7575
spinner.fail();
7676
throw error;
77+
} finally {
78+
if (!options.watch) {
79+
entryPoint.cache.stylesheetProcessor?.destroy();
80+
}
7781
}
7882

7983
spinner.succeed();

Diff for: ‎src/lib/ng-package/package.transform.ts

+24-35
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { DepGraph } from 'dependency-graph';
2-
import { NEVER, Observable, from, of as observableOf, pipe } from 'rxjs';
2+
import { NEVER, Observable, finalize, from, of as observableOf, pipe } from 'rxjs';
33
import {
44
catchError,
55
concatMap,
66
debounceTime,
77
defaultIfEmpty,
88
filter,
99
map,
10-
mapTo,
1110
startWith,
1211
switchMap,
1312
takeLast,
@@ -61,48 +60,30 @@ export const packageTransformFactory =
6160
entryPointTransform: Transform,
6261
) =>
6362
(source$: Observable<BuildGraph>): Observable<BuildGraph> => {
64-
const pkgUri = ngUrl(project);
63+
log.info(`Building Angular Package`);
6564

6665
const buildTransform = options.watch
6766
? watchTransformFactory(project, options, analyseSourcesTransform, entryPointTransform)
6867
: buildTransformFactory(project, analyseSourcesTransform, entryPointTransform);
6968

69+
const pkgUri = ngUrl(project);
70+
const ngPkg = new PackageNode(pkgUri);
71+
7072
return source$.pipe(
71-
tap(() => log.info(`Building Angular Package`)),
7273
// Discover packages and entry points
73-
switchMap(graph => {
74-
const pkg = discoverPackages({ project });
75-
76-
return from(pkg).pipe(
77-
map(value => {
78-
const ngPkg = new PackageNode(pkgUri);
79-
ngPkg.data = value;
80-
81-
return graph.put(ngPkg);
82-
}),
83-
);
84-
}),
8574
// Clean the primary dest folder (should clean all secondary sub-directory, as well)
86-
switchMap(
87-
async graph => {
88-
const { dest, deleteDestPath } = graph.get(pkgUri).data;
75+
switchMap(async graph => {
76+
ngPkg.data = await discoverPackages({ project });
8977

90-
if (deleteDestPath) {
91-
try {
92-
await rmdir(dest, { recursive: true });
93-
} catch {}
94-
}
95-
},
96-
(graph, _) => graph,
97-
),
98-
// Add entry points to graph
99-
map(graph => {
100-
const foundNode = graph.get(pkgUri);
101-
if (!isPackage(foundNode)) {
102-
return graph;
78+
graph.put(ngPkg);
79+
const { dest, deleteDestPath } = ngPkg.data;
80+
81+
if (deleteDestPath) {
82+
try {
83+
await rmdir(dest, { recursive: true });
84+
} catch {}
10385
}
10486

105-
const ngPkg: PackageNode = foundNode;
10687
const entryPoints = [ngPkg.data.primary, ...ngPkg.data.secondaries].map(entryPoint => {
10788
const { destinationFiles, moduleId } = entryPoint;
10889
const node = new EntryPointNode(
@@ -118,12 +99,20 @@ export const packageTransformFactory =
11899
return node;
119100
});
120101

102+
// Add entry points to graph
121103
return graph.put(entryPoints);
122104
}),
123105
// Initialize the tsconfig for each entry point
124106
initTsConfigTransform,
125107
// perform build
126108
buildTransform,
109+
finalize(() => {
110+
for (const node of ngPkg.dependents) {
111+
if (node instanceof EntryPointNode) {
112+
node.cache.stylesheetProcessor?.destroy();
113+
}
114+
}
115+
}),
127116
);
128117
};
129118

@@ -190,7 +179,7 @@ const watchTransformFactory =
190179
debounceTime(200),
191180
tap(() => log.msg(FileChangeDetected)),
192181
startWith(undefined),
193-
mapTo(graph),
182+
map(() => graph),
194183
);
195184
}),
196185
switchMap(graph => {
@@ -264,7 +253,7 @@ const scheduleEntryPoints = (epTransform: Transform): Transform =>
264253
observableOf(ep).pipe(
265254
// Mark the entry point as 'in-progress'
266255
tap(entryPoint => (entryPoint.state = STATE_IN_PROGRESS)),
267-
mapTo(graph),
256+
map(() => graph),
268257
epTransform,
269258
),
270259
),

Diff for: ‎src/lib/styles/stylesheet-processor-worker.ts

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import autoprefixer from 'autoprefixer';
2+
import { extname, relative } from 'node:path';
3+
import { pathToFileURL } from 'node:url';
4+
import { workerData } from 'node:worker_threads';
5+
import postcss from 'postcss';
6+
import postcssUrl from 'postcss-url';
7+
import { EsbuildExecutor } from '../esbuild/esbuild-executor';
8+
import { generateKey, readCacheEntry, saveCacheEntry } from '../utils/cache';
9+
import * as log from '../utils/log';
10+
import { CssUrl } from './stylesheet-processor';
11+
12+
const { tailwindConfigPath, projectBasePath, browserslistData, targets, cssUrl, styleIncludePaths } = workerData as {
13+
tailwindConfigPath: string | undefined;
14+
browserslistData: string;
15+
targets: string[];
16+
projectBasePath: string;
17+
cssUrl: CssUrl;
18+
styleIncludePaths: string[];
19+
cacheDirectory: string | undefined;
20+
};
21+
22+
let cacheDirectory = workerData.cacheDirectory;
23+
let postCssProcessor: ReturnType<typeof postcss>;
24+
let esbuild: EsbuildExecutor;
25+
26+
interface RenderRequest {
27+
content: string;
28+
filePath: string;
29+
}
30+
31+
async function render({ content, filePath }: RenderRequest): Promise<string> {
32+
let key: string | undefined;
33+
if (cacheDirectory && !content.includes('@import') && !content.includes('@use')) {
34+
// No transitive deps, we can cache more aggressively.
35+
key = await generateKey(content, ...browserslistData);
36+
const result = await readCacheEntry(cacheDirectory, key);
37+
if (result) {
38+
result.warnings.forEach(msg => log.warn(msg));
39+
40+
return result.css;
41+
}
42+
}
43+
44+
// Render pre-processor language (sass, styl, less)
45+
const renderedCss = await renderCss(filePath, content);
46+
47+
// We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output.
48+
// Example a change in a mixin or SCSS variable.
49+
if (!key) {
50+
key = await generateKey(renderedCss, ...browserslistData);
51+
}
52+
53+
if (cacheDirectory) {
54+
const cachedResult = await readCacheEntry(cacheDirectory, key);
55+
if (cachedResult) {
56+
cachedResult.warnings.forEach(msg => log.warn(msg));
57+
58+
return cachedResult.css;
59+
}
60+
}
61+
62+
// Render postcss (autoprefixing and friends)
63+
const result = await postCssProcessor.process(renderedCss, {
64+
from: filePath,
65+
to: filePath.replace(extname(filePath), '.css'),
66+
});
67+
68+
const warnings = result.warnings().map(w => w.toString());
69+
const { code, warnings: esBuildWarnings } = await esbuild.transform(result.css, {
70+
loader: 'css',
71+
minify: true,
72+
target: targets,
73+
sourcefile: filePath,
74+
});
75+
76+
if (esBuildWarnings.length > 0) {
77+
warnings.push(...(await esbuild.formatMessages(esBuildWarnings, { kind: 'warning' })));
78+
}
79+
80+
if (cacheDirectory) {
81+
await saveCacheEntry(
82+
cacheDirectory,
83+
key,
84+
JSON.stringify({
85+
css: code,
86+
warnings,
87+
}),
88+
);
89+
}
90+
91+
warnings.forEach(msg => log.warn(msg));
92+
93+
return code;
94+
}
95+
96+
async function renderCss(filePath: string, css: string): Promise<string> {
97+
const ext = extname(filePath);
98+
99+
switch (ext) {
100+
case '.sass':
101+
case '.scss': {
102+
return (await import('sass')).compileString(css, {
103+
url: pathToFileURL(filePath),
104+
syntax: '.sass' === ext ? 'indented' : 'scss',
105+
loadPaths: styleIncludePaths,
106+
}).css;
107+
}
108+
case '.less': {
109+
const { css: content } = await (
110+
await import('less')
111+
).default.render(css, {
112+
filename: filePath,
113+
math: 'always',
114+
javascriptEnabled: true,
115+
paths: styleIncludePaths,
116+
});
117+
118+
return content;
119+
}
120+
121+
case '.css':
122+
default:
123+
return css;
124+
}
125+
}
126+
127+
function getTailwindPlugin() {
128+
// Attempt to setup Tailwind CSS
129+
// Only load Tailwind CSS plugin if configuration file was found.
130+
// This acts as a guard to ensure the project actually wants to use Tailwind CSS.
131+
// The package may be unknowningly present due to a third-party transitive package dependency.
132+
if (tailwindConfigPath) {
133+
let tailwindPackagePath;
134+
try {
135+
tailwindPackagePath = require.resolve('tailwindcss', { paths: [projectBasePath] });
136+
} catch {
137+
const relativeTailwindConfigPath = relative(projectBasePath, tailwindConfigPath);
138+
log.warn(
139+
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
140+
` but the 'tailwindcss' package is not installed.` +
141+
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
142+
);
143+
}
144+
145+
if (tailwindPackagePath) {
146+
return require(tailwindPackagePath)({ config: tailwindConfigPath });
147+
}
148+
}
149+
}
150+
151+
async function initialize() {
152+
const postCssPlugins = [];
153+
const tailwinds = getTailwindPlugin();
154+
if (tailwinds) {
155+
postCssPlugins.push(tailwinds);
156+
cacheDirectory = undefined;
157+
}
158+
159+
if (cssUrl !== CssUrl.none) {
160+
postCssPlugins.push(postcssUrl({ url: cssUrl }));
161+
}
162+
163+
postCssPlugins.push(
164+
autoprefixer({
165+
ignoreUnknownVersions: true,
166+
overrideBrowserslist: browserslistData,
167+
}),
168+
);
169+
170+
postCssProcessor = postcss(postCssPlugins);
171+
172+
esbuild = new EsbuildExecutor();
173+
174+
// Return the render function for use
175+
return render;
176+
}
177+
178+
/**
179+
* The default export will be the promise returned by the initialize function.
180+
* This is awaited by piscina prior to using the Worker.
181+
*/
182+
export default initialize();

Diff for: ‎src/lib/styles/stylesheet-processor.ts

+49-172
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,29 @@
1-
import autoprefixer from 'autoprefixer';
21
import browserslist from 'browserslist';
3-
import { existsSync } from 'fs';
4-
import { dirname, extname, join, relative } from 'path';
5-
import postcss from 'postcss';
6-
import postcssUrl from 'postcss-url';
7-
import { pathToFileURL } from 'url';
8-
import { EsbuildExecutor } from '../esbuild/esbuild-executor';
9-
import { generateKey, readCacheEntry, saveCacheEntry } from '../utils/cache';
10-
import * as log from '../utils/log';
2+
import { dirname, join } from 'path';
3+
import Piscina from 'piscina';
4+
import { colors } from '../utils/color';
5+
import { exists } from '../utils/fs';
6+
7+
const maxWorkersVariable = process.env['NG_BUILD_MAX_WORKERS'];
8+
const maxThreads = typeof maxWorkersVariable === 'string' && maxWorkersVariable !== '' ? +maxWorkersVariable : 4;
119

1210
export enum CssUrl {
1311
inline = 'inline',
1412
none = 'none',
1513
}
1614

17-
export interface Result {
18-
css: string;
19-
warnings: string[];
20-
error?: string;
21-
}
2215
export class StylesheetProcessor {
23-
private browserslistData: string[];
24-
private targets: string[];
25-
private postCssProcessor: ReturnType<typeof postcss>;
26-
private esbuild = new EsbuildExecutor();
27-
private styleIncludePaths: string[];
28-
private readonly nodeModulesDirs = [];
16+
private renderWorker: Piscina | undefined;
2917

3018
constructor(
3119
private readonly projectBasePath: string,
3220
private readonly basePath: string,
3321
private readonly cssUrl?: CssUrl,
3422
private readonly includePaths?: string[],
35-
private cacheDirectory?: string | false,
23+
private readonly cacheDirectory?: string | false,
3624
) {
37-
log.debug(`determine browserslist for ${this.basePath}`);
3825
// By default, browserslist defaults are too inclusive
3926
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
40-
4127
// We change the default query to browsers that Angular support.
4228
// https://angular.io/guide/browser-support
4329
(browserslist.defaults as string[]) = [
@@ -48,142 +34,57 @@ export class StylesheetProcessor {
4834
'last 2 iOS major versions',
4935
'Firefox ESR',
5036
];
51-
52-
this.styleIncludePaths = [...this.includePaths];
53-
let prevDir = null;
54-
let currentDir = this.basePath;
55-
56-
while (currentDir !== prevDir) {
57-
const p = join(currentDir, 'node_modules');
58-
if (existsSync(p)) {
59-
this.styleIncludePaths.push(p);
60-
this.nodeModulesDirs.push(p);
61-
}
62-
63-
prevDir = currentDir;
64-
currentDir = dirname(prevDir);
65-
}
66-
67-
this.browserslistData = browserslist(undefined, { path: this.basePath });
68-
this.targets = transformSupportedBrowsersToTargets(this.browserslistData);
69-
this.postCssProcessor = this.createPostCssPlugins();
7037
}
7138

7239
async process({ filePath, content }: { filePath: string; content: string }): Promise<string> {
73-
let key: string | undefined;
74-
75-
if (!content.includes('@import') && !content.includes('@use') && this.cacheDirectory) {
76-
// No transitive deps, we can cache more aggressively.
77-
key = await generateKey(content, ...this.browserslistData);
78-
const result = await readCacheEntry(this.cacheDirectory, key);
79-
if (result) {
80-
result.warnings.forEach(msg => log.warn(msg));
81-
82-
return result.css;
83-
}
84-
}
85-
86-
// Render pre-processor language (sass, styl, less)
87-
const renderedCss = await this.renderCss(filePath, content);
88-
89-
// We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output.
90-
// Example a change in a mixin or SCSS variable.
91-
if (!key) {
92-
key = await generateKey(renderedCss, ...this.browserslistData);
93-
}
94-
95-
if (this.cacheDirectory) {
96-
const cachedResult = await readCacheEntry(this.cacheDirectory, key);
97-
if (cachedResult) {
98-
cachedResult.warnings.forEach(msg => log.warn(msg));
99-
100-
return cachedResult.css;
101-
}
102-
}
103-
// Render postcss (autoprefixing and friends)
104-
const result = await this.postCssProcessor.process(renderedCss, {
105-
from: filePath,
106-
to: filePath.replace(extname(filePath), '.css'),
107-
});
40+
await this.getOrCreateRenderWorker();
10841

109-
const warnings = result.warnings().map(w => w.toString());
110-
const { code, warnings: esBuildWarnings } = await this.esbuild.transform(result.css, {
111-
loader: 'css',
112-
minify: true,
113-
target: this.targets,
114-
sourcefile: filePath,
115-
});
116-
117-
if (esBuildWarnings.length > 0) {
118-
warnings.push(...(await this.esbuild.formatMessages(esBuildWarnings, { kind: 'warning' })));
119-
}
120-
121-
if (this.cacheDirectory) {
122-
await saveCacheEntry(
123-
this.cacheDirectory,
124-
key,
125-
JSON.stringify({
126-
css: code,
127-
warnings,
128-
}),
129-
);
130-
}
131-
warnings.forEach(msg => log.warn(msg));
132-
133-
return code;
42+
return this.renderWorker.run({ content, filePath });
13443
}
13544

136-
private createPostCssPlugins(): ReturnType<typeof postcss> {
137-
const postCssPlugins = [];
138-
const tailwinds = getTailwindPlugin(this.projectBasePath);
139-
if (tailwinds) {
140-
postCssPlugins.push(tailwinds);
141-
this.cacheDirectory = false;
142-
}
143-
144-
if (this.cssUrl !== CssUrl.none) {
145-
postCssPlugins.push(postcssUrl({ url: this.cssUrl }));
146-
}
147-
148-
postCssPlugins.push(
149-
autoprefixer({
150-
ignoreUnknownVersions: true,
151-
overrideBrowserslist: this.browserslistData,
152-
}),
153-
);
154-
155-
return postcss(postCssPlugins);
45+
/** Destory workers in pool. */
46+
destroy(): void {
47+
void this.renderWorker?.destroy();
15648
}
15749

158-
private async renderCss(filePath: string, css: string): Promise<string> {
159-
const ext = extname(filePath);
50+
private async getOrCreateRenderWorker(): Promise<void> {
51+
if (this.renderWorker) {
52+
return;
53+
}
16054

161-
switch (ext) {
162-
case '.sass':
163-
case '.scss': {
164-
return (await import('sass')).compileString(css, {
165-
url: pathToFileURL(filePath),
166-
syntax: '.sass' === ext ? 'indented' : 'scss',
167-
loadPaths: this.styleIncludePaths,
168-
}).css;
169-
}
170-
case '.less': {
171-
const { css: content } = await (
172-
await import('less')
173-
).default.render(css, {
174-
filename: filePath,
175-
math: 'always',
176-
javascriptEnabled: true,
177-
paths: this.styleIncludePaths,
178-
});
55+
const styleIncludePaths = [...this.includePaths];
56+
let prevDir = null;
57+
let currentDir = this.basePath;
17958

180-
return content;
59+
while (currentDir !== prevDir) {
60+
const p = join(currentDir, 'node_modules');
61+
if (await exists(p)) {
62+
styleIncludePaths.push(p);
18163
}
18264

183-
case '.css':
184-
default:
185-
return css;
65+
prevDir = currentDir;
66+
currentDir = dirname(prevDir);
18667
}
68+
69+
const browserslistData = browserslist(undefined, { path: this.basePath });
70+
71+
this.renderWorker = new Piscina({
72+
filename: require.resolve('./stylesheet-processor-worker'),
73+
maxThreads,
74+
env: {
75+
...process.env,
76+
FORCE_COLOR: '' + colors.enabled,
77+
},
78+
workerData: {
79+
tailwindConfigPath: await getTailwindConfigPath(this.projectBasePath),
80+
projectBasePath: this.projectBasePath,
81+
browserslistData,
82+
targets: transformSupportedBrowsersToTargets(browserslistData),
83+
cacheDirectory: this.cacheDirectory,
84+
cssUrl: this.cssUrl,
85+
styleIncludePaths: styleIncludePaths,
86+
},
87+
});
18788
}
18889
}
18990

@@ -219,39 +120,15 @@ function transformSupportedBrowsersToTargets(supportedBrowsers: string[]): strin
219120
return transformed.length ? transformed : undefined;
220121
}
221122

222-
function getTailwindPlugin(projectBasePath: string) {
223-
// Attempt to setup Tailwind CSS
224-
// Only load Tailwind CSS plugin if configuration file was found.
225-
// This acts as a guard to ensure the project actually wants to use Tailwind CSS.
226-
// The package may be unknowningly present due to a third-party transitive package dependency.
227-
const tailwindConfigPath = getTailwindConfigPath(projectBasePath);
228-
if (tailwindConfigPath) {
229-
let tailwindPackagePath;
230-
try {
231-
tailwindPackagePath = require.resolve('tailwindcss', { paths: [projectBasePath] });
232-
} catch {
233-
const relativeTailwindConfigPath = relative(projectBasePath, tailwindConfigPath);
234-
log.warn(
235-
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
236-
` but the 'tailwindcss' package is not installed.` +
237-
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
238-
);
239-
}
240-
if (tailwindPackagePath) {
241-
return require(tailwindPackagePath)({ config: tailwindConfigPath });
242-
}
243-
}
244-
}
245-
246-
function getTailwindConfigPath(projectRoot: string): string | undefined {
123+
async function getTailwindConfigPath(projectRoot: string): Promise<string | undefined> {
247124
// A configuration file can exist in the project or workspace root
248125
// The list of valid config files can be found:
249126
// https://github.com/tailwindlabs/tailwindcss/blob/8845d112fb62d79815b50b3bae80c317450b8b92/src/util/resolveConfigPath.js#L46-L52
250127
const tailwindConfigFiles = ['tailwind.config.js', 'tailwind.config.cjs'];
251128
for (const configFile of tailwindConfigFiles) {
252129
// Irrespective of the name project level configuration should always take precedence.
253130
const fullPath = join(projectRoot, configFile);
254-
if (existsSync(fullPath)) {
131+
if (await exists(fullPath)) {
255132
return fullPath;
256133
}
257134
}

Diff for: ‎src/tsconfig.packagr.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"skipLibCheck": true, // due to: https://github.com/angular/angular/issues/23112
77
"types": ["node"]
88
},
9-
"files": ["cli/main.ts", "public_api.ts"],
9+
"files": ["cli/main.ts", "public_api.ts", "./lib/styles/stylesheet-processor-worker.ts"],
1010
"exclude": ["**/*.spec.ts"]
1111
}

Diff for: ‎yarn.lock

+59-1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@
124124
dependencies:
125125
tslib "^2.3.0"
126126

127+
"@assemblyscript/loader@^0.10.1":
128+
version "0.10.1"
129+
resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06"
130+
integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==
131+
127132
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6":
128133
version "7.18.6"
129134
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
@@ -1880,7 +1885,7 @@ balanced-match@^1.0.0:
18801885
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
18811886
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
18821887

1883-
base64-js@^1.3.1:
1888+
base64-js@^1.2.0, base64-js@^1.3.1:
18841889
version "1.5.1"
18851890
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
18861891
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -2917,6 +2922,11 @@ event-emitter@^0.3.5:
29172922
d "1"
29182923
es5-ext "~0.10.14"
29192924

2925+
eventemitter-asyncresource@^1.0.0:
2926+
version "1.0.0"
2927+
resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b"
2928+
integrity sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==
2929+
29202930
execa@^4.0.0:
29212931
version "4.1.0"
29222932
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
@@ -3370,6 +3380,20 @@ has@^1.0.3:
33703380
dependencies:
33713381
function-bind "^1.1.1"
33723382

3383+
hdr-histogram-js@^2.0.1:
3384+
version "2.0.3"
3385+
resolved "https://registry.yarnpkg.com/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz#0b860534655722b6e3f3e7dca7b78867cf43dcb5"
3386+
integrity sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==
3387+
dependencies:
3388+
"@assemblyscript/loader" "^0.10.1"
3389+
base64-js "^1.2.0"
3390+
pako "^1.0.3"
3391+
3392+
hdr-histogram-percentiles-obj@^3.0.0:
3393+
version "3.0.0"
3394+
resolved "https://registry.yarnpkg.com/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz#9409f4de0c2dda78e61de2d9d78b1e9f3cba283c"
3395+
integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==
3396+
33733397
hosted-git-info@^2.1.4:
33743398
version "2.8.9"
33753399
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@@ -4226,6 +4250,24 @@ next-tick@1, next-tick@^1.1.0:
42264250
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
42274251
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
42284252

4253+
nice-napi@^1.0.2:
4254+
version "1.0.2"
4255+
resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b"
4256+
integrity sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==
4257+
dependencies:
4258+
node-addon-api "^3.0.0"
4259+
node-gyp-build "^4.2.2"
4260+
4261+
node-addon-api@^3.0.0:
4262+
version "3.2.1"
4263+
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
4264+
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
4265+
4266+
node-gyp-build@^4.2.2:
4267+
version "4.5.0"
4268+
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
4269+
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
4270+
42294271
node-releases@^2.0.6:
42304272
version "2.0.8"
42314273
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
@@ -4414,6 +4456,11 @@ p-try@^2.0.0:
44144456
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
44154457
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
44164458

4459+
pako@^1.0.3:
4460+
version "1.0.11"
4461+
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
4462+
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
4463+
44174464
parent-module@^1.0.0:
44184465
version "1.0.1"
44194466
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -4518,6 +4565,17 @@ pify@^4.0.1:
45184565
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
45194566
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
45204567

4568+
piscina@^3.2.0:
4569+
version "3.2.0"
4570+
resolved "https://registry.yarnpkg.com/piscina/-/piscina-3.2.0.tgz#f5a1dde0c05567775690cccefe59d9223924d154"
4571+
integrity sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==
4572+
dependencies:
4573+
eventemitter-asyncresource "^1.0.0"
4574+
hdr-histogram-js "^2.0.1"
4575+
hdr-histogram-percentiles-obj "^3.0.0"
4576+
optionalDependencies:
4577+
nice-napi "^1.0.2"
4578+
45214579
pkg-dir@^4.1.0:
45224580
version "4.2.0"
45234581
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"

0 commit comments

Comments
 (0)
Please sign in to comment.