Lint and format src/runtime/internal/ssr.js

pull/8569/head
S. Elliott Johnson 3 years ago
parent bcf02b5707
commit 79a4c8ad00

@ -2,70 +2,65 @@ import { set_current_component, current_component } from './lifecycle';
import { run_all, blank_object } from './utils'; import { run_all, blank_object } from './utils';
import { boolean_attributes } from '../../shared/boolean_attributes'; import { boolean_attributes } from '../../shared/boolean_attributes';
export { is_void } from '../../shared/utils/names'; export { is_void } from '../../shared/utils/names';
export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; export const invalid_attribute_name_character =
/[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
// https://infra.spec.whatwg.org/#noncharacter // https://infra.spec.whatwg.org/#noncharacter
/** @returns {string} */ /** @returns {string} */
export function spread(args, attrs_to_add) { export function spread(args, attrs_to_add) {
const attributes = Object.assign({}, ...args); const attributes = Object.assign({}, ...args);
if (attrs_to_add) { if (attrs_to_add) {
const classes_to_add = attrs_to_add.classes; const classes_to_add = attrs_to_add.classes;
const styles_to_add = attrs_to_add.styles; const styles_to_add = attrs_to_add.styles;
if (classes_to_add) { if (classes_to_add) {
if (attributes.class == null) { if (attributes.class == null) {
attributes.class = classes_to_add; attributes.class = classes_to_add;
} } else {
else { attributes.class += ' ' + classes_to_add;
attributes.class += ' ' + classes_to_add; }
} }
} if (styles_to_add) {
if (styles_to_add) { if (attributes.style == null) {
if (attributes.style == null) { attributes.style = style_object_to_string(styles_to_add);
attributes.style = style_object_to_string(styles_to_add); } else {
} attributes.style = style_object_to_string(
else { merge_ssr_styles(attributes.style, styles_to_add)
attributes.style = style_object_to_string(merge_ssr_styles(attributes.style, styles_to_add)); );
} }
} }
} }
let str = ''; let str = '';
Object.keys(attributes).forEach((name) => { Object.keys(attributes).forEach((name) => {
if (invalid_attribute_name_character.test(name)) if (invalid_attribute_name_character.test(name)) return;
return; const value = attributes[name];
const value = attributes[name]; if (value === true) str += ' ' + name;
if (value === true) else if (boolean_attributes.has(name.toLowerCase())) {
str += ' ' + name; if (value) str += ' ' + name;
else if (boolean_attributes.has(name.toLowerCase())) { } else if (value != null) {
if (value) str += ` ${name}="${value}"`;
str += ' ' + name; }
} });
else if (value != null) { return str;
str += ` ${name}="${value}"`;
}
});
return str;
} }
/** @returns {{}} */ /** @returns {{}} */
export function merge_ssr_styles(style_attribute, style_directive) { export function merge_ssr_styles(style_attribute, style_directive) {
const style_object = {}; const style_object = {};
for (const individual_style of style_attribute.split(';')) { for (const individual_style of style_attribute.split(';')) {
const colon_index = individual_style.indexOf(':'); const colon_index = individual_style.indexOf(':');
const name = individual_style.slice(0, colon_index).trim(); const name = individual_style.slice(0, colon_index).trim();
const value = individual_style.slice(colon_index + 1).trim(); const value = individual_style.slice(colon_index + 1).trim();
if (!name) if (!name) continue;
continue; style_object[name] = value;
style_object[name] = value; }
} for (const name in style_directive) {
for (const name in style_directive) { const value = style_directive[name];
const value = style_directive[name]; if (value) {
if (value) { style_object[name] = value;
style_object[name] = value; } else {
} delete style_object[name];
else { }
delete style_object[name]; }
} return style_object;
}
return style_object;
} }
const ATTR_REGEX = /[&"]/g; const ATTR_REGEX = /[&"]/g;
const CONTENT_REGEX = /[&<]/g; const CONTENT_REGEX = /[&<]/g;
@ -76,123 +71,119 @@ const CONTENT_REGEX = /[&<]/g;
* @returns {string} * @returns {string}
*/ */
export function escape(value, is_attr = false) { export function escape(value, is_attr = false) {
const str = String(value); const str = String(value);
const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX; const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
pattern.lastIndex = 0; pattern.lastIndex = 0;
let escaped = ''; let escaped = '';
let last = 0; let last = 0;
while (pattern.test(str)) { while (pattern.test(str)) {
const i = pattern.lastIndex - 1; const i = pattern.lastIndex - 1;
const ch = str[i]; const ch = str[i];
escaped += str.substring(last, i) + (ch === '&' ? '&amp;' : ch === '"' ? '&quot;' : '&lt;'); escaped += str.substring(last, i) + (ch === '&' ? '&amp;' : ch === '"' ? '&quot;' : '&lt;');
last = i + 1; last = i + 1;
} }
return escaped + str.substring(last); return escaped + str.substring(last);
} }
/** @returns {any} */ /** @returns {any} */
export function escape_attribute_value(value) { export function escape_attribute_value(value) {
// keep booleans, null, and undefined for the sake of `spread` // keep booleans, null, and undefined for the sake of `spread`
const should_escape = typeof value === 'string' || (value && typeof value === 'object'); const should_escape = typeof value === 'string' || (value && typeof value === 'object');
return should_escape ? escape(value, true) : value; return should_escape ? escape(value, true) : value;
} }
/** @returns {{}} */ /** @returns {{}} */
export function escape_object(obj) { export function escape_object(obj) {
const result = {}; const result = {};
for (const key in obj) { for (const key in obj) {
result[key] = escape_attribute_value(obj[key]); result[key] = escape_attribute_value(obj[key]);
} }
return result; return result;
} }
/** @returns {string} */ /** @returns {string} */
export function each(items, fn) { export function each(items, fn) {
let str = ''; let str = '';
for (let i = 0; i < items.length; i += 1) { for (let i = 0; i < items.length; i += 1) {
str += fn(items[i], i); str += fn(items[i], i);
} }
return str; return str;
} }
export const missing_component = { export const missing_component = {
$$render: () => '' $$render: () => ''
}; };
/** @returns {any} */ /** @returns {any} */
export function validate_component(component, name) { export function validate_component(component, name) {
if (!component || !component.$$render) { if (!component || !component.$$render) {
if (name === 'svelte:component') if (name === 'svelte:component') name += ' this={...}';
name += ' this={...}'; throw new Error(
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`); `<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`
} );
return component; }
return component;
} }
/** @returns {string} */ /** @returns {string} */
export function debug(file, line, column, values) { export function debug(file, line, column, values) {
console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console
console.log(values); // eslint-disable-line no-console console.log(values); // eslint-disable-line no-console
return ''; return '';
} }
let on_destroy; let on_destroy;
/** @returns {{ render: (props?: {}, { $$slots, context }?: { $$slots?: {}; context?: Map<any, any>; }) => { html: any; css: { code: string; map: any; }; head: string; }; $$render: (result: any, props: any, bindings: any, slots: any, context: any) => any; }} */ /** @returns {{ render: (props?: {}, { $$slots, context }?: { $$slots?: {}; context?: Map<any, any>; }) => { html: any; css: { code: string; map: any; }; head: string; }; $$render: (result: any, props: any, bindings: any, slots: any, context: any) => any; }} */
export function create_ssr_component(fn) { export function create_ssr_component(fn) {
/** @returns {any} */ /** @returns {any} */
function $$render(result, props, bindings, slots, context) { function $$render(result, props, bindings, slots, context) {
const parent_component = current_component; const parent_component = current_component;
const $$ = { const $$ = {
on_destroy, on_destroy,
context: new Map(context || (parent_component ? parent_component.$$.context : [])), context: new Map(context || (parent_component ? parent_component.$$.context : [])),
// these will be immediately discarded // these will be immediately discarded
on_mount: [], on_mount: [],
before_update: [], before_update: [],
after_update: [], after_update: [],
callbacks: blank_object() callbacks: blank_object()
}; };
set_current_component({ $$ }); set_current_component({ $$ });
const html = fn(result, props, bindings, slots); const html = fn(result, props, bindings, slots);
set_current_component(parent_component); set_current_component(parent_component);
return html; return html;
} }
return { return {
render: (props = {}, { $$slots = {}, context = new Map() } = {}) => { render: (props = {}, { $$slots = {}, context = new Map() } = {}) => {
on_destroy = []; on_destroy = [];
const result = { title: '', head: '', css: new Set() }; const result = { title: '', head: '', css: new Set() };
const html = $$render(result, props, {}, $$slots, context); const html = $$render(result, props, {}, $$slots, context);
run_all(on_destroy); run_all(on_destroy);
return { return {
html, html,
css: { css: {
code: Array.from(result.css) code: Array.from(result.css)
.map((css) => css.code) .map((css) => css.code)
.join('\n'), .join('\n'),
map: null // TODO map: null // TODO
}, },
head: result.title + result.head head: result.title + result.head
}; };
}, },
$$render $$render
}; };
} }
/** @returns {string} */ /** @returns {string} */
export function add_attribute(name, value, boolean) { export function add_attribute(name, value, boolean) {
if (value == null || (boolean && !value)) if (value == null || (boolean && !value)) return '';
return ''; const assignment = boolean && value === true ? '' : `="${escape(value, true)}"`;
const assignment = boolean && value === true ? '' : `="${escape(value, true)}"`; return ` ${name}${assignment}`;
return ` ${name}${assignment}`;
} }
/** @returns {string} */ /** @returns {string} */
export function add_classes(classes) { export function add_classes(classes) {
return classes ? ` class="${classes}"` : ''; return classes ? ` class="${classes}"` : '';
} }
/** @returns {string} */ /** @returns {string} */
function style_object_to_string(style_object) { function style_object_to_string(style_object) {
return Object.keys(style_object) return Object.keys(style_object)
.filter((key) => style_object[key]) .filter((key) => style_object[key])
.map((key) => `${key}: ${escape_attribute_value(style_object[key])};`) .map((key) => `${key}: ${escape_attribute_value(style_object[key])};`)
.join(' '); .join(' ');
} }
/** @returns {string} */ /** @returns {string} */
export function add_styles(style_object) { export function add_styles(style_object) {
const styles = style_object_to_string(style_object); const styles = style_object_to_string(style_object);
return styles ? ` style="${styles}"` : ''; return styles ? ` style="${styles}"` : '';
} }

Loading…
Cancel
Save