Lint and format src/compiler/compile/render_dom/wrappers/Element/StyleAttribute.js

pull/8569/head
Simon Holthausen 3 years ago
parent 84371f2564
commit 6d9ade6b1e

@ -5,183 +5,170 @@ import add_to_set from '../../../utils/add_to_set.js';
/** @extends AttributeWrapper */ /** @extends AttributeWrapper */
export default class StyleAttributeWrapper extends AttributeWrapper { export default class StyleAttributeWrapper extends AttributeWrapper {
/** @type {import('../../../nodes/Attribute.js').default} */
node;
/** @type {import('../../../nodes/Attribute.js').default} */ /** @type {import('../Element.js').default} */
node; parent;
/** @type {import('../Element.js').default} */ /** @param {import('../../Block.js').default} block */
parent; render(block) {
const style_props = optimize_style(this.node.chunks);
/** @param {import('../../Block.js').default} block */ if (!style_props) return super.render(block);
render(block) { style_props.forEach((prop) => {
const style_props = optimize_style(this.node.chunks); let value;
if (!style_props) if (is_dynamic(prop.value)) {
return super.render(block); const prop_dependencies = new Set();
style_props.forEach((prop) => { value = prop.value
let value; .map((chunk) => {
if (is_dynamic(prop.value)) { if (chunk.type === 'Text') {
const prop_dependencies = new Set(); return string_literal(chunk.data);
value = prop.value } else {
.map((chunk) => { add_to_set(prop_dependencies, chunk.dynamic_dependencies());
if (chunk.type === 'Text') { return chunk.manipulate(block);
return string_literal(chunk.data); }
} })
else { .reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
add_to_set(prop_dependencies, chunk.dynamic_dependencies()); // TODO is this necessary? style.setProperty always treats value as string, no?
return chunk.manipulate(block); // if (prop.value.length === 1 || prop.value[0].type !== 'Text') {
} // value = x`"" + ${value}`;
}) // }
.reduce((lhs, rhs) => x `${lhs} + ${rhs}`); if (prop_dependencies.size) {
// TODO is this necessary? style.setProperty always treats value as string, no? let condition = block.renderer.dirty(Array.from(prop_dependencies));
// if (prop.value.length === 1 || prop.value[0].type !== 'Text') { if (block.has_outros) {
// value = x`"" + ${value}`; condition = x`!#current || ${condition}`;
// } }
if (prop_dependencies.size) { const update = b`
let condition = block.renderer.dirty(Array.from(prop_dependencies));
if (block.has_outros) {
condition = x `!#current || ${condition}`;
}
const update = b `
if (${condition}) { if (${condition}) {
@set_style(${this.parent.var}, "${prop.key}", ${value}, ${prop.important ? 1 : null}); @set_style(${this.parent.var}, "${prop.key}", ${value}, ${prop.important ? 1 : null});
}`; }`;
block.chunks.update.push(update); block.chunks.update.push(update);
} }
} } else {
else { value = string_literal(
value = string_literal(( /** @type {import('../../../nodes/Text.js').default} */(prop.value[0])).data); /** @type {import('../../../nodes/Text.js').default} */ (prop.value[0]).data
} );
block.chunks.hydrate.push(b `@set_style(${this.parent.var}, "${prop.key}", ${value}, ${prop.important ? 1 : null});`); }
}); block.chunks.hydrate.push(
} b`@set_style(${this.parent.var}, "${prop.key}", ${value}, ${prop.important ? 1 : null});`
);
});
}
} }
const regex_style_prop_key = /^\s*([\w-]+):\s*/; const regex_style_prop_key = /^\s*([\w-]+):\s*/;
/** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} value */ /** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} value */
function optimize_style(value) { function optimize_style(value) {
/** @type {StyleProp[]} */
/** @type {StyleProp[]} */ const props = [];
const props = []; let chunks = value.slice();
let chunks = value.slice(); while (chunks.length) {
while (chunks.length) { const chunk = chunks[0];
const chunk = chunks[0]; if (chunk.type !== 'Text') return null;
if (chunk.type !== 'Text') const key_match = regex_style_prop_key.exec(chunk.data);
return null; if (!key_match) return null;
const key_match = regex_style_prop_key.exec(chunk.data); const key = key_match[1];
if (!key_match) const offset = key_match.index + key_match[0].length;
return null; const remaining_data = chunk.data.slice(offset);
const key = key_match[1]; if (remaining_data) {
const offset = key_match.index + key_match[0].length; chunks[0] = /** @type {import('../../../nodes/Text.js').default} */ ({
const remaining_data = chunk.data.slice(offset); start: chunk.start + offset,
if (remaining_data) { end: chunk.end,
chunks[0] = /** @type {import('../../../nodes/Text.js').default} */ ({ type: 'Text',
start: chunk.start + offset, data: remaining_data
end: chunk.end, });
type: 'Text', } else {
data: remaining_data chunks.shift();
}); }
} const result = get_style_value(chunks);
else { props.push({ key, value: result.value, important: result.important });
chunks.shift(); chunks = result.chunks;
} }
const result = get_style_value(chunks); return props;
props.push({ key, value: result.value, important: result.important });
chunks = result.chunks;
}
return props;
} }
const regex_important_flag = /\s*!important\s*$/; const regex_important_flag = /\s*!important\s*$/;
const regex_semicolon_or_whitespace = /[;\s]/; const regex_semicolon_or_whitespace = /[;\s]/;
/** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} chunks */ /** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} chunks */
function get_style_value(chunks) { function get_style_value(chunks) {
/** @type {Array<Text | import('../../../nodes/shared/Expression.js').default>} */
/** @type {Array<Text | import('../../../nodes/shared/Expression.js').default>} */ const value = [];
const value = []; let in_url = false;
let in_url = false; let quote_mark = null;
let quote_mark = null; let escaped = false;
let escaped = false; let closed = false;
let closed = false; while (chunks.length && !closed) {
while (chunks.length && !closed) { const chunk = chunks.shift();
const chunk = chunks.shift(); if (chunk.type === 'Text') {
if (chunk.type === 'Text') { let c = 0;
let c = 0; while (c < chunk.data.length) {
while (c < chunk.data.length) { const char = chunk.data[c];
const char = chunk.data[c]; if (escaped) {
if (escaped) { escaped = false;
escaped = false; } else if (char === '\\') {
} escaped = true;
else if (char === '\\') { } else if (char === quote_mark) {
escaped = true; quote_mark = null;
} } else if (char === '"' || char === "'") {
else if (char === quote_mark) { quote_mark = char;
quote_mark = null; } else if (char === ')' && in_url) {
} in_url = false;
else if (char === '"' || char === "'") { } else if (char === 'u' && chunk.data.slice(c, c + 4) === 'url(') {
quote_mark = char; in_url = true;
} } else if (char === ';' && !in_url && !quote_mark) {
else if (char === ')' && in_url) { closed = true;
in_url = false; break;
} }
else if (char === 'u' && chunk.data.slice(c, c + 4) === 'url(') { c += 1;
in_url = true; }
} if (c > 0) {
else if (char === ';' && !in_url && !quote_mark) { value.push(
closed = true; /** @type {import('../../../nodes/Text.js').default} */ ({
break; type: 'Text',
} start: chunk.start,
c += 1; end: chunk.start + c,
} data: chunk.data.slice(0, c)
if (c > 0) { })
value.push(/** @type {import('../../../nodes/Text.js').default} */ ({ );
type: 'Text', }
start: chunk.start, while (regex_semicolon_or_whitespace.test(chunk.data[c])) c += 1;
end: chunk.start + c, const remaining_data = chunk.data.slice(c);
data: chunk.data.slice(0, c) if (remaining_data) {
})); chunks.unshift(
} /** @type {import('../../../nodes/Text.js').default} */ ({
while (regex_semicolon_or_whitespace.test(chunk.data[c])) start: chunk.start + c,
c += 1; end: chunk.end,
const remaining_data = chunk.data.slice(c); type: 'Text',
if (remaining_data) { data: remaining_data
chunks.unshift(/** @type {import('../../../nodes/Text.js').default} */ ({ })
start: chunk.start + c, );
end: chunk.end, break;
type: 'Text', }
data: remaining_data } else {
})); value.push(chunk);
break; }
} }
} let important = false;
else { const last_chunk = value[value.length - 1];
value.push(chunk); if (last_chunk && last_chunk.type === 'Text' && regex_important_flag.test(last_chunk.data)) {
} important = true;
} last_chunk.data = last_chunk.data.replace(regex_important_flag, '');
let important = false; if (!last_chunk.data) value.pop();
const last_chunk = value[value.length - 1]; }
if (last_chunk && last_chunk.type === 'Text' && regex_important_flag.test(last_chunk.data)) { return {
important = true; chunks,
last_chunk.data = last_chunk.data.replace(regex_important_flag, ''); value,
if (!last_chunk.data) important
value.pop(); };
}
return {
chunks,
value,
important
};
} }
/** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} value */ /** @param {Array<Text | import('../../../nodes/shared/Expression.js').default>} value */
function is_dynamic(value) { function is_dynamic(value) {
return value.length > 1 || value[0].type !== 'Text'; return value.length > 1 || value[0].type !== 'Text';
} }
/** @typedef {Object} StyleProp /** @typedef {Object} StyleProp
* @property {string} key * @property {string} key
* @property {Array<Text|Expression>} value * @property {Array<Text|Expression>} value
* @property {boolean} important * @property {boolean} important
*/ */

Loading…
Cancel
Save