@savvy-web/lint-staged 0.7.3 → 1.0.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/878.js +310 -187
- package/README.md +3 -3
- package/biome/silk.jsonc +5 -2
- package/index.d.ts +121 -512
- package/index.js +22 -616
- package/package.json +10 -13
- package/tsdoc-metadata.json +1 -1
package/878.js
CHANGED
|
@@ -2,17 +2,298 @@ import { Args, Command, Options } from "@effect/cli";
|
|
|
2
2
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
3
3
|
import { BiomeSchemaSync, BiomeSchemaSyncLive, CheckResult, ConfigDiscovery, ConfigDiscoveryLive, ManagedSection, ManagedSectionLive, SectionDefinition, ShellSectionDefinition, SyncResult, ToolDefinition, ToolDiscovery, ToolDiscoveryLive } from "@savvy-web/silk-effects";
|
|
4
4
|
import { Effect, Layer } from "effect";
|
|
5
|
-
import {
|
|
5
|
+
import { WorkspacesLive, findWorkspaceRootSync, getWorkspacePackagesSync } from "workspaces-effect";
|
|
6
6
|
import { isDeepStrictEqual } from "node:util";
|
|
7
7
|
import { FileSystem } from "@effect/platform";
|
|
8
8
|
import { applyEdits, modify, parse } from "jsonc-effect";
|
|
9
9
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { dirname, join, resolve } from "node:path";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
10
12
|
import sort_package_json from "sort-package-json";
|
|
11
13
|
import { parse as external_yaml_parse, stringify } from "yaml";
|
|
12
|
-
import { execSync } from "node:child_process";
|
|
13
|
-
import { dirname, join, resolve } from "node:path";
|
|
14
14
|
import { format, resolveConfig } from "prettier";
|
|
15
15
|
import { lint } from "yaml-lint";
|
|
16
|
+
const VALID_COMMAND_PATTERN = /^[\w@/-]+$/;
|
|
17
|
+
function validateCommandName(name) {
|
|
18
|
+
if (!VALID_COMMAND_PATTERN.test(name)) throw new Error(`Invalid command name: "${name}". Only alphanumeric characters, hyphens, underscores, @ and / are allowed.`);
|
|
19
|
+
}
|
|
20
|
+
class Command_Command {
|
|
21
|
+
static cachedPackageManager = null;
|
|
22
|
+
static cachedRoot = null;
|
|
23
|
+
static findRoot(cwd = process.cwd()) {
|
|
24
|
+
if (null !== Command_Command.cachedRoot) return Command_Command.cachedRoot;
|
|
25
|
+
let dir = resolve(cwd);
|
|
26
|
+
while(true){
|
|
27
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
28
|
+
Command_Command.cachedRoot = dir;
|
|
29
|
+
return dir;
|
|
30
|
+
}
|
|
31
|
+
const parent = dirname(dir);
|
|
32
|
+
if (parent === dir) break;
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
Command_Command.cachedRoot = cwd;
|
|
36
|
+
return cwd;
|
|
37
|
+
}
|
|
38
|
+
static detectPackageManager(cwd = Command_Command.findRoot()) {
|
|
39
|
+
if (null !== Command_Command.cachedPackageManager) return Command_Command.cachedPackageManager;
|
|
40
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
41
|
+
if (!existsSync(packageJsonPath)) {
|
|
42
|
+
Command_Command.cachedPackageManager = "npm";
|
|
43
|
+
return "npm";
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
47
|
+
const pkg = JSON.parse(content);
|
|
48
|
+
if (pkg.packageManager) {
|
|
49
|
+
const match = pkg.packageManager.match(/^(npm|pnpm|yarn|bun)@/);
|
|
50
|
+
if (match) {
|
|
51
|
+
Command_Command.cachedPackageManager = match[1];
|
|
52
|
+
return Command_Command.cachedPackageManager;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
Command_Command.cachedPackageManager = "npm";
|
|
57
|
+
return "npm";
|
|
58
|
+
}
|
|
59
|
+
static getExecPrefix(packageManager) {
|
|
60
|
+
switch(packageManager){
|
|
61
|
+
case "pnpm":
|
|
62
|
+
return [
|
|
63
|
+
"pnpm",
|
|
64
|
+
"exec"
|
|
65
|
+
];
|
|
66
|
+
case "yarn":
|
|
67
|
+
return [
|
|
68
|
+
"yarn",
|
|
69
|
+
"exec"
|
|
70
|
+
];
|
|
71
|
+
case "bun":
|
|
72
|
+
return [
|
|
73
|
+
"bun",
|
|
74
|
+
"x",
|
|
75
|
+
"--no-install"
|
|
76
|
+
];
|
|
77
|
+
default:
|
|
78
|
+
return [
|
|
79
|
+
"npx",
|
|
80
|
+
"--no"
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
static clearCache() {
|
|
85
|
+
Command_Command.cachedPackageManager = null;
|
|
86
|
+
Command_Command.cachedRoot = null;
|
|
87
|
+
}
|
|
88
|
+
static isAvailable(command) {
|
|
89
|
+
validateCommandName(command);
|
|
90
|
+
try {
|
|
91
|
+
execSync(`command -v ${command}`, {
|
|
92
|
+
stdio: "ignore"
|
|
93
|
+
});
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
static findTool(tool) {
|
|
100
|
+
validateCommandName(tool);
|
|
101
|
+
if (Command_Command.isAvailable(tool)) return {
|
|
102
|
+
available: true,
|
|
103
|
+
command: tool,
|
|
104
|
+
source: "global"
|
|
105
|
+
};
|
|
106
|
+
const pm = Command_Command.detectPackageManager();
|
|
107
|
+
const prefix = Command_Command.getExecPrefix(pm);
|
|
108
|
+
const execCmd = [
|
|
109
|
+
...prefix,
|
|
110
|
+
tool
|
|
111
|
+
].join(" ");
|
|
112
|
+
try {
|
|
113
|
+
execSync(`${execCmd} --version`, {
|
|
114
|
+
stdio: "ignore"
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
available: true,
|
|
118
|
+
command: execCmd,
|
|
119
|
+
source: pm
|
|
120
|
+
};
|
|
121
|
+
} catch {}
|
|
122
|
+
return {
|
|
123
|
+
available: false,
|
|
124
|
+
command: void 0,
|
|
125
|
+
source: void 0
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
static requireTool(tool, errorMessage) {
|
|
129
|
+
const result = Command_Command.findTool(tool);
|
|
130
|
+
if (!result.available || !result.command) throw new Error(errorMessage ?? `Required tool '${tool}' is not available. Install it globally or add it as a dev dependency.`);
|
|
131
|
+
return result.command;
|
|
132
|
+
}
|
|
133
|
+
static findSavvyLint() {
|
|
134
|
+
const result = Command_Command.findTool("savvy-lint");
|
|
135
|
+
if (result.available && result.command) return result.command;
|
|
136
|
+
const root = Command_Command.findRoot();
|
|
137
|
+
return `node ${root}/dist/dev/bin/savvy-lint.js`;
|
|
138
|
+
}
|
|
139
|
+
static exec(command) {
|
|
140
|
+
return execSync(command, {
|
|
141
|
+
encoding: "utf-8"
|
|
142
|
+
}).trim();
|
|
143
|
+
}
|
|
144
|
+
static execSilent(command) {
|
|
145
|
+
try {
|
|
146
|
+
execSync(command, {
|
|
147
|
+
stdio: "ignore"
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
class Filter {
|
|
156
|
+
static exclude(filenames, patterns) {
|
|
157
|
+
if (0 === patterns.length) return [
|
|
158
|
+
...filenames
|
|
159
|
+
];
|
|
160
|
+
return filenames.filter((file)=>!patterns.some((pattern)=>file.includes(pattern)));
|
|
161
|
+
}
|
|
162
|
+
static include(filenames, patterns) {
|
|
163
|
+
if (0 === patterns.length) return [];
|
|
164
|
+
return filenames.filter((file)=>patterns.some((pattern)=>file.includes(pattern)));
|
|
165
|
+
}
|
|
166
|
+
static apply(filenames, options) {
|
|
167
|
+
let result = [
|
|
168
|
+
...filenames
|
|
169
|
+
];
|
|
170
|
+
if (options.include && options.include.length > 0) result = Filter.include(result, options.include);
|
|
171
|
+
if (options.exclude && options.exclude.length > 0) result = Filter.exclude(result, options.exclude);
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
static shellEscape(filenames) {
|
|
175
|
+
return filenames.map((f)=>`'${f.replace(/'/g, "'\\''")}'`).join(" ");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const UNRESOLVED = Symbol("unresolved");
|
|
179
|
+
let cachedRoot = UNRESOLVED;
|
|
180
|
+
let cachedPackages = UNRESOLVED;
|
|
181
|
+
let cachedPaths = UNRESOLVED;
|
|
182
|
+
function getWorkspaceRoot() {
|
|
183
|
+
if (cachedRoot !== UNRESOLVED) return cachedRoot;
|
|
184
|
+
cachedRoot = findWorkspaceRootSync() ?? null;
|
|
185
|
+
return cachedRoot;
|
|
186
|
+
}
|
|
187
|
+
function getWorkspacePackages() {
|
|
188
|
+
if (cachedPackages !== UNRESOLVED) return cachedPackages;
|
|
189
|
+
const root = getWorkspaceRoot();
|
|
190
|
+
if (null === root) {
|
|
191
|
+
cachedPackages = null;
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const all = getWorkspacePackagesSync(root) ?? [];
|
|
195
|
+
cachedPackages = all.filter((pkg)=>pkg.path !== root);
|
|
196
|
+
return cachedPackages;
|
|
197
|
+
}
|
|
198
|
+
function getWorkspacePackagePaths() {
|
|
199
|
+
if (cachedPaths !== UNRESOLVED) return cachedPaths;
|
|
200
|
+
const packages = getWorkspacePackages();
|
|
201
|
+
cachedPaths = packages?.map((pkg)=>pkg.path) ?? [];
|
|
202
|
+
return cachedPaths;
|
|
203
|
+
}
|
|
204
|
+
function isWorkspacePackagePath(filePath) {
|
|
205
|
+
const root = getWorkspaceRoot();
|
|
206
|
+
if (null === root) return true;
|
|
207
|
+
const dir = dirname(filePath);
|
|
208
|
+
if (dir === root) return true;
|
|
209
|
+
const packagePaths = getWorkspacePackagePaths();
|
|
210
|
+
return packagePaths.includes(dir);
|
|
211
|
+
}
|
|
212
|
+
function resetWorkspaceCache() {
|
|
213
|
+
cachedRoot = UNRESOLVED;
|
|
214
|
+
cachedPackages = UNRESOLVED;
|
|
215
|
+
cachedPaths = UNRESOLVED;
|
|
216
|
+
}
|
|
217
|
+
class Biome {
|
|
218
|
+
static glob = "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}";
|
|
219
|
+
static defaultExcludes = [
|
|
220
|
+
"package.json",
|
|
221
|
+
"package-lock.json",
|
|
222
|
+
"__fixtures__",
|
|
223
|
+
"__test__/fixtures"
|
|
224
|
+
];
|
|
225
|
+
static CONFIG_NAMES = [
|
|
226
|
+
"biome.jsonc",
|
|
227
|
+
"biome.json"
|
|
228
|
+
];
|
|
229
|
+
static handler = Biome.create();
|
|
230
|
+
static findBiome() {
|
|
231
|
+
const result = Command_Command.findTool("biome");
|
|
232
|
+
return result.command;
|
|
233
|
+
}
|
|
234
|
+
static isAvailable() {
|
|
235
|
+
return Command_Command.findTool("biome").available;
|
|
236
|
+
}
|
|
237
|
+
static findConfig() {
|
|
238
|
+
const root = getWorkspaceRoot();
|
|
239
|
+
const searchDir = root ?? process.cwd();
|
|
240
|
+
for (const name of Biome.CONFIG_NAMES){
|
|
241
|
+
const fullPath = join(searchDir, name);
|
|
242
|
+
if (existsSync(fullPath)) return fullPath;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
static findAllConfigs() {
|
|
246
|
+
const root = getWorkspaceRoot();
|
|
247
|
+
const configs = [];
|
|
248
|
+
if (null === root) {
|
|
249
|
+
const cwd = process.cwd();
|
|
250
|
+
for (const name of Biome.CONFIG_NAMES){
|
|
251
|
+
const fullPath = join(cwd, name);
|
|
252
|
+
if (existsSync(fullPath)) {
|
|
253
|
+
configs.push(fullPath);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return configs;
|
|
258
|
+
}
|
|
259
|
+
for (const name of Biome.CONFIG_NAMES){
|
|
260
|
+
const fullPath = join(root, name);
|
|
261
|
+
if (existsSync(fullPath)) {
|
|
262
|
+
configs.push(fullPath);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (const pkgPath of getWorkspacePackagePaths())for (const name of Biome.CONFIG_NAMES){
|
|
267
|
+
const fullPath = join(pkgPath, name);
|
|
268
|
+
if (existsSync(fullPath)) {
|
|
269
|
+
configs.push(fullPath);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return configs;
|
|
274
|
+
}
|
|
275
|
+
static create(options = {}) {
|
|
276
|
+
const excludes = options.exclude ?? [
|
|
277
|
+
...Biome.defaultExcludes
|
|
278
|
+
];
|
|
279
|
+
const config = options.config ?? Biome.findConfig();
|
|
280
|
+
return (filenames)=>{
|
|
281
|
+
const filtered = Filter.exclude(filenames, excludes);
|
|
282
|
+
if (0 === filtered.length) return [];
|
|
283
|
+
const biomeCmd = Command_Command.requireTool("biome", "Biome is not available. Install it globally (recommended) or add @biomejs/biome as a dev dependency.");
|
|
284
|
+
const files = Filter.shellEscape(filtered);
|
|
285
|
+
const flags = options.flags ?? [];
|
|
286
|
+
const configFlag = config ? `--config-path=${config}` : "";
|
|
287
|
+
const cmd = [
|
|
288
|
+
`${biomeCmd} check --write --no-errors-on-unmatched`,
|
|
289
|
+
configFlag,
|
|
290
|
+
...flags,
|
|
291
|
+
files
|
|
292
|
+
].filter(Boolean).join(" ");
|
|
293
|
+
return cmd;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
16
297
|
const HUSKY_HOOK_PATH = ".husky/pre-commit";
|
|
17
298
|
const POST_CHECKOUT_HOOK_PATH = ".husky/post-checkout";
|
|
18
299
|
const POST_MERGE_HOOK_PATH = ".husky/post-merge";
|
|
@@ -373,25 +654,31 @@ function checkMarkdownlintConfig(content) {
|
|
|
373
654
|
}
|
|
374
655
|
function checkBiomeSchemas() {
|
|
375
656
|
return Effect.gen(function*() {
|
|
376
|
-
const version = "2.4.
|
|
657
|
+
const version = "2.4.12";
|
|
377
658
|
const statuses = [];
|
|
378
659
|
if (!version) return {
|
|
379
660
|
statuses,
|
|
380
661
|
warnings: []
|
|
381
662
|
};
|
|
382
|
-
const
|
|
383
|
-
const result = yield* syncer.check(version);
|
|
663
|
+
const fs = yield* FileSystem.FileSystem;
|
|
384
664
|
const warnings = [];
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
665
|
+
const expectedSchema = `https://biomejs.dev/schemas/${version}/schema.json`;
|
|
666
|
+
const configPaths = Biome.findAllConfigs();
|
|
667
|
+
for (const configPath of configPaths){
|
|
668
|
+
const content = yield* fs.readFileString(configPath);
|
|
669
|
+
const parsed = yield* parse(content);
|
|
670
|
+
const currentSchema = parsed.$schema;
|
|
671
|
+
if (currentSchema === expectedSchema) statuses.push({
|
|
391
672
|
path: configPath,
|
|
392
|
-
matches:
|
|
673
|
+
matches: true
|
|
393
674
|
});
|
|
394
|
-
|
|
675
|
+
else {
|
|
676
|
+
statuses.push({
|
|
677
|
+
path: configPath,
|
|
678
|
+
matches: false
|
|
679
|
+
});
|
|
680
|
+
warnings.push(`${WARNING} ${configPath}: biome $schema is outdated.\n Run 'savvy-lint init' to update it.`);
|
|
681
|
+
}
|
|
395
682
|
}
|
|
396
683
|
return {
|
|
397
684
|
statuses,
|
|
@@ -438,7 +725,7 @@ const checkCommand = Command.make("check", {
|
|
|
438
725
|
if (status.found && status.needsUpdate) warnings.push(`${WARNING} Your ${hookPath} managed section is outdated.\n Run 'savvy-lint init' to update it (preserves your custom hooks).`);
|
|
439
726
|
}
|
|
440
727
|
}
|
|
441
|
-
const biomeSchemaStatus = yield* checkBiomeSchemas().pipe(Effect.
|
|
728
|
+
const biomeSchemaStatus = yield* checkBiomeSchemas().pipe(Effect.catchAll(()=>Effect.succeed({
|
|
442
729
|
statuses: [],
|
|
443
730
|
warnings: [
|
|
444
731
|
`${WARNING} Could not check biome $schema URLs.`
|
|
@@ -509,9 +796,6 @@ const checkCommand = Command.make("check", {
|
|
|
509
796
|
if (tsgoAvailable) yield* Effect.log(` ${CHECK_MARK} TypeScript (tsgo)`);
|
|
510
797
|
else if (tscAvailable) yield* Effect.log(` ${CHECK_MARK} TypeScript (tsc)`);
|
|
511
798
|
else yield* Effect.log(` ${BULLET} TypeScript: not installed`);
|
|
512
|
-
const tsdocConfig = yield* discovery.find("tsdoc.json");
|
|
513
|
-
if (tsdocConfig) yield* Effect.log(` ${CHECK_MARK} TSDoc (tsdoc.json found)`);
|
|
514
|
-
else yield* Effect.log(` ${BULLET} TSDoc: no tsdoc.json found`);
|
|
515
799
|
if (hasMarkdownlintConfig) if (markdownlintStatus.isUpToDate) yield* Effect.log(` ${CHECK_MARK} ${MARKDOWNLINT_CONFIG_PATH}: up-to-date`);
|
|
516
800
|
else {
|
|
517
801
|
const issues = [];
|
|
@@ -530,145 +814,6 @@ const checkCommand = Command.make("check", {
|
|
|
530
814
|
if (hasIssues) yield* Effect.log(`${WARNING} Some issues found. Run 'savvy-lint init' to fix.`);
|
|
531
815
|
else yield* Effect.log(`${CHECK_MARK} Lint-staged is configured correctly.`);
|
|
532
816
|
})).pipe(Command.withDescription("Check current lint-staged configuration and tool availability"));
|
|
533
|
-
const VALID_COMMAND_PATTERN = /^[\w@/-]+$/;
|
|
534
|
-
function validateCommandName(name) {
|
|
535
|
-
if (!VALID_COMMAND_PATTERN.test(name)) throw new Error(`Invalid command name: "${name}". Only alphanumeric characters, hyphens, underscores, @ and / are allowed.`);
|
|
536
|
-
}
|
|
537
|
-
class Command_Command {
|
|
538
|
-
static cachedPackageManager = null;
|
|
539
|
-
static cachedRoot = null;
|
|
540
|
-
static findRoot(cwd = process.cwd()) {
|
|
541
|
-
if (null !== Command_Command.cachedRoot) return Command_Command.cachedRoot;
|
|
542
|
-
let dir = resolve(cwd);
|
|
543
|
-
while(true){
|
|
544
|
-
if (existsSync(join(dir, "package.json"))) {
|
|
545
|
-
Command_Command.cachedRoot = dir;
|
|
546
|
-
return dir;
|
|
547
|
-
}
|
|
548
|
-
const parent = dirname(dir);
|
|
549
|
-
if (parent === dir) break;
|
|
550
|
-
dir = parent;
|
|
551
|
-
}
|
|
552
|
-
Command_Command.cachedRoot = cwd;
|
|
553
|
-
return cwd;
|
|
554
|
-
}
|
|
555
|
-
static detectPackageManager(cwd = Command_Command.findRoot()) {
|
|
556
|
-
if (null !== Command_Command.cachedPackageManager) return Command_Command.cachedPackageManager;
|
|
557
|
-
const packageJsonPath = join(cwd, "package.json");
|
|
558
|
-
if (!existsSync(packageJsonPath)) {
|
|
559
|
-
Command_Command.cachedPackageManager = "npm";
|
|
560
|
-
return "npm";
|
|
561
|
-
}
|
|
562
|
-
try {
|
|
563
|
-
const content = readFileSync(packageJsonPath, "utf-8");
|
|
564
|
-
const pkg = JSON.parse(content);
|
|
565
|
-
if (pkg.packageManager) {
|
|
566
|
-
const match = pkg.packageManager.match(/^(npm|pnpm|yarn|bun)@/);
|
|
567
|
-
if (match) {
|
|
568
|
-
Command_Command.cachedPackageManager = match[1];
|
|
569
|
-
return Command_Command.cachedPackageManager;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
} catch {}
|
|
573
|
-
Command_Command.cachedPackageManager = "npm";
|
|
574
|
-
return "npm";
|
|
575
|
-
}
|
|
576
|
-
static getExecPrefix(packageManager) {
|
|
577
|
-
switch(packageManager){
|
|
578
|
-
case "pnpm":
|
|
579
|
-
return [
|
|
580
|
-
"pnpm",
|
|
581
|
-
"exec"
|
|
582
|
-
];
|
|
583
|
-
case "yarn":
|
|
584
|
-
return [
|
|
585
|
-
"yarn",
|
|
586
|
-
"exec"
|
|
587
|
-
];
|
|
588
|
-
case "bun":
|
|
589
|
-
return [
|
|
590
|
-
"bun",
|
|
591
|
-
"x",
|
|
592
|
-
"--no-install"
|
|
593
|
-
];
|
|
594
|
-
default:
|
|
595
|
-
return [
|
|
596
|
-
"npx",
|
|
597
|
-
"--no"
|
|
598
|
-
];
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
static clearCache() {
|
|
602
|
-
Command_Command.cachedPackageManager = null;
|
|
603
|
-
Command_Command.cachedRoot = null;
|
|
604
|
-
}
|
|
605
|
-
static isAvailable(command) {
|
|
606
|
-
validateCommandName(command);
|
|
607
|
-
try {
|
|
608
|
-
execSync(`command -v ${command}`, {
|
|
609
|
-
stdio: "ignore"
|
|
610
|
-
});
|
|
611
|
-
return true;
|
|
612
|
-
} catch {
|
|
613
|
-
return false;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
static findTool(tool) {
|
|
617
|
-
validateCommandName(tool);
|
|
618
|
-
if (Command_Command.isAvailable(tool)) return {
|
|
619
|
-
available: true,
|
|
620
|
-
command: tool,
|
|
621
|
-
source: "global"
|
|
622
|
-
};
|
|
623
|
-
const pm = Command_Command.detectPackageManager();
|
|
624
|
-
const prefix = Command_Command.getExecPrefix(pm);
|
|
625
|
-
const execCmd = [
|
|
626
|
-
...prefix,
|
|
627
|
-
tool
|
|
628
|
-
].join(" ");
|
|
629
|
-
try {
|
|
630
|
-
execSync(`${execCmd} --version`, {
|
|
631
|
-
stdio: "ignore"
|
|
632
|
-
});
|
|
633
|
-
return {
|
|
634
|
-
available: true,
|
|
635
|
-
command: execCmd,
|
|
636
|
-
source: pm
|
|
637
|
-
};
|
|
638
|
-
} catch {}
|
|
639
|
-
return {
|
|
640
|
-
available: false,
|
|
641
|
-
command: void 0,
|
|
642
|
-
source: void 0
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
static requireTool(tool, errorMessage) {
|
|
646
|
-
const result = Command_Command.findTool(tool);
|
|
647
|
-
if (!result.available || !result.command) throw new Error(errorMessage ?? `Required tool '${tool}' is not available. Install it globally or add it as a dev dependency.`);
|
|
648
|
-
return result.command;
|
|
649
|
-
}
|
|
650
|
-
static findSavvyLint() {
|
|
651
|
-
const result = Command_Command.findTool("savvy-lint");
|
|
652
|
-
if (result.available && result.command) return result.command;
|
|
653
|
-
const root = Command_Command.findRoot();
|
|
654
|
-
return `node ${root}/dist/dev/bin/savvy-lint.js`;
|
|
655
|
-
}
|
|
656
|
-
static exec(command) {
|
|
657
|
-
return execSync(command, {
|
|
658
|
-
encoding: "utf-8"
|
|
659
|
-
}).trim();
|
|
660
|
-
}
|
|
661
|
-
static execSilent(command) {
|
|
662
|
-
try {
|
|
663
|
-
execSync(command, {
|
|
664
|
-
stdio: "ignore"
|
|
665
|
-
});
|
|
666
|
-
return true;
|
|
667
|
-
} catch {
|
|
668
|
-
return false;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
817
|
const DEFAULT_STRINGIFY_OPTIONS = {
|
|
673
818
|
indent: 2,
|
|
674
819
|
lineWidth: 0,
|
|
@@ -730,29 +875,6 @@ class PnpmWorkspace {
|
|
|
730
875
|
};
|
|
731
876
|
}
|
|
732
877
|
}
|
|
733
|
-
class Filter {
|
|
734
|
-
static exclude(filenames, patterns) {
|
|
735
|
-
if (0 === patterns.length) return [
|
|
736
|
-
...filenames
|
|
737
|
-
];
|
|
738
|
-
return filenames.filter((file)=>!patterns.some((pattern)=>file.includes(pattern)));
|
|
739
|
-
}
|
|
740
|
-
static include(filenames, patterns) {
|
|
741
|
-
if (0 === patterns.length) return [];
|
|
742
|
-
return filenames.filter((file)=>patterns.some((pattern)=>file.includes(pattern)));
|
|
743
|
-
}
|
|
744
|
-
static apply(filenames, options) {
|
|
745
|
-
let result = [
|
|
746
|
-
...filenames
|
|
747
|
-
];
|
|
748
|
-
if (options.include && options.include.length > 0) result = Filter.include(result, options.include);
|
|
749
|
-
if (options.exclude && options.exclude.length > 0) result = Filter.exclude(result, options.exclude);
|
|
750
|
-
return result;
|
|
751
|
-
}
|
|
752
|
-
static shellEscape(filenames) {
|
|
753
|
-
return filenames.map((f)=>`'${f.replace(/'/g, "'\\''")}'`).join(" ");
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
878
|
class Yaml {
|
|
757
879
|
static glob = "**/*.{yml,yaml}";
|
|
758
880
|
static defaultExcludes = [
|
|
@@ -762,9 +884,11 @@ class Yaml {
|
|
|
762
884
|
];
|
|
763
885
|
static handler = Yaml.create();
|
|
764
886
|
static findConfig() {
|
|
765
|
-
const
|
|
887
|
+
const root = getWorkspaceRoot() ?? process.cwd();
|
|
888
|
+
const libPath = join(root, "lib/configs/.yaml-lint.json");
|
|
766
889
|
if (existsSync(libPath)) return libPath;
|
|
767
|
-
|
|
890
|
+
const rootPath = join(root, ".yaml-lint.json");
|
|
891
|
+
if (existsSync(rootPath)) return rootPath;
|
|
768
892
|
}
|
|
769
893
|
static loadConfig(filepath) {
|
|
770
894
|
try {
|
|
@@ -935,7 +1059,7 @@ function writeMarkdownlintConfig(fs, preset, force) {
|
|
|
935
1059
|
}
|
|
936
1060
|
function syncBiomeSchemas() {
|
|
937
1061
|
return Effect.gen(function*() {
|
|
938
|
-
const version = "2.4.
|
|
1062
|
+
const version = "2.4.12";
|
|
939
1063
|
if (!version) return;
|
|
940
1064
|
const syncer = yield* BiomeSchemaSync;
|
|
941
1065
|
const result = yield* syncer.sync(version);
|
|
@@ -1003,9 +1127,8 @@ const initCommand = Command.make("init", {
|
|
|
1003
1127
|
}
|
|
1004
1128
|
yield* Effect.log("\nDone! Lint-staged is ready to use.");
|
|
1005
1129
|
})).pipe(Command.withDescription("Initialize lint-staged configuration and husky hooks"));
|
|
1006
|
-
const WorkspaceLive = Layer.mergeAll(PackageManagerDetectorLive, WorkspaceRootLive);
|
|
1007
1130
|
const SilkLive = Layer.mergeAll(ManagedSectionLive, BiomeSchemaSyncLive, ConfigDiscoveryLive, ToolDiscoveryLive);
|
|
1008
|
-
const AppLayer = SilkLive.pipe(Layer.provideMerge(
|
|
1131
|
+
const AppLayer = SilkLive.pipe(Layer.provideMerge(WorkspacesLive), Layer.provideMerge(NodeContext.layer));
|
|
1009
1132
|
const rootCommand = Command.make("savvy-lint").pipe(Command.withSubcommands([
|
|
1010
1133
|
initCommand,
|
|
1011
1134
|
checkCommand,
|
|
@@ -1013,10 +1136,10 @@ const rootCommand = Command.make("savvy-lint").pipe(Command.withSubcommands([
|
|
|
1013
1136
|
]));
|
|
1014
1137
|
const cli = Command.run(rootCommand, {
|
|
1015
1138
|
name: "savvy-lint",
|
|
1016
|
-
version: "0.
|
|
1139
|
+
version: "1.0.0"
|
|
1017
1140
|
});
|
|
1018
1141
|
function runCli() {
|
|
1019
1142
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(AppLayer));
|
|
1020
1143
|
NodeRuntime.runMain(main);
|
|
1021
1144
|
}
|
|
1022
|
-
export { Command_Command as Command, Filter, PnpmWorkspace, Yaml, checkCommand, fmtCommand, initCommand, rootCommand, runCli };
|
|
1145
|
+
export { Biome, Command_Command as Command, Filter, PnpmWorkspace, Yaml, checkCommand, fmtCommand, getWorkspacePackagePaths, getWorkspacePackages, getWorkspaceRoot, initCommand, isWorkspacePackagePath, resetWorkspaceCache, rootCommand, runCli };
|
package/README.md
CHANGED
|
@@ -13,9 +13,9 @@ sensible defaults and easy customization.
|
|
|
13
13
|
- Composable handlers for Biome, Markdown, YAML, TypeScript, and more
|
|
14
14
|
- Zero-config presets for instant setup
|
|
15
15
|
- CLI tool (`savvy-lint`) to bootstrap and validate your configuration
|
|
16
|
-
- Workspace-aware
|
|
16
|
+
- Workspace-aware config discovery anchored to workspace root
|
|
17
17
|
- Shareable Biome configuration via `@savvy-web/lint-staged/biome/silk.jsonc`
|
|
18
|
-
- Static class API with excellent TypeScript
|
|
18
|
+
- Static class API with excellent TypeScript support
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
@@ -79,7 +79,7 @@ export default {
|
|
|
79
79
|
| `Yaml` | `**/*.{yml,yaml}` | Format (Prettier) and validate (yaml-lint) |
|
|
80
80
|
| `PnpmWorkspace` | `pnpm-workspace.yaml` | Sort and format |
|
|
81
81
|
| `ShellScripts` | `**/*.sh` | Manage permissions |
|
|
82
|
-
| `TypeScript` | `*.{ts,cts,mts,tsx}` |
|
|
82
|
+
| `TypeScript` | `*.{ts,cts,mts,tsx}` | Type checking (tsgo/tsc) |
|
|
83
83
|
|
|
84
84
|
## CLI
|
|
85
85
|
|
package/biome/silk.jsonc
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.4.
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
|
|
3
3
|
"assist": {
|
|
4
4
|
"actions": {
|
|
5
5
|
"source": {
|
|
@@ -133,9 +133,12 @@
|
|
|
133
133
|
"!**/.vitest",
|
|
134
134
|
"!**/.coverage",
|
|
135
135
|
"!coverage",
|
|
136
|
+
"!**/__test__/fixtures",
|
|
137
|
+
"!**/__test__/snapshots",
|
|
136
138
|
"!**/__test__/**/fixtures",
|
|
137
139
|
"!**/__test__/**/snapshots",
|
|
138
|
-
"!**/__fixtures__"
|
|
140
|
+
"!**/__fixtures__",
|
|
141
|
+
"!**/__snapshots__"
|
|
139
142
|
]
|
|
140
143
|
}
|
|
141
144
|
}
|