@ttsc/lint 0.7.2 → 0.8.0-dev.20260505

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 CHANGED
@@ -130,7 +130,7 @@ The `config` field accepts:
130
130
 
131
131
  `@ttsc/lint` must be the first plugin in `compilerOptions.plugins`.
132
132
 
133
- It inspects the source you wrote, so output plugins (`@ttsc/banner`, `@ttsc/paths`, `@ttsc/strip`) have to come after it.
133
+ It inspects the source you wrote, so transform plugins such as `@ttsc/banner`, `@ttsc/paths`, and `@ttsc/strip` have to come after it.
134
134
 
135
135
  ```jsonc
136
136
  {
@@ -139,8 +139,8 @@ It inspects the source you wrote, so output plugins (`@ttsc/banner`, `@ttsc/path
139
139
  // Keep lint first.
140
140
  { "transform": "@ttsc/lint", "config": { "no-var": "error", "prefer-const": "error" } },
141
141
 
142
- // Output plugins run after emit, in order.
143
- { "transform": "@ttsc/banner", "banner": "/*! @license MIT */" },
142
+ // First-party utilities use their documented source/emit hook order.
143
+ { "transform": "@ttsc/banner", "banner": "License MIT" },
144
144
  { "transform": "@ttsc/paths" },
145
145
  { "transform": "@ttsc/strip", "calls": ["console.log"] }
146
146
  ]
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,0DAA6B;AAQ7B,qDAAmC;AAEnC,0BACE,QAA0D;IAE1D,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC;QAC/C,KAAK,EAAE,OAAO;KACf,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,0DAA6B;AAK7B,qDAAmC;AAEnC,0BACE,QAA0D;IAE1D,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC;QAC/C,KAAK,EAAE,OAAO;KACf,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttsc/lint",
3
- "version": "0.7.2",
3
+ "version": "0.8.0-dev.20260505",
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",
@@ -27,10 +27,10 @@
27
27
  "plugin"
28
28
  ],
29
29
  "devDependencies": {
30
- "@typescript/native-preview": "7.0.0-dev.20260430.1",
30
+ "@typescript/native-preview": "7.0.0-dev.20260504.1",
31
31
  "@types/node": "^25.3.0",
32
32
  "rimraf": "^6.1.2",
33
- "ttsc": "0.7.2"
33
+ "ttsc": "0.8.0-dev.20260505"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",
@@ -1,10 +1,10 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "strings"
4
+ "strings"
5
5
 
6
- shimast "github.com/microsoft/typescript-go/shim/ast"
7
- shimscanner "github.com/microsoft/typescript-go/shim/scanner"
6
+ shimast "github.com/microsoft/typescript-go/shim/ast"
7
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
8
8
  )
9
9
 
10
10
  // nodeText returns the source text under a node with all leading
