@savvy-web/silk-effects 0.6.1 → 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.
Files changed (155) hide show
  1. package/README.md +48 -17
  2. package/_virtual/_rolldown/runtime.js +18 -0
  3. package/changesets/api/categories.js +247 -0
  4. package/changesets/api/changelog.js +134 -0
  5. package/changesets/api/dependency-table.js +163 -0
  6. package/changesets/api/linter.js +168 -0
  7. package/changesets/api/transformer.js +140 -0
  8. package/changesets/categories/index.js +299 -0
  9. package/changesets/categories/types.js +66 -0
  10. package/changesets/changelog/formatting.js +119 -0
  11. package/changesets/changelog/getDependencyReleaseLine.js +114 -0
  12. package/changesets/changelog/getReleaseLine.js +122 -0
  13. package/changesets/changelog/index.js +99 -0
  14. package/changesets/constants.js +43 -0
  15. package/changesets/errors.js +305 -0
  16. package/changesets/index.js +146 -0
  17. package/changesets/markdownlint/index.js +29 -0
  18. package/changesets/markdownlint/rules/content-structure.js +98 -0
  19. package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
  20. package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
  21. package/changesets/markdownlint/rules/required-sections.js +54 -0
  22. package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
  23. package/changesets/markdownlint/rules/utils.js +30 -0
  24. package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
  25. package/changesets/remark/plugins/contributor-footnotes.js +123 -0
  26. package/changesets/remark/plugins/deduplicate-items.js +30 -0
  27. package/changesets/remark/plugins/issue-link-refs.js +58 -0
  28. package/changesets/remark/plugins/merge-sections.js +43 -0
  29. package/changesets/remark/plugins/normalize-format.js +47 -0
  30. package/changesets/remark/plugins/reorder-sections.js +34 -0
  31. package/changesets/remark/presets.js +119 -0
  32. package/changesets/remark/rules/content-structure.js +22 -0
  33. package/changesets/remark/rules/dependency-table-format.js +40 -0
  34. package/changesets/remark/rules/heading-hierarchy.js +19 -0
  35. package/changesets/remark/rules/required-sections.js +17 -0
  36. package/changesets/remark/rules/uncategorized-content.js +31 -0
  37. package/changesets/schemas/changeset.js +146 -0
  38. package/changesets/schemas/dependency-table.js +189 -0
  39. package/changesets/schemas/git.js +69 -0
  40. package/changesets/schemas/github.js +175 -0
  41. package/changesets/schemas/options.js +182 -0
  42. package/changesets/schemas/package-scope.js +128 -0
  43. package/changesets/schemas/primitives.js +72 -0
  44. package/changesets/schemas/version-files.js +151 -0
  45. package/changesets/services/branch-analyzer.js +278 -0
  46. package/changesets/services/changelog.js +50 -0
  47. package/changesets/services/config-inspector.js +390 -0
  48. package/changesets/services/github.js +178 -0
  49. package/changesets/services/markdown.js +106 -0
  50. package/changesets/services/workspace-snapshot.js +182 -0
  51. package/changesets/utils/commit-parser.js +80 -0
  52. package/changesets/utils/dep-diff.js +77 -0
  53. package/changesets/utils/dependency-table.js +347 -0
  54. package/changesets/utils/issue-refs.js +101 -0
  55. package/changesets/utils/jsonpath.js +175 -0
  56. package/changesets/utils/logger.js +50 -0
  57. package/changesets/utils/markdown-link.js +57 -0
  58. package/changesets/utils/publishability.js +39 -0
  59. package/changesets/utils/remark-pipeline.js +79 -0
  60. package/changesets/utils/section-parser.js +94 -0
  61. package/changesets/utils/strip-frontmatter.js +46 -0
  62. package/changesets/utils/version-blocks.js +108 -0
  63. package/changesets/utils/version-files.js +336 -0
  64. package/changesets/utils/worktree-snapshot.js +142 -0
  65. package/changesets/vendor/github-info.js +55 -0
  66. package/commitlint/config/factory.js +69 -0
  67. package/commitlint/config/plugins.js +227 -0
  68. package/commitlint/config/rules.js +155 -0
  69. package/commitlint/config/schema.js +46 -0
  70. package/commitlint/detection/dco.js +53 -0
  71. package/commitlint/detection/scopes.js +45 -0
  72. package/commitlint/formatter/format.js +85 -0
  73. package/commitlint/formatter/messages.js +79 -0
  74. package/commitlint/hook/diagnostics/branch.js +36 -0
  75. package/commitlint/hook/diagnostics/cache.js +37 -0
  76. package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
  77. package/commitlint/hook/diagnostics/open-issues.js +56 -0
  78. package/commitlint/hook/diagnostics/package-manager.js +51 -0
  79. package/commitlint/hook/diagnostics/signing.js +107 -0
  80. package/commitlint/hook/envelope.js +46 -0
  81. package/commitlint/hook/output.js +45 -0
  82. package/commitlint/hook/parse-bash-command.js +105 -0
  83. package/commitlint/hook/rules/closes-trailer.js +31 -0
  84. package/commitlint/hook/rules/forbidden-content.js +32 -0
  85. package/commitlint/hook/rules/plan-leakage.js +36 -0
  86. package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
  87. package/commitlint/hook/rules/soft-wrap.js +37 -0
  88. package/commitlint/hook/rules/types.js +14 -0
  89. package/commitlint/hook/rules/verbosity.js +31 -0
  90. package/commitlint/hook/silence-logger.js +39 -0
  91. package/commitlint/index.js +146 -0
  92. package/commitlint/prompt/config.js +91 -0
  93. package/commitlint/prompt/emojis.js +74 -0
  94. package/commitlint/prompt/prompter.js +135 -0
  95. package/commitlint/static.js +73 -0
  96. package/errors/BiomeSyncError.js +21 -0
  97. package/errors/ChangesetConfigError.js +20 -0
  98. package/errors/ConfigNotFoundError.js +21 -0
  99. package/errors/SectionParseError.js +16 -0
  100. package/errors/SectionValidationError.js +16 -0
  101. package/errors/SectionWriteError.js +16 -0
  102. package/errors/TagFormatError.js +20 -0
  103. package/errors/ToolNotFoundError.js +11 -0
  104. package/errors/ToolResolutionError.js +11 -0
  105. package/errors/ToolVersionMismatchError.js +11 -0
  106. package/errors/VersioningDetectionError.js +20 -0
  107. package/errors/WorkspaceAnalysisError.js +21 -0
  108. package/index.d.ts +9743 -8380
  109. package/index.js +36 -6657
  110. package/lint/Handler.js +39 -0
  111. package/lint/cli/sections.js +65 -0
  112. package/lint/cli/templates/markdownlint.gen.js +183 -0
  113. package/lint/config/Preset.js +152 -0
  114. package/lint/config/createConfig.js +89 -0
  115. package/lint/handlers/Biome.js +179 -0
  116. package/lint/handlers/Markdown.js +139 -0
  117. package/lint/handlers/PackageJson.js +130 -0
  118. package/lint/handlers/PnpmWorkspace.js +141 -0
  119. package/lint/handlers/ShellScripts.js +58 -0
  120. package/lint/handlers/TypeScript.js +134 -0
  121. package/lint/handlers/Yaml.js +167 -0
  122. package/lint/index.js +52 -0
  123. package/lint/utils/Command.js +285 -0
  124. package/lint/utils/Filter.js +100 -0
  125. package/lint/utils/Workspace.js +86 -0
  126. package/package.json +52 -63
  127. package/schemas/CommentStyle.js +16 -0
  128. package/schemas/ResolvedTool.js +63 -0
  129. package/schemas/SavvySections.js +113 -0
  130. package/schemas/SectionBlock.js +70 -0
  131. package/schemas/SectionDefinition.js +121 -0
  132. package/schemas/SectionResults.js +12 -0
  133. package/schemas/TagStrategySchemas.js +18 -0
  134. package/schemas/ToolDefinition.js +39 -0
  135. package/schemas/ToolResults.js +14 -0
  136. package/schemas/VersioningSchemas.js +95 -0
  137. package/schemas/WorkspaceAnalysisSchemas.js +190 -0
  138. package/services/BiomeSchemaSync.js +133 -0
  139. package/services/ChangesetConfig.js +78 -0
  140. package/services/ChangesetConfigReader.js +106 -0
  141. package/services/ConfigDiscovery.js +71 -0
  142. package/services/ManagedSection.js +288 -0
  143. package/services/SilkPublishability.js +193 -0
  144. package/services/SilkWorkspaceAnalyzer.js +213 -0
  145. package/services/TagStrategy.js +54 -0
  146. package/services/ToolDiscovery.js +229 -0
  147. package/services/VersioningStrategy.js +67 -0
  148. package/tsdoc-metadata.json +11 -11
  149. package/turbo/digest.js +127 -0
  150. package/turbo/errors.js +48 -0
  151. package/turbo/index.js +32 -0
  152. package/turbo/schemas/DryRun.js +57 -0
  153. package/turbo/schemas/results.js +61 -0
  154. package/turbo/services/TurboInspector.js +100 -0
  155. package/utils/ToolCommand.js +40 -0
