optimize `.replace() calls

pull/7716/head
Ivan Hofer 4 years ago committed by tanhauhau
parent f2b557f8af
commit edd74df0a6

@ -23,6 +23,8 @@ export interface BlockOptions {
dependencies?: Set<string>; dependencies?: Set<string>;
} }
const regex_double_quotes = /"/g;
export default class Block { export default class Block {
parent?: Block; parent?: Block;
renderer: Renderer; renderer: Renderer;
@ -415,7 +417,7 @@ export default class Block {
block: ${block}, block: ${block},
id: ${this.name || 'create_fragment'}.name, id: ${this.name || 'create_fragment'}.name,
type: "${this.type}", type: "${this.type}",
source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", source: "${this.comment ? this.comment.replace(regex_double_quotes, '\\"') : ''}",
ctx: #ctx ctx: #ctx
}); });
return ${block};` return ${block};`

@ -13,6 +13,8 @@ import { flatten } from '../../utils/flatten';
import check_enable_sourcemap from '../utils/check_enable_sourcemap'; import check_enable_sourcemap from '../utils/check_enable_sourcemap';
import { push_array } from '../../utils/push_array'; import { push_array } from '../../utils/push_array';
const regex_backslash = /\\/g;
export default function dom( export default function dom(
component: Component, component: Component,
options: CompileOptions options: CompileOptions
@ -530,7 +532,7 @@ export default function dom(
constructor(options) { constructor(options) {
super(); super();
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${css_sourcemap_enabled && options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`} ${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(regex_backslash, '\\\\')}${css_sourcemap_enabled && options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, null, ${dirty}); @init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, null, ${dirty});