@@ -14,213 +14,213 @@ import (
14
14
  // would include any preceding comment because tsgo's Pos points at
15
15
  // the start of leading trivia, not the actual token.
16
16
  func nodeText(file *shimast.SourceFile, node *shimast.Node) string {
17
- if file == nil || node == nil {
18
- return ""
19
- }
20
- src := file.Text()
21
- end := node.End()
22
- pos := shimscanner.SkipTrivia(src, node.Pos())
23
- if pos < 0 || end > len(src) || pos >= end {
24
- return ""
25
- }
26
- return strings.TrimRight(src[pos:end], " \t\r\n")
17
+ if file == nil || node == nil {
18
+ return ""
19
+ }
20
+ src := file.Text()
21
+ end := node.End()
22
+ pos := shimscanner.SkipTrivia(src, node.Pos())
23
+ if pos < 0 || end > len(src) || pos >= end {
24
+ return ""
25
+ }
26
+ return strings.TrimRight(src[pos:end], " \t\r\n")
27
27
  }
28
28
 
29
29
  // identifierText returns the lexical name of an Identifier node, or "" if
30
30
  // the node isn't an Identifier.
31
31
  func identifierText(node *shimast.Node) string {
32
- if node == nil || node.Kind != shimast.KindIdentifier {
33
- return ""
34
- }
35
- id := node.AsIdentifier()
36
- if id == nil {
37
- return ""
38
- }
39
- return id.Text
32
+ if node == nil || node.Kind != shimast.KindIdentifier {
33
+ return ""
34
+ }
35
+ id := node.AsIdentifier()
36
+ if id == nil {
37
+ return ""
38
+ }
39
+ return id.Text
40
40
  }
41
41
 
42
42
  // stripParens descends through ParenthesizedExpression nodes and returns
43
43
  // the first non-parenthesized child. ESLint rules typically operate on
44
44
  // the canonical form.
45
45
  func stripParens(node *shimast.Node) *shimast.Node {
46
- for node != nil && node.Kind == shimast.KindParenthesizedExpression {
47
- next := node.AsParenthesizedExpression()
48
- if next == nil || next.Expression == nil {
49
- return node
50
- }
51
- node = next.Expression
52
- }
53
- return node
46
+ for node != nil && node.Kind == shimast.KindParenthesizedExpression {
47
+ next := node.AsParenthesizedExpression()
48
+ if next == nil || next.Expression == nil {
49
+ return node
50
+ }
51
+ node = next.Expression
52
+ }
53
+ return node
54
54
  }
55
55
 
56
56
  // isMatchingPropertyAccess reports whether `node` reads the chain
57
57
  // `head.tail[0].tail[1]…`. Useful for detecting `obj.__proto__` or
58
58
  // `console.log` shapes regardless of nesting.
59
59
  func isMatchingPropertyAccess(node *shimast.Node, head string, tail ...string) bool {
60
- if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
61
- return false
62
- }
63
- chain := []*shimast.Node{}
64
- cur := node
65
- for cur != nil && cur.Kind == shimast.KindPropertyAccessExpression {
66
- access := cur.AsPropertyAccessExpression()
67
- if access == nil {
68
- return false
69
- }
70
- chain = append([]*shimast.Node{access.Name()}, chain...)
71
- cur = access.Expression
72
- }
73
- if cur == nil || identifierText(cur) != head {
74
- return false
75
- }
76
- if len(chain) != len(tail) {
77
- return false
78
- }
79
- for i, want := range tail {
80
- if identifierText(chain[i]) != want {
81
- return false
82
- }
83
- }
84
- return true
60
+ if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
61
+ return false
62
+ }
63
+ chain := []*shimast.Node{}
64
+ cur := node
65
+ for cur != nil && cur.Kind == shimast.KindPropertyAccessExpression {
66
+ access := cur.AsPropertyAccessExpression()
67
+ if access == nil {
68
+ return false
69
+ }
70
+ chain = append([]*shimast.Node{access.Name()}, chain...)
71
+ cur = access.Expression
72
+ }
73
+ if cur == nil || identifierText(cur) != head {
74
+ return false
75
+ }
76
+ if len(chain) != len(tail) {
77
+ return false
78
+ }
79
+ for i, want := range tail {
80
+ if identifierText(chain[i]) != want {
81
+ return false
82
+ }
83
+ }
84
+ return true
85
85
  }
86
86
 
87
87
  // isLiteralBoolean returns the boolean value (and ok=true) for a
88
88
  // `KindTrueKeyword` / `KindFalseKeyword` literal. Other nodes return
89
89
  // (false, false).
90
90
  func isLiteralBoolean(node *shimast.Node) (bool, bool) {
91
- if node == nil {
92
- return false, false
93
- }
94
- switch node.Kind {
95
- case shimast.KindTrueKeyword:
96
- return true, true
97
- case shimast.KindFalseKeyword:
98
- return false, true
99
- }
100
- return false, false
91
+ if node == nil {
92
+ return false, false
93
+ }
94
+ switch node.Kind {
95
+ case shimast.KindTrueKeyword:
96
+ return true, true
97
+ case shimast.KindFalseKeyword:
98
+ return false, true
99
+ }
100
+ return false, false
101
101
  }
