@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
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
// Go port of the core-js-compat 3.49.0 pieces `unicorn/no-unnecessary-
|
|
2
|
+
// polyfills` consumes — the targets parser, the sloppy SemVer comparator, and
|
|
3
|
+
// the unavailable-module computation — plus the upstream rule's polyfill
|
|
4
|
+
// pattern/token tables (built from the same pinned `data.json` key order and
|
|
5
|
+
// a port of change-case 5.4.4 `camelCase`). The generated fixture
|
|
6
|
+
// `test/testdata/polyfills/upstream-patterns.json` pins the table
|
|
7
|
+
// construction byte-for-byte against the real upstream packages, and
|
|
8
|
+
// `corejs-compat-cases.json` pins the compat computation.
|
|
9
|
+
package linthost
|
|
10
|
+
|
|
11
|
+
import (
|
|
12
|
+
"fmt"
|
|
13
|
+
"math"
|
|
14
|
+
"regexp"
|
|
15
|
+
"sort"
|
|
16
|
+
"strconv"
|
|
17
|
+
"strings"
|
|
18
|
+
"sync"
|
|
19
|
+
"time"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// --- change-case camelCase port ----------------------------------------------
|
|
23
|
+
|
|
24
|
+
var (
|
|
25
|
+
polyfillSplitLowerUpperPattern = regexp.MustCompile(`([\p{Ll}\d])(\p{Lu})`)
|
|
26
|
+
polyfillSplitUpperUpperPattern = regexp.MustCompile(`(\p{Lu})(\p{Lu}\p{Ll})`)
|
|
27
|
+
polyfillSplitStripPattern = regexp.MustCompile(`[^\p{L}\d]+`)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// polyfillChangeCaseSplit ports change-case's split(): mark case and
|
|
31
|
+
// separator boundaries with NUL, then split.
|
|
32
|
+
func polyfillChangeCaseSplit(value string) []string {
|
|
33
|
+
result := strings.TrimSpace(value)
|
|
34
|
+
result = polyfillSplitLowerUpperPattern.ReplaceAllString(result, "${1}\x00${2}")
|
|
35
|
+
result = polyfillSplitUpperUpperPattern.ReplaceAllString(result, "${1}\x00${2}")
|
|
36
|
+
result = polyfillSplitStripPattern.ReplaceAllString(result, "\x00")
|
|
37
|
+
result = strings.Trim(result, "\x00")
|
|
38
|
+
if result == "" {
|
|
39
|
+
return nil
|
|
40
|
+
}
|
|
41
|
+
return strings.Split(result, "\x00")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// polyfillCamelCase ports change-case's camelCase(), including the
|
|
45
|
+
// underscore it inserts before digit-initial words.
|
|
46
|
+
func polyfillCamelCase(input string) string {
|
|
47
|
+
words := polyfillChangeCaseSplit(input)
|
|
48
|
+
var builder strings.Builder
|
|
49
|
+
for index, word := range words {
|
|
50
|
+
if index == 0 {
|
|
51
|
+
builder.WriteString(strings.ToLower(word))
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
runes := []rune(word)
|
|
55
|
+
if runes[0] >= '0' && runes[0] <= '9' {
|
|
56
|
+
builder.WriteByte('_')
|
|
57
|
+
builder.WriteRune(runes[0])
|
|
58
|
+
} else {
|
|
59
|
+
builder.WriteString(strings.ToUpper(string(runes[0])))
|
|
60
|
+
}
|
|
61
|
+
builder.WriteString(strings.ToLower(string(runes[1:])))
|
|
62
|
+
}
|
|
63
|
+
return builder.String()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- core-js-compat sloppy SemVer ---------------------------------------------
|
|
67
|
+
|
|
68
|
+
var polyfillSemverPattern = regexp.MustCompile(`(\d+)(?:\.(\d+))?(?:\.(\d+))?`)
|
|
69
|
+
|
|
70
|
+
// polyfillParseSemver mirrors core-js-compat's SemVer: the FIRST digit run
|
|
71
|
+
// anywhere in the string is the major version (">=18" parses as 18.0.0), and
|
|
72
|
+
// inputs without digits are invalid.
|
|
73
|
+
func polyfillParseSemver(input string) ([3]int, error) {
|
|
74
|
+
match := polyfillSemverPattern.FindStringSubmatch(input)
|
|
75
|
+
if match == nil {
|
|
76
|
+
return [3]int{}, fmt.Errorf("invalid version: %s", input)
|
|
77
|
+
}
|
|
78
|
+
var parsed [3]int
|
|
79
|
+
for i := 0; i < 3; i++ {
|
|
80
|
+
if match[i+1] == "" {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
value, err := strconv.Atoi(match[i+1])
|
|
84
|
+
if err != nil {
|
|
85
|
+
return [3]int{}, fmt.Errorf("invalid version: %s", input)
|
|
86
|
+
}
|
|
87
|
+
parsed[i] = value
|
|
88
|
+
}
|
|
89
|
+
return parsed, nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// polyfillCompareSemver mirrors compare($a, operator, $b).
|
|
93
|
+
func polyfillCompareSemver(a, operator, b string) (bool, error) {
|
|
94
|
+
parsedA, err := polyfillParseSemver(a)
|
|
95
|
+
if err != nil {
|
|
96
|
+
return false, err
|
|
97
|
+
}
|
|
98
|
+
parsedB, err := polyfillParseSemver(b)
|
|
99
|
+
if err != nil {
|
|
100
|
+
return false, err
|
|
101
|
+
}
|
|
102
|
+
for i := 0; i < 3; i++ {
|
|
103
|
+
if parsedA[i] < parsedB[i] {
|
|
104
|
+
return operator == "<" || operator == "<=" || operator == "!=", nil
|
|
105
|
+
}
|
|
106
|
+
if parsedA[i] > parsedB[i] {
|
|
107
|
+
return operator == ">" || operator == ">=" || operator == "!=", nil
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return operator == "==" || operator == "<=" || operator == ">=", nil
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// --- targets parsing -----------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
// polyfillTargetEntry is one parsed [engine, version] target.
|
|
116
|
+
type polyfillTargetEntry struct {
|
|
117
|
+
engine string
|
|
118
|
+
version string
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// polyfillJSString mirrors String(value) over decoded ordered-JSON values.
|
|
122
|
+
func polyfillJSString(value interface{}) string {
|
|
123
|
+
switch typed := value.(type) {
|
|
124
|
+
case string:
|
|
125
|
+
return typed
|
|
126
|
+
case float64:
|
|
127
|
+
return polyfillJSNumberString(typed)
|
|
128
|
+
case bool:
|
|
129
|
+
if typed {
|
|
130
|
+
return "true"
|
|
131
|
+
}
|
|
132
|
+
return "false"
|
|
133
|
+
case nil:
|
|
134
|
+
return "null"
|
|
135
|
+
case []interface{}:
|
|
136
|
+
parts := make([]string, len(typed))
|
|
137
|
+
for i, element := range typed {
|
|
138
|
+
if element == nil {
|
|
139
|
+
parts[i] = ""
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
parts[i] = polyfillJSString(element)
|
|
143
|
+
}
|
|
144
|
+
return strings.Join(parts, ",")
|
|
145
|
+
case *browserslistOrderedObject:
|
|
146
|
+
return "[object Object]"
|
|
147
|
+
default:
|
|
148
|
+
return fmt.Sprintf("%v", typed)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
func polyfillJSNumberString(value float64) string {
|
|
153
|
+
if value == math.Trunc(value) && math.Abs(value) < 1e21 {
|
|
154
|
+
return strconv.FormatFloat(value, 'f', -1, 64)
|
|
155
|
+
}
|
|
156
|
+
return strconv.FormatFloat(value, 'g', -1, 64)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// polyfillParseTargets ports core-js-compat's targets-parser: normalize the
|
|
160
|
+
// targets input into deduplicated, alias-resolved, lowest-version [engine,
|
|
161
|
+
// version] pairs. `cwd` stands in for process.cwd() when a `browsers` query
|
|
162
|
+
// needs resolving, `now` feeds time-dependent browserslist queries.
|
|
163
|
+
func polyfillParseTargets(targets interface{}, cwd string, now func() time.Time) ([]polyfillTargetEntry, error) {
|
|
164
|
+
data := loadPolyfillData()
|
|
165
|
+
|
|
166
|
+
var browsers interface{}
|
|
167
|
+
var esmodules interface{}
|
|
168
|
+
var node interface{}
|
|
169
|
+
var rest [][2]interface{}
|
|
170
|
+
|
|
171
|
+
object, isObject := targets.(*browserslistOrderedObject)
|
|
172
|
+
if !isObject {
|
|
173
|
+
if _, isArray := targets.([]interface{}); !isArray {
|
|
174
|
+
if _, isString := targets.(string); !isString {
|
|
175
|
+
if targets == nil {
|
|
176
|
+
return nil, fmt.Errorf("targets must not be null")
|
|
177
|
+
}
|
|
178
|
+
// JS spreads non-object targets into {browsers: targets}; a
|
|
179
|
+
// non-string/array browsers value fails browserslist's query check.
|
|
180
|
+
return nil, fmt.Errorf("browser queries must be an array or string")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
browsers = targets
|
|
184
|
+
} else {
|
|
185
|
+
lowered := &browserslistOrderedObject{values: map[string]interface{}{}}
|
|
186
|
+
for _, key := range object.keys {
|
|
187
|
+
lowerKey := strings.ToLower(key)
|
|
188
|
+
if _, exists := lowered.values[lowerKey]; !exists {
|
|
189
|
+
lowered.keys = append(lowered.keys, lowerKey)
|
|
190
|
+
}
|
|
191
|
+
lowered.values[lowerKey] = object.values[key]
|
|
192
|
+
}
|
|
193
|
+
for _, key := range lowered.keys {
|
|
194
|
+
switch key {
|
|
195
|
+
case "browsers":
|
|
196
|
+
browsers = lowered.values[key]
|
|
197
|
+
case "esmodules":
|
|
198
|
+
esmodules = lowered.values[key]
|
|
199
|
+
case "node":
|
|
200
|
+
node = lowered.values[key]
|
|
201
|
+
default:
|
|
202
|
+
rest = append(rest, [2]interface{}{key, lowered.values[key]})
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var list [][2]interface{}
|
|
208
|
+
list = append(list, rest...)
|
|
209
|
+
|
|
210
|
+
esmodulesIntersect := false
|
|
211
|
+
esmodulesTrue := false
|
|
212
|
+
if esString, isString := esmodules.(string); isString && esString == "intersect" {
|
|
213
|
+
esmodulesIntersect = true
|
|
214
|
+
} else if polyfillJSTruthy(esmodules) {
|
|
215
|
+
esmodulesTrue = true
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if polyfillJSTruthy(browsers) && !esmodulesTrue {
|
|
219
|
+
switch typed := browsers.(type) {
|
|
220
|
+
case string:
|
|
221
|
+
resolved, err := browserslistResolve([]string{typed}, true, browserslistOpts{path: cwd, now: now})
|
|
222
|
+
if err != nil {
|
|
223
|
+
return nil, err
|
|
224
|
+
}
|
|
225
|
+
for _, item := range resolved {
|
|
226
|
+
engine, version, _ := strings.Cut(item, " ")
|
|
227
|
+
list = append(list, [2]interface{}{engine, version})
|
|
228
|
+
}
|
|
229
|
+
case []interface{}:
|
|
230
|
+
queries := make([]string, len(typed))
|
|
231
|
+
for i, element := range typed {
|
|
232
|
+
query, isString := element.(string)
|
|
233
|
+
if !isString {
|
|
234
|
+
return nil, fmt.Errorf("browser queries must be strings")
|
|
235
|
+
}
|
|
236
|
+
queries[i] = query
|
|
237
|
+
}
|
|
238
|
+
resolved, err := browserslistResolve(queries, true, browserslistOpts{path: cwd, now: now})
|
|
239
|
+
if err != nil {
|
|
240
|
+
return nil, err
|
|
241
|
+
}
|
|
242
|
+
for _, item := range resolved {
|
|
243
|
+
engine, version, _ := strings.Cut(item, " ")
|
|
244
|
+
list = append(list, [2]interface{}{engine, version})
|
|
245
|
+
}
|
|
246
|
+
case *browserslistOrderedObject:
|
|
247
|
+
for _, key := range typed.keys {
|
|
248
|
+
list = append(list, [2]interface{}{key, typed.values[key]})
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
return nil, fmt.Errorf("browser queries must be an array or string")
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if esmodulesTrue {
|
|
256
|
+
for _, engine := range polyfillSortedKeys(data.EsModulesTargets) {
|
|
257
|
+
list = append(list, [2]interface{}{engine, data.EsModulesTargets[engine]})
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if polyfillJSTruthy(node) {
|
|
262
|
+
if nodeString, isString := node.(string); isString && nodeString == "current" {
|
|
263
|
+
return nil, fmt.Errorf(
|
|
264
|
+
"`node: \"current\"` is not supported by the ttsc port: the lint host does not run inside Node.js")
|
|
265
|
+
}
|
|
266
|
+
list = append(list, [2]interface{}{"node", node})
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
type namedEntry struct {
|
|
270
|
+
engine string
|
|
271
|
+
version string
|
|
272
|
+
}
|
|
273
|
+
normalized := make([]namedEntry, 0, len(list))
|
|
274
|
+
for _, pair := range list {
|
|
275
|
+
engine := polyfillJSString(pair[0])
|
|
276
|
+
if alias, ok := data.Browserslist.Aliases[engine]; ok {
|
|
277
|
+
engine = alias
|
|
278
|
+
}
|
|
279
|
+
if alias, ok := data.CoreJs.Aliases[engine]; ok {
|
|
280
|
+
engine = alias
|
|
281
|
+
}
|
|
282
|
+
valid := false
|
|
283
|
+
for _, target := range data.CoreJs.ValidTargets {
|
|
284
|
+
if target == engine {
|
|
285
|
+
valid = true
|
|
286
|
+
break
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if !valid {
|
|
290
|
+
continue
|
|
291
|
+
}
|
|
292
|
+
normalized = append(normalized, namedEntry{engine: engine, version: polyfillJSString(pair[1])})
|
|
293
|
+
}
|
|
294
|
+
sort.SliceStable(normalized, func(i, j int) bool {
|
|
295
|
+
return normalized[i].engine < normalized[j].engine
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
var order []string
|
|
299
|
+
reduced := map[string]string{}
|
|
300
|
+
for _, entry := range normalized {
|
|
301
|
+
current, exists := reduced[entry.engine]
|
|
302
|
+
if !exists {
|
|
303
|
+
order = append(order, entry.engine)
|
|
304
|
+
reduced[entry.engine] = entry.version
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
lower, err := polyfillCompareSemver(entry.version, "<=", current)
|
|
308
|
+
if err != nil {
|
|
309
|
+
return nil, err
|
|
310
|
+
}
|
|
311
|
+
if lower {
|
|
312
|
+
reduced[entry.engine] = entry.version
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
result := make([]polyfillTargetEntry, 0, len(order))
|
|
317
|
+
if esmodulesIntersect {
|
|
318
|
+
for _, engine := range order {
|
|
319
|
+
moduleVersion, supported := data.EsModulesTargets[engine]
|
|
320
|
+
if !supported {
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
323
|
+
version := reduced[engine]
|
|
324
|
+
higher, err := polyfillCompareSemver(moduleVersion, ">", version)
|
|
325
|
+
if err != nil {
|
|
326
|
+
return nil, err
|
|
327
|
+
}
|
|
328
|
+
if higher {
|
|
329
|
+
version = moduleVersion
|
|
330
|
+
}
|
|
331
|
+
result = append(result, polyfillTargetEntry{engine: engine, version: version})
|
|
332
|
+
}
|
|
333
|
+
return result, nil
|
|
334
|
+
}
|
|
335
|
+
for _, engine := range order {
|
|
336
|
+
result = append(result, polyfillTargetEntry{engine: engine, version: reduced[engine]})
|
|
337
|
+
}
|
|
338
|
+
return result, nil
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// polyfillJSTruthy mirrors JS truthiness over decoded ordered-JSON values.
|
|
342
|
+
func polyfillJSTruthy(value interface{}) bool {
|
|
343
|
+
switch typed := value.(type) {
|
|
344
|
+
case nil:
|
|
345
|
+
return false
|
|
346
|
+
case bool:
|
|
347
|
+
return typed
|
|
348
|
+
case string:
|
|
349
|
+
return typed != ""
|
|
350
|
+
case float64:
|
|
351
|
+
return typed != 0 && !math.IsNaN(typed)
|
|
352
|
+
default:
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// polyfillSortedKeys returns map keys sorted lexicographically; the
|
|
358
|
+
// esmodules table is iterated through Object.entries whose keys are
|
|
359
|
+
// non-numeric and therefore insertion-ordered, but the emitted artifact is a
|
|
360
|
+
// JSON object, so a canonical order keeps the port deterministic. The pairs
|
|
361
|
+
// feed a full sort in the targets parser, so the choice is unobservable.
|
|
362
|
+
func polyfillSortedKeys(table map[string]string) []string {
|
|
363
|
+
keys := make([]string, 0, len(table))
|
|
364
|
+
for key := range table {
|
|
365
|
+
keys = append(keys, key)
|
|
366
|
+
}
|
|
367
|
+
sort.Strings(keys)
|
|
368
|
+
return keys
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// --- unavailable module computation ---------------------------------------------
|
|
372
|
+
|
|
373
|
+
var (
|
|
374
|
+
polyfillFilteredModulesOnce sync.Once
|
|
375
|
+
polyfillFilteredModulesValue []string
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
// polyfillFilteredModules mirrors filterOutStabilizedProposals over the full
|
|
379
|
+
// module list: esnext.* entries with a stabilized es.* twin are dropped.
|
|
380
|
+
func polyfillFilteredModules() []string {
|
|
381
|
+
polyfillFilteredModulesOnce.Do(func() {
|
|
382
|
+
data := loadPolyfillData()
|
|
383
|
+
modules := make(map[string]struct{}, len(data.Modules))
|
|
384
|
+
for _, module := range data.Modules {
|
|
385
|
+
modules[module] = struct{}{}
|
|
386
|
+
}
|
|
387
|
+
for _, module := range data.Modules {
|
|
388
|
+
if !strings.HasPrefix(module, "esnext.") {
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
391
|
+
if _, stabilized := modules["es."+strings.TrimPrefix(module, "esnext.")]; stabilized {
|
|
392
|
+
delete(modules, module)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
filtered := make([]string, 0, len(modules))
|
|
396
|
+
for _, module := range data.Modules {
|
|
397
|
+
if _, kept := modules[module]; kept {
|
|
398
|
+
filtered = append(filtered, module)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
polyfillFilteredModulesValue = filtered
|
|
402
|
+
})
|
|
403
|
+
return polyfillFilteredModulesValue
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// polyfillCompatList mirrors `coreJsCompat({targets}).list`: the modules some
|
|
407
|
+
// target still requires, in data.json order.
|
|
408
|
+
func polyfillCompatList(targets []polyfillTargetEntry) ([]string, error) {
|
|
409
|
+
data := loadPolyfillData()
|
|
410
|
+
var list []string
|
|
411
|
+
for _, module := range polyfillFilteredModules() {
|
|
412
|
+
requirements := data.Compat[module]
|
|
413
|
+
required := false
|
|
414
|
+
for _, target := range targets {
|
|
415
|
+
requiredVersion, supported := requirements[target.engine]
|
|
416
|
+
if !supported {
|
|
417
|
+
required = true
|
|
418
|
+
continue
|
|
419
|
+
}
|
|
420
|
+
below, err := polyfillCompareSemver(target.version, "<", requiredVersion)
|
|
421
|
+
if err != nil {
|
|
422
|
+
return nil, err
|
|
423
|
+
}
|
|
424
|
+
if below {
|
|
425
|
+
required = true
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if required {
|
|
429
|
+
list = append(list, module)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return list, nil
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// polyfillUnavailableFeatureSet resolves and caches the unavailable-feature
|
|
436
|
+
// set for one targets value, mirroring the upstream rule's module-level
|
|
437
|
+
// `unavailableFeatureSetByTargets` cache (failed resolutions cache too). The
|
|
438
|
+
// key derives from the resolved targets content, so editing a Browserslist
|
|
439
|
+
// config or package.json always produces a fresh entry.
|
|
440
|
+
func polyfillUnavailableFeatureSet(cacheKey string, targets interface{}, cwd string, now func() time.Time) map[string]struct{} {
|
|
441
|
+
polyfillUnavailableCacheMutex.Lock()
|
|
442
|
+
cached, hit := polyfillUnavailableCache[cacheKey]
|
|
443
|
+
polyfillUnavailableCacheMutex.Unlock()
|
|
444
|
+
if hit {
|
|
445
|
+
return cached
|
|
446
|
+
}
|
|
447
|
+
var set map[string]struct{}
|
|
448
|
+
if entries, err := polyfillParseTargets(targets, cwd, now); err == nil {
|
|
449
|
+
if list, listErr := polyfillCompatList(entries); listErr == nil {
|
|
450
|
+
set = make(map[string]struct{}, len(list))
|
|
451
|
+
for _, module := range list {
|
|
452
|
+
set[module] = struct{}{}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
polyfillUnavailableCacheMutex.Lock()
|
|
457
|
+
polyfillUnavailableCache[cacheKey] = set
|
|
458
|
+
polyfillUnavailableCacheMutex.Unlock()
|
|
459
|
+
return set
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
var (
|
|
463
|
+
polyfillUnavailableCacheMutex sync.Mutex
|
|
464
|
+
polyfillUnavailableCache = map[string]map[string]struct{}{}
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
// --- polyfill pattern / token tables ----------------------------------------------
|
|
468
|
+
|
|
469
|
+
// polyfillPattern is one feature's matching data from the upstream rule's
|
|
470
|
+
// module-level `polyfills` table.
|
|
471
|
+
type polyfillPattern struct {
|
|
472
|
+
feature string
|
|
473
|
+
segments int
|
|
474
|
+
patternSource string
|
|
475
|
+
pattern *regexp.Regexp
|
|
476
|
+
tokens []string
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
type polyfillPatternTables struct {
|
|
480
|
+
polyfills []*polyfillPattern
|
|
481
|
+
byToken map[string][]*polyfillPattern
|
|
482
|
+
tokensByFirstChar map[byte][]string
|
|
483
|
+
esConstructorTokens map[string]struct{}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
var (
|
|
487
|
+
polyfillPatternOnce sync.Once
|
|
488
|
+
polyfillPatternTablesValue *polyfillPatternTables
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
// polyfillAdditionalModules mirrors additionalPolyfillModules.
|
|
492
|
+
var polyfillAdditionalModules = map[string][]string{
|
|
493
|
+
"es.promise.finally": {"p-finally"},
|
|
494
|
+
"es.object.set-prototype-of": {"setprototypeof"},
|
|
495
|
+
"es.string.code-point-at": {"code-point-at"},
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// polyfillDirectFeatureCheck mirrors directFeatureCheckPolyfills.
|
|
499
|
+
var polyfillDirectFeatureCheck = map[string]struct{}{
|
|
500
|
+
"es6-symbol": {},
|
|
501
|
+
"promise-polyfill": {},
|
|
502
|
+
"es6-promise": {},
|
|
503
|
+
"weakmap-polyfill": {},
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const (
|
|
507
|
+
polyfillPrefixesPattern = "(mdn-polyfills/|polyfill-)"
|
|
508
|
+
polyfillSuffixesPattern = "(-polyfill)"
|
|
509
|
+
polyfillDelimiterPattern = `(\.|-|\.prototype\.|/)?`
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
// polyfillGetFirstSegment mirrors getFirstSegment: the substring before the
|
|
513
|
+
// first `-`, `.`, or `/`.
|
|
514
|
+
func polyfillGetFirstSegment(value string) string {
|
|
515
|
+
if index := strings.IndexAny(value, "-./"); index >= 0 {
|
|
516
|
+
return value[:index]
|
|
517
|
+
}
|
|
518
|
+
return value
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
func polyfillAddToken(seen map[string]struct{}, tokens *[]string, value string) {
|
|
522
|
+
if value == "" {
|
|
523
|
+
return
|
|
524
|
+
}
|
|
525
|
+
add := func(token string) {
|
|
526
|
+
if _, dup := seen[token]; dup {
|
|
527
|
+
return
|
|
528
|
+
}
|
|
529
|
+
seen[token] = struct{}{}
|
|
530
|
+
*tokens = append(*tokens, token)
|
|
531
|
+
}
|
|
532
|
+
lowercase := strings.ToLower(value)
|
|
533
|
+
add(lowercase)
|
|
534
|
+
add(polyfillGetFirstSegment(lowercase))
|
|
535
|
+
camel := strings.ToLower(polyfillCamelCase(value))
|
|
536
|
+
add(camel)
|
|
537
|
+
add(polyfillGetFirstSegment(camel))
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// polyfillPatterns builds the pattern/token tables from the embedded compat
|
|
541
|
+
// module list, transliterating the upstream rule's module-level setup.
|
|
542
|
+
func polyfillPatterns() *polyfillPatternTables {
|
|
543
|
+
polyfillPatternOnce.Do(func() {
|
|
544
|
+
data := loadPolyfillData()
|
|
545
|
+
tables := &polyfillPatternTables{
|
|
546
|
+
byToken: map[string][]*polyfillPattern{},
|
|
547
|
+
tokensByFirstChar: map[byte][]string{},
|
|
548
|
+
esConstructorTokens: map[string]struct{}{},
|
|
549
|
+
}
|
|
550
|
+
seenByFirstChar := map[byte]map[string]struct{}{}
|
|
551
|
+
for _, feature := range data.Modules {
|
|
552
|
+
parts := strings.Split(feature, ".")
|
|
553
|
+
rawEcmaVersion := parts[0]
|
|
554
|
+
rawConstructorName := ""
|
|
555
|
+
if len(parts) > 1 {
|
|
556
|
+
rawConstructorName = parts[1]
|
|
557
|
+
}
|
|
558
|
+
rawMethodName := ""
|
|
559
|
+
if len(parts) > 2 {
|
|
560
|
+
rawMethodName = parts[2]
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
ecmaVersion := rawEcmaVersion
|
|
564
|
+
if ecmaVersion == "es" {
|
|
565
|
+
ecmaVersion = `(es\d*)`
|
|
566
|
+
}
|
|
567
|
+
constructorName := "(" + rawConstructorName + "|" + polyfillCamelCase(rawConstructorName) + ")"
|
|
568
|
+
methodName := ""
|
|
569
|
+
if rawMethodName != "" {
|
|
570
|
+
methodName = "(" + rawMethodName + "|" + polyfillCamelCase(rawMethodName) + ")"
|
|
571
|
+
}
|
|
572
|
+
methodOrConstructor := methodName
|
|
573
|
+
if methodOrConstructor == "" {
|
|
574
|
+
methodOrConstructor = constructorName
|
|
575
|
+
}
|
|
576
|
+
additionalPattern := ""
|
|
577
|
+
if modules, ok := polyfillAdditionalModules[feature]; ok {
|
|
578
|
+
additionalPattern = "|(" + strings.Join(modules, "|") + ")"
|
|
579
|
+
}
|
|
580
|
+
var patternBuilder strings.Builder
|
|
581
|
+
patternBuilder.WriteString("^((" + polyfillPrefixesPattern + "?(")
|
|
582
|
+
if methodName != "" {
|
|
583
|
+
patternBuilder.WriteString("(" + ecmaVersion + polyfillDelimiterPattern + constructorName + polyfillDelimiterPattern + methodName + ")|")
|
|
584
|
+
patternBuilder.WriteString("(" + constructorName + polyfillDelimiterPattern + methodName + ")|")
|
|
585
|
+
}
|
|
586
|
+
patternBuilder.WriteString("(" + ecmaVersion + polyfillDelimiterPattern + constructorName + "))")
|
|
587
|
+
patternBuilder.WriteString(polyfillSuffixesPattern + "?)|")
|
|
588
|
+
patternBuilder.WriteString("(" + polyfillPrefixesPattern + methodOrConstructor + "|" + methodOrConstructor + polyfillSuffixesPattern + ")")
|
|
589
|
+
patternBuilder.WriteString(additionalPattern + ")$")
|
|
590
|
+
patternSource := patternBuilder.String()
|
|
591
|
+
|
|
592
|
+
seen := map[string]struct{}{}
|
|
593
|
+
var tokens []string
|
|
594
|
+
if rawEcmaVersion == "es" {
|
|
595
|
+
seen["es"] = struct{}{}
|
|
596
|
+
tokens = append(tokens, "es")
|
|
597
|
+
} else {
|
|
598
|
+
polyfillAddToken(seen, &tokens, rawEcmaVersion)
|
|
599
|
+
}
|
|
600
|
+
polyfillAddToken(seen, &tokens, rawConstructorName)
|
|
601
|
+
polyfillAddToken(seen, &tokens, rawMethodName)
|
|
602
|
+
for _, module := range polyfillAdditionalModules[feature] {
|
|
603
|
+
polyfillAddToken(seen, &tokens, module)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
entry := &polyfillPattern{
|
|
607
|
+
feature: feature,
|
|
608
|
+
segments: len(parts),
|
|
609
|
+
patternSource: patternSource,
|
|
610
|
+
pattern: regexp.MustCompile("(?i)" + patternSource),
|
|
611
|
+
tokens: tokens,
|
|
612
|
+
}
|
|
613
|
+
tables.polyfills = append(tables.polyfills, entry)
|
|
614
|
+
|
|
615
|
+
if rawEcmaVersion == "es" {
|
|
616
|
+
tables.esConstructorTokens[strings.ToLower(rawConstructorName)] = struct{}{}
|
|
617
|
+
tables.esConstructorTokens[strings.ToLower(polyfillCamelCase(rawConstructorName))] = struct{}{}
|
|
618
|
+
}
|
|
619
|
+
for _, token := range tokens {
|
|
620
|
+
if token == "" {
|
|
621
|
+
continue
|
|
622
|
+
}
|
|
623
|
+
tables.byToken[token] = append(tables.byToken[token], entry)
|
|
624
|
+
firstChar := token[0]
|
|
625
|
+
if seenByFirstChar[firstChar] == nil {
|
|
626
|
+
seenByFirstChar[firstChar] = map[string]struct{}{}
|
|
627
|
+
}
|
|
628
|
+
if _, dup := seenByFirstChar[firstChar][token]; !dup {
|
|
629
|
+
seenByFirstChar[firstChar][token] = struct{}{}
|
|
630
|
+
tables.tokensByFirstChar[firstChar] = append(tables.tokensByFirstChar[firstChar], token)
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
polyfillPatternTablesValue = tables
|
|
635
|
+
})
|
|
636
|
+
return polyfillPatternTablesValue
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// polyfillStripPrefix mirrors stripPolyfillPrefix.
|
|
640
|
+
func polyfillStripPrefix(value string) string {
|
|
641
|
+
if strings.HasPrefix(value, "polyfill-") {
|
|
642
|
+
return value[len("polyfill-"):]
|
|
643
|
+
}
|
|
644
|
+
if strings.HasPrefix(value, "mdn-polyfills/") {
|
|
645
|
+
return value[len("mdn-polyfills/"):]
|
|
646
|
+
}
|
|
647
|
+
return value
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// polyfillHasEsConstructorPrefix mirrors hasEsConstructorPrefix.
|
|
651
|
+
func polyfillHasEsConstructorPrefix(tables *polyfillPatternTables, value string) bool {
|
|
652
|
+
for token := range tables.esConstructorTokens {
|
|
653
|
+
if strings.HasPrefix(value, token) {
|
|
654
|
+
return true
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return false
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// polyfillIsPotentialEsPrefix mirrors isPotentialEsPrefix.
|
|
661
|
+
func polyfillIsPotentialEsPrefix(tables *polyfillPatternTables, importedModule string) bool {
|
|
662
|
+
if !strings.HasPrefix(importedModule, "es") {
|
|
663
|
+
return false
|
|
664
|
+
}
|
|
665
|
+
index := 2
|
|
666
|
+
for index < len(importedModule) && importedModule[index] >= '0' && importedModule[index] <= '9' {
|
|
667
|
+
index++
|
|
668
|
+
}
|
|
669
|
+
if strings.HasPrefix(importedModule[index:], ".prototype.") {
|
|
670
|
+
index += len(".prototype.")
|
|
671
|
+
} else if index < len(importedModule) &&
|
|
672
|
+
(importedModule[index] == '.' || importedModule[index] == '-' || importedModule[index] == '/') {
|
|
673
|
+
index++
|
|
674
|
+
}
|
|
675
|
+
if index > len(importedModule) {
|
|
676
|
+
return false
|
|
677
|
+
}
|
|
678
|
+
return polyfillHasEsConstructorPrefix(tables, importedModule[index:])
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// polyfillGetCandidates mirrors getPolyfillCandidates: the token prefilter
|
|
682
|
+
// that selects which patterns are worth testing, preserving upstream's
|
|
683
|
+
// candidate iteration order (it breaks best-match ties).
|
|
684
|
+
func polyfillGetCandidates(normalizedImportedModule string) []*polyfillPattern {
|
|
685
|
+
tables := polyfillPatterns()
|
|
686
|
+
stripped := polyfillStripPrefix(normalizedImportedModule)
|
|
687
|
+
if stripped == "" {
|
|
688
|
+
return nil
|
|
689
|
+
}
|
|
690
|
+
tokens, ok := tables.tokensByFirstChar[stripped[0]]
|
|
691
|
+
if !ok {
|
|
692
|
+
return nil
|
|
693
|
+
}
|
|
694
|
+
var candidates []*polyfillPattern
|
|
695
|
+
seen := map[*polyfillPattern]struct{}{}
|
|
696
|
+
add := func(entries []*polyfillPattern) {
|
|
697
|
+
for _, entry := range entries {
|
|
698
|
+
if _, dup := seen[entry]; dup {
|
|
699
|
+
continue
|
|
700
|
+
}
|
|
701
|
+
seen[entry] = struct{}{}
|
|
702
|
+
candidates = append(candidates, entry)
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
firstSegment := polyfillGetFirstSegment(stripped)
|
|
706
|
+
if firstSegment == stripped {
|
|
707
|
+
for _, token := range tokens {
|
|
708
|
+
if token == "es" {
|
|
709
|
+
if !polyfillIsPotentialEsPrefix(tables, stripped) {
|
|
710
|
+
continue
|
|
711
|
+
}
|
|
712
|
+
} else if !strings.HasPrefix(stripped, token) {
|
|
713
|
+
continue
|
|
714
|
+
}
|
|
715
|
+
add(tables.byToken[token])
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
for _, token := range tokens {
|
|
719
|
+
if token == "es" || !strings.HasPrefix(firstSegment, token) {
|
|
720
|
+
continue
|
|
721
|
+
}
|
|
722
|
+
add(tables.byToken[token])
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if polyfillIsPotentialEsPrefix(tables, stripped) {
|
|
726
|
+
add(tables.byToken["es"])
|
|
727
|
+
}
|
|
728
|
+
if len(candidates) == 0 {
|
|
729
|
+
return nil
|
|
730
|
+
}
|
|
731
|
+
return candidates
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// polyfillBestMatch mirrors getBestMatchingPolyfill: the least specific
|
|
735
|
+
// (fewest feature segments) candidate whose pattern matches, first-wins on
|
|
736
|
+
// ties.
|
|
737
|
+
func polyfillBestMatch(candidates []*polyfillPattern, importedModule string) *polyfillPattern {
|
|
738
|
+
var best *polyfillPattern
|
|
739
|
+
bestSegments := int(^uint(0) >> 1)
|
|
740
|
+
for _, candidate := range candidates {
|
|
741
|
+
if !candidate.pattern.MatchString(importedModule) {
|
|
742
|
+
continue
|
|
743
|
+
}
|
|
744
|
+
if candidate.segments < bestSegments {
|
|
745
|
+
best = candidate
|
|
746
|
+
bestSegments = candidate.segments
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return best
|
|
750
|
+
}
|