@ttsc/lint 0.18.2 → 0.18.4
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/linthost/contrib_adapter.go +82 -17
- package/package.json +2 -2
|
@@ -34,12 +34,23 @@ import (
|
|
|
34
34
|
// with a stderr warning. The host prefers a deterministic, debuggable
|
|
35
35
|
// outcome over panicking inside startup.
|
|
36
36
|
func registerContributors() {
|
|
37
|
-
|
|
37
|
+
registered := rule.Registered()
|
|
38
|
+
contributors := make([]contributorMetadata, 0, len(registered))
|
|
39
|
+
for _, contributor := range registered {
|
|
40
|
+
metadata, err := inspectContributor(contributor)
|
|
41
|
+
if err != nil {
|
|
42
|
+
fmt.Fprintf(os.Stderr,
|
|
43
|
+
"@ttsc/lint: %v; dropping contributor entry\n",
|
|
44
|
+
err)
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
contributors = append(contributors, metadata)
|
|
48
|
+
}
|
|
38
49
|
sort.SliceStable(contributors, func(i, j int) bool {
|
|
39
|
-
return contributors[i].
|
|
50
|
+
return contributors[i].name < contributors[j].name
|
|
40
51
|
})
|
|
41
52
|
for _, contributor := range contributors {
|
|
42
|
-
name := contributor.
|
|
53
|
+
name := contributor.name
|
|
43
54
|
if name == "" {
|
|
44
55
|
fmt.Fprintln(os.Stderr, "@ttsc/lint: contributor rule with empty name ignored")
|
|
45
56
|
continue
|
|
@@ -50,22 +61,78 @@ func registerContributors() {
|
|
|
50
61
|
name)
|
|
51
62
|
continue
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
adapter := newContributorAdapter(contributor)
|
|
65
|
+
if contributor.isFormat {
|
|
66
|
+
Register(formatContributorAdapter{adapter})
|
|
55
67
|
continue
|
|
56
68
|
}
|
|
57
|
-
Register(
|
|
69
|
+
Register(adapter)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// contributorMetadata is the immutable host-side view of a public contributor
|
|
74
|
+
// rule. Every contributor-defined metadata method is evaluated exactly once
|
|
75
|
+
// behind inspectContributor's recover barrier so a broken declaration cannot
|
|
76
|
+
// panic later during registry sorting or engine construction.
|
|
77
|
+
type contributorMetadata struct {
|
|
78
|
+
inner rule.Rule
|
|
79
|
+
isFormat bool
|
|
80
|
+
name string
|
|
81
|
+
visits []shimast.Kind
|
|
82
|
+
visitsDeclarationFiles bool
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// inspectContributor evaluates the public rule metadata behind a recover
|
|
86
|
+
// barrier. Check itself is protected separately by runRuleCheck; this function
|
|
87
|
+
// covers the startup methods the engine must call before it can dispatch a
|
|
88
|
+
// node. A panicking contributor is rejected as one entry while the rest of the
|
|
89
|
+
// lint registry remains usable.
|
|
90
|
+
func inspectContributor(contributor rule.Rule) (metadata contributorMetadata, err error) {
|
|
91
|
+
defer func() {
|
|
92
|
+
if recovered := recover(); recovered != nil {
|
|
93
|
+
err = fmt.Errorf("contributor %T metadata panicked: %v", contributor, recovered)
|
|
94
|
+
}
|
|
95
|
+
}()
|
|
96
|
+
metadata = contributorMetadata{
|
|
97
|
+
inner: contributor,
|
|
98
|
+
name: contributor.Name(),
|
|
99
|
+
visits: append([]shimast.Kind(nil), contributor.Visits()...),
|
|
100
|
+
visitsDeclarationFiles: true,
|
|
101
|
+
}
|
|
102
|
+
if formatRule, ok := contributor.(rule.FormatRule); ok {
|
|
103
|
+
metadata.isFormat = formatRule.IsFormat()
|
|
104
|
+
}
|
|
105
|
+
if declarationRule, ok := contributor.(rule.DeclarationFileRule); ok {
|
|
106
|
+
metadata.visitsDeclarationFiles = declarationRule.VisitsDeclarationFiles()
|
|
107
|
+
}
|
|
108
|
+
return metadata, nil
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// newContributorAdapter builds the engine-facing adapter from metadata already
|
|
112
|
+
// evaluated by inspectContributor, so no adapter method re-enters contributor
|
|
113
|
+
// code after startup. This is the only construction path; a zero-value
|
|
114
|
+
// contributorAdapter carries no usable metadata.
|
|
115
|
+
func newContributorAdapter(metadata contributorMetadata) contributorAdapter {
|
|
116
|
+
return contributorAdapter{
|
|
117
|
+
inner: metadata.inner,
|
|
118
|
+
name: metadata.name,
|
|
119
|
+
visits: metadata.visits,
|
|
120
|
+
visitsDeclarationFiles: metadata.visitsDeclarationFiles,
|
|
58
121
|
}
|
|
59
122
|
}
|
|
60
123
|
|
|
61
124
|
// contributorAdapter wraps a public `rule.Rule` so the engine's
|
|
62
|
-
// `Register` accepts it.
|
|
63
|
-
//
|
|
64
|
-
// into the engine's existing
|
|
125
|
+
// `Register` accepts it. Name, Visits, and declaration-file policy are
|
|
126
|
+
// cached by inspectContributor; Check bridges through a `rule.Context`
|
|
127
|
+
// whose `Reporter` calls back into the engine's existing Context.Report /
|
|
128
|
+
// ReportRange. The
|
|
65
129
|
// public `rule.Context` and the engine's internal `Context` share the
|
|
66
130
|
// same shim AST types, so no wrapping / unwrapping of nodes is needed.
|
|
67
131
|
type contributorAdapter struct {
|
|
68
|
-
inner
|
|
132
|
+
inner rule.Rule
|
|
133
|
+
name string
|
|
134
|
+
visits []shimast.Kind
|
|
135
|
+
visitsDeclarationFiles bool
|
|
69
136
|
}
|
|
70
137
|
|
|
71
138
|
// NeedsTypeChecker keeps contributor rules on the historical checker path.
|
|
@@ -79,12 +146,10 @@ func (a contributorAdapter) NeedsTypeChecker() bool {
|
|
|
79
146
|
// files unless the contributor opts out through the public
|
|
80
147
|
// `rule.DeclarationFileRule` marker. Same conservative-default reasoning
|
|
81
148
|
// as NeedsTypeChecker: the host cannot infer a third-party rule's grammar
|
|
82
|
-
// shape, and a wrong skip silently loses findings.
|
|
149
|
+
// shape, and a wrong skip silently loses findings. The default-true /
|
|
150
|
+
// marker-override policy is applied once in inspectContributor.
|
|
83
151
|
func (a contributorAdapter) VisitsDeclarationFiles() bool {
|
|
84
|
-
|
|
85
|
-
return dr.VisitsDeclarationFiles()
|
|
86
|
-
}
|
|
87
|
-
return true
|
|
152
|
+
return a.visitsDeclarationFiles
|
|
88
153
|
}
|
|
89
154
|
|
|
90
155
|
// formatContributorAdapter is the FormatRule-tagged variant of
|
|
@@ -98,8 +163,8 @@ type formatContributorAdapter struct {
|
|
|
98
163
|
|
|
99
164
|
func (formatContributorAdapter) IsFormat() bool { return true }
|
|
100
165
|
|
|
101
|
-
func (a contributorAdapter) Name() string { return a.
|
|
102
|
-
func (a contributorAdapter) Visits() []shimast.Kind { return a.
|
|
166
|
+
func (a contributorAdapter) Name() string { return a.name }
|
|
167
|
+
func (a contributorAdapter) Visits() []shimast.Kind { return a.visits }
|
|
103
168
|
func (a contributorAdapter) Check(ctx *Context, node *shimast.Node) {
|
|
104
169
|
if ctx == nil {
|
|
105
170
|
return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttsc/lint",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
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",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@types/node": "^25.3.0",
|
|
37
37
|
"rimraf": "^6.1.2",
|
|
38
38
|
"typescript": "^7.0.2",
|
|
39
|
-
"ttsc": "0.18.
|
|
39
|
+
"ttsc": "0.18.4"
|
|
40
40
|
},
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|