@savvy-web/lint-staged 0.2.0 → 0.2.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/376.js +15 -10
- package/README.md +2 -2
- package/index.d.ts +29 -11
- package/index.js +12 -18
- package/package.json +2 -2
package/376.js
CHANGED
|
@@ -270,10 +270,14 @@ class Filter {
|
|
|
270
270
|
if (options.exclude && options.exclude.length > 0) result = Filter.exclude(result, options.exclude);
|
|
271
271
|
return result;
|
|
272
272
|
}
|
|
273
|
+
static shellEscape(filenames) {
|
|
274
|
+
return filenames.map((f)=>`'${f.replace(/'/g, "'\\''")}'`).join(" ");
|
|
275
|
+
}
|
|
273
276
|
}
|
|
274
277
|
class Biome {
|
|
275
278
|
static glob = "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}";
|
|
276
279
|
static defaultExcludes = [
|
|
280
|
+
"package.json",
|
|
277
281
|
"package-lock.json",
|
|
278
282
|
"__fixtures__"
|
|
279
283
|
];
|
|
@@ -298,7 +302,7 @@ class Biome {
|
|
|
298
302
|
const filtered = Filter.exclude(filenames, excludes);
|
|
299
303
|
if (0 === filtered.length) return [];
|
|
300
304
|
const biomeCmd = Command_Command.requireTool("biome", "Biome is not available. Install it globally (recommended) or add @biomejs/biome as a dev dependency.");
|
|
301
|
-
const files =
|
|
305
|
+
const files = Filter.shellEscape(filtered);
|
|
302
306
|
const flags = options.flags ?? [];
|
|
303
307
|
const configFlag = config ? `--config-path=${config}` : "";
|
|
304
308
|
const cmd = [
|
|
@@ -336,7 +340,7 @@ class Markdown {
|
|
|
336
340
|
const filtered = Filter.exclude(filenames, excludes);
|
|
337
341
|
if (0 === filtered.length) return [];
|
|
338
342
|
const mdlintCmd = Command_Command.requireTool("markdownlint-cli2", "markdownlint-cli2 is not available. Install it globally or add it as a dev dependency.");
|
|
339
|
-
const files =
|
|
343
|
+
const files = Filter.shellEscape(filtered);
|
|
340
344
|
const fixFlag = noFix ? "" : "--fix";
|
|
341
345
|
const configFlag = config ? `--config '${config}'` : "";
|
|
342
346
|
const cmd = [
|
|
@@ -921,7 +925,7 @@ const CHECK_MARK = "\u2713";
|
|
|
921
925
|
const WARNING = "\u26A0";
|
|
922
926
|
const EXECUTABLE_MODE = 493;
|
|
923
927
|
const HUSKY_HOOK_PATH = ".husky/pre-commit";
|
|
924
|
-
const DEFAULT_CONFIG_PATH = "lib/configs/lint-staged.config.
|
|
928
|
+
const DEFAULT_CONFIG_PATH = "lib/configs/lint-staged.config.ts";
|
|
925
929
|
const BEGIN_MARKER = "# --- BEGIN SAVVY-LINT MANAGED SECTION ---";
|
|
926
930
|
const END_MARKER = "# --- END SAVVY-LINT MANAGED SECTION ---";
|
|
927
931
|
function generateManagedContent(configPath) {
|
|
@@ -1001,12 +1005,13 @@ function updateManagedSection(existingContent, configPath) {
|
|
|
1001
1005
|
}
|
|
1002
1006
|
function generateConfigContent(preset) {
|
|
1003
1007
|
return `/**
|
|
1004
|
-
*
|
|
1008
|
+
* lint-staged configuration
|
|
1005
1009
|
* Generated by savvy-lint init
|
|
1006
1010
|
*/
|
|
1011
|
+
import type { Configuration } from "lint-staged";
|
|
1007
1012
|
import { Preset } from "@savvy-web/lint-staged";
|
|
1008
1013
|
|
|
1009
|
-
export default Preset.${preset}();
|
|
1014
|
+
export default Preset.${preset}() satisfies Configuration;
|
|
1010
1015
|
`;
|
|
1011
1016
|
}
|
|
1012
1017
|
const forceOption = Options.boolean("force").pipe(Options.withAlias("f"), Options.withDescription("Overwrite entire hook file (not just managed section)"), Options.withDefault(false));
|
|
@@ -1014,8 +1019,8 @@ const configOption = Options.text("config").pipe(Options.withAlias("c"), Options
|
|
|
1014
1019
|
const presetOption = Options.choice("preset", [
|
|
1015
1020
|
"minimal",
|
|
1016
1021
|
"standard",
|
|
1017
|
-
"
|
|
1018
|
-
]).pipe(Options.withAlias("p"), Options.withDescription("Preset to use: minimal, standard, or
|
|
1022
|
+
"silk"
|
|
1023
|
+
]).pipe(Options.withAlias("p"), Options.withDescription("Preset to use: minimal, standard, or silk"), Options.withDefault("silk"));
|
|
1019
1024
|
function makeExecutable(path) {
|
|
1020
1025
|
return Effect.tryPromise(()=>import("node:fs/promises").then((fs)=>fs.chmod(path, EXECUTABLE_MODE)));
|
|
1021
1026
|
}
|
|
@@ -1066,10 +1071,10 @@ const check_WARNING = "\u26A0";
|
|
|
1066
1071
|
const BULLET = "\u2022";
|
|
1067
1072
|
const check_HUSKY_HOOK_PATH = ".husky/pre-commit";
|
|
1068
1073
|
const CONFIG_FILES = [
|
|
1074
|
+
"lint-staged.config.ts",
|
|
1069
1075
|
"lint-staged.config.js",
|
|
1070
1076
|
"lint-staged.config.mjs",
|
|
1071
1077
|
"lint-staged.config.cjs",
|
|
1072
|
-
"lint-staged.config.ts",
|
|
1073
1078
|
".lintstagedrc",
|
|
1074
1079
|
".lintstagedrc.json",
|
|
1075
1080
|
".lintstagedrc.yaml",
|
|
@@ -1079,8 +1084,8 @@ const CONFIG_FILES = [
|
|
|
1079
1084
|
".lintstagedrc.mjs"
|
|
1080
1085
|
];
|
|
1081
1086
|
const CONFIG_SEARCH_PATHS = [
|
|
1082
|
-
"lib/configs/lint-staged.config.js",
|
|
1083
1087
|
"lib/configs/lint-staged.config.ts",
|
|
1088
|
+
"lib/configs/lint-staged.config.js",
|
|
1084
1089
|
...CONFIG_FILES
|
|
1085
1090
|
];
|
|
1086
1091
|
function findConfigFile(fs) {
|
|
@@ -1189,7 +1194,7 @@ const rootCommand = Command.make("savvy-lint").pipe(Command.withSubcommands([
|
|
|
1189
1194
|
]));
|
|
1190
1195
|
const cli = Command.run(rootCommand, {
|
|
1191
1196
|
name: "savvy-lint",
|
|
1192
|
-
version: "0.2.
|
|
1197
|
+
version: "0.2.2"
|
|
1193
1198
|
});
|
|
1194
1199
|
function runCli() {
|
|
1195
1200
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(NodeContext.layer));
|
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ export default {
|
|
|
59
59
|
| ------ | -------- |
|
|
60
60
|
| `minimal()` | PackageJson, Biome |
|
|
61
61
|
| `standard()` | + Markdown, Yaml, PnpmWorkspace, ShellScripts |
|
|
62
|
-
| `
|
|
62
|
+
| `silk()` | + TypeScript |
|
|
63
63
|
|
|
64
64
|
Extend any preset with options:
|
|
65
65
|
|
|
@@ -77,7 +77,7 @@ export default Preset.standard({
|
|
|
77
77
|
| Handler | Files | Description |
|
|
78
78
|
| ------- | ----- | ----------- |
|
|
79
79
|
| `PackageJson` | `**/package.json` | Sort and format with Biome |
|
|
80
|
-
| `Biome` | `*.{js,ts,jsx,tsx,json}` | Format and lint |
|
|
80
|
+
| `Biome` | `*.{js,ts,jsx,tsx,json,jsonc}` | Format and lint |
|
|
81
81
|
| `Markdown` | `**/*.{md,mdx}` | Lint with markdownlint-cli2 |
|
|
82
82
|
| `Yaml` | `**/*.{yml,yaml}` | Format and validate |
|
|
83
83
|
| `PnpmWorkspace` | `pnpm-workspace.yaml` | Sort and format |
|
package/index.d.ts
CHANGED
|
@@ -77,9 +77,10 @@ export declare class Biome {
|
|
|
77
77
|
static readonly glob = "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}";
|
|
78
78
|
/**
|
|
79
79
|
* Default patterns to exclude from processing.
|
|
80
|
-
*
|
|
80
|
+
* Excludes package.json since PackageJson handler processes those files.
|
|
81
|
+
* @defaultValue `['package.json', 'package-lock.json', '__fixtures__']`
|
|
81
82
|
*/
|
|
82
|
-
static readonly defaultExcludes: readonly ["package-lock.json", "__fixtures__"];
|
|
83
|
+
static readonly defaultExcludes: readonly ["package.json", "package-lock.json", "__fixtures__"];
|
|
83
84
|
/**
|
|
84
85
|
* Pre-configured handler with default options.
|
|
85
86
|
* Auto-discovers biome command and config file location.
|
|
@@ -534,7 +535,7 @@ export declare type ExportsField = string | Record<string, unknown> | null;
|
|
|
534
535
|
*
|
|
535
536
|
* const handler = (filenames: readonly string[]) => {
|
|
536
537
|
* const filtered = Filter.exclude(filenames, ['dist/', '__fixtures__']);
|
|
537
|
-
* return filtered.length > 0 ? `biome check ${
|
|
538
|
+
* return filtered.length > 0 ? `biome check ${Filter.shellEscape(filtered)}` : [];
|
|
538
539
|
* };
|
|
539
540
|
* ```
|
|
540
541
|
*/
|
|
@@ -593,6 +594,23 @@ export declare class Filter {
|
|
|
593
594
|
include?: readonly string[];
|
|
594
595
|
exclude?: readonly string[];
|
|
595
596
|
}): string[];
|
|
597
|
+
/**
|
|
598
|
+
* Escape file paths for safe shell command construction.
|
|
599
|
+
*
|
|
600
|
+
* Wraps each path in single quotes and escapes any embedded single quotes.
|
|
601
|
+
* This prevents issues with paths containing spaces or special characters.
|
|
602
|
+
*
|
|
603
|
+
* @param filenames - Array of file paths
|
|
604
|
+
* @returns Space-separated string of shell-escaped paths
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```typescript
|
|
608
|
+
* const files = ['/path/to/file.ts', '/path/with spaces/file.ts'];
|
|
609
|
+
* const escaped = Filter.shellEscape(files);
|
|
610
|
+
* // Result: "'/path/to/file.ts' '/path/with spaces/file.ts'"
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
static shellEscape(filenames: readonly string[]): string;
|
|
596
614
|
}
|
|
597
615
|
|
|
598
616
|
/**
|
|
@@ -788,7 +806,7 @@ export declare interface ImportGraphResult {
|
|
|
788
806
|
export declare const initCommand: Command_2.Command<"init", FileSystem.FileSystem, Error | PlatformError, {
|
|
789
807
|
readonly force: boolean;
|
|
790
808
|
readonly config: string;
|
|
791
|
-
readonly preset: "
|
|
809
|
+
readonly preset: "minimal" | "silk" | "standard";
|
|
792
810
|
}>;
|
|
793
811
|
|
|
794
812
|
/**
|
|
@@ -1151,7 +1169,7 @@ export declare class Preset {
|
|
|
1151
1169
|
*/
|
|
1152
1170
|
static standard(extend?: PresetExtendOptions): LintStagedConfig;
|
|
1153
1171
|
/**
|
|
1154
|
-
*
|
|
1172
|
+
* Silk preset: all handlers enabled.
|
|
1155
1173
|
*
|
|
1156
1174
|
* Includes:
|
|
1157
1175
|
* - PackageJson (sort + format)
|
|
@@ -1169,16 +1187,16 @@ export declare class Preset {
|
|
|
1169
1187
|
* ```typescript
|
|
1170
1188
|
* import { Preset } from '@savvy-web/lint-staged';
|
|
1171
1189
|
*
|
|
1172
|
-
* export default Preset.
|
|
1190
|
+
* export default Preset.silk({
|
|
1173
1191
|
* typescript: { skipTypecheck: true },
|
|
1174
1192
|
* });
|
|
1175
1193
|
* ```
|
|
1176
1194
|
*/
|
|
1177
|
-
static
|
|
1195
|
+
static silk(extend?: PresetExtendOptions): LintStagedConfig;
|
|
1178
1196
|
/**
|
|
1179
1197
|
* Get a preset by name.
|
|
1180
1198
|
*
|
|
1181
|
-
* @param name - The preset name: 'minimal', 'standard', or '
|
|
1199
|
+
* @param name - The preset name: 'minimal', 'standard', or 'silk'
|
|
1182
1200
|
* @param extend - Options to customize or extend the preset
|
|
1183
1201
|
* @returns A lint-staged configuration object
|
|
1184
1202
|
*
|
|
@@ -1190,7 +1208,7 @@ export declare class Preset {
|
|
|
1190
1208
|
* export default Preset.get(presetName);
|
|
1191
1209
|
* ```
|
|
1192
1210
|
*/
|
|
1193
|
-
static get(name: "minimal" | "standard" | "
|
|
1211
|
+
static get(name: "minimal" | "standard" | "silk", extend?: PresetExtendOptions): LintStagedConfig;
|
|
1194
1212
|
}
|
|
1195
1213
|
|
|
1196
1214
|
/**
|
|
@@ -1202,14 +1220,14 @@ export declare type PresetExtendOptions = CreateConfigOptions;
|
|
|
1202
1220
|
/**
|
|
1203
1221
|
* Preset type for standard configurations.
|
|
1204
1222
|
*/
|
|
1205
|
-
export declare type PresetType = "minimal" | "standard" | "
|
|
1223
|
+
export declare type PresetType = "minimal" | "standard" | "silk";
|
|
1206
1224
|
|
|
1207
1225
|
/** Root command for the CLI with all subcommands. */
|
|
1208
1226
|
export declare const rootCommand: Command_2.Command<"savvy-lint", FileSystem_2, Error | PlatformError, {
|
|
1209
1227
|
readonly subcommand: Option< {
|
|
1210
1228
|
readonly force: boolean;
|
|
1211
1229
|
readonly config: string;
|
|
1212
|
-
readonly preset: "
|
|
1230
|
+
readonly preset: "minimal" | "silk" | "standard";
|
|
1213
1231
|
} | {
|
|
1214
1232
|
readonly quiet: boolean;
|
|
1215
1233
|
}>;
|
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import sort_package_json from "sort-package-json";
|
|
1
2
|
import { parse, stringify } from "yaml";
|
|
2
|
-
import { Biome,
|
|
3
|
+
import { Biome, Markdown, readFileSync, TypeScript, writeFileSync, existsSync, Filter } from "./376.js";
|
|
3
4
|
class PackageJson {
|
|
4
5
|
static glob = "**/package.json";
|
|
5
6
|
static defaultExcludes = [
|
|
@@ -15,21 +16,14 @@ class PackageJson {
|
|
|
15
16
|
return (filenames)=>{
|
|
16
17
|
const filtered = Filter.exclude(filenames, excludes);
|
|
17
18
|
if (0 === filtered.length) return [];
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const prefix = Command_Command.getExecPrefix(pm);
|
|
23
|
-
const sortCmd = [
|
|
24
|
-
...prefix,
|
|
25
|
-
"sort-package-json",
|
|
26
|
-
files
|
|
27
|
-
].join(" ");
|
|
28
|
-
commands.push(sortCmd);
|
|
19
|
+
if (!skipSort) for (const filepath of filtered){
|
|
20
|
+
const content = readFileSync(filepath, "utf-8");
|
|
21
|
+
const sorted = sort_package_json(content);
|
|
22
|
+
if (sorted !== content) writeFileSync(filepath, sorted, "utf-8");
|
|
29
23
|
}
|
|
24
|
+
const files = Filter.shellEscape(filtered);
|
|
30
25
|
const biomeCmd = options.biomeConfig ? `biome check --write --max-diagnostics=none --config-path=${options.biomeConfig} ${files}` : `biome check --write --max-diagnostics=none ${files}`;
|
|
31
|
-
|
|
32
|
-
return commands.join(" && ");
|
|
26
|
+
return `${biomeCmd} && git add ${files}`;
|
|
33
27
|
};
|
|
34
28
|
}
|
|
35
29
|
}
|
|
@@ -147,7 +141,7 @@ class Yaml {
|
|
|
147
141
|
} catch (error) {
|
|
148
142
|
throw new Error(`Invalid YAML in ${filepath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
149
143
|
}
|
|
150
|
-
if (!skipFormat && filtered.length > 0) return `git add ${
|
|
144
|
+
if (!skipFormat && filtered.length > 0) return `git add ${Filter.shellEscape(filtered)}`;
|
|
151
145
|
return [];
|
|
152
146
|
};
|
|
153
147
|
}
|
|
@@ -212,7 +206,7 @@ class Preset {
|
|
|
212
206
|
if (void 0 !== extend.custom) options.custom = extend.custom;
|
|
213
207
|
return createConfig(options);
|
|
214
208
|
}
|
|
215
|
-
static
|
|
209
|
+
static silk(extend = {}) {
|
|
216
210
|
const options = {
|
|
217
211
|
packageJson: extend.packageJson ?? {},
|
|
218
212
|
biome: extend.biome ?? {},
|
|
@@ -229,8 +223,8 @@ class Preset {
|
|
|
229
223
|
switch(name){
|
|
230
224
|
case "minimal":
|
|
231
225
|
return Preset.minimal(extend);
|
|
232
|
-
case "
|
|
233
|
-
return Preset.
|
|
226
|
+
case "silk":
|
|
227
|
+
return Preset.silk(extend);
|
|
234
228
|
default:
|
|
235
229
|
return Preset.standard(extend);
|
|
236
230
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/lint-staged",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Composable, configurable lint-staged handlers for pre-commit hooks. Provides reusable handlers for Biome, Markdown, YAML, TypeScript, and more.",
|
|
6
6
|
"keywords": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/savvy-web/lint-staged.git"
|
|
23
|
+
"url": "git+https://github.com/savvy-web/lint-staged.git"
|
|
24
24
|
},
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"author": {
|