@ttsc/lint 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +2 -2
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +3 -3
- package/linthost/contrib_adapter.go +12 -0
- package/linthost/declaration_rules.go +173 -0
- package/linthost/engine.go +32 -15
- package/linthost/lsp.go +4 -1
- package/package.json +3 -3
- package/rule/rule.go +15 -0
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +2 -2
- package/src/structures/rules/ITtscLintUnicornRules.ts +3 -3
package/README.md
CHANGED
|
@@ -817,7 +817,7 @@ Source: [`eslint-plugin-unicorn`](https://github.com/sindresorhus/eslint-plugin-
|
|
|
817
817
|
- [`unicorn/custom-error-definition`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-custom-error-definition.ts): require user-defined `Error` subclasses to set `name`, call `super(message)`, and assign their stack correctly.
|
|
818
818
|
- [`unicorn/empty-brace-spaces`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-empty-brace-spaces.ts): reject whitespace inside empty `{}` braces.
|
|
819
819
|
- [`unicorn/error-message`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-error-message.ts): require a non-empty `message` argument when constructing a built-in `Error`.
|
|
820
|
-
- [`unicorn/escape-case`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-escape-case.ts): require consistent case for escape sequences (`\xA9` over `\xa9`,
|
|
820
|
+
- [`unicorn/escape-case`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-escape-case.ts): require consistent case for escape sequences (`\xA9` over `\xa9`, `\u00B5` over `\u00b5`).
|
|
821
821
|
- [`unicorn/expiring-todo-comments`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-expiring-todo-comments.ts): require every `TODO`/`FIXME`/`XXX` comment to declare an expiration date or package version.
|
|
822
822
|
- [`unicorn/explicit-length-check`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-explicit-length-check.ts): require explicit comparison of `.length` / `.size` instead of relying on truthy coercion.
|
|
823
823
|
- [`unicorn/filename-case`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-filename-case.ts): enforce a single case style (kebab/camel/snake/pascal) for source filenames.
|
|
@@ -839,7 +839,7 @@ Source: [`eslint-plugin-unicorn`](https://github.com/sindresorhus/eslint-plugin-
|
|
|
839
839
|
- [`unicorn/no-document-cookie`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-document-cookie.ts): reject direct reads or assignments to `document.cookie`; use the Cookie Store API or a wrapper.
|
|
840
840
|
- [`unicorn/no-empty-file`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-empty-file.ts): reject source files whose only content is whitespace and/or comments.
|
|
841
841
|
- [`unicorn/no-for-loop`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-for-loop.ts): prefer `for...of` over index-based `for` loops over arrays.
|
|
842
|
-
- [`unicorn/no-hex-escape`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-hex-escape.ts): prefer Unicode escape (
|
|
842
|
+
- [`unicorn/no-hex-escape`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-hex-escape.ts): prefer Unicode escape (`\u00A9`) over hexadecimal escape (`\xA9`).
|
|
843
843
|
- [`unicorn/no-immediate-mutation`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-immediate-mutation.ts): reject mutating a value on the same expression that produces it (`[...x].push(y)`); separate the construction and the mutation.
|
|
844
844
|
- [`unicorn/no-instanceof-builtins`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-instanceof-builtins.ts): reject `instanceof Array`, `instanceof Error`, `instanceof Map`, etc.. They fail across realms and for subclasses.
|
|
845
845
|
- [`unicorn/no-invalid-fetch-options`](https://github.com/samchon/ttsc/blob/master/tests/test-lint/src/cases/unicorn-no-invalid-fetch-options.ts): reject GET / HEAD `fetch()` calls that also set a request `body`, which throws at runtime.
|
|
@@ -1132,6 +1132,8 @@ export default {
|
|
|
1132
1132
|
|
|
1133
1133
|
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 [contributor autofix path](https://ttsc.dev/docs/development/walkthroughs/lint#the-contributor-autofix-path) section for the full contract and an example.
|
|
1134
1134
|
|
|
1135
|
+
Contributor rules run on declaration files (`.d.ts`) by default. The engine skips its own value-level rules there — executable grammar cannot appear in a declaration file — but it cannot infer a third-party rule's shape, so contributors keep the conservative default. A rule that only inspects executable code can implement the optional `rule.DeclarationFileRule` marker (`VisitsDeclarationFiles() bool { return false }`) to get the same skip and save the dispatch on declaration-heavy projects.
|
|
1136
|
+
|
|
1135
1137
|
## Sponsors
|
|
1136
1138
|
|
|
1137
1139
|
[](https://github.com/sponsors/samchon)
|
|
@@ -230,9 +230,9 @@ export interface ITtscLintTestingLibraryRules {
|
|
|
230
230
|
*/
|
|
231
231
|
"testing-library/prefer-presence-queries"?: TtscLintRuleSetting;
|
|
232
232
|
/**
|
|
233
|
-
* Prefer `queryBy*` inside
|
|
233
|
+
* Prefer `queryBy*` inside disappearance waits.
|
|
234
234
|
*
|
|
235
|
-
*
|
|
235
|
+
* `waitFor` retries the callback against the disappearing element, but
|
|
236
236
|
* `getBy*` throws on the very state being waited for, which produces a noisy
|
|
237
237
|
* error in the trace each retry.
|
|
238
238
|
*
|
|
@@ -94,8 +94,8 @@ export interface ITtscLintUnicornRules {
|
|
|
94
94
|
*/
|
|
95
95
|
"unicorn/error-message"?: TtscLintRuleSetting;
|
|
96
96
|
/**
|
|
97
|
-
* Require consistent case for escape sequences (`\xA9` over `\xa9`,
|
|
98
|
-
*
|
|
97
|
+
* Require consistent case for escape sequences (`\xA9` over `\xa9`, `\u00B5`
|
|
98
|
+
* over `\u00b5`).
|
|
99
99
|
*
|
|
100
100
|
* @reference https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/escape-case.md
|
|
101
101
|
*/
|
|
@@ -243,7 +243,7 @@ export interface ITtscLintUnicornRules {
|
|
|
243
243
|
*/
|
|
244
244
|
"unicorn/no-for-loop"?: TtscLintRuleSetting;
|
|
245
245
|
/**
|
|
246
|
-
* Prefer Unicode escape (
|
|
246
|
+
* Prefer Unicode escape (`\u00A9`) over hexadecimal escape (`\xA9`).
|
|
247
247
|
*
|
|
248
248
|
* @reference https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-hex-escape.md
|
|
249
249
|
*/
|
|
@@ -75,6 +75,18 @@ func (a contributorAdapter) NeedsTypeChecker() bool {
|
|
|
75
75
|
return true
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// VisitsDeclarationFiles keeps contributor rules running on declaration
|
|
79
|
+
// files unless the contributor opts out through the public
|
|
80
|
+
// `rule.DeclarationFileRule` marker. Same conservative-default reasoning
|
|
81
|
+
// as NeedsTypeChecker: the host cannot infer a third-party rule's grammar
|
|
82
|
+
// shape, and a wrong skip silently loses findings.
|
|
83
|
+
func (a contributorAdapter) VisitsDeclarationFiles() bool {
|
|
84
|
+
if dr, ok := a.inner.(rule.DeclarationFileRule); ok {
|
|
85
|
+
return dr.VisitsDeclarationFiles()
|
|
86
|
+
}
|
|
87
|
+
return true
|
|
88
|
+
}
|
|
89
|
+
|
|
78
90
|
// formatContributorAdapter is the FormatRule-tagged variant of
|
|
79
91
|
// contributorAdapter. Wrapping the lint-only adapter (rather than
|
|
80
92
|
// duplicating its method set) keeps the marker addition trivial and
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Declaration-file dispatch policy.
|
|
2
|
+
//
|
|
3
|
+
// The engine skips rules on `.d.ts` (and other declaration) sources unless
|
|
4
|
+
// the rule is known to produce legitimate findings there. Declaration files
|
|
5
|
+
// carry no executable code, so most value-level rules can never fire on one;
|
|
6
|
+
// walking them anyway is pure dispatch overhead on declaration-heavy
|
|
7
|
+
// projects (see issue #177).
|
|
8
|
+
//
|
|
9
|
+
// Three ways a rule participates in declaration files:
|
|
10
|
+
//
|
|
11
|
+
// - FormatRule markers: every format rule visits declaration files —
|
|
12
|
+
// `ttsc format` / `ttsc fix` must keep formatting hand-written `.d.ts`
|
|
13
|
+
// on the same boundary as other sources.
|
|
14
|
+
// - declarationFileRule: the optional interface a rule (built-in or
|
|
15
|
+
// contributor adapter) implements to answer for itself.
|
|
16
|
+
// - declarationFileRuleNames: the curated allowlist below for built-in
|
|
17
|
+
// rules, kept in one place so the whole audit stays reviewable.
|
|
18
|
+
//
|
|
19
|
+
// Audit principle for the allowlist: a rule is listed when its grammar
|
|
20
|
+
// shape occurs in declaration files under normal usage — type syntax,
|
|
21
|
+
// signatures, import/export forms, enums and their constant initializers,
|
|
22
|
+
// comments/JSDoc, identifier naming, or per-file metrics. Rules that need
|
|
23
|
+
// executable statements, expressions outside ambient-legal constant
|
|
24
|
+
// expressions, or runtime semantics (promises, DOM, JSX, test frameworks)
|
|
25
|
+
// are deliberately absent. When in doubt the rule is listed: a wrongly
|
|
26
|
+
// listed rule only costs dispatch time, while a wrongly skipped rule
|
|
27
|
+
// silently loses findings.
|
|
28
|
+
package linthost
|
|
29
|
+
|
|
30
|
+
// declarationFileRule marks rules that want to fire on declaration-file
|
|
31
|
+
// (`.d.ts`, `.d.mts`, `.d.cts`) inputs. Rules that neither implement this
|
|
32
|
+
// interface (returning true) nor appear in declarationFileRuleNames nor
|
|
33
|
+
// carry the FormatRule marker are skipped on every
|
|
34
|
+
// `file.IsDeclarationFile == true` source.
|
|
35
|
+
type declarationFileRule interface {
|
|
36
|
+
VisitsDeclarationFiles() bool
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ruleVisitsDeclarationFiles reports whether the engine dispatches `r` on
|
|
40
|
+
// declaration-file sources.
|
|
41
|
+
func ruleVisitsDeclarationFiles(r Rule) bool {
|
|
42
|
+
if isFormatRule(r) {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
if dr, ok := r.(declarationFileRule); ok {
|
|
46
|
+
return dr.VisitsDeclarationFiles()
|
|
47
|
+
}
|
|
48
|
+
return declarationFileRuleNames[r.Name()]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// declarationFileRuleNames is the audited allowlist of built-in rules whose
|
|
52
|
+
// grammar shapes occur in declaration files. Grouped by namespace; each
|
|
53
|
+
// group states the shape that justifies it. A name listed here must exist
|
|
54
|
+
// in the registry — the registry-parity test pins that.
|
|
55
|
+
var declarationFileRuleNames = map[string]bool{
|
|
56
|
+
// JSDoc lives on declarations; declaration files are its prime habitat.
|
|
57
|
+
"jsdoc/check-tag-names": true,
|
|
58
|
+
"jsdoc/check-values": true,
|
|
59
|
+
"jsdoc/empty-tags": true,
|
|
60
|
+
"jsdoc/no-types": true,
|
|
61
|
+
"jsdoc/reject-any-type": true,
|
|
62
|
+
"jsdoc/reject-function-type": true,
|
|
63
|
+
"jsdoc/require-description": true,
|
|
64
|
+
"jsdoc/require-param-description": true,
|
|
65
|
+
"jsdoc/require-param-name": true,
|
|
66
|
+
"jsdoc/require-property-description": true,
|
|
67
|
+
"jsdoc/require-property-name": true,
|
|
68
|
+
"jsdoc/require-returns-description": true,
|
|
69
|
+
"jsdoc/tsdoc-syntax": true,
|
|
70
|
+
|
|
71
|
+
// Import-graph boundaries: declaration files import and re-export, and
|
|
72
|
+
// an architectural boundary holds for hand-written `.d.ts` too.
|
|
73
|
+
"boundaries/dependencies": true,
|
|
74
|
+
"boundaries/element-types": true,
|
|
75
|
+
"boundaries/entry-point": true,
|
|
76
|
+
"boundaries/external": true,
|
|
77
|
+
"boundaries/no-private": true,
|
|
78
|
+
"boundaries/no-unknown": true,
|
|
79
|
+
|
|
80
|
+
// Core rules over shapes a declaration file contains: identifier
|
|
81
|
+
// naming, import/export forms, accessor and class-member signatures,
|
|
82
|
+
// per-file metrics, user-configured selectors, and the constant
|
|
83
|
+
// expressions enum initializers allow (bitwise flags, numeric
|
|
84
|
+
// literals).
|
|
85
|
+
"camelcase": true,
|
|
86
|
+
"grouped-accessor-pairs": true,
|
|
87
|
+
"id-length": true,
|
|
88
|
+
"max-classes-per-file": true,
|
|
89
|
+
"max-lines": true,
|
|
90
|
+
"max-params": true,
|
|
91
|
+
"no-bitwise": true,
|
|
92
|
+
"no-dupe-class-members": true,
|
|
93
|
+
"no-duplicate-imports": true,
|
|
94
|
+
"no-empty-named-blocks": true,
|
|
95
|
+
"no-irregular-whitespace": true,
|
|
96
|
+
"no-loss-of-precision": true,
|
|
97
|
+
"no-magic-numbers": true,
|
|
98
|
+
"no-mixed-operators": true,
|
|
99
|
+
"no-redeclare": true,
|
|
100
|
+
"no-restricted-imports": true,
|
|
101
|
+
"no-restricted-syntax": true,
|
|
102
|
+
"no-shadow": true,
|
|
103
|
+
"no-shadow-restricted-names": true,
|
|
104
|
+
"no-useless-computed-key": true,
|
|
105
|
+
"no-useless-rename": true,
|
|
106
|
+
"sort-imports": true,
|
|
107
|
+
|
|
108
|
+
// typescript/* rules over type syntax, signatures, enums, import/export
|
|
109
|
+
// type forms, comments, and declaration merging.
|
|
110
|
+
"typescript/adjacent-overload-signatures": true,
|
|
111
|
+
"typescript/array-type": true,
|
|
112
|
+
"typescript/ban-ts-comment": true,
|
|
113
|
+
"typescript/ban-tslint-comment": true,
|
|
114
|
+
"typescript/class-literal-property-style": true,
|
|
115
|
+
"typescript/consistent-indexed-object-style": true,
|
|
116
|
+
"typescript/consistent-type-definitions": true,
|
|
117
|
+
"typescript/consistent-type-exports": true,
|
|
118
|
+
"typescript/consistent-type-imports": true,
|
|
119
|
+
"typescript/explicit-function-return-type": true,
|
|
120
|
+
"typescript/explicit-member-accessibility": true,
|
|
121
|
+
"typescript/method-signature-style": true,
|
|
122
|
+
"typescript/no-deprecated": true,
|
|
123
|
+
"typescript/no-duplicate-enum-values": true,
|
|
124
|
+
"typescript/no-empty-interface": true,
|
|
125
|
+
"typescript/no-empty-object-type": true,
|
|
126
|
+
"typescript/no-explicit-any": true,
|
|
127
|
+
"typescript/no-extraneous-class": true,
|
|
128
|
+
"typescript/no-import-type-side-effects": true,
|
|
129
|
+
"typescript/no-invalid-void-type": true,
|
|
130
|
+
"typescript/no-magic-numbers": true,
|
|
131
|
+
"typescript/no-misused-new": true,
|
|
132
|
+
"typescript/no-mixed-enums": true,
|
|
133
|
+
"typescript/no-redundant-type-constituents": true,
|
|
134
|
+
"typescript/no-require-imports": true,
|
|
135
|
+
"typescript/no-restricted-types": true,
|
|
136
|
+
"typescript/no-unnecessary-qualifier": true,
|
|
137
|
+
"typescript/no-unnecessary-template-expression": true,
|
|
138
|
+
"typescript/no-unnecessary-type-arguments": true,
|
|
139
|
+
"typescript/no-unnecessary-type-constraint": true,
|
|
140
|
+
"typescript/no-unsafe-declaration-merging": true,
|
|
141
|
+
"typescript/no-unsafe-function-type": true,
|
|
142
|
+
"typescript/no-wrapper-object-types": true,
|
|
143
|
+
"typescript/prefer-enum-initializers": true,
|
|
144
|
+
"typescript/prefer-function-type": true,
|
|
145
|
+
"typescript/prefer-literal-enum-member": true,
|
|
146
|
+
"typescript/prefer-namespace-keyword": true,
|
|
147
|
+
"typescript/prefer-return-this-type": true,
|
|
148
|
+
"typescript/related-getter-setter-pairs": true,
|
|
149
|
+
"typescript/sort-type-constituents": true,
|
|
150
|
+
"typescript/triple-slash-reference": true,
|
|
151
|
+
|
|
152
|
+
// functional/* rules that police type shapes (not statements).
|
|
153
|
+
"functional/no-mixed-types": true,
|
|
154
|
+
"functional/no-return-void": true,
|
|
155
|
+
"functional/prefer-immutable-types": true,
|
|
156
|
+
"functional/prefer-property-signatures": true,
|
|
157
|
+
"functional/prefer-readonly-type": true,
|
|
158
|
+
"functional/readonly-type": true,
|
|
159
|
+
"functional/type-declaration-immutability": true,
|
|
160
|
+
|
|
161
|
+
// unicorn/* rules over file names, comments, identifier naming, and
|
|
162
|
+
// the numeric literals that appear in enum initializers and literal
|
|
163
|
+
// types.
|
|
164
|
+
"unicorn/empty-brace-spaces": true,
|
|
165
|
+
"unicorn/expiring-todo-comments": true,
|
|
166
|
+
"unicorn/filename-case": true,
|
|
167
|
+
"unicorn/no-abusive-eslint-disable": true,
|
|
168
|
+
"unicorn/no-empty-file": true,
|
|
169
|
+
"unicorn/no-keyword-prefix": true,
|
|
170
|
+
"unicorn/number-literal-case": true,
|
|
171
|
+
"unicorn/numeric-separators-style": true,
|
|
172
|
+
"unicorn/prevent-abbreviations": true,
|
|
173
|
+
}
|
package/linthost/engine.go
CHANGED
|
@@ -534,6 +534,12 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
534
534
|
// Context for every (node, rule) pair, which on a large program meant
|
|
535
535
|
// millions of short-lived heap allocations and the GC pressure they
|
|
536
536
|
// carry. Rules never mutate their Context, so reuse is safe.
|
|
537
|
+
//
|
|
538
|
+
// Declaration files only bind rules that opt into them (see
|
|
539
|
+
// declaration_rules.go): value-level rules can never fire on a `.d.ts`,
|
|
540
|
+
// so dispatching to them is pure overhead on declaration-heavy trees.
|
|
541
|
+
declarationFile := file.IsDeclarationFile
|
|
542
|
+
bound := 0
|
|
537
543
|
byKind := make([][]boundRule, len(e.rules))
|
|
538
544
|
ctxByRule := make(map[string]*Context, len(e.enabled))
|
|
539
545
|
for kind, rules := range e.rules {
|
|
@@ -541,6 +547,9 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
541
547
|
continue
|
|
542
548
|
}
|
|
543
549
|
for _, rule := range rules {
|
|
550
|
+
if declarationFile && !ruleVisitsDeclarationFiles(rule) {
|
|
551
|
+
continue
|
|
552
|
+
}
|
|
544
553
|
name := rule.Name()
|
|
545
554
|
ctx, built := ctxByRule[name]
|
|
546
555
|
if !built {
|
|
@@ -563,6 +572,7 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
563
572
|
continue
|
|
564
573
|
}
|
|
565
574
|
byKind[kind] = append(byKind[kind], boundRule{rule: rule, ctx: ctx})
|
|
575
|
+
bound++
|
|
566
576
|
}
|
|
567
577
|
}
|
|
568
578
|
|
|
@@ -572,23 +582,30 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
572
582
|
// the heap on every invocation; converting to a method value with a
|
|
573
583
|
// cached function field removes that allocation from the hot path —
|
|
574
584
|
// ~38 % of pre-Opt-4 CPU was in the inner ForEachChild closure.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
+
//
|
|
586
|
+
// With no bound rules at all (every active rule was filtered out, e.g.
|
|
587
|
+
// a declaration file where nothing opted in) the walk cannot produce a
|
|
588
|
+
// finding, so it is skipped entirely; inline directives are still
|
|
589
|
+
// parsed below so unknown-directive warnings stay file-complete.
|
|
590
|
+
if bound != 0 {
|
|
591
|
+
w := &lintFileWalker{byKind: byKind, collect: collect}
|
|
592
|
+
w.childCB = w.visitChild
|
|
593
|
+
|
|
594
|
+
// SourceFile dispatches into its statement list directly; we walk
|
|
595
|
+
// statements explicitly so the file node itself can be inspected by
|
|
596
|
+
// rules (e.g., `ban-ts-comment` reads CommentDirectives off the
|
|
597
|
+
// SourceFile).
|
|
598
|
+
if k := int(shimast.KindSourceFile); k >= 0 && k < len(byKind) {
|
|
599
|
+
for _, bound := range byKind[k] {
|
|
600
|
+
runRuleCheck(bound.rule, bound.ctx, file.AsNode(), collect)
|
|
601
|
+
}
|
|
585
602
|
}
|
|
586
|
-
}
|
|
587
603
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
604
|
+
statements := file.Statements
|
|
605
|
+
if statements != nil {
|
|
606
|
+
for _, stmt := range statements.Nodes {
|
|
607
|
+
w.walk(stmt)
|
|
608
|
+
}
|
|
592
609
|
}
|
|
593
610
|
}
|
|
594
611
|
directives := parseLintInlineDirectives(file)
|
package/linthost/lsp.go
CHANGED
|
@@ -502,8 +502,11 @@ func lspFormatBuffer(content string, opts *lspCommandOptions) (*lspWorkspaceEdit
|
|
|
502
502
|
|
|
503
503
|
text := content
|
|
504
504
|
converged := false
|
|
505
|
+
// The tsgo parser asserts on normalized (forward-slash) absolute paths;
|
|
506
|
+
// `target` comes from filepath.Abs and carries backslashes on Windows.
|
|
507
|
+
parseName := filepath.ToSlash(target)
|
|
505
508
|
for pass := 0; pass < maxFormatPasses; pass++ {
|
|
506
|
-
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName:
|
|
509
|
+
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: parseName}, text, scriptKind)
|
|
507
510
|
if file == nil {
|
|
508
511
|
// Match the disk path: a buffer we can't parse is a benign no-op, not a
|
|
509
512
|
// hard error — don't fight the editor's own diagnostics on a dirty buffer.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttsc/lint",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "Reference ttsc plugin: ESLint-style lint rules hosted in the same Program/Checker as the type-check pass.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"src"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
36
|
+
"@typescript/native-preview": "7.0.0-dev.20260612.1",
|
|
37
37
|
"@types/node": "^25.3.0",
|
|
38
38
|
"rimraf": "^6.1.2",
|
|
39
|
-
"ttsc": "0.15.
|
|
39
|
+
"ttsc": "0.15.3"
|
|
40
40
|
},
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
package/rule/rule.go
CHANGED
|
@@ -97,6 +97,21 @@ type FormatRule interface {
|
|
|
97
97
|
IsFormat() bool
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// DeclarationFileRule is an optional marker contributors implement to
|
|
101
|
+
// control whether their rule runs on declaration-file inputs (`.d.ts`,
|
|
102
|
+
// `.d.mts`, `.d.cts`). The engine skips most built-in rules on declaration
|
|
103
|
+
// files because value-level grammar cannot appear there; contributor rules
|
|
104
|
+
// keep the conservative default — they DO run on declaration files — since
|
|
105
|
+
// the host cannot infer a third-party rule's shape (mirror of the implicit
|
|
106
|
+
// checker default). A contributor whose rule inspects executable code only
|
|
107
|
+
// can implement this with `return false` to skip declaration files and
|
|
108
|
+
// save the dispatch on declaration-heavy projects; returning `true` is
|
|
109
|
+
// equivalent to not implementing the interface at all.
|
|
110
|
+
type DeclarationFileRule interface {
|
|
111
|
+
Rule
|
|
112
|
+
VisitsDeclarationFiles() bool
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
// Reporter is the engine-supplied callback that records a finding. The
|
|
101
116
|
// host implements this and passes it to `NewContext` when invoking a
|
|
102
117
|
// contributor rule.
|
|
@@ -257,9 +257,9 @@ export interface ITtscLintTestingLibraryRules {
|
|
|
257
257
|
"testing-library/prefer-presence-queries"?: TtscLintRuleSetting;
|
|
258
258
|
|
|
259
259
|
/**
|
|
260
|
-
* Prefer `queryBy*` inside
|
|
260
|
+
* Prefer `queryBy*` inside disappearance waits.
|
|
261
261
|
*
|
|
262
|
-
*
|
|
262
|
+
* `waitFor` retries the callback against the disappearing element, but
|
|
263
263
|
* `getBy*` throws on the very state being waited for, which produces a noisy
|
|
264
264
|
* error in the trace each retry.
|
|
265
265
|
*
|
|
@@ -107,8 +107,8 @@ export interface ITtscLintUnicornRules {
|
|
|
107
107
|
"unicorn/error-message"?: TtscLintRuleSetting;
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
* Require consistent case for escape sequences (`\xA9` over `\xa9`,
|
|
111
|
-
*
|
|
110
|
+
* Require consistent case for escape sequences (`\xA9` over `\xa9`, `\u00B5`
|
|
111
|
+
* over `\u00b5`).
|
|
112
112
|
*
|
|
113
113
|
* @reference https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/escape-case.md
|
|
114
114
|
*/
|
|
@@ -278,7 +278,7 @@ export interface ITtscLintUnicornRules {
|
|
|
278
278
|
"unicorn/no-for-loop"?: TtscLintRuleSetting;
|
|
279
279
|
|
|
280
280
|
/**
|
|
281
|
-
* Prefer Unicode escape (
|
|
281
|
+
* Prefer Unicode escape (`\u00A9`) over hexadecimal escape (`\xA9`).
|
|
282
282
|
*
|
|
283
283
|
* @reference https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-hex-escape.md
|
|
284
284
|
*/
|