diff --git a/.eslintrc.js b/.eslintrc.js index a093de610b..66c533eb5d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,5 +10,8 @@ module.exports = { 'estree' ], 'svelte3/compiler': require('./compiler') + }, + rules: { + '@typescript-eslint/no-non-null-assertion': 'off' } }; diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 9247436e64..9d42d0e102 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1525,7 +1525,7 @@ function process_component_options(component: Component, nodes) { ? component.compile_options.accessors : !!component.compile_options.customElement, preserveWhitespace: !!component.compile_options.preserveWhitespace, - namespace: component.compile_options.namespace, + namespace: component.compile_options.namespace }; const node = nodes.find(node => node.name === 'svelte:options'); @@ -1582,7 +1582,7 @@ function process_component_options(component: Component, nodes) { if (!chunk) { break; - }; + } if (value.length > 1 || chunk.expression?.type !== 'ObjectExpression') { return error(); diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index b05feed749..590b0fe65c 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -543,11 +543,6 @@ export default function dom( body.push(declaration); if (options.customElement && component.tag != null) { - let init_props = x`@attribute_to_object(this.attributes)`; - if (uses_slots) { - init_props = x`{ ...${init_props}, $$slots: @get_custom_elements_slots(this) }`; - } - const props_str = writable_props.reduce((def, prop) => { def[prop.export_name] = component.component_options.cePropsDefinition?.[prop.export_name] || {}; if (prop.is_boolean && !def[prop.export_name].type) { @@ -560,6 +555,7 @@ export default function dom( .filter(accessor => !writable_props.some(prop => prop.export_name === accessor.key.name)) .map(accessor => `"${accessor.key.name}"`) .join(','); + body.push( b`@_customElements.define("${component.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}]));` ); diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 380d7527b3..0a589e3394 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -132,10 +132,6 @@ export default class SlotWrapper extends Wrapper { const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name}; const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}); ${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null} - ${has_fallback && this.renderer.options.customElement && this.renderer.options.tag - // This ensures that fallback content is rendered into the element given by the custom element wrapper - ? b`if (${slot_or_fallback}.$$c_e) ${this.fallback.name}(#ctx);` - : null} `); block.chunks.create.push( diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index c2225f6267..1afb139ae7 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -155,7 +155,7 @@ if (typeof HTMLElement === 'function') { constructor( private $$componentCtor: ComponentType, - private $$slots: string[], + private $$slots: string[] ) { super(); this.attachShadow({ mode: 'open' }); @@ -187,15 +187,14 @@ if (typeof HTMLElement === 'function') { }, d: function destroy(detaching: boolean) { if (detaching) { - detach(node) + detach(node); } - }, - $$c_e: true + } }; }; } - let $$slots: Record = {}; + const $$slots: Record = {}; const existing_slots = get_custom_elements_slots(this); for (const name of this.$$slots) { if (name in existing_slots) { @@ -251,7 +250,7 @@ if (typeof HTMLElement === 'function') { }; } -function get_custom_element_value(prop, value, props_definition: Record, transform?: 'toAttribute' | 'toProp') { +function get_custom_element_value(prop: string, value: any, props_definition: Record, transform?: 'toAttribute' | 'toProp') { value = props_definition[prop]?.type === 'Boolean' && typeof value !== 'boolean' ? value != null : value; if (!transform || !props_definition[prop]) { return value; @@ -259,7 +258,7 @@ function get_custom_element_value(prop, value, props_definition: Record, slots: string[], accessors: string[], - styles?: string, + styles?: string ) { const Class = class extends SvelteElement { constructor() { @@ -336,14 +335,15 @@ export function create_custom_element( this.$$component[prop] = value; } - if(props_definition[prop].reflect) { + if (props_definition[prop].reflect) { this.$$reflecting = true; - if (value === false || value == null) { + const attribute_value = get_custom_element_value(prop, value, props_definition, 'toAttribute'); + if (attribute_value == null) { this.removeAttribute(prop); } else { this.setAttribute( props_definition[prop].attribute || prop, - get_custom_element_value(prop, value, props_definition, 'toAttribute') as string + attribute_value as string ); } this.$$reflecting = false; @@ -365,8 +365,8 @@ export function create_custom_element( Object.defineProperty(Class.prototype, accessor, { get() { return this.$$component?.[accessor]; - }, - }) + } + }); }); return Class; diff --git a/test/custom-elements/samples/camel-case-attribute/test.js b/test/custom-elements/samples/camel-case-attribute/test.js index 6747a77d13..9b0c35d9d9 100644 --- a/test/custom-elements/samples/camel-case-attribute/test.js +++ b/test/custom-elements/samples/camel-case-attribute/test.js @@ -10,15 +10,15 @@ export default function (target) { el.setAttribute('camel-case', 'universe'); el.setAttribute('an-array', '[3,4]'); assert.equal(el.shadowRoot.innerHTML, '

Hello universe!

3

4

'); - assert.equal(target.innerHTML, '') + assert.equal(target.innerHTML, ''); el.camelCase = 'galaxy'; el.anArray = [5, 6]; assert.equal(el.shadowRoot.innerHTML, '

Hello galaxy!

5

6

'); - assert.equal(target.innerHTML, '') + assert.equal(target.innerHTML, ''); el.camelcase = 'solar system'; el.anarray = [7, 8]; assert.equal(el.shadowRoot.innerHTML, '

Hello solar system!

7

8

'); - assert.equal(target.innerHTML, '') + assert.equal(target.innerHTML, ''); } diff --git a/test/js/samples/css-shadow-dom-keyframes/expected.js b/test/js/samples/css-shadow-dom-keyframes/expected.js index ba7ca9a667..0fd49de5f7 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected.js @@ -1,7 +1,9 @@ /* generated by Svelte vX.Y.Z */ import { - SvelteElement, - attribute_to_object, + SvelteComponent, + append_styles, + attr, + create_custom_element, detach, element, init, @@ -10,6 +12,10 @@ import { safe_not_equal } from "svelte/internal"; +function add_css(target) { + append_styles(target, "svelte-66l35w", "div.svelte-66l35w{animation:svelte-66l35w-foo 1s}@keyframes svelte-66l35w-foo{0%{opacity:0}100%{opacity:1}}"); +} + function create_fragment(ctx) { let div; @@ -17,7 +23,7 @@ function create_fragment(ctx) { c() { div = element("div"); div.textContent = "fades in"; - this.c = noop; + attr(div, "class", "svelte-66l35w"); }, m(target, anchor) { insert(target, div, anchor); @@ -31,34 +37,12 @@ function create_fragment(ctx) { }; } -class Component extends SvelteElement { +class Component extends SvelteComponent { constructor(options) { super(); - const style = document.createElement('style'); - style.textContent = `div{animation:foo 1s}@keyframes foo{0%{opacity:0}100%{opacity:1}}`; - this.shadowRoot.appendChild(style); - - init( - this, - { - target: this.shadowRoot, - props: attribute_to_object(this.attributes), - customElement: true - }, - null, - create_fragment, - safe_not_equal, - {}, - null - ); - - if (options) { - if (options.target) { - insert(options.target, this, options.anchor); - } - } + init(this, options, null, create_fragment, safe_not_equal, {}, add_css); } } -customElements.define("custom-element", Component); -export default Component; +customElements.define("custom-element", create_custom_element(Component, {}, [], [])); +export default Component; \ No newline at end of file