102
102
 
103
103
  // isLiteralExpression returns true for nodes whose value is intrinsically
104
104
  // truthy / falsy at parse time — these flag `no-constant-condition` etc.
105
105
  func isLiteralExpression(node *shimast.Node) bool {
106
- if node == nil {
107
- return false
108
- }
109
- switch node.Kind {
110
- case
111
- shimast.KindStringLiteral,
112
- shimast.KindNumericLiteral,
113
- shimast.KindBigIntLiteral,
114
- shimast.KindNoSubstitutionTemplateLiteral,
115
- shimast.KindRegularExpressionLiteral,
116
- shimast.KindTrueKeyword,
117
- shimast.KindFalseKeyword,
118
- shimast.KindNullKeyword:
119
- return true
120
- }
121
- return false
106
+ if node == nil {
107
+ return false
108
+ }
109
+ switch node.Kind {
110
+ case
111
+ shimast.KindStringLiteral,
112
+ shimast.KindNumericLiteral,
113
+ shimast.KindBigIntLiteral,
114
+ shimast.KindNoSubstitutionTemplateLiteral,
115
+ shimast.KindRegularExpressionLiteral,
116
+ shimast.KindTrueKeyword,
117
+ shimast.KindFalseKeyword,
118
+ shimast.KindNullKeyword:
119
+ return true
120
+ }
121
+ return false
122
122
  }
123
123
 
124
124
  // callCalleeName returns the simple-identifier callee of a CallExpression
125
125
  // (e.g. `eval` from `eval("...")`). Returns "" when the callee is more
126
126
  // complex than a bare identifier.
127
127
  func callCalleeName(call *shimast.CallExpression) string {
128
- if call == nil || call.Expression == nil {
129
- return ""
130
- }
131
- return identifierText(call.Expression)
128
+ if call == nil || call.Expression == nil {
129
+ return ""
130
+ }
131
+ return identifierText(call.Expression)
132
132
  }
133
133
 
134
134
  // numericLiteralText returns the literal text of a numeric / bigint
135
135
  // literal, normalized for the comparisons rules need (`-0`, `0xFF`).
136
136
  func numericLiteralText(node *shimast.Node) string {
137
- if node == nil {
138
- return ""
139
- }
140
- switch node.Kind {
141
- case shimast.KindNumericLiteral:
142
- if lit := node.AsNumericLiteral(); lit != nil {
143
- return lit.Text
144
- }
145
- case shimast.KindBigIntLiteral:
146
- if lit := node.AsBigIntLiteral(); lit != nil {
147
- return lit.Text
148
- }
149
- }
150
- return ""
137
+ if node == nil {
138
+ return ""
139
+ }
140
+ switch node.Kind {
141
+ case shimast.KindNumericLiteral:
142
+ if lit := node.AsNumericLiteral(); lit != nil {
143
+ return lit.Text
144
+ }
145
+ case shimast.KindBigIntLiteral:
146
+ if lit := node.AsBigIntLiteral(); lit != nil {
147
+ return lit.Text
148
+ }
149
+ }
150
+ return ""
151
151
  }
152
152
 
153
153
  // stringLiteralText returns the value of a string-shaped literal:
154
154
  // StringLiteral or NoSubstitutionTemplateLiteral.
