@hublo/sentinel 1.2.0-alpha.3 → 1.2.0-alpha.31
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/dist/bin/sentinel.d.ts +1 -0
- package/dist/bin/sentinel.js +28 -2
- package/dist/{chunk-YZ4EKBK7.js → chunk-PLZ45MWU.js} +1545 -772
- package/dist/index.js +1 -1
- package/dist/roles/build/nest/toolchain.d.ts +130 -0
- package/dist/roles/build/nest/toolchain.js +403 -0
- package/dist/roles/test/react/toolchain.d.ts +2 -0
- package/dist/roles/test/react/toolchain.js +9 -0
- package/lint/react-lib.json +3 -1
- package/lint/react.json +4 -1
- package/package.json +26 -5
- package/plugins/hublo.js +68 -0
package/plugins/hublo.js
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
* sentinel exists to remove. The cost is that `@hublo/sentinel` carries a rule about Hublo's
|
|
11
11
|
* colour palette, which is honest for a package named after the company.
|
|
12
12
|
*
|
|
13
|
+
* It now hosts a SECOND, unrelated rule. `no-regex-lookbehind` is not a carried-over selector
|
|
14
|
+
* but a browser-support rule: regex lookbehind is Safari 16.4+, and unlike everything else a
|
|
15
|
+
* preset controls it can be neither transpiled nor polyfilled, because no bundler rewrites the
|
|
16
|
+
* contents of a regex literal. It is here for the same reason as the palette rule — a house
|
|
17
|
+
* rule is code, not configuration — and because lint is the only layer left that can catch it.
|
|
18
|
+
*
|
|
13
19
|
* The original selectors, and where each is handled below:
|
|
14
20
|
* 1-2 MemberExpression[object.property.name='palette'][property.name='common'] theme.palette.common
|
|
15
21
|
* …[computed=true][property.value='common'] theme.palette['common']
|
|
@@ -25,6 +31,15 @@ const MESSAGE =
|
|
|
25
31
|
const COLOUR_PROPS =
|
|
26
32
|
/^(color|bgcolor|background|backgroundColor|borderColor|borderTopColor|borderRightColor|borderBottomColor|borderLeftColor|outlineColor|fill|stroke|textDecorationColor|caretColor|accentColor)$/
|
|
27
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Lookbehind, positive or negative. NOT named groups `(?<name>)`, which Safari has had since
|
|
36
|
+
* 11.3: the class is exactly `(?<` followed by `=` or `!`.
|
|
37
|
+
*/
|
|
38
|
+
const LOOKBEHIND = /\(\?<[=!]/
|
|
39
|
+
|
|
40
|
+
const LOOKBEHIND_MESSAGE =
|
|
41
|
+
'Regex lookbehind ((?<=) / (?<!)) is Safari 16.4+ and can be neither transpiled nor polyfilled: on older iOS the literal is a parse-time SyntaxError that takes the whole chunk with it. Rewrite the pattern. Named groups (?<name>) are fine.'
|
|
42
|
+
|
|
28
43
|
/** `common.something` as a string value, e.g. `{ color: 'common.white' }`. */
|
|
29
44
|
const isCommonPath = (node) =>
|
|
30
45
|
node?.type === 'Literal' && typeof node.value === 'string' && /^common\./.test(node.value)
|
|
@@ -82,6 +97,59 @@ const plugin = {
|
|
|
82
97
|
}
|
|
83
98
|
},
|
|
84
99
|
},
|
|
100
|
+
|
|
101
|
+
'no-regex-lookbehind': {
|
|
102
|
+
meta: {
|
|
103
|
+
docs: {
|
|
104
|
+
description: 'Ban regex lookbehind, unsupported by Safari below 16.4.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
create(context) {
|
|
108
|
+
const report = (node) => context.report({ node, message: LOOKBEHIND_MESSAGE })
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* `new RegExp(...)` and `RegExp(...)`, when the pattern is readable statically: a
|
|
112
|
+
* string, or a template whose fixed parts carry the lookbehind.
|
|
113
|
+
*
|
|
114
|
+
* Each template chunk is tested on its own rather than joined. Joining invents a
|
|
115
|
+
* `(?<=` that the runtime string never has, across an interpolation boundary:
|
|
116
|
+
* `` `(?<${op}=x)` `` would read as one.
|
|
117
|
+
*/
|
|
118
|
+
const isStaticLookbehind = (pattern) => {
|
|
119
|
+
if (pattern?.type === 'Literal') {
|
|
120
|
+
return typeof pattern.value === 'string' && LOOKBEHIND.test(pattern.value)
|
|
121
|
+
}
|
|
122
|
+
if (pattern?.type === 'TemplateLiteral') {
|
|
123
|
+
return (pattern.quasis ?? []).some((quasi) =>
|
|
124
|
+
LOOKBEHIND.test(quasi.value?.cooked ?? quasi.value?.raw ?? ''),
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const checkRegExpCall = (node) => {
|
|
131
|
+
if (node.callee?.name !== 'RegExp') {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
const pattern = node.arguments?.[0]
|
|
135
|
+
if (isStaticLookbehind(pattern)) {
|
|
136
|
+
report(pattern)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
// The case that actually breaks a page: a literal is an EARLY error, so the whole
|
|
142
|
+
// chunk fails to parse rather than the one call site failing.
|
|
143
|
+
Literal(node) {
|
|
144
|
+
if (node.regex && LOOKBEHIND.test(node.regex.pattern)) {
|
|
145
|
+
report(node)
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
NewExpression: checkRegExpCall,
|
|
149
|
+
CallExpression: checkRegExpCall,
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
},
|
|
85
153
|
},
|
|
86
154
|
}
|
|
87
155
|
|