mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(devtools): use JavaScriptTransformer for Angular optimization esbuild plugin
Refactor the devtools optimization esbuild plugin to use JavaScriptTransformer from @angular/build/private.
This commit is contained in:
@@ -5,7 +5,6 @@ package(default_visibility = ["//visibility:public"])
|
||||
copy_to_bin(
|
||||
name = "js_lib_files",
|
||||
srcs = [
|
||||
"ensure-no-linker-decl.mjs",
|
||||
"esbuild-plugin.d.ts",
|
||||
"esbuild-plugin.mjs",
|
||||
],
|
||||
@@ -16,7 +15,5 @@ js_library(
|
||||
srcs = [":js_lib_files"],
|
||||
deps = [
|
||||
"//:node_modules/@angular/build",
|
||||
"//:node_modules/@babel/core",
|
||||
"//packages/compiler-cli",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
/** Naively checks whether this node path resolves to an Angular declare invocation. */
|
||||
function isNgDeclareCallExpression(nodePath) {
|
||||
if (!nodePath.node.name.startsWith('ɵɵngDeclare')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expect the `ngDeclare` identifier to be used as part of a property access that
|
||||
// is invoked within a call expression. e.g. `i0.ɵɵngDeclare<>`.
|
||||
return (
|
||||
nodePath.parentPath?.type === 'MemberExpression' &&
|
||||
nodePath.parentPath.parentPath?.type === 'CallExpression'
|
||||
);
|
||||
}
|
||||
|
||||
/** Asserts that the given AST does not contain any Angular partial declaration. */
|
||||
export async function assertNoPartialDeclaration(filePath, ast, traverseFn) {
|
||||
// Naively check if there are any Angular declarations left that haven't been linked.
|
||||
traverseFn(ast, {
|
||||
Identifier: (astPath) => {
|
||||
if (isNgDeclareCallExpression(astPath)) {
|
||||
throw astPath.buildCodeFrameError(
|
||||
`Found Angular declaration that has not been linked. ${filePath}`,
|
||||
Error,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
+2
-8
@@ -6,17 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {PluginItem} from '@babel/core';
|
||||
|
||||
export interface OptimizationOptions {
|
||||
enableLinker?: {
|
||||
ensureNoPartialDeclaration: boolean;
|
||||
filterPaths?: RegExp;
|
||||
linkerOptions?: object;
|
||||
};
|
||||
optimize?: {
|
||||
isSideEffectFree?: (absoluteDiskPath: string) => boolean;
|
||||
};
|
||||
enableLinker?: boolean;
|
||||
}
|
||||
|
||||
export function createEsbuildAngularOptimizePlugin(opts: OptimizationOptions, additionalBabelPlugins?: PluginItem[]): Promise<any>
|
||||
export function createEsbuildAngularOptimizePlugin(opts: OptimizationOptions): Promise<any>
|
||||
@@ -6,96 +6,42 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
|
||||
import {transformAsync, traverse} from '@babel/core';
|
||||
import {assertNoPartialDeclaration} from './ensure-no-linker-decl.mjs';
|
||||
|
||||
/**
|
||||
* Creates an ESBuild plugin that configures various Angular optimization Babel plugins.
|
||||
* The Babel plugins configured usually run in the Angular CLI compilation pipeline.
|
||||
* Creates an ESBuild plugin that configures various Angular optimization.
|
||||
* The optimization plugins usually run in the Angular CLI compilation pipeline.
|
||||
*
|
||||
* @param {import('./esbuild-plugin').OptimizationOptions} opts Options
|
||||
* @param additionalBabelPlugins List of additional Babel plugins that should run as part
|
||||
* of this ESBuild plugin. This is primarily supported for reducing the amount of ESBuild
|
||||
* load plugins needed (as they can impact performance significantly).
|
||||
*/
|
||||
export async function createEsbuildAngularOptimizePlugin(opts, additionalBabelPlugins = []) {
|
||||
let linkerCreator = {
|
||||
compiler: null,
|
||||
babel: null,
|
||||
};
|
||||
|
||||
if (opts.enableLinker) {
|
||||
linkerCreator = {
|
||||
compiler: await import('@angular/compiler-cli'),
|
||||
babel: await import('@angular/compiler-cli/linker/babel'),
|
||||
};
|
||||
}
|
||||
|
||||
const {adjustStaticMembers, adjustTypeScriptEnums, elideAngularMetadata, markTopLevelPure} = (
|
||||
await import('@angular/build/private')
|
||||
).default;
|
||||
|
||||
export function createEsbuildAngularOptimizePlugin(opts) {
|
||||
return {
|
||||
name: 'ng-babel-optimize-esbuild',
|
||||
setup: (build) => {
|
||||
name: 'ng-optimize-esbuild',
|
||||
setup: async (build) => {
|
||||
const {JavaScriptTransformer} = (await import('@angular/build/private')).default;
|
||||
const javascriptTransformer = new JavaScriptTransformer(
|
||||
{
|
||||
jit: false,
|
||||
advancedOptimizations: !!opts.optimize,
|
||||
sourcemap: !!build.initialOptions.sourcemap,
|
||||
},
|
||||
/** maxWorkers */ 2,
|
||||
);
|
||||
|
||||
build.onLoad({filter: /\.[cm]?js$/}, async (args) => {
|
||||
const filePath = args.path;
|
||||
const content = await fs.promises.readFile(filePath, 'utf8');
|
||||
const plugins = [...additionalBabelPlugins];
|
||||
const sideEffects = opts.optimize?.isSideEffectFree
|
||||
? !opts.optimize.isSideEffectFree(args.path)
|
||||
: true;
|
||||
|
||||
if (opts.optimize) {
|
||||
plugins.push(adjustStaticMembers, adjustTypeScriptEnums, elideAngularMetadata);
|
||||
const contents = await javascriptTransformer.transformFile(
|
||||
args.path,
|
||||
/** skipLinker */ !opts.enableLinker,
|
||||
sideEffects,
|
||||
);
|
||||
|
||||
// If the current file is denoted as explicit side effect free, add the pure
|
||||
// top-level functions optimization plugin for this file.
|
||||
if (opts.optimize.isSideEffectFree && opts.optimize.isSideEffectFree(args.path)) {
|
||||
plugins.push(markTopLevelPure);
|
||||
}
|
||||
}
|
||||
return {contents};
|
||||
});
|
||||
|
||||
const shouldRunLinker =
|
||||
opts.enableLinker &&
|
||||
(opts.enableLinker.filterPaths == null || opts.enableLinker.filterPaths.test(args.path));
|
||||
|
||||
if (shouldRunLinker) {
|
||||
plugins.push(
|
||||
linkerCreator.babel.createEs2015LinkerPlugin({
|
||||
...(opts.enableLinker.linkerOptions ?? {}),
|
||||
fileSystem: new linkerCreator.compiler.NodeJSFileSystem(),
|
||||
logger: new linkerCreator.compiler.ConsoleLogger(
|
||||
linkerCreator.compiler.LogLevel.warn,
|
||||
),
|
||||
// Workaround for https://github.com/angular/angular/issues/42769 and https://github.com/angular/angular-cli/issues/22647.
|
||||
sourceMapping: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// If no plugins are enabled, return the original code and save time.
|
||||
if (plugins.length === 0) {
|
||||
return {contents: content};
|
||||
}
|
||||
|
||||
const ensureNoPartialDeclaration =
|
||||
opts.enableLinker && opts.enableLinker.ensureNoPartialDeclaration;
|
||||
const {code, ast} = await transformAsync(content, {
|
||||
filename: filePath,
|
||||
filenameRelative: filePath,
|
||||
plugins: plugins,
|
||||
// Sourcemaps are generated inline so that ESBuild can process them.
|
||||
sourceMaps: 'inline',
|
||||
compact: false,
|
||||
// AST is needed when we want to ensure no partial declarations later.
|
||||
ast: ensureNoPartialDeclaration,
|
||||
});
|
||||
|
||||
if (ensureNoPartialDeclaration) {
|
||||
assertNoPartialDeclaration(filePath, ast, traverse);
|
||||
}
|
||||
|
||||
return {contents: code};
|
||||
build.onDispose(() => {
|
||||
void javascriptTransformer.close();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,31 +36,11 @@ export default async function createConfig({
|
||||
conditions: ['es2020', 'es2015', 'module'],
|
||||
define: optimize ? convertObjectToStringDictionary(GLOBAL_DEFS_FOR_TERSER_WITH_AOT) : undefined,
|
||||
plugins: [
|
||||
await createEsbuildAngularOptimizePlugin({
|
||||
createEsbuildAngularOptimizePlugin({
|
||||
optimize: {
|
||||
isSideEffectFree: undefined,
|
||||
},
|
||||
enableLinker: enableLinker
|
||||
? {
|
||||
ensureNoPartialDeclaration: false,
|
||||
// Only run the linker on fesm2020 and fesm2022 bundles. This should not have an effect on
|
||||
// the bundle output, but helps speeding up ESBuild when it visits other modules.
|
||||
filterPaths: /fesm2020|fesm2022/,
|
||||
linkerOptions: {
|
||||
// DevTools relies on angular framework packages that are consumed,
|
||||
// locally via bazel. These packages have a version of 0.0.0-PLACEHOLDER.
|
||||
// DevTools also relies on Angular CDK and Material packages that are consumed via npm.
|
||||
// Because of this, we set unknownDeclarationVersionHandling to ignore so that we bypass
|
||||
// selecting a linker for our CDK and Material dependencies based on our local framework
|
||||
// version (0.0.0-PLACEHOLDER).
|
||||
// Instead this option defaults to the latest linker version, which should
|
||||
// be correct, except for the small time interval where we rollout a new
|
||||
// declaration version and target a material release that hasn't been compiled
|
||||
// with that version yet.
|
||||
unknownDeclarationVersionHandling: 'ignore',
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
enableLinker,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@
|
||||
"@angular/animations": "workspace:*",
|
||||
"@angular/aria": "22.1.0-rc.0",
|
||||
"@angular/benchpress": "workspace: *",
|
||||
"@angular/build": "22.2.0-next.0",
|
||||
"@angular/build": "22.2.0-next.1",
|
||||
"@angular/cdk": "22.1.0-rc.0",
|
||||
"@angular/cli": "22.2.0-next.0",
|
||||
"@angular/common": "workspace:*",
|
||||
|
||||
@@ -34,7 +34,6 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//adev:__pkg__",
|
||||
"//devtools/tools/angular-optimization:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//modules/ssr-benchmarks:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
|
||||
Generated
+150
-2
@@ -28,8 +28,8 @@ importers:
|
||||
specifier: 'workspace: *'
|
||||
version: link:packages/benchpress
|
||||
'@angular/build':
|
||||
specifier: 22.2.0-next.0
|
||||
version: 22.2.0-next.0(a32c4cb6cdd76a7ab914156bb758f065)
|
||||
specifier: 22.2.0-next.1
|
||||
version: 22.2.0-next.1(a32c4cb6cdd76a7ab914156bb758f065)
|
||||
'@angular/cdk':
|
||||
specifier: 22.1.0-rc.0
|
||||
version: 22.1.0-rc.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2)
|
||||
@@ -1493,6 +1493,11 @@ packages:
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
hasBin: true
|
||||
|
||||
'@angular-devkit/architect@0.2202.0-next.1':
|
||||
resolution: {integrity: sha512-P+oXENhfY/xyr2T/2cwa3jIZAwGQCWLS0//CuHKqjDq0FbCHau45Bc64oTfcQib5ROrc7gHMXqK/DYPIfmycvQ==}
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
hasBin: true
|
||||
|
||||
'@angular-devkit/core@22.2.0-next.0':
|
||||
resolution: {integrity: sha512-VsmT/WaZWKw0cU/QbVJdV/mgoAtDvvjFD1jejXHwC4kK/sqBVmxNjon96N110/aXwOzDmu+MPlTYKyviA1LWQQ==}
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
@@ -1502,6 +1507,15 @@ packages:
|
||||
chokidar:
|
||||
optional: true
|
||||
|
||||
'@angular-devkit/core@22.2.0-next.1':
|
||||
resolution: {integrity: sha512-SBWUvFYxp6xeXQhLFv8PYPSUjh/j94YrIbaEw7Sok6VEJkm4cQfDJAq1vMGwDXbrLmjgR9RaP+XPjfjUDid18A==}
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
peerDependencies:
|
||||
chokidar: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
chokidar:
|
||||
optional: true
|
||||
|
||||
'@angular-devkit/schematics@22.2.0-next.0':
|
||||
resolution: {integrity: sha512-6Ok5cZ/i/ox7xkGYCLpDRgMEVfR6N7DrZ/qUXM7jVPOQXXayqfrMCCl1nmXPMu/g+C6nk81TRWfSzDJactBxmA==}
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
@@ -1564,6 +1578,58 @@ packages:
|
||||
vitest:
|
||||
optional: true
|
||||
|
||||
'@angular/build@22.2.0-next.1':
|
||||
resolution: {integrity: sha512-u5Fhud9XR5z0r57xMWlAJnabuSgPV+uswI9NEKRm29kmltPcoTB2e61p7WT9gwQ2kKg2wIKD6YOcxB8f+2VzpQ==}
|
||||
engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
peerDependencies:
|
||||
'@angular/compiler': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/compiler-cli': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/core': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/localize': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/platform-browser': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/platform-server': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/service-worker': ^22.0.0 || ^22.2.0-next.0
|
||||
'@angular/ssr': ^22.2.0-next.1
|
||||
istanbul-lib-instrument: ^6.0.0
|
||||
karma: ^6.4.0
|
||||
less: ^4.2.0
|
||||
ng-packagr: ^22.0.0 || ^22.2.0-next.0
|
||||
postcss: ^8.4.0
|
||||
rollup: ^4.0.0
|
||||
tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0
|
||||
tslib: ^2.3.0
|
||||
typescript: '>=6.0 <6.1'
|
||||
vitest: ^4.0.8
|
||||
peerDependenciesMeta:
|
||||
'@angular/core':
|
||||
optional: true
|
||||
'@angular/localize':
|
||||
optional: true
|
||||
'@angular/platform-browser':
|
||||
optional: true
|
||||
'@angular/platform-server':
|
||||
optional: true
|
||||
'@angular/service-worker':
|
||||
optional: true
|
||||
'@angular/ssr':
|
||||
optional: true
|
||||
istanbul-lib-instrument:
|
||||
optional: true
|
||||
karma:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
ng-packagr:
|
||||
optional: true
|
||||
postcss:
|
||||
optional: true
|
||||
rollup:
|
||||
optional: true
|
||||
tailwindcss:
|
||||
optional: true
|
||||
vitest:
|
||||
optional: true
|
||||
|
||||
'@angular/cdk@22.1.0-rc.0':
|
||||
resolution: {integrity: sha512-E0IbkeugB0fjXFHT2fjb3Cicqz8wt8SEAY/8GqtmP/CC5U9sXuzWLnH8JjkR/96i05WgNT0PfBnycYZEkI59Pw==}
|
||||
peerDependencies:
|
||||
@@ -11603,6 +11669,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- chokidar
|
||||
|
||||
'@angular-devkit/architect@0.2202.0-next.1(chokidar@5.0.0)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 22.2.0-next.1(chokidar@5.0.0)
|
||||
rxjs: 7.8.2
|
||||
transitivePeerDependencies:
|
||||
- chokidar
|
||||
|
||||
'@angular-devkit/core@22.2.0-next.0(chokidar@5.0.0)':
|
||||
dependencies:
|
||||
ajv: 8.20.0
|
||||
@@ -11614,6 +11687,17 @@ snapshots:
|
||||
optionalDependencies:
|
||||
chokidar: 5.0.0
|
||||
|
||||
'@angular-devkit/core@22.2.0-next.1(chokidar@5.0.0)':
|
||||
dependencies:
|
||||
ajv: 8.20.0
|
||||
ajv-formats: 3.0.1
|
||||
jsonc-parser: 3.3.1
|
||||
picomatch: 4.0.5
|
||||
rxjs: 7.8.2
|
||||
source-map: 0.8.0
|
||||
optionalDependencies:
|
||||
chokidar: 5.0.0
|
||||
|
||||
'@angular-devkit/schematics@22.2.0-next.0(chokidar@5.0.0)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 22.2.0-next.0(chokidar@5.0.0)
|
||||
@@ -11754,6 +11838,66 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
'@angular/build@22.2.0-next.1(a32c4cb6cdd76a7ab914156bb758f065)':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@angular-devkit/architect': 0.2202.0-next.1(chokidar@5.0.0)
|
||||
'@angular/compiler': link:packages/compiler
|
||||
'@angular/compiler-cli': link:packages/compiler-cli
|
||||
'@babel/core': 8.0.1
|
||||
'@inquirer/confirm': 6.1.1(@types/node@20.19.43)
|
||||
'@vitejs/plugin-basic-ssl': 2.3.0(vite@8.2.0(@types/node@20.19.43)(esbuild@0.28.1)(jiti@1.21.7)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0))
|
||||
beasties: 0.4.3
|
||||
browserslist: 4.28.7
|
||||
esbuild: 0.28.1
|
||||
https-proxy-agent: 9.1.0(supports-color@11.0.0)
|
||||
jsonc-parser: 3.3.1
|
||||
listr2: 11.0.0
|
||||
magic-string: 1.1.0
|
||||
mrmime: 2.0.1
|
||||
oxc-parser: 0.142.0
|
||||
parse5-html-rewriting-stream: 8.0.1
|
||||
picomatch: 4.0.5
|
||||
piscina: 5.3.0
|
||||
rolldown: 1.2.1
|
||||
sass: 1.102.0
|
||||
semver: 7.8.5
|
||||
source-map-support: 0.5.21
|
||||
tinyglobby: 0.2.17
|
||||
tslib: 2.8.1
|
||||
typescript: 6.0.3
|
||||
vite: 8.2.0(@types/node@20.19.43)(esbuild@0.28.1)(jiti@1.21.7)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
watchpack: 2.5.2
|
||||
optionalDependencies:
|
||||
'@angular/core': link:packages/core
|
||||
'@angular/localize': link:packages/localize
|
||||
'@angular/platform-browser': link:packages/platform-browser
|
||||
'@angular/platform-server': link:packages/platform-server
|
||||
'@angular/service-worker': link:packages/service-worker
|
||||
'@angular/ssr': 22.2.0-next.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-server@packages+platform-server)(@angular/router@packages+router)
|
||||
istanbul-lib-instrument: 6.0.3(supports-color@11.0.0)
|
||||
karma: 6.4.4(bufferutil@4.1.0)(debug@4.4.3(supports-color@11.0.0))(supports-color@11.0.0)(utf-8-validate@6.0.6)
|
||||
less: 4.8.1(supports-color@11.0.0)
|
||||
lmdb: 3.5.6
|
||||
ng-packagr: 22.2.0-next.2(@angular/compiler-cli@packages+compiler-cli)(supports-color@11.0.0)(tailwindcss@3.4.19(tsx@4.23.1)(yaml@2.9.0))(tslib@2.8.1)(typescript@6.0.3)
|
||||
postcss: 8.5.25
|
||||
rollup: 4.62.3
|
||||
tailwindcss: 3.4.19(tsx@4.23.1)(yaml@2.9.0)
|
||||
vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(esbuild@0.28.1)(jiti@1.21.7)(jsdom@30.0.1)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- '@vitejs/devtools'
|
||||
- chokidar
|
||||
- jiti
|
||||
- kerberos
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
'@angular/cdk@22.1.0-rc.0(@angular/common@packages+common)(@angular/core@packages+core)(@angular/platform-browser@packages+platform-browser)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@angular/common': link:packages/common
|
||||
@@ -15296,6 +15440,10 @@ snapshots:
|
||||
dependencies:
|
||||
vite: 8.1.5(@types/node@24.13.3)(esbuild@0.28.1)(jiti@1.21.7)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
|
||||
'@vitejs/plugin-basic-ssl@2.3.0(vite@8.2.0(@types/node@20.19.43)(esbuild@0.28.1)(jiti@1.21.7)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
vite: 8.2.0(@types/node@20.19.43)(esbuild@0.28.1)(jiti@1.21.7)(less@4.8.1(supports-color@11.0.0))(sass@1.102.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
|
||||
'@vitest/expect@4.1.10':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
|
||||
Reference in New Issue
Block a user