@ -46,6 +46,8 @@ export class BaseAttributeWrapper {
} }
const regex_minus_sign = /-/; const regex_minus_sign = /-/;
const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g; // is 0-9 missing?
const regex_double_quotes = /"/g;
export default class AttributeWrapper extends BaseAttributeWrapper { export default class AttributeWrapper extends BaseAttributeWrapper {
node: Attribute; node: Attribute;
@ -198,7 +200,7 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
get_init(block: Block, value) { get_init(block: Block, value) {
this.last = this.should_cache && block.get_unique_name( this.last = this.should_cache && block.get_unique_name(
`${this.parent.var.name}_${this.name.replace(/[^a-zA-Z_$]/g, '_')}_value` `${this.parent.var.name}_${this.name.replace(regex_invalid_variable_identifier_characters, '_')}_value`
); );
if (this.should_cache) block.add_variable(this.last); if (this.should_cache) block.add_variable(this.last);
@ -315,7 +317,7 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
return `="${value.map(chunk => { return `="${value.map(chunk => {
return chunk.type === 'Text' return chunk.type === 'Text'
? chunk.data.replace(/"/g, '\\"') ? chunk.data.replace(regex_double_quotes, '\\"')
: `\${${chunk.manipulate()}}`; : `\${${chunk.manipulate()}}`;
}).join('')}"`; }).join('')}"`;
} }

@ -108,6 +108,8 @@ function optimize_style(value: Array<Text | Expression>) {
return props; return props;
} }
const regex_important_flag = /\s*!important\s*$/;
function get_style_value(chunks: Array<Text | Expression>) { function get_style_value(chunks: Array<Text | Expression>) {
const value: Array<Text | Expression> = []; const value: Array<Text | Expression> = [];
@ -174,9 +176,9 @@ function get_style_value(chunks: Array<Text | Expression>) {
let important = false; let important = false;
const last_chunk = value[value.length - 1]; const last_chunk = value[value.length - 1];
if (last_chunk && last_chunk.type === 'Text' && /\s*!important\s*$/.test(last_chunk.data)) { if (last_chunk && last_chunk.type === 'Text' && regex_important_flag.test(last_chunk.data)) {
important = true; important = true;
last_chunk.data = last_chunk.data.replace(/\s*!important\s*$/, ''); last_chunk.data = last_chunk.data.replace(regex_important_flag, '');
if (!last_chunk.data) value.pop(); if (!last_chunk.data) value.pop();
} }

@ -136,6 +136,8 @@ const events = [
]; ];
const CHILD_DYNAMIC_ELEMENT_BLOCK = 'child_dynamic_element'; const CHILD_DYNAMIC_ELEMENT_BLOCK = 'child_dynamic_element';
const regex_invalid_variable_identifier_characters = /[^a-zA-Z0-9_$]/g;
const regex_minus_signs = /-/g;
export default class ElementWrapper extends Wrapper { export default class ElementWrapper extends Wrapper {
node: Element; node: Element;
@ -182,7 +184,7 @@ export default class ElementWrapper extends Wrapper {
this.var = { this.var = {
type: 'Identifier', type: 'Identifier',
name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_') name: node.name.replace(regex_invalid_variable_identifier_characters, '_')
}; };
this.void = is_void(node.name); this.void = is_void(node.name);
@ -320,19 +322,19 @@ export default class ElementWrapper extends Wrapper {
} }
} else if (${previous_tag}) { } else if (${previous_tag}) {
${ ${
has_transitions has_transitions
? b` ? b`
@group_outros(); @group_outros();
@transition_out(${this.var}, 1, 1, () => { @transition_out(${this.var}, 1, 1, () => {
${this.var} = null; ${this.var} = null;
}); });
@check_outros(); @check_outros();
` `
: b` : b`
${this.var}.d(1); ${this.var}.d(1);
${this.var} = null; ${this.var} = null;
` `
} }
} }
${previous_tag} = ${tag}; ${previous_tag} = ${tag};
`); `);
@ -547,7 +549,7 @@ export default class ElementWrapper extends Wrapper {
} }
} }
add_directives_in_order (block: Block) { add_directives_in_order(block: Block) {
type OrderedAttribute = EventHandler | BindingGroup | Binding | Action; type OrderedAttribute = EventHandler | BindingGroup | Binding | Action;
const binding_groups = events const binding_groups = events
@ -561,7 +563,7 @@ export default class ElementWrapper extends Wrapper {
const this_binding = this.bindings.find(b => b.node.name === 'this'); const this_binding = this.bindings.find(b => b.node.name === 'this');
function getOrder (item: OrderedAttribute) { function getOrder(item: OrderedAttribute) {
if (item instanceof EventHandler) { if (item instanceof EventHandler) {
return item.node.start; return item.node.start;
} else if (item instanceof Binding) { } else if (item instanceof Binding) {
@ -674,9 +676,9 @@ export default class ElementWrapper extends Wrapper {
function ${handler}(${params}) { function ${handler}(${params}) {
${binding_group.bindings.map(b => b.handler.mutation)} ${binding_group.bindings.map(b => b.handler.mutation)}
${Array.from(dependencies) ${Array.from(dependencies)
.filter(dep => dep[0] !== '$') .filter(dep => dep[0] !== '$')
.filter(dep => !contextual_dependencies.has(dep)) .filter(dep => !contextual_dependencies.has(dep))
.map(dep => b`${this.renderer.invalidate(dep)};`)} .map(dep => b`${this.renderer.invalidate(dep)};`)}
} }
`); `);
@ -1100,7 +1102,7 @@ export default class ElementWrapper extends Wrapper {
const snippet = expression.manipulate(block); const snippet = expression.manipulate(block);
let cached_snippet; let cached_snippet;
if (should_cache) { if (should_cache) {
cached_snippet = block.get_unique_name(`style_${name.replace(/-/g, '_')}`); cached_snippet = block.get_unique_name(`style_${name.replace(regex_minus_signs, '_')}`);
block.add_variable(cached_snippet, snippet); block.add_variable(cached_snippet, snippet);
} }
@ -1154,10 +1156,14 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapp
can_use_raw_text can_use_raw_text
); );
const regex_backslashes = /\\/g;
const regex_backticks = /`/g;
const regex_dollar_signs = /\$/g;
state.quasi.value.raw += (raw ? wrapper.data : escape_html(wrapper.data)) state.quasi.value.raw += (raw ? wrapper.data : escape_html(wrapper.data))
.replace(/\\/g, '\\\\') .replace(regex_backslashes, '\\\\')
.replace(/`/g, '\\`') .replace(regex_backticks, '\\`')
.replace(/\$/g, '\\$'); .replace(regex_dollar_signs, '\\$');
} else if (wrapper instanceof MustacheTagWrapper || wrapper instanceof RawMustacheTagWrapper) { } else if (wrapper instanceof MustacheTagWrapper || wrapper instanceof RawMustacheTagWrapper) {
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
literal.expressions.push(wrapper.node.expression.manipulate(block)); literal.expressions.push(wrapper.node.expression.manipulate(block));

@ -24,6 +24,8 @@ import { namespaces } from '../../../../utils/namespaces';
type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node }; type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node };
const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g; // is 0-9 missing?
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier; var: Identifier;
slots: Map<string, SlotDefinition> = new Map(); slots: Map<string, SlotDefinition> = new Map();
@ -596,7 +598,7 @@ export default class InlineComponentWrapper extends Wrapper {
this.node.css_custom_properties.forEach((attr) => { this.node.css_custom_properties.forEach((attr) => {
const dependencies = attr.get_dependencies(); const dependencies = attr.get_dependencies();
const should_cache = attr.should_cache(); const should_cache = attr.should_cache();
const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`); const last = should_cache && block.get_unique_name(`${attr.name.replace(regex_invalid_variable_identifier_characters, '_')}_last`);
if (should_cache) block.add_variable(last); if (should_cache) block.add_variable(last);
const value = attr.get_value(block); const value = attr.get_value(block);
const init = should_cache ? x`${last} = ${value}` : value; const init = should_cache ? x`${last} = ${value}` : value;

