@ttsc/lint 0.12.3 → 0.13.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 (74) hide show
  1. package/lib/defaultFormat.d.ts +10 -11
  2. package/lib/defaultFormat.js +10 -11
  3. package/lib/defaultFormat.js.map +1 -1
  4. package/lib/index.js +233 -135
  5. package/lib/index.js.map +1 -1
  6. package/lib/structures/ITtscLintConfig.d.ts +2 -2
  7. package/lib/structures/ITtscLintFormatConfig.d.ts +53 -55
  8. package/lib/structures/ITtscLintPluginConfig.d.ts +10 -77
  9. package/lib/structures/ITtscLintPluginMeta.d.ts +9 -1
  10. package/lib/structures/TtscLintRule.d.ts +10 -1
  11. package/lib/structures/TtscLintRuleMap.d.ts +2 -2
  12. package/lib/structures/TtscLintRuleOptions.d.ts +14 -0
  13. package/linthost/ast_helpers.go +80 -11
  14. package/linthost/compile.go +116 -112
  15. package/linthost/config.go +647 -687
  16. package/linthost/config_format.go +272 -247
  17. package/linthost/contrib_adapter.go +7 -0
  18. package/linthost/directives.go +116 -0
  19. package/linthost/dispatch.go +33 -33
  20. package/linthost/engine.go +156 -43
  21. package/linthost/fix.go +47 -27
  22. package/linthost/flags_gen.go +31 -0
  23. package/linthost/format.go +119 -3
  24. package/linthost/host.go +115 -5
  25. package/linthost/print_dispatch.go +128 -28
  26. package/linthost/print_doc.go +23 -0
  27. package/linthost/print_engine.go +168 -4
  28. package/linthost/print_nodes_array.go +17 -7
  29. package/linthost/print_nodes_call.go +138 -20
  30. package/linthost/print_nodes_function.go +353 -0
  31. package/linthost/print_nodes_imports.go +46 -29
  32. package/linthost/print_nodes_list.go +86 -5
  33. package/linthost/print_nodes_object.go +56 -11
  34. package/linthost/rules_arrays.go +5 -2
  35. package/linthost/rules_debugger.go +3 -2
  36. package/linthost/rules_dupes.go +7 -4
  37. package/linthost/rules_empty.go +3 -2
  38. package/linthost/rules_escape.go +35 -3
  39. package/linthost/rules_eval.go +3 -0
  40. package/linthost/rules_finally.go +11 -0
  41. package/linthost/rules_format_jsdoc.go +7 -0
  42. package/linthost/rules_format_print_width.go +270 -15
  43. package/linthost/rules_format_quotes.go +3 -0
  44. package/linthost/rules_format_sort_imports.go +4 -0
  45. package/linthost/rules_gap.go +75 -3
  46. package/linthost/rules_imports.go +5 -7
  47. package/linthost/rules_logic.go +75 -5
  48. package/linthost/rules_loops.go +4 -0
  49. package/linthost/rules_misc.go +4 -2
  50. package/linthost/rules_params.go +7 -11
  51. package/linthost/rules_problems.go +89 -22
  52. package/linthost/rules_promise.go +15 -0
  53. package/linthost/rules_protos.go +3 -2
  54. package/linthost/rules_self.go +6 -0
  55. package/linthost/rules_strings.go +10 -0
  56. package/linthost/rules_suggestions.go +174 -9
  57. package/linthost/rules_throw.go +2 -0
  58. package/linthost/rules_ts.go +13 -0
  59. package/linthost/rules_ts_extra.go +25 -8
  60. package/linthost/rules_var.go +15 -2
  61. package/package.json +3 -3
  62. package/plugin/main.go +4 -4
  63. package/rule/astutil/astutil.go +12 -0
  64. package/rule/rule.go +123 -123
  65. package/src/defaultFormat.ts +10 -11
  66. package/src/index.ts +257 -168
  67. package/src/structures/ITtscLintConfig.ts +2 -2
  68. package/src/structures/ITtscLintFormatConfig.ts +53 -55
  69. package/src/structures/ITtscLintPluginConfig.ts +10 -83
  70. package/src/structures/ITtscLintPluginMeta.ts +9 -1
  71. package/src/structures/TtscLintRule.ts +10 -1
  72. package/src/structures/TtscLintRuleMap.ts +17 -18
  73. package/src/structures/TtscLintRuleOptions.ts +15 -0
  74. package/linthost/eslint_runtime.go +0 -320