155
155
  func stringLiteralText(node *shimast.Node) string {
156
- if node == nil {
157
- return ""
158
- }
159
- switch node.Kind {
160
- case shimast.KindStringLiteral:
161
- if lit := node.AsStringLiteral(); lit != nil {
162
- return lit.Text
163
- }
164
- case shimast.KindNoSubstitutionTemplateLiteral:
165
- if lit := node.AsNoSubstitutionTemplateLiteral(); lit != nil {
166
- return lit.Text
167
- }
168
- }
169
- return ""
156
+ if node == nil {
157
+ return ""
158
+ }
159
+ switch node.Kind {
160
+ case shimast.KindStringLiteral:
161
+ if lit := node.AsStringLiteral(); lit != nil {
162
+ return lit.Text
163
+ }
164
+ case shimast.KindNoSubstitutionTemplateLiteral:
165
+ if lit := node.AsNoSubstitutionTemplateLiteral(); lit != nil {
166
+ return lit.Text
167
+ }
168
+ }
169
+ return ""
170
170
  }
171
171
 
172
172
  // walkDescendants visits node and every child below it, depth-first.
173
173
  func walkDescendants(node *shimast.Node, visit func(*shimast.Node)) {
174
- if node == nil {
175
- return
176
- }
177
- visit(node)
178
- node.ForEachChild(func(child *shimast.Node) bool {
179
- walkDescendants(child, visit)
180
- return false
181
- })
174
+ if node == nil {
175
+ return
176
+ }
177
+ visit(node)
178
+ node.ForEachChild(func(child *shimast.Node) bool {
179
+ walkDescendants(child, visit)
180
+ return false
181
+ })
182
182
  }
183
183
 
184
184
  func bindingIdentifierNames(node *shimast.Node) []string {
185
- if node == nil {
186
- return nil
187
- }
188
- if name := identifierText(node); name != "" {
189
- return []string{name}
190
- }
191
- if node.Kind != shimast.KindObjectBindingPattern && node.Kind != shimast.KindArrayBindingPattern {
192
- return nil
193
- }
194
- var names []string
195
- walkDescendants(node, func(child *shimast.Node) {
196
- if child == node {
197
- return
198
- }
199
- if name := identifierText(child); name != "" {
200
- names = append(names, name)
201
- }
202
- })
203
- return names
185
+ if node == nil {
186
+ return nil
187
+ }
188
+ if name := identifierText(node); name != "" {
189
+ return []string{name}
190
+ }
191
+ if node.Kind != shimast.KindObjectBindingPattern && node.Kind != shimast.KindArrayBindingPattern {
192
+ return nil
193
+ }
194
+ var names []string
195
+ walkDescendants(node, func(child *shimast.Node) {
196
+ if child == node {
197
+ return
198
+ }
199
+ if name := identifierText(child); name != "" {
200
+ names = append(names, name)
201
+ }
202
+ })
203
+ return names
204
204
  }
205
205
 
206
206
  func isLiteralLike(node *shimast.Node) bool {
207
- node = stripParens(node)
208
- if node == nil {
209
- return false
210
- }
211
- if isLiteralExpression(node) {
212
- return true
213
- }
214
- if node.Kind == shimast.KindPrefixUnaryExpression {
215
- prefix := node.AsPrefixUnaryExpression()
216
- if prefix == nil {
217
- return false
218
- }
219
- switch prefix.Operator {
220
- case shimast.KindPlusToken, shimast.KindMinusToken:
221
- return prefix.Operand != nil &&
222
- (prefix.Operand.Kind == shimast.KindNumericLiteral || prefix.Operand.Kind == shimast.KindBigIntLiteral)
223
- }
224
- }
225
- return false
207
+ node = stripParens(node)
208
+ if node == nil {
209
+ return false
210
+ }
211
+ if isLiteralExpression(node) {
212
+ return true
213
+ }
214
+ if node.Kind == shimast.KindPrefixUnaryExpression {
215
+ prefix := node.AsPrefixUnaryExpression()
216
+ if prefix == nil {
217
+ return false
218
+ }
219
+ switch prefix.Operator {
220
+ case shimast.KindPlusToken, shimast.KindMinusToken:
221
+ return prefix.Operand != nil &&
222
+ (prefix.Operand.Kind == shimast.KindNumericLiteral || prefix.Operand.Kind == shimast.KindBigIntLiteral)
223
+ }
224
+ }
225
+ return false
226
226
  }