diff --git a/.editorconfig b/.editorconfig
index ed2a319d58..90846de5ee 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,4 +13,3 @@ insert_final_newline = false
[{package.json,.travis.yml,.eslintrc.json}]
indent_style = space
-indent_size = 2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af1a97d1f9..5f9e04e32a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Svelte changelog
+## Unreleased
+
+* Custom Elements - Call `onMount` when connected & clean-up when disconnected ([#4522](https://github.com/sveltejs/svelte/pull/4522))
+
## 3.32.3
* Fix removal of lone `:host` selectors ([#5982](https://github.com/sveltejs/svelte/issues/5982))
diff --git a/check_publish_env.js b/check_publish_env.js
new file mode 100644
index 0000000000..ce6f066cff
--- /dev/null
+++ b/check_publish_env.js
@@ -0,0 +1,4 @@
+if (!process.env.PUBLISH) {
+ console.error('npm publish must be run with the PUBLISH environment variable set');
+ process.exit(1);
+}
diff --git a/package.json b/package.json
index 333838e69d..5b04c3e82e 100644
--- a/package.json
+++ b/package.json
@@ -73,7 +73,7 @@
"dev": "rollup -cw",
"pretest": "npm run build",
"posttest": "agadoo internal/index.mjs",
- "prepublishOnly": "npm run lint && PUBLISH=true npm test",
+ "prepublishOnly": "node check_publish_env.js && npm run lint && npm test",
"tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly",
"lint": "eslint \"{src,test}/**/*.{ts,js}\""
},
diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts
index 8e7deb7a78..a56f3fb2b3 100644
--- a/src/compiler/compile/render_dom/index.ts
+++ b/src/compiler/compile/render_dom/index.ts
@@ -485,7 +485,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
- @init(this, { target: this.shadowRoot, props: ${init_props} }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
+ @init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check}
diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts
index 6d211dfedd..5a2a966d0b 100644
--- a/src/runtime/internal/Component.ts
+++ b/src/runtime/internal/Component.ts
@@ -34,6 +34,7 @@ interface T$$ {
on_mount: any[];
on_destroy: any[];
skip_bound: boolean;
+ on_disconnect: any[];
}
export function bind(component, name, callback) {
@@ -52,23 +53,26 @@ export function claim_component(block, parent_nodes) {
block && block.l(parent_nodes);
}
-export function mount_component(component, target, anchor) {
+export function mount_component(component, target, anchor, customElement) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment && fragment.m(target, anchor);
- // onMount happens before the initial afterUpdate
- add_render_callback(() => {
- const new_on_destroy = on_mount.map(run).filter(is_function);
- if (on_destroy) {
- on_destroy.push(...new_on_destroy);
- } else {
- // Edge case - component was destroyed immediately,
- // most likely as a result of a binding initialising
- run_all(new_on_destroy);
- }
- component.$$.on_mount = [];
- });
+ if (!customElement) {
+ // onMount happens before the initial afterUpdate
+ add_render_callback(() => {
+
+ const new_on_destroy = on_mount.map(run).filter(is_function);
+ if (on_destroy) {
+ on_destroy.push(...new_on_destroy);
+ } else {
+ // Edge case - component was destroyed immediately,
+ // most likely as a result of a binding initialising
+ run_all(new_on_destroy);
+ }
+ component.$$.on_mount = [];
+ });
+ }
after_update.forEach(add_render_callback);
}
@@ -113,6 +117,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
// lifecycle
on_mount: [],
on_destroy: [],
+ on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(parent_component ? parent_component.$$.context : []),
@@ -155,7 +160,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
}
if (options.intro) transition_in(component.$$.fragment);
- mount_component(component, options.target, options.anchor);
+ mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
@@ -173,6 +178,9 @@ if (typeof HTMLElement === 'function') {
}
connectedCallback() {
+ const { on_mount } = this.$$;
+ this.$$.on_disconnect = on_mount.map(run).filter(is_function);
+
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
@@ -184,6 +192,10 @@ if (typeof HTMLElement === 'function') {
this[attr] = newValue;
}
+ disconnectedCallback() {
+ run_all(this.$$.on_disconnect);
+ }
+
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
diff --git a/test/custom-elements/index.ts b/test/custom-elements/index.ts
index d3644a3142..35df156879 100644
--- a/test/custom-elements/index.ts
+++ b/test/custom-elements/index.ts
@@ -110,8 +110,8 @@ describe('custom-elements', function() {
const page = await browser.newPage();
- page.on('console', (type, ...args) => {
- console[type](...args);
+ page.on('console', (type) => {
+ console[type._type](type._text);
});
page.on('error', error => {
diff --git a/test/custom-elements/samples/oncreate/main.svelte b/test/custom-elements/samples/oncreate/main.svelte
index ed3980a28e..23819e660f 100644
--- a/test/custom-elements/samples/oncreate/main.svelte
+++ b/test/custom-elements/samples/oncreate/main.svelte
@@ -3,9 +3,12 @@
diff --git a/test/custom-elements/samples/oncreate/test.js b/test/custom-elements/samples/oncreate/test.js
index c33f8a6a10..f451979976 100644
--- a/test/custom-elements/samples/oncreate/test.js
+++ b/test/custom-elements/samples/oncreate/test.js
@@ -2,7 +2,9 @@ import * as assert from 'assert';
import './main.svelte';
export default function (target) {
- target.innerHTML = '