@@ -3,10 +3,10 @@ import type { TtscLintSeverity } from "./TtscLintSeverity";
3
3
  * Prettier-style flat configuration for the `format/*` rules.
4
4
  *
5
5
  * The `format` block is the recommended way to enable formatting in
6
- * `@ttsc/lint`. Each key mirrors a Prettier option of the same name —
7
- * users coming from a `.prettierrc` can copy their config almost
8
- * verbatim. The block is opt-in by presence: a `lint.config.ts` with no
9
- * `format` field keeps every format rule off, exactly as before.
6
+ * `@ttsc/lint`. Each key mirrors a Prettier option of the same name — users
7
+ * coming from a `.prettierrc` can copy their config almost verbatim. The block
8
+ * is opt-in by presence: a `lint.config.ts` with no `format` field keeps every
9
+ * format rule off, exactly as before.
10
10
  *
11
11
  * Once present, the block configures a curated set of format rules at
12
12
  * Prettier-aligned defaults. `ttsc format` uses these rules to rewrite source.
@@ -18,37 +18,37 @@ import type { TtscLintSeverity } from "./TtscLintSeverity";
18
18
  * import type { ITtscLintConfig } from "@ttsc/lint";
19
19
  *
20
20
  * export default {
21
- * rules: { "no-var": "error" },
22
- * format: {
23
- * severity: "warning",
24
- * printWidth: 100,
25
- * singleQuote: true,
26
- * importOrder: ["<THIRD_PARTY_MODULES>", "^[./]"],
27
- * },
21
+ * rules: { "no-var": "error" },
22
+ * format: {
23
+ * severity: "warning",
24
+ * printWidth: 100,
25
+ * singleQuote: true,
26
+ * importOrder: ["<THIRD_PARTY_MODULES>", "^[./]"],
27
+ * },
28
28
  * } satisfies ITtscLintConfig;
29
29
  *
30
- * Deviations from Prettier:
31
- * - `endOfLine` is restricted to `"lf"` and `"crlf"`. Prettier's
32
- * `"cr"` and `"auto"` modes are intentionally unsupported — the
33
- * printer does not auto-detect terminators.
34
- * - Many Prettier knobs (`bracketSpacing`, `arrowParens`,
35
- * `quoteProps`, JSX-specific switches) are not yet implemented.
30
+ * Deviations from Prettier:
31
+ * - `endOfLine` is restricted to `"lf"` and `"crlf"`. Prettier's
32
+ * `"cr"` and `"auto"` modes are intentionally unsupported — the
33
+ * printer does not auto-detect terminators.
34
+ * - Many Prettier knobs (`bracketSpacing`, `arrowParens`,
35
+ * `quoteProps`, JSX-specific switches) are not yet implemented.
36
36
  *
37
- * Rule enablement matrix (when the `format` block is present):
37
+ * Rule enablement matrix (when the `format` block is present):
38
38
  *
39
- * - `format/semi` — always on. `semi: false` flips it to `prefer:
40
- * "never"`.
41
- * - `format/quotes` — always on. `singleQuote: true` flips to
42
- * `prefer: "single"`.
43
- * - `format/trailing-comma` — always on. `trailingComma: "none"`
44
- * disables the rule's edits without removing the surface.
45
- * - `format/print-width` — always on, driven by `printWidth`,
46
- * `tabWidth`, `useTabs`, `endOfLine`.
47
- * - `format/sort-imports` — opt-in. Setting `importOrder` enables it.
48
- * - `format/jsdoc` — opt-in. Setting `jsdoc` enables it.
39
+ * - `format/semi` — always on. `semi: false` flips it to `prefer:
40
+ * "never"`.
41
+ * - `format/quotes` — always on. `singleQuote: true` flips to
42
+ * `prefer: "single"`.
43
+ * - `format/trailing-comma` — always on. `trailingComma: "none"`
44
+ * disables the rule's edits without removing the surface.
45
+ * - `format/print-width` — always on, driven by `printWidth`,
46
+ * `tabWidth`, `useTabs`, `endOfLine`.
47
+ * - `format/sort-imports` — opt-in. Setting `importOrder` enables it.
48
+ * - `format/jsdoc` — opt-in. Setting `jsdoc` enables it.
49
49
  *
