@ttsc/lint 0.18.3 → 0.19.0

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 (188) hide show
  1. package/README.md +95 -43
  2. package/go.mod +5 -4
  3. package/internal/rulecode/rulecode.go +88 -0
  4. package/lib/index.d.ts +10 -1
  5. package/lib/index.js +45 -17
  6. package/lib/index.js.map +1 -1
  7. package/lib/structures/ITtscLintConfig.d.ts +3 -0
  8. package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
  9. package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
  10. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
  11. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
  12. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
  13. package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
  14. package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
  15. package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
  16. package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
  17. package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
  18. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
  19. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
  20. package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
  21. package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
  22. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
  23. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
  24. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
  25. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
  26. package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
  27. package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
  28. package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
  29. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
  30. package/lib/structures/rules/index.d.ts +3 -0
  31. package/lib/structures/rules/index.js +3 -0
  32. package/lib/structures/rules/index.js.map +1 -1
  33. package/linthost/ast_helpers.go +521 -23
  34. package/linthost/ast_selector_matcher.go +1628 -0
  35. package/linthost/ast_selector_parser.go +628 -0
  36. package/linthost/comment_scan.go +154 -0
  37. package/linthost/compile.go +129 -79
  38. package/linthost/config.go +352 -88
  39. package/linthost/config_format.go +15 -1
  40. package/linthost/contrib_adapter.go +129 -25
  41. package/linthost/declaration_rules.go +12 -11
  42. package/linthost/directives.go +9 -67
  43. package/linthost/dispatch.go +37 -3
  44. package/linthost/engine.go +368 -53
  45. package/linthost/escape_string.go +77 -0
  46. package/linthost/fix.go +133 -23
  47. package/linthost/flags_gen.go +18 -17
  48. package/linthost/format.go +73 -167
  49. package/linthost/format_editor_settings.go +134 -26
  50. package/linthost/grapheme_tables_gen.go +1920 -0
  51. package/linthost/host.go +160 -61
  52. package/linthost/literal_escape_scan.go +117 -0
  53. package/linthost/lsp.go +545 -98
  54. package/linthost/numeric_literal_precision.go +228 -0
  55. package/linthost/polyfill_browserslist.go +1439 -0
  56. package/linthost/polyfill_browserslist_config.go +603 -0
  57. package/linthost/polyfill_corejs.go +750 -0
  58. package/linthost/polyfill_data.go +113 -0
  59. package/linthost/polyfill_data_gen.json +39868 -0
  60. package/linthost/print_nodes_array.go +7 -1
  61. package/linthost/print_nodes_object.go +7 -1
  62. package/linthost/project_engine.go +151 -0
  63. package/linthost/project_rules.go +78 -0
  64. package/linthost/regex_clean.go +191 -0
  65. package/linthost/regex_tree.go +1544 -0
  66. package/linthost/regex_tree_optimizer.go +1685 -0
  67. package/linthost/regexp_cache.go +44 -0
  68. package/linthost/rule_codes.go +105 -0
  69. package/linthost/rule_codes.json +745 -0
  70. package/linthost/rules_ban_ts_comment.go +312 -0
  71. package/linthost/rules_boundaries.go +128 -43
  72. package/linthost/rules_boundaries_dependencies.go +1013 -0
  73. package/linthost/rules_core_extra.go +62 -92
  74. package/linthost/rules_cypress.go +1 -1
  75. package/linthost/rules_default_case.go +87 -11
  76. package/linthost/rules_dupes.go +38 -13
  77. package/linthost/rules_empty.go +188 -20
  78. package/linthost/rules_escape.go +62 -48
  79. package/linthost/rules_format_arrow_parens.go +19 -5
  80. package/linthost/rules_format_bracket_spacing.go +1 -1
  81. package/linthost/rules_format_clause_join.go +1 -1
  82. package/linthost/rules_format_declaration_header.go +31 -19
  83. package/linthost/rules_format_indent.go +1 -1
  84. package/linthost/rules_format_jsdoc.go +1 -1
  85. package/linthost/rules_format_orphan_semi.go +1 -1
  86. package/linthost/rules_format_parameter_properties.go +17 -7
  87. package/linthost/rules_format_print_width.go +1 -1
  88. package/linthost/rules_format_quote_props.go +1 -1
  89. package/linthost/rules_format_quotes.go +1 -1
  90. package/linthost/rules_format_semi.go +106 -79
  91. package/linthost/rules_format_sort_imports.go +79 -45
  92. package/linthost/rules_format_statement_split.go +1 -1
  93. package/linthost/rules_format_ternary_nullish_parens.go +1 -1
  94. package/linthost/rules_format_trailing_comma.go +64 -1
  95. package/linthost/rules_format_whitespace.go +1 -1
  96. package/linthost/rules_functional.go +21 -22
  97. package/linthost/rules_gap.go +80 -3
  98. package/linthost/rules_grouped_accessor_pairs.go +84 -25
  99. package/linthost/rules_guard_for_in.go +64 -147
  100. package/linthost/rules_jsx_a11y.go +232 -22
  101. package/linthost/rules_logic.go +84 -38
  102. package/linthost/rules_nextjs.go +105 -16
  103. package/linthost/rules_no_class_assign.go +70 -0
  104. package/linthost/rules_no_duplicate_imports.go +266 -0
  105. package/linthost/rules_no_else_return.go +156 -38
  106. package/linthost/rules_no_extend_native.go +113 -17
  107. package/linthost/rules_no_fallthrough.go +1013 -0
  108. package/linthost/rules_no_func_assign.go +70 -0
  109. package/linthost/rules_no_import_assign.go +278 -86
  110. package/linthost/rules_no_inner_declarations.go +186 -0
  111. package/linthost/rules_no_loop_func.go +438 -53
  112. package/linthost/rules_no_mixed_operators.go +208 -52
  113. package/linthost/rules_no_param_reassign.go +376 -61
  114. package/linthost/rules_no_promise_executor_return.go +156 -0
  115. package/linthost/rules_no_restricted_imports.go +1013 -57
  116. package/linthost/rules_no_restricted_syntax.go +190 -26
  117. package/linthost/rules_no_useless_constructor.go +6 -36
  118. package/linthost/rules_problems.go +154 -311
  119. package/linthost/rules_promise.go +2578 -109
  120. package/linthost/rules_react_extras.go +71 -45
  121. package/linthost/rules_react_hooks.go +18 -2
  122. package/linthost/rules_react_perf.go +1 -0
  123. package/linthost/rules_react_refresh.go +1 -1
  124. package/linthost/rules_regexp.go +230 -14
  125. package/linthost/rules_security.go +45 -19
  126. package/linthost/rules_self.go +21 -1
  127. package/linthost/rules_solid.go +13 -7
  128. package/linthost/rules_storybook.go +1 -1
  129. package/linthost/rules_suggestions.go +490 -84
  130. package/linthost/rules_testing_library.go +12 -1
  131. package/linthost/rules_ts.go +227 -54
  132. package/linthost/rules_ts_async.go +0 -131
  133. package/linthost/rules_ts_no_base_to_string.go +3 -4
  134. package/linthost/rules_ts_no_misused_promises.go +817 -0
  135. package/linthost/rules_ts_no_restricted_types.go +320 -34
  136. package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
  137. package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
  138. package/linthost/rules_ts_require_array_sort_compare.go +5 -5
  139. package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
  140. package/linthost/rules_unicorn_better_regex.go +224 -16
  141. package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
  142. package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
  143. package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
  144. package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
  145. package/linthost/rules_unicorn_escape_case.go +30 -19
  146. package/linthost/rules_unicorn_filename_case.go +763 -16
  147. package/linthost/rules_unicorn_import_style.go +783 -17
  148. package/linthost/rules_unicorn_isolated_functions.go +934 -14
  149. package/linthost/rules_unicorn_no_hex_escape.go +26 -11
  150. package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
  151. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
  152. package/linthost/rules_unicorn_no_unused_properties.go +756 -19
  153. package/linthost/rules_unicorn_number_literal_case.go +58 -39
  154. package/linthost/rules_unicorn_prefer_at.go +7 -3
  155. package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
  156. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
  157. package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
  158. package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
  159. package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
  160. package/linthost/rules_unicorn_string_content.go +611 -15
  161. package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
  162. package/linthost/rules_unicorn_template_indent.go +923 -18
  163. package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
  164. package/linthost/rules_unicorn_throw_new_error.go +218 -26
  165. package/linthost/rules_var.go +829 -142
  166. package/linthost/string_length.go +222 -0
  167. package/package.json +4 -3
  168. package/rule/project.go +145 -0
  169. package/rule/rule.go +58 -1
  170. package/src/index.ts +58 -17
  171. package/src/structures/ITtscLintConfig.ts +3 -0
  172. package/src/structures/TtscLintRuleSetting.ts +3 -2
  173. package/src/structures/format/ITtscLintFormat.ts +3 -2
  174. package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
  175. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
  176. package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
  177. package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
  178. package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
  179. package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
  180. package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
  181. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
  182. package/src/structures/rules/ITtscLintRules.ts +7 -2
  183. package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
  184. package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
  185. package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
  186. package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
  187. package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
  188. 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,151 @@
