@ttsc/lint 0.10.1 → 0.11.0-dev.20260517

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 (113) hide show
  1. package/README.md +172 -33
  2. package/lib/defineConfig.d.ts +55 -0
  3. package/lib/defineConfig.js +39 -0
  4. package/lib/defineConfig.js.map +1 -0
  5. package/lib/index.d.ts +25 -1
  6. package/lib/index.js +622 -2
  7. package/lib/index.js.map +1 -1
  8. package/lib/structures/ITtscLintPlugin.d.ts +53 -0
  9. package/lib/structures/ITtscLintPlugin.js +3 -0
  10. package/lib/structures/ITtscLintPlugin.js.map +1 -0
  11. package/lib/structures/ITtscLintPluginConfig.d.ts +82 -5
  12. package/lib/structures/ITtscLintPluginMeta.d.ts +12 -0
  13. package/lib/structures/ITtscLintPluginMeta.js +3 -0
  14. package/lib/structures/ITtscLintPluginMeta.js.map +1 -0
  15. package/lib/structures/PluginRuleNames.d.ts +14 -0
  16. package/lib/structures/PluginRuleNames.js +3 -0
  17. package/lib/structures/PluginRuleNames.js.map +1 -0
  18. package/lib/structures/TtscLintConfig.d.ts +24 -8
  19. package/lib/structures/TtscLintConfigEntry.d.ts +39 -0
  20. package/lib/structures/TtscLintConfigEntry.js +3 -0
  21. package/lib/structures/TtscLintConfigEntry.js.map +1 -0
  22. package/lib/structures/TtscLintFormatConfig.d.ts +154 -0
  23. package/lib/structures/TtscLintFormatConfig.js +3 -0
  24. package/lib/structures/TtscLintFormatConfig.js.map +1 -0
  25. package/lib/structures/TtscLintPlugins.d.ts +9 -0
  26. package/lib/structures/TtscLintPlugins.js +3 -0
  27. package/lib/structures/TtscLintPlugins.js.map +1 -0
  28. package/lib/structures/TtscLintRule.d.ts +1 -1
  29. package/lib/structures/TtscLintRuleEntry.d.ts +22 -0
  30. package/lib/structures/TtscLintRuleEntry.js +3 -0
  31. package/lib/structures/TtscLintRuleEntry.js.map +1 -0
  32. package/lib/structures/TtscLintRuleMap.d.ts +44 -0
  33. package/lib/structures/TtscLintRuleMap.js +3 -0
  34. package/lib/structures/TtscLintRuleMap.js.map +1 -0
  35. package/lib/structures/TtscLintRuleOptions.d.ts +149 -0
  36. package/lib/structures/TtscLintRuleOptions.js +3 -0
  37. package/lib/structures/TtscLintRuleOptions.js.map +1 -0
  38. package/lib/structures/defaultFormat.d.ts +31 -0
  39. package/lib/structures/defaultFormat.js +35 -0
  40. package/lib/structures/defaultFormat.js.map +1 -0
  41. package/lib/structures/index.d.ts +10 -0
  42. package/lib/structures/index.js +10 -0
  43. package/lib/structures/index.js.map +1 -1
  44. package/{plugin → linthost}/ast_helpers.go +92 -1
  45. package/{plugin → linthost}/compile.go +1 -1
  46. package/{plugin → linthost}/config.go +583 -66
  47. package/linthost/config_format.go +360 -0
  48. package/linthost/contrib_adapter.go +134 -0
  49. package/{plugin → linthost}/directives.go +1 -1
  50. package/linthost/dispatch.go +62 -0
  51. package/{plugin → linthost}/engine.go +156 -11
  52. package/{plugin → linthost}/eslint_runtime.go +76 -2
  53. package/linthost/fix.go +249 -0
  54. package/linthost/format.go +107 -0
  55. package/{plugin → linthost}/host.go +1 -1
  56. package/linthost/print_dispatch.go +118 -0
  57. package/linthost/print_doc.go +168 -0
  58. package/linthost/print_engine.go +343 -0
  59. package/linthost/print_nodes_array.go +46 -0
  60. package/linthost/print_nodes_call.go +180 -0
  61. package/linthost/print_nodes_imports.go +171 -0
  62. package/linthost/print_nodes_list.go +64 -0
  63. package/linthost/print_nodes_object.go +51 -0
  64. package/{plugin → linthost}/rules_arrays.go +1 -1
  65. package/{plugin → linthost}/rules_console.go +1 -1
  66. package/{plugin → linthost}/rules_debugger.go +1 -1
  67. package/{plugin → linthost}/rules_dupes.go +1 -1
  68. package/{plugin → linthost}/rules_empty.go +1 -1
  69. package/linthost/rules_escape.go +238 -0
  70. package/{plugin → linthost}/rules_eval.go +1 -1
  71. package/{plugin → linthost}/rules_finally.go +1 -1
  72. package/linthost/rules_format_jsdoc.go +230 -0
  73. package/linthost/rules_format_print_width.go +332 -0
  74. package/linthost/rules_format_quotes.go +219 -0
  75. package/linthost/rules_format_semi.go +190 -0
  76. package/linthost/rules_format_sort_imports.go +462 -0
  77. package/linthost/rules_format_trailing_comma.go +460 -0
  78. package/{plugin → linthost}/rules_gap.go +101 -6
  79. package/linthost/rules_imports.go +101 -0
  80. package/{plugin → linthost}/rules_logic.go +55 -3
  81. package/{plugin → linthost}/rules_loops.go +1 -1
  82. package/{plugin → linthost}/rules_misc.go +1 -1
  83. package/linthost/rules_params.go +64 -0
  84. package/{plugin → linthost}/rules_problems.go +10 -2
  85. package/linthost/rules_promise.go +111 -0
  86. package/{plugin → linthost}/rules_protos.go +1 -1
  87. package/{plugin → linthost}/rules_self.go +1 -1
  88. package/{plugin → linthost}/rules_strings.go +1 -1
  89. package/{plugin → linthost}/rules_suggestions.go +51 -9
  90. package/{plugin → linthost}/rules_throw.go +1 -1
  91. package/{plugin → linthost}/rules_ts.go +12 -2
  92. package/{plugin → linthost}/rules_ts_extra.go +90 -4
  93. package/{plugin → linthost}/rules_var.go +36 -5
  94. package/package.json +8 -6
  95. package/plugin/main.go +14 -27
  96. package/rule/astutil/astutil.go +148 -0
  97. package/rule/rule.go +291 -0
  98. package/src/defineConfig.ts +69 -0
  99. package/src/index.ts +807 -3
  100. package/src/structures/ITtscLintPlugin.ts +56 -0
  101. package/src/structures/ITtscLintPluginConfig.ts +86 -5
  102. package/src/structures/ITtscLintPluginMeta.ts +14 -0
  103. package/src/structures/PluginRuleNames.ts +19 -0
  104. package/src/structures/TtscLintConfig.ts +27 -8
  105. package/src/structures/TtscLintConfigEntry.ts +50 -0
  106. package/src/structures/TtscLintFormatConfig.ts +169 -0
  107. package/src/structures/TtscLintPlugins.ts +10 -0
  108. package/src/structures/TtscLintRule.ts +14 -1
  109. package/src/structures/TtscLintRuleEntry.ts +28 -0
  110. package/src/structures/TtscLintRuleMap.ts +52 -0
  111. package/src/structures/TtscLintRuleOptions.ts +162 -0
  112. package/src/structures/defaultFormat.ts +33 -0
  113. package/src/structures/index.ts +10 -0