50
- * Format findings produced from this block are off by default. Set `severity`
51
- * only when a project intentionally wants check-time format diagnostics.
50
+ * Format findings produced from this block are off by default. Set `severity`
51
+ * only when a project intentionally wants check-time format diagnostics.
52
52
  */
53
53
  export interface ITtscLintFormatConfig {
54
54
  /**
@@ -62,30 +62,30 @@ export interface ITtscLintFormatConfig {
62
62
  */
63
63
  severity?: TtscLintSeverity;
64
64
  /**
65
- * Insert trailing semicolons on ASI-terminated statements. Mirrors
66
- * Prettier's `semi`. `false` flips the rule to require *no*
67
- * trailing semicolon (rare; matches prettier's `semi: false`).
65
+ * Insert trailing semicolons on ASI-terminated statements. Mirrors Prettier's
66
+ * `semi`. `false` flips the rule to require _no_ trailing semicolon (rare;
67
+ * matches prettier's `semi: false`).
68
68
  *
69
69
  * @default true
70
70
  */
71
71
  semi?: boolean;
72
72
  /**
73
- * Prefer single-quoted strings. Mirrors Prettier's `singleQuote`.
74
- * `false` means double quotes (Prettier's default).
73
+ * Prefer single-quoted strings. Mirrors Prettier's `singleQuote`. `false`
74
+ * means double quotes (Prettier's default).
75
75
  *
76
76
  * @default false
77
77
  */
78
78
  singleQuote?: boolean;
79
79
  /**
80
- * Trailing-comma policy. Mirrors Prettier's `trailingComma`. The
81
- * `"none"` mode disables the rule's edits.
80
+ * Trailing-comma policy. Mirrors Prettier's `trailingComma`. The `"none"`
81
+ * mode disables the rule's edits.
82
82
  *
83
83
  * @default "all"
84
84
  */
85
85
  trailingComma?: "all" | "es5" | "none";
86
86
  /**
87
- * Maximum column width before broken-form layout is chosen.
88
- * Mirrors Prettier's `printWidth`.
87
+ * Maximum column width before broken-form layout is chosen. Mirrors
88
+ * Prettier's `printWidth`.
89
89
  *
90
90
  * @default 80
91
91
  */
@@ -103,19 +103,18 @@ export interface ITtscLintFormatConfig {
103
103
  */
104
104
  useTabs?: boolean;
105
105
  /**
106
- * Line terminator the printer emits on reflow. `@ttsc/lint`
107
- * supports `"lf"` and `"crlf"`. Prettier's `"cr"` and `"auto"` are
108
- * intentionally unsupported because the printer does not
109
- * auto-detect line endings.
106
+ * Line terminator the printer emits on reflow. `@ttsc/lint` supports `"lf"`
107
+ * and `"crlf"`. Prettier's `"cr"` and `"auto"` are intentionally unsupported
108
+ * because the printer does not auto-detect line endings.
110
109
  *
111
110
  * @default "lf"
112
111
  */
113
112
  endOfLine?: "lf" | "crlf";
114
113
  /**
115
- * Group order for `format/sort-imports`. Setting this enables the
116
- * rule; mirrors `@trivago/prettier-plugin-sort-imports`'
117
- * `importOrder`. The `<THIRD_PARTY_MODULES>` literal is the
118
- * catch-all placeholder for specifiers that match no other group.
114
+ * Group order for `format/sort-imports`. Setting this enables the rule;
115
+ * mirrors `@trivago/prettier-plugin-sort-imports`' `importOrder`. The
116
+ * `<THIRD_PARTY_MODULES>` literal is the catch-all placeholder for specifiers
117
+ * that match no other group.
119
118
  */
120
119
  importOrder?: readonly ("<THIRD_PARTY_MODULES>" | (string & {}))[];
121
120
  /**
@@ -125,8 +124,7 @@ export interface ITtscLintFormatConfig {
125
124
  */
126
125
  importOrderSeparation?: boolean;
127
126
  /**
128
- * Sort named import specifiers alphabetically within each
129
- * declaration.
127
+ * Sort named import specifiers alphabetically within each declaration.
130
128
  *
131
129
  * @default true
132
130
  */
@@ -138,13 +136,13 @@ export interface ITtscLintFormatConfig {
138
136
  */
139
137
  importOrderCaseInsensitive?: boolean;
140
138
  /**
141
- * Enable `format/jsdoc`. Pass `true` to turn it on with built-in
142
- * defaults, or an object to customize:
139
+ * Enable `format/jsdoc`. Pass `true` to turn it on with built-in defaults, or
140
+ * an object to customize:
143
141
  *
144
- * - `tagSynonyms` — extra `from → to` rewrites layered on the
145
- * built-in synonym table.
146
- * - `sortTags` — sort JSDoc tags into canonical order (reserved;
147
- * today's MVP only rewrites tag names).
142
+ * - `tagSynonyms` — extra `from → to` rewrites layered on the built-in synonym
143
+ * table.
144
+ * - `sortTags` — sort JSDoc tags into canonical order (reserved; today's MVP
145
+ * only rewrites tag names).
148
146
  *
149
147
  * @default false (off)
150
148
  */
@@ -1,5 +1,3 @@
1
- import type { ITtscLintFormatConfig } from "./ITtscLintFormatConfig";
2
- import type { TtscLintRuleMap } from "./TtscLintRuleMap";
3
1
  /** `compilerOptions.plugins[]` entry shape consumed by `@ttsc/lint`. */
4
2
  export interface ITtscLintPluginConfig {
5
3
  /** Set to `false` to keep the entry while disabling this plugin. */
@@ -7,89 +5,24 @@ export interface ITtscLintPluginConfig {
7
5
  /** Plugin module specifier. */
8
6
  transform?: string;
9
7
  /**
10
- * Inline rule severity map applied to the project.
8
+ * Path to the lint config file, overriding auto-discovery.
11
9
  *
12
- * Mirrors the `rules` field of an ESLint flat-config entry. When set, the
13
- * sidecar uses this map directly and does NOT consult any `lint.config.*`
14
- * file (use `extends` for that). Combine with `plugins` to register
15
- * contributor rule namespaces in the same entry.
10
+ * Relative paths are resolved from the tsconfig directory; absolute paths are
11
+ * used as-is. Accepts the usual `lint.config.*` / `ttsc-lint.config.*`
12
+ * extensions (`.ts`, `.cts`, `.mts`, `.js`, `.cjs`, `.mjs`, `.json`).
16
13
  *
17
- * ```jsonc
18
- * {
19
- * "transform": "@ttsc/lint",
20
- * "rules": { "no-var": "error", "prefer-const": "warning" }
21
- * }
22
- * ```
23
- */
24
- rules?: TtscLintRuleMap;
25
- /**
26
- * Path to a standalone lint config file whose rules should be applied to this
27
- * project. Relative paths are resolved from the tsconfig directory. Accepts
28
- * the usual `lint.config.*` / `ttsc-lint.config.*` / `eslint.config.*`
29
- * extensions.
30
- *
31
- * Mirrors the `extends` field of an ESLint flat-config entry — "inherit this
32
- * file's configuration".
33
- *
34
- * ```jsonc
35
- * {
36
- * "transform": "@ttsc/lint",
37
- * "extends": "./lint.config.ts"
38
- * }
39
- * ```
40
- *
41
- * `rules` and `extends` are mutually exclusive on a single plugin entry; the
42
- * sidecar surfaces a loud error when both are set.
43
- */
44
- extends?: string;
45
- /**
46
- * Contributor lint plugins to compile into the `@ttsc/lint` binary.
47
- *
48
- * Each entry maps a namespace (rule-name prefix) to an npm specifier or
49
- * relative path. The factory resolves the package, reads its exported
50
- * `ITtscLintPlugin` descriptor, and forwards the Go source directory to
51
- * ttsc's plugin builder via the `contributors` field.
14
+ * When omitted, `@ttsc/lint` discovers a `lint.config.*` /
15
+ * `ttsc-lint.config.*` file by walking upward from the tsconfig directory.
52
16
  *
53
17
  * ```jsonc
54
18
  * {
55
19
  * "transform": "@ttsc/lint",
56
- * "plugins": { "demo": "ttsc-lint-plugin-demo" },
57
- * "rules": { "demo/no-todo-comment": "error" }
20
+ * "configFile": "./lint.config.ts"
58
21
  * }
59
22
  * ```
60
- */
61
- plugins?: Record<string, string>;
62
- /**
63
- * Prettier-style flat configuration for the `format/*` rules. Sibling of
64
- * `rules`. See {@link ITtscLintFormatConfig} for the full surface.
65
- *
66
- * ```jsonc
67
- * {
68
- * "transform": "@ttsc/lint",
69
- * "format": { "printWidth": 100, "singleQuote": true }
70
- * }
71
- * ```
72
- *
73
- * Combines with `rules` (per-rule overrides win on collision). Cannot be
74
- * combined with `extends` on the same plugin entry — put format options
75
- * inside the extends-target lint.config.ts instead.
76
- */
77
- format?: ITtscLintFormatConfig;
78
- /**
79
- * Inline rule map or path to a standalone lint config file.
80
- *
81
- * @deprecated Use `rules` for inline severity maps or `extends` for a config
82
- * file path. The sidecar maps a legacy `config` entry onto the appropriate
83
- * new field and emits a one-time stderr deprecation notice. Removed in a
84
- * future minor.
85
23
  *
86
- * The legacy shape is intentionally narrower than `rules`/`extends`: a string
87
- * (file path) or a flat rule-name severity map. The Go-side parser only
88
- * accepts those two shapes; widening the TS type to the full config object
89
- * would silently let unsupported nested shapes pass type-checking and fail
90
- * at runtime.
24
+ * Every rule, format, and plugin setting lives in the config file — the
25
+ * tsconfig plugin entry carries nothing but this pointer.
91
26
  */
92
- config?: string | TtscLintRuleMap;
93
- /** Extra plugin-owned fields are passed through unchanged. */
94
- [key: string]: unknown;
27
+ configFile?: string;
95
28
  }
@@ -1,4 +1,12 @@
1
- /** Plugin-level metadata shared with the diagnostic surface. */
1
+ /**
2
+ * Plugin-level metadata exposed on `ITtscLintPlugin.meta`.
3
+ *
4
+ * All fields are optional. When `namespace` is absent, `@ttsc/lint` falls back
5
+ * to the key used in the tsconfig `plugins` map (or `lint.config.*` `plugins`
6
+ * object) as the rule-name prefix. `name` and `version` are purely
7
+ * informational — they appear in diagnostic messages and are not validated at
8
+ * build time.
9
+ */
2
10
  export interface ITtscLintPluginMeta {
3
11
  /** Plugin package name as published on npm. */
4
12
  name?: string;
@@ -1,8 +1,17 @@
1
1
  /**
2
- * Lint rule names understood by the `@ttsc/lint` native sidecar.
2
+ * Union of every built-in lint rule name understood by the `@ttsc/lint` native
3
+ * sidecar.
3
4
  *
4
5
  * These names intentionally mirror familiar ESLint / TypeScript-ESLint rule
5
6
  * names so a `compilerOptions.plugins[]` entry can configure native linting
6
7
  * without introducing a separate lint config file.
8
+ *
9
+ * The `format/*` members at the bottom of the union are applied by `ttsc
10
+ * format`; their severity also controls whether `ttsc check` surfaces
11
+ * unformatted code as a diagnostic.
12
+ *
13
+ * This type is used as the key constraint in `TtscLintRuleMap` for rules that
14
+ * carry no per-rule options. Rules with typed option objects are additionally
15
+ * listed in `ITtscLintRuleOptionsMap`.
7
16
  */
8
17
  export type TtscLintRule = "adjacent-overload-signatures" | "array-type" | "await-thenable" | "ban-ts-comment" | "ban-tslint-comment" | "consistent-indexed-object-style" | "consistent-type-assertions" | "consistent-type-definitions" | "consistent-type-imports" | "default-param-last" | "dot-notation" | "eqeqeq" | "for-direction" | "no-alert" | "no-array-constructor" | "no-array-delete" | "no-async-promise-executor" | "no-bitwise" | "no-caller" | "no-case-declarations" | "no-class-assign" | "no-compare-neg-zero" | "no-cond-assign" | "no-confusing-non-null-assertion" | "no-console" | "no-constant-condition" | "no-continue" | "no-control-regex" | "no-debugger" | "no-delete-var" | "no-dupe-args" | "no-dupe-else-if" | "no-dupe-keys" | "no-duplicate-case" | "no-duplicate-enum-values" | "no-dynamic-delete" | "no-empty" | "no-empty-character-class" | "no-empty-function" | "no-empty-interface" | "no-empty-object-type" | "no-empty-pattern" | "no-empty-static-block" | "no-eq-null" | "no-eval" | "no-ex-assign" | "no-explicit-any" | "no-extra-bind" | "no-extra-boolean-cast" | "no-extra-non-null-assertion" | "no-fallthrough" | "no-func-assign" | "no-import-type-side-effects" | "no-inferrable-types" | "no-inner-declarations" | "no-irregular-whitespace" | "no-iterator" | "no-labels" | "no-lone-blocks" | "no-lonely-if" | "no-loss-of-precision" | "no-misleading-character-class" | "no-misused-new" | "no-mixed-enums" | "no-multi-assign" | "no-multi-str" | "no-namespace" | "no-negated-condition" | "no-nested-ternary" | "no-new" | "no-new-func" | "no-new-wrappers" | "no-non-null-asserted-nullish-coalescing" | "no-non-null-asserted-optional-chain" | "no-non-null-assertion" | "no-obj-calls" | "no-object-constructor" | "no-octal" | "no-octal-escape" | "no-plusplus" | "no-promise-executor-return" | "no-proto" | "no-prototype-builtins" | "no-regex-spaces" | "no-require-imports" | "no-return-assign" | "no-script-url" | "no-self-assign" | "no-self-compare" | "no-sequences" | "no-setter-return" | "no-shadow-restricted-names" | "no-sparse-arrays" | "no-template-curly-in-string" | "no-this-alias" | "no-throw-literal" | "no-undef-init" | "no-undefined" | "no-unnecessary-type-constraint" | "no-unneeded-ternary" | "no-unsafe-declaration-merging" | "no-unsafe-finally" | "no-unsafe-function-type" | "no-unsafe-negation" | "no-unused-expressions" | "no-unused-labels" | "no-useless-call" | "no-useless-catch" | "no-useless-computed-key" | "no-useless-concat" | "no-useless-constructor" | "no-useless-escape" | "no-useless-rename" | "no-var" | "no-with" | "no-wrapper-object-types" | "object-shorthand" | "operator-assignment" | "prefer-as-const" | "prefer-const" | "prefer-enum-initializers" | "prefer-exponentiation-operator" | "prefer-for-of" | "prefer-function-type" | "prefer-literal-enum-member" | "prefer-namespace-keyword" | "prefer-spread" | "prefer-template" | "radix" | "require-yield" | "triple-slash-reference" | "use-isnan" | "valid-typeof" | "vars-on-top" | "yoda" | "format/semi" | "format/quotes" | "format/trailing-comma" | "format/sort-imports" | "format/jsdoc" | "format/print-width";
@@ -10,8 +10,8 @@ import type { TtscLintSeverity } from "./TtscLintSeverity";
10
10
  * `[severity, options]`, with the options type picked per rule key.
11
11
  * - Every other built-in rule accepts `severity` or `[severity]`.
12
12
  * - Namespaced contributor plugin rules accept `severity`, `[severity]`, or
13
- * `[severity, unknownOptions]` because the host type surface no longer
14
- * knows contributor rule schemas.
13
+ * `[severity, unknownOptions]` because the host type surface no longer knows
14
+ * contributor rule schemas.
15
15
  *
16
16
  * Splitting the two halves (instead of folding them into one mapped type with a
17
17
  * conditional value) keeps TypeScript's contextual typing intact inside the
@@ -89,6 +89,20 @@ export interface ITtscLintPrintWidthRuleOptions {
89
89
  * @default "lf"
90
90
  */
91
91
  endOfLine?: "lf" | "crlf";
92
+ /**
93
+ * Trailing-comma policy the reflow honors when it breaks a list across
94
+ * lines. Mirrors prettier's `trailingComma` and must match the
95
+ * `format/trailing-comma` rule's `mode`; otherwise the two rules
96
+ * disagree on every cascade pass and oscillate against each other.
97
+ *
98
+ * When a `format` block is configured, `format.trailingComma` is mirrored
99
+ * into this option automatically. Set it directly only when overriding
100
+ * the print-width rule via a `rules` tuple — see the conflict-resolution
101
+ * notes in the README.
102
+ *
103
+ * @default "all"
104
+ */
105
+ trailingComma?: "all" | "es5" | "none";
92
106
  }
93
107
  /** `format/jsdoc` rule options. */
94
108
  export interface ITtscLintJsdocRuleOptions {
@@ -96,6 +96,10 @@ func findKeyword(file *shimast.SourceFile, pos, end int, keyword string) int {
96
96
  return -1
97
97
  }
98
98
 
99
+ // tokenRange returns the half-open byte range [pos, end) of `node` with
100
+ // leading trivia stripped, mirroring what ReportFix would anchor to.
101
+ // Returns (-1, -1) when either argument is nil or the computed range is
102
+ // out of bounds.
99
103
  func tokenRange(file *shimast.SourceFile, node *shimast.Node) (int, int) {
100
104
  if file == nil || node == nil {
101
105
  return -1, -1
@@ -109,6 +113,10 @@ func tokenRange(file *shimast.SourceFile, node *shimast.Node) (int, int) {
109
113
  return pos, end
110
114
  }
111
115
 
116
+ // isIdentifierPart reports whether `ch` can appear inside a JavaScript
117
+ // identifier — used as a word-boundary guard by keyword search helpers.
118
+ // Handles only ASCII; multibyte Unicode identifier parts are treated as
119
+ // non-identifier (conservative; callers only need ASCII keyword tokens).
112
120
  func isIdentifierPart(ch byte) bool {
113
121
  return (ch >= 'a' && ch <= 'z') ||
114
122
  (ch >= 'A' && ch <= 'Z') ||
@@ -272,28 +280,89 @@ func walkDescendants(node *shimast.Node, visit func(*shimast.Node)) {
272
280
  })
273
281
  }
274
282
 
275
- func bindingIdentifierNames(node *shimast.Node) []string {
283
+ // assignmentTargetNames collects the identifier names written by an
284
+ // assignment's left-hand side. A bare Identifier yields one name. A
285
+ // destructuring-assignment target — parsed as an ArrayLiteralExpression
286
+ // (`[a, b] = …`) or ObjectLiteralExpression (`({a} = …)`) rather than a
287
+ // binding pattern — is walked so every nested write position is counted:
288
+ // array elements, object property values, shorthand properties, defaults
289
+ // (`[a = 1]`, `{a = 1}`), nested patterns, and rest elements.
290
+ //
291
+ // Property names in `{key: target}` are read positions, not writes, so
292
+ // only the property value contributes; member-access targets (`obj.x`)
293
+ // declare no local binding and are skipped. Returns nil for other shapes.
294
+ func assignmentTargetNames(node *shimast.Node) []string {
276
295
  if node == nil {
277
296
  return nil
278
297
  }
279
298
  if name := identifierText(node); name != "" {
280
299
  return []string{name}
281
300
  }
282
- if node.Kind != shimast.KindObjectBindingPattern && node.Kind != shimast.KindArrayBindingPattern {
283
- return nil
284
- }
285
301
  var names []string
286
- walkDescendants(node, func(child *shimast.Node) {
287
- if child == node {
288
- return
302
+ collectAssignmentTargetNames(node, &names)
303
+ return names
304
+ }
305
+
306
+ // collectAssignmentTargetNames appends to `names` every identifier in a
307
+ // destructuring-assignment target. It descends only through write-target
308
+ // positions so reads (object property keys, computed-member expressions)
309
+ // never count as reassignments.
310
+ func collectAssignmentTargetNames(node *shimast.Node, names *[]string) {
311
+ if node == nil {
312
+ return
313
+ }
314
+ switch node.Kind {
315
+ case shimast.KindIdentifier:
316
+ if name := identifierText(node); name != "" {
317
+ *names = append(*names, name)
289
318
  }
290
- if name := identifierText(child); name != "" {
291
- names = append(names, name)
319
+ case shimast.KindParenthesizedExpression:
320
+ collectAssignmentTargetNames(stripParens(node), names)
321
+ case shimast.KindArrayLiteralExpression:
322
+ if arr := node.AsArrayLiteralExpression(); arr != nil && arr.Elements != nil {
323
+ for _, el := range arr.Elements.Nodes {
324
+ collectAssignmentTargetNames(el, names)
325
+ }
292
326
  }
293
- })
294
- return names
327
+ case shimast.KindObjectLiteralExpression:
328
+ if obj := node.AsObjectLiteralExpression(); obj != nil && obj.Properties != nil {
329
+ for _, prop := range obj.Properties.Nodes {
330
+ collectAssignmentTargetNames(prop, names)
331
+ }
332
+ }
333
+ case shimast.KindSpreadElement:
334
+ if spread := node.AsSpreadElement(); spread != nil {
335
+ collectAssignmentTargetNames(spread.Expression, names)
336
+ }
337
+ case shimast.KindSpreadAssignment:
338
+ if spread := node.AsSpreadAssignment(); spread != nil {
339
+ collectAssignmentTargetNames(spread.Expression, names)
340
+ }
341
+ case shimast.KindShorthandPropertyAssignment:
342
+ // `{a}` and `{a = 1}` — the property name is the write target; any
343
+ // ObjectAssignmentInitializer is a default value, not a target.
344
+ if short := node.AsShorthandPropertyAssignment(); short != nil {
345
+ collectAssignmentTargetNames(short.Name(), names)
346
+ }
347
+ case shimast.KindPropertyAssignment:
348
+ // `{key: target}` — only the value (initializer) is written to.
349
+ if assignment := node.AsPropertyAssignment(); assignment != nil {
350
+ collectAssignmentTargetNames(assignment.Initializer, names)
351
+ }
352
+ case shimast.KindBinaryExpression:
353
+ // A default inside a pattern (`[a = 1]`, `{key: a = 1}`) parses as an
354
+ // `=` BinaryExpression; only its left side is the write target.
355
+ if expr := node.AsBinaryExpression(); expr != nil &&
356
+ expr.OperatorToken != nil && expr.OperatorToken.Kind == shimast.KindEqualsToken {
357
+ collectAssignmentTargetNames(expr.Left, names)
358
+ }
359
+ }
295
360
  }
296
361
 
362
+ // isLiteralLike reports whether `node` (after stripping parentheses) is a
363
+ // compile-time constant expression: a bare literal or a unary `+`/`-`
364
+ // applied to a numeric or bigint literal. Used by rules that flag
365
+ // constant-valued operands (e.g. `no-constant-condition`).
297
366
  func isLiteralLike(node *shimast.Node) bool {
298
367
  node = stripParens(node)
299
368
  if node == nil {