@ttsc/lint 0.18.4 → 0.19.1
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 +125 -43
- package/go.mod +5 -4
- package/internal/rulecode/rulecode.go +88 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +45 -17
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +3 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
- package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
- package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
- package/lib/structures/rules/index.d.ts +3 -0
- package/lib/structures/rules/index.js +3 -0
- package/lib/structures/rules/index.js.map +1 -1
- package/linthost/ast_helpers.go +521 -23
- package/linthost/ast_selector_matcher.go +1628 -0
- package/linthost/ast_selector_parser.go +628 -0
- package/linthost/comment_scan.go +154 -0
- package/linthost/compile.go +129 -79
- package/linthost/config.go +352 -88
- package/linthost/config_format.go +15 -1
- package/linthost/contrib_adapter.go +47 -8
- package/linthost/declaration_rules.go +12 -11
- package/linthost/directives.go +9 -67
- package/linthost/dispatch.go +37 -3
- package/linthost/engine.go +368 -53
- package/linthost/escape_string.go +77 -0
- package/linthost/fix.go +133 -23
- package/linthost/flags_gen.go +18 -17
- package/linthost/format.go +73 -167
- package/linthost/format_editor_settings.go +134 -26
- package/linthost/grapheme_tables_gen.go +1920 -0
- package/linthost/host.go +159 -61
- package/linthost/literal_escape_scan.go +117 -0
- package/linthost/lsp.go +545 -98
- package/linthost/numeric_literal_precision.go +228 -0
- package/linthost/polyfill_browserslist.go +1439 -0
- package/linthost/polyfill_browserslist_config.go +603 -0
- package/linthost/polyfill_corejs.go +750 -0
- package/linthost/polyfill_data.go +113 -0
- package/linthost/polyfill_data_gen.json +39868 -0
- package/linthost/print_nodes_array.go +7 -1
- package/linthost/print_nodes_object.go +7 -1
- package/linthost/project_engine.go +231 -0
- package/linthost/project_rules.go +78 -0
- package/linthost/regex_clean.go +191 -0
- package/linthost/regex_tree.go +1544 -0
- package/linthost/regex_tree_optimizer.go +1685 -0
- package/linthost/regexp_cache.go +44 -0
- package/linthost/rule_codes.go +105 -0
- package/linthost/rule_codes.json +745 -0
- package/linthost/rules_ban_ts_comment.go +312 -0
- package/linthost/rules_boundaries.go +128 -43
- package/linthost/rules_boundaries_dependencies.go +1013 -0
- package/linthost/rules_core_extra.go +62 -92
- package/linthost/rules_cypress.go +1 -1
- package/linthost/rules_default_case.go +87 -11
- package/linthost/rules_dupes.go +38 -13
- package/linthost/rules_empty.go +188 -20
- package/linthost/rules_escape.go +62 -48
- package/linthost/rules_format_arrow_parens.go +19 -5
- package/linthost/rules_format_bracket_spacing.go +1 -1
- package/linthost/rules_format_clause_join.go +1 -1
- package/linthost/rules_format_declaration_header.go +31 -19
- package/linthost/rules_format_indent.go +1 -1
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_orphan_semi.go +1 -1
- package/linthost/rules_format_parameter_properties.go +17 -7
- package/linthost/rules_format_print_width.go +1 -1
- package/linthost/rules_format_quote_props.go +1 -1
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +106 -79
- package/linthost/rules_format_sort_imports.go +79 -45
- package/linthost/rules_format_statement_split.go +1 -1
- package/linthost/rules_format_ternary_nullish_parens.go +1 -1
- package/linthost/rules_format_trailing_comma.go +64 -1
- package/linthost/rules_format_whitespace.go +1 -1
- package/linthost/rules_functional.go +21 -22
- package/linthost/rules_gap.go +80 -3
- package/linthost/rules_grouped_accessor_pairs.go +84 -25
- package/linthost/rules_guard_for_in.go +64 -147
- package/linthost/rules_jsx_a11y.go +232 -22
- package/linthost/rules_logic.go +84 -38
- package/linthost/rules_nextjs.go +105 -16
- package/linthost/rules_no_class_assign.go +70 -0
- package/linthost/rules_no_duplicate_imports.go +266 -0
- package/linthost/rules_no_else_return.go +156 -38
- package/linthost/rules_no_extend_native.go +113 -17
- package/linthost/rules_no_fallthrough.go +1013 -0
- package/linthost/rules_no_func_assign.go +70 -0
- package/linthost/rules_no_import_assign.go +278 -86
- package/linthost/rules_no_inner_declarations.go +186 -0
- package/linthost/rules_no_loop_func.go +438 -53
- package/linthost/rules_no_mixed_operators.go +208 -52
- package/linthost/rules_no_param_reassign.go +376 -61
- package/linthost/rules_no_promise_executor_return.go +156 -0
- package/linthost/rules_no_restricted_imports.go +1013 -57
- package/linthost/rules_no_restricted_syntax.go +190 -26
- package/linthost/rules_no_useless_constructor.go +6 -36
- package/linthost/rules_problems.go +154 -311
- package/linthost/rules_promise.go +2578 -109
- package/linthost/rules_react_extras.go +71 -45
- package/linthost/rules_react_hooks.go +18 -2
- package/linthost/rules_react_perf.go +1 -0
- package/linthost/rules_react_refresh.go +1 -1
- package/linthost/rules_regexp.go +230 -14
- package/linthost/rules_security.go +45 -19
- package/linthost/rules_self.go +21 -1
- package/linthost/rules_solid.go +13 -7
- package/linthost/rules_storybook.go +1 -1
- package/linthost/rules_suggestions.go +490 -84
- package/linthost/rules_testing_library.go +12 -1
- package/linthost/rules_ts.go +227 -54
- package/linthost/rules_ts_async.go +0 -131
- package/linthost/rules_ts_no_base_to_string.go +3 -4
- package/linthost/rules_ts_no_misused_promises.go +817 -0
- package/linthost/rules_ts_no_restricted_types.go +320 -34
- package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
- package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
- package/linthost/rules_ts_require_array_sort_compare.go +5 -5
- package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
- package/linthost/rules_unicorn_better_regex.go +224 -16
- package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
- package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
- package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
- package/linthost/rules_unicorn_escape_case.go +30 -19
- package/linthost/rules_unicorn_filename_case.go +763 -16
- package/linthost/rules_unicorn_import_style.go +783 -17
- package/linthost/rules_unicorn_isolated_functions.go +934 -14
- package/linthost/rules_unicorn_no_hex_escape.go +26 -11
- package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
- package/linthost/rules_unicorn_no_unused_properties.go +756 -19
- package/linthost/rules_unicorn_number_literal_case.go +58 -39
- package/linthost/rules_unicorn_prefer_at.go +7 -3
- package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
- package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
- package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
- package/linthost/rules_unicorn_string_content.go +611 -15
- package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
- package/linthost/rules_unicorn_template_indent.go +923 -18
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
- package/linthost/rules_unicorn_throw_new_error.go +218 -26
- package/linthost/rules_var.go +829 -142
- package/linthost/string_length.go +222 -0
- package/package.json +4 -3
- package/rule/project.go +206 -0
- package/rule/rule.go +58 -1
- package/src/index.ts +58 -17
- package/src/structures/ITtscLintConfig.ts +3 -0
- package/src/structures/TtscLintRuleSetting.ts +3 -2
- package/src/structures/format/ITtscLintFormat.ts +3 -2
- package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
- package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
- package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
- package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
- package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
- package/src/structures/rules/ITtscLintRules.ts +7 -2
- package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
- package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
- package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
- package/src/structures/rules/index.ts +3 -0
|
@@ -53,12 +53,18 @@ func printArrayLiteral(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
|
|
|
53
53
|
// suppresses. Hardcoding `true` would oscillate against Prettier on
|
|
54
54
|
// every `none` project (the trailing-comma rule wouldn't insert one
|
|
55
55
|
// and the printer would put one back).
|
|
56
|
+
//
|
|
57
|
+
// A destructuring assignment target ending in a rest
|
|
58
|
+
// (`[a, ...rest] = arr`) is the one exception: a trailing comma after
|
|
59
|
+
// its `AssignmentRestElement` is a syntax error, so the reflow must not
|
|
60
|
+
// emit one even under "all"/"es5". Matches the same suppression in
|
|
61
|
+
// `format/trailing-comma`; see isRestAssignmentTargetLiteral.
|
|
56
62
|
return printList(ctx, listShape{
|
|
57
63
|
OpenTok: "[",
|
|
58
64
|
CloseTok: "]",
|
|
59
65
|
Items: items,
|
|
60
66
|
Space: false,
|
|
61
|
-
AddComma: ctx.allowsEs5TrailingComma(),
|
|
67
|
+
AddComma: ctx.allowsEs5TrailingComma() && !isRestAssignmentTargetLiteral(node),
|
|
62
68
|
Fill: isConciselyPrintedArray(arr.Elements.Nodes),
|
|
63
69
|
ForceBreak: arrayShouldForceBreak(arr.Elements.Nodes),
|
|
64
70
|
BlankBefore: blankBeforeItems(ctx.Source, arr.Elements.Nodes),
|
|
@@ -66,12 +66,18 @@ func printObjectLiteral(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
|
|
|
66
66
|
// trailing commas in ES5 so both "all" and "es5" keep them; only
|
|
67
67
|
// "none" suppresses. Pairs with the call/array branches so the printer
|
|
68
68
|
// never disagrees with the trailing-comma rule on the same setting.
|
|
69
|
+
//
|
|
70
|
+
// A destructuring assignment target ending in a rest
|
|
71
|
+
// (`({ a, ...rest } = obj)`) is the one exception: a trailing comma
|
|
72
|
+
// after its `AssignmentRestProperty` is a syntax error, so the reflow
|
|
73
|
+
// must not emit one even under "all"/"es5". Matches the same suppression
|
|
74
|
+
// in `format/trailing-comma`; see isRestAssignmentTargetLiteral.
|
|
69
75
|
return printList(ctx, listShape{
|
|
70
76
|
OpenTok: "{",
|
|
71
77
|
CloseTok: "}",
|
|
72
78
|
Items: items,
|
|
73
79
|
Space: true,
|
|
74
|
-
AddComma: ctx.allowsEs5TrailingComma(),
|
|
80
|
+
AddComma: ctx.allowsEs5TrailingComma() && !isRestAssignmentTargetLiteral(node),
|
|
75
81
|
ForceBreak: forceBreak,
|
|
76
82
|
BlankBefore: blankBeforeItems(ctx.Source, obj.Properties.Nodes),
|
|
77
83
|
}), covered
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"sort"
|
|
6
|
+
"sync"
|
|
7
|
+
|
|
8
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
10
|
+
publicrule "github.com/samchon/ttsc/packages/lint/rule"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type projectCycleResults struct {
|
|
14
|
+
byName map[string]projectCycleResult
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type projectCycleResult struct {
|
|
18
|
+
status publicrule.ProjectRuleStatus
|
|
19
|
+
severity Severity
|
|
20
|
+
reporter *projectReporter
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func (r *projectCycleResults) ProjectResult(name string) publicrule.ProjectRuleResult {
|
|
24
|
+
if r == nil {
|
|
25
|
+
return publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleAbsent}
|
|
26
|
+
}
|
|
27
|
+
result, ok := r.byName[name]
|
|
28
|
+
if !ok {
|
|
29
|
+
return publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleAbsent}
|
|
30
|
+
}
|
|
31
|
+
if result.reporter == nil {
|
|
32
|
+
return publicrule.ProjectRuleResult{Status: result.status}
|
|
33
|
+
}
|
|
34
|
+
return result.reporter.snapshot()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type projectCycle struct {
|
|
38
|
+
finalizeOnce sync.Once
|
|
39
|
+
findings []*Finding
|
|
40
|
+
results *projectCycleResults
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type projectReporter struct {
|
|
44
|
+
mu sync.Mutex
|
|
45
|
+
active bool
|
|
46
|
+
failed bool
|
|
47
|
+
messages map[string]struct{}
|
|
48
|
+
state any
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (r *projectReporter) Fail() {
|
|
52
|
+
if r == nil {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
r.mu.Lock()
|
|
56
|
+
defer r.mu.Unlock()
|
|
57
|
+
if r.active {
|
|
58
|
+
r.failed = true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func (r *projectReporter) Report(message string) {
|
|
63
|
+
if r == nil {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
r.mu.Lock()
|
|
67
|
+
defer r.mu.Unlock()
|
|
68
|
+
if !r.active {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
r.failed = true
|
|
72
|
+
if r.messages == nil {
|
|
73
|
+
r.messages = map[string]struct{}{}
|
|
74
|
+
}
|
|
75
|
+
r.messages[message] = struct{}{}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func (r *projectReporter) SetState(state any) {
|
|
79
|
+
if r == nil {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
r.mu.Lock()
|
|
83
|
+
defer r.mu.Unlock()
|
|
84
|
+
if r.active {
|
|
85
|
+
r.state = state
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func (r *projectReporter) snapshot() publicrule.ProjectRuleResult {
|
|
90
|
+
return r.snapshotLocked(false)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func (r *projectReporter) snapshotAndClose() publicrule.ProjectRuleResult {
|
|
94
|
+
return r.snapshotLocked(true)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func (r *projectReporter) snapshotLocked(close bool) publicrule.ProjectRuleResult {
|
|
98
|
+
if r == nil {
|
|
99
|
+
return publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleAbsent}
|
|
100
|
+
}
|
|
101
|
+
r.mu.Lock()
|
|
102
|
+
defer r.mu.Unlock()
|
|
103
|
+
if close {
|
|
104
|
+
r.active = false
|
|
105
|
+
}
|
|
106
|
+
messages := make([]string, 0, len(r.messages))
|
|
107
|
+
for message := range r.messages {
|
|
108
|
+
messages = append(messages, message)
|
|
109
|
+
}
|
|
110
|
+
sort.Strings(messages)
|
|
111
|
+
status := publicrule.ProjectRulePassed
|
|
112
|
+
if r.failed {
|
|
113
|
+
status = publicrule.ProjectRuleFailed
|
|
114
|
+
}
|
|
115
|
+
findings := make([]publicrule.ProjectFinding, 0, len(messages))
|
|
116
|
+
for _, message := range messages {
|
|
117
|
+
findings = append(findings, publicrule.ProjectFinding{Message: message})
|
|
118
|
+
}
|
|
119
|
+
return publicrule.NewProjectRuleResult(status, r.state, findings, r)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func (c *projectCycle) finalize() []*Finding {
|
|
123
|
+
if c == nil || c.results == nil {
|
|
124
|
+
return nil
|
|
125
|
+
}
|
|
126
|
+
c.finalizeOnce.Do(func() {
|
|
127
|
+
names := make([]string, 0, len(c.results.byName))
|
|
128
|
+
for name := range c.results.byName {
|
|
129
|
+
names = append(names, name)
|
|
130
|
+
}
|
|
131
|
+
sort.Strings(names)
|
|
132
|
+
for _, name := range names {
|
|
133
|
+
entry := c.results.byName[name]
|
|
134
|
+
if entry.reporter == nil {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
result := entry.reporter.snapshotAndClose()
|
|
138
|
+
for _, finding := range result.Findings {
|
|
139
|
+
c.findings = append(c.findings, &Finding{
|
|
140
|
+
Rule: name,
|
|
141
|
+
Severity: entry.severity,
|
|
142
|
+
Message: finding.Message,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
return append([]*Finding(nil), c.findings...)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
func (e *Engine) evaluateProject(
|
|
151
|
+
identity publicrule.ProjectIdentity,
|
|
152
|
+
files []*shimast.SourceFile,
|
|
153
|
+
checker *shimchecker.Checker,
|
|
154
|
+
) *projectCycle {
|
|
155
|
+
results := &projectCycleResults{byName: map[string]projectCycleResult{}}
|
|
156
|
+
cycle := &projectCycle{results: results}
|
|
157
|
+
names := allProjectRuleNames()
|
|
158
|
+
if len(names) == 0 {
|
|
159
|
+
return cycle
|
|
160
|
+
}
|
|
161
|
+
var sources []*shimast.SourceFile
|
|
162
|
+
sourcesResolved := false
|
|
163
|
+
for _, name := range names {
|
|
164
|
+
setting := e.projectSettings[name]
|
|
165
|
+
if !setting.Declared {
|
|
166
|
+
results.byName[name] = projectCycleResult{status: publicrule.ProjectRuleNotEvaluated}
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
if setting.Severity == SeverityOff {
|
|
170
|
+
results.byName[name] = projectCycleResult{status: publicrule.ProjectRuleOff}
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
adapter, exists := registeredProjectRules[name]
|
|
174
|
+
if !exists {
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
if !sourcesResolved {
|
|
178
|
+
sources = e.projectSources(files)
|
|
179
|
+
sourcesResolved = true
|
|
180
|
+
}
|
|
181
|
+
reporter := &projectReporter{active: true}
|
|
182
|
+
context := publicrule.NewProjectContext(
|
|
183
|
+
identity,
|
|
184
|
+
sources,
|
|
185
|
+
checker,
|
|
186
|
+
publicrule.Severity(setting.Severity),
|
|
187
|
+
setting.Options,
|
|
188
|
+
reporter,
|
|
189
|
+
)
|
|
190
|
+
runProjectRuleCheck(adapter, context, reporter)
|
|
191
|
+
results.byName[name] = projectCycleResult{
|
|
192
|
+
severity: setting.Severity,
|
|
193
|
+
reporter: reporter,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return cycle
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// projectSources applies only project-wide ignores to the source set exposed
|
|
200
|
+
// to project rules. File-scoped ignores merely refine their containing entry,
|
|
201
|
+
// while ResolveRules marks a source Ignored only for a global ignore entry.
|
|
202
|
+
func (e *Engine) projectSources(files []*shimast.SourceFile) []*shimast.SourceFile {
|
|
203
|
+
sources := make([]*shimast.SourceFile, 0, len(files))
|
|
204
|
+
for _, file := range files {
|
|
205
|
+
if file == nil {
|
|
206
|
+
continue
|
|
207
|
+
}
|
|
208
|
+
if e != nil && e.config != nil && e.config.ResolveRules(file.FileName()).Ignored {
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
sources = append(sources, file)
|
|
212
|
+
}
|
|
213
|
+
return sources
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
func runProjectRuleCheck(
|
|
217
|
+
adapter projectRuleAdapter,
|
|
218
|
+
context *publicrule.ProjectContext,
|
|
219
|
+
reporter *projectReporter,
|
|
220
|
+
) {
|
|
221
|
+
defer func() {
|
|
222
|
+
if recovered := recover(); recovered != nil {
|
|
223
|
+
reporter.Report(fmt.Sprintf(
|
|
224
|
+
"Project rule %q panicked while checking this Program: %v. Report this to the rule's author.",
|
|
225
|
+
adapter.name,
|
|
226
|
+
recovered,
|
|
227
|
+
))
|
|
228
|
+
}
|
|
229
|
+
}()
|
|
230
|
+
adapter.inner.Check(context)
|
|
231
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"sort"
|
|
7
|
+
|
|
8
|
+
publicrule "github.com/samchon/ttsc/packages/lint/rule"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// projectRuleAdapter caches contributor metadata and keeps project checks on a
|
|
12
|
+
// separate lifecycle from node rules.
|
|
13
|
+
type projectRuleAdapter struct {
|
|
14
|
+
inner publicrule.ProjectRule
|
|
15
|
+
name string
|
|
16
|
+
acceptsOptions bool
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var registeredProjectRules = map[string]projectRuleAdapter{}
|
|
20
|
+
|
|
21
|
+
func registerProjectContributors() {
|
|
22
|
+
projects := publicrule.RegisteredProjects()
|
|
23
|
+
adapters := make([]projectRuleAdapter, 0, len(projects))
|
|
24
|
+
for _, project := range projects {
|
|
25
|
+
adapter, err := inspectProjectContributor(project)
|
|
26
|
+
if err != nil {
|
|
27
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint: %v; dropping project contributor entry\n", err)
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
adapters = append(adapters, adapter)
|
|
31
|
+
}
|
|
32
|
+
sort.SliceStable(adapters, func(i, j int) bool { return adapters[i].name < adapters[j].name })
|
|
33
|
+
for _, adapter := range adapters {
|
|
34
|
+
if adapter.name == "" {
|
|
35
|
+
fmt.Fprintln(os.Stderr, "@ttsc/lint: contributor project rule with empty name ignored")
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
if LookupRule(adapter.name) != nil {
|
|
39
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint: contributor project rule %q collides with a file rule; dropping project contributor entry\n", adapter.name)
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
if _, exists := registeredProjectRules[adapter.name]; exists {
|
|
43
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint: contributor project rule %q collides with an existing project rule; dropping contributor entry\n", adapter.name)
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
registeredProjectRules[adapter.name] = adapter
|
|
47
|
+
invalidateRuntimeRuleCodes()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func inspectProjectContributor(project publicrule.ProjectRule) (adapter projectRuleAdapter, err error) {
|
|
52
|
+
defer func() {
|
|
53
|
+
if recovered := recover(); recovered != nil {
|
|
54
|
+
err = fmt.Errorf("contributor project rule %T metadata panicked: %v", project, recovered)
|
|
55
|
+
}
|
|
56
|
+
}()
|
|
57
|
+
if project == nil {
|
|
58
|
+
return projectRuleAdapter{}, fmt.Errorf("nil contributor project rule")
|
|
59
|
+
}
|
|
60
|
+
adapter = projectRuleAdapter{
|
|
61
|
+
inner: project,
|
|
62
|
+
name: project.Name(),
|
|
63
|
+
acceptsOptions: true,
|
|
64
|
+
}
|
|
65
|
+
if optionsRule, ok := project.(publicrule.OptionsRule); ok {
|
|
66
|
+
adapter.acceptsOptions = optionsRule.AcceptsTtscLintOptions()
|
|
67
|
+
}
|
|
68
|
+
return adapter, nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func allProjectRuleNames() []string {
|
|
72
|
+
names := make([]string, 0, len(registeredProjectRules))
|
|
73
|
+
for name := range registeredProjectRules {
|
|
74
|
+
names = append(names, name)
|
|
75
|
+
}
|
|
76
|
+
sort.Strings(names)
|
|
77
|
+
return names
|
|
78
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// regex_clean.go ports the `clean-regexp` npm package (v1.0.0), the exact
|
|
2
|
+
// dependency the upstream unicorn/better-regex rule uses for its
|
|
3
|
+
// `new RegExp("pattern", "flags")` string-constructor branch. Unlike the
|
|
4
|
+
// literal branch (which runs the full regexp-tree optimizer in
|
|
5
|
+
// regex_tree_optimizer.go), upstream's constructor branch applies a fixed,
|
|
6
|
+
// ordered table of character-class shorthands via a global substring
|
|
7
|
+
// replace, gated by whether the regex carries the flags a replacement needs.
|
|
8
|
+
// Reproducing that table verbatim is what keeps the constructor branch's
|
|
9
|
+
// diagnostics and fixes identical to upstream; the full optimizer would
|
|
10
|
+
// rewrite strictly more and diverge.
|
|
11
|
+
//
|
|
12
|
+
// clean-regexp source: https://github.com/sindresorhus/clean-regexp
|
|
13
|
+
package linthost
|
|
14
|
+
|
|
15
|
+
import "strings"
|
|
16
|
+
|
|
17
|
+
type regexCleanMapping struct {
|
|
18
|
+
key string
|
|
19
|
+
value string
|
|
20
|
+
flags string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// regexCleanMappings mirrors clean-regexp's `lib/mappings` Map in declaration
|
|
24
|
+
// order. Entries without flags always apply; entries with `flags: "i"` apply
|
|
25
|
+
// only when the regex is case-insensitive.
|
|
26
|
+
var regexCleanMappings = []regexCleanMapping{
|
|
27
|
+
{key: "[0-9]", value: `\d`},
|
|
28
|
+
{key: "[^0-9]", value: `\D`},
|
|
29
|
+
|
|
30
|
+
// Word
|
|
31
|
+
{key: "[a-zA-Z0-9_]", value: `\w`},
|
|
32
|
+
{key: "[a-zA-Z_0-9]", value: `\w`},
|
|
33
|
+
{key: "[a-z0-9A-Z_]", value: `\w`},
|
|
34
|
+
{key: "[a-z0-9_A-Z]", value: `\w`},
|
|
35
|
+
{key: "[a-z_A-Z0-9]", value: `\w`},
|
|
36
|
+
{key: "[a-z_0-9A-Z]", value: `\w`},
|
|
37
|
+
{key: "[A-Za-z0-9_]", value: `\w`},
|
|
38
|
+
{key: "[A-Za-z_0-9]", value: `\w`},
|
|
39
|
+
{key: "[A-Z0-9a-z_]", value: `\w`},
|
|
40
|
+
{key: "[A-Z0-9_a-z]", value: `\w`},
|
|
41
|
+
{key: "[A-Z_a-z0-9]", value: `\w`},
|
|
42
|
+
{key: "[A-Z_0-9a-z]", value: `\w`},
|
|
43
|
+
{key: "[0-9a-zA-Z_]", value: `\w`},
|
|
44
|
+
{key: "[0-9a-z_A-Z]", value: `\w`},
|
|
45
|
+
{key: "[0-9A-Za-z_]", value: `\w`},
|
|
46
|
+
{key: "[0-9A-Z_a-z]", value: `\w`},
|
|
47
|
+
{key: "[0-9_a-zA-Z]", value: `\w`},
|
|
48
|
+
{key: "[0-9_A-Za-z]", value: `\w`},
|
|
49
|
+
{key: "[_a-zA-Z0-9]", value: `\w`},
|
|
50
|
+
{key: "[_a-z0-9A-Z]", value: `\w`},
|
|
51
|
+
{key: "[_A-Za-z0-9]", value: `\w`},
|
|
52
|
+
{key: "[_A-Z0-9a-z]", value: `\w`},
|
|
53
|
+
{key: "[_0-9a-zA-Z]", value: `\w`},
|
|
54
|
+
{key: "[_0-9A-Za-z]", value: `\w`},
|
|
55
|
+
|
|
56
|
+
// Word with digit
|
|
57
|
+
{key: `[a-zA-Z\d_]`, value: `\w`},
|
|
58
|
+
{key: `[a-zA-Z_\d]`, value: `\w`},
|
|
59
|
+
{key: `[a-z\dA-Z_]`, value: `\w`},
|
|
60
|
+
{key: `[a-z\d_A-Z]`, value: `\w`},
|
|
61
|
+
{key: `[a-z_A-Z\d]`, value: `\w`},
|
|
62
|
+
{key: `[a-z_\dA-Z]`, value: `\w`},
|
|
63
|
+
{key: `[A-Za-z\d_]`, value: `\w`},
|
|
64
|
+
{key: `[A-Za-z_\d]`, value: `\w`},
|
|
65
|
+
{key: `[A-Z\da-z_]`, value: `\w`},
|
|
66
|
+
{key: `[A-Z\d_a-z]`, value: `\w`},
|
|
67
|
+
{key: `[A-Z_a-z\d]`, value: `\w`},
|
|
68
|
+
{key: `[A-Z_\da-z]`, value: `\w`},
|
|
69
|
+
{key: `[\da-zA-Z_]`, value: `\w`},
|
|
70
|
+
{key: `[\da-z_A-Z]`, value: `\w`},
|
|
71
|
+
{key: `[\dA-Za-z_]`, value: `\w`},
|
|
72
|
+
{key: `[\dA-Z_a-z]`, value: `\w`},
|
|
73
|
+
{key: `[\d_a-zA-Z]`, value: `\w`},
|
|
74
|
+
{key: `[\d_A-Za-z]`, value: `\w`},
|
|
75
|
+
{key: `[_a-zA-Z\d]`, value: `\w`},
|
|
76
|
+
{key: `[_a-z\dA-Z]`, value: `\w`},
|
|
77
|
+
{key: `[_A-Za-z\d]`, value: `\w`},
|
|
78
|
+
{key: `[_A-Z\da-z]`, value: `\w`},
|
|
79
|
+
{key: `[_\da-zA-Z]`, value: `\w`},
|
|
80
|
+
{key: `[_\dA-Za-z]`, value: `\w`},
|
|
81
|
+
|
|
82
|
+
// Non-word
|
|
83
|
+
{key: "[^a-zA-Z0-9_]", value: `\W`},
|
|
84
|
+
{key: "[^a-zA-Z_0-9]", value: `\W`},
|
|
85
|
+
{key: "[^a-z0-9A-Z_]", value: `\W`},
|
|
86
|
+
{key: "[^a-z0-9_A-Z]", value: `\W`},
|
|
87
|
+
{key: "[^a-z_A-Z0-9]", value: `\W`},
|
|
88
|
+
{key: "[^a-z_0-9A-Z]", value: `\W`},
|
|
89
|
+
{key: "[^A-Za-z0-9_]", value: `\W`},
|
|
90
|
+
{key: "[^A-Za-z_0-9]", value: `\W`},
|
|
91
|
+
{key: "[^A-Z0-9a-z_]", value: `\W`},
|
|
92
|
+
{key: "[^A-Z0-9_a-z]", value: `\W`},
|
|
93
|
+
{key: "[^A-Z_a-z0-9]", value: `\W`},
|
|
94
|
+
{key: "[^A-Z_0-9a-z]", value: `\W`},
|
|
95
|
+
{key: "[^0-9a-zA-Z_]", value: `\W`},
|
|
96
|
+
{key: "[^0-9a-z_A-Z]", value: `\W`},
|
|
97
|
+
{key: "[^0-9A-Za-z_]", value: `\W`},
|
|
98
|
+
{key: "[^0-9A-Z_a-z]", value: `\W`},
|
|
99
|
+
{key: "[^0-9_a-zA-Z]", value: `\W`},
|
|
100
|
+
{key: "[^0-9_A-Za-z]", value: `\W`},
|
|
101
|
+
{key: "[^_a-zA-Z0-9]", value: `\W`},
|
|
102
|
+
{key: "[^_a-z0-9A-Z]", value: `\W`},
|
|
103
|
+
{key: "[^_A-Za-z0-9]", value: `\W`},
|
|
104
|
+
{key: "[^_A-Z0-9a-z]", value: `\W`},
|
|
105
|
+
{key: "[^_0-9a-zA-Z]", value: `\W`},
|
|
106
|
+
{key: "[^_0-9A-Za-z]", value: `\W`},
|
|
107
|
+
|
|
108
|
+
// Non-word with digit
|
|
109
|
+
{key: `[^a-zA-Z\d_]`, value: `\W`},
|
|
110
|
+
{key: `[^a-zA-Z_\d]`, value: `\W`},
|
|
111
|
+
{key: `[^a-z\dA-Z_]`, value: `\W`},
|
|
112
|
+
{key: `[^a-z\d_A-Z]`, value: `\W`},
|
|
113
|
+
{key: `[^a-z_A-Z\d]`, value: `\W`},
|
|
114
|
+
{key: `[^a-z_\dA-Z]`, value: `\W`},
|
|
115
|
+
{key: `[^A-Za-z\d_]`, value: `\W`},
|
|
116
|
+
{key: `[^A-Za-z_\d]`, value: `\W`},
|
|
117
|
+
{key: `[^A-Z\da-z_]`, value: `\W`},
|
|
118
|
+
{key: `[^A-Z\d_a-z]`, value: `\W`},
|
|
119
|
+
{key: `[^A-Z_a-z\d]`, value: `\W`},
|
|
120
|
+
{key: `[^A-Z_\da-z]`, value: `\W`},
|
|
121
|
+
{key: `[^\da-zA-Z_]`, value: `\W`},
|
|
122
|
+
{key: `[^\da-z_A-Z]`, value: `\W`},
|
|
123
|
+
{key: `[^\dA-Za-z_]`, value: `\W`},
|
|
124
|
+
{key: `[^\dA-Z_a-z]`, value: `\W`},
|
|
125
|
+
{key: `[^\d_a-zA-Z]`, value: `\W`},
|
|
126
|
+
{key: `[^\d_A-Za-z]`, value: `\W`},
|
|
127
|
+
{key: `[^_a-zA-Z\d]`, value: `\W`},
|
|
128
|
+
{key: `[^_a-z\dA-Z]`, value: `\W`},
|
|
129
|
+
{key: `[^_A-Za-z\d]`, value: `\W`},
|
|
130
|
+
{key: `[^_A-Z\da-z]`, value: `\W`},
|
|
131
|
+
{key: `[^_\da-zA-Z]`, value: `\W`},
|
|
132
|
+
{key: `[^_\dA-Za-z]`, value: `\W`},
|
|
133
|
+
|
|
134
|
+
// Word with case insensitivity
|
|
135
|
+
{key: "[a-z0-9_]", value: `\w`, flags: "i"},
|
|
136
|
+
{key: "[a-z_0-9]", value: `\w`, flags: "i"},
|
|
137
|
+
{key: "[0-9a-z_]", value: `\w`, flags: "i"},
|
|
138
|
+
{key: "[0-9_a-z]", value: `\w`, flags: "i"},
|
|
139
|
+
{key: "[_a-z0-9]", value: `\w`, flags: "i"},
|
|
140
|
+
{key: "[_0-9a-z]", value: `\w`, flags: "i"},
|
|
141
|
+
{key: "[^a-z0-9_]", value: `\W`, flags: "i"},
|
|
142
|
+
|
|
143
|
+
// Word with case insensitivity and digit
|
|
144
|
+
{key: `[a-z\d_]`, value: `\w`, flags: "i"},
|
|
145
|
+
{key: `[a-z_\d]`, value: `\w`, flags: "i"},
|
|
146
|
+
{key: `[\da-z_]`, value: `\w`, flags: "i"},
|
|
147
|
+
{key: `[\d_a-z]`, value: `\w`, flags: "i"},
|
|
148
|
+
{key: `[_a-z\d]`, value: `\w`, flags: "i"},
|
|
149
|
+
{key: `[_\da-z]`, value: `\w`, flags: "i"},
|
|
150
|
+
|
|
151
|
+
// Non-word with case insensitivity
|
|
152
|
+
{key: "[^a-z0-9_]", value: `\W`, flags: "i"},
|
|
153
|
+
{key: "[^a-z_0-9]", value: `\W`, flags: "i"},
|
|
154
|
+
{key: "[^0-9a-z_]", value: `\W`, flags: "i"},
|
|
155
|
+
{key: "[^0-9_a-z]", value: `\W`, flags: "i"},
|
|
156
|
+
{key: "[^_a-z0-9]", value: `\W`, flags: "i"},
|
|
157
|
+
{key: "[^_0-9a-z]", value: `\W`, flags: "i"},
|
|
158
|
+
|
|
159
|
+
// Non-word with case insensitivity and digit
|
|
160
|
+
{key: `[^a-z\d_]`, value: `\W`, flags: "i"},
|
|
161
|
+
{key: `[^a-z_\d]`, value: `\W`, flags: "i"},
|
|
162
|
+
{key: `[^\da-z_]`, value: `\W`, flags: "i"},
|
|
163
|
+
{key: `[^\d_a-z]`, value: `\W`, flags: "i"},
|
|
164
|
+
{key: `[^_a-z\d]`, value: `\W`, flags: "i"},
|
|
165
|
+
{key: `[^_\da-z]`, value: `\W`, flags: "i"},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// regexCleanRegexp applies clean-regexp's ordered substring replacements to a
|
|
169
|
+
// `new RegExp` pattern string, gated on `flags`. Mirrors clean-regexp's
|
|
170
|
+
// default export: for each mapping whose required flags are all present in the
|
|
171
|
+
// regex flags, globally replace the key substring with its shorthand.
|
|
172
|
+
func regexCleanRegexp(pattern, flags string) string {
|
|
173
|
+
for _, mapping := range regexCleanMappings {
|
|
174
|
+
if regexCleanHasFlags(flags, mapping.flags) {
|
|
175
|
+
pattern = strings.ReplaceAll(pattern, mapping.key, mapping.value)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return pattern
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// regexCleanHasFlags reports whether every flag a replacement requires is
|
|
182
|
+
// present in the regex's flags (clean-regexp's `hasFlags`). An empty
|
|
183
|
+
// requirement is always satisfied.
|
|
184
|
+
func regexCleanHasFlags(regexFlags, replaceFlags string) bool {
|
|
185
|
+
for _, flag := range replaceFlags {
|
|
186
|
+
if !strings.ContainsRune(regexFlags, flag) {
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return true
|
|
191
|
+
}
|