package/README.md CHANGED
@@ -18,7 +18,7 @@ Type errors and lint violations appear in one `ttsc` run.
18
18
  Given this file:
19
19
 
20
20
  ```typescript
21
- // src/lint.ts
21
+ // src/index.ts
22
22
  var x: number = 3;
23
23
  let y: number = 4;
24
24
  const z: string = 5;
@@ -30,22 +30,22 @@ Run `ttsc` with `@ttsc/lint` enabled (see [Setup](#setup)):
30
30
 
31
31
  ```bash
32
32
  $ pnpm ttsc
33
- src/lint.ts:3:7 - error TS2322: Type 'number' is not assignable to type 'string'.
33
+ src/index.ts:3:7 - error TS2322: Type 'number' is not assignable to type 'string'.
34
34
 
35
35
  3 const z: string = 5;
36
36
  ~
37
37
 
38
- src/lint.ts:2:5 - error TS17397: [prefer-const] Use const instead of let.
38
+ src/index.ts:2:5 - error TS17397: [prefer-const] Use const instead of let.
39
39
 
40
40
  2 let y: number = 4;
41
41
  ~~~~~~~~~~~~~
42
42
 
43
- src/lint.ts:1:1 - error TS11966: [no-var] Unexpected var, use let or const instead.
43
+ src/index.ts:1:1 - error TS11966: [no-var] Unexpected var, use let or const instead.
44
44
 
