ts-builds 2.6.1 ā 2.6.2
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/README.md +1 -1
- package/dist/cli.js +637 -109
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ Create `ts-builds.config.json` to customize behavior:
|
|
|
107
107
|
|
|
108
108
|
### With Custom ESLint Plugins
|
|
109
109
|
|
|
110
|
-
If your project uses ESLint plugins not bundled with ts-builds (e.g., `eslint-plugin-
|
|
110
|
+
If your project uses ESLint plugins not bundled with ts-builds (e.g., `eslint-plugin-react-hooks`):
|
|
111
111
|
|
|
112
112
|
```json
|
|
113
113
|
{
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
2
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { $, Do, List, Option } from "functype";
|
|
6
|
+
import { Fs, Process } from "functype-os";
|
|
5
7
|
import { createInterface } from "node:readline";
|
|
8
|
+
import { gzipSync } from "node:zlib";
|
|
6
9
|
|
|
7
|
-
//#region src/cli.ts
|
|
10
|
+
//#region src/cli/config.ts
|
|
8
11
|
const targetDir = process.cwd();
|
|
9
12
|
const defaultChains = { validate: [
|
|
10
13
|
"format",
|
|
@@ -31,10 +34,15 @@ function loadConfig() {
|
|
|
31
34
|
testDir: userConfig.testDir ?? "./test",
|
|
32
35
|
buildMode: userConfig.buildMode ?? "tsdown",
|
|
33
36
|
lint: { useProjectEslint: userConfig.lint?.useProjectEslint ?? false },
|
|
37
|
+
size: userConfig.size ?? {},
|
|
38
|
+
changelog: userConfig.changelog ?? {},
|
|
34
39
|
commands,
|
|
35
40
|
chains
|
|
36
41
|
};
|
|
37
42
|
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/cli/runner.ts
|
|
38
46
|
function runCommand(command, args, options = {}) {
|
|
39
47
|
const cwd = options.cwd ? join(targetDir, options.cwd) : targetDir;
|
|
40
48
|
return new Promise((resolve) => {
|
|
@@ -69,6 +77,264 @@ function runShellCommand(shellCmd, options = {}) {
|
|
|
69
77
|
});
|
|
70
78
|
});
|
|
71
79
|
}
|
|
80
|
+
function getBuiltinCommands(config) {
|
|
81
|
+
const eslintCmd = config.lint.useProjectEslint ? "npx eslint" : "eslint";
|
|
82
|
+
const buildCmd = config.buildMode === "vite" ? { run: "rimraf dist && vite build" } : { run: "rimraf dist && cross-env NODE_ENV=production tsdown" };
|
|
83
|
+
const buildWatchCmd = config.buildMode === "vite" ? { run: "vite build --watch" } : { run: "tsdown --watch" };
|
|
84
|
+
const devCmd = config.buildMode === "vite" ? { run: "vite" } : { run: "tsdown --watch" };
|
|
85
|
+
return {
|
|
86
|
+
format: { run: "prettier --write ." },
|
|
87
|
+
"format:check": { run: "prettier --check ." },
|
|
88
|
+
lint: { run: `${eslintCmd} --fix ${config.srcDir}` },
|
|
89
|
+
"lint:check": { run: `${eslintCmd} ${config.srcDir}` },
|
|
90
|
+
typecheck: { run: "tsc --noEmit" },
|
|
91
|
+
"ts-types": { run: "tsc --noEmit" },
|
|
92
|
+
test: { run: "vitest run" },
|
|
93
|
+
"test:watch": { run: "vitest" },
|
|
94
|
+
"test:coverage": { run: "vitest run --coverage" },
|
|
95
|
+
"test:ui": { run: "vitest --ui" },
|
|
96
|
+
build: buildCmd,
|
|
97
|
+
"build:watch": buildWatchCmd,
|
|
98
|
+
dev: devCmd,
|
|
99
|
+
preview: { run: "vite preview" },
|
|
100
|
+
compile: { run: "tsc" }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
async function runChain(chainName, config, visited = /* @__PURE__ */ new Set()) {
|
|
104
|
+
if (visited.has(chainName)) {
|
|
105
|
+
console.error(`Circular chain reference detected: ${chainName}`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
visited.add(chainName);
|
|
109
|
+
const chain = config.chains[chainName];
|
|
110
|
+
if (!chain) {
|
|
111
|
+
console.error(`Unknown chain: ${chainName}`);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
const builtins = getBuiltinCommands(config);
|
|
115
|
+
console.log(`\nš Running chain: ${chainName} [${chain.join(" ā ")}]`);
|
|
116
|
+
for (const step of chain) {
|
|
117
|
+
if (config.chains[step]) {
|
|
118
|
+
const code = await runChain(step, config, visited);
|
|
119
|
+
if (code !== 0) return code;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const cmdDef = config.commands[step] ?? builtins[step];
|
|
123
|
+
if (!cmdDef) {
|
|
124
|
+
console.error(`Unknown command or chain: ${step}`);
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
const cwdLabel = cmdDef.cwd ? ` (in ${cmdDef.cwd})` : "";
|
|
128
|
+
console.log(`\nā¶ Running ${step}...${cwdLabel}`);
|
|
129
|
+
const code = await runShellCommand(cmdDef.run, { cwd: cmdDef.cwd });
|
|
130
|
+
if (code !== 0) {
|
|
131
|
+
console.error(`\nā ${step} failed with exit code ${code}`);
|
|
132
|
+
return code;
|
|
133
|
+
}
|
|
134
|
+
console.log(`ā ${step} complete`);
|
|
135
|
+
}
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/cli/commands/build.ts
|
|
141
|
+
async function runFormat(check = false) {
|
|
142
|
+
return runCommand("prettier", check ? ["--check", "."] : ["--write", "."]);
|
|
143
|
+
}
|
|
144
|
+
async function runLint(check = false) {
|
|
145
|
+
const config = loadConfig();
|
|
146
|
+
return runCommand(config.lint.useProjectEslint ? "npx eslint" : "eslint", check ? [config.srcDir] : ["--fix", config.srcDir]);
|
|
147
|
+
}
|
|
148
|
+
async function runTypecheck() {
|
|
149
|
+
return runCommand("tsc", ["--noEmit"]);
|
|
150
|
+
}
|
|
151
|
+
async function runTest(mode = "run") {
|
|
152
|
+
switch (mode) {
|
|
153
|
+
case "watch": return runCommand("vitest", []);
|
|
154
|
+
case "coverage": return runCommand("vitest", ["run", "--coverage"]);
|
|
155
|
+
case "ui": return runCommand("vitest", ["--ui"]);
|
|
156
|
+
default: return runCommand("vitest", ["run"]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function runBuild(watch = false) {
|
|
160
|
+
if (loadConfig().buildMode === "vite") {
|
|
161
|
+
if (watch) return runCommand("vite", ["build", "--watch"]);
|
|
162
|
+
const cleanCode = await runCommand("rimraf", ["dist"]);
|
|
163
|
+
if (cleanCode !== 0) return cleanCode;
|
|
164
|
+
return runCommand("vite", ["build"]);
|
|
165
|
+
}
|
|
166
|
+
if (watch) return runCommand("tsdown", ["--watch"]);
|
|
167
|
+
const cleanCode = await runCommand("rimraf", ["dist"]);
|
|
168
|
+
if (cleanCode !== 0) return cleanCode;
|
|
169
|
+
return runCommand("cross-env", ["NODE_ENV=production", "tsdown"]);
|
|
170
|
+
}
|
|
171
|
+
async function runDev() {
|
|
172
|
+
return loadConfig().buildMode === "vite" ? runCommand("vite", []) : runCommand("tsdown", ["--watch"]);
|
|
173
|
+
}
|
|
174
|
+
async function runValidate(chainName = "validate") {
|
|
175
|
+
return runChain(chainName, loadConfig());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/cli/commands/changelog.ts
|
|
180
|
+
const defaultTypeMap = {
|
|
181
|
+
feat: "Features",
|
|
182
|
+
fix: "Bug Fixes",
|
|
183
|
+
perf: "Performance",
|
|
184
|
+
refactor: "Refactoring",
|
|
185
|
+
docs: "Documentation",
|
|
186
|
+
test: "Tests",
|
|
187
|
+
ci: "CI/CD",
|
|
188
|
+
build: "Build",
|
|
189
|
+
style: "Style",
|
|
190
|
+
chore: "Chores"
|
|
191
|
+
};
|
|
192
|
+
const defaultExclude = List(["chore"]);
|
|
193
|
+
function execGit(args) {
|
|
194
|
+
return Process.execSync(`git ${args}`, { cwd: targetDir }).fold(() => Option.none(), (result) => {
|
|
195
|
+
const trimmed = result.stdout.trim();
|
|
196
|
+
return trimmed ? Option(trimmed) : Option.none();
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function getLastTag() {
|
|
200
|
+
return execGit("describe --tags --abbrev=0");
|
|
201
|
+
}
|
|
202
|
+
function getCommitsSince(since) {
|
|
203
|
+
const delimiter = "---COMMIT---";
|
|
204
|
+
const fieldSep = "|||";
|
|
205
|
+
const format = `${delimiter}%H${fieldSep}%s${fieldSep}%b${fieldSep}%an${fieldSep}%aI`;
|
|
206
|
+
return execGit(`log ${since.fold(() => "HEAD", (tag) => `${tag}..HEAD`)} --format="${format}"`).fold(() => List.empty(), (output) => List(output.split(delimiter)).filter((s) => s.trim().length > 0).map((entry) => {
|
|
207
|
+
const parts = entry.split(fieldSep);
|
|
208
|
+
return {
|
|
209
|
+
hash: (parts[0] ?? "").trim(),
|
|
210
|
+
subject: (parts[1] ?? "").trim(),
|
|
211
|
+
body: (parts[2] ?? "").trim(),
|
|
212
|
+
author: (parts[3] ?? "").trim(),
|
|
213
|
+
date: (parts[4] ?? "").trim()
|
|
214
|
+
};
|
|
215
|
+
}).filter((c) => c.hash.length > 0 && c.subject.length > 0));
|
|
216
|
+
}
|
|
217
|
+
function parseConventionalCommit(subject) {
|
|
218
|
+
const match = subject.match(/^(\w+)(?:\(([^)]+)\))?(!)?:\s+(.+)$/);
|
|
219
|
+
if (!match) return null;
|
|
220
|
+
return {
|
|
221
|
+
type: match[1],
|
|
222
|
+
scope: Option(match[2]),
|
|
223
|
+
breaking: match[3] === "!",
|
|
224
|
+
description: match[4]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function extractIssueRefs(text) {
|
|
228
|
+
const matches = text.match(/#(\d+)/g);
|
|
229
|
+
return matches ? List(matches.map((m) => m.slice(1))) : List.empty();
|
|
230
|
+
}
|
|
231
|
+
function getRepoUrl() {
|
|
232
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
233
|
+
return Fs.readFileSync(packageJsonPath).toOption().flatMap((content) => {
|
|
234
|
+
const pkg = JSON.parse(content);
|
|
235
|
+
return Option(typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url);
|
|
236
|
+
}).map((url) => url.replace(/^git\+/, "").replace(/\.git$/, "").replace(/^git:\/\//, "https://"));
|
|
237
|
+
}
|
|
238
|
+
function groupCommits(commits, typeMap, exclude) {
|
|
239
|
+
const filtered = commits.filter((c) => !exclude.contains(c.type));
|
|
240
|
+
const breaking = filtered.filter((c) => c.breaking);
|
|
241
|
+
const byType = filtered.filter((c) => typeMap[c.type] !== void 0).groupBy((c) => typeMap[c.type]);
|
|
242
|
+
return {
|
|
243
|
+
breaking,
|
|
244
|
+
sections: List([...new Set(Object.values(typeMap))]).filter((title) => byType.has(title)).map((title) => ({
|
|
245
|
+
title,
|
|
246
|
+
commits: byType.get(title) ?? List.empty()
|
|
247
|
+
}))
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function formatCommitLine(commit, repoUrl) {
|
|
251
|
+
const shortHash = commit.hash.slice(0, 7);
|
|
252
|
+
const scope = commit.scope.fold(() => "", (s) => `**${s}**: `);
|
|
253
|
+
const hashLink = repoUrl.fold(() => `(${shortHash})`, (url) => `([${shortHash}](${url}/commit/${commit.hash}))`);
|
|
254
|
+
const issueLinks = commit.issues.map((num) => repoUrl.fold(() => `#${num}`, (url) => `[#${num}](${url}/issues/${num})`)).toArray().join(", ");
|
|
255
|
+
const issueRef = issueLinks ? ` ${issueLinks}` : "";
|
|
256
|
+
return `- ${scope}${commit.description} ${hashLink}${issueRef}`;
|
|
257
|
+
}
|
|
258
|
+
function formatMarkdown(grouped, repoUrl, version) {
|
|
259
|
+
const lines = [];
|
|
260
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
261
|
+
lines.push(version.fold(() => `## Unreleased (${date})`, (v) => `## ${v} (${date})`));
|
|
262
|
+
lines.push("");
|
|
263
|
+
if (grouped.breaking.nonEmpty) {
|
|
264
|
+
lines.push("### BREAKING CHANGES");
|
|
265
|
+
lines.push("");
|
|
266
|
+
for (const commit of grouped.breaking) lines.push(formatCommitLine(commit, repoUrl));
|
|
267
|
+
lines.push("");
|
|
268
|
+
}
|
|
269
|
+
for (const section of grouped.sections) {
|
|
270
|
+
lines.push(`### ${section.title}`);
|
|
271
|
+
lines.push("");
|
|
272
|
+
for (const commit of section.commits) lines.push(formatCommitLine(commit, repoUrl));
|
|
273
|
+
lines.push("");
|
|
274
|
+
}
|
|
275
|
+
return lines.join("\n");
|
|
276
|
+
}
|
|
277
|
+
async function runChangelog(args) {
|
|
278
|
+
const changelogConfig = loadConfig().changelog;
|
|
279
|
+
const typeMap = {
|
|
280
|
+
...defaultTypeMap,
|
|
281
|
+
...changelogConfig.types
|
|
282
|
+
};
|
|
283
|
+
const exclude = changelogConfig.exclude ? List(changelogConfig.exclude) : defaultExclude;
|
|
284
|
+
let since = Option.none();
|
|
285
|
+
let output = Option.none();
|
|
286
|
+
let version = Option.none();
|
|
287
|
+
let sinceExplicit = false;
|
|
288
|
+
for (let i = 0; i < args.length; i++) switch (args[i]) {
|
|
289
|
+
case "--since":
|
|
290
|
+
since = Option(args[++i]);
|
|
291
|
+
sinceExplicit = true;
|
|
292
|
+
break;
|
|
293
|
+
case "--output":
|
|
294
|
+
output = Option(args[++i]);
|
|
295
|
+
break;
|
|
296
|
+
case "--version":
|
|
297
|
+
version = Option(args[++i]);
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
if (!sinceExplicit) since = getLastTag();
|
|
301
|
+
const rawCommits = getCommitsSince(since);
|
|
302
|
+
if (rawCommits.isEmpty) {
|
|
303
|
+
console.log(since.fold(() => "No commits found", (tag) => `No commits found since ${tag}`));
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
const repoUrl = getRepoUrl();
|
|
307
|
+
const parsed = rawCommits.map((raw) => {
|
|
308
|
+
const conv = parseConventionalCommit(raw.subject);
|
|
309
|
+
if (!conv) return null;
|
|
310
|
+
const bodyBreaking = raw.body.includes("BREAKING CHANGE");
|
|
311
|
+
const issues = extractIssueRefs(raw.subject).concat(extractIssueRefs(raw.body)).distinct();
|
|
312
|
+
return {
|
|
313
|
+
hash: raw.hash,
|
|
314
|
+
type: conv.type,
|
|
315
|
+
scope: conv.scope,
|
|
316
|
+
breaking: conv.breaking || bodyBreaking,
|
|
317
|
+
description: conv.description,
|
|
318
|
+
author: raw.author,
|
|
319
|
+
date: raw.date,
|
|
320
|
+
issues
|
|
321
|
+
};
|
|
322
|
+
}).filter((c) => c !== null);
|
|
323
|
+
if (parsed.isEmpty) {
|
|
324
|
+
console.log("No conventional commits found");
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
const markdown = formatMarkdown(groupCommits(parsed, typeMap, exclude), repoUrl, version);
|
|
328
|
+
output.fold(() => console.log(markdown), (file) => {
|
|
329
|
+
const outputPath = join(targetDir, file);
|
|
330
|
+
Fs.writeFileSync(outputPath, markdown);
|
|
331
|
+
console.log(`Changelog written to ${file}`);
|
|
332
|
+
});
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region src/cli/commands/info.ts
|
|
72
338
|
const bundledPackages = [
|
|
73
339
|
"@eslint/js",
|
|
74
340
|
"@vitest/coverage-v8",
|
|
@@ -85,30 +351,6 @@ const bundledPackages = [
|
|
|
85
351
|
"typescript-eslint",
|
|
86
352
|
"vitest"
|
|
87
353
|
];
|
|
88
|
-
const requiredHoistPatterns = [
|
|
89
|
-
"public-hoist-pattern[]=*eslint*",
|
|
90
|
-
"public-hoist-pattern[]=*prettier*",
|
|
91
|
-
"public-hoist-pattern[]=*vitest*",
|
|
92
|
-
"public-hoist-pattern[]=typescript",
|
|
93
|
-
"public-hoist-pattern[]=*rimraf*",
|
|
94
|
-
"public-hoist-pattern[]=*cross-env*"
|
|
95
|
-
];
|
|
96
|
-
function ensureNpmrcHoistPatterns() {
|
|
97
|
-
const npmrcPath = join(targetDir, ".npmrc");
|
|
98
|
-
const existingContent = existsSync(npmrcPath) ? readFileSync(npmrcPath, "utf-8") : "";
|
|
99
|
-
const missingPatterns = requiredHoistPatterns.filter((pattern) => !existingContent.includes(pattern));
|
|
100
|
-
if (missingPatterns.length === 0) return;
|
|
101
|
-
const header = "# Hoist CLI tool binaries from peer dependencies";
|
|
102
|
-
const hasHeader = existingContent.includes(header);
|
|
103
|
-
let newContent = existingContent;
|
|
104
|
-
if (!hasHeader && missingPatterns.length > 0) newContent = existingContent + (existingContent.length > 0 && !existingContent.endsWith("\n") ? "\n\n" : existingContent.length > 0 ? "\n" : "") + header + "\n";
|
|
105
|
-
for (const pattern of missingPatterns) {
|
|
106
|
-
if (!newContent.endsWith("\n") && newContent.length > 0) newContent += "\n";
|
|
107
|
-
newContent += pattern + "\n";
|
|
108
|
-
}
|
|
109
|
-
writeFileSync(npmrcPath, newContent);
|
|
110
|
-
console.log(`ā Updated .npmrc with ${missingPatterns.length} missing hoist pattern(s)`);
|
|
111
|
-
}
|
|
112
354
|
function showHelp() {
|
|
113
355
|
console.log(`
|
|
114
356
|
ts-builds - Shared TypeScript build tooling
|
|
@@ -139,6 +381,11 @@ SCRIPT COMMANDS:
|
|
|
139
381
|
dev Development mode (tsdown --watch or vite dev server)
|
|
140
382
|
preview Preview production build (vite preview)
|
|
141
383
|
|
|
384
|
+
ANALYSIS COMMANDS:
|
|
385
|
+
size Report bundle sizes (use --save to update baseline)
|
|
386
|
+
doctor Check package health (exports, files, types)
|
|
387
|
+
changelog Generate changelog from conventional commits
|
|
388
|
+
|
|
142
389
|
CONFIGURATION:
|
|
143
390
|
Create ts-builds.config.json in your project root:
|
|
144
391
|
|
|
@@ -154,7 +401,7 @@ CONFIGURATION:
|
|
|
154
401
|
"buildMode": "vite"
|
|
155
402
|
}
|
|
156
403
|
|
|
157
|
-
With custom ESLint plugins (e.g., eslint-plugin-
|
|
404
|
+
With custom ESLint plugins (e.g., eslint-plugin-react-hooks):
|
|
158
405
|
{
|
|
159
406
|
"srcDir": "./src",
|
|
160
407
|
"lint": {
|
|
@@ -223,6 +470,9 @@ Example minimal package.json for SPAs/React apps:
|
|
|
223
470
|
}
|
|
224
471
|
`);
|
|
225
472
|
}
|
|
473
|
+
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/cli/commands/cleanup.ts
|
|
226
476
|
async function cleanup() {
|
|
227
477
|
const packageJsonPath = join(targetDir, "package.json");
|
|
228
478
|
if (!existsSync(packageJsonPath)) {
|
|
@@ -272,6 +522,247 @@ async function cleanup() {
|
|
|
272
522
|
console.log(`\nā Removed ${totalRemoved} redundant package(s) from package.json`);
|
|
273
523
|
console.log("\nRun 'pnpm install' to update your lockfile.");
|
|
274
524
|
}
|
|
525
|
+
|
|
526
|
+
//#endregion
|
|
527
|
+
//#region src/cli/commands/doctor.ts
|
|
528
|
+
function resolveExportValue(value) {
|
|
529
|
+
if (typeof value === "string") return List([value]);
|
|
530
|
+
if (typeof value === "object" && value !== null) return List(Object.values(value)).flatMap((v) => resolveExportValue(v));
|
|
531
|
+
return List.empty();
|
|
532
|
+
}
|
|
533
|
+
function checkExports(pkg) {
|
|
534
|
+
return Option(pkg.exports).fold(() => List([{
|
|
535
|
+
severity: "info",
|
|
536
|
+
message: "No exports field defined"
|
|
537
|
+
}]), (exports) => List(Object.entries(exports)).flatMap(([key, value]) => resolveExportValue(value).map((p) => {
|
|
538
|
+
const absPath = join(targetDir, p);
|
|
539
|
+
return Fs.existsSync(absPath) ? {
|
|
540
|
+
severity: "info",
|
|
541
|
+
message: `${key} -> ${p}`
|
|
542
|
+
} : {
|
|
543
|
+
severity: "error",
|
|
544
|
+
message: `${key} -> ${p} -- file not found`
|
|
545
|
+
};
|
|
546
|
+
})));
|
|
547
|
+
}
|
|
548
|
+
function checkFiles(pkg) {
|
|
549
|
+
return Option(pkg.files).fold(() => List([{
|
|
550
|
+
severity: "warning",
|
|
551
|
+
message: "No files field defined -- npm will publish everything"
|
|
552
|
+
}]), (files) => List(files).map((entry) => {
|
|
553
|
+
const absPath = join(targetDir, entry);
|
|
554
|
+
if (!Fs.existsSync(absPath)) return {
|
|
555
|
+
severity: "warning",
|
|
556
|
+
message: `${entry} -- not found`
|
|
557
|
+
};
|
|
558
|
+
return Fs.statSync(absPath).fold(() => ({
|
|
559
|
+
severity: "warning",
|
|
560
|
+
message: `${entry} -- cannot stat`
|
|
561
|
+
}), (info) => ({
|
|
562
|
+
severity: "info",
|
|
563
|
+
message: `${entry}${info.isDirectory ? "/" : ""}`
|
|
564
|
+
}));
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
567
|
+
function checkEntryPoints(pkg) {
|
|
568
|
+
const fieldResults = List([
|
|
569
|
+
{
|
|
570
|
+
name: "main",
|
|
571
|
+
value: Option(pkg.main)
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: "module",
|
|
575
|
+
value: Option(pkg.module)
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
name: "types",
|
|
579
|
+
value: Option(pkg.types)
|
|
580
|
+
}
|
|
581
|
+
]).flatMap(({ name, value }) => value.fold(() => List.empty(), (v) => {
|
|
582
|
+
const absPath = join(targetDir, v);
|
|
583
|
+
return List([Fs.existsSync(absPath) ? {
|
|
584
|
+
severity: "info",
|
|
585
|
+
message: `${name} -> ${v}`
|
|
586
|
+
} : {
|
|
587
|
+
severity: "error",
|
|
588
|
+
message: `${name} -> ${v} -- file not found`
|
|
589
|
+
}]);
|
|
590
|
+
}));
|
|
591
|
+
const binResults = Option(pkg.bin).fold(() => List.empty(), (bin) => {
|
|
592
|
+
const bins = typeof bin === "string" ? { [pkg.name ?? "cli"]: bin } : bin;
|
|
593
|
+
return List(Object.entries(bins)).map(([name, path]) => {
|
|
594
|
+
const absPath = join(targetDir, path);
|
|
595
|
+
return Fs.existsSync(absPath) ? {
|
|
596
|
+
severity: "info",
|
|
597
|
+
message: `bin.${name} -> ${path}`
|
|
598
|
+
} : {
|
|
599
|
+
severity: "error",
|
|
600
|
+
message: `bin.${name} -> ${path} -- file not found`
|
|
601
|
+
};
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
return fieldResults.concat(binResults);
|
|
605
|
+
}
|
|
606
|
+
function checkDeclarations(distDir) {
|
|
607
|
+
const absDir = join(targetDir, distDir);
|
|
608
|
+
if (!Fs.existsSync(absDir)) return List([{
|
|
609
|
+
severity: "warning",
|
|
610
|
+
message: `${distDir}/ directory not found`
|
|
611
|
+
}]);
|
|
612
|
+
return Fs.readdirSync(absDir).fold(() => List([{
|
|
613
|
+
severity: "warning",
|
|
614
|
+
message: `Cannot read ${distDir}/`
|
|
615
|
+
}]), (entries) => {
|
|
616
|
+
const allFiles = entries.flatMap((entry) => {
|
|
617
|
+
const fullPath = join(absDir, entry);
|
|
618
|
+
return Fs.statSync(fullPath).fold(() => List.empty(), (info) => info.isFile ? List([{
|
|
619
|
+
name: entry,
|
|
620
|
+
relPath: `${distDir}/${entry}`
|
|
621
|
+
}]) : List.empty());
|
|
622
|
+
});
|
|
623
|
+
const dtsFiles = allFiles.filter((f) => f.name.endsWith(".d.ts") || f.name.endsWith(".d.mts") || f.name.endsWith(".d.cts")).map((f) => f.relPath.replace(/\.d\.(m?ts|cts)$/, ""));
|
|
624
|
+
const jsFiles = allFiles.filter((f) => f.name.endsWith(".js") || f.name.endsWith(".mjs") || f.name.endsWith(".cjs"));
|
|
625
|
+
const warnings = jsFiles.flatMap((jsFile) => {
|
|
626
|
+
const base = jsFile.relPath.replace(/\.(m?js|cjs)$/, "");
|
|
627
|
+
return dtsFiles.contains(base) ? List.empty() : List([{
|
|
628
|
+
severity: "warning",
|
|
629
|
+
message: `${jsFile.relPath} has no matching .d.ts`
|
|
630
|
+
}]);
|
|
631
|
+
});
|
|
632
|
+
return warnings.isEmpty ? List([{
|
|
633
|
+
severity: "info",
|
|
634
|
+
message: `All ${jsFiles.size} JS files have declarations`
|
|
635
|
+
}]) : warnings;
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
function checkRequiredFields(pkg) {
|
|
639
|
+
return List([
|
|
640
|
+
{
|
|
641
|
+
field: "name",
|
|
642
|
+
severity: "error"
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
field: "version",
|
|
646
|
+
severity: "error"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
field: "license",
|
|
650
|
+
severity: "warning"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
field: "description",
|
|
654
|
+
severity: "warning"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
field: "repository",
|
|
658
|
+
severity: "warning"
|
|
659
|
+
}
|
|
660
|
+
]).map(({ field, severity }) => Option(pkg[field]).fold(() => ({
|
|
661
|
+
severity,
|
|
662
|
+
message: `${field} is missing`
|
|
663
|
+
}), (value) => ({
|
|
664
|
+
severity: "info",
|
|
665
|
+
message: `${field}: ${typeof value === "string" ? value : "defined"}`
|
|
666
|
+
})));
|
|
667
|
+
}
|
|
668
|
+
function checkPeerDeps(pkg) {
|
|
669
|
+
return Option(pkg.peerDependencies).fold(() => List.empty(), (deps) => List(Object.entries(deps)).map(([name, range]) => {
|
|
670
|
+
return range.startsWith("^") || range.startsWith(">=") || range.startsWith("~") || range.includes("||") ? {
|
|
671
|
+
severity: "info",
|
|
672
|
+
message: `${name}: "${range}"`
|
|
673
|
+
} : {
|
|
674
|
+
severity: "warning",
|
|
675
|
+
message: `${name}: "${range}" -- consider using a range (^, ~, >=)`
|
|
676
|
+
};
|
|
677
|
+
}));
|
|
678
|
+
}
|
|
679
|
+
async function runDoctor() {
|
|
680
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
681
|
+
return Fs.readFileSync(packageJsonPath).fold(() => {
|
|
682
|
+
console.error("No package.json found in current directory");
|
|
683
|
+
return 1;
|
|
684
|
+
}, (content) => {
|
|
685
|
+
const pkg = JSON.parse(content);
|
|
686
|
+
console.log("\nts-builds doctor\n");
|
|
687
|
+
const sections = List([
|
|
688
|
+
{
|
|
689
|
+
name: "Required fields",
|
|
690
|
+
results: checkRequiredFields(pkg)
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
name: "Entry points",
|
|
694
|
+
results: checkEntryPoints(pkg)
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
name: "Exports",
|
|
698
|
+
results: checkExports(pkg)
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
name: "Files",
|
|
702
|
+
results: checkFiles(pkg)
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: "Declarations",
|
|
706
|
+
results: checkDeclarations("dist")
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: "Peer dependencies",
|
|
710
|
+
results: checkPeerDeps(pkg)
|
|
711
|
+
}
|
|
712
|
+
]);
|
|
713
|
+
let errors = 0;
|
|
714
|
+
let warnings = 0;
|
|
715
|
+
let passed = 0;
|
|
716
|
+
for (const section of sections) {
|
|
717
|
+
if (section.results.isEmpty) continue;
|
|
718
|
+
console.log(`Checking ${section.name}...`);
|
|
719
|
+
for (const result of section.results) switch (result.severity) {
|
|
720
|
+
case "error":
|
|
721
|
+
console.log(` x ${result.message}`);
|
|
722
|
+
errors++;
|
|
723
|
+
break;
|
|
724
|
+
case "warning":
|
|
725
|
+
console.log(` ! ${result.message}`);
|
|
726
|
+
warnings++;
|
|
727
|
+
break;
|
|
728
|
+
case "info":
|
|
729
|
+
console.log(` + ${result.message}`);
|
|
730
|
+
passed++;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
console.log();
|
|
734
|
+
}
|
|
735
|
+
console.log(`Summary: ${errors} error(s), ${warnings} warning(s), ${passed} passed`);
|
|
736
|
+
return errors > 0 ? 1 : 0;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/cli/commands/init.ts
|
|
742
|
+
const requiredHoistPatterns = [
|
|
743
|
+
"public-hoist-pattern[]=*eslint*",
|
|
744
|
+
"public-hoist-pattern[]=*prettier*",
|
|
745
|
+
"public-hoist-pattern[]=*vitest*",
|
|
746
|
+
"public-hoist-pattern[]=typescript",
|
|
747
|
+
"public-hoist-pattern[]=*rimraf*",
|
|
748
|
+
"public-hoist-pattern[]=*cross-env*"
|
|
749
|
+
];
|
|
750
|
+
function ensureNpmrcHoistPatterns() {
|
|
751
|
+
const npmrcPath = join(targetDir, ".npmrc");
|
|
752
|
+
const existingContent = existsSync(npmrcPath) ? readFileSync(npmrcPath, "utf-8") : "";
|
|
753
|
+
const missingPatterns = requiredHoistPatterns.filter((pattern) => !existingContent.includes(pattern));
|
|
754
|
+
if (missingPatterns.length === 0) return;
|
|
755
|
+
const header = "# Hoist CLI tool binaries from peer dependencies";
|
|
756
|
+
const hasHeader = existingContent.includes(header);
|
|
757
|
+
let newContent = existingContent;
|
|
758
|
+
if (!hasHeader && missingPatterns.length > 0) newContent = existingContent + (existingContent.length > 0 && !existingContent.endsWith("\n") ? "\n\n" : existingContent.length > 0 ? "\n" : "") + header + "\n";
|
|
759
|
+
for (const pattern of missingPatterns) {
|
|
760
|
+
if (!newContent.endsWith("\n") && newContent.length > 0) newContent += "\n";
|
|
761
|
+
newContent += pattern + "\n";
|
|
762
|
+
}
|
|
763
|
+
writeFileSync(npmrcPath, newContent);
|
|
764
|
+
console.log(`ā Updated .npmrc with ${missingPatterns.length} missing hoist pattern(s)`);
|
|
765
|
+
}
|
|
275
766
|
function init() {
|
|
276
767
|
console.log("Initializing ts-builds...");
|
|
277
768
|
ensureNpmrcHoistPatterns();
|
|
@@ -331,100 +822,128 @@ Example with custom commands:
|
|
|
331
822
|
}
|
|
332
823
|
`);
|
|
333
824
|
}
|
|
334
|
-
|
|
335
|
-
|
|
825
|
+
|
|
826
|
+
//#endregion
|
|
827
|
+
//#region src/cli/commands/size.ts
|
|
828
|
+
function measureEntry(dir, entry, absDir) {
|
|
829
|
+
const fullPath = join(absDir, entry);
|
|
830
|
+
return Do(function* () {
|
|
831
|
+
const info = yield* $(Fs.statSync(fullPath));
|
|
832
|
+
if (info.isDirectory) return getFileSizes(join(dir, entry));
|
|
833
|
+
const content = yield* $(Fs.readFileSync(fullPath));
|
|
834
|
+
const buf = Buffer.from(content);
|
|
835
|
+
return List([{
|
|
836
|
+
path: relative(targetDir, fullPath),
|
|
837
|
+
raw: info.size,
|
|
838
|
+
gzip: gzipSync(buf).length
|
|
839
|
+
}]);
|
|
840
|
+
}).fold(() => List.empty(), (list) => list);
|
|
336
841
|
}
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
842
|
+
function getFileSizes(dir) {
|
|
843
|
+
const absDir = join(targetDir, dir);
|
|
844
|
+
if (!Fs.existsSync(absDir)) return List.empty();
|
|
845
|
+
return Fs.readdirSync(absDir).fold(() => List.empty(), (entries) => entries.flatMap((entry) => measureEntry(dir, entry, absDir)));
|
|
340
846
|
}
|
|
341
|
-
|
|
342
|
-
|
|
847
|
+
function loadBaseline(baselinePath) {
|
|
848
|
+
const absPath = join(targetDir, baselinePath);
|
|
849
|
+
return Fs.readFileSync(absPath).toOption().flatMap((content) => {
|
|
850
|
+
try {
|
|
851
|
+
return Option(JSON.parse(content));
|
|
852
|
+
} catch {
|
|
853
|
+
return Option.none();
|
|
854
|
+
}
|
|
855
|
+
});
|
|
343
856
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
857
|
+
function saveBaseline(baselinePath, files) {
|
|
858
|
+
const absPath = join(targetDir, baselinePath);
|
|
859
|
+
const totalRaw = files.fold(0, (sum, f) => sum + f.raw);
|
|
860
|
+
const totalGzip = files.fold(0, (sum, f) => sum + f.gzip);
|
|
861
|
+
const baseline = {
|
|
862
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
863
|
+
total: {
|
|
864
|
+
raw: totalRaw,
|
|
865
|
+
gzip: totalGzip
|
|
866
|
+
},
|
|
867
|
+
files: Object.fromEntries(files.map((f) => [f.path, {
|
|
868
|
+
raw: f.raw,
|
|
869
|
+
gzip: f.gzip
|
|
870
|
+
}]).toArray())
|
|
871
|
+
};
|
|
872
|
+
Fs.writeFileSync(absPath, JSON.stringify(baseline, null, 2) + "\n");
|
|
351
873
|
}
|
|
352
|
-
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (cleanCode !== 0) return cleanCode;
|
|
357
|
-
return runCommand("vite", ["build"]);
|
|
358
|
-
}
|
|
359
|
-
if (watch) return runCommand("tsdown", ["--watch"]);
|
|
360
|
-
const cleanCode = await runCommand("rimraf", ["dist"]);
|
|
361
|
-
if (cleanCode !== 0) return cleanCode;
|
|
362
|
-
return runCommand("cross-env", ["NODE_ENV=production", "tsdown"]);
|
|
874
|
+
function formatBytes(bytes) {
|
|
875
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
876
|
+
const kb = bytes / 1024;
|
|
877
|
+
return kb < 1024 ? `${kb.toFixed(2)} kB` : `${(kb / 1024).toFixed(2)} MB`;
|
|
363
878
|
}
|
|
364
|
-
|
|
365
|
-
|
|
879
|
+
function formatDelta(current, previous) {
|
|
880
|
+
const diff = current - previous;
|
|
881
|
+
if (diff === 0) return "";
|
|
882
|
+
return ` (${diff > 0 ? "+" : ""}${formatBytes(diff)})`;
|
|
366
883
|
}
|
|
367
|
-
function
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
884
|
+
function formatSizeTable(files, baseline, showGzip) {
|
|
885
|
+
const lines = [];
|
|
886
|
+
const nameWidth = Math.max(4, ...files.map((f) => f.path.length).toArray());
|
|
887
|
+
const sizeWidth = 10;
|
|
888
|
+
const gzipWidth = 10;
|
|
889
|
+
const deltaWidth = 16;
|
|
890
|
+
const header = showGzip ? `${"File".padEnd(nameWidth)} ${"Size".padStart(sizeWidth)} ${"Gzip".padStart(gzipWidth)} ${"Delta".padStart(deltaWidth)}` : `${"File".padEnd(nameWidth)} ${"Size".padStart(sizeWidth)} ${"Delta".padStart(deltaWidth)}`;
|
|
891
|
+
lines.push(header);
|
|
892
|
+
lines.push("-".repeat(header.length));
|
|
893
|
+
const baselineData = baseline.orUndefined();
|
|
894
|
+
for (const file of files) {
|
|
895
|
+
const prev = baselineData?.files[file.path];
|
|
896
|
+
const delta = prev ? formatDelta(file.raw, prev.raw) : "";
|
|
897
|
+
const row = showGzip ? `${file.path.padEnd(nameWidth)} ${formatBytes(file.raw).padStart(sizeWidth)} ${formatBytes(file.gzip).padStart(gzipWidth)} ${delta.padStart(deltaWidth)}` : `${file.path.padEnd(nameWidth)} ${formatBytes(file.raw).padStart(sizeWidth)} ${delta.padStart(deltaWidth)}`;
|
|
898
|
+
lines.push(row);
|
|
899
|
+
}
|
|
900
|
+
const totalRaw = files.fold(0, (sum, f) => sum + f.raw);
|
|
901
|
+
const totalGzip = files.fold(0, (sum, f) => sum + f.gzip);
|
|
902
|
+
const totalDelta = baselineData ? formatDelta(totalRaw, baselineData.total.raw) : "";
|
|
903
|
+
lines.push("-".repeat(header.length));
|
|
904
|
+
const totalRow = showGzip ? `${"Total".padEnd(nameWidth)} ${formatBytes(totalRaw).padStart(sizeWidth)} ${formatBytes(totalGzip).padStart(gzipWidth)} ${totalDelta.padStart(deltaWidth)}` : `${"Total".padEnd(nameWidth)} ${formatBytes(totalRaw).padStart(sizeWidth)} ${totalDelta.padStart(deltaWidth)}`;
|
|
905
|
+
lines.push(totalRow);
|
|
906
|
+
return lines.join("\n");
|
|
389
907
|
}
|
|
390
|
-
async function
|
|
391
|
-
|
|
392
|
-
|
|
908
|
+
async function runSize(args) {
|
|
909
|
+
const sizeConfig = loadConfig().size;
|
|
910
|
+
const showGzip = sizeConfig.gzip !== false;
|
|
911
|
+
const baselineFile = sizeConfig.baselineFile ?? ".ts-builds-size.json";
|
|
912
|
+
const save = args.includes("--save");
|
|
913
|
+
const files = getFileSizes("dist").sorted((a, b) => b.raw - a.raw);
|
|
914
|
+
if (files.isEmpty) {
|
|
915
|
+
console.error("No files found in dist/. Run a build first.");
|
|
393
916
|
return 1;
|
|
394
917
|
}
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
918
|
+
const baseline = loadBaseline(baselineFile);
|
|
919
|
+
const table = formatSizeTable(files, baseline, showGzip);
|
|
920
|
+
console.log("\nBundle Size Report\n");
|
|
921
|
+
console.log(table);
|
|
922
|
+
baseline.map((b) => {
|
|
923
|
+
console.log(`\nBaseline: ${b.timestamp}`);
|
|
924
|
+
return b;
|
|
925
|
+
});
|
|
926
|
+
if (save) {
|
|
927
|
+
saveBaseline(baselineFile, files);
|
|
928
|
+
console.log(`\nBaseline saved to ${baselineFile}`);
|
|
400
929
|
}
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
console.error(`Unknown command or chain: ${step}`);
|
|
412
|
-
return 1;
|
|
413
|
-
}
|
|
414
|
-
const cwdLabel = cmdDef.cwd ? ` (in ${cmdDef.cwd})` : "";
|
|
415
|
-
console.log(`\nā¶ Running ${step}...${cwdLabel}`);
|
|
416
|
-
const code = await runShellCommand(cmdDef.run, { cwd: cmdDef.cwd });
|
|
417
|
-
if (code !== 0) {
|
|
418
|
-
console.error(`\nā ${step} failed with exit code ${code}`);
|
|
419
|
-
return code;
|
|
930
|
+
const totalRaw = files.fold(0, (sum, f) => sum + f.raw);
|
|
931
|
+
let failed = false;
|
|
932
|
+
if (sizeConfig.maxTotal && totalRaw > sizeConfig.maxTotal) {
|
|
933
|
+
console.error(`\nTotal size ${formatBytes(totalRaw)} exceeds max ${formatBytes(sizeConfig.maxTotal)}`);
|
|
934
|
+
failed = true;
|
|
935
|
+
}
|
|
936
|
+
if (sizeConfig.maxFile) {
|
|
937
|
+
for (const file of files) if (file.raw > sizeConfig.maxFile) {
|
|
938
|
+
console.error(`\n${file.path} (${formatBytes(file.raw)}) exceeds max file size ${formatBytes(sizeConfig.maxFile)}`);
|
|
939
|
+
failed = true;
|
|
420
940
|
}
|
|
421
|
-
console.log(`ā ${step} complete`);
|
|
422
941
|
}
|
|
423
|
-
return 0;
|
|
424
|
-
}
|
|
425
|
-
async function runValidate(chainName = "validate") {
|
|
426
|
-
return runChain(chainName, loadConfig());
|
|
942
|
+
return failed ? 1 : 0;
|
|
427
943
|
}
|
|
944
|
+
|
|
945
|
+
//#endregion
|
|
946
|
+
//#region src/cli.ts
|
|
428
947
|
const command = process.argv[2] || "init";
|
|
429
948
|
const subCommand = process.argv[3];
|
|
430
949
|
switch (command) {
|
|
@@ -484,6 +1003,15 @@ switch (command) {
|
|
|
484
1003
|
case "validate":
|
|
485
1004
|
process.exit(await runValidate());
|
|
486
1005
|
break;
|
|
1006
|
+
case "size":
|
|
1007
|
+
process.exit(await runSize(process.argv.slice(3)));
|
|
1008
|
+
break;
|
|
1009
|
+
case "doctor":
|
|
1010
|
+
process.exit(await runDoctor());
|
|
1011
|
+
break;
|
|
1012
|
+
case "changelog":
|
|
1013
|
+
process.exit(await runChangelog(process.argv.slice(3)));
|
|
1014
|
+
break;
|
|
487
1015
|
case "init":
|
|
488
1016
|
init();
|
|
489
1017
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-builds",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.2",
|
|
4
4
|
"description": "Shared TypeScript configuration files for library templates. Provides standardized ESLint, Prettier, Vitest, TypeScript, and build configs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -55,24 +55,26 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@eslint/js": "^10.0.1",
|
|
57
57
|
"@types/node": "~24.10.15",
|
|
58
|
-
"@vitest/coverage-v8": "^4.1.
|
|
59
|
-
"@vitest/ui": "^4.1.
|
|
58
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
59
|
+
"@vitest/ui": "^4.1.1",
|
|
60
60
|
"cross-env": "^10.1.0",
|
|
61
61
|
"eslint": "^10.1.0",
|
|
62
|
-
"eslint-config-functype": "^2.1.
|
|
63
|
-
"eslint-plugin-functype": "^2.1.
|
|
62
|
+
"eslint-config-functype": "^2.1.1",
|
|
63
|
+
"eslint-plugin-functype": "^2.1.1",
|
|
64
64
|
"eslint-plugin-prettier": "^5.5.5",
|
|
65
65
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
66
|
+
"functype": "^0.51.0",
|
|
67
|
+
"functype-os": "^0.4.1",
|
|
66
68
|
"globals": "^17.4.0",
|
|
67
69
|
"prettier": "^3.8.1",
|
|
68
70
|
"rimraf": "^6.1.3",
|
|
69
71
|
"ts-node": "^10.9.2",
|
|
70
72
|
"typescript": "^5.9.3",
|
|
71
|
-
"typescript-eslint": "^8.57.
|
|
72
|
-
"vitest": "^4.1.
|
|
73
|
+
"typescript-eslint": "^8.57.2",
|
|
74
|
+
"vitest": "^4.1.1"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
75
|
-
"tsdown": "^0.21.
|
|
77
|
+
"tsdown": "^0.21.5"
|
|
76
78
|
},
|
|
77
79
|
"peerDependencies": {
|
|
78
80
|
"tsdown": "^0.x",
|
|
@@ -108,5 +110,5 @@
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
},
|
|
111
|
-
"packageManager": "pnpm@10.
|
|
113
|
+
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
|
112
114
|
}
|