@@ -0,0 +1,139 @@
1
+ import { Command } from "../utils/Command.js";
2
+ import { Filter } from "../utils/Filter.js";
3
+ import { getWorkspaceRoot } from "../utils/Workspace.js";
4
+ import { existsSync } from "node:fs";
5
+ import { join } from "node:path";
6
+
7
+ //#region src/lint/handlers/Markdown.ts
8
+ /**
9
+ * Handler for Markdown files.
10
+ *
11
+ * Lints and auto-fixes with markdownlint-cli2.
12
+ */
13
+ /**
14
+ * Handler for Markdown files.
15
+ *
16
+ * Lints and auto-fixes with markdownlint-cli2.
17
+ *
18
+ * Tool discovery order:
19
+ * 1. Global `markdownlint-cli2` command (preferred)
20
+ * 2. Local installation via `pnpm exec markdownlint-cli2`
21
+ * 3. Local installation via `npx markdownlint-cli2`
22
+ *
23
+ * Config file discovery order:
24
+ * 1. Explicit `config` option if provided
25
+ * 2. `lib/configs/.markdownlint-cli2.jsonc` (and variants)
26
+ * 3. Standard locations (`.markdownlint-cli2.jsonc` at repo root, etc.)
27
+ *
28
+ * @throws Error if markdownlint-cli2 is not available (globally or locally)
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { Markdown } from '@savvy-web/lint-staged';
33
+ *
34
+ * export default {
35
+ * // Auto-discovers command and config file
36
+ * [Markdown.glob]: Markdown.handler,
37
+ *
38
+ * // Or explicit config path
39
+ * [Markdown.glob]: Markdown.create({
40
+ * config: './config/.markdownlint.jsonc',
41
+ * }),
42
+ * };
43
+ * ```
44
+ */
45
+ var Markdown = class Markdown {
46
+ /**
47
+ * Glob pattern for matching Markdown files.
48
+ * @defaultValue `'**\/*.{md,mdx}'`
49
+ */
50
+ static glob = "**/*.{md,mdx}";
51
+ /**
52
+ * Default patterns to exclude from processing.
53
+ * @defaultValue `[]`
54
+ */
55
+ static defaultExcludes = [];
56
+ /**
57
+ * Pre-configured handler with default options.
58
+ * Auto-discovers command and config file location.
59
+ */
60
+ static handler = Markdown.create();
61
+ /**
62
+ * Find the markdownlint-cli2 executable.
63
+ *
64
+ * Searches in order:
65
+ * 1. Global `markdownlint-cli2` command
66
+ * 2. `pnpm exec markdownlint-cli2`
67
+ * 3. `npx markdownlint-cli2`
68
+ *
69
+ * @returns The command to run markdownlint-cli2, or undefined if not found
70
+ */
71
+ static findMarkdownlint() {
72
+ return Command.findTool("markdownlint-cli2").command;
73
+ }
74
+ /**
75
+ * Check if markdownlint-cli2 is available.
76
+ *
77
+ * @returns `true` if markdownlint-cli2 is available globally or locally
78
+ */
79
+ static isAvailable() {
80
+ return Command.findTool("markdownlint-cli2").available;
81
+ }
82
+ /**
83
+ * Find the markdownlint config file.
84
+ *
85
+ * Paths are anchored to the workspace root (via {@link getWorkspaceRoot}),
86
+ * falling back to `process.cwd()` when not inside a workspace.
87
+ *
88
+ * Searches in order:
89
+ * 1. `{workspaceRoot}/lib/configs/` directory
90
+ * 2. `{workspaceRoot}/` (repo root)
91
+ *
92
+ * @returns The config file path, or undefined if not found
93
+ */
94
+ static findConfig() {
95
+ const root = getWorkspaceRoot() ?? process.cwd();
96
+ for (const name of [
97
+ ".markdownlint-cli2.jsonc",
98
+ ".markdownlint-cli2.json",
99
+ ".markdownlint-cli2.yaml",
100
+ ".markdownlint-cli2.cjs",
101
+ ".markdownlint.jsonc",
102
+ ".markdownlint.json",
103
+ ".markdownlint.yaml"
104
+ ]) {
105
+ const libPath = join(root, "lib/configs", name);
106
+ if (existsSync(libPath)) return libPath;
107
+ const rootPath = join(root, name);
108
+ if (existsSync(rootPath)) return rootPath;
109
+ }
110
+ }
111
+ /**
112
+ * Create a handler with custom options.
113
+ *
114
+ * @param options - Configuration options
115
+ * @returns A lint-staged compatible handler function
116
+ * @throws Error if markdownlint-cli2 is not available when handler is invoked
117
+ */
118
+ static create(options = {}) {
119
+ const excludes = options.exclude ?? [...Markdown.defaultExcludes];
120
+ const noFix = options.noFix ?? false;
121
+ const config = options.config ?? Markdown.findConfig();
122
+ return (filenames) => {
123
+ const filtered = Filter.exclude(filenames, excludes);
124
+ if (filtered.length === 0) return [];
125
+ const mdlintCmd = Command.requireTool("markdownlint-cli2", "markdownlint-cli2 is not available. Install it globally or add it as a dev dependency.");
126
+ const files = Filter.shellEscape(filtered);
127
+ const fixFlag = noFix ? "" : "--fix";
128
+ return [
129
+ mdlintCmd,
130
+ config ? `--config '${config}'` : "",
131
+ fixFlag,
132
+ files
133
+ ].filter(Boolean).join(" ");
134
+ };
135
+ }
136
+ };
137
+
138
+ //#endregion
139
+ export { Markdown };
@@ -0,0 +1,130 @@
1
+ import { Command } from "../utils/Command.js";
2
+ import { Filter } from "../utils/Filter.js";
3
+ import { isWorkspacePackagePath } from "../utils/Workspace.js";
4
+ import { readFileSync, writeFileSync } from "node:fs";
5
+ import sortPackageJson from "sort-package-json";
6
+
7
+ //#region src/lint/handlers/PackageJson.ts
8
+ /**
9
+ * Handler for package.json files.
10
+ *
11
+ * Sorts fields with sort-package-json and formats with Biome.
12
+ */
13
+ /**
14
+ * Handler for package.json files.
15
+ *
16
+ * Sorts fields with sort-package-json and formats with Biome.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { PackageJson } from '@savvy-web/lint-staged';
21
+ *
22
+ * export default {
23
+ * // Use defaults
24
+ * [PackageJson.glob]: PackageJson.handler,
25
+ *
26
+ * // Or customize
27
+ * [PackageJson.glob]: PackageJson.create({
28
+ * exclude: ['packages/legacy/package.json'],
29
+ * skipSort: true,
30
+ * }),
31
+ * };
32
+ * ```
33
+ */
34
+ var PackageJson = class PackageJson {
35
+ /**
36
+ * Glob pattern for matching package.json files.
37
+ * @defaultValue `'**\/package.json'`
38
+ */
39
+ static glob = "**/package.json";
40
+ /**
41
+ * Default patterns to exclude from processing.
42
+ * @defaultValue `['dist/package.json', '__fixtures__']`
43
+ */
44
+ static defaultExcludes = ["dist/package.json", "__fixtures__"];
45
+ /**
46
+ * Pre-configured handler with default options.
47
+ */
48
+ static handler = PackageJson.create();
49
+ /**
50
+ * Filter filenames to workspace roots only.
51
+ *
52
+ * @remarks
53
+ * Applies exclude patterns first (for backward compatibility with custom
54
+ * excludes), then filters to workspace root and leaf workspace roots only.
55
+ * Falls back permissively when not in a workspace.
56
+ *
57
+ * @param filenames - Incoming file list from lint-staged
58
+ * @param excludes - Patterns to exclude before workspace filtering
59
+ * @returns Filtered list of files at workspace roots
60
+ */
61
+ static filterToWorkspaceRoots(filenames, excludes) {
62
+ return Filter.exclude(filenames, [...excludes]).filter((f) => isWorkspacePackagePath(f));
63
+ }
64
+ /**
65
+ * Create a handler with custom options.
66
+ *
67
+ * @param options - Configuration options
68
+ * @returns A lint-staged compatible handler function
69
+ */
70
+ /**
71
+ * Create a handler that returns a CLI command to sort package.json files.
72
+ *
73
+ * @remarks
74
+ * Unlike {@link create}, this does not modify files in the handler function
75
+ * body. Instead it returns a `savvy-lint fmt package-json` command so
76
+ * lint-staged can detect the modification and auto-stage it.
77
+ * Use this in lint-staged array syntax for sequential execution.
78
+ *
79
+ * @param options - Configuration options
80
+ * @returns A lint-staged compatible handler function
81
+ */
82
+ static fmtCommand(options = {}) {
83
+ const excludes = options.exclude ?? [...PackageJson.defaultExcludes];
84
+ return (filenames) => {
85
+ const filtered = PackageJson.filterToWorkspaceRoots(filenames, excludes);
86
+ if (filtered.length === 0) return [];
87
+ return `${Command.findSavvyLint()} fmt package-json ${Filter.shellEscape(filtered)}`;
88
+ };
89
+ }
90
+ /**
91
+ * Sort the keys of a package.json document string.
92
+ *
93
+ * @remarks
94
+ * Pure transform over the file contents using `sort-package-json`. Used by
95
+ * the `savvy lint fmt package-json` subcommand so the CLI does not depend on
96
+ * `sort-package-json` directly.
97
+ *
98
+ * @param content - The raw package.json file contents
99
+ * @returns The sorted package.json file contents
100
+ */
101
+ static sortContent(content) {
102
+ return sortPackageJson(content);
103
+ }
104
+ /**
105
+ * Create a handler with custom options.
106
+ *
107
+ * @param options - Configuration options
108
+ * @returns A lint-staged compatible handler function
109
+ */
110
+ static create(options = {}) {
111
+ const excludes = options.exclude ?? [...PackageJson.defaultExcludes];
112
+ const skipSort = options.skipSort ?? false;
113
+ const skipFormat = options.skipFormat ?? false;
114
+ return (filenames) => {
115
+ const filtered = PackageJson.filterToWorkspaceRoots(filenames, excludes);
116
+ if (filtered.length === 0) return [];
117
+ if (!skipSort) for (const filepath of filtered) {
118
+ const content = readFileSync(filepath, "utf-8");
119
+ const sorted = sortPackageJson(content);
120
+ if (sorted !== content) writeFileSync(filepath, sorted, "utf-8");
121
+ }
122
+ if (skipFormat) return [];
123
+ const files = Filter.shellEscape(filtered);
124
+ return options.biomeConfig ? `biome check --write --max-diagnostics=none --config-path=${options.biomeConfig} ${files}` : `biome check --write --max-diagnostics=none ${files}`;
125
+ };
126
+ }
127
+ };
128
+
129
+ //#endregion
130
+ export { PackageJson };
@@ -0,0 +1,141 @@
1
+ import { Command } from "../utils/Command.js";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { parse, stringify } from "yaml";
4
+
5
+ //#region src/lint/handlers/PnpmWorkspace.ts
6
+ /**
7
+ * Handler for pnpm-workspace.yaml.
8
+ *
9
+ * Sorts, formats, and validates pnpm-workspace.yaml using bundled libraries.
10
+ */
11
+ /**
12
+ * Default YAML stringify options for consistent formatting.
13
+ */
14
+ const DEFAULT_STRINGIFY_OPTIONS = {
15
+ indent: 2,
16
+ lineWidth: 0,
17
+ singleQuote: false
18
+ };
19
+ /**
20
+ * Handler for pnpm-workspace.yaml.
21
+ *
22
+ * Sorts, formats, and validates pnpm-workspace.yaml using bundled libraries.
23
+ *
24
+ * @remarks
25
+ * This handler processes the workspace file entirely in JavaScript:
26
+ * - Sorts the `packages` array alphabetically (if present)
27
+ * - Sorts `onlyBuiltDependencies` and `publicHoistPattern` arrays (if present)
28
+ * - Sorts all top-level keys alphabetically, keeping `packages` first
29
+ * - Formats the output with consistent YAML styling
30
+ * - Validates the YAML structure
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { PnpmWorkspace } from '\@savvy-web/lint-staged';
35
+ *
36
+ * export default {
37
+ * [PnpmWorkspace.glob]: PnpmWorkspace.create({
38
+ * skipSort: true, // Skip sorting
39
+ * }),
40
+ * };
41
+ * ```
42
+ */
43
+ var PnpmWorkspace = class PnpmWorkspace {
44
+ /**
45
+ * Glob pattern for matching pnpm-workspace.yaml.
46
+ * @defaultValue `'pnpm-workspace.yaml'`
47
+ */
48
+ static glob = "pnpm-workspace.yaml";
49
+ /**
50
+ * Default patterns to exclude (none, since this is a single file).
51
+ * @defaultValue `[]`
52
+ */
53
+ static defaultExcludes = [];
54
+ /**
55
+ * Pre-configured handler with default options.
56
+ */
57
+ static handler = PnpmWorkspace.create();
58
+ /**
59
+ * Keys whose array values should be sorted alphabetically.
60
+ */
61
+ static SORTABLE_ARRAY_KEYS = new Set([
62
+ "packages",
63
+ "onlyBuiltDependencies",
64
+ "publicHoistPattern"
65
+ ]);
66
+ /**
67
+ * Sort the pnpm-workspace.yaml content.
68
+ *
69
+ * Sorts:
70
+ * - `packages` array alphabetically
71
+ * - `onlyBuiltDependencies` array (if present)
72
+ * - `publicHoistPattern` array (if present)
73
+ * - All keys alphabetically, keeping `packages` first
74
+ *
75
+ * @param content - Parsed pnpm-workspace.yaml content
76
+ * @returns Sorted content
77
+ */
78
+ static sortContent(content) {
79
+ const result = {};
80
+ const keys = Object.keys(content).sort((a, b) => {
81
+ if (a === "packages") return -1;
82
+ if (b === "packages") return 1;
83
+ return a.localeCompare(b);
84
+ });
85
+ for (const key of keys) {
86
+ const value = content[key];
87
+ if (PnpmWorkspace.SORTABLE_ARRAY_KEYS.has(key) && Array.isArray(value)) result[key] = [...value].sort();
88
+ else result[key] = value;
89
+ }
90
+ return result;
91
+ }
92
+ /**
93
+ * Create a handler that returns a CLI command to sort/format pnpm-workspace.yaml.
94
+ *
95
+ * @remarks
96
+ * Unlike {@link create}, this does not modify files in the handler function
97
+ * body. Instead it returns a `savvy-lint fmt pnpm-workspace` command so
98
+ * lint-staged can detect the modification and auto-stage it.
99
+ * Use this in lint-staged array syntax for sequential execution.
100
+ *
101
+ * @returns A lint-staged compatible handler function
102
+ */
103
+ static fmtCommand() {
104
+ return () => {
105
+ if (!existsSync("pnpm-workspace.yaml")) return [];
106
+ return `${Command.findSavvyLint()} fmt pnpm-workspace`;
107
+ };
108
+ }
109
+ /**
110
+ * Create a handler with custom options.
111
+ *
112
+ * @param options - Configuration options
113
+ * @returns A lint-staged compatible handler function
114
+ */
115
+ static create(options = {}) {
116
+ const skipSort = options.skipSort ?? false;
117
+ const skipFormat = options.skipFormat ?? false;
118
+ const skipLint = options.skipLint ?? false;
119
+ return () => {
120
+ const filepath = "pnpm-workspace.yaml";
121
+ if (!existsSync(filepath)) return [];
122
+ const content = readFileSync(filepath, "utf-8");
123
+ let parsed;
124
+ try {
125
+ parsed = parse(content);
126
+ } catch (error) {
127
+ if (!skipLint) throw new Error(`Invalid YAML in ${filepath}: ${error instanceof Error ? error.message : String(error)}`);
128
+ return [];
129
+ }
130
+ if (!skipSort) parsed = PnpmWorkspace.sortContent(parsed);
131
+ if (!skipSort || !skipFormat) {
132
+ writeFileSync(filepath, stringify(parsed, DEFAULT_STRINGIFY_OPTIONS), "utf-8");
133
+ return [];
134
+ }
135
+ return [];
136
+ };
137
+ }
138
+ };
139
+
140
+ //#endregion
141
+ export { PnpmWorkspace };
@@ -0,0 +1,58 @@
1
+ import { Filter } from "../utils/Filter.js";
2
+
3
+ //#region src/lint/handlers/ShellScripts.ts
4
+ /**
5
+ * Handler for shell script files.
6
+ *
7
+ * Removes executable bit by default (security best practice).
8
+ *
9
+ * @remarks
10
+ * By default, excludes `.claude/scripts/` which need to remain executable
11
+ * for lint-staged hooks to work.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { ShellScripts } from '@savvy-web/lint-staged';
16
+ *
17
+ * export default {
18
+ * [ShellScripts.glob]: ShellScripts.create({
19
+ * exclude: ['.claude/scripts/', 'bin/'],
20
+ * }),
21
+ * };
22
+ * ```
23
+ */
24
+ var ShellScripts = class ShellScripts {
25
+ /**
26
+ * Glob pattern for matching shell script files.
27
+ * @defaultValue `'**\/*.sh'`
28
+ */
29
+ static glob = "**/*.sh";
30
+ /**
31
+ * Default patterns to exclude from processing.
32
+ * @defaultValue `['.claude/scripts/']`
33
+ */
34
+ static defaultExcludes = [".claude/scripts/"];
35
+ /**
36
+ * Pre-configured handler with default options.
37
+ */
38
+ static handler = ShellScripts.create();
39
+ /**
40
+ * Create a handler with custom options.
41
+ *
42
+ * @param options - Configuration options
43
+ * @returns A lint-staged compatible handler function
44
+ */
45
+ static create(options = {}) {
46
+ const excludes = options.exclude ?? [...ShellScripts.defaultExcludes];
47
+ const makeExecutable = options.makeExecutable ?? false;
48
+ return (filenames) => {
49
+ const filtered = Filter.exclude(filenames, excludes);
50
+ if (filtered.length === 0) return [];
51
+ const chmodFlag = makeExecutable ? "+x" : "-x";
52
+ return filtered.map((file) => `chmod ${chmodFlag} ${file}`);
53
+ };
54
+ }
55
+ };
56
+
57
+ //#endregion
58
+ export { ShellScripts };
@@ -0,0 +1,134 @@
1
+ import { Command } from "../utils/Command.js";
2
+ import { Filter } from "../utils/Filter.js";
3
+
4
+ //#region src/lint/handlers/TypeScript.ts
5
+ /**
6
+ * Handler for TypeScript files.
7
+ *
8
+ * Runs type checking with tsgo or tsc.
9
+ *
10
+ * @remarks
11
+ * Type checking runs on all staged TypeScript files using the configured
12
+ * compiler (tsgo or tsc). The compiler is auto-detected at runtime using
13
+ * `Command.findTool()`, which correctly handles pnpm catalogs, peer
14
+ * dependencies, and hoisted/transitive deps.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { TypeScript } from '\@savvy-web/lint-staged';
19
+ *
20
+ * export default {
21
+ * // Auto-detects compiler and runs type checking
22
+ * [TypeScript.glob]: TypeScript.handler,
23
+ *
24
+ * // Or explicit config
25
+ * [TypeScript.glob]: TypeScript.create({
26
+ * skipTypecheck: true,
27
+ * }),
28
+ * };
29
+ * ```
30
+ */
31
+ var TypeScript = class TypeScript {
32
+ /**
33
+ * Glob pattern for matching TypeScript files.
34
+ * @defaultValue `'*.{ts,cts,mts,tsx}'`
35
+ */
36
+ static glob = "*.{ts,cts,mts,tsx}";
37
+ /**
38
+ * Default patterns to exclude from processing.
39
+ * @defaultValue `[]`
40
+ */
41
+ static defaultExcludes = [];
42
+ /** Cached compiler detection result */
43
+ static cachedCompilerResult = null;
44
+ /**
45
+ * Detect which TypeScript compiler to use.
46
+ *
47
+ * Uses `Command.findTool()` to check for available compilers:
48
+ * 1. `tsgo` (native TypeScript) — checked first
49
+ * 2. `tsc` (standard TypeScript) — fallback
50
+ *
51
+ * @remarks
52
+ * Unlike the previous implementation that parsed `package.json` dependencies,
53
+ * this uses runtime tool detection which works correctly with pnpm catalogs,
54
+ * peer dependencies, and hoisted/transitive deps.
55
+ *
56
+ * @param _cwd - Ignored (kept for backward compatibility)
57
+ * @returns The compiler to use, or undefined if neither is available
58
+ */
59
+ static detectCompiler(_cwd) {
60
+ if (TypeScript.cachedCompilerResult !== null) return TypeScript.cachedCompilerResult.compiler;
61
+ const tsgo = Command.findTool("tsgo");
62
+ if (tsgo.available) {
63
+ TypeScript.cachedCompilerResult = {
64
+ compiler: "tsgo",
65
+ tool: tsgo
66
+ };
67
+ return "tsgo";
68
+ }
69
+ const tsc = Command.findTool("tsc");
70
+ if (tsc.available) {
71
+ TypeScript.cachedCompilerResult = {
72
+ compiler: "tsc",
73
+ tool: tsc
74
+ };
75
+ return "tsc";
76
+ }
77
+ }
78
+ /**
79
+ * Check if a TypeScript compiler is available.
80
+ *
81
+ * @returns `true` if either tsgo or tsc is available
82
+ */
83
+ static isAvailable() {
84
+ return TypeScript.detectCompiler() !== void 0;
85
+ }
86
+ /**
87
+ * Get the default type checking command for the detected compiler.
88
+ *
89
+ * @remarks
90
+ * Uses the cached `ToolSearchResult` from `detectCompiler()` to build
91
+ * the command string, avoiding a separate package manager detection step.
92
+ *
93
+ * @returns Command string like `pnpm exec tsgo --noEmit` or `tsgo --noEmit`
94
+ * @throws Error if no TypeScript compiler is available
95
+ */
96
+ static getDefaultTypecheckCommand() {
97
+ if (!TypeScript.detectCompiler() || !TypeScript.cachedCompilerResult) throw new Error("No TypeScript compiler found. Install 'typescript' or '@typescript/native-preview' as a dev dependency.");
98
+ return `${TypeScript.cachedCompilerResult.tool.command} --noEmit`;
99
+ }
100
+ /**
101
+ * Clear the cached compiler detection result.
102
+ * Useful for testing or when the environment changes.
103
+ */
104
+ static clearCache() {
105
+ TypeScript.cachedCompilerResult = null;
106
+ }
107
+ /**
108
+ * Pre-configured handler with default options.
109
+ */
110
+ static handler = TypeScript.create();
111
+ /**
112
+ * Create a handler with custom options.
113
+ *
114
+ * @param options - Configuration options
115
+ * @returns A lint-staged compatible handler function
116
+ */
117
+ static create(options = {}) {
118
+ const excludes = options.exclude ?? [...TypeScript.defaultExcludes];
119
+ const skipTypecheck = options.skipTypecheck ?? false;
120
+ let typecheckCommand;
121
+ const getTypecheckCommand = () => {
122
+ if (typecheckCommand === void 0) typecheckCommand = options.typecheckCommand ?? TypeScript.getDefaultTypecheckCommand();
123
+ return typecheckCommand;
124
+ };
125
+ return (filenames) => {
126
+ if (Filter.exclude(filenames, excludes).length === 0) return [];
127
+ if (!skipTypecheck) return [getTypecheckCommand()];
128
+ return [];
129
+ };
130
+ }
131
+ };
132
+
133
+ //#endregion
134
+ export { TypeScript };