1
+ package linthost
2
+
3
+ import (
4
+ "fmt"
5
+ "sort"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
9
+ publicrule "github.com/samchon/ttsc/packages/lint/rule"
10
+ )
11
+
12
+ type projectCycleResults struct {
13
+ byName map[string]publicrule.ProjectRuleResult
14
+ }
15
+
16
+ func (r *projectCycleResults) ProjectResult(name string) publicrule.ProjectRuleResult {
17
+ if r == nil {
18
+ return publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleAbsent}
19
+ }
20
+ result, ok := r.byName[name]
21
+ if !ok {
22
+ return publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleAbsent}
23
+ }
24
+ result.Findings = append([]publicrule.ProjectFinding(nil), result.Findings...)
25
+ return result
26
+ }
27
+
28
+ type projectCycle struct {
29
+ findings []*Finding
30
+ results *projectCycleResults
31
+ }
32
+
33
+ type projectReporter struct {
34
+ failed bool
35
+ messages map[string]struct{}
36
+ }
37
+
38
+ func (r *projectReporter) Fail() {
39
+ if r != nil {
40
+ r.failed = true
41
+ }
42
+ }
43
+
44
+ func (r *projectReporter) Report(message string) {
45
+ if r == nil {
46
+ return
47
+ }
48
+ r.failed = true
49
+ if r.messages == nil {
50
+ r.messages = map[string]struct{}{}
51
+ }
52
+ r.messages[message] = struct{}{}
53
+ }
54
+
55
+ func (e *Engine) evaluateProject(
56
+ identity publicrule.ProjectIdentity,
57
+ files []*shimast.SourceFile,
58
+ checker *shimchecker.Checker,
59
+ ) projectCycle {
60
+ results := &projectCycleResults{byName: map[string]publicrule.ProjectRuleResult{}}
61
+ cycle := projectCycle{results: results}
62
+ names := allProjectRuleNames()
63
+ if len(names) == 0 {
64
+ return cycle
65
+ }
66
+ var sources []*shimast.SourceFile
67
+ sourcesResolved := false
68
+ for _, name := range names {
69
+ setting := e.projectSettings[name]
70
+ if !setting.Declared {
71
+ results.byName[name] = publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleNotEvaluated}
72
+ continue
73
+ }
74
+ if setting.Severity == SeverityOff {
75
+ results.byName[name] = publicrule.ProjectRuleResult{Status: publicrule.ProjectRuleOff}
76
+ continue
77
+ }
78
+ adapter, exists := registeredProjectRules[name]
79
+ if !exists {
80
+ continue
81
+ }
82
+ if !sourcesResolved {
83
+ sources = e.projectSources(files)
84
+ sourcesResolved = true
85
+ }
86
+ reporter := &projectReporter{}
87
+ context := publicrule.NewProjectContext(
88
+ identity,
89
+ sources,
90
+ checker,
91
+ publicrule.Severity(setting.Severity),
92
+ setting.Options,
93
+ reporter,
94
+ )
95
+ runProjectRuleCheck(adapter, context, reporter)
96
+ messages := make([]string, 0, len(reporter.messages))
97
+ for message := range reporter.messages {
98
+ messages = append(messages, message)
99
+ }
100
+ sort.Strings(messages)
101
+ status := publicrule.ProjectRulePassed
102
+ if reporter.failed {
103
+ status = publicrule.ProjectRuleFailed
104
+ }
105
+ result := publicrule.ProjectRuleResult{Status: status}
106
+ for _, message := range messages {
107
+ result.Findings = append(result.Findings, publicrule.ProjectFinding{Message: message})
108
+ cycle.findings = append(cycle.findings, &Finding{
109
+ Rule: name,
110
+ Severity: setting.Severity,
111
+ Message: message,
112
+ })
113
+ }
114
+ results.byName[name] = result
115
+ }
116
+ return cycle
117
+ }
118
+
119
+ // projectSources applies only project-wide ignores to the source set exposed
120
+ // to project rules. File-scoped ignores merely refine their containing entry,
121
+ // while ResolveRules marks a source Ignored only for a global ignore entry.
122
+ func (e *Engine) projectSources(files []*shimast.SourceFile) []*shimast.SourceFile {
123
+ sources := make([]*shimast.SourceFile, 0, len(files))
124
+ for _, file := range files {
125
+ if file == nil {
126
+ continue
127
+ }
128
+ if e != nil && e.config != nil && e.config.ResolveRules(file.FileName()).Ignored {
129
+ continue
130
+ }
131
+ sources = append(sources, file)
132
+ }
133
+ return sources
134
+ }
135
+
136
+ func runProjectRuleCheck(
137
+ adapter projectRuleAdapter,
138
+ context *publicrule.ProjectContext,
139
+ reporter *projectReporter,
140
+ ) {
141
+ defer func() {
142
+ if recovered := recover(); recovered != nil {
143
+ reporter.Report(fmt.Sprintf(
144
+ "Project rule %q panicked while checking this Program: %v. Report this to the rule's author.",
145
+ adapter.name,
146
+ recovered,
147
+ ))
148
+ }
149
+ }()
150
+ adapter.inner.Check(context)
151
+ }
@@ -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
+ }