@bitwisedesign/xanoscriptlint 0.1.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -0
  3. package/dist/cli.d.ts +6 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +121 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +43 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +239 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/customRules.d.ts +4 -0
  12. package/dist/customRules.d.ts.map +1 -0
  13. package/dist/customRules.js +43 -0
  14. package/dist/customRules.js.map +1 -0
  15. package/dist/discover.d.ts +7 -0
  16. package/dist/discover.d.ts.map +1 -0
  17. package/dist/discover.js +70 -0
  18. package/dist/discover.js.map +1 -0
  19. package/dist/lint.d.ts +14 -0
  20. package/dist/lint.d.ts.map +1 -0
  21. package/dist/lint.js +64 -0
  22. package/dist/lint.js.map +1 -0
  23. package/dist/report.d.ts +7 -0
  24. package/dist/report.d.ts.map +1 -0
  25. package/dist/report.js +86 -0
  26. package/dist/report.js.map +1 -0
  27. package/dist/rules/empty_function_run.d.ts +3 -0
  28. package/dist/rules/empty_function_run.d.ts.map +1 -0
  29. package/dist/rules/empty_function_run.js +32 -0
  30. package/dist/rules/empty_function_run.js.map +1 -0
  31. package/dist/rules/index.d.ts +5 -0
  32. package/dist/rules/index.d.ts.map +1 -0
  33. package/dist/rules/index.js +12 -0
  34. package/dist/rules/index.js.map +1 -0
  35. package/dist/rules/no_trailing_newline.d.ts +3 -0
  36. package/dist/rules/no_trailing_newline.d.ts.map +1 -0
  37. package/dist/rules/no_trailing_newline.js +34 -0
  38. package/dist/rules/no_trailing_newline.js.map +1 -0
  39. package/dist/rules/no_var_response.d.ts +3 -0
  40. package/dist/rules/no_var_response.d.ts.map +1 -0
  41. package/dist/rules/no_var_response.js +32 -0
  42. package/dist/rules/no_var_response.js.map +1 -0
  43. package/dist/rules/types.d.ts +30 -0
  44. package/dist/rules/types.d.ts.map +1 -0
  45. package/dist/rules/types.js +2 -0
  46. package/dist/rules/types.js.map +1 -0
  47. package/dist/suppress.d.ts +8 -0
  48. package/dist/suppress.d.ts.map +1 -0
  49. package/dist/suppress.js +107 -0
  50. package/dist/suppress.js.map +1 -0
  51. package/dist/util.d.ts +9 -0
  52. package/dist/util.d.ts.map +1 -0
  53. package/dist/util.js +37 -0
  54. package/dist/util.js.map +1 -0
  55. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bitwise Design
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # xanoscriptlint
2
+
3
+ A linter for [XanoScript](https://docs.xano.com/xanoscript/key-concepts).
4
+
5
+ xanoscriptlint is a style and convention linter, not a compiler. Syntax and semantics stay with Xano (`@xano/xanoscript-language-server`, `xano_validate_xanoscript`). This tool owns convention and pitfall rules, project configuration, path filters, and custom regex rules.
6
+
7
+ Requires Node.js 20 or later. Runs on linux and macOS (Windows is untested but not blocked).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install --save-dev @bitwisedesign/xanoscriptlint
13
+ ```
14
+
15
+ ```bash
16
+ npx @bitwisedesign/xanoscriptlint
17
+ npx @bitwisedesign/xanoscriptlint rules
18
+ npx @bitwisedesign/xanoscriptlint path/to/file.xs
19
+ ```
20
+
21
+ `npx` is optional. After a local install, npm puts the binary on `PATH` for scripts:
22
+
23
+ ```json
24
+ "scripts": {
25
+ "lint:xs": "xanoscriptlint"
26
+ }
27
+ ```
28
+
29
+ Then `npm run lint:xs` runs `xanoscriptlint` with no `npx`.
30
+
31
+ `npm install -g @bitwisedesign/xanoscriptlint` (no path) only works after the package is published to npm. To put the command on your PATH from a git checkout:
32
+
33
+ ```bash
34
+ npm install
35
+ npm run build
36
+ npm link
37
+ ```
38
+
39
+ Or `npm install -g /path/to/xanoscriptlint`. Remove a link with `npm unlink -g @bitwisedesign/xanoscriptlint`.
40
+
41
+ ## Configuration
42
+
43
+ Place `.xanoscriptlint.yml` at the project root (or pass `--config`). With no file, built-in defaults apply.
44
+
45
+ ```yaml
46
+ disabled_rules:
47
+ - no_trailing_newline
48
+ opt_in_rules:
49
+ - no_var_response
50
+ # only_rules: [empty_function_run] # exclusive; cannot mix with the two above
51
+
52
+ included:
53
+ - "**/*.xs"
54
+ excluded:
55
+ - ".xano/**"
56
+ - "**/node_modules/**"
57
+
58
+ empty_function_run:
59
+ severity: error
60
+
61
+ custom_rules:
62
+ no_todo_in_stack:
63
+ name: No TODO
64
+ regex: "TODO"
65
+ message: "Remove TODO before shipping."
66
+ severity: warning
67
+ excluded:
68
+ - ".*Test\\.xs"
69
+ ```
70
+
71
+ - Built-in rules with `defaultEnabled: true` are on unless listed in `disabled_rules`.
72
+ - Off-by-default rules are enabled with `opt_in_rules`.
73
+ - `only_rules` is an exclusive allowlist and cannot be combined with `disabled_rules` or `opt_in_rules`.
74
+ - `custom_rules` run when defined, unless `only_rules` is set and does not include `custom_rules` or that rule id.
75
+ - `excluded` always wins over `included`. Paths are relative to the config file directory.
76
+
77
+ See [docs/configuration.md](docs/configuration.md) and [docs/rules.md](docs/rules.md).
78
+
79
+ ## Built-in rules
80
+
81
+ | Id | Default | Severity | What it catches |
82
+ | --- | --- | --- | --- |
83
+ | `empty_function_run` | on | error | `function.run ""` / `function.run ''` |
84
+ | `no_trailing_newline` | on | error | File does not end with `}` (Xano pull strips trailing newlines); auto-fixable with `--fix` |
85
+ | `no_var_response` | opt-in | warning | `var $response` (Xano rewrites it) |
86
+
87
+ House style such as a `// Modified:` timestamp belongs in `custom_rules`, not in the default catalog:
88
+
89
+ ```yaml
90
+ custom_rules:
91
+ modified_stamp:
92
+ name: Modified stamp
93
+ regex: "^// Modified: \\d{2}-\\d{2}-\\d{4} \\d{2}:\\d{2}$"
94
+ message: "Use this only as an example of a team-specific custom rule."
95
+ severity: warning
96
+ ```
97
+
98
+ (That example matches a stamp line; requiring it as the last header comment is left to your team.)
99
+
100
+ ## Suppressions
101
+
102
+ XanoScript comments must be on their own line, so suppressions cannot trail a statement.
103
+
104
+ ```xs
105
+ // xanoscriptlint:disable empty_function_run
106
+ // xanoscriptlint:enable empty_function_run
107
+ // xanoscriptlint:disable:next empty_function_run
108
+ // xanoscriptlint:disable:previous empty_function_run
109
+ ```
110
+
111
+ `disable` without `next`/`previous` lasts until a matching `enable` or end of file. A `disable` at the top of the file is the file-wide form.
112
+
113
+ ## CLI
114
+
115
+ ```text
116
+ xanoscriptlint [paths…]
117
+ xanoscriptlint rules
118
+ xanoscriptlint --config path/to/.xanoscriptlint.yml
119
+ xanoscriptlint --reporter json
120
+ xanoscriptlint --strict
121
+ xanoscriptlint --fix
122
+ ```
123
+
124
+ Exit `0` when there are no error-severity violations. Exit `2` when there is at least one error. Warnings alone do not fail CI unless `--strict`.
125
+
126
+ ## License
127
+
128
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ export declare function runCli(argv: string[], io?: {
3
+ stdout: NodeJS.WritableStream;
4
+ stderr: NodeJS.WritableStream;
5
+ }): Promise<number>;
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAuBA,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE;IAAE,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAA;CAAY,GAC7E,OAAO,CAAC,MAAM,CAAC,CAiDjB"}
package/dist/cli.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Command, CommanderError } from "commander";
6
+ import { ConfigError, loadConfig } from "./config.js";
7
+ import { discoverXsFiles } from "./discover.js";
8
+ import { fixFile, lintFiles, readSourceFile } from "./lint.js";
9
+ import { builtinRules } from "./rules/index.js";
10
+ import { applyStrict, exitCodeFor, formatFixSummary, formatReport, } from "./report.js";
11
+ import { isMainModule } from "./util.js";
12
+ const require = createRequire(import.meta.url);
13
+ const { version } = require("../package.json");
14
+ export async function runCli(argv, io = process) {
15
+ let exitCode = 0;
16
+ const program = new Command();
17
+ program
18
+ .name("xanoscriptlint")
19
+ .description("A linter for XanoScript")
20
+ .version(version)
21
+ .option("-c, --config <path>", "path to .xanoscriptlint.yml")
22
+ .option("--reporter <name>", "stylish or json", "stylish")
23
+ .option("--strict", "treat warnings as errors", false)
24
+ .option("--fix", "automatically fix violations where possible", false)
25
+ .exitOverride()
26
+ .configureOutput({
27
+ writeOut: (str) => io.stdout.write(str),
28
+ writeErr: (str) => io.stderr.write(str),
29
+ });
30
+ program
31
+ .command("lint", { isDefault: true })
32
+ .description("Lint XanoScript files")
33
+ .argument("[paths...]", "files or directories to lint")
34
+ .action(async (paths) => {
35
+ const opts = program.opts();
36
+ exitCode = await runLint(paths, opts, io);
37
+ });
38
+ program
39
+ .command("rules")
40
+ .description("List built-in rules")
41
+ .action(() => {
42
+ io.stdout.write(formatRulesList());
43
+ });
44
+ try {
45
+ await program.parseAsync(argv);
46
+ return exitCode;
47
+ }
48
+ catch (error) {
49
+ if (error instanceof CommanderError) {
50
+ return error.exitCode;
51
+ }
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ io.stderr.write(`${message}\n`);
54
+ return 1;
55
+ }
56
+ }
57
+ async function runLint(paths, opts, io) {
58
+ const reporter = parseReporter(opts.reporter);
59
+ if (!reporter) {
60
+ io.stderr.write(`unknown reporter: ${opts.reporter}\n`);
61
+ return 1;
62
+ }
63
+ try {
64
+ const cwd = process.cwd();
65
+ const config = loadConfig({ cwd, configPath: opts.config });
66
+ const files = await discoverXsFiles({ config, cwd, cliPaths: paths });
67
+ let sources = files.map((filePath) => readSourceFile(filePath));
68
+ if (opts.fix) {
69
+ const corrections = [];
70
+ const fixed = [];
71
+ for (const source of sources) {
72
+ const result = fixFile(source, config);
73
+ if (result.changed) {
74
+ writeFileSync(source.path, result.text, "utf8");
75
+ }
76
+ corrections.push(...result.corrections);
77
+ fixed.push({ path: source.path, text: result.text });
78
+ }
79
+ sources = fixed;
80
+ const summary = formatFixSummary(corrections, cwd);
81
+ if (summary.length > 0) {
82
+ const text = summary.endsWith("\n") ? summary : `${summary}\n`;
83
+ const stream = reporter === "json" ? io.stderr : io.stdout;
84
+ stream.write(text);
85
+ }
86
+ }
87
+ const violations = applyStrict(lintFiles(sources, config), opts.strict);
88
+ const report = formatReport(violations, reporter, cwd);
89
+ if (report.length > 0) {
90
+ io.stdout.write(report.endsWith("\n") ? report : `${report}\n`);
91
+ }
92
+ return exitCodeFor(violations);
93
+ }
94
+ catch (error) {
95
+ if (error instanceof ConfigError) {
96
+ io.stderr.write(`${error.message}\n`);
97
+ return 1;
98
+ }
99
+ const message = error instanceof Error ? error.message : String(error);
100
+ io.stderr.write(`${message}\n`);
101
+ return 1;
102
+ }
103
+ }
104
+ function formatRulesList() {
105
+ const lines = ["id default severity description", "-".repeat(90)];
106
+ for (const rule of builtinRules) {
107
+ const enabled = rule.defaultEnabled ? "on" : "opt-in";
108
+ lines.push(`${rule.id.padEnd(26)}${enabled.padEnd(11)}${rule.defaultSeverity.padEnd(11)}${rule.description}`);
109
+ }
110
+ return `${lines.join("\n")}\n`;
111
+ }
112
+ function parseReporter(name) {
113
+ if (name === "stylish" || name === "json") {
114
+ return name;
115
+ }
116
+ return undefined;
117
+ }
118
+ if (isMainModule(process.argv[1], fileURLToPath(import.meta.url))) {
119
+ process.exitCode = await runCli(process.argv);
120
+ }
121
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { RuleOptions, Severity } from "./rules/types.js";
2
+ export declare class ConfigError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare const CONFIG_FILENAME = ".xanoscriptlint.yml";
6
+ export interface CustomRuleConfig {
7
+ id: string;
8
+ name: string;
9
+ regex: string;
10
+ pattern: RegExp;
11
+ message: string;
12
+ severity: Severity;
13
+ included?: string[];
14
+ excluded?: string[];
15
+ }
16
+ export interface ResolvedConfig {
17
+ configDir: string;
18
+ configPath: string | null;
19
+ enabledRuleIds: Set<string>;
20
+ ruleOptions: Map<string, RuleOptions>;
21
+ included: string[];
22
+ excluded: string[];
23
+ customRules: CustomRuleConfig[];
24
+ }
25
+ interface RawConfig {
26
+ disabled_rules?: unknown;
27
+ opt_in_rules?: unknown;
28
+ only_rules?: unknown;
29
+ included?: unknown;
30
+ excluded?: unknown;
31
+ custom_rules?: unknown;
32
+ [key: string]: unknown;
33
+ }
34
+ export declare function findConfigPath(startDir: string): string | null;
35
+ export declare function loadConfig(options: {
36
+ cwd: string;
37
+ configPath?: string;
38
+ }): ResolvedConfig;
39
+ export declare function loadConfigFile(configPath: string): ResolvedConfig;
40
+ export declare function loadConfigText(text: string, configDir: string, configPath: string | null): ResolvedConfig;
41
+ export declare function resolveConfig(raw: RawConfig, configDir: string, configPath: string | null): ResolvedConfig;
42
+ export {};
43
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAWrD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAWD,UAAU,SAAS;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa9D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,cAAc,CAajB;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAGjE;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,cAAc,CAYhB;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,cAAc,CA6FhB"}
package/dist/config.js ADDED
@@ -0,0 +1,239 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { builtinRules } from "./rules/index.js";
5
+ export class ConfigError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "ConfigError";
9
+ }
10
+ }
11
+ export const CONFIG_FILENAME = ".xanoscriptlint.yml";
12
+ const KNOWN_KEYS = new Set([
13
+ "disabled_rules",
14
+ "opt_in_rules",
15
+ "only_rules",
16
+ "included",
17
+ "excluded",
18
+ "custom_rules",
19
+ ]);
20
+ export function findConfigPath(startDir) {
21
+ let dir = path.resolve(startDir);
22
+ while (true) {
23
+ const candidate = path.join(dir, CONFIG_FILENAME);
24
+ if (existsSync(candidate)) {
25
+ return candidate;
26
+ }
27
+ const parent = path.dirname(dir);
28
+ if (parent === dir) {
29
+ return null;
30
+ }
31
+ dir = parent;
32
+ }
33
+ }
34
+ export function loadConfig(options) {
35
+ if (options.configPath) {
36
+ const configPath = path.resolve(options.cwd, options.configPath);
37
+ if (!existsSync(configPath)) {
38
+ throw new ConfigError(`config file not found: ${configPath}`);
39
+ }
40
+ return loadConfigFile(configPath);
41
+ }
42
+ const found = findConfigPath(options.cwd);
43
+ if (!found) {
44
+ return resolveConfig({}, path.resolve(options.cwd), null);
45
+ }
46
+ return loadConfigFile(found);
47
+ }
48
+ export function loadConfigFile(configPath) {
49
+ const text = readFileSync(configPath, "utf8");
50
+ return loadConfigText(text, path.dirname(configPath), configPath);
51
+ }
52
+ export function loadConfigText(text, configDir, configPath) {
53
+ let raw;
54
+ try {
55
+ raw = parseYaml(text) ?? {};
56
+ }
57
+ catch (error) {
58
+ const detail = error instanceof Error ? error.message : String(error);
59
+ throw new ConfigError(`invalid YAML: ${detail}`);
60
+ }
61
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
62
+ throw new ConfigError("config must be a YAML mapping");
63
+ }
64
+ return resolveConfig(raw, configDir, configPath);
65
+ }
66
+ export function resolveConfig(raw, configDir, configPath) {
67
+ const builtinIds = new Set(builtinRules.map((rule) => rule.id));
68
+ const disabledRules = optionalStringList(raw.disabled_rules, "disabled_rules");
69
+ const optInRules = optionalStringList(raw.opt_in_rules, "opt_in_rules");
70
+ const onlyRules = optionalStringList(raw.only_rules, "only_rules");
71
+ const included = optionalStringList(raw.included, "included") ?? ["**/*.xs"];
72
+ const excluded = optionalStringList(raw.excluded, "excluded") ?? [];
73
+ const customRules = parseCustomRules(raw.custom_rules);
74
+ const customIds = new Set(customRules.map((rule) => rule.id));
75
+ for (const key of Object.keys(raw)) {
76
+ if (KNOWN_KEYS.has(key) || builtinIds.has(key) || customIds.has(key)) {
77
+ continue;
78
+ }
79
+ throw new ConfigError(`unknown config key: ${key}`);
80
+ }
81
+ if (onlyRules !== undefined) {
82
+ if (disabledRules !== undefined || optInRules !== undefined) {
83
+ throw new ConfigError("only_rules cannot be combined with disabled_rules or opt_in_rules");
84
+ }
85
+ }
86
+ const knownIds = new Set([...builtinIds, ...customIds]);
87
+ for (const [listName, list] of [
88
+ ["disabled_rules", disabledRules],
89
+ ["opt_in_rules", optInRules],
90
+ ["only_rules", onlyRules],
91
+ ]) {
92
+ if (!list) {
93
+ continue;
94
+ }
95
+ for (const id of list) {
96
+ if (id === "custom_rules") {
97
+ if (listName !== "only_rules") {
98
+ throw new ConfigError("custom_rules is only valid in only_rules");
99
+ }
100
+ continue;
101
+ }
102
+ if (!knownIds.has(id)) {
103
+ throw new ConfigError(`unknown rule id in ${listName}: ${id}`);
104
+ }
105
+ }
106
+ }
107
+ const enabledRuleIds = new Set();
108
+ if (onlyRules !== undefined) {
109
+ for (const id of onlyRules) {
110
+ if (id === "custom_rules") {
111
+ for (const customId of customIds) {
112
+ enabledRuleIds.add(customId);
113
+ }
114
+ }
115
+ else {
116
+ enabledRuleIds.add(id);
117
+ }
118
+ }
119
+ }
120
+ else {
121
+ for (const rule of builtinRules) {
122
+ if (rule.defaultEnabled && !disabledRules?.includes(rule.id)) {
123
+ enabledRuleIds.add(rule.id);
124
+ }
125
+ if (!rule.defaultEnabled && optInRules?.includes(rule.id)) {
126
+ enabledRuleIds.add(rule.id);
127
+ }
128
+ }
129
+ for (const custom of customRules) {
130
+ if (!disabledRules?.includes(custom.id)) {
131
+ enabledRuleIds.add(custom.id);
132
+ }
133
+ }
134
+ }
135
+ const ruleOptions = new Map();
136
+ for (const rule of builtinRules) {
137
+ if (rule.id in raw) {
138
+ ruleOptions.set(rule.id, parseRuleOptions(raw[rule.id], rule.id));
139
+ }
140
+ }
141
+ const activeCustomRules = customRules.filter((rule) => enabledRuleIds.has(rule.id));
142
+ return {
143
+ configDir,
144
+ configPath,
145
+ enabledRuleIds,
146
+ ruleOptions,
147
+ included,
148
+ excluded,
149
+ customRules: activeCustomRules,
150
+ };
151
+ }
152
+ function parseCustomRules(value) {
153
+ if (value === undefined || value === null) {
154
+ return [];
155
+ }
156
+ if (typeof value !== "object" || Array.isArray(value)) {
157
+ throw new ConfigError("custom_rules must be a mapping of id to rule");
158
+ }
159
+ const result = [];
160
+ for (const [id, spec] of Object.entries(value)) {
161
+ if (typeof spec !== "object" || spec === null || Array.isArray(spec)) {
162
+ throw new ConfigError(`custom_rules.${id} must be a mapping`);
163
+ }
164
+ const raw = spec;
165
+ if (typeof raw.regex !== "string" || raw.regex.length === 0) {
166
+ throw new ConfigError(`custom_rules.${id}.regex is required`);
167
+ }
168
+ let pattern;
169
+ try {
170
+ pattern = new RegExp(raw.regex);
171
+ }
172
+ catch (error) {
173
+ const detail = error instanceof Error ? error.message : String(error);
174
+ throw new ConfigError(`custom_rules.${id}.regex is invalid: ${detail}`);
175
+ }
176
+ result.push({
177
+ id,
178
+ name: optionalString(raw.name, `custom_rules.${id}.name`) ?? id,
179
+ regex: raw.regex,
180
+ pattern,
181
+ message: optionalString(raw.message, `custom_rules.${id}.message`) ??
182
+ `custom rule ${id} matched`,
183
+ severity: optionalSeverity(raw.severity, `custom_rules.${id}.severity`) ?? "warning",
184
+ included: optionalStringList(raw.included, `custom_rules.${id}.included`),
185
+ excluded: optionalStringList(raw.excluded, `custom_rules.${id}.excluded`),
186
+ });
187
+ }
188
+ return result;
189
+ }
190
+ function parseRuleOptions(value, id) {
191
+ if (typeof value === "string") {
192
+ return { severity: asSeverity(value, id) };
193
+ }
194
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
195
+ throw new ConfigError(`${id} must be a severity or a mapping`);
196
+ }
197
+ const record = value;
198
+ const options = {};
199
+ if (record.severity !== undefined) {
200
+ options.severity = asSeverity(record.severity, `${id}.severity`);
201
+ }
202
+ for (const key of Object.keys(record)) {
203
+ if (key !== "severity") {
204
+ throw new ConfigError(`unknown option for ${id}: ${key}`);
205
+ }
206
+ }
207
+ return options;
208
+ }
209
+ function optionalStringList(value, label) {
210
+ if (value === undefined || value === null) {
211
+ return undefined;
212
+ }
213
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
214
+ throw new ConfigError(`${label} must be a list of strings`);
215
+ }
216
+ return value;
217
+ }
218
+ function optionalString(value, label) {
219
+ if (value === undefined || value === null) {
220
+ return undefined;
221
+ }
222
+ if (typeof value !== "string") {
223
+ throw new ConfigError(`${label} must be a string`);
224
+ }
225
+ return value;
226
+ }
227
+ function optionalSeverity(value, label) {
228
+ if (value === undefined || value === null) {
229
+ return undefined;
230
+ }
231
+ return asSeverity(value, label);
232
+ }
233
+ function asSeverity(value, label) {
234
+ if (value === "error" || value === "warning") {
235
+ return value;
236
+ }
237
+ throw new ConfigError(`${label} must be "error" or "warning"`);
238
+ }
239
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { CustomRuleConfig } from "./config.js";
2
+ import type { SourceFile, Violation } from "./rules/types.js";
3
+ export declare function runCustomRules(file: SourceFile, rules: CustomRuleConfig[]): Violation[];
4
+ //# sourceMappingURL=customRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customRules.d.ts","sourceRoot":"","sources":["../src/customRules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG9D,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,gBAAgB,EAAE,GACxB,SAAS,EAAE,CA2Bb"}
@@ -0,0 +1,43 @@
1
+ import path from "node:path";
2
+ import { isCommentLine, splitLines } from "./util.js";
3
+ export function runCustomRules(file, rules) {
4
+ const violations = [];
5
+ const posixPath = file.path.split(path.sep).join("/");
6
+ for (const rule of rules) {
7
+ if (!pathAllowed(posixPath, rule)) {
8
+ continue;
9
+ }
10
+ const lines = splitLines(file.text);
11
+ for (let i = 0; i < lines.length; i += 1) {
12
+ const line = lines[i];
13
+ if (isCommentLine(line)) {
14
+ continue;
15
+ }
16
+ const match = rule.pattern.exec(line);
17
+ if (match) {
18
+ violations.push({
19
+ ruleId: rule.id,
20
+ message: rule.message,
21
+ severity: rule.severity,
22
+ file: file.path,
23
+ line: i + 1,
24
+ column: match.index + 1,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ return violations;
30
+ }
31
+ function pathAllowed(posixPath, rule) {
32
+ if (rule.excluded?.some((pattern) => matchesPathRegex(posixPath, pattern))) {
33
+ return false;
34
+ }
35
+ if (rule.included && rule.included.length > 0) {
36
+ return rule.included.some((pattern) => matchesPathRegex(posixPath, pattern));
37
+ }
38
+ return true;
39
+ }
40
+ function matchesPathRegex(posixPath, pattern) {
41
+ return new RegExp(pattern).test(posixPath);
42
+ }
43
+ //# sourceMappingURL=customRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customRules.js","sourceRoot":"","sources":["../src/customRules.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,KAAyB;IAEzB,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,KAAK,EAAE,CAAC;gBACV,UAAU,CAAC,IAAI,CAAC;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC;iBACxB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,WAAW,CAAC,SAAiB,EAAE,IAAsB;IAC5D,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,OAAe;IAC1D,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ResolvedConfig } from "./config.js";
2
+ export declare function discoverXsFiles(options: {
3
+ config: ResolvedConfig;
4
+ cwd: string;
5
+ cliPaths: string[];
6
+ }): Promise<string[]>;
7
+ //# sourceMappingURL=discover.d.ts.map