Skip to content

Commit 7ae21ea

Browse files
feat: make <svelte:option> customElement configuration's tag property optional (#12751) (#12754)
* feat: make svelte:option customElement tag property optional (#12751) * tweak comment * tweak docs * tweak some more wording * Update .changeset/four-kids-flow.md --------- Co-authored-by: Rich Harris <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent 97c0150 commit 7ae21ea

File tree

13 files changed

+42
-19
lines changed

13 files changed

+42
-19
lines changed

.changeset/four-kids-flow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": minor
3+
---
4+
5+
feat: make custom element `tag` property optional

documentation/docs/02-template-syntax/09-special-elements.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ The `<svelte:options>` element provides a place to specify per-component compile
185185
- `accessors={true}` — adds getters and setters for the component's props
186186
- `accessors={false}` — the default
187187
- `namespace="..."` — the namespace where this component will be used, most commonly "svg"; use the "foreign" namespace to opt out of case-insensitive attribute names and HTML-specific warnings
188-
- `customElement="..."` — the name to use when compiling this component as a custom element
188+
- `customElement={...}` — the [options](/docs/custom-elements-api#component-options) to use when compiling this component as a custom element. If a string is passed, it is used as the `tag` option
189189

190190
```svelte
191191
<svelte:options customElement="my-custom-element" />

documentation/docs/05-misc/04-custom-elements.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ The inner Svelte component is destroyed in the next tick after the `disconnected
6363

6464
When constructing a custom element, you can tailor several aspects by defining `customElement` as an object within `<svelte:options>` since Svelte 4. This object may contain the following properties:
6565

66-
- `tag`: the mandatory `tag` property for the custom element's name
66+
- `tag: string`: an optional `tag` property for the custom element's name. If set, a custom element with this tag name will be defined with the document's `customElements` registry upon importing this component.
6767
- `shadow`: an optional property that can be set to `"none"` to forgo shadow root creation. Note that styles are then no longer encapsulated, and you can't use slots
6868
- `props`: an optional property to modify certain details and behaviors of your component's properties. It offers the following settings:
6969
- `attribute: string`: To update a custom element's prop, you have two alternatives: either set the property on the custom element's reference as illustrated above or use an HTML attribute. For the latter, the default attribute name is the lowercase property name. Modify this by assigning `attribute: "<desired name>"`.

documentation/tutorial/16-special-elements/09-svelte-options/text.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ The options that can be set here are:
2525
- `accessors={true}` — adds getters and setters for the component's props
2626
- `accessors={false}` — the default
2727
- `namespace="..."` — the namespace where this component will be used, most commonly `"svg"`
28-
- `customElement="..."` — the name to use when compiling this component as a custom element
28+
- `customElement={...}` — the [options](/docs/custom-elements-api#component-options) to use when compiling this component as a custom element. If a string is passed, it is used as the `tag` option
2929

3030
Consult the [API reference](/docs) for more information on these options.

packages/svelte/messages/compile-errors/template.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ HTML restricts where certain elements can appear. In case of a violation the bro
328328
329329
## svelte_options_invalid_customelement
330330

331-
> "customElement" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
331+
> "customElement" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
332332
333333
## svelte_options_invalid_customelement_props
334334

packages/svelte/src/compiler/errors.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1285,12 +1285,12 @@ export function svelte_options_invalid_attribute_value(node, list) {
12851285
}
12861286

12871287
/**
1288-
* "customElement" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
1288+
* "customElement" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
12891289
* @param {null | number | NodeLike} node
12901290
* @returns {never}
12911291
*/
12921292
export function svelte_options_invalid_customelement(node) {
1293-
e(node, "svelte_options_invalid_customelement", "\"customElement\" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: \"open\" | \"none\"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }");
1293+
e(node, "svelte_options_invalid_customelement", "\"customElement\" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: \"open\" | \"none\"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }");
12941294
}
12951295

12961296
/**

packages/svelte/src/compiler/phases/1-parse/read/options.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default function read_options(node) {
4040
}
4141
case 'customElement': {
4242
/** @type {SvelteOptions['customElement']} */
43-
const ce = { tag: '' };
43+
const ce = {};
4444
const { value: v } = attribute;
4545
const value = v === true || Array.isArray(v) ? v : [v];
4646

@@ -79,8 +79,6 @@ export default function read_options(node) {
7979
const tag_value = tag[1]?.value;
8080
validate_tag(tag, tag_value);
8181
ce.tag = tag_value;
82-
} else {
83-
e.svelte_options_invalid_customelement(attribute);
8482
}
8583

8684
const props = properties.find(([name]) => name === 'props')?.[1];
@@ -251,8 +249,4 @@ function validate_tag(attribute, tag) {
251249
if (tag && !regex_valid_tag_name.test(tag)) {
252250
e.svelte_options_invalid_tagname(attribute);
253251
}
254-
// TODO do we still need this?
255-
// if (tag && !component.compile_options.customElement) {
256-
// component.warn(attribute, compiler_warnings.missing_custom_element_compile_options);
257-
// }
258252
}

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,8 @@ export function client_component(analysis, options) {
611611
/** @type {any} */ (typeof ce !== 'boolean' ? ce.extend : undefined)
612612
);
613613

614-
// If customElement option is set, we define the custom element directly. Else we still create
615-
// the custom element class so that the user may instantiate a custom element themselves later.
616-
if (typeof ce !== 'boolean') {
614+
// If a tag name is provided, call `customElements.define`, otherwise leave to the user
615+
if (typeof ce !== 'boolean' && typeof ce.tag === 'string') {
617616
body.push(b.stmt(b.call('customElements.define', b.literal(ce.tag), create_ce)));
618617
} else {
619618
body.push(b.stmt(create_ce));

packages/svelte/src/compiler/types/template.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export interface SvelteOptions {
7878
namespace?: Namespace;
7979
css?: 'injected';
8080
customElement?: {
81-
tag: string;
81+
tag?: string;
8282
shadow?: 'open' | 'none';
8383
props?: Record<
8484
string,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { test } from '../../assert';
2+
const tick = () => Promise.resolve();
3+
4+
export default test({
5+
warnings: [],
6+
async test({ assert, target, componentCtor }) {
7+
customElements.define('no-tag', componentCtor.element);
8+
target.innerHTML = '<no-tag name="world"></no-tag>';
9+
await tick();
10+
11+
/** @type {any} */
12+
const el = target.querySelector('no-tag');
13+
const h1 = el.querySelector('h1');
14+
15+
assert.equal(el.shadowRoot, null);
16+
assert.equal(h1.textContent, 'Hello world!');
17+
}
18+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<svelte:options customElement={{ shadow: "none" }} />
2+
3+
<script>
4+
export let name;
5+
</script>
6+
7+
<h1>Hello {name}!</h1>

packages/svelte/tests/validator/samples/tag-non-string/errors.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"code": "svelte_options_invalid_customelement",
4-
"message": "\"customElement\" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: \"open\" | \"none\"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }",
4+
"message": "\"customElement\" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: \"open\" | \"none\"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }",
55
"start": {
66
"line": 1,
77
"column": 16

packages/svelte/types/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,7 @@ declare module 'svelte/compiler' {
15331533
namespace?: Namespace;
15341534
css?: 'injected';
15351535
customElement?: {
1536-
tag: string;
1536+
tag?: string;
15371537
shadow?: 'open' | 'none';
15381538
props?: Record<
15391539
string,

0 commit comments

Comments
 (0)