45
45
  1 var x: number = 3;
46
46
  ~~~~~~~~~~~~~~~~~~
47
47
 
48
- Found 3 errors in the same file, starting at: src/lint.ts:3
48
+ Found 3 errors in the same file, starting at: src/index.ts:3
49
49
  ```
50
50
 
51
51
  Type errors (`TS2322`) and lint violations (`TS17397`, `TS11966`) come out together, in the same `error TSxxxxx` shape.
@@ -54,65 +54,189 @@ CI that already runs `ttsc` blocks on lint with no extra wiring.
54
54
 
55
55
  ## Setup
56
56
 
57
- Install `ttsc` and TypeScript-Go, then the lint plugin:
57
+ Install the plugin alongside `ttsc` and TypeScript-Go:
58
58
 
59
59
  ```bash
60
- npm install -D ttsc @typescript/native-preview
61
- npm install -D @ttsc/lint
60
+ npm install -D ttsc @typescript/native-preview @ttsc/lint
62
61
  ```
63
62
 
64
- Add `lint.config.ts`, or reuse an existing `eslint.config.ts`, next to your project config. If no config file or inline config is found, the compile fails.
63
+ Add a `lint.config.ts` next to your `tsconfig.json`:
65
64
 
66
65
  ```ts
67
66
  // lint.config.ts
68
- import type { TtscLintConfig } from "@ttsc/lint/config";
67
+ import type { TtscLintConfig } from "@ttsc/lint";
69
68
 
70
69
  export default {
71
- "no-var": "error",
72
- "prefer-const": "error",
73
- "no-explicit-any": "warning",
74
- "no-console": "off",
70
+ rules: {
71
+ "no-var": "error",
72
+ "prefer-const": "error",
73
+ "no-explicit-any": "warning",
74
+ "no-console": "off",
75
+ },
76
+ } satisfies TtscLintConfig;
77
+ ```
78
+
79
+ Run your normal `ttsc` or `ttsx`:
80
+
81
+ ```bash
82
+ npx ttsc
83
+ npx ttsx src/index.ts
84
+ ```
85
+
86
+ Errors fail the command; warnings print without affecting the exit code. Under `ttsx`, errors stop the program before your entrypoint runs.
87
+
88
+ > Alternate config surfaces — inline `compilerOptions.plugins[].rules`, `extends` paths, and `eslint.config.*` reuse — are described in [Config Files](#config-files) below.
89
+
90
+ ### Fix
91
+
92
+ ```bash
93
+ npx ttsc fix
94
+ ```
95
+
96
+ Applies every autofix the enabled rules offer (lint *and* format), writes the result to disk, then runs the usual no-emit typecheck + lint pass.
97
+
98
+ Native fixers currently cover:
99
+
100
+ - `no-var`, single-declaration `prefer-const`, ESLint-safe `eqeqeq`.
101
+ - `no-wrapper-object-types` (`String`/`Number`/`Boolean`/`Symbol`/`BigInt` → primitive; `Object` stays detection-only).
102
+ - `prefer-as-const`, `no-useless-rename`, `object-shorthand`, `no-extra-non-null-assertion`.
103
+ - `no-unnecessary-type-constraint`, `prefer-namespace-keyword`, `no-useless-escape`.
104
+ - `no-import-type-side-effects` (hoists inline `type` modifiers).
105
+ - `await-thenable` (type-aware — drops `await` on non-thenable operands).
106
+
107
+ If your project uses `eslint.config.*` with an installed ESLint runtime, `ttsc fix` delegates to ESLint's own fixers and reloads the Program before reporting remaining diagnostics.
108
+
109
+ `ttsc fix` is a one-shot project pass and rejects `--watch`, single-file mode, and `--emit`. Fixes are written to disk before the recheck runs, so source stays modified even when the command exits non-zero on remaining errors.
110
+
111
+ Recommended flow: run `ttsc fix` locally, commit, then have CI run `ttsc --noEmit` to gate on zero remaining errors.
112
+
113
+ ### Format
114
+
115
+ ```bash
116
+ npx ttsc format
117
+ ```
118
+
119
+ Applies the format-class rules and exits silently. `ttsc fix` does the same plus the lint cascade; pick `format` when you want to reshape source without lint rewrites.
120
+
121
+ Configure formatting with a Prettier-style `format` block — keys mirror `.prettierrc`:
122
+
123
+ ```ts
124
+ // lint.config.ts
125
+ import type { TtscLintConfig } from "@ttsc/lint";
126
+
127
+ export default {
128
+ rules: { "no-var": "error" },
129
+ format: {
130
+ printWidth: 100,
131
+ singleQuote: true,
132
+ trailingComma: "all",
133
+ importOrder: ["<THIRD_PARTY_MODULES>", "@api(.*)$", "^[./]"],
134
+ },
75
135
  } satisfies TtscLintConfig;
