Skip to content

Commit

Permalink
implement shadowdom option
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm committed Apr 12, 2023
1 parent 96e9768 commit 840a7be
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 10 deletions.
12 changes: 12 additions & 0 deletions src/compiler/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface ComponentOptions {
accessors?: boolean;
preserveWhitespace?: boolean;
ceProps?: Record<string, { reflect?: boolean; type?: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object', attribute?: string }>;
shadowdom?: 'open' | 'none';
}

const regex_leading_directory_separator = /^[/\\]/;
Expand Down Expand Up @@ -1574,6 +1575,17 @@ function process_component_options(component: Component, nodes) {
break;
}

case 'shadowdom': {
const shadowdom = get_value(attribute, compiler_errors.invalid_shadowdom_attribute);

if (shadowdom !== 'open' && shadowdom !== 'none') {
return component.error(attribute, compiler_errors.invalid_shadowdom_attribute);
}

component_options.shadowdom = shadowdom;
break;
}

case 'ceProps': {
const error = () => component.error(attribute, compiler_errors.invalid_ceProps_attribute);
const { value } = attribute;
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/compile/compiler_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ export default {
code: 'invalid-tag-attribute',
message: "'tag' must be a string literal"
},
invalid_shadowdom_attribute: {
code: 'invalid-shadowdom-attribute',
message: "'shadowdom' must be either 'open' or 'none'"
},
invalid_ceProps_attribute: {
code: 'invalid-ceProps-attribute',
message: "'ceProps' must be a statically analyzable object literal of the form " +
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/compile/render_dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,10 @@ export default function dom(
.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.shadowdom !== 'none' ? 'true' : 'false';

body.push(
b`@_customElements.define("${component.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}]));`
b`@_customElements.define("${component.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}], ${use_shadow_dom}));`
);
}

Expand Down
15 changes: 10 additions & 5 deletions src/runtime/internal/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,13 @@ if (typeof HTMLElement === 'function') {

constructor(
private $$componentCtor: ComponentType,
private $$slots: string[]
private $$slots: string[],
use_shadow_dom: boolean
) {
super();
this.attachShadow({ mode: 'open' });
if (use_shadow_dom) {
this.attachShadow({ mode: 'open' });
}
}

addEventListener(type: string, listener: any, options?: any): void {
Expand Down Expand Up @@ -237,7 +240,7 @@ if (typeof HTMLElement === 'function') {
}

this.$$component = new this.$$componentCtor({
target: this.shadowRoot!,
target: this.shadowRoot || this,
props: {
...this.$$data,
$$slots,
Expand Down Expand Up @@ -330,17 +333,19 @@ interface CustomElementPropDefinition {
* @param props_definition The props to observe
* @param slots The slots to create
* @param accessors Other accessors besides the ones for props the component has
* @param use_shadow_dom Whether to use shadow DOM
* @returns A custom element class
*/
export function create_custom_element(
Component: ComponentType,
props_definition: Record<string, CustomElementPropDefinition>,
slots: string[],
accessors: string[]
accessors: string[],
use_shadow_dom: boolean
) {
const Class = class extends SvelteElement {
constructor() {
super(Component, slots);
super(Component, slots, use_shadow_dom);
this.$$props_definition = props_definition;
}

Expand Down
13 changes: 13 additions & 0 deletions test/custom-elements/samples/no-shadow-dom/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<svelte:options tag="custom-element" shadowdom="none" />

<script>
export let name;
</script>

<h1>Hello {name}!</h1>

<style>
h1 {
color: red;
}
</style>
16 changes: 16 additions & 0 deletions test/custom-elements/samples/no-shadow-dom/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as assert from 'assert';
import { tick } from 'svelte';
import './main.svelte';

export default async function (target) {
target.innerHTML = '<custom-element name="world"></custom-element>';
await tick();

const el = target.querySelector('custom-element');
const h1 = el.querySelector('h1');

assert.equal(el.name, 'world');
assert.equal(el.shadowRoot, null);
assert.equal(h1.innerHTML, 'Hello world!');
assert.equal(getComputedStyle(h1).color, 'rgb(255, 0, 0)');
}
2 changes: 1 addition & 1 deletion test/custom-elements/samples/no-svelte-options/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';

export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, {name: {}}, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, {name: {}}, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

Expand Down
2 changes: 1 addition & 1 deletion test/custom-elements/samples/no-tag-warning/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';

export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, { name: {}}, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, { name: {}}, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

Expand Down
2 changes: 1 addition & 1 deletion test/custom-elements/samples/no-tag/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';

export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, { name: {} }, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, { name: {} }, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

Expand Down
2 changes: 1 addition & 1 deletion test/js/samples/css-shadow-dom-keyframes/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ class Component extends SvelteComponent {
}
}

customElements.define("custom-element", create_custom_element(Component, {}, [], []));
customElements.define("custom-element", create_custom_element(Component, {}, [], [], true));
export default Component;

0 comments on commit 840a7be

Please sign in to comment.