diff --git a/src/compiler/compile/render_dom/index.js b/src/compiler/compile/render_dom/index.js
index a7622e61a9..e6faa458ec 100644
--- a/src/compiler/compile/render_dom/index.js
+++ b/src/compiler/compile/render_dom/index.js
@@ -14,404 +14,459 @@ import { push_array } from '../../utils/push_array.js';
* @returns {{ js: import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').Node[]; css: import('../../interfaces.js').CssResult; }}
*/
export default function dom(component, options) {
- const { name } = component;
- const renderer = new Renderer(component, options);
- const { block } = renderer;
- block.has_outro_method = true;
- const body = [];
- if (renderer.file_var) {
- const file = component.file ? x `"${component.file}"` : x `undefined`;
- body.push(b `const ${renderer.file_var} = ${file};`);
- }
- const css = component.stylesheet.render(options.filename);
- const css_sourcemap_enabled = check_enable_sourcemap(options.enableSourcemap, 'css');
- if (css_sourcemap_enabled) {
- css.map = apply_preprocessor_sourcemap(options.filename, css.map,
- /** @type {string | import('@ampproject/remapping').RawSourceMap | import('@ampproject/remapping').DecodedSourceMap} */ (options.sourcemap));
- }
- else {
- css.map = null;
- }
- const styles = css_sourcemap_enabled && component.stylesheet.has_styles && options.dev
- ? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
- : css.code;
- const add_css = component.get_unique_name('add_css');
- const should_add_css = !!styles && (options.customElement || options.css === 'injected');
- if (should_add_css) {
- body.push(b `
+ const { name } = component;
+ const renderer = new Renderer(component, options);
+ const { block } = renderer;
+ block.has_outro_method = true;
+ const body = [];
+ if (renderer.file_var) {
+ const file = component.file ? x`"${component.file}"` : x`undefined`;
+ body.push(b`const ${renderer.file_var} = ${file};`);
+ }
+ const css = component.stylesheet.render(options.filename);
+ const css_sourcemap_enabled = check_enable_sourcemap(options.enableSourcemap, 'css');
+ if (css_sourcemap_enabled) {
+ css.map = apply_preprocessor_sourcemap(
+ options.filename,
+ css.map,
+ /** @type {string | import('@ampproject/remapping').RawSourceMap | import('@ampproject/remapping').DecodedSourceMap} */ (
+ options.sourcemap
+ )
+ );
+ } else {
+ css.map = null;
+ }
+ const styles =
+ css_sourcemap_enabled && component.stylesheet.has_styles && options.dev
+ ? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
+ : css.code;
+ const add_css = component.get_unique_name('add_css');
+ const should_add_css = !!styles && (options.customElement || options.css === 'injected');
+ if (should_add_css) {
+ body.push(b`
function ${add_css}(target) {
@append_styles(target, "${component.stylesheet.id}", "${styles}");
}
`);
- }
- // fix order
- // TODO the deconflicted names of blocks are reversed... should set them here
- const blocks = renderer.blocks.slice().reverse();
- push_array(body, blocks.map((block) => {
- // TODO this is a horrible mess — renderer.blocks
- // contains a mixture of Blocks and Nodes
- if (( /** @type {import('./Block.js').default} */(block)).render)
- return ( /** @type {import('./Block.js').default} */(block)).render();
- return block;
- }));
- if (options.dev && !options.hydratable) {
- block.chunks.claim.push(b `throw new @_Error("options.hydrate only works if the component was compiled with the \`hydratable: true\` option");`);
- }
- const uses_slots = component.var_lookup.has('$$slots');
-
- /** @type {import('estree').Node[] | undefined} */
- let compute_slots;
- if (uses_slots) {
- compute_slots = b `
+ }
+ // fix order
+ // TODO the deconflicted names of blocks are reversed... should set them here
+ const blocks = renderer.blocks.slice().reverse();
+ push_array(
+ body,
+ blocks.map((block) => {
+ // TODO this is a horrible mess — renderer.blocks
+ // contains a mixture of Blocks and Nodes
+ if (/** @type {import('./Block.js').default} */ (block).render)
+ return /** @type {import('./Block.js').default} */ (block).render();
+ return block;
+ })
+ );
+ if (options.dev && !options.hydratable) {
+ block.chunks.claim.push(
+ b`throw new @_Error("options.hydrate only works if the component was compiled with the \`hydratable: true\` option");`
+ );
+ }
+ const uses_slots = component.var_lookup.has('$$slots');
+
+ /** @type {import('estree').Node[] | undefined} */
+ let compute_slots;
+ if (uses_slots) {
+ compute_slots = b`
const $$slots = @compute_slots(#slots);
`;
- }
- const uses_props = component.var_lookup.has('$$props');
- const uses_rest = component.var_lookup.has('$$restProps');
- const $$props = uses_props || uses_rest ? '$$new_props' : '$$props';
- const props = component.vars.filter((variable) => !variable.module && variable.export_name);
- const writable_props = props.filter((variable) => variable.writable);
- const omit_props_names = component.get_unique_name('omit_props_names');
- const compute_rest = x `@compute_rest_props($$props, ${omit_props_names.name})`;
- const rest = uses_rest
- ? b `
+ }
+ const uses_props = component.var_lookup.has('$$props');
+ const uses_rest = component.var_lookup.has('$$restProps');
+ const $$props = uses_props || uses_rest ? '$$new_props' : '$$props';
+ const props = component.vars.filter((variable) => !variable.module && variable.export_name);
+ const writable_props = props.filter((variable) => variable.writable);
+ const omit_props_names = component.get_unique_name('omit_props_names');
+ const compute_rest = x`@compute_rest_props($$props, ${omit_props_names.name})`;
+ const rest = uses_rest
+ ? b`
const ${omit_props_names.name} = [${props.map((prop) => `"${prop.export_name}"`).join(',')}];
let $$restProps = ${compute_rest};
`
- : null;
- const set = uses_props || uses_rest || writable_props.length > 0 || component.slots.size > 0
- ? x `
+ : null;
+ const set =
+ uses_props || uses_rest || writable_props.length > 0 || component.slots.size > 0
+ ? x`
${$$props} => {
- ${uses_props &&
- renderer.invalidate('$$props', x `$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`)}
- ${uses_rest &&
- !uses_props &&
- x `$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`}
- ${uses_rest && renderer.invalidate('$$restProps', x `$$restProps = ${compute_rest}`)}
- ${writable_props.map((prop) => b `if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x `${prop.name} = ${$$props}.${prop.export_name}`)};`)}
- ${component.slots.size > 0 &&
- b `if ('$$scope' in ${$$props}) ${renderer.invalidate('$$scope', x `$$scope = ${$$props}.$$scope`)};`}
+ ${
+ uses_props &&
+ renderer.invalidate(
+ '$$props',
+ x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`
+ )
+ }
+ ${
+ uses_rest &&
+ !uses_props &&
+ x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`
+ }
+ ${uses_rest && renderer.invalidate('$$restProps', x`$$restProps = ${compute_rest}`)}
+ ${writable_props.map(
+ (prop) =>
+ b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(
+ prop.name,
+ x`${prop.name} = ${$$props}.${prop.export_name}`
+ )};`
+ )}
+ ${
+ component.slots.size > 0 &&
+ b`if ('$$scope' in ${$$props}) ${renderer.invalidate(
+ '$$scope',
+ x`$$scope = ${$$props}.$$scope`
+ )};`
+ }
}
`
- : null;
- const accessors = [];
- const not_equal = component.component_options.immutable ? x `@not_equal` : x `@safe_not_equal`;
-
- /** @type {import('estree').Node[] | import('estree').Node} */
- let missing_props_check;
-
- /** @type {import('estree').Expression} */
- let inject_state;
-
- /** @type {import('estree').Expression} */
- let capture_state;
-
- /** @type {import('estree').Node[] | import('estree').Node} */
- let props_inject;
- props.forEach((prop) => {
- const variable = component.var_lookup.get(prop.name);
- if (!variable.writable || component.component_options.accessors) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'get',
- key: { type: 'Identifier', name: prop.export_name },
- value: x `function() {
- return ${prop.hoistable
- ? prop.name
- : x `this.$$.ctx[${renderer.context_lookup.get(prop.name).index}]`}
+ : null;
+ const accessors = [];
+ const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
+
+ /** @type {import('estree').Node[] | import('estree').Node} */
+ let missing_props_check;
+
+ /** @type {import('estree').Expression} */
+ let inject_state;
+
+ /** @type {import('estree').Expression} */
+ let capture_state;
+
+ /** @type {import('estree').Node[] | import('estree').Node} */
+ let props_inject;
+ props.forEach((prop) => {
+ const variable = component.var_lookup.get(prop.name);
+ if (!variable.writable || component.component_options.accessors) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'get',
+ key: { type: 'Identifier', name: prop.export_name },
+ value: x`function() {
+ return ${
+ prop.hoistable
+ ? prop.name
+ : x`this.$$.ctx[${renderer.context_lookup.get(prop.name).index}]`
+ }
}`
- });
- }
- else if (component.compile_options.dev) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'get',
- key: { type: 'Identifier', name: prop.export_name },
- value: x `function() {
+ });
+ } else if (component.compile_options.dev) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'get',
+ key: { type: 'Identifier', name: prop.export_name },
+ value: x`function() {
throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''");
}`
- });
- }
- if (component.component_options.accessors) {
- if (variable.writable && !renderer.readonly.has(prop.name)) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'set',
- key: { type: 'Identifier', name: prop.export_name },
- value: x `function(${prop.name}) {
+ });
+ }
+ if (component.component_options.accessors) {
+ if (variable.writable && !renderer.readonly.has(prop.name)) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'set',
+ key: { type: 'Identifier', name: prop.export_name },
+ value: x`function(${prop.name}) {
this.$$set({ ${prop.export_name}: ${prop.name} });
@flush();
}`
- });
- }
- else if (component.compile_options.dev) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'set',
- key: { type: 'Identifier', name: prop.export_name },
- value: x `function(value) {
+ });
+ } else if (component.compile_options.dev) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'set',
+ key: { type: 'Identifier', name: prop.export_name },
+ value: x`function(value) {
throw new @_Error("<${component.tag}>: Cannot set read-only property '${prop.export_name}'");
}`
- });
- }
- }
- else if (component.compile_options.dev) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'set',
- key: { type: 'Identifier', name: prop.export_name },
- value: x `function(value) {
+ });
+ }
+ } else if (component.compile_options.dev) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'set',
+ key: { type: 'Identifier', name: prop.export_name },
+ value: x`function(value) {
throw new @_Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ''");
}`
- });
- }
- });
- component.instance_exports_from.forEach((exports_from) => {
- const import_declaration = {
- ...exports_from,
- type: 'ImportDeclaration',
- specifiers: [],
- source: exports_from.source
- };
- component.imports.push(/** @type {import('estree').ImportDeclaration} */ (import_declaration));
- exports_from.specifiers.forEach((specifier) => {
- if (component.component_options.accessors) {
- const name = component.get_unique_name(specifier.exported.name);
- import_declaration.specifiers.push({
- ...specifier,
- type: 'ImportSpecifier',
- imported: specifier.local,
- local: name
- });
- accessors.push({
- type: 'MethodDefinition',
- kind: 'get',
- key: { type: 'Identifier', name: specifier.exported.name },
- value: x `function() {
+ });
+ }
+ });
+ component.instance_exports_from.forEach((exports_from) => {
+ const import_declaration = {
+ ...exports_from,
+ type: 'ImportDeclaration',
+ specifiers: [],
+ source: exports_from.source
+ };
+ component.imports.push(/** @type {import('estree').ImportDeclaration} */ (import_declaration));
+ exports_from.specifiers.forEach((specifier) => {
+ if (component.component_options.accessors) {
+ const name = component.get_unique_name(specifier.exported.name);
+ import_declaration.specifiers.push({
+ ...specifier,
+ type: 'ImportSpecifier',
+ imported: specifier.local,
+ local: name
+ });
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'get',
+ key: { type: 'Identifier', name: specifier.exported.name },
+ value: x`function() {
return ${name}
}`
- });
- }
- else if (component.compile_options.dev) {
- accessors.push({
- type: 'MethodDefinition',
- kind: 'get',
- key: { type: 'Identifier', name: specifier.exported.name },
- value: x `function() {
+ });
+ } else if (component.compile_options.dev) {
+ accessors.push({
+ type: 'MethodDefinition',
+ kind: 'get',
+ key: { type: 'Identifier', name: specifier.exported.name },
+ value: x`function() {
throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''");
}`
- });
- }
- });
- });
- if (component.compile_options.dev) {
- // checking that expected ones were passed
- const expected = props.filter((prop) => prop.writable && !prop.initialised);
- if (expected.length) {
- missing_props_check = b `
+ });
+ }
+ });
+ });
+ if (component.compile_options.dev) {
+ // checking that expected ones were passed
+ const expected = props.filter((prop) => prop.writable && !prop.initialised);
+ if (expected.length) {
+ missing_props_check = b`
$$self.$$.on_mount.push(function () {
- ${expected.map((prop) => b `
+ ${expected.map(
+ (prop) => b`
if (${prop.name} === undefined && !(('${prop.export_name}' in $$props) || $$self.$$.bound[$$self.$$.props['${prop.export_name}']])) {
@_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'");
- }`)}
+ }`
+ )}
});
`;
- }
- const capturable_vars = component.vars.filter((v) => !v.internal && !v.global && !v.name.startsWith('$$'));
- if (capturable_vars.length > 0) {
- capture_state = x `() => ({ ${capturable_vars.map((prop) => p `${prop.name}`)} })`;
- }
- const injectable_vars = capturable_vars.filter((v) => !v.module && v.writable && v.name[0] !== '$');
- if (uses_props || injectable_vars.length > 0) {
- inject_state = x `
+ }
+ const capturable_vars = component.vars.filter(
+ (v) => !v.internal && !v.global && !v.name.startsWith('$$')
+ );
+ if (capturable_vars.length > 0) {
+ capture_state = x`() => ({ ${capturable_vars.map((prop) => p`${prop.name}`)} })`;
+ }
+ const injectable_vars = capturable_vars.filter(
+ (v) => !v.module && v.writable && v.name[0] !== '$'
+ );
+ if (uses_props || injectable_vars.length > 0) {
+ inject_state = x`
${$$props} => {
- ${uses_props &&
- renderer.invalidate('$$props', x `$$props = @assign(@assign({}, $$props), $$new_props)`)}
- ${injectable_vars.map((v) => b `if ('${v.name}' in $$props) ${renderer.invalidate(v.name, x `${v.name} = ${$$props}.${v.name}`)};`)}
+ ${
+ uses_props &&
+ renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)
+ }
+ ${injectable_vars.map(
+ (v) =>
+ b`if ('${v.name}' in $$props) ${renderer.invalidate(
+ v.name,
+ x`${v.name} = ${$$props}.${v.name}`
+ )};`
+ )}
}
`;
- props_inject = b `
+ props_inject = b`
if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}
`;
- }
- }
- // instrument assignments
- if (component.ast.instance) {
- let scope = component.instance_scope;
- const map = component.instance_scope_map;
-
- /** @type {import('estree').Node | null} */
- let execution_context = null;
- walk(component.ast.instance.content, {
- enter(node) {
- if (map.has(node)) {
- scope = /** @type {import('periscopic').Scope} */ (map.get(node));
- if (!execution_context && !scope.block) {
- execution_context = node;
- }
- }
- else if (!execution_context &&
- node.type === 'LabeledStatement' &&
- node.label.name === '$') {
- execution_context = node;
- }
- },
- leave(node) {
- if (map.has(node)) {
- scope = scope.parent;
- }
- if (execution_context === node) {
- execution_context = null;
- }
- if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
- const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
- // normally (`a = 1`, `b.c = 2`), there'll be a single name
- // (a or b). In destructuring cases (`[d, e] = [e, d]`) there
- // may be more, in which case we need to tack the extra ones
- // onto the initial function call
- const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee)));
- this.replace(invalidate(renderer, scope, node, names, execution_context === null));
- }
- }
- });
- component.rewrite_props(({ name, reassigned, export_name }) => {
- const value = `$${name}`;
- const i = renderer.context_lookup.get(`$${name}`).index;
- const insert = reassigned || export_name
- ? b `${`$$subscribe_${name}`}()`
- : b `@component_subscribe($$self, ${name}, #value => $$invalidate(${i}, ${value} = #value))`;
- if (component.compile_options.dev) {
- return b `@validate_store(${name}, '${name}'); ${insert}`;
- }
- return insert;
- });
- }
- const args = [x `$$self`];
- const has_invalidate = props.length > 0 ||
- component.has_reactive_assignments ||
- component.slots.size > 0 ||
- capture_state ||
- inject_state;
- if (has_invalidate) {
- args.push(x `$$props`, x `$$invalidate`);
- }
- else if (component.compile_options.dev) {
- // $$props arg is still needed for unknown prop check
- args.push(x `$$props`);
- }
- // has_create_fragment is intentionally to be true in dev mode.
- const has_create_fragment = component.compile_options.dev || block.has_content();
- if (has_create_fragment) {
- body.push(b `
+ }
+ }
+ // instrument assignments
+ if (component.ast.instance) {
+ let scope = component.instance_scope;
+ const map = component.instance_scope_map;
+
+ /** @type {import('estree').Node | null} */
+ let execution_context = null;
+ walk(component.ast.instance.content, {
+ enter(node) {
+ if (map.has(node)) {
+ scope = /** @type {import('periscopic').Scope} */ (map.get(node));
+ if (!execution_context && !scope.block) {
+ execution_context = node;
+ }
+ } else if (
+ !execution_context &&
+ node.type === 'LabeledStatement' &&
+ node.label.name === '$'
+ ) {
+ execution_context = node;
+ }
+ },
+ leave(node) {
+ if (map.has(node)) {
+ scope = scope.parent;
+ }
+ if (execution_context === node) {
+ execution_context = null;
+ }
+ if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
+ const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
+ // normally (`a = 1`, `b.c = 2`), there'll be a single name
+ // (a or b). In destructuring cases (`[d, e] = [e, d]`) there
+ // may be more, in which case we need to tack the extra ones
+ // onto the initial function call
+ const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee)));
+ this.replace(invalidate(renderer, scope, node, names, execution_context === null));
+ }
+ }
+ });
+ component.rewrite_props(({ name, reassigned, export_name }) => {
+ const value = `$${name}`;
+ const i = renderer.context_lookup.get(`$${name}`).index;
+ const insert =
+ reassigned || export_name
+ ? b`${`$$subscribe_${name}`}()`
+ : b`@component_subscribe($$self, ${name}, #value => $$invalidate(${i}, ${value} = #value))`;
+ if (component.compile_options.dev) {
+ return b`@validate_store(${name}, '${name}'); ${insert}`;
+ }
+ return insert;
+ });
+ }
+ const args = [x`$$self`];
+ const has_invalidate =
+ props.length > 0 ||
+ component.has_reactive_assignments ||
+ component.slots.size > 0 ||
+ capture_state ||
+ inject_state;
+ if (has_invalidate) {
+ args.push(x`$$props`, x`$$invalidate`);
+ } else if (component.compile_options.dev) {
+ // $$props arg is still needed for unknown prop check
+ args.push(x`$$props`);
+ }
+ // has_create_fragment is intentionally to be true in dev mode.
+ const has_create_fragment = component.compile_options.dev || block.has_content();
+ if (has_create_fragment) {
+ body.push(b`
function create_fragment(#ctx) {
${block.get_contents()}
}
`);
- }
- body.push(b `
+ }
+ body.push(b`
${component.extract_javascript(component.ast.module)}
${component.fully_hoisted}
`);
- const filtered_props = props.filter((prop) => {
- const variable = component.var_lookup.get(prop.name);
- if (variable.hoistable)
- return false;
- return prop.name[0] !== '$';
- });
- const reactive_stores = component.vars.filter((variable) => variable.name[0] === '$' && variable.name[1] !== '$');
- const instance_javascript = component.extract_javascript(component.ast.instance);
- const has_definition = component.compile_options.dev ||
- (instance_javascript && instance_javascript.length > 0) ||
- filtered_props.length > 0 ||
- uses_props ||
- component.partly_hoisted.length > 0 ||
- renderer.initial_context.length > 0 ||
- component.reactive_declarations.length > 0 ||
- capture_state ||
- inject_state;
- const definition = has_definition
- ? component.alias('instance')
- : { type: 'Literal', value: null };
- const reactive_store_subscriptions = reactive_stores
- .filter((store) => {
- const variable = component.var_lookup.get(store.name.slice(1));
- return !variable || variable.hoistable;
- })
- .map(({ name }) => b `
- ${component.compile_options.dev && b `@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
- @component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate(${renderer.context_lookup.get(name).index}, ${name} = $$value));
- `);
- const resubscribable_reactive_store_unsubscribers = reactive_stores
- .filter((store) => {
- const variable = component.var_lookup.get(store.name.slice(1));
- return variable && (variable.reassigned || variable.export_name);
- })
- .map(({ name }) => b `$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`);
- if (has_definition) {
-
- /** @type {import('estree').Node | import('estree').Node[]} */
- const reactive_declarations = [];
-
- /** @type {import('estree').Node[]} */
- const fixed_reactive_declarations = []; // not really 'reactive' but whatever
- component.reactive_declarations.forEach((d) => {
- const dependencies = Array.from(d.dependencies);
- const uses_rest_or_props = !!dependencies.find((n) => n === '$$props' || n === '$$restProps');
- const writable = dependencies.filter((n) => {
- const variable = component.var_lookup.get(n);
- return variable && (variable.export_name || variable.mutated || variable.reassigned);
- });
- const condition = !uses_rest_or_props && writable.length > 0 && renderer.dirty(writable, true);
- let statement = d.node; // TODO remove label (use d.node.body) if it's not referenced
- if (condition)
- statement = /** @type {import('estree').Statement} */ (b `if (${condition}) { ${statement} }`[0]);
- if (condition || uses_rest_or_props) {
- reactive_declarations.push(statement);
- }
- else {
- fixed_reactive_declarations.push(statement);
- }
- });
- const injected = Array.from(component.injected_reactive_declaration_vars).filter((name) => {
- const variable = component.var_lookup.get(name);
- return variable.injected && variable.name[0] !== '$';
- });
- const reactive_store_declarations = reactive_stores.map((variable) => {
- const $name = variable.name;
- const name = $name.slice(1);
- const store = component.var_lookup.get(name);
- if (store && (store.reassigned || store.export_name)) {
- const unsubscribe = `$$unsubscribe_${name}`;
- const subscribe = `$$subscribe_${name}`;
- const i = renderer.context_lookup.get($name).index;
- return b `let ${$name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${name}, $$value => $$invalidate(${i}, ${$name} = $$value)), ${name})`;
- }
- return b `let ${$name};`;
- });
-
- /** @type {import('estree').Node[] | undefined} */
- let unknown_props_check;
- if (component.compile_options.dev && !(uses_props || uses_rest)) {
- unknown_props_check = b `
- const writable_props = [${writable_props.map((prop) => x `'${prop.export_name}'`)}];
+ const filtered_props = props.filter((prop) => {
+ const variable = component.var_lookup.get(prop.name);
+ if (variable.hoistable) return false;
+ return prop.name[0] !== '$';
+ });
+ const reactive_stores = component.vars.filter(
+ (variable) => variable.name[0] === '$' && variable.name[1] !== '$'
+ );
+ const instance_javascript = component.extract_javascript(component.ast.instance);
+ const has_definition =
+ component.compile_options.dev ||
+ (instance_javascript && instance_javascript.length > 0) ||
+ filtered_props.length > 0 ||
+ uses_props ||
+ component.partly_hoisted.length > 0 ||
+ renderer.initial_context.length > 0 ||
+ component.reactive_declarations.length > 0 ||
+ capture_state ||
+ inject_state;
+ const definition = has_definition
+ ? component.alias('instance')
+ : { type: 'Literal', value: null };
+ const reactive_store_subscriptions = reactive_stores
+ .filter((store) => {
+ const variable = component.var_lookup.get(store.name.slice(1));
+ return !variable || variable.hoistable;
+ })
+ .map(
+ ({ name }) => b`
+ ${component.compile_options.dev && b`@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
+ @component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate(${
+ renderer.context_lookup.get(name).index
+ }, ${name} = $$value));
+ `
+ );
+ const resubscribable_reactive_store_unsubscribers = reactive_stores
+ .filter((store) => {
+ const variable = component.var_lookup.get(store.name.slice(1));
+ return variable && (variable.reassigned || variable.export_name);
+ })
+ .map(({ name }) => b`$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`);
+ if (has_definition) {
+ /** @type {import('estree').Node | import('estree').Node[]} */
+ const reactive_declarations = [];
+
+ /** @type {import('estree').Node[]} */
+ const fixed_reactive_declarations = []; // not really 'reactive' but whatever
+ component.reactive_declarations.forEach((d) => {
+ const dependencies = Array.from(d.dependencies);
+ const uses_rest_or_props = !!dependencies.find((n) => n === '$$props' || n === '$$restProps');
+ const writable = dependencies.filter((n) => {
+ const variable = component.var_lookup.get(n);
+ return variable && (variable.export_name || variable.mutated || variable.reassigned);
+ });
+ const condition =
+ !uses_rest_or_props && writable.length > 0 && renderer.dirty(writable, true);
+ let statement = d.node; // TODO remove label (use d.node.body) if it's not referenced
+ if (condition)
+ statement = /** @type {import('estree').Statement} */ (
+ b`if (${condition}) { ${statement} }`[0]
+ );
+ if (condition || uses_rest_or_props) {
+ reactive_declarations.push(statement);
+ } else {
+ fixed_reactive_declarations.push(statement);
+ }
+ });
+ const injected = Array.from(component.injected_reactive_declaration_vars).filter((name) => {
+ const variable = component.var_lookup.get(name);
+ return variable.injected && variable.name[0] !== '$';
+ });
+ const reactive_store_declarations = reactive_stores.map((variable) => {
+ const $name = variable.name;
+ const name = $name.slice(1);
+ const store = component.var_lookup.get(name);
+ if (store && (store.reassigned || store.export_name)) {
+ const unsubscribe = `$$unsubscribe_${name}`;
+ const subscribe = `$$subscribe_${name}`;
+ const i = renderer.context_lookup.get($name).index;
+ return b`let ${$name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${name}, $$value => $$invalidate(${i}, ${$name} = $$value)), ${name})`;
+ }
+ return b`let ${$name};`;
+ });
+
+ /** @type {import('estree').Node[] | undefined} */
+ let unknown_props_check;
+ if (component.compile_options.dev && !(uses_props || uses_rest)) {
+ unknown_props_check = b`
+ const writable_props = [${writable_props.map((prop) => x`'${prop.export_name}'`)}];
@_Object.keys($$props).forEach(key => {
- if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$' && key !== 'slot') @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`);
+ if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$' && key !== 'slot') @_console.warn(\`<${
+ component.tag
+ }> was created with unknown prop '\${key}'\`);
});
`;
- }
- const return_value = {
- type: 'ArrayExpression',
- elements: renderer.initial_context.map((member) => ( /** @type {import('estree').Expression} */({
- type: 'Identifier',
- name: member.name
- })))
- };
- body.push(b `
+ }
+ const return_value = {
+ type: 'ArrayExpression',
+ elements: renderer.initial_context.map(
+ (member) =>
+ /** @type {import('estree').Expression} */ ({
+ type: 'Identifier',
+ name: member.name
+ })
+ )
+ };
+ body.push(b`
function ${definition}(${args}) {
- ${injected.map((name) => b `let ${name};`)}
+ ${injected.map((name) => b`let ${name};`)}
${rest}
@@ -421,13 +476,17 @@ export default function dom(component, options) {
${resubscribable_reactive_store_unsubscribers}
- ${component.slots.size || component.compile_options.dev || uses_slots
- ? b `let { $$slots: #slots = {}, $$scope } = $$props;`
- : null}
- ${component.compile_options.dev &&
- b `@validate_slots('${component.tag}', #slots, [${[...component.slots.keys()]
- .map((key) => `'${key}'`)
- .join(',')}]);`}
+ ${
+ component.slots.size || component.compile_options.dev || uses_slots
+ ? b`let { $$slots: #slots = {}, $$scope } = $$props;`
+ : null
+ }
+ ${
+ component.compile_options.dev &&
+ b`@validate_slots('${component.tag}', #slots, [${[...component.slots.keys()]
+ .map((key) => `'${key}'`)
+ .join(',')}]);`
+ }
${compute_slots}
${instance_javascript}
@@ -435,97 +494,114 @@ export default function dom(component, options) {
${missing_props_check}
${unknown_props_check}
- ${renderer.binding_groups.size > 0 &&
- b `const $$binding_groups = [${[...renderer.binding_groups.keys()].map((_) => x `[]`)}];`}
+ ${
+ renderer.binding_groups.size > 0 &&
+ b`const $$binding_groups = [${[...renderer.binding_groups.keys()].map((_) => x`[]`)}];`
+ }
${component.partly_hoisted}
- ${set && b `$$self.$$set = ${set};`}
+ ${set && b`$$self.$$set = ${set};`}
- ${capture_state && b `$$self.$capture_state = ${capture_state};`}
+ ${capture_state && b`$$self.$capture_state = ${capture_state};`}
- ${inject_state && b `$$self.$inject_state = ${inject_state};`}
+ ${inject_state && b`$$self.$inject_state = ${inject_state};`}
- ${ /* before reactive declarations */props_inject}
+ ${/* before reactive declarations */ props_inject}
- ${reactive_declarations.length > 0 &&
- b `
+ ${
+ reactive_declarations.length > 0 &&
+ b`
$$self.$$.update = () => {
${reactive_declarations}
};
- `}
+ `
+ }
${fixed_reactive_declarations}
- ${uses_props && b `$$props = @exclude_internal_props($$props);`}
+ ${uses_props && b`$$props = @exclude_internal_props($$props);`}
return ${return_value};
}
`);
- }
- const prop_indexes = /** @type {import('estree').ObjectExpression} */ (x `{
+ }
+ const prop_indexes = /** @type {import('estree').ObjectExpression} */ (
+ x`{
${props
- .filter((v) => v.export_name && !v.module)
- .map((v) => p `${v.export_name}: ${renderer.context_lookup.get(v.name).index}`)}
- }`);
- let dirty;
- if (renderer.context_overflow) {
- dirty = x `[]`;
- for (let i = 0; i < renderer.context.length; i += 31) {
- dirty.elements.push(x `-1`);
- }
- }
- const superclass = {
- type: 'Identifier',
- name: options.dev ? '@SvelteComponentDev' : '@SvelteComponent'
- };
- const optional_parameters = [];
- if (should_add_css) {
- optional_parameters.push(add_css);
- }
- else if (dirty) {
- optional_parameters.push(x `null`);
- }
- if (dirty) {
- optional_parameters.push(dirty);
- }
- const declaration = /** @type {import('estree').ClassDeclaration} */ (b `
+ .filter((v) => v.export_name && !v.module)
+ .map((v) => p`${v.export_name}: ${renderer.context_lookup.get(v.name).index}`)}
+ }`
+ );
+ let dirty;
+ if (renderer.context_overflow) {
+ dirty = x`[]`;
+ for (let i = 0; i < renderer.context.length; i += 31) {
+ dirty.elements.push(x`-1`);
+ }
+ }
+ const superclass = {
+ type: 'Identifier',
+ name: options.dev ? '@SvelteComponentDev' : '@SvelteComponent'
+ };
+ const optional_parameters = [];
+ if (should_add_css) {
+ optional_parameters.push(add_css);
+ } else if (dirty) {
+ optional_parameters.push(x`null`);
+ }
+ if (dirty) {
+ optional_parameters.push(dirty);
+ }
+ const declaration = /** @type {import('estree').ClassDeclaration} */ (
+ b`
class ${name} extends ${superclass} {
constructor(options) {
super(${options.dev && 'options'});
- @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${optional_parameters});
- ${options.dev &&
- b `@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
+ @init(this, options, ${definition}, ${
+ has_create_fragment ? 'create_fragment' : 'null'
+ }, ${not_equal}, ${prop_indexes}, ${optional_parameters});
+ ${
+ options.dev &&
+ b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`
+ }
}
}
- `[0]);
- push_array(declaration.body.body, accessors);
- body.push(declaration);
- if (options.customElement) {
- const props_str = writable_props.reduce((def, prop) => {
- def[prop.export_name] =
- component.component_options.customElement?.props?.[prop.export_name] || {};
- if (prop.is_boolean && !def[prop.export_name].type) {
- def[prop.export_name].type = 'Boolean';
- }
- return def;
- }, {});
- const slots_str = [...component.slots.keys()].map((key) => `"${key}"`).join(',');
- const accessors_str = accessors
- .filter((accessor) => !writable_props.some((prop) => prop.export_name === accessor.key.name))
- .map((accessor) => `"${accessor.key.name}"`)
- .join(',');
- const use_shadow_dom = component.component_options.customElement?.shadow !== 'none' ? 'true' : 'false';
- if (component.component_options.customElement?.tag) {
- body.push(b `@_customElements.define("${component.component_options.customElement.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}], ${use_shadow_dom}));`);
- }
- else {
- body.push(b `@create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}], ${use_shadow_dom});`);
- }
- }
- return { js: flatten(body), css };
+ `[0]
+ );
+ push_array(declaration.body.body, accessors);
+ body.push(declaration);
+ if (options.customElement) {
+ const props_str = writable_props.reduce((def, prop) => {
+ def[prop.export_name] =
+ component.component_options.customElement?.props?.[prop.export_name] || {};
+ if (prop.is_boolean && !def[prop.export_name].type) {
+ def[prop.export_name].type = 'Boolean';
+ }
+ return def;
+ }, {});
+ const slots_str = [...component.slots.keys()].map((key) => `"${key}"`).join(',');
+ const accessors_str = accessors
+ .filter((accessor) => !writable_props.some((prop) => prop.export_name === accessor.key.name))
+ .map((accessor) => `"${accessor.key.name}"`)
+ .join(',');
+ const use_shadow_dom =
+ component.component_options.customElement?.shadow !== 'none' ? 'true' : 'false';
+ if (component.component_options.customElement?.tag) {
+ body.push(
+ b`@_customElements.define("${
+ component.component_options.customElement.tag
+ }", @create_custom_element(${name}, ${JSON.stringify(
+ props_str
+ )}, [${slots_str}], [${accessors_str}], ${use_shadow_dom}));`
+ );
+ } else {
+ body.push(
+ b`@create_custom_element(${name}, ${JSON.stringify(
+ props_str
+ )}, [${slots_str}], [${accessors_str}], ${use_shadow_dom});`
+ );
+ }
+ }
+ return { js: flatten(body), css };
}
-
-
-
-