Lint and format src/compiler/compile/render_ssr/index.js

pull/8569/head
Simon Holthausen 3 years ago
parent 35735ea403
commit 56e9dd7d2c

@ -12,122 +12,136 @@ import check_enable_sourcemap from '../utils/check_enable_sourcemap.js';
* @returns {{ js: import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").Node[]; css: CssResult; }} * @returns {{ js: import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").Node[]; css: CssResult; }}
*/ */
export default function ssr(component, options) { export default function ssr(component, options) {
const renderer = new Renderer({ const renderer = new Renderer({
name: component.name name: component.name
}); });
const { name } = component; const { name } = component;
// create $$render function // create $$render function
renderer.render(trim(component.fragment.children), Object.assign({ renderer.render(
locate: component.locate trim(component.fragment.children),
}, options)); Object.assign(
// TODO put this inside the Renderer class {
const literal = renderer.pop(); locate: component.locate
// TODO concatenate CSS maps },
const css = options.customElement options
? { code: null, map: null } )
: component.stylesheet.render(options.filename); );
const uses_rest = component.var_lookup.has('$$restProps'); // TODO put this inside the Renderer class
const props = component.vars.filter((variable) => !variable.module && variable.export_name); const literal = renderer.pop();
const rest = uses_rest // TODO concatenate CSS maps
? b `let $$restProps = @compute_rest_props($$props, [${props const css = options.customElement
.map((prop) => `"${prop.export_name}"`) ? { code: null, map: null }
.join(',')}]);` : component.stylesheet.render(options.filename);
: null; const uses_rest = component.var_lookup.has('$$restProps');
const uses_slots = component.var_lookup.has('$$slots'); const props = component.vars.filter((variable) => !variable.module && variable.export_name);
const slots = uses_slots ? b `let $$slots = @compute_slots(#slots);` : null; const rest = uses_rest
const reactive_stores = component.vars.filter((variable) => variable.name[0] === '$' && variable.name[1] !== '$'); ? b`let $$restProps = @compute_rest_props($$props, [${props
const reactive_store_subscriptions = reactive_stores .map((prop) => `"${prop.export_name}"`)
.filter((store) => { .join(',')}]);`
const variable = component.var_lookup.get(store.name.slice(1)); : null;
return !variable || variable.hoistable; const uses_slots = component.var_lookup.has('$$slots');
}) const slots = uses_slots ? b`let $$slots = @compute_slots(#slots);` : null;
.map(({ name }) => { const reactive_stores = component.vars.filter(
const store_name = name.slice(1); (variable) => variable.name[0] === '$' && variable.name[1] !== '$'
return b ` );
${component.compile_options.dev && b `@validate_store(${store_name}, '${store_name}');`} const reactive_store_subscriptions = reactive_stores
.filter((store) => {
const variable = component.var_lookup.get(store.name.slice(1));
return !variable || variable.hoistable;
})
.map(({ name }) => {
const store_name = name.slice(1);
return b`
${component.compile_options.dev && b`@validate_store(${store_name}, '${store_name}');`}
${`$$unsubscribe_${store_name}`} = @subscribe(${store_name}, #value => ${name} = #value) ${`$$unsubscribe_${store_name}`} = @subscribe(${store_name}, #value => ${name} = #value)
`; `;
}); });
const reactive_store_unsubscriptions = reactive_stores.map(({ name }) => b `${`$$unsubscribe_${name.slice(1)}`}()`); const reactive_store_unsubscriptions = reactive_stores.map(
const reactive_store_declarations = reactive_stores.map(({ name }) => { ({ name }) => b`${`$$unsubscribe_${name.slice(1)}`}()`
const store_name = name.slice(1); );
const store = component.var_lookup.get(store_name); const reactive_store_declarations = reactive_stores.map(({ name }) => {
if (store && store.reassigned) { const store_name = name.slice(1);
const unsubscribe = `$$unsubscribe_${store_name}`; const store = component.var_lookup.get(store_name);
const subscribe = `$$subscribe_${store_name}`; if (store && store.reassigned) {
return b `let ${name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${store_name}, $$value => ${name} = $$value), ${store_name})`; const unsubscribe = `$$unsubscribe_${store_name}`;
} const subscribe = `$$subscribe_${store_name}`;
return b `let ${name}, ${`$$unsubscribe_${store_name}`};`; return b`let ${name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${store_name}, $$value => ${name} = $$value), ${store_name})`;
}); }
// instrument get/set store value return b`let ${name}, ${`$$unsubscribe_${store_name}`};`;
if (component.ast.instance) { });
let scope = component.instance_scope; // instrument get/set store value
const map = component.instance_scope_map; if (component.ast.instance) {
walk(component.ast.instance.content, { let scope = component.instance_scope;
enter(node) { const map = component.instance_scope_map;
if (map.has(node)) { walk(component.ast.instance.content, {
scope = map.get(node); enter(node) {
} if (map.has(node)) {
}, scope = map.get(node);
leave(node) { }
if (map.has(node)) { },
scope = scope.parent; leave(node) {
} if (map.has(node)) {
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { scope = scope.parent;
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; }
const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee))); if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
const to_invalidate = new Set(); const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
for (const name of names) { const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee)));
const variable = component.var_lookup.get(name); const to_invalidate = new Set();
if (variable && for (const name of names) {
!variable.hoistable && const variable = component.var_lookup.get(name);
!variable.global && if (
!variable.module && variable &&
(variable.subscribable || variable.name[0] === '$')) { !variable.hoistable &&
to_invalidate.add(variable.name); !variable.global &&
} !variable.module &&
} (variable.subscribable || variable.name[0] === '$')
if (to_invalidate.size) { ) {
this.replace(invalidate(/** @type {any} */ ({ component }), scope, node, to_invalidate, true)); to_invalidate.add(variable.name);
} }
} }
} if (to_invalidate.size) {
}); this.replace(
} invalidate(/** @type {any} */ ({ component }), scope, node, to_invalidate, true)
component.rewrite_props(({ name, reassigned }) => { );
const value = `$${name}`; }
let insert = reassigned }
? b `${`$$subscribe_${name}`}()` }
: b `${`$$unsubscribe_${name}`} = @subscribe(${name}, #value => $${value} = #value)`; });
if (component.compile_options.dev) { }
insert = b `@validate_store(${name}, '${name}'); ${insert}`; component.rewrite_props(({ name, reassigned }) => {
} const value = `$${name}`;
return insert; let insert = reassigned
}); ? b`${`$$subscribe_${name}`}()`
const instance_javascript = component.extract_javascript(component.ast.instance); : b`${`$$unsubscribe_${name}`} = @subscribe(${name}, #value => $${value} = #value)`;
// TODO only do this for props with a default value if (component.compile_options.dev) {
const parent_bindings = instance_javascript insert = b`@validate_store(${name}, '${name}'); ${insert}`;
? component.vars }
.filter((variable) => !variable.module && variable.export_name) return insert;
.map((prop) => { });
return b `if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`; const instance_javascript = component.extract_javascript(component.ast.instance);
}) // TODO only do this for props with a default value
: []; const parent_bindings = instance_javascript
const injected = Array.from(component.injected_reactive_declaration_vars).filter((name) => { ? component.vars
const variable = component.var_lookup.get(name); .filter((variable) => !variable.module && variable.export_name)
return variable.injected; .map((prop) => {
}); return b`if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`;
const reactive_declarations = component.reactive_declarations.map((d) => { })
const body = ( /** @type {import('estree').LabeledStatement} */(d.node)).body; : [];
let statement = b `${body}`; const injected = Array.from(component.injected_reactive_declaration_vars).filter((name) => {
if (!d.declaration) { const variable = component.var_lookup.get(name);
// TODO do not add label if it's not referenced return variable.injected;
statement = b `$: { ${statement} }`; });
} const reactive_declarations = component.reactive_declarations.map((d) => {
return statement; const body = /** @type {import('estree').LabeledStatement} */ (d.node).body;
}); let statement = b`${body}`;
const main = renderer.has_bindings if (!d.declaration) {
? b ` // TODO do not add label if it's not referenced
statement = b`$: { ${statement} }`;
}
return statement;
});
const main = renderer.has_bindings
? b`
let $$settled; let $$settled;
let $$rendered; let $$rendered;
@ -143,32 +157,34 @@ export default function ssr(component, options) {
return $$rendered; return $$rendered;
` `
: b ` : b`
${reactive_declarations} ${reactive_declarations}
${reactive_store_unsubscriptions} ${reactive_store_unsubscriptions}
return ${literal};`; return ${literal};`;
const blocks = [ const blocks = [
...injected.map((name) => b `let ${name};`), ...injected.map((name) => b`let ${name};`),
rest, rest,
slots, slots,
...reactive_store_declarations, ...reactive_store_declarations,
...reactive_store_subscriptions, ...reactive_store_subscriptions,
instance_javascript, instance_javascript,
...parent_bindings, ...parent_bindings,
css.code && b `$$result.css.add(#css);`, css.code && b`$$result.css.add(#css);`,
main main
].filter(Boolean); ].filter(Boolean);
const css_sourcemap_enabled = check_enable_sourcemap(options.enableSourcemap, 'css'); const css_sourcemap_enabled = check_enable_sourcemap(options.enableSourcemap, 'css');
const js = b ` const js = b`
${css.code ${
? b ` css.code
? b`
const #css = { const #css = {
code: "${css.code}", code: "${css.code}",
map: ${css_sourcemap_enabled && css.map ? string_literal(css.map.toString()) : 'null'} map: ${css_sourcemap_enabled && css.map ? string_literal(css.map.toString()) : 'null'}
};` };`
: null} : null
}
${component.extract_javascript(component.ast.module)} ${component.extract_javascript(component.ast.module)}
@ -178,32 +194,24 @@ export default function ssr(component, options) {
${blocks} ${blocks}
}); });
`; `;
return { js, css }; return { js, css };
} }
/** @param {TemplateNode[]} nodes */ /** @param {TemplateNode[]} nodes */
function trim(nodes) { function trim(nodes) {
let start = 0; let start = 0;
for (; start < nodes.length; start += 1) { for (; start < nodes.length; start += 1) {
const node = /** @type {import('../nodes/Text.js').default} */ (nodes[start]); const node = /** @type {import('../nodes/Text.js').default} */ (nodes[start]);
if (node.type !== 'Text') if (node.type !== 'Text') break;
break; node.data = node.data.replace(/^\s+/, '');
node.data = node.data.replace(/^\s+/, ''); if (node.data) break;
if (node.data) }
break; let end = nodes.length;
} for (; end > start; end -= 1) {
let end = nodes.length; const node = /** @type {import('../nodes/Text.js').default} */ (nodes[end - 1]);
for (; end > start; end -= 1) { if (node.type !== 'Text') break;
const node = /** @type {import('../nodes/Text.js').default} */ (nodes[end - 1]); node.data = node.data.trimRight();
if (node.type !== 'Text') if (node.data) break;
break; }
node.data = node.data.trimRight(); return nodes.slice(start, end);
if (node.data)
break;
}
return nodes.slice(start, end);
} }

Loading…
Cancel
Save