@procore/hammer-lib-tsup 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +16 -0
- package/dist/index.js +132 -2
- package/dist/index.mjs +133 -3
- package/package.json +14 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @procore/hammer-lib-tsup
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 59a4b08: Upgrades tsup, esbuild, and other dependencies.
|
|
8
|
+
- 32ad5d2: Add validate command to libs-tsup
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 59a4b08: Upgrades `picocolors` to 1.1.1.
|
|
13
|
+
- 5dd6e18: Fixes interface for `lib:validate` command.
|
|
14
|
+
- Updated dependencies [59a4b08]
|
|
15
|
+
- Updated dependencies [59a4b08]
|
|
16
|
+
- Updated dependencies [5dd6e18]
|
|
17
|
+
- Updated dependencies [32ad5d2]
|
|
18
|
+
- @procore/hammer-utils@0.2.0
|
|
19
|
+
- @procore/hammer-types@0.5.1
|
|
20
|
+
|
|
3
21
|
## 0.6.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -46,9 +46,25 @@ This project uses [`yarn`][yarn], and supports the following commands:
|
|
|
46
46
|
- `lint:types`: uses [`TypeScript`][typescript] to validate types.
|
|
47
47
|
- `test`: runs the unit test suite.
|
|
48
48
|
- `test:ci`: run the unit test suite, reporting coverage.
|
|
49
|
+
- `lib:build --analyze`: builds the library and generates metadata for bundle analysis.
|
|
49
50
|
|
|
51
|
+
### Using `lib:build --analyze`
|
|
52
|
+
|
|
53
|
+
When you run `hammer lib:build --analyze`, it generates the following output files:
|
|
54
|
+
|
|
55
|
+
- `metafile-esm.json`: Metadata for the ESM (ECMAScript Module) output.
|
|
56
|
+
- `metafile-cjs.json`: Metadata for the CJS (CommonJS) output.
|
|
57
|
+
|
|
58
|
+
These files can be used to analyze the bundle, understand the size of individual modules, and identify optimizations.
|
|
59
|
+
|
|
60
|
+
For more detailed information on how the metadata works, refer to the [Tsup Metafile Documentation][tsup-metafile].
|
|
61
|
+
|
|
62
|
+
Additionally, you can use tools like [Bundle Buddy][bundle-buddy] to visualize the output and track down duplicate dependencies or optimize your bundle for better performance.
|
|
63
|
+
|
|
64
|
+
[bundle-buddy]: https://www.bundle-buddy.com/esbuild
|
|
50
65
|
[eslint]: https://eslint.org/
|
|
51
66
|
[prettier]: https://prettier.io/
|
|
52
67
|
[tsup]: https://tsup.egoist.dev/
|
|
68
|
+
[tsup-metafile]: https://tsup.egoist.dev/#metafile
|
|
53
69
|
[typescript]: https://www.typescriptlang.org/
|
|
54
70
|
[yarn]: https://classic.yarnpkg.com/
|
package/dist/index.js
CHANGED
|
@@ -148,10 +148,138 @@ async function performBuild(buildOptions) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/index.ts
|
|
151
|
+
var import_hammer_utils2 = require("@procore/hammer-utils");
|
|
152
|
+
|
|
153
|
+
// src/validate-package.ts
|
|
151
154
|
var import_hammer_utils = require("@procore/hammer-utils");
|
|
155
|
+
var import_picocolors3 = __toESM(require("picocolors"));
|
|
156
|
+
var import_node_fs = __toESM(require("fs"));
|
|
157
|
+
var errorIsNotExisted = (name) => `The property ${import_picocolors3.default.yellow(name)} doesn't exist in the package.json.`;
|
|
158
|
+
var errorFileIsNotExisted = (name) => `The file from ${import_picocolors3.default.yellow(name)} property doesn't exist in the dist folder.
|
|
159
|
+
`;
|
|
160
|
+
var areDeepFileExist = (value) => {
|
|
161
|
+
let isValid = true;
|
|
162
|
+
if (Array.isArray(value) && isValid) {
|
|
163
|
+
isValid = value.every(import_node_fs.default.existsSync);
|
|
164
|
+
}
|
|
165
|
+
if (typeof value === "object" && !Array.isArray(value) && value !== null && isValid) {
|
|
166
|
+
isValid = Object.values(value).every(
|
|
167
|
+
(file) => areDeepFileExist(file)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (typeof value === "string" && isValid) {
|
|
171
|
+
isValid = import_node_fs.default.existsSync(value);
|
|
172
|
+
}
|
|
173
|
+
return isValid;
|
|
174
|
+
};
|
|
175
|
+
var validatePackage = async (rootDir) => {
|
|
176
|
+
const pkg = await (0, import_hammer_utils.readRootPackageJson)(rootDir);
|
|
177
|
+
let isValid = true;
|
|
178
|
+
if (!pkg.type) {
|
|
179
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#type";
|
|
180
|
+
process.stderr.write(errorIsNotExisted("type"));
|
|
181
|
+
process.stdout.write(
|
|
182
|
+
`Please specify it as ${import_picocolors3.default.bold('"module" (recommended)')} or read ${import_picocolors3.default.bold(docUrl)}.
|
|
183
|
+
`
|
|
184
|
+
);
|
|
185
|
+
isValid = false;
|
|
186
|
+
}
|
|
187
|
+
if (!pkg.exports) {
|
|
188
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#exports";
|
|
189
|
+
const exampleStructure = {
|
|
190
|
+
exports: {
|
|
191
|
+
".": {
|
|
192
|
+
import: {
|
|
193
|
+
types: "./dist/modern/index.d.ts",
|
|
194
|
+
default: "./dist/modern/index.js"
|
|
195
|
+
},
|
|
196
|
+
require: {
|
|
197
|
+
types: "./dist/modern/index.d.cts",
|
|
198
|
+
default: "./dist/modern/index.cjs"
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"./package.json": "./package.json"
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
process.stderr.write(errorIsNotExisted("exports"));
|
|
205
|
+
process.stdout.write(
|
|
206
|
+
`Please specify it as ${import_picocolors3.default.bold(JSON.stringify(exampleStructure, null, 2))} or read ${import_picocolors3.default.bold(docUrl)}.
|
|
207
|
+
`
|
|
208
|
+
);
|
|
209
|
+
isValid = false;
|
|
210
|
+
}
|
|
211
|
+
if (pkg.exports && !areDeepFileExist(pkg.exports)) {
|
|
212
|
+
process.stderr.write(errorFileIsNotExisted("exports"));
|
|
213
|
+
isValid = false;
|
|
214
|
+
}
|
|
215
|
+
if (!pkg.main) {
|
|
216
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#main";
|
|
217
|
+
process.stderr.write(errorIsNotExisted("main"));
|
|
218
|
+
process.stdout.write(
|
|
219
|
+
`Please point it on your main file. You can read more in ${import_picocolors3.default.bold(docUrl)}.
|
|
220
|
+
`
|
|
221
|
+
);
|
|
222
|
+
isValid = false;
|
|
223
|
+
}
|
|
224
|
+
if (pkg.main && !import_node_fs.default.existsSync(pkg.main)) {
|
|
225
|
+
process.stderr.write(errorFileIsNotExisted("main"));
|
|
226
|
+
isValid = false;
|
|
227
|
+
}
|
|
228
|
+
if (!pkg.module) {
|
|
229
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#package-entry-points";
|
|
230
|
+
process.stderr.write(errorIsNotExisted("module"));
|
|
231
|
+
process.stdout.write(`You can read more in ${import_picocolors3.default.bold(docUrl)}.
|
|
232
|
+
`);
|
|
233
|
+
isValid = false;
|
|
234
|
+
}
|
|
235
|
+
if (pkg.module && !import_node_fs.default.existsSync(pkg.module)) {
|
|
236
|
+
process.stderr.write(errorFileIsNotExisted("module"));
|
|
237
|
+
isValid = false;
|
|
238
|
+
}
|
|
239
|
+
if (!pkg.types) {
|
|
240
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#community-conditions-definitions";
|
|
241
|
+
process.stderr.write(errorIsNotExisted("types"));
|
|
242
|
+
process.stdout.write(`You can read more in ${import_picocolors3.default.bold(docUrl)}.
|
|
243
|
+
`);
|
|
244
|
+
isValid = false;
|
|
245
|
+
}
|
|
246
|
+
if (pkg.types && !areDeepFileExist(pkg.types)) {
|
|
247
|
+
process.stderr.write(errorFileIsNotExisted("types"));
|
|
248
|
+
isValid = false;
|
|
249
|
+
}
|
|
250
|
+
if (!pkg.files) {
|
|
251
|
+
const docUrl = "https://classic.yarnpkg.com/lang/en/docs/package-json/#toc-files";
|
|
252
|
+
process.stderr.write(
|
|
253
|
+
`${errorIsNotExisted("files")}
|
|
254
|
+
The optional ${import_picocolors3.default.yellow("files")} field is an array of file patterns that describes the entries to be included when your package is installed as a dependency.
|
|
255
|
+
`
|
|
256
|
+
);
|
|
257
|
+
process.stdout.write(`You can read more in ${import_picocolors3.default.bold(docUrl)}.
|
|
258
|
+
`);
|
|
259
|
+
isValid = false;
|
|
260
|
+
}
|
|
261
|
+
if (pkg.files && !areDeepFileExist(pkg.files)) {
|
|
262
|
+
process.stderr.write(errorFileIsNotExisted("files"));
|
|
263
|
+
isValid = false;
|
|
264
|
+
}
|
|
265
|
+
if (!pkg.sideEffects) {
|
|
266
|
+
process.stderr.write(
|
|
267
|
+
`${errorIsNotExisted("sideEffects")}
|
|
268
|
+
This field helps bundlers make assumptions about packages that improve tree shaking, or pruning files that aren't used and don't have any global side effects.
|
|
269
|
+
`
|
|
270
|
+
);
|
|
271
|
+
isValid = false;
|
|
272
|
+
}
|
|
273
|
+
if (isValid) {
|
|
274
|
+
process.stdout.write(import_picocolors3.default.green(`Validation finished succesfully.
|
|
275
|
+
`));
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// src/index.ts
|
|
152
280
|
var { init, build, clean, inspect, start, validate } = {
|
|
153
281
|
init: async function init2(rootDir, config, options) {
|
|
154
|
-
await (0,
|
|
282
|
+
await (0, import_hammer_utils2.updatePackageJsonFromTemplate)(rootDir, options.force);
|
|
155
283
|
},
|
|
156
284
|
build: async function build2(rootDir, config, options) {
|
|
157
285
|
const pluginOptions = { ...options, config };
|
|
@@ -176,7 +304,9 @@ ${highlightedConfig}
|
|
|
176
304
|
const buildOptions = await createBuildOptions(base_configs_default, pluginOptions);
|
|
177
305
|
await performBuild(buildOptions);
|
|
178
306
|
},
|
|
179
|
-
validate: async function validate2() {
|
|
307
|
+
validate: async function validate2(rootDir, config) {
|
|
308
|
+
await this.build(rootDir, config, { analyze: false });
|
|
309
|
+
await validatePackage(rootDir);
|
|
180
310
|
}
|
|
181
311
|
};
|
|
182
312
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import process2 from "process";
|
|
2
|
+
import process2 from "node:process";
|
|
3
3
|
|
|
4
4
|
// src/base-configs.ts
|
|
5
5
|
import { postcssModules, sassPlugin } from "esbuild-sass-plugin";
|
|
@@ -68,7 +68,7 @@ function toHighlightedString(input) {
|
|
|
68
68
|
|
|
69
69
|
// src/clean-output.ts
|
|
70
70
|
import { resolve } from "pathe";
|
|
71
|
-
import { rm } from "fs/promises";
|
|
71
|
+
import { rm } from "node:fs/promises";
|
|
72
72
|
import c from "picocolors";
|
|
73
73
|
async function cleanOutput(rootDir, buildOptions) {
|
|
74
74
|
for (const buildOption of buildOptions) {
|
|
@@ -110,6 +110,134 @@ async function performBuild(buildOptions) {
|
|
|
110
110
|
|
|
111
111
|
// src/index.ts
|
|
112
112
|
import { updatePackageJsonFromTemplate } from "@procore/hammer-utils";
|
|
113
|
+
|
|
114
|
+
// src/validate-package.ts
|
|
115
|
+
import { readRootPackageJson } from "@procore/hammer-utils";
|
|
116
|
+
import c3 from "picocolors";
|
|
117
|
+
import fs from "node:fs";
|
|
118
|
+
var errorIsNotExisted = (name) => `The property ${c3.yellow(name)} doesn't exist in the package.json.`;
|
|
119
|
+
var errorFileIsNotExisted = (name) => `The file from ${c3.yellow(name)} property doesn't exist in the dist folder.
|
|
120
|
+
`;
|
|
121
|
+
var areDeepFileExist = (value) => {
|
|
122
|
+
let isValid = true;
|
|
123
|
+
if (Array.isArray(value) && isValid) {
|
|
124
|
+
isValid = value.every(fs.existsSync);
|
|
125
|
+
}
|
|
126
|
+
if (typeof value === "object" && !Array.isArray(value) && value !== null && isValid) {
|
|
127
|
+
isValid = Object.values(value).every(
|
|
128
|
+
(file) => areDeepFileExist(file)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (typeof value === "string" && isValid) {
|
|
132
|
+
isValid = fs.existsSync(value);
|
|
133
|
+
}
|
|
134
|
+
return isValid;
|
|
135
|
+
};
|
|
136
|
+
var validatePackage = async (rootDir) => {
|
|
137
|
+
const pkg = await readRootPackageJson(rootDir);
|
|
138
|
+
let isValid = true;
|
|
139
|
+
if (!pkg.type) {
|
|
140
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#type";
|
|
141
|
+
process.stderr.write(errorIsNotExisted("type"));
|
|
142
|
+
process.stdout.write(
|
|
143
|
+
`Please specify it as ${c3.bold('"module" (recommended)')} or read ${c3.bold(docUrl)}.
|
|
144
|
+
`
|
|
145
|
+
);
|
|
146
|
+
isValid = false;
|
|
147
|
+
}
|
|
148
|
+
if (!pkg.exports) {
|
|
149
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#exports";
|
|
150
|
+
const exampleStructure = {
|
|
151
|
+
exports: {
|
|
152
|
+
".": {
|
|
153
|
+
import: {
|
|
154
|
+
types: "./dist/modern/index.d.ts",
|
|
155
|
+
default: "./dist/modern/index.js"
|
|
156
|
+
},
|
|
157
|
+
require: {
|
|
158
|
+
types: "./dist/modern/index.d.cts",
|
|
159
|
+
default: "./dist/modern/index.cjs"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"./package.json": "./package.json"
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
process.stderr.write(errorIsNotExisted("exports"));
|
|
166
|
+
process.stdout.write(
|
|
167
|
+
`Please specify it as ${c3.bold(JSON.stringify(exampleStructure, null, 2))} or read ${c3.bold(docUrl)}.
|
|
168
|
+
`
|
|
169
|
+
);
|
|
170
|
+
isValid = false;
|
|
171
|
+
}
|
|
172
|
+
if (pkg.exports && !areDeepFileExist(pkg.exports)) {
|
|
173
|
+
process.stderr.write(errorFileIsNotExisted("exports"));
|
|
174
|
+
isValid = false;
|
|
175
|
+
}
|
|
176
|
+
if (!pkg.main) {
|
|
177
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#main";
|
|
178
|
+
process.stderr.write(errorIsNotExisted("main"));
|
|
179
|
+
process.stdout.write(
|
|
180
|
+
`Please point it on your main file. You can read more in ${c3.bold(docUrl)}.
|
|
181
|
+
`
|
|
182
|
+
);
|
|
183
|
+
isValid = false;
|
|
184
|
+
}
|
|
185
|
+
if (pkg.main && !fs.existsSync(pkg.main)) {
|
|
186
|
+
process.stderr.write(errorFileIsNotExisted("main"));
|
|
187
|
+
isValid = false;
|
|
188
|
+
}
|
|
189
|
+
if (!pkg.module) {
|
|
190
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#package-entry-points";
|
|
191
|
+
process.stderr.write(errorIsNotExisted("module"));
|
|
192
|
+
process.stdout.write(`You can read more in ${c3.bold(docUrl)}.
|
|
193
|
+
`);
|
|
194
|
+
isValid = false;
|
|
195
|
+
}
|
|
196
|
+
if (pkg.module && !fs.existsSync(pkg.module)) {
|
|
197
|
+
process.stderr.write(errorFileIsNotExisted("module"));
|
|
198
|
+
isValid = false;
|
|
199
|
+
}
|
|
200
|
+
if (!pkg.types) {
|
|
201
|
+
const docUrl = "https://nodejs.org/docs/latest/api/packages.html#community-conditions-definitions";
|
|
202
|
+
process.stderr.write(errorIsNotExisted("types"));
|
|
203
|
+
process.stdout.write(`You can read more in ${c3.bold(docUrl)}.
|
|
204
|
+
`);
|
|
205
|
+
isValid = false;
|
|
206
|
+
}
|
|
207
|
+
if (pkg.types && !areDeepFileExist(pkg.types)) {
|
|
208
|
+
process.stderr.write(errorFileIsNotExisted("types"));
|
|
209
|
+
isValid = false;
|
|
210
|
+
}
|
|
211
|
+
if (!pkg.files) {
|
|
212
|
+
const docUrl = "https://classic.yarnpkg.com/lang/en/docs/package-json/#toc-files";
|
|
213
|
+
process.stderr.write(
|
|
214
|
+
`${errorIsNotExisted("files")}
|
|
215
|
+
The optional ${c3.yellow("files")} field is an array of file patterns that describes the entries to be included when your package is installed as a dependency.
|
|
216
|
+
`
|
|
217
|
+
);
|
|
218
|
+
process.stdout.write(`You can read more in ${c3.bold(docUrl)}.
|
|
219
|
+
`);
|
|
220
|
+
isValid = false;
|
|
221
|
+
}
|
|
222
|
+
if (pkg.files && !areDeepFileExist(pkg.files)) {
|
|
223
|
+
process.stderr.write(errorFileIsNotExisted("files"));
|
|
224
|
+
isValid = false;
|
|
225
|
+
}
|
|
226
|
+
if (!pkg.sideEffects) {
|
|
227
|
+
process.stderr.write(
|
|
228
|
+
`${errorIsNotExisted("sideEffects")}
|
|
229
|
+
This field helps bundlers make assumptions about packages that improve tree shaking, or pruning files that aren't used and don't have any global side effects.
|
|
230
|
+
`
|
|
231
|
+
);
|
|
232
|
+
isValid = false;
|
|
233
|
+
}
|
|
234
|
+
if (isValid) {
|
|
235
|
+
process.stdout.write(c3.green(`Validation finished succesfully.
|
|
236
|
+
`));
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/index.ts
|
|
113
241
|
var { init, build, clean, inspect, start, validate } = {
|
|
114
242
|
init: async function init2(rootDir, config, options) {
|
|
115
243
|
await updatePackageJsonFromTemplate(rootDir, options.force);
|
|
@@ -137,7 +265,9 @@ ${highlightedConfig}
|
|
|
137
265
|
const buildOptions = await createBuildOptions(base_configs_default, pluginOptions);
|
|
138
266
|
await performBuild(buildOptions);
|
|
139
267
|
},
|
|
140
|
-
validate: async function validate2() {
|
|
268
|
+
validate: async function validate2(rootDir, config) {
|
|
269
|
+
await this.build(rootDir, config, { analyze: false });
|
|
270
|
+
await validatePackage(rootDir);
|
|
141
271
|
}
|
|
142
272
|
};
|
|
143
273
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@procore/hammer-lib-tsup",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Hammer library builder, tsup-style",
|
|
5
5
|
"author": "Procore Technologies, Inc",
|
|
6
6
|
"homepage": "https://github.com/procore/hammer/packages/lib-tsup",
|
|
@@ -51,29 +51,29 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@procore/hammer-types": "^0.5.
|
|
55
|
-
"@procore/hammer-utils": "^0.
|
|
54
|
+
"@procore/hammer-types": "^0.5.1",
|
|
55
|
+
"@procore/hammer-utils": "^0.2.0",
|
|
56
56
|
"cli-highlight": "^2.1.11",
|
|
57
|
-
"esbuild": "^0.
|
|
57
|
+
"esbuild": "^0.24.0",
|
|
58
58
|
"esbuild-sass-plugin": "^3.3.1",
|
|
59
59
|
"pathe": "^1.1.2",
|
|
60
|
-
"picocolors": "^1.
|
|
61
|
-
"sass-embedded": "^1.
|
|
62
|
-
"tsup": "^8.3.
|
|
60
|
+
"picocolors": "^1.1.1",
|
|
61
|
+
"sass-embedded": "^1.81.0",
|
|
62
|
+
"tsup": "^8.3.5"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@procore/eslint-config": "^15.2.1",
|
|
66
66
|
"@procore/hammer-test-utils": "^0.0.1",
|
|
67
67
|
"@procore/prettier-config": "^1.1.1",
|
|
68
68
|
"@procore/typescript-config": "^2.0.0",
|
|
69
|
-
"@types/node": "^20.
|
|
70
|
-
"@vitest/coverage-v8": "^2.1.
|
|
71
|
-
"del-cli": "^
|
|
72
|
-
"eslint": "^
|
|
73
|
-
"postcss": "^8.4.
|
|
69
|
+
"@types/node": "^20.17.6",
|
|
70
|
+
"@vitest/coverage-v8": "^2.1.5",
|
|
71
|
+
"del-cli": "^6.0.0",
|
|
72
|
+
"eslint": "^9.15.0",
|
|
73
|
+
"postcss": "^8.4.49",
|
|
74
74
|
"prettier": "^3.3.3",
|
|
75
75
|
"strip-ansi": "^7.1.0",
|
|
76
|
-
"typescript": "
|
|
77
|
-
"vitest": "^2.1.
|
|
76
|
+
"typescript": "~5.5.4",
|
|
77
|
+
"vitest": "^2.1.5"
|
|
78
78
|
}
|
|
79
79
|
}
|