Merge branch 'master' into pr/5841

pull/5841/head
Conduitry 6 years ago
commit a6b38e72b0

@ -1,5 +1,13 @@
# Svelte changelog
## Unreleased
* Fix location of automatically declared reactive variables ([#5749](https://github.com/sveltejs/svelte/issues/5749))
* Warn when using `className` or `htmlFor` attributes ([#5777](https://github.com/sveltejs/svelte/issues/5777))
* Fix checkbox `bind:group` in keyed `{#each}` where the array can be reordered ([#5779](https://github.com/sveltejs/svelte/issues/5779))
* Fix checkbox `bind:group` in nested `{#each}` contexts ([#5811](https://github.com/sveltejs/svelte/issues/5811))
* Add graphics roles as known ARIA roles ([#5822](https://github.com/sveltejs/svelte/pull/5822))
## 3.31.0
* Use a separate `SvelteComponentTyped` interface for typed components ([#5738](https://github.com/sveltejs/svelte/pull/5738))

@ -0,0 +1,86 @@
---
title: What's new in Svelte: January 2021
description: A Svelte-packed showcase to kick-off the new year!
author: Daniel Sandoval
authorURL: https://desandoval.net
---
Happy new year from Svelte! In the last month we made progress on Sapper's upcoming release, fine-tuned our `SvelteComponent` typings, and have seen some amazing apps, sites, and libraries coming out in the showcase.
## What's changed in Svelte?
A new minor release replaces the `SvelteComponent` class with a `SvelteComponentTyped` class. This renaming should help with backwards compatibility. We've updated [last month's blog post](https://svelte.dev/blog/whats-new-in-svelte-december-2020) to avoid any confusion with the name change.
If you're using `SvelteComponent` or the new `SvelteComponentTyped` in your project or library, let us know what you're using it for and we'll add it to the showcase!
## What's going on in Sapper?
More quality of life features are landing in the upcoming release every day. `0.29.0` will include new TypeScript definitions, fixes to scroll tracking and prefetching behavior, and improvements to the runtime router to support encoded query parameters.
If you're upgrading from 0.28.x, check out [the migration guide](https://sapper.svelte.dev/migrating/#0_28_to_0_29) for steps on updating to Sapper 0.29.
## Is SvelteKit ready yet?
To avoid too much churn during development, SvelteKit is still being worked on in a private repo. There will be an announcement on the Discord, blog and Twitter when it's ready for a larger group of users and contributors.
In the meantime, you can explore the current build by running `npm init svelte@next` from your command line.
As cautioned in _[What's the deal with SvelteKit?](https://svelte.dev/blog/whats-the-deal-with-sveltekit)_, there are no docs or support available yet... So use at your own risk / for your own enjoyment!
---
## Community Showcase
**Apps & Sites**
- [manitu.me](https://manitu.me/) is a background sound / pomodoro timer for focus and relaxation
- [Answer Socrates](https://answersocrates.com/) helps you find trending questions on the internet so that you can write the most relevant blog post, tweet, or billboard
- [multris](https://multris.s1h.org/) is a multiplayer Tetris game. You can read about its development [here](https://blog.s1h.org/svelte-multiplayer-game/)
- [weather-ab](https://github.com/ganochenkodg/weather-ab) compares the archive of weather in different cities of the world. Indispensable for people thinking about migration
- [Game Nibs](https://gamenibs.com/) is a platform for gamers to find and share concise bite-sized bits of gaming advice, tips, tricks, screenshots, builds, and much more
- [Ora](https://github.com/cupcakearmy/ora) is an open source website tracking and limiting tool for Chrome and Firefox
- [vscode-dms](https://github.com/techsyndicate/vscode-dms) is a group direct messaging chat app for VSCode
- [Zero.2](https://zero.oleksandrdemian.tech/) is a math-based challenge game where you try to get to zero as quickly as possible
- [Octave Compass](https://octavecompass.com/2741) is a chord table and scale explorer for many popular musical scales
- [Infinite Walking Bass Generator 2](https://github.com/elialbert/infinitewalkingbass2) is an online music player that generates a unique walking bass line
- [ListenAddict](https://www.listenaddict.com/) is a site that notifies you whenever a person has a new talk/interview on podcast
**Demos, Libraries & Components**
- [svelte-tiny-virtual-list](https://github.com/Skayo/svelte-tiny-virtual-list) speeds up long lists by only rendering visible items
- [svelte-query](https://github.com/TanStack/svelte-query) is a collection of helpful hooks for managing, caching and syncing asynchronous and remote data
- [svelte-previous](https://github.com/bryanmylee/svelte-previous) is a svelte store to remember previous values - helpful for transitions or a quick undo stack
- [Let's Build a Confetti Cannon](https://varun.ca/confetti/) explains how to build a particle system and integrate a Canvas based animation into a larger application
- [svelte-micro](https://github.com/ayndqy/svelte-micro) is a one-component router
- [svelte-standalone-router](https://github.com/hjalmar/svelte-standalone-router) is a standalone router with an API based on [standalone-router](https://github.com/hjalmar/standalone-router)
- [svelte-datepicker](https://github.com/beyonk-adventures/svelte-datepicker) is a datepicker component with variations for time selection, date ranges and responsive themes
- [svelte-slimscroll](https://github.com/MelihAltintas/svelte-slimscroll) is a action for Svelte.js, which can transforms any div into a scrollable area with a nice scrollbar.
- [Svelte Zoomable](https://svelte.dev/repl/58dfe87756ee4db897c281b52fdef7b7?version=3.31.0) is a custom transition with a nice zoom effect
**Have a component you'd like to share?** Check out the [Components](https://sveltesociety.dev/components) page on the Svelte Society site. You can contribute by making [a PR to this file](https://github.com/svelte-society/sveltesociety.dev/blob/master/src/pages/components/components.json).
**Learning Resources**
- [Using Svelte to create a scroll video effect](https://blog.koenvangilst.nl/tutorial-svelte-scroll-video/) showcases how the `bind` command can be used to create a cool scroll video effect with very little code
- [How to make a flappybird game in svelte and typescript](https://www.youtube.com/watch?v=nhrYBoVI8pQ) is a video tutorial including docs and code for reference
- [Accessible Svelte Transition](https://www.youtube.com/watch?v=QK_QuRL7nSo&feature=youtu.be) walks through `prefers-reduced-motion` to make svelte transitions more accessible
- [Svelte's module scripts explained](https://codechips.me/svelte-module-scripts-explained/) is a great introduction to the module context, a common Sapper pattern
- [Awesome Svelte](https://github.com/TheComputerM/awesome-svelte#readme) is a curated list of Svelte resources
- [.NET Core and Svelte](https://dev.to/cainux/net-core-and-svelte-f8o) explains how to get Svelte up and running with .NET Core
- [A la découverte de Svelte JS](https://www.youtube.com/watch?v=SLpx1Y8e1ek&list=PLff5I1miao9ZEUhpqkrOx7k8RGAZt-nm9) is a svelte tutorial series in French!
- [Svelte for React Developers](https://soshace.com/svelte-for-react-developers/) explains Svelte's core concepts to folks who are used to React
- [Building a Svelte Static Website with Smooth Page Transitions](https://www.youtube.com/watch?v=dvPfmcGtmrI&feature=emb_title) shows how to build a static website with Svelte and add smooth page transitions using Three.js and GSAP.
- [Using Apollo Client in Sapper](https://bjornlu.com/blog/using-apollo-client-in-sapper/) explains the "simplest" solutions to integrate the Apollo query client into Sapper
- [Reactive web apps with Crystal + Svelte](https://www.youtube.com/watch?v=i1xjLd6z7BU) explores how to build full-stack, server-rendered Svelte apps with a [Crystal](https://crystal-lang.org) backend
**Related Projects**
- [Snowpack's v3 release candidate](https://www.snowpack.dev/posts/2020-12-03-snowpack-3-release-candidate) is out now in preparation for a January 6 release date. Check out the [Getting Started with Svelte](https://www.snowpack.dev/tutorials/svelte) for more info on how to use Snowpack.
- [Uppy](https://uppy.io/blog/2020/12/1.24/), the open source file uploader, announced Svelte support in its new version 1.24
## See you next month!
Want to add your work to the Showcase? Want to contribute to Svelte? Check out the [Svelte Society](https://sveltesociety.dev/), [Reddit](https://www.reddit.com/r/sveltejs/) and [Discord](https://discord.com/invite/yy75DKs) to get involved!

3027
site/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -23,7 +23,7 @@ const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateM
const aria_attributes = 'activedescendant atomic autocomplete busy checked colcount colindex colspan controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowcount rowindex rowspan selected setsize sort valuemax valuemin valuenow valuetext'.split(' ');
const aria_attribute_set = new Set(aria_attributes);
const aria_roles = 'alert alertdialog application article banner blockquote button caption cell checkbox code columnheader combobox complementary contentinfo definition deletion dialog directory document emphasis feed figure form generic grid gridcell group heading img link list listbox listitem log main marquee math meter menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option paragraph presentation progressbar radio radiogroup region row rowgroup rowheader scrollbar search searchbox separator slider spinbutton status strong subscript superscript switch tab table tablist tabpanel term textbox time timer toolbar tooltip tree treegrid treeitem'.split(' ');
const aria_roles = 'alert alertdialog application article banner blockquote button caption cell checkbox code columnheader combobox complementary contentinfo definition deletion dialog directory document emphasis feed figure form generic graphics-document graphics-object graphics-symbol grid gridcell group heading img link list listbox listitem log main marquee math meter menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option paragraph presentation progressbar radio radiogroup region row rowgroup rowheader scrollbar search searchbox separator slider spinbutton status strong subscript superscript switch tab table tablist tabpanel term textbox time timer toolbar tooltip tree treegrid treeitem'.split(' ');
const aria_role_set = new Set(aria_roles);
const a11y_required_attributes = {
@ -93,6 +93,11 @@ const passive_events = new Set([
'touchcancel'
]);
const react_attributes = new Map([
['className', 'class'],
['htmlFor', 'for']
]);
function get_namespace(parent: Element, element: Element, explicit_namespace: string) {
const parent_element = parent.find_nearest(/^Element/);
@ -444,6 +449,13 @@ export default class Element extends Node {
});
}
if (react_attributes.has(attribute.name)) {
component.warn(attribute, {
code: 'invalid-html-attribute',
message: `'${attribute.name}' is not a valid HTML attribute. Did you mean '${react_attributes.get(attribute.name)}'?`
});
}
attribute_map.set(attribute.name, attribute);
});
}

@ -39,6 +39,7 @@ export default class Block {
dependencies: Set<string> = new Set();
bindings: Map<string, Bindings>;
binding_group_initialised: Set<string> = new Set();
chunks: {
declarations: Array<Node | Node[]>;

@ -32,7 +32,7 @@ export default class Renderer {
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Array<Node | Node[]> = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
binding_groups: Map<string, { binding_group: (to_reference?: boolean) => Node; is_context: boolean; contexts: string[]; index: number }> = new Map();
binding_groups: Map<string, { binding_group: (to_reference?: boolean) => Node; is_context: boolean; contexts: string[]; index: number; keypath: string }> = new Map();
block: Block;
fragment: FragmentWrapper;

@ -414,6 +414,8 @@ export default function dom(
body.push(b`
function ${definition}(${args}) {
${injected.map(name => b`let ${name};`)}
${rest}
${reactive_store_declarations}
@ -440,8 +442,6 @@ export default function dom(
${inject_state && b`$$self.$inject_state = ${inject_state};`}
${injected.map(name => b`let ${name};`)}
${/* before reactive declarations */ props_inject}
${reactive_declarations.length > 0 && b`

@ -447,6 +447,8 @@ export default class EachBlockWrapper extends Wrapper {
: '@destroy_block';
if (this.dependencies.size) {
this.block.maintain_context = true;
this.updates.push(b`
${this.vars.each_block_value} = ${snippet};
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}

@ -116,11 +116,11 @@ export default class BindingWrapper {
switch (this.node.name) {
case 'group':
{
const { binding_group, is_context, contexts, index } = get_binding_group(parent.renderer, this.node, block);
const { binding_group, is_context, contexts, index, keypath } = get_binding_group(parent.renderer, this.node, block);
block.renderer.add_to_context('$$binding_groups');
if (is_context) {
if (is_context && !block.binding_group_initialised.has(keypath)) {
if (contexts.length > 1) {
let binding_group = x`${block.renderer.reference('$$binding_groups')}[${index}]`;
for (const name of contexts.slice(0, -1)) {
@ -133,6 +133,7 @@ export default class BindingWrapper {
block.chunks.init.push(
b`${binding_group(true)} = [];`
);
block.binding_group_initialised.add(keypath);
}
block.chunks.hydrate.push(
@ -257,8 +258,22 @@ function get_binding_group(renderer: Renderer, value: Binding, block: Block) {
let keypath = parts.join('.');
const contexts = [];
const contextual_dependencies = new Set<string>();
const { template_scope } = value.expression;
const add_contextual_dependency = (dep: string) => {
contextual_dependencies.add(dep);
const owner = template_scope.get_owner(dep);
if (owner.type === 'EachBlock') {
for (const dep of owner.expression.contextual_dependencies) {
add_contextual_dependency(dep);
}
}
};
for (const dep of value.expression.contextual_dependencies) {
add_contextual_dependency(dep);
}
for (const dep of contextual_dependencies) {
const context = block.bindings.get(dep);
let key;
let name;
@ -302,7 +317,8 @@ function get_binding_group(renderer: Renderer, value: Binding, block: Block) {
},
is_context: contexts.length > 0,
contexts,
index
index,
keypath
});
}

@ -851,7 +851,21 @@ export default class ElementWrapper extends Wrapper {
${outro && b`@add_transform(${this.var}, ${rect});`}
`);
const params = this.node.animation.expression ? this.node.animation.expression.manipulate(block) : x`{}`;
let params;
if (this.node.animation.expression) {
params = this.node.animation.expression.manipulate(block);
if (this.node.animation.expression.dynamic_dependencies().length) {
// if `params` is dynamic, calculate params ahead of time in the `.r()` method
const params_var = block.get_unique_name('params');
block.add_variable(params_var);
block.chunks.measure.push(b`${params_var} = ${params};`);
params = params_var;
}
} else {
params = x`{}`;
}
const name = this.renderer.reference(this.node.animation.name);

@ -17,10 +17,18 @@ export default function mark_each_block_bindings(
});
if (binding.name === 'group') {
const add_index_binding = (name: string) => {
const each_block = parent.node.scope.get_owner(name);
if (each_block.type === 'EachBlock') {
each_block.has_index_binding = true;
for (const dep of each_block.expression.contextual_dependencies) {
add_index_binding(dep);
}
}
};
// for `<input bind:group={} >`, we make sure that all the each blocks creates context with `index`
for (const name of binding.expression.contextual_dependencies) {
const each_block = parent.node.scope.get_owner(name);
(each_block as EachBlock).has_index_binding = true;
add_index_binding(name);
}
}
}

@ -101,8 +101,6 @@ export default function ssr(
${reactive_store_values}
${injected.map(name => b`let ${name};`)}
${reactive_declarations}
$$rendered = ${literal};
@ -113,13 +111,12 @@ export default function ssr(
: b`
${reactive_store_values}
${injected.map(name => b`let ${name};`)}
${reactive_declarations}
return ${literal};`;
const blocks = [
...injected.map(name => b`let ${name};`),
rest,
slots,
...reactive_stores.map(({ name }) => {

@ -98,6 +98,8 @@ let shadowedByModule;
const priv = "priv";
function instance($$self, $$props, $$invalidate) {
let computed;
let $prop,
$$unsubscribe_prop = noop,
$$subscribe_prop = () => ($$unsubscribe_prop(), $$unsubscribe_prop = subscribe(prop, $$value => $$invalidate(2, $prop = $$value)), prop);
@ -145,8 +147,6 @@ function instance($$self, $$props, $$invalidate) {
if ("computed" in $$props) computed = $$props.computed;
};
let computed;
if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}

@ -43,7 +43,8 @@ function create_each_block(key_1, ctx) {
insert(target, div, anchor);
append(div, t);
},
p(ctx, dirty) {
p(new_ctx, dirty) {
ctx = new_ctx;
if (dirty & /*things*/ 1 && t_value !== (t_value = /*thing*/ ctx[1].name + "")) set_data(t, t_value);
},
r() {

@ -39,7 +39,8 @@ function create_each_block(key_1, ctx) {
insert(target, div, anchor);
append(div, t);
},
p(ctx, dirty) {
p(new_ctx, dirty) {
ctx = new_ctx;
if (dirty & /*things*/ 1 && t_value !== (t_value = /*thing*/ ctx[1].name + "")) set_data(t, t_value);
},
d(detaching) {

@ -39,6 +39,8 @@ function create_fragment(ctx) {
}
function instance($$self, $$props, $$invalidate) {
let x;
let y;
let a = 1, b = 2, c = 3;
onMount(() => {
@ -54,9 +56,6 @@ function instance($$self, $$props, $$invalidate) {
return () => clearInterval(interval);
});
let x;
let y;
$$self.$$.update = () => {
if ($$self.$$.dirty & /*b*/ 2) {
$: $$invalidate(0, y = b * 2);

@ -18,7 +18,7 @@ export default {
`,
test({ assert, component, target, window, raf }) {
let divs = document.querySelectorAll('div');
let divs = window.document.querySelectorAll('div');
divs.forEach(div => {
div.getBoundingClientRect = function() {
const index = [...this.parentNode.children].indexOf(this);
@ -41,7 +41,7 @@ export default {
{ id: 1, name: 'a' }
];
divs = document.querySelectorAll('div');
divs = window.document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
assert.equal(divs[4].dy, -120);
@ -56,5 +56,29 @@ export default {
raf.tick(150);
assert.equal(divs[0].dy, 0);
assert.equal(divs[4].dy, 0);
component.things = [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd' },
{ id: 5, name: 'e' }
];
divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
assert.equal(divs[4].dy, -120);
raf.tick(200);
assert.equal(divs[0].dy, 108);
assert.equal(divs[4].dy, -60);
raf.tick(250);
assert.equal(divs[0].dy, 48);
assert.equal(divs[4].dy, 0);
raf.tick(300);
assert.equal(divs[0].dy, 0);
assert.equal(divs[4].dy, 0);
}
};

@ -0,0 +1,53 @@
export default {
html: `
<input type="checkbox" value="a" data-index="x-1">
<input type="checkbox" value="b" data-index="x-1">
<input type="checkbox" value="c" data-index="x-1">
<input type="checkbox" value="a" data-index="x-2">
<input type="checkbox" value="b" data-index="x-2">
<input type="checkbox" value="c" data-index="x-2">
<input type="checkbox" value="a" data-index="y-1">
<input type="checkbox" value="b" data-index="y-1">
<input type="checkbox" value="c" data-index="y-1">
<input type="checkbox" value="a" data-index="y-2">
<input type="checkbox" value="b" data-index="y-2">
<input type="checkbox" value="c" data-index="y-2">
<input type="checkbox" value="a" data-index="z-1">
<input type="checkbox" value="b" data-index="z-1">
<input type="checkbox" value="c" data-index="z-1">
<input type="checkbox" value="a" data-index="z-2">
<input type="checkbox" value="b" data-index="z-2">
<input type="checkbox" value="c" data-index="z-2">
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
const checked = new Set();
const checkInbox = async (i) => {
checked.add(i);
inputs[i].checked = true;
await inputs[i].dispatchEvent(event);
};
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
const event = new window.Event('change');
await checkInbox(2);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(12);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(8);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
}
};

@ -0,0 +1,15 @@
<script>
const list = [
{ id: 'x', data: [{ id: 1, data: [] }, { id: 2, data: [] }] },
{ id: 'y', data: [{ id: 1, data: [] }, { id: 2, data: [] }] },
{ id: 'z', data: [{ id: 1, data: [] }, { id: 2, data: [] }] }
];
</script>
{#each list as { id, data }}
{#each data as item}
<input type="checkbox" bind:group={item.data} value="a" data-index="{id}-{item.id}" />
<input type="checkbox" bind:group={item.data} value="b" data-index="{id}-{item.id}" />
<input type="checkbox" bind:group={item.data} value="c" data-index="{id}-{item.id}" />
{/each}
{/each}

@ -0,0 +1,65 @@
export default {
html: `
<label><input type="checkbox" value="Vanilla"> Vanilla</label>
<label><input type="checkbox" value="Strawberry"> Strawberry</label>
<label><input type="checkbox" value="Chocolate"> Chocolate</label>
<label><input type="checkbox" value="Lemon"> Lemon</label>
<label><input type="checkbox" value="Coconut"> Coconut</label>
`,
async test({ assert, target, window }) {
const [input1, input2, input3, input4, input5] = target.querySelectorAll('input');
const event = new window.Event('change');
input3.checked = true;
await input3.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="Chocolate"> Chocolate</label>
<label><input type="checkbox" value="Vanilla"> Vanilla</label>
<label><input type="checkbox" value="Strawberry"> Strawberry</label>
<label><input type="checkbox" value="Lemon"> Lemon</label>
<label><input type="checkbox" value="Coconut"> Coconut</label>
`);
assert.equal(input1.checked, false);
assert.equal(input2.checked, false);
assert.equal(input3.checked, true);
assert.equal(input4.checked, false);
assert.equal(input5.checked, false);
input4.checked = true;
await input4.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="Chocolate"> Chocolate</label>
<label><input type="checkbox" value="Lemon"> Lemon</label>
<label><input type="checkbox" value="Vanilla"> Vanilla</label>
<label><input type="checkbox" value="Strawberry"> Strawberry</label>
<label><input type="checkbox" value="Coconut"> Coconut</label>
`);
assert.equal(input1.checked, false);
assert.equal(input2.checked, false);
assert.equal(input3.checked, true);
assert.equal(input4.checked, true);
assert.equal(input5.checked, false);
input3.checked = false;
await input3.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<label><input type="checkbox" value="Lemon"> Lemon</label>
<label><input type="checkbox" value="Chocolate"> Chocolate</label>
<label><input type="checkbox" value="Vanilla"> Vanilla</label>
<label><input type="checkbox" value="Strawberry"> Strawberry</label>
<label><input type="checkbox" value="Coconut"> Coconut</label>
`);
assert.equal(input1.checked, false);
assert.equal(input2.checked, false);
assert.equal(input3.checked, false);
assert.equal(input4.checked, true);
assert.equal(input5.checked, false);
}
};

@ -0,0 +1,21 @@
<script>
let flavours = [
'Vanilla',
'Strawberry',
'Chocolate',
'Lemon',
'Coconut'
];
let choices = [];
// Put choices first by sorting
$: flavours = flavours.sort((a, b) => choices.includes(b) - choices.includes(a));
</script>
{#each flavours as flavour (flavour)}
<label>
<input type=checkbox bind:group={choices} value={flavour}>
{flavour}
</label>
{/each}

@ -0,0 +1,3 @@
export default {
html: '<p>aca</p>'
};

@ -0,0 +1,12 @@
<script>
export let a = 'a';
let b;
$: c = a;
function foo() {
b = c === 'a' ? 'b' : 'c';
}
foo();
</script>
<p>{a}{b}{c}</p>

@ -0,0 +1,2 @@
<div className="abc"></div>
<label htmlFor="i"><input /></label>

@ -0,0 +1,32 @@
[
{
"code": "invalid-html-attribute",
"message": "'className' is not a valid HTML attribute. Did you mean 'class'?",
"pos": 5,
"start": {
"character": 5,
"column": 5,
"line": 1
},
"end": {
"character": 20,
"column": 20,
"line": 1
}
},
{
"code": "invalid-html-attribute",
"message": "'htmlFor' is not a valid HTML attribute. Did you mean 'for'?",
"pos": 35,
"start": {
"character": 35,
"column": 7,
"line": 2
},
"end": {
"character": 46,
"column": 18,
"line": 2
}
}
]
Loading…
Cancel
Save