diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts
index c1f67c0a8af0..8f5740ecb444 100644
--- a/src/compiler/compile/render_dom/Block.ts
+++ b/src/compiler/compile/render_dom/Block.ts
@@ -406,6 +406,21 @@ export default class Block {
return body;
}
+ has_content() {
+ return this.renderer.options.dev ||
+ this.first ||
+ this.event_listeners.length > 0 ||
+ this.chunks.intro.length > 0 ||
+ this.chunks.outro.length > 0 ||
+ this.chunks.create.length > 0 ||
+ this.chunks.hydrate.length > 0 ||
+ this.chunks.claim.length > 0 ||
+ this.chunks.mount.length > 0 ||
+ this.chunks.update.length > 0 ||
+ this.chunks.destroy.length > 0 ||
+ this.has_animation;
+ }
+
render() {
const key = this.key && this.get_unique_name('key');
diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts
index a50a86e2f3db..49ce17236a49 100644
--- a/src/compiler/compile/render_dom/index.ts
+++ b/src/compiler/compile/render_dom/index.ts
@@ -241,11 +241,16 @@ export default function dom(
args.push(x`$$props`, x`$$invalidate`);
}
- body.push(b`
- function create_fragment(#ctx) {
- ${block.get_contents()}
- }
+ const has_create_fragment = block.has_content();
+ if (has_create_fragment) {
+ body.push(b`
+ function create_fragment(#ctx) {
+ ${block.get_contents()}
+ }
+ `);
+ }
+ body.push(b`
${component.extract_javascript(component.ast.module)}
${component.fully_hoisted}
@@ -437,7 +442,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
- @init(this, { target: this.shadowRoot }, ${definition}, create_fragment, ${not_equal}, ${prop_names});
+ @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${dev_props_check}
@@ -489,7 +494,7 @@ export default function dom(
constructor(options) {
super(${options.dev && `options`});
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
- @init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names});
+ @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
${dev_props_check}
diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
index c4a848b456da..a9d989c92a20 100644
--- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
@@ -396,12 +396,12 @@ export default class InlineComponentWrapper extends Wrapper {
`);
block.chunks.create.push(
- b`if (${name}) ${name}.$$.fragment.c();`
+ b`if (${name}) @create_component(${name}.$$.fragment);`
);
if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push(
- b`if (${name}) ${name}.$$.fragment.l(${parent_nodes});`
+ b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
);
}
@@ -437,7 +437,7 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_bindings}
${munged_handlers}
- ${name}.$$.fragment.c();
+ @create_component(${name}.$$.fragment);
@transition_in(${name}.$$.fragment, 1);
@mount_component(${name}, ${update_mount_node}, ${anchor});
} else {
@@ -472,11 +472,11 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers}
`);
- block.chunks.create.push(b`${name}.$$.fragment.c();`);
+ block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push(
- b`${name}.$$.fragment.l(${parent_nodes});`
+ b`@claim_component(${name}.$$.fragment, ${parent_nodes});`
);
}
diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts
index 3855d3c361a7..0ce3b3209bb6 100644
--- a/src/runtime/internal/Component.ts
+++ b/src/runtime/internal/Component.ts
@@ -4,6 +4,21 @@ import { blank_object, is_function, run, run_all, noop, has_prop } from './utils
import { children } from './dom';
import { transition_in } from './transitions';
+interface Fragment {
+ key: string|null;
+ first: null;
+ /* create */ c: () => void;
+ /* claim */ l: (nodes: any) => void;
+ /* hydrate */ h: () => void;
+ /* mount */ m: (target: HTMLElement, anchor: any) => void;
+ /* update */ p: (changed: any, ctx: any) => void;
+ /* measure */ r: () => void;
+ /* fix */ f: () => void;
+ /* animate */ a: () => void;
+ /* intro */ i: (local: any) => void;
+ /* outro */ o: (local: any) => void;
+ /* destroy */ d: (detaching: 0|1) => void;
+}
// eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ {
dirty: null;
@@ -13,7 +28,7 @@ interface T$$ {
callbacks: any;
after_update: any[];
props: Record;
- fragment: null|any;
+ fragment: null|false|Fragment;
not_equal: any;
before_update: any[];
context: Map;
@@ -29,10 +44,18 @@ export function bind(component, name, callback) {
}
}
+export function create_component(block) {
+ block && block.c();
+}
+
+export function claim_component(block, parent_nodes) {
+ block && block.l(parent_nodes);
+}
+
export function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
- fragment.m(target, anchor);
+ fragment && fragment.m(target, anchor);
// onMount happens before the initial afterUpdate
add_render_callback(() => {
@@ -51,15 +74,16 @@ export function mount_component(component, target, anchor) {
}
export function destroy_component(component, detaching) {
- if (component.$$.fragment) {
- run_all(component.$$.on_destroy);
+ const $$ = component.$$;
+ if ($$.fragment !== null) {
+ run_all($$.on_destroy);
- component.$$.fragment.d(detaching);
+ $$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
- component.$$.on_destroy = component.$$.fragment = null;
- component.$$.ctx = {};
+ $$.on_destroy = $$.fragment = null;
+ $$.ctx = {};
}
}
@@ -115,15 +139,17 @@ export function init(component, options, instance, create_fragment, not_equal, p
$$.update();
ready = true;
run_all($$.before_update);
- $$.fragment = create_fragment($$.ctx);
+
+ // `false` as a special case of no DOM component
+ $$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- $$.fragment!.l(children(options.target));
+ $$.fragment && $$.fragment!.l(children(options.target));
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- $$.fragment!.c();
+ $$.fragment && $$.fragment!.c();
}
if (options.intro) transition_in(component.$$.fragment);
diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts
index e3d7181fcbec..7cb00c085bbc 100644
--- a/src/runtime/internal/scheduler.ts
+++ b/src/runtime/internal/scheduler.ts
@@ -70,10 +70,10 @@ export function flush() {
}
function update($$) {
- if ($$.fragment) {
+ if ($$.fragment !== null) {
$$.update($$.dirty);
run_all($$.before_update);
- $$.fragment.p($$.dirty, $$.ctx);
+ $$.fragment && $$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null;
$$.after_update.forEach(add_render_callback);
diff --git a/test/js/samples/component-static-array/expected.js b/test/js/samples/component-static-array/expected.js
index 2c38d80f3082..0f32421e912c 100644
--- a/test/js/samples/component-static-array/expected.js
+++ b/test/js/samples/component-static-array/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
init,
mount_component,
@@ -15,7 +16,7 @@ function create_fragment(ctx) {
return {
c() {
- nested.$$.fragment.c();
+ create_component(nested.$$.fragment);
},
m(target, anchor) {
mount_component(nested, target, anchor);
diff --git a/test/js/samples/component-static-immutable/expected.js b/test/js/samples/component-static-immutable/expected.js
index d60aeb393922..817db4e494b6 100644
--- a/test/js/samples/component-static-immutable/expected.js
+++ b/test/js/samples/component-static-immutable/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
init,
mount_component,
@@ -15,7 +16,7 @@ function create_fragment(ctx) {
return {
c() {
- nested.$$.fragment.c();
+ create_component(nested.$$.fragment);
},
m(target, anchor) {
mount_component(nested, target, anchor);
diff --git a/test/js/samples/component-static-immutable2/expected.js b/test/js/samples/component-static-immutable2/expected.js
index d60aeb393922..817db4e494b6 100644
--- a/test/js/samples/component-static-immutable2/expected.js
+++ b/test/js/samples/component-static-immutable2/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
init,
mount_component,
@@ -15,7 +16,7 @@ function create_fragment(ctx) {
return {
c() {
- nested.$$.fragment.c();
+ create_component(nested.$$.fragment);
},
m(target, anchor) {
mount_component(nested, target, anchor);
diff --git a/test/js/samples/component-static-var/expected.js b/test/js/samples/component-static-var/expected.js
index e1372a7b6d14..0120b2c8cf48 100644
--- a/test/js/samples/component-static-var/expected.js
+++ b/test/js/samples/component-static-var/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
detach,
element,
@@ -28,9 +29,9 @@ function create_fragment(ctx) {
return {
c() {
- foo.$$.fragment.c();
+ create_component(foo.$$.fragment);
t0 = space();
- bar.$$.fragment.c();
+ create_component(bar.$$.fragment);
t1 = space();
input = element("input");
dispose = listen(input, "input", ctx.input_input_handler);
diff --git a/test/js/samples/component-static/expected.js b/test/js/samples/component-static/expected.js
index 14b85a917a6d..76321cfd8288 100644
--- a/test/js/samples/component-static/expected.js
+++ b/test/js/samples/component-static/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
init,
mount_component,
@@ -15,7 +16,7 @@ function create_fragment(ctx) {
return {
c() {
- nested.$$.fragment.c();
+ create_component(nested.$$.fragment);
},
m(target, anchor) {
mount_component(nested, target, anchor);
diff --git a/test/js/samples/component-store-file-invalidate/expected.js b/test/js/samples/component-store-file-invalidate/expected.js
index 987f3971a1a4..5a466d778676 100644
--- a/test/js/samples/component-store-file-invalidate/expected.js
+++ b/test/js/samples/component-store-file-invalidate/expected.js
@@ -2,24 +2,12 @@ import {
SvelteComponent,
component_subscribe,
init,
- noop,
safe_not_equal,
set_store_value
} from "svelte/internal";
import { count } from "./store.js";
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
-
function instance($$self, $$props, $$invalidate) {
let $count;
component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value));
@@ -34,7 +22,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, instance, create_fragment, safe_not_equal, { increment: 0 });
+ init(this, options, instance, null, safe_not_equal, { increment: 0 });
}
get increment() {
diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js
index a63ee30ca0f5..d4b02892128c 100644
--- a/test/js/samples/computed-collapsed-if/expected.js
+++ b/test/js/samples/computed-collapsed-if/expected.js
@@ -1,15 +1,4 @@
-import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
-
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function instance($$self, $$props, $$invalidate) {
let { x } = $$props;
@@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, instance, create_fragment, safe_not_equal, { x: 0, a: 0, b: 0 });
+ init(this, options, instance, null, safe_not_equal, { x: 0, a: 0, b: 0 });
}
get a() {
diff --git a/test/js/samples/deconflict-globals/expected.js b/test/js/samples/deconflict-globals/expected.js
index bf67b6aae47c..92492dcd0552 100644
--- a/test/js/samples/deconflict-globals/expected.js
+++ b/test/js/samples/deconflict-globals/expected.js
@@ -1,17 +1,6 @@
-import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import { onMount } from "svelte";
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
-
function instance($$self, $$props, $$invalidate) {
let { foo = "bar" } = $$props;
@@ -29,7 +18,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, instance, create_fragment, safe_not_equal, { foo: 0 });
+ init(this, options, instance, null, safe_not_equal, { foo: 0 });
}
}
diff --git a/test/js/samples/dynamic-import/expected.js b/test/js/samples/dynamic-import/expected.js
index 95a50f4a4d81..605f19983f01 100644
--- a/test/js/samples/dynamic-import/expected.js
+++ b/test/js/samples/dynamic-import/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
init,
mount_component,
@@ -17,7 +18,7 @@ function create_fragment(ctx) {
return {
c() {
- lazyload.$$.fragment.c();
+ create_component(lazyload.$$.fragment);
},
m(target, anchor) {
mount_component(lazyload, target, anchor);
diff --git a/test/js/samples/empty-dom/expected.js b/test/js/samples/empty-dom/expected.js
new file mode 100644
index 000000000000..6ae0bc2999dc
--- /dev/null
+++ b/test/js/samples/empty-dom/expected.js
@@ -0,0 +1,15 @@
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
+
+function instance($$self) {
+ const a = 1 + 2;
+ return {};
+}
+
+class Component extends SvelteComponent {
+ constructor(options) {
+ super();
+ init(this, options, instance, null, safe_not_equal, {});
+ }
+}
+
+export default Component;
\ No newline at end of file
diff --git a/test/js/samples/empty-dom/input.svelte b/test/js/samples/empty-dom/input.svelte
new file mode 100644
index 000000000000..3098443ea3d4
--- /dev/null
+++ b/test/js/samples/empty-dom/input.svelte
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js
index d9176f35f7fd..be1ff282c360 100644
--- a/test/js/samples/non-imported-component/expected.js
+++ b/test/js/samples/non-imported-component/expected.js
@@ -1,5 +1,6 @@
import {
SvelteComponent,
+ create_component,
destroy_component,
detach,
init,
@@ -22,9 +23,9 @@ function create_fragment(ctx) {
return {
c() {
- imported.$$.fragment.c();
+ create_component(imported.$$.fragment);
t = space();
- nonimported.$$.fragment.c();
+ create_component(nonimported.$$.fragment);
},
m(target, anchor) {
mount_component(imported, target, anchor);
diff --git a/test/js/samples/reactive-values-non-topologically-ordered/expected.js b/test/js/samples/reactive-values-non-topologically-ordered/expected.js
index abd22c4da08b..69fd368ee84d 100644
--- a/test/js/samples/reactive-values-non-topologically-ordered/expected.js
+++ b/test/js/samples/reactive-values-non-topologically-ordered/expected.js
@@ -1,15 +1,4 @@
-import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
-
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function instance($$self, $$props, $$invalidate) {
let { x } = $$props;
@@ -36,7 +25,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, instance, create_fragment, safe_not_equal, { x: 0 });
+ init(this, options, instance, null, safe_not_equal, { x: 0 });
}
}
diff --git a/test/js/samples/reactive-values-non-writable-dependencies/expected.js b/test/js/samples/reactive-values-non-writable-dependencies/expected.js
index 6b6836889486..233e9ef83566 100644
--- a/test/js/samples/reactive-values-non-writable-dependencies/expected.js
+++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js
@@ -1,15 +1,4 @@
-import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
-
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function instance($$self, $$props, $$invalidate) {
let { a = 1 } = $$props;
@@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, instance, create_fragment, safe_not_equal, { a: 0, b: 0 });
+ init(this, options, instance, null, safe_not_equal, { a: 0, b: 0 });
}
}
diff --git a/test/js/samples/setup-method/expected.js b/test/js/samples/setup-method/expected.js
index 40fc04938335..e22398748e6b 100644
--- a/test/js/samples/setup-method/expected.js
+++ b/test/js/samples/setup-method/expected.js
@@ -1,16 +1,4 @@
-import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
-
-function create_fragment(ctx) {
- return {
- c: noop,
- m: noop,
- p: noop,
- i: noop,
- o: noop,
- d: noop
- };
-}
-
+import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
const SOME_CONSTANT = 42;
function foo(bar) {
@@ -20,7 +8,7 @@ function foo(bar) {
class Component extends SvelteComponent {
constructor(options) {
super();
- init(this, options, null, create_fragment, safe_not_equal, { foo: 0 });
+ init(this, options, null, null, safe_not_equal, { foo: 0 });
}
get foo() {
diff --git a/test/runtime/samples/empty-dom/_config.js b/test/runtime/samples/empty-dom/_config.js
new file mode 100644
index 000000000000..e3e3d0ecd521
--- /dev/null
+++ b/test/runtime/samples/empty-dom/_config.js
@@ -0,0 +1,3 @@
+export default {
+ html: '',
+};
\ No newline at end of file
diff --git a/test/runtime/samples/empty-dom/main.svelte b/test/runtime/samples/empty-dom/main.svelte
new file mode 100644
index 000000000000..3098443ea3d4
--- /dev/null
+++ b/test/runtime/samples/empty-dom/main.svelte
@@ -0,0 +1,3 @@
+
\ No newline at end of file