@ -12,6 +12,8 @@ export default function add_actions(
actions.forEach(action => add_action(block, target, action)); actions.forEach(action => add_action(block, target, action));
} }
const regex_invalid_variable_identifier_characters = /[^a-zA-Z0-9_$]/g;
export function add_action(block: Block, target: string | Expression, action: Action) { export function add_action(block: Block, target: string | Expression, action: Action) {
const { expression, template_scope } = action; const { expression, template_scope } = action;
let snippet; let snippet;
@ -23,7 +25,7 @@ export function add_action(block: Block, target: string | Expression, action: Ac
} }
const id = block.get_unique_name( const id = block.get_unique_name(
`${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action` `${action.name.replace(regex_invalid_variable_identifier_characters, '_')}_action`
); );
block.add_variable(id); block.add_variable(id);

@ -1,6 +1,8 @@
import Component from '../../../Component'; import Component from '../../../Component';
import { INode } from '../../../nodes/interfaces'; import { INode } from '../../../nodes/interfaces';
const regex_whitespace_characters = /\s/g;
export default function create_debugging_comment( export default function create_debugging_comment(
node: INode, node: INode,
component: Component component: Component
@ -36,5 +38,5 @@ export default function create_debugging_comment(
const start = locate(c); const start = locate(c);
const loc = `(${start.line}:${start.column})`; const loc = `(${start.line}:${start.column})`;
return `${loc} ${source.slice(c, d)}`.replace(/\s/g, ' '); return `${loc} ${source.slice(c, d)}`.replace(regex_whitespace_characters, ' ');
} }

@ -15,13 +15,15 @@ export function get_class_attribute_value(attribute: Attribute): ESTreeExpressio
return get_attribute_value(attribute); return get_attribute_value(attribute);
} }
const regex_double_quotes = /"/g;
export function get_attribute_value(attribute: Attribute): ESTreeExpression { export function get_attribute_value(attribute: Attribute): ESTreeExpression {
if (attribute.chunks.length === 0) return x`""`; if (attribute.chunks.length === 0) return x`""`;
return attribute.chunks return attribute.chunks
.map((chunk) => { .map((chunk) => {
return chunk.type === 'Text' return chunk.type === 'Text'
? string_literal(chunk.data.replace(/"/g, '&quot;')) as ESTreeExpression ? string_literal(chunk.data.replace(regex_double_quotes, '&quot;')) as ESTreeExpression
: x`@escape(${chunk.node}, true)`; : x`@escape(${chunk.node}, true)`;
}) })
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`); .reduce((lhs, rhs) => x`${lhs} + ${rhs}`);

@ -1,3 +1,10 @@
const regex_percentage_characters = /%/g;
const regex_file_ending = /\.[^.]+$/;
const regex_repeated_invalid_variable_identifier_characters = /[^a-zA-Z_$0-9]+/g;
const regex_starts_with_underscore = /^_/;
const regex_ends_with_underscore = /_$/;
const regex_starts_with_digit = /^(\d)/;
export default function get_name_from_filename(filename: string) { export default function get_name_from_filename(filename: string) {
if (!filename) return null; if (!filename) return null;
@ -12,12 +19,12 @@ export default function get_name_from_filename(filename: string) {
} }
const base = parts.pop() const base = parts.pop()
.replace(/%/g, 'u') .replace(regex_percentage_characters, 'u')
.replace(/\.[^.]+$/, '') .replace(regex_file_ending, '')
.replace(/[^a-zA-Z_$0-9]+/g, '_') .replace(regex_repeated_invalid_variable_identifier_characters, '_')
.replace(/^_/, '') .replace(regex_starts_with_underscore, '')
.replace(/_$/, '') .replace(regex_ends_with_underscore, '')
.replace(/^(\d)/, '_$1'); .replace(regex_starts_with_digit, '_$1');
if (!base) { if (!base) {
throw new Error(`Could not derive component name from file ${filename}`); throw new Error(`Could not derive component name from file ${filename}`);

@ -1,6 +1,9 @@
// https://github.com/darkskyapp/string-hash/blob/master/index.js // https://github.com/darkskyapp/string-hash/blob/master/index.js
const const_return_characters = /\r/g;
export default function hash(str: string): string { export default function hash(str: string): string {
str = str.replace(/\r/g, ''); str = str.replace(const_return_characters, '');
let hash = 5381; let hash = 5381;
let i = str.length; let i = str.length;

@ -19,10 +19,14 @@ const escaped = {
'>': '&gt;' '>': '&gt;'
}; };
const regex_html_characters_to_escape = /["'&<>]/g;
export function escape_html(html) { export function escape_html(html) {
return String(html).replace(/["'&<>]/g, match => escaped[match]); return String(html).replace(regex_html_characters_to_escape, match => escaped[match]);
} }
const regex_template_characters_to_escape = /(\${|`|\\)/g;
export function escape_template(str) { export function escape_template(str) {
return str.replace(/(\${|`|\\)/g, '\\$1'); return str.replace(regex_template_characters_to_escape, '\\$1');
} }

@ -15,6 +15,8 @@ interface LastAutoClosedTag {
depth: number; depth: number;
} }
const regex_position_indicator = / \(\d+:\d+\)$/;
export class Parser { export class Parser {
readonly template: string; readonly template: string;
readonly filename?: string; readonly filename?: string;
@ -93,7 +95,7 @@ export class Parser {
acorn_error(err: any) { acorn_error(err: any) {
this.error({ this.error({
code: 'parse-error', code: 'parse-error',
message: err.message.replace(/ \(\d+:\d+\)$/, '') message: err.message.replace(regex_position_indicator, '')
}, err.pos); }, err.pos);
} }

@ -11,6 +11,8 @@ import { parse_expression_at } from '../acorn';
import { Pattern } from 'estree'; import { Pattern } from 'estree';
import parser_errors from '../errors'; import parser_errors from '../errors';
const regex_not_newline_characters = /[^\n]/g;
export default function read_context( export default function read_context(
parser: Parser parser: Parser
): Pattern & { start: number; end: number } { ): Pattern & { start: number; end: number } {
@ -65,7 +67,7 @@ export default function read_context(
// so we offset it by removing 1 character in the `space_with_newline` // so we offset it by removing 1 character in the `space_with_newline`
// to achieve that, we remove the 1st space encountered, // to achieve that, we remove the 1st space encountered,
// so it will not affect the `column` of the node // so it will not affect the `column` of the node
let space_with_newline = parser.template.slice(0, start).replace(/[^\n]/g, ' '); let space_with_newline = parser.template.slice(0, start).replace(regex_not_newline_characters, ' ');
const first_space = space_with_newline.indexOf(' '); const first_space = space_with_newline.indexOf(' ');
space_with_newline = space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1); space_with_newline = space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);

@ -4,6 +4,9 @@ import { Script } from '../../interfaces';
import { Node, Program } from 'estree'; import { Node, Program } from 'estree';
import parser_errors from '../errors'; import parser_errors from '../errors';
const regex_not_newline_characters = /[^\n]/g;
const regex_closing_script_tag = /<\/script\s*>/;
function get_context(parser: Parser, attributes: any[], start: number): string { function get_context(parser: Parser, attributes: any[], start: number): string {
const context = attributes.find(attribute => attribute.name === 'context'); const context = attributes.find(attribute => attribute.name === 'context');
if (!context) return 'default'; if (!context) return 'default';
@ -23,13 +26,13 @@ function get_context(parser: Parser, attributes: any[], start: number): string {
export default function read_script(parser: Parser, start: number, attributes: Node[]): Script { export default function read_script(parser: Parser, start: number, attributes: Node[]): Script {
const script_start = parser.index; const script_start = parser.index;
const data = parser.read_until(/<\/script\s*>/, parser_errors.unclosed_script); const data = parser.read_until(regex_closing_script_tag, parser_errors.unclosed_script);
if (parser.index >= parser.template.length) { if (parser.index >= parser.template.length) {
parser.error(parser_errors.unclosed_script); parser.error(parser_errors.unclosed_script);
} }
const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') + data; const source = parser.template.slice(0, script_start).replace(regex_not_newline_characters, ' ') + data;
parser.read(/<\/script\s*>/); parser.read(regex_closing_script_tag);
let ast: Program; let ast: Program;

Loading…
Cancel
Save