Skip to content

Commit e769631

Browse files
committed
feat: make svelte:option customElement tag property optional (sveltejs#12751)
1 parent ce7abe4 commit e769631

File tree

13 files changed

+43
-19
lines changed

13 files changed

+43
-19
lines changed

.changeset/four-kids-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": minor
3+
---
4+
5+
feat: make customElement tag property optional (#12751)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ The `<svelte:options>` element provides a place to specify per-component compile
319319
- `accessors={true}` — adds getters and setters for the component's props
320320
- `accessors={false}` — the default
321321
- `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
322-
- `customElement="..."` — the name to use when compiling this component as a custom element
322+
- `customElement="..."` — the name or [fine grained settings](/docs/custom-elements-api#component-options) to use when compiling this component as a custom element.
323323

324324
```svelte
325325
<svelte:options customElement="my-custom-element" />

documentation/docs/04-compiler-and-api/04-custom-elements-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ The inner Svelte component is destroyed in the next tick after the `disconnected
6969

7070
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:
7171

72-
- `tag`: the mandatory `tag` property for the custom element's name
72+
- `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.
7373
- `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
7474
- `props`: an optional property to modify certain details and behaviors of your component's properties. It offers the following settings:
7575
- `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

Lines changed: 1 addition & 1 deletion
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 name or [fine grained settings](/docs/custom-elements-api#component-options) to use when compiling this component as a custom element.
2929

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@
318318
319319
## svelte_options_invalid_customelement
320320

321-
> "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: .. } } }
321+
> "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: .. } } }
322322
323323
## svelte_options_invalid_customelement_props
324324

packages/svelte/src/compiler/errors.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,12 +1281,12 @@ export function svelte_options_invalid_attribute_value(node, list) {
12811281
}
12821282

12831283
/**
1284-
* "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: .. } } }
1284+
* "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: .. } } }
12851285
* @param {null | number | NodeLike} node
12861286
* @returns {never}
12871287
*/
12881288
export function svelte_options_invalid_customelement(node) {
1289-
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: .. } } }");
1289+
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: .. } } }");
12901290
}
12911291

12921292
/**

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default function read_options(node) {
3838
}
3939
case 'customElement': {
4040
/** @type {import('#compiler').SvelteOptions['customElement']} */
41-
const ce = { tag: '' };
41+
const ce = {};
4242

4343
const { value } = attribute;
4444
if (value === true) {
@@ -76,8 +76,6 @@ export default function read_options(node) {
7676
const tag_value = tag[1]?.value;
7777
validate_tag(tag, tag_value);
7878
ce.tag = tag_value;
79-
} else {
80-
e.svelte_options_invalid_customelement(attribute);
8179
}
8280

8381
const props = properties.find(([name]) => name === 'props')?.[1];
@@ -233,8 +231,4 @@ function validate_tag(attribute, tag) {
233231
if (tag && !regex_valid_tag_name.test(tag)) {
234232
e.svelte_options_invalid_tagname(attribute);
235233
}
236-
// TODO do we still need this?
237-
// if (tag && !component.compile_options.customElement) {
238-
// component.warn(attribute, compiler_warnings.missing_custom_element_compile_options);
239-
// }
240234
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,9 @@ export function client_component(source, analysis, options) {
522522
/** @type {any} */ (typeof ce !== 'boolean' ? ce.extend : undefined)
523523
);
524524

525-
// If customElement option is set, we define the custom element directly. Else we still create
526-
// the custom element class so that the user may instantiate a custom element themselves later.
527-
if (typeof ce !== 'boolean') {
525+
// If a tag name is set, we register the custom element directly. Else we still create
526+
// the custom element class so that the user may instantiate/register a custom element themselves later.
527+
if (typeof ce !== 'boolean' && typeof ce.tag === 'string') {
528528
body.push(b.stmt(b.call('customElements.define', b.literal(ce.tag), create_ce)));
529529
} else {
530530
body.push(b.stmt(create_ce));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export interface SvelteOptions {
7676
preserveWhitespace?: boolean;
7777
namespace?: Namespace;
7878
customElement?: {
79-
tag: string;
79+
tag?: string;
8080
shadow?: 'open' | 'none';
8181
props?: Record<
8282
string,
Lines changed: 18 additions & 0 deletions
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+
});
Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,7 @@ declare module 'svelte/compiler' {
14001400
preserveWhitespace?: boolean;
14011401
namespace?: Namespace;
14021402
customElement?: {
1403-
tag: string;
1403+
tag?: string;
14041404
shadow?: 'open' | 'none';
14051405
props?: Record<
14061406
string,

0 commit comments

Comments
 (0)