Skip to content

Commit 0eba571

Browse files
authored
[chore]: store regexp as variable instead of defining it inline (#7716)
* store regexp as variable instead of defining it inline * fix naming of `regex_quoted_value` * some more variables * optimize `.replace() calls * restore formatting changes * optimize `parser.*` calls * small refactor * optimize `.test() calls * rename some variables * fix tests * rename pattern variables * extract common regexes into `patters.ts` * rename variables to use snake_case * fix trim
1 parent 26a4289 commit 0eba571

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+283
-147
lines changed

src/compiler/compile/Component.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ interface ComponentOptions {
4747
preserveWhitespace?: boolean;
4848
}
4949

50+
const regex_leading_directory_separator = /^[/\\]/;
51+
const regex_starts_with_term_export = /^Export/;
52+
const regex_contains_term_function = /Function/;
53+
5054
export default class Component {
5155
stats: Stats;
5256
warnings: Warning[];
@@ -136,7 +140,7 @@ export default class Component {
136140
(typeof process !== 'undefined'
137141
? compile_options.filename
138142
.replace(process.cwd(), '')
139-
.replace(/^[/\\]/, '')
143+
.replace(regex_leading_directory_separator, '')
140144
: compile_options.filename);
141145
this.locate = getLocator(this.source, { offsetLine: 1 });
142146

@@ -638,7 +642,7 @@ export default class Component {
638642
body.splice(i, 1);
639643
}
640644

641-
if (/^Export/.test(node.type)) {
645+
if (regex_starts_with_term_export.test(node.type)) {
642646
const replacement = this.extract_exports(node, true);
643647
if (replacement) {
644648
body[i] = replacement;
@@ -795,7 +799,7 @@ export default class Component {
795799
return this.skip();
796800
}
797801

798-
if (/^Export/.test(node.type)) {
802+
if (regex_starts_with_term_export.test(node.type)) {
799803
const replacement = component.extract_exports(node);
800804
if (replacement) {
801805
this.replace(replacement);
@@ -918,7 +922,7 @@ export default class Component {
918922
}
919923

920924
if (name[1] !== '$' && scope.has(name.slice(1)) && scope.find_owner(name.slice(1)) !== this.instance_scope) {
921-
if (!((/Function/.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) {
925+
if (!((regex_contains_term_function.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) {
922926
return this.error(node as any, compiler_errors.contextual_store);
923927
}
924928
}
@@ -965,7 +969,7 @@ export default class Component {
965969

966970
walk(this.ast.instance.content, {
967971
enter(node: Node) {
968-
if (/Function/.test(node.type)) {
972+
if (regex_contains_term_function.test(node.type)) {
969973
return this.skip();
970974
}
971975

@@ -1089,7 +1093,7 @@ export default class Component {
10891093

10901094
this.replace(b`
10911095
${node.declarations.length ? node : null}
1092-
${ props.length > 0 && b`let { ${ props } } = $$props;`}
1096+
${ props.length > 0 && b`let { ${props} } = $$props;`}
10931097
${inserts}
10941098
` as any);
10951099
return this.skip();
@@ -1460,6 +1464,8 @@ export default class Component {
14601464
}
14611465
}
14621466

1467+
const regex_valid_tag_name = /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/;
1468+
14631469
function process_component_options(component: Component, nodes) {
14641470
const component_options: ComponentOptions = {
14651471
immutable: component.compile_options.immutable || false,
@@ -1473,7 +1479,7 @@ function process_component_options(component: Component, nodes) {
14731479

14741480
const node = nodes.find(node => node.name === 'svelte:options');
14751481

1476-
function get_value(attribute, {code, message}) {
1482+
function get_value(attribute, { code, message }) {
14771483
const { value } = attribute;
14781484
const chunk = value[0];
14791485

@@ -1505,7 +1511,7 @@ function process_component_options(component: Component, nodes) {
15051511
return component.error(attribute, compiler_errors.invalid_tag_attribute);
15061512
}
15071513

1508-
if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
1514+
if (tag && !regex_valid_tag_name.test(tag)) {
15091515
return component.error(attribute, compiler_errors.invalid_tag_property);
15101516
}
15111517

src/compiler/compile/css/Selector.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import EachBlock from '../nodes/EachBlock';
99
import IfBlock from '../nodes/IfBlock';
1010
import AwaitBlock from '../nodes/AwaitBlock';
1111
import compiler_errors from '../compiler_errors';
12+
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns';
1213

1314
enum BlockAppliesToNode {
1415
NotPossible,
@@ -25,6 +26,8 @@ const whitelist_attribute_selector = new Map([
2526
['dialog', new Set(['open'])]
2627
]);
2728

29+
const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/;
30+
2831
export default class Selector {
2932
node: CssNode;
3033
stylesheet: Stylesheet;
@@ -157,7 +160,7 @@ export default class Selector {
157160
for (const block of this.blocks) {
158161
for (const selector of block.selectors) {
159162
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
160-
if (/[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/.test(selector.children[0].value)) {
163+
if (regex_is_single_css_selector.test(selector.children[0].value)) {
161164
component.error(selector, compiler_errors.css_invalid_global_selector);
162165
}
163166
}
@@ -281,12 +284,14 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{
281284
return true;
282285
}
283286

287+
const regex_backslash_and_following_character = /\\(.)/g;
288+
284289
function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToNode {
285290
let i = block.selectors.length;
286291

287292
while (i--) {
288293
const selector = block.selectors[i];
289-
const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1');
294+
const name = typeof selector.name === 'string' && selector.name.replace(regex_backslash_and_following_character, '$1');
290295

291296
if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) {
292297
return BlockAppliesToNode.NotPossible;
@@ -371,7 +376,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
371376
const start_with_space = [];
372377
const remaining = [];
373378
current_possible_values.forEach((current_possible_value: string) => {
374-
if (/^\s/.test(current_possible_value)) {
379+
if (regex_starts_with_whitespace.test(current_possible_value)) {
375380
start_with_space.push(current_possible_value);
376381
} else {
377382
remaining.push(current_possible_value);
@@ -392,7 +397,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
392397
prev_values = combined;
393398

394399
start_with_space.forEach((value: string) => {
395-
if (/\s$/.test(value)) {
400+
if (regex_ends_with_whitespace.test(value)) {
396401
possible_values.add(value);
397402
} else {
398403
prev_values.push(value);
@@ -406,7 +411,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
406411
}
407412

408413
current_possible_values.forEach((current_possible_value: string) => {
409-
if (/\s$/.test(current_possible_value)) {
414+
if (regex_ends_with_whitespace.test(current_possible_value)) {
410415
possible_values.add(current_possible_value);
411416
} else {
412417
prev_values.push(current_possible_value);

src/compiler/compile/css/Stylesheet.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import hash from '../utils/hash';
99
import compiler_warnings from '../compiler_warnings';
1010
import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore';
1111
import { push_array } from '../../utils/push_array';
12+
import { regex_only_whitespaces, regex_whitespace } from '../../utils/patterns';
13+
14+
const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/;
1215

1316
function remove_css_prefix(name: string): string {
14-
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
17+
return name.replace(regex_css_browser_prefix, '');
1518
}
1619

1720
const is_keyframes_node = (node: CssNode) =>
@@ -147,10 +150,10 @@ class Declaration {
147150

148151
// Don't minify whitespace in custom properties, since some browsers (Chromium < 99)
149152
// treat --foo: ; and --foo:; differently
150-
if (first.type === 'Raw' && /^\s+$/.test(first.value)) return;
153+
if (first.type === 'Raw' && regex_only_whitespaces.test(first.value)) return;
151154

152155
let start = first.start;
153-
while (/\s/.test(code.original[start])) start += 1;
156+
while (regex_whitespace.test(code.original[start])) start += 1;
154157

155158
if (start - c > 1) {
156159
code.overwrite(c, start, ':');

src/compiler/compile/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const valid_options = [
3535
'cssHash'
3636
];
3737

38+
const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
39+
const regex_starts_with_lowercase_character = /^[a-z]/;
40+
3841
function validate_options(options: CompileOptions, warnings: Warning[]) {
3942
const { name, filename, loopGuardTimeout, dev, namespace } = options;
4043

@@ -48,11 +51,11 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
4851
}
4952
});
5053

51-
if (name && !/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name)) {
54+
if (name && !regex_valid_identifier.test(name)) {
5255
throw new Error(`options.name must be a valid identifier (got '${name}')`);
5356
}
5457

55-
if (name && /^[a-z]/.test(name)) {
58+
if (name && regex_starts_with_lowercase_character.test(name)) {
5659
const message = 'options.name should be capitalised';
5760
warnings.push({
5861
code: 'options-lowercase-name',

src/compiler/compile/nodes/Binding.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import get_object from '../utils/get_object';
33
import Expression from './shared/Expression';
44
import Component from '../Component';
55
import TemplateScope from './shared/TemplateScope';
6-
import {dimensions} from '../../utils/patterns';
6+
import { regex_dimensions } from '../../utils/patterns';
77
import { Node as ESTreeNode } from 'estree';
88
import { TemplateNode } from '../../interfaces';
99
import Element from './Element';
@@ -88,7 +88,7 @@ export default class Binding extends Node {
8888
const type = parent.get_static_attribute_value('type');
8989

9090
this.is_readonly =
91-
dimensions.test(this.name) ||
91+
regex_dimensions.test(this.name) ||
9292
(isElement(parent) &&
9393
((parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
9494
(parent.name === 'input' && type === 'file')) /* TODO others? */);

src/compiler/compile/nodes/Element.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import StyleDirective from './StyleDirective';
1111
import Text from './Text';
1212
import { namespaces } from '../../utils/namespaces';
1313
import map_children from './shared/map_children';
14-
import { dimensions, start_newline } from '../../utils/patterns';
14+
import { regex_dimensions, regex_starts_with_newline, regex_non_whitespace_character } from '../../utils/patterns';
1515
import fuzzymatch from '../../utils/fuzzymatch';
1616
import list from '../../utils/list';
1717
import Let from './Let';
@@ -203,6 +203,10 @@ function is_valid_aria_attribute_value(schema: ARIAPropertyDefinition, value: st
203203
}
204204
}
205205

206+
const regex_any_repeated_whitespaces = /[\s]+/g;
207+
const regex_heading_tags = /^h[1-6]$/;
208+
const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/;
209+
206210
export default class Element extends Node {
207211
type: 'Element';
208212
name: string;
@@ -253,7 +257,7 @@ export default class Element extends Node {
253257
// places if there's another newline afterwards.
254258
// see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
255259
// see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
256-
first.data = first.data.replace(start_newline, '');
260+
first.data = first.data.replace(regex_starts_with_newline, '');
257261
}
258262
}
259263

@@ -398,7 +402,7 @@ export default class Element extends Node {
398402

399403
// Errors
400404

401-
if (/(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/.test(name)) {
405+
if (regex_illegal_attribute_character.test(name)) {
402406
return component.error(attribute, compiler_errors.illegal_attribute(name));
403407
}
404408

@@ -464,7 +468,7 @@ export default class Element extends Node {
464468
component.warn(attribute, compiler_warnings.a11y_unknown_aria_attribute(type, match));
465469
}
466470

467-
if (name === 'aria-hidden' && /^h[1-6]$/.test(this.name)) {
471+
if (name === 'aria-hidden' && regex_heading_tags.test(this.name)) {
468472
component.warn(attribute, compiler_warnings.a11y_hidden(this.name));
469473
}
470474

@@ -729,7 +733,7 @@ export default class Element extends Node {
729733
if (this.name === 'figure') {
730734
const children = this.children.filter(node => {
731735
if (node.type === 'Comment') return false;
732-
if (node.type === 'Text') return /\S/.test(node.data);
736+
if (node.type === 'Text') return regex_non_whitespace_character.test(node.data);
733737
return true;
734738
});
735739

@@ -861,7 +865,7 @@ export default class Element extends Node {
861865
if (this.name !== 'video') {
862866
return component.error(binding, compiler_errors.invalid_binding_element_with('<video>', name));
863867
}
864-
} else if (dimensions.test(name)) {
868+
} else if (regex_dimensions.test(name)) {
865869
if (this.name === 'svg' && (name === 'offsetWidth' || name === 'offsetHeight')) {
866870
return component.error(binding, compiler_errors.invalid_binding_on(binding.name, `<svg>. Use '${name.replace('offset', 'client')}' instead`));
867871
} else if (is_svg(this.name)) {
@@ -988,7 +992,7 @@ export default class Element extends Node {
988992
if (attribute && !attribute.is_true) {
989993
attribute.chunks.forEach((chunk, index) => {
990994
if (chunk.type === 'Text') {
991-
let data = chunk.data.replace(/[\s\n\t]+/g, ' ');
995+
let data = chunk.data.replace(regex_any_repeated_whitespaces, ' ');
992996
if (index === 0) {
993997
data = data.trimLeft();
994998
} else if (index === attribute.chunks.length - 1) {
@@ -1002,23 +1006,27 @@ export default class Element extends Node {
10021006
}
10031007
}
10041008

1009+
const regex_starts_with_vovel = /^[aeiou]/;
1010+
10051011
function should_have_attribute(
10061012
node,
10071013
attributes: string[],
10081014
name = node.name
10091015
) {
1010-
const article = /^[aeiou]/.test(attributes[0]) ? 'an' : 'a';
1016+
const article = regex_starts_with_vovel.test(attributes[0]) ? 'an' : 'a';
10111017
const sequence = attributes.length > 1 ?
10121018
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
10131019
attributes[0];
10141020

10151021
node.component.warn(node, compiler_warnings.a11y_missing_attribute(name, article, sequence));
10161022
}
10171023

1024+
const regex_minus_sign = /-/;
1025+
10181026
function within_custom_element(parent: INode) {
10191027
while (parent) {
10201028
if (parent.type === 'InlineComponent') return false;
1021-
if (parent.type === 'Element' && /-/.test(parent.name)) return true;
1029+
if (parent.type === 'Element' && regex_minus_sign.test(parent.name)) return true;
10221030
parent = parent.parent;
10231031
}
10241032
return false;

src/compiler/compile/nodes/EventHandler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Identifier } from 'estree';
66
import TemplateScope from './shared/TemplateScope';
77
import { TemplateNode } from '../../interfaces';
88

9+
const regex_contains_term_function_expression = /FunctionExpression/;
10+
911
export default class EventHandler extends Node {
1012
type: 'EventHandler';
1113
name: string;
@@ -25,7 +27,7 @@ export default class EventHandler extends Node {
2527
this.expression = new Expression(component, this, template_scope, info.expression);
2628
this.uses_context = this.expression.uses_context;
2729

28-
if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) {
30+
if (regex_contains_term_function_expression.test(info.expression.type) && info.expression.params.length === 0) {
2931
// TODO make this detection more accurate — if `event.preventDefault` isn't called, and
3032
// `event` is passed to another function, we can make it passive
3133
this.can_make_passive = true;
@@ -55,7 +57,7 @@ export default class EventHandler extends Node {
5557
}
5658
const node = this.expression.node;
5759

58-
if (/FunctionExpression/.test(node.type)) {
60+
if (regex_contains_term_function_expression.test(node.type)) {
5961
return false;
6062
}
6163

src/compiler/compile/nodes/Head.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Component from '../Component';
55
import TemplateScope from './shared/TemplateScope';
66
import { TemplateNode } from '../../interfaces';
77
import compiler_errors from '../compiler_errors';
8+
import { regex_non_whitespace_character } from '../../utils/patterns';
89

910
export default class Head extends Node {
1011
type: 'Head';
@@ -20,7 +21,7 @@ export default class Head extends Node {
2021
}
2122

2223
this.children = map_children(component, parent, scope, info.children.filter(child => {
23-
return (child.type !== 'Text' || /\S/.test(child.data));
24+
return (child.type !== 'Text' || regex_non_whitespace_character.test(child.data));
2425
}));
2526

2627
if (this.children.length > 0) {

0 commit comments

Comments
 (0)