76
136
  ```
77
137
 
78
- Use `compilerOptions.plugins` only when the project needs inline config or an explicit config file path.
138
+ Presence (even empty `format: {}`) enables the always-on rules at Prettier defaults. Setting `importOrder` enables `format/sort-imports`; setting `jsdoc: true` (or an options object) enables `format/jsdoc`. Omit the `format` block entirely to keep every format rule off — same as today's opt-in default.
139
+
140
+ | Rule | Driven by | Effect |
141
+ | --- | --- | --- |
142
+ | `format/semi` | `semi` | Insert trailing semicolons on ASI-terminated statements. |
143
+ | `format/quotes` | `singleQuote` | Convert quoted strings to the preferred quote style. |
144
+ | `format/trailing-comma` | `trailingComma` | Add trailing commas to multi-line lists. |
145
+ | `format/print-width` | `printWidth`, `tabWidth`, `useTabs`, `endOfLine` | Prettier-style line reflow. Object/array literals, call/new arguments, and named import/export clauses break across lines when their flat form overflows the budget. See [docs/13-format-print-width.md](https://github.com/samchon/ttsc/blob/master/docs/13-format-print-width.md). |
146
+ | `format/sort-imports` | `importOrder*` | Group external/relative imports and alphabetize each group + its specifiers. |
147
+ | `format/jsdoc` | `jsdoc` | Normalize JSDoc blocks toward [prettier-plugin-jsdoc](https://github.com/hosseinmd/prettier-plugin-jsdoc). |
148
+
149
+ `format.severity` (default `"warning"`) sets the diagnostic level for every format rule in `ttsc check`. `severity: "off"` disables every format rule AND skips its rewrite under `ttsc format` — a one-line CI escape hatch.
150
+
151
+ To override a single rule's severity or options, drop a sibling `rules` entry — `rules` wins on conflict:
152
+
153
+ ```ts
154
+ export default {
155
+ format: { semi: true },
156
+ rules: { "format/semi": "error" }, // bump severity for this one rule only
157
+ } satisfies TtscLintConfig;
158
+ ```
159
+
160
+ Migrating from a `.prettierrc`? See [docs/14-prettier-migration.md](https://github.com/samchon/ttsc/blob/master/docs/14-prettier-migration.md) for the field-by-field cheat sheet and the list of Prettier knobs `@ttsc/lint` does not yet support.
161
+
162
+ ### Config Files
163
+
164
+ `@ttsc/lint` discovers config from one of three surfaces, in order:
165
+
166
+ 1. **Inline `tsconfig.json` rules** — most local, overrides everything else for that plugin entry.
167
+
168
+ ```jsonc
169
+ {
170
+ "compilerOptions": {
171
+ "plugins": [
172
+ {
173
+ "transform": "@ttsc/lint",
174
+ "rules": { "no-var": "error", "prefer-const": "error" },
175
+ },
176
+ ],
177
+ },
178
+ }
179
+ ```
180
+
181
+ 2. **`extends` path** — relative path to a standalone config file.
182
+
183
+ ```jsonc
184
+ {
185
+ "compilerOptions": {
186
+ "plugins": [
187
+ { "transform": "@ttsc/lint", "extends": "./lint.config.ts" },
188
+ ],
189
+ },
190
+ }
191
+ ```
192
+
193
+ 3. **Auto-discovery** — walks upward from the `tsconfig.json` directory and uses the first match:
194
+
195
+ - `lint.config.{json,js,mjs,cjs,ts,mts,cts}`
196
+ - `ttsc-lint.config.{json,js,mjs,cjs,ts,mts,cts}`
197
+ - `eslint.config.{js,mjs,cjs,ts,mts,cts}` (flat-config compatibility)
198
+
199
+ `.ts` configs load directly through `ttsx` — no compile step required. `rules` and `extends` cannot be combined on a single plugin entry; pick one or rely on auto-discovery.
200
+
201
+ ### Third-Party Rule Plugins
202
+
203
+ Third-party packages can contribute Go-implemented lint rules that compile into the same `@ttsc/lint` binary and report through the same diagnostic stream as the built-ins. Two declaration surfaces:
79
204
 
80
205
  ```jsonc
