-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the problem
As Svelte is a compiler, it would be nice to allow developper to enhance it by providing some hooks.
This will allow to add some specific requirement at compile time, and specific warning/error.
For exemple, I wrote a component MyComp and I want to handle some specific warning/error :
<MyComp format="small" data={data} /> <!-- Warning : 'data' is deprecated, use 'value' instead -->
<MyComp format="mini" /> <!-- Warning : Bad format. -->
<MyComp data={data} /> <!-- Error : format is required -->Or another FancyList component that should only accept <li> or <FancyItem> :
<FancyList>
<li>One item</li>
<FancyItem>Another Item</FancyItem>
</FancyList> <!-- OK -->
<FancyList>
One text <!-- ERROR : text is not allowed here -->
<li>One item</li>
<div> <!--ERROR : <div> is not allowed here -->
<FancyItem>Another Item</FancyItem>
<FancyList>Content</FancyList> <!-- ERROR : <FancyList> is not allowed here -->
</FancyList> <!-- OK -->I would be nice if Svelte (6 ?) allow us to add these sort of hooks into the compiler.
I know that some checks can be done via TypeScript, but this could allow more control.
Describe the proposed solution
We could use a <script context="compiler"> for that (or a specific method in context="module").
The code inside this tag will only be used at compile time by the compiler, and will not be included in the generated code (client or server).
A special variable $$tag will allow us to manipulate the compiler, in order to display messages/errors :
// MyComp.svelte
<script context="compiler">
$$tag.error("Display an error message on the entire component, and make the compilation fail");
$$tag.warn("Display an warning message on the entire component");
$$tag.info("Display an info message on the entire component");
$$tag.error("Display an error message on the prop 'data', and make the compilation fail", 'data');
$$tag.warn("Display an warning message on the prop 'data'", 'data');
$$tag.info("Display an info message on the prop 'data'", 'data');
</script>The $$tag should also contain utility methods to check the status of the props :
// MyComp.svelte
<script context="compiler" lang="ts">
// Get the name of all the props passed
const propsNames : string[] = $$tags.props();
if ($$tag.is_present('data')) {
// true if the prop 'data' is present on the component's tag
// Ex:
// <MyComp data="data" />
// <MyComp data={data} />
// <MyComp {data} />
// <MyComp data />
// <MyComp data="data" {...props}/>
// <MyComp {...props} data="data" />
}
if ($$tag.is_missing('data')) {
// true if the prop 'data' is not directly present on the component's tag
// Ex:
// <MyComp />
// <MyComp {...props} />
}
if ($$tag.has_spread()) {
// true if the component's tag has at least one spread
// Ex:
// <MyComp {...props} />
}
if ($$tag.is_spreadable('data')) {
// true if the component's tag has at least on spread,
// and the prop 'data' can be set by the spread
// Ex:
// <MyComp {...props} />
// <MyComp data="data" {...props} />
//
// But false for :
// <MyComp {...props} data="data" />
}
if ($$tag.is_constant('data')) {
// true if the prop 'data' is present and has a constant value
// Ex:
// <MyComp data="data" />
// <MyComp data={false} />
// <MyComp data={true} />
// <MyComp data={15} />
// <MyComp data={null} />
// <MyComp data={undefined} />
// <MyComp data={"data"} />
//
// But false for :
// <MyComp data={data} />
// <MyComp bind:data={data} />
// <MyComp data="text with {data}" />
}
if ($$tag.is_constant('data', ['a', 'b', 'c'])) {
// true if the prop 'data' is present and has a constant value
// which corresponds to one of the values
// Ex:
// <MyComp data="a" />
// <MyComp data="b" />
// <MyComp data="c" />
//
// But false for :
// <MyComp data="d" />
// <MyComp data={data} />
}
if ($$tag.is_bind('data')) {
// true if the prop 'data' is binded
// Ex:
// <MyComp bind:data={data} />
// <MyComp bind:data />
}
if ($$tag.is_snippet('data')) {
// true if the prop 'data' is a snippet
// Ex:
// <MyComp> {#snippet data()} XXX {/snippet} </MyComp>
}
</script>It should also include some function that make basic and classic checks :
// MyComp.svelte
<script context="compiler">
/** Show an error if the props is missng */
$$tag.require('format', 'The props "format" is required');
/** If the props is a constant, verify that his value match one of the provideds values */
$$tag.verify('format', ['full', 'medium', 'small'], "Bad format");
/** Same thing, but with a regexp : */
$$tag.verify('format', /full|medium|small/g, "Bad format");
/** Or using a callback to verify the value : */
$$tag.verify('format', (v) => {
return v === 'full' || v === 'medium' || v === 'small'
}, "Bad format");
/** Same thing, but using an Error as message */
$$tag.verify('pattern', (v) => {
return new Regexp(v, "g"); // the catched error will be used as error message
});
</script>Snippets
And a verify_snippet() to check the format of the snippets.
Some examples :
// UList.svelte
<script context="compiler">
$$tags.verify_snippet('children', {
text: false, // disallow text on the root of the snippet
html: false, // disallow {@html} on the root of the snippet
allows: [ 'li', Li ], // Accept allow only tag <li> or component <Li>
});
<script>// Dialog.svelte
<script context="compiler">
$$tags.verify_snippet('children', {
// snippet must have exactly one Header, one Content and one Footer :
match: [ Header, Content, Footer ]
});
<script>// Table.svelte
<script context="compiler">
$$tags.verify_snippet('children', {
// snippet must have :
match: [
[ THead, 0, 1], // zero or one THead
[ TBody, 1, 1], // one TBody
[ TFoot, 0, 1] // zero or one TFoot
]
});
<script>Importance
nice to have