206
+ // tsconfig.json — inline declaration
81
207
  {
82
208
  "compilerOptions": {
83
209
  "plugins": [
84
210
  {
85
211
  "transform": "@ttsc/lint",
86
- "config": {
87
- "no-var": "error",
88
- "prefer-const": "error",
89
- "no-explicit-any": "warning",
90
- "no-console": "off",
91
- },
212
+ "plugins": { "demo": "ttsc-lint-plugin-demo" },
213
+ "rules": { "demo/no-todo-comment": "error" },
92
214
  },
93
215
  ],
94
216
  },
95
217
  }
96
218
  ```
97
219
 
98
- Then run your normal `ttsc` or `ttsx`:
99
-
100
- ```bash
101
- npx ttsc
102
- npx ttsx src/index.ts
220
+ ```ts
221
+ // lint.config.ts — ESLint flat-config style
222
+ import demoPlugin from "ttsc-lint-plugin-demo";
223
+ import { defineConfig } from "@ttsc/lint";
224
+
225
+ export default defineConfig([
226
+ {
227
+ plugins: { demo: demoPlugin },
228
+ rules: { "demo/no-todo-comment": "error" },
229
+ },
230
+ ]);
103
231
  ```
104
232
 
105
- - Lint errors fail the command.
106
- - Under `ttsx`, lint errors stop the program before your entrypoint runs.
107
- - Lint warnings are printed without changing the exit code.
108
-
109
- ### Config Files
233
+ ttsc copies each declared contributor's Go source into a sub-package of `@ttsc/lint`'s module at build time, so the resulting binary has both built-in and contributor rules registered before `main`. Authoring instructions and the public Go API live in [`docs/10-reference-plugins.md`](https://github.com/samchon/ttsc/blob/master/docs/10-reference-plugins.md#authoring-a-lint-rule-contributor).
110
234
 
111
- By default, `@ttsc/lint` reads config files such as `lint.config.ts` or `eslint.config.ts` next to the selected `tsconfig.json`.
235
+ Contributor rules emit autofixes the same way built-ins do — call `ctx.ReportFix(node, message, edits...)` or `ctx.ReportRangeFix(pos, end, message, edits...)`. The `rule/astutil` package re-exports the byte-range helpers built-ins use (`NodeText`, `KeywordStart`, `FindKeyword`, `TokenRange`). See the [Emitting Autofixes](https://github.com/samchon/ttsc/blob/master/docs/10-reference-plugins.md#emitting-autofixes) section for the full contract and an example.
112
236
 
113
237
  ## Scope
114
238
 
115
- Diagnostic-only today: no autofix and no bundled recommended preset.
239
+ No bundled recommended preset yet. Rules remain off until you enable them.
116
240
 
117
241
  ## Rules
118
242
 
@@ -129,16 +253,20 @@ Rules are off until you enable them:
129
253
 
130
254
  Rule severities are `"error"`, `"warning"`, and `"off"`.
131
255
 
256
+ Format rules (`format/semi`, `format/quotes`, `format/trailing-comma`, `format/sort-imports`, `format/jsdoc`) are listed separately in the [Format](#format) section above with their per-rule option shapes. The catalog below covers lint-class rules only.
257
+
132
258
  The rule corpus is tested in `tests/test-lint/src/cases/*.ts`, which is the best place to check the exact patterns currently covered. Each rule below links to its tested fixture:
133
259
 
134
260
  - [`adjacent-overload-signatures`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/adjacent-overload-signatures.ts): keeps overload declarations for the same member adjacent.
135
261
  - [`array-type`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/array-type.ts): prefers `T[]` and `readonly T[]` over array helper types.
262
+ - [`await-thenable`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/await-thenable.ts): rejects `await` on a value that is neither a Promise nor a thenable (type-aware).
136
263
  - [`ban-ts-comment`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/ban-ts-comment.ts): rejects TypeScript suppression comments such as `@ts-ignore`.
137
264
  - [`ban-tslint-comment`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/ban-tslint-comment.ts): rejects obsolete `tslint:` comments.
138
265
  - [`consistent-indexed-object-style`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/consistent-indexed-object-style.ts): prefers `Record` for single index-signature object types.
139
266
  - [`consistent-type-assertions`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/consistent-type-assertions.ts): prefers `as` type assertions over angle-bracket assertions.
140
267
  - [`consistent-type-definitions`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/consistent-type-definitions.ts): prefers interfaces for object-shaped type definitions.
141
268
  - [`consistent-type-imports`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/consistent-type-imports/violation.ts): uses `import type` when imported names are type-only.
269
+ - [`default-param-last`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/default-param-last.ts): keeps parameters with default values at the end of the list.
142
270
  - [`dot-notation`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/dot-notation.ts): prefers dot property access when a string-literal key is a valid identifier.
143
271
  - [`eqeqeq`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/eqeqeq.ts): requires strict equality operators.
144
272
  - [`for-direction`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/for-direction.ts): catches loop counters updated in the wrong direction.
@@ -181,6 +309,7 @@ The rule corpus is tested in `tests/test-lint/src/cases/*.ts`, which is the best
181
309
  - [`no-extra-non-null-assertion`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-extra-non-null-assertion.ts): rejects repeated non-null assertions.
182
310
  - [`no-fallthrough`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-fallthrough.ts): rejects unmarked `switch` fallthrough.
183
311
  - [`no-func-assign`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-func-assign.ts): rejects reassignment of function declarations.
312
+ - [`no-import-type-side-effects`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-import-type-side-effects/violation.ts): hoists inline `type` modifiers into a single `import type` declaration.
184
313
  - [`no-inferrable-types`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-inferrable-types.ts): rejects type annotations TypeScript can infer.
185
314
  - [`no-inner-declarations`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-inner-declarations.ts): rejects function declarations nested in blocks.
186
315
  - [`no-irregular-whitespace`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-irregular-whitespace.ts): rejects irregular whitespace.
@@ -191,6 +320,7 @@ The rule corpus is tested in `tests/test-lint/src/cases/*.ts`, which is the best
191
320
  - [`no-loss-of-precision`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-loss-of-precision.ts): rejects number literals that lose precision.
192
321
  - [`no-misleading-character-class`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-misleading-character-class.ts): rejects misleading regex character classes.
193
322
  - [`no-misused-new`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-misused-new.ts): rejects constructor-like signatures in interfaces.
323
+ - [`no-mixed-enums`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-mixed-enums.ts): rejects enums that mix numeric and string members.
194
324
  - [`no-multi-assign`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-multi-assign.ts): rejects chained assignments.
195
325
  - [`no-multi-str`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-multi-str.ts): rejects multiline string escapes.
196
326
  - [`no-namespace`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-namespace.ts): rejects non-ambient namespaces.
@@ -238,6 +368,7 @@ The rule corpus is tested in `tests/test-lint/src/cases/*.ts`, which is the best
238
368
  - [`no-useless-computed-key`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-useless-computed-key.ts): rejects unnecessary computed property keys.
239
369
  - [`no-useless-concat`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-useless-concat.ts): rejects unnecessary string concatenation.
240
370
  - [`no-useless-constructor`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-useless-constructor.ts): rejects empty constructors with no parameters.
371
+ - [`no-useless-escape`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-useless-escape.ts): rejects backslash escapes that have no effect inside strings or regexes.
241
372
  - [`no-useless-rename`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-useless-rename.ts): rejects import/export/destructure renames to the same name.
242
373
  - [`no-var`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-var.ts): rejects `var`.
243
374
  - [`no-with`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/no-with.ts): rejects `with` statements.
@@ -261,3 +392,11 @@ The rule corpus is tested in `tests/test-lint/src/cases/*.ts`, which is the best
261
392
  - [`valid-typeof`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/valid-typeof.ts): restricts `typeof` comparisons to valid strings.
262
393
  - [`vars-on-top`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/vars-on-top.ts): requires `var` declarations at the top of their scope.
263
394
  - [`yoda`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/yoda.ts): rejects literal-first comparisons.
395
+
396
+ ## Sponsors
397
+
398
+ [![Sponsors](https://raw.githubusercontent.com/samchon/sponsor-images/refs/heads/master/public/circle.svg)](https://github.com/sponsors/samchon)
399
+
400
+ Thanks for your support.
401
+
402
+ Your [donation](https://github.com/sponsors/samchon) encourages `ttsc` development.
@@ -0,0 +1,55 @@
1
+ import type { TtscLintConfig } from "./structures/TtscLintConfig";
2
+ import type { TtscLintPlugins } from "./structures/TtscLintPlugins";
3
+ /**
4
+ * Authoring helper that preserves the literal type of a lint config.
5
+ *
6
+ * ESLint's flat-config experience relies on `defineConfig` to capture the
7
+ * const-narrowed type of the configuration array, which is what makes plugin
8
+ * rule names autocomplete across entries. `@ttsc/lint` follows the same
9
+ * pattern: pass your config through this helper and the contributor plugin
10
+ * objects in `plugins` propagate their `rules` tuples into the rule-name
11
+ * union.
12
+ *
13
+ * ```ts
14
+ * import { defineConfig } from "@ttsc/lint";
15
+ * import importPlugin from "@ttsc/lint-plugin-import";
16
+ *
17
+ * export default defineConfig([
18
+ * {
19
+ * plugins: { import: importPlugin },
20
+ * rules: {
21
+ * "no-var": "error",
22
+ * "import/no-cycle": ["error", { maxDepth: 1 }],
23
+ * },
24
+ * },
25
+ * ]);
26
+ * ```
27
+ *
28
+ * The function is a pure pass-through at runtime. The generic gymnastics below
29
+ * gather every `plugins` map across the array of config entries into one
30
+ * intersected `TtscLintPlugins` shape that gets threaded back into the
31
+ * `TtscLintConfig<P>` constraint. Without this, `TtscLintConfig`'s default `P =
32
+ * Record<string, never>` rules out every namespaced rule name and `{
33
+ * "import/no-cycle": "error" }` would be flagged as a typo.
34
+ */
35
+ export declare function defineConfig<const T extends TtscLintConfig<GatherPlugins<T>>>(config: T): T;
36
+ /**
37
+ * Walks the input type to collect every plugin map declared across entries.
38
+ * Single entries yield the entry's `plugins`; arrays intersect every entry's
39
+ * `plugins` so each entry's rule-name union remains valid for the whole array.
40
+ * The intersection (vs. union) is load-bearing — TypeScript's `keyof (A | B)`
41
+ * collapses to `never`, which would reject every namespaced rule name.
42
+ *
43
+ * Plugins declared exclusively inside an `extends` chain are NOT collected:
44
+ * recursing into `T extends { extends?: infer E } ? GatherPlugins<E> : {}`
45
+ * blows TypeScript's instantiation budget (`extends` is itself a
46
+ * `TtscLintConfigEntry` tree). Authors should declare each plugin's `plugins`
47
+ * map on the outermost entry where its rules are configured. The runtime
48
+ * resolver still picks up plugins from `extends` entries; only the autocomplete
49
+ * domain is bounded.
50
+ */
51
+ type GatherPlugins<T> = T extends {
52
+ plugins?: infer P;
53
+ } ? P extends TtscLintPlugins ? P : {} : T extends readonly (infer Item)[] ? UnionToIntersection<Item extends unknown ? GatherPlugins<Item> : never> : {};
54
+ type UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
55
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineConfig = defineConfig;
4
+ /**
5
+ * Authoring helper that preserves the literal type of a lint config.
6
+ *
7
+ * ESLint's flat-config experience relies on `defineConfig` to capture the
8
+ * const-narrowed type of the configuration array, which is what makes plugin
9
+ * rule names autocomplete across entries. `@ttsc/lint` follows the same
10
+ * pattern: pass your config through this helper and the contributor plugin
11
+ * objects in `plugins` propagate their `rules` tuples into the rule-name
12
+ * union.
13
+ *
14
+ * ```ts
15
+ * import { defineConfig } from "@ttsc/lint";
16
+ * import importPlugin from "@ttsc/lint-plugin-import";
17
+ *
18
+ * export default defineConfig([
19
+ * {
20
+ * plugins: { import: importPlugin },
21
+ * rules: {
22
+ * "no-var": "error",
23
+ * "import/no-cycle": ["error", { maxDepth: 1 }],
24
+ * },
25
+ * },
26
+ * ]);
27
+ * ```
28
+ *
29
+ * The function is a pure pass-through at runtime. The generic gymnastics below
30
+ * gather every `plugins` map across the array of config entries into one
31
+ * intersected `TtscLintPlugins` shape that gets threaded back into the
32
+ * `TtscLintConfig<P>` constraint. Without this, `TtscLintConfig`'s default `P =
33
+ * Record<string, never>` rules out every namespaced rule name and `{
34
+ * "import/no-cycle": "error" }` would be flagged as a typo.
35
+ */
36
+ function defineConfig(config) {
37
+ return config;
38
+ }
39
+ //# sourceMappingURL=defineConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineConfig.js","sourceRoot":"","sources":["../src/defineConfig.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,sBACE,MAAS;IAET,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/lib/index.d.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  import type { ITtscLintPluginConfig } from "./structures";
2
+ export * from "./defineConfig";
2
3
  export * from "./structures/index";
4
+ type TtscPluginContributor = {
5
+ name: string;
6
+ source: string;
7
+ };
3
8
  type TtscPluginDescriptor = {
4
9
  name: string;
5
10
  source: string;
6
11
  stage?: "check" | "transform";
12
+ contributors?: TtscPluginContributor[];
7
13
  };
8
14
  type TtscPluginFactoryContext<TConfig> = {
9
15
  binary: string;
@@ -12,4 +18,22 @@ type TtscPluginFactoryContext<TConfig> = {
12
18
  projectRoot: string;
13
19
  tsconfig: string;
14
20
  };
15
- export default function createTtscPlugin(_context: TtscPluginFactoryContext<ITtscLintPluginConfig>): TtscPluginDescriptor;
21
+ /**
22
+ * Plugin descriptor factory for `@ttsc/lint`.
23
+ *
24
+ * Two discovery surfaces feed the descriptor's `contributors` field:
25
+ *
26
+ * 1. The tsconfig plugin entry's `plugins` map — namespace → npm specifier. Inline
27
+ * for projects that prefer to keep everything in `tsconfig.json`.
28
+ * 2. The companion `lint.config.{ts,cts,mts,js,cjs,mjs,json}` (or
29
+ * `eslint.config.*`) file — flat-config-style with an in-memory `plugins: {
30
+ * ns: pluginObject }` map. The factory evaluates the config (via ttsx for TS
31
+ * / ESM sources, `require` for CommonJS, `JSON.parse` for JSON) and walks
32
+ * every entry's `plugins` field.
33
+ *
34
+ * Contributions from both sources are merged with the tsconfig entry winning on
35
+ * namespace collisions, so a project can opt into a hand-curated subset of an
36
+ * external `lint.config.ts` by overriding specific namespaces in
37
+ * `tsconfig.json`.
38
+ */
39
+ export default function createTtscPlugin(context: TtscPluginFactoryContext<ITtscLintPluginConfig>): TtscPluginDescriptor;