Merge branch 'upstream-master' into introducing-runes

introducing-runes
Simon Holthausen 1 year ago
commit 5afc2dffc0

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: css sourcemap generation with unicode filenames

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: head duplication when binding is present

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: take custom attribute name into account when reflecting property

@ -106,11 +106,13 @@ An element or component can have multiple spread attributes, interspersed with r
## Text expressions
A JavaScript expression can be included as text by surrounding it with curly braces.
```svelte
{expression}
```
Text can also contain JavaScript expressions:
Curly braces can be included in a Svelte template by using their [HTML entity](https://developer.mozilla.org/docs/Glossary/Entity) strings: `{`, `{`, or `{` for `{` and `}`, `}`, or `}` for `}`.
> If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses.

@ -5,14 +5,17 @@ title: Logic blocks
## {#if ...}
```svelte
<!--- copy: false --->
{#if expression}...{/if}
```
```svelte
<!--- copy: false --->
{#if expression}...{:else if expression}...{/if}
```
```svelte
<!--- copy: false --->
{#if expression}...{:else}...{/if}
```
@ -41,22 +44,27 @@ Additional conditions can be added with `{:else if expression}`, optionally endi
## {#each ...}
```svelte
<!--- copy: false --->
{#each expression as name}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name, index (key)}...{/each}
```
```svelte
<!--- copy: false --->
{#each expression as name}...{:else}...{/each}
```
@ -125,18 +133,22 @@ Since Svelte 4 it is possible to iterate over iterables like `Map` or `Set`. Ite
## {#await ...}
```svelte
<!--- copy: false --->
{#await expression}...{:then name}...{:catch name}...{/await}
```
```svelte
<!--- copy: false --->
{#await expression}...{:then name}...{/await}
```
```svelte
<!--- copy: false --->
{#await expression then name}...{/await}
```
```svelte
<!--- copy: false --->
{#await expression catch name}...{/await}
```
@ -186,6 +198,7 @@ Similarly, if you only want to show the error state, you can omit the `then` blo
## {#key ...}
```svelte
<!--- copy: false --->
{#key expression}...{/key}
```

@ -5,6 +5,7 @@ title: Special tags
## {@html ...}
```svelte
<!--- copy: false --->
{@html expression}
```
@ -24,10 +25,12 @@ The expression should be valid standalone HTML — `{@html "<div>"}content{@html
## {@debug ...}
```svelte
<!--- copy: false --->
{@debug}
```
```svelte
<!--- copy: false --->
{@debug var1, var2, ..., varN}
```
@ -65,6 +68,7 @@ The `{@debug}` tag without any arguments will insert a `debugger` statement that
## {@const ...}
```svelte
<!--- copy: false --->
{@const assignment}
```

@ -7,10 +7,12 @@ As well as attributes, elements can have _directives_, which control the element
## on:_eventname_
```svelte
<!--- copy: false --->
on:eventname={handler}
```
```svelte
<!--- copy: false --->
on:eventname|modifiers={handler}
```
@ -72,7 +74,6 @@ If the `on:` directive is used without a value, the component will _forward_ the
It's possible to have multiple event listeners for the same event:
```svelte
<!--- file: App.svelte --->
<script>
let counter = 0;
function increment() {
@ -91,6 +92,7 @@ It's possible to have multiple event listeners for the same event:
## bind:_property_
```svelte
<!--- copy: false --->
bind:property={variable}
```
@ -186,6 +188,8 @@ Elements with the `contenteditable` attribute support the following bindings:
There are slight differences between each of these, read more about them [here](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText).
<!-- for some reason puts the comment and html on same line -->
<!-- prettier-ignore -->
```svelte
<div contenteditable="true" bind:innerHTML={html} />
```
@ -273,13 +277,13 @@ Block-level elements have 4 read-only bindings, measured using a technique simil
## bind:group
```svelte
<!--- copy: false --->
bind:group={variable}
```
Inputs that work together can use `bind:group`.
```svelte
<!--- file: App.svelte --->
<script>
let tortilla = 'Plain';
@ -304,13 +308,13 @@ Inputs that work together can use `bind:group`.
## bind:this
```svelte
<!--- copy: false --->
bind:this={dom_node}
```
To get a reference to a DOM node, use `bind:this`.
```svelte
<!--- file: App.svelte --->
<script>
import { onMount } from 'svelte';
@ -329,10 +333,12 @@ To get a reference to a DOM node, use `bind:this`.
## class:_name_
```svelte
<!--- copy: false --->
class:name={value}
```
```svelte
<!--- copy: false --->
class:name
```
@ -393,14 +399,17 @@ When `style:` directives are combined with `style` attributes, the directives wi
## use:_action_
```svelte
<!--- copy: false --->
use:action
```
```svelte
<!--- copy: false --->
use:action={parameters}
```
```ts
/// copy: false
// @noErrors
action = (node: HTMLElement, parameters: any) => {
update?: (parameters: any) => void,
@ -411,7 +420,6 @@ action = (node: HTMLElement, parameters: any) => {
Actions are functions that are called when an element is created. They can return an object with a `destroy` method that is called after the element is unmounted:
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/action').Action} */
function foo(node) {
@ -433,7 +441,6 @@ An action can have a parameter. If the returned value has an `update` method, it
> Don't worry about the fact that we're redeclaring the `foo` function for every component instance — Svelte will hoist any functions that don't depend on local state out of the component definition.
```svelte
<!--- file: App.svelte --->
<script>
export let bar;
@ -461,30 +468,37 @@ Read more in the [`svelte/action`](/docs/svelte-action) page.
## transition:_fn_
```svelte
<!--- copy: false --->
transition:fn
```
```svelte
<!--- copy: false --->
transition:fn={params}
```
```svelte
<!--- copy: false --->
transition:fn|global
```
```svelte
<!--- copy: false --->
transition:fn|global={params}
```
```svelte
<!--- copy: false --->
transition:fn|local
```
```svelte
<!--- copy: false --->
transition:fn|local={params}
```
```js
/// copy: false
// @noErrors
transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
delay?: number,
@ -544,7 +558,6 @@ The `t` argument passed to `css` is a value between `0` and `1` after the `easin
The function is called repeatedly _before_ the transition begins, with different `t` and `u` arguments.
```svelte
<!--- file: App.svelte --->
<script>
import { elasticOut } from 'svelte/easing';
@ -644,50 +657,62 @@ An element with transitions will dispatch the following events in addition to an
## in:_fn_/out:_fn_
```svelte
<!--- copy: false --->
in:fn
```
```svelte
<!--- copy: false --->
in:fn={params}
```
```svelte
<!--- copy: false --->
in:fn|global
```
```svelte
<!--- copy: false --->
in:fn|global={params}
```
```svelte
<!--- copy: false --->
in:fn|local
```
```svelte
<!--- copy: false --->
in:fn|local={params}
```
```svelte
<!--- copy: false --->
out:fn
```
```svelte
<!--- copy: false --->
out:fn={params}
```
```svelte
<!--- copy: false --->
out:fn|global
```
```svelte
<!--- copy: false --->
out:fn|global={params}
```
```svelte
<!--- copy: false --->
out:fn|local
```
```svelte
<!--- copy: false --->
out:fn|local={params}
```
@ -704,14 +729,17 @@ Unlike with `transition:`, transitions applied with `in:` and `out:` are not bid
## animate:_fn_
```svelte
<!--- copy: false --->
animate:name
```
```svelte
<!--- copy: false --->
animate:name={params}
```
```js
/// copy: false
// @noErrors
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
delay?: number,
@ -723,6 +751,7 @@ animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) =>
```
```ts
/// copy: false
// @noErrors
DOMRect {
bottom: number,
@ -772,7 +801,6 @@ The function is called repeatedly _before_ the animation begins, with different
<!-- TODO: Types -->
```svelte
<!--- file: App.svelte --->
<script>
import { cubicOut } from 'svelte/easing';
@ -806,7 +834,6 @@ A custom animation function can also return a `tick` function, which is called _
> If it's possible to use `css` instead of `tick`, do so — CSS animations can run off the main thread, preventing jank on slower devices.
```svelte
<!--- file: App.svelte --->
<script>
import { cubicOut } from 'svelte/easing';

@ -5,28 +5,24 @@ title: Component directives
## on:_eventname_
```svelte
<!--- copy: false --->
on:eventname={handler}
```
Components can emit events using [`createEventDispatcher`](/docs/svelte#createeventdispatcher) or by forwarding DOM events.
```svelte
<!-- SomeComponent.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher();
</script>
<!-- programmatic dispatching -->
<button on:click={() => dispatch('hello')}>
one
</button>
<button on:click={() => dispatch('hello')}> one </button>
<!-- declarative event forwarding -->
<button on:click>
two
</button>
<button on:click> two </button>
```
Listening for component events looks the same as listening for DOM events:
@ -44,6 +40,7 @@ As with DOM events, if the `on:` directive is used without a value, the event wi
## --style-props
```svelte
<!--- copy: false --->
--style-props="anycssvalue"
```
@ -78,7 +75,6 @@ For SVG namespace, the example above desugars into using `<g>` instead:
Svelte's CSS Variables support allows for easily themeable components:
```svelte
<!-- Slider.svelte -->
<style>
.potato-slider-rail {
background-color: var(--rail-color, var(--theme-color, 'purple'));
@ -118,6 +114,7 @@ While Svelte props are reactive without binding, that reactivity only flows down
## bind:this
```svelte
<!--- copy: false --->
bind:this={component_instance}
```

@ -198,7 +198,6 @@ If `this` is the name of a [void element](https://developer.mozilla.org/en-US/do
<script>
let tag = 'div';
/** @type {(e: MouseEvent) => void} */
export let handler;
</script>

@ -61,9 +61,7 @@ Note that the value of a `writable` is lost when it is destroyed, for example wh
Creates a store whose value cannot be set from 'outside', the first argument is the store's initial value, and the second argument to `readable` is the same as the second argument to `writable`.
```js
<!--- file: App.svelte --->
// ---cut---
```ts
import { readable } from 'svelte/store';
const time = readable(new Date(), (set) => {
@ -114,7 +112,7 @@ The callback can set a value asynchronously by accepting a second argument, `set
In this case, you can also pass a third argument to `derived` — the initial value of the derived store before `set` or `update` is first called. If no initial value is specified, the store's initial value will be `undefined`.
```js
```ts
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
@ -129,13 +127,17 @@ export {};
// ---cut---
import { derived } from 'svelte/store';
const delayed = derived(a, ($a, set) => {
setTimeout(() => set($a), 1000);
}, 2000);
const delayed = derived(
a,
($a, set) => {
setTimeout(() => set($a), 1000);
},
2000
);
const delayedIncrement = derived(a, ($a, set, update) => {
set($a);
setTimeout(() => update(x => x + 1), 1000);
setTimeout(() => update((x) => x + 1), 1000);
// every time $a produces a value, this produces two
// values, $a immediately and then $a + 1 a second later
});
@ -143,7 +145,7 @@ const delayedIncrement = derived(a, ($a, set, update) => {
If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes.
```js
```ts
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
@ -224,7 +226,7 @@ Generally, you should read the value of a store by subscribing to it and using t
> This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
```js
```ts
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';

@ -9,14 +9,17 @@ The `svelte/transition` module exports seven functions: `fade`, `blur`, `fly`, `
> EXPORT_SNIPPET: svelte/transition#fade
```svelte
<!--- copy: false --->
transition:fade={params}
```
```svelte
<!--- copy: false --->
in:fade={params}
```
```svelte
<!--- copy: false --->
out:fade={params}
```
@ -45,14 +48,17 @@ You can see the `fade` transition in action in the [transition tutorial](https:/
> EXPORT_SNIPPET: svelte/transition#blur
```svelte
<!--- copy: false --->
transition:blur={params}
```
```svelte
<!--- copy: false --->
in:blur={params}
```
```svelte
<!--- copy: false --->
out:blur={params}
```
@ -81,14 +87,17 @@ Animates a `blur` filter alongside an element's opacity.
> EXPORT_SNIPPET: svelte/transition#fly
```svelte
<!--- copy: false --->
transition:fly={params}
```
```svelte
<!--- copy: false --->
in:fly={params}
```
```svelte
<!--- copy: false --->
out:fly={params}
```
@ -126,14 +135,17 @@ You can see the `fly` transition in action in the [transition tutorial](https://
> EXPORT_SNIPPET: svelte/transition#slide
```svelte
<!--- copy: false --->
transition:slide={params}
```
```svelte
<!--- copy: false --->
in:slide={params}
```
```svelte
<!--- copy: false --->
out:slide={params}
```
@ -165,14 +177,17 @@ Slides an element in and out.
> EXPORT_SNIPPET: svelte/transition#scale
```svelte
<!--- copy: false --->
transition:scale={params}
```
```svelte
<!--- copy: false --->
in:scale={params}
```
```svelte
<!--- copy: false --->
out:scale={params}
```
@ -204,14 +219,17 @@ Animates the opacity and scale of an element. `in` transitions animate from an e
> EXPORT_SNIPPET: svelte/transition#draw
```svelte
<!--- copy: false --->
transition:draw={params}
```
```svelte
<!--- copy: false --->
in:draw={params}
```
```svelte
<!--- copy: false --->
out:draw={params}
```

@ -9,6 +9,7 @@ The `svelte/animate` module exports one function for use with Svelte [animations
> EXPORT_SNIPPET: svelte/animate#flip
```svelte
<!--- copy: false --->
animate:flip={params}
```

@ -5,7 +5,6 @@ title: svelte/action
Actions are functions that are called when an element is created. They can return an object with a `destroy` method that is called after the element is unmounted:
```svelte
<!--- file: App.svelte --->
<script>
/** @type {import('svelte/action').Action} */
function foo(node) {
@ -27,7 +26,6 @@ An action can have a parameter. If the returned value has an `update` method, it
> Don't worry that we're redeclaring the `foo` function for every component instance — Svelte will hoist any functions that don't depend on local state out of the component definition.
```svelte
<!--- file: App.svelte --->
<script>
/** @type {string} */
export let bar;
@ -56,7 +54,6 @@ An action can have a parameter. If the returned value has an `update` method, it
Sometimes actions emit custom events and apply custom attributes to the element they are applied to. To support this, actions typed with `Action` or `ActionReturn` type can have a last parameter, `Attributes`:
```svelte
<!--- file: App.svelte --->
<script>
/**
* @type {import('svelte/action').Action<HTMLDivElement, { prop: any }, { 'on:emit': (e: CustomEvent<string>) => void }>}

@ -90,7 +90,7 @@ Each `markup`, `script` or `style` function must return an object (or a Promise
> Preprocessor functions should return a `map` object whenever possible or else debugging becomes harder as stack traces can't link to the original code correctly.
```js
```ts
// @filename: ambient.d.ts
declare global {
var source: string;
@ -128,6 +128,7 @@ const { code } = await preprocess(
If a `dependencies` array is returned, it will be included in the result object. This is used by packages like [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte) and [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) to watch additional files for changes, in the case where your `<style>` tag has an `@import` (for example).
```ts
/// file: preprocess-sass.js
// @filename: ambient.d.ts
declare global {
var source: string;
@ -204,6 +205,7 @@ Multiple preprocessors can be used together. The output of the first becomes the
> In Svelte 3, all `markup` functions ran first, then all `script` and then all `style` preprocessors. This order was changed in Svelte 4.
```js
/// file: multiple-preprocessor.js
// @errors: 2322
// @filename: ambient.d.ts
declare global {
@ -255,6 +257,7 @@ The `walk` function provides a way to walk the abstract syntax trees generated b
The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node.
```js
/// file: compiler-walk.js
// @filename: ambient.d.ts
declare global {
var ast: import('estree').Node;

@ -67,6 +67,7 @@ Whereas children of `target` are normally left alone, `hydrate: true` will cause
The existing DOM doesn't need to match the component — Svelte will 'repair' the DOM as it goes.
```ts
/// file: index.js
// @filename: ambient.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
@ -150,6 +151,7 @@ Causes the `callback` function to be called whenever the component dispatches an
A function is returned that will remove the event listener when called.
```ts
/// file: index.js
// @filename: ambient.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
@ -231,6 +233,7 @@ If a component is compiled with `accessors: true`, each instance will have gette
By default, `accessors` is `false`, unless you're compiling as a custom element.
```js
/// file: index.js
// @filename: ambient.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';

@ -59,42 +59,13 @@ It will show up on hover.
Note: The `@component` is necessary in the HTML comment which describes your component.
## What about TypeScript support?
You need to install a preprocessor such as [svelte-preprocess](https://github.com/sveltejs/svelte-preprocess). You can run type checking from the command line with [svelte-check](https://www.npmjs.com/package/svelte-check).
To declare the type of a reactive variable in a Svelte template, you should use the following syntax:
```ts
const count: number = 100;
// ---cut---
let x: number;
$: x = count + 1;
```
To import a type or interface make sure to use [TypeScript's `type` modifier](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export):
```ts
// @filename: SomeFile.ts
export interface SomeInterface {
foo: string;
}
// @filename: index.ts
// ---cut---
import type { SomeInterface } from './SomeFile';
```
You must use the `type` modifier because `svelte-preprocess` doesn't know whether an import is a type or a value — it only transpiles one file at a time without knowledge of the other files and therefore can't safely erase imports which only contain types without this modifier present.
## Does Svelte scale?
There will be a blog post about this eventually, but in the meantime, check out [this issue](https://github.com/sveltejs/svelte/issues/2546).
## Is there a UI component library?
There are several UI component libraries as well as standalone components. Find them under the [components section](https://sveltesociety.dev/components#design-systems) of the Svelte Society website.
There are several UI component libraries as well as standalone components. Find them under the [design systems section of the components page](https://sveltesociety.dev/components#design-systems) on the Svelte Society website.
## How do I test Svelte apps?
@ -131,6 +102,17 @@ If you need hash-based routing on the client side, check out [svelte-spa-router]
You can see a [community-maintained list of routers on sveltesociety.dev](https://sveltesociety.dev/components#routers).
## Can I tell Svelte not to remove my unused styles?
No. Svelte removes the styles from the component and warns you about them in order to prevent issues that would otherwise arise.
Svelte's component style scoping works by generating a class unique to the given component, adding it to the relevant elements in the component that are under Svelte's control, and then adding it to each of the selectors in that component's styles. When the compiler can't see what elements a style selector applies to, there would be two bad options for keeping it:
- If it keeps the selector and adds the scoping class to it, the selector will likely not match the expected elements in the component, and they definitely won't if they were created by a child component or `{@html ...}`.
- If it keeps the selector without adding the scoping class to it, the given style will become a global style, affecting your entire page.
If you need to style something that Svelte can't identify at compile time, you will need to explicitly opt into global styles by using `:global(...)`. But also keep in mind that you can wrap `:global(...)` around only part of a selector. `.foo :global(.bar) { ... }` will style any `.bar` elements that appear within the component's `.foo` elements. As long as there's some parent element in the current component to start from, partially global selectors like this will almost always be able to get you what you want.
## Is Svelte v2 still available?
New features aren't being added to it, and bugs will probably only be fixed if they are extremely nasty or present some sort of security vulnerability.

@ -144,17 +144,17 @@ Since Svelte version 4.2 / `svelte-check` version 3.5 / VS Code extension versio
```ts
/// file: additional-svelte-typings.d.ts
import { HTMLButtonAttributes } from 'svelte/elements'
import { HTMLButtonAttributes } from 'svelte/elements';
declare module 'svelte/elements' {
export interface SvelteHTMLElements {
'custom-button': HTMLButtonAttributes;
}
export interface SvelteHTMLElements {
'custom-button': HTMLButtonAttributes;
}
// allows for more granular control over what element to add the typings to
export interface HTMLButtonAttributes {
'veryexperimentalattribute'?: string;
}
export interface HTMLButtonAttributes {
veryexperimentalattribute?: string;
}
}
export {}; // ensure this is not an ambient module, else types will be overridden instead of augmented

@ -1,5 +1,27 @@
# svelte
## 4.2.1
### Patch Changes
- fix: update style directive when style attribute is present and is updated via an object prop ([#9187](https://github.com/sveltejs/svelte/pull/9187))
- fix: css sourcemap generation with unicode filenames ([#9120](https://github.com/sveltejs/svelte/pull/9120))
- fix: do not add module declared variables as dependencies ([#9122](https://github.com/sveltejs/svelte/pull/9122))
- fix: handle `svelte:element` with dynamic this and spread attributes ([#9112](https://github.com/sveltejs/svelte/pull/9112))
- fix: silence false positive reactive component warning ([#9094](https://github.com/sveltejs/svelte/pull/9094))
- fix: head duplication when binding is present ([#9124](https://github.com/sveltejs/svelte/pull/9124))
- fix: take custom attribute name into account when reflecting property ([#9140](https://github.com/sveltejs/svelte/pull/9140))
- fix: add `indeterminate` to the list of HTMLAttributes ([#9180](https://github.com/sveltejs/svelte/pull/9180))
- fix: recognize option value on spread attribute ([#9125](https://github.com/sveltejs/svelte/pull/9125))
## 4.2.0
### Minor Changes

@ -808,6 +808,7 @@ export interface HTMLInputAttributes extends HTMLAttributes<HTMLInputElement> {
formnovalidate?: boolean | undefined | null;
formtarget?: string | undefined | null;
height?: number | string | undefined | null;
indeterminate?: boolean | undefined | null;
list?: string | undefined | null;
max?: number | string | undefined | null;
maxlength?: number | undefined | null;

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "4.2.0",
"version": "4.2.1",
"description": "Cybernetically enhanced web apps",
"type": "module",
"module": "src/runtime/index.js",
@ -83,7 +83,7 @@
"posttest": "agadoo src/internal/index.js",
"prepublishOnly": "pnpm build",
"types": "node ./scripts/generate-dts.js",
"lint": "prettier . --cache --plugin-search-dir=. --check && eslint \"{src,test}/**/*.{ts,js}\" --cache"
"lint": "prettier . --cache --plugin-search-dir=. --check && eslint \"{scripts,src,test}/**/*.js\" --cache --fix"
},
"repository": {
"type": "git",
@ -129,6 +129,7 @@
"agadoo": "^3.0.0",
"dts-buddy": "^0.1.7",
"esbuild": "^0.18.11",
"eslint-plugin-lube": "^0.1.7",
"happy-dom": "^9.20.3",
"jsdom": "^21.1.2",
"kleur": "^4.1.5",

@ -0,0 +1,6 @@
{
"plugins": ["lube"],
"rules": {
"lube/svelte-naming-convention": ["error", { "fixSameNames": true }]
}
}

@ -1,8 +1,8 @@
// Compile all Svelte files in a directory to JS and CSS files
// Usage: node scripts/compile-test.js <directory>
import { mkdirSync, readFileSync, writeFileSync } from 'fs';
import path from 'path';
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import glob from 'tiny-glob/sync.js';
import { compile } from '../src/compiler/index.js';

@ -1,18 +1,18 @@
import * as fs from 'fs';
import * as fs from 'node:fs';
import { createBundle } from 'dts-buddy';
// It may look weird, but the imports MUST be ending with index.js to be properly resolved in all TS modes
for (const name of ['action', 'animate', 'easing', 'motion', 'store', 'transition']) {
fs.writeFileSync(`${name}.d.ts`, `import './types/index.js';`);
fs.writeFileSync(`${name}.d.ts`, "import './types/index.js';");
}
fs.writeFileSync('index.d.ts', `import './types/index.js';`);
fs.writeFileSync('compiler.d.ts', `import './types/index.js';`);
fs.writeFileSync('index.d.ts', "import './types/index.js';");
fs.writeFileSync('compiler.d.ts', "import './types/index.js';");
// TODO: some way to mark these as deprecated
fs.mkdirSync('./types/compiler', { recursive: true });
fs.writeFileSync('./types/compiler/preprocess.d.ts', `import '../index.js';`);
fs.writeFileSync('./types/compiler/interfaces.d.ts', `import '../index.js';`);
fs.writeFileSync('./types/compiler/preprocess.d.ts', "import '../index.js';");
fs.writeFileSync('./types/compiler/interfaces.d.ts', "import '../index.js';");
await createBundle({
output: 'types/index.d.ts',

@ -6,8 +6,8 @@ Please run `node scripts/globals-extractor.js` at the project root.
see: https://github.com/microsoft/TypeScript/tree/main/lib
---------------------------------------------------------------------- */
import http from 'https';
import fs from 'fs';
import http from 'node:https';
import fs from 'node:fs';
const GLOBAL_TS_PATH = './src/compiler/utils/globals.js';

@ -0,0 +1,6 @@
{
"plugins": ["lube"],
"rules": {
"lube/svelte-naming-convention": ["error", { "fixSameNames": true }]
}
}

@ -1555,8 +1555,8 @@ export default class Component {
}, [])
);
if (cycle && cycle.length) {
const declarationList = lookup.get(cycle[0]);
const declaration = declarationList[0];
const declaration_list = lookup.get(cycle[0]);
const declaration = declaration_list[0];
return this.error(declaration.node, compiler_errors.cyclical_reactive_declaration(cycle));
}

@ -4,7 +4,7 @@ import { b } from 'code-red';
* @param {any} program
* @param {import('estree').Identifier} name
* @param {string} banner
* @param {any} sveltePath
* @param {any} svelte_path
* @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
* @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
* @param {import('estree').ImportDeclaration[]} imports
@ -15,21 +15,21 @@ export default function create_module(
program,
name,
banner,
sveltePath = 'svelte',
svelte_path = 'svelte',
helpers,
globals,
imports,
module_exports,
exports_from
) {
const internal_path = `${sveltePath}/internal`;
const internal_path = `${svelte_path}/internal`;
helpers.sort((a, b) => (a.name < b.name ? -1 : 1));
globals.sort((a, b) => (a.name < b.name ? -1 : 1));
return esm(
program,
name,
banner,
sveltePath,
svelte_path,
internal_path,
helpers,
globals,
@ -41,11 +41,11 @@ export default function create_module(
/**
* @param {any} source
* @param {any} sveltePath
* @param {any} svelte_path
*/
function edit_source(source, sveltePath) {
function edit_source(source, svelte_path) {
return source === 'svelte' || source.startsWith('svelte/')
? source.replace('svelte', sveltePath)
? source.replace('svelte', svelte_path)
: source;
}
@ -84,7 +84,7 @@ function get_internal_globals(globals, helpers) {
* @param {any} program
* @param {import('estree').Identifier} name
* @param {string} banner
* @param {string} sveltePath
* @param {string} svelte_path
* @param {string} internal_path
* @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
* @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
@ -96,7 +96,7 @@ function esm(
program,
name,
banner,
sveltePath,
svelte_path,
internal_path,
helpers,
globals,
@ -118,7 +118,7 @@ function esm(
/** @param {any} node */
function rewrite_import(node) {
const value = edit_source(node.source.value, sveltePath);
const value = edit_source(node.source.value, svelte_path);
if (node.source.value !== value) {
node.source.value = value;
node.source.raw = null;

@ -98,7 +98,7 @@ export default class Binding extends Node {
this.is_readonly =
regex_dimensions.test(this.name) ||
regex_box_size.test(this.name) ||
(isElement(parent) &&
(is_element(parent) &&
((parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
(parent.name === 'input' && type === 'file'))) /* TODO others? */;
}
@ -127,6 +127,6 @@ export default class Binding extends Node {
* @param {import('./shared/Node.js').default} node
* @returns {node is import('./Element.js').default}
*/
function isElement(node) {
function is_element(node) {
return !!(/** @type {any} */ (node).is_media_node);
}

@ -84,7 +84,9 @@ export default class EachBlock extends AbstractBlock {
this.has_animation = false;
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
if (this.has_animation) {
this.children = this.children.filter((child) => !isEmptyNode(child) && !isCommentNode(child));
this.children = this.children.filter(
(child) => !is_empty_node(child) && !is_comment_node(child)
);
if (this.children.length !== 1) {
const child = this.children.find(
(child) => !!(/** @type {import('./Element.js').default} */ (child).animation)
@ -102,11 +104,11 @@ export default class EachBlock extends AbstractBlock {
}
/** @param {import('./interfaces.js').INode} node */
function isEmptyNode(node) {
function is_empty_node(node) {
return node.type === 'Text' && node.data.trim() === '';
}
/** @param {import('./interfaces.js').INode} node */
function isCommentNode(node) {
function is_comment_node(node) {
return node.type === 'Comment';
}

@ -1,5 +1,6 @@
import { is_html, is_svg, is_void } from '../../../shared/utils/names.js';
import Node from './shared/Node.js';
import { walk } from 'estree-walker';
import Attribute from './Attribute.js';
import Binding from './Binding.js';
import EventHandler from './EventHandler.js';
@ -430,7 +431,7 @@ export default class Element extends Node {
}
if (this.name === 'textarea') {
if (info.children.length > 0) {
const value_attribute = info.attributes.find((node) => node.name === 'value');
const value_attribute = get_value_attribute(info.attributes);
if (value_attribute) {
component.error(value_attribute, compiler_errors.textarea_duplicate_value);
return;
@ -449,7 +450,7 @@ export default class Element extends Node {
// Special case — treat these the same way:
// <option>{foo}</option>
// <option value={foo}>{foo}</option>
const value_attribute = info.attributes.find((attribute) => attribute.name === 'value');
const value_attribute = get_value_attribute(info.attributes);
if (!value_attribute) {
info.attributes.push({
type: 'Attribute',
@ -875,7 +876,7 @@ export default class Element extends Node {
) {
const interactive_handlers = handlers
.map((handler) => handler.name)
.filter((handlerName) => a11y_interactive_handlers.has(handlerName));
.filter((handler_name) => a11y_interactive_handlers.has(handler_name));
if (interactive_handlers.length > 0) {
component.warn(
this,
@ -1420,3 +1421,30 @@ function within_custom_element(parent) {
}
return false;
}
/**
* @param {any[]} attributes
*/
function get_value_attribute(attributes) {
let node_value;
attributes.forEach((node) => {
if (node.type !== 'Spread' && node.name.toLowerCase() === 'value') {
node_value = node;
}
if (node.type === 'Spread') {
walk(/** @type {any} */ (node.expression), {
enter(/** @type {import('estree').Node} */ node) {
if (node_value) {
this.skip();
}
if (node.type === 'Identifier') {
if (/** @type {import('estree').Identifier} */ (node).name.toLowerCase() === 'value') {
node_value = node;
}
}
}
});
}
});
return node_value;
}

@ -73,8 +73,8 @@ function sort_consts_nodes(consts_nodes, component) {
}, [])
);
if (cycle && cycle.length) {
const nodeList = lookup.get(cycle[0]);
const node = nodeList[0];
const node_list = lookup.get(cycle[0]);
const node = node_list[0];
component.error(node.node, compiler_errors.cyclical_const_tags(cycle));
}

@ -990,7 +990,8 @@ export default class ElementWrapper extends Wrapper {
const static_attributes = [];
this.attributes.forEach((attr) => {
if (attr instanceof SpreadAttributeWrapper) {
static_attributes.push({ type: 'SpreadElement', argument: attr.node.expression.node });
const snippet = { type: 'SpreadElement', argument: attr.node.expression.manipulate(block) };
static_attributes.push(snippet);
} else {
const name = attr.property_name || attr.name;
static_attributes.push(p`${name}: ${attr.get_value(block)}`);
@ -1240,11 +1241,7 @@ export default class ElementWrapper extends Wrapper {
}
if (this.dynamic_style_dependencies.size > 0) {
maybe_create_style_changed_var();
// If all dependencies are same as the style attribute dependencies, then we can skip the dirty check
condition =
all_deps.size === this.dynamic_style_dependencies.size
? style_changed_var
: x`${style_changed_var} || ${condition}`;
condition = x`${condition} || ${style_changed_var}`;
}
block.chunks.update.push(b`
if (${condition}) {

@ -105,14 +105,19 @@ export default class InlineComponentWrapper extends Wrapper {
this.slots.set(name, slot_definition);
}
warn_if_reactive() {
const { name } = this.node;
const variable = this.renderer.component.var_lookup.get(name);
let { name } = this.node;
const top = name.split('.')[0]; // <T.foo/> etc. should check for T instead of "T.foo"
const variable = this.renderer.component.var_lookup.get(top);
if (!variable) {
return;
}
const ignores = extract_ignores_above_node(this.node);
this.renderer.component.push_ignores(ignores);
if (variable.reassigned || variable.export_name || variable.is_reactive_dependency) {
if (
variable.reassigned ||
variable.export_name || // or a prop
variable.mutated
) {
this.renderer.component.warn(this.node, compiler_warnings.reactive_component(name));
}
this.renderer.component.pop_ignores();

@ -94,14 +94,14 @@ export default class WindowWrapper extends Wrapper {
bindings.scrollX && bindings.scrollY
? x`"${bindings.scrollX}" in this._state || "${bindings.scrollY}" in this._state`
: x`"${bindings.scrollX || bindings.scrollY}" in this._state`;
const scrollX = bindings.scrollX && x`this._state.${bindings.scrollX}`;
const scrollY = bindings.scrollY && x`this._state.${bindings.scrollY}`;
const scroll_x = bindings.scrollX && x`this._state.${bindings.scrollX}`;
const scroll_y = bindings.scrollY && x`this._state.${bindings.scrollY}`;
renderer.meta_bindings.push(b`
if (${condition}) {
@_scrollTo(${scrollX || '@_window.pageXOffset'}, ${scrollY || '@_window.pageYOffset'});
@_scrollTo(${scroll_x || '@_window.pageXOffset'}, ${scroll_y || '@_window.pageYOffset'});
}
${scrollX && `${scrollX} = @_window.pageXOffset;`}
${scrollY && `${scrollY} = @_window.pageYOffset;`}
${scroll_x && `${scroll_x} = @_window.pageXOffset;`}
${scroll_y && `${scroll_y} = @_window.pageYOffset;`}
`);
block.event_listeners.push(x`
@listen(@_window, "${event}", () => {
@ -132,17 +132,17 @@ export default class WindowWrapper extends Wrapper {
// special case... might need to abstract this out if we add more special cases
if (bindings.scrollX || bindings.scrollY) {
const condition = renderer.dirty([bindings.scrollX, bindings.scrollY].filter(Boolean));
const scrollX = bindings.scrollX
const scroll_x = bindings.scrollX
? renderer.reference(bindings.scrollX)
: x`@_window.pageXOffset`;
const scrollY = bindings.scrollY
const scroll_y = bindings.scrollY
? renderer.reference(bindings.scrollY)
: x`@_window.pageYOffset`;
block.chunks.update.push(b`
if (${condition} && !${scrolling}) {
${scrolling} = true;
@_clearTimeout(${scrolling_timeout});
@_scrollTo(${scrollX}, ${scrollY});
@_scrollTo(${scroll_x}, ${scroll_y});
${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100);
}
`);

@ -3,8 +3,11 @@ import { is_reserved_keyword } from '../../../utils/reserved_keywords.js';
/** @param {import('../../../../interfaces.js').Var} variable */
export default function is_dynamic(variable) {
if (variable) {
if (variable.mutated || variable.reassigned) return true; // dynamic internal state
if (!variable.module && variable.writable && variable.export_name) return true; // writable props
// Only variables declared in the instance script tags should be considered dynamic
const is_declared_in_reactive_context = !variable.module && !variable.global;
if (is_declared_in_reactive_context && (variable.mutated || variable.reassigned)) return true; // dynamic internal state
if (is_declared_in_reactive_context && variable.writable && variable.export_name) return true; // writable props
if (is_reserved_keyword(variable.name)) return true;
}
return false;

@ -8,7 +8,7 @@ import * as node from './node/index.js';
*
* The new nodes are located in `./node`.
*/
const cqSyntax = fork({
const cq_syntax = fork({
atrule: {
// extend or override at-rule dictionary
container: {
@ -16,8 +16,8 @@ const cqSyntax = fork({
prelude() {
return this.createSingleNodeList(this.ContainerQuery());
},
block(isStyleBlock = false) {
return this.Block(isStyleBlock);
block(is_style_block = false) {
return this.Block(is_style_block);
}
}
}
@ -25,4 +25,4 @@ const cqSyntax = fork({
node
});
export const parse = cqSyntax.parse;
export const parse = cq_syntax.parse;

@ -16,7 +16,7 @@ export const structure = {
value: ['Identifier', 'Number', 'Comparison', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
};
function lookup_non_WS_type_and_value(offset, type, referenceStr) {
function lookup_non_ws_type_and_value(offset, type, reference_str) {
let current_type;
do {
@ -26,7 +26,7 @@ function lookup_non_WS_type_and_value(offset, type, referenceStr) {
}
} while (current_type !== 0); // NULL -> 0
return current_type === type ? this.lookupValue(offset - 1, referenceStr) : false;
return current_type === type ? this.lookupValue(offset - 1, reference_str) : false;
}
export function parse() {
@ -40,7 +40,7 @@ export function parse() {
while (!this.eof && this.tokenType !== RightParenthesis) {
switch (this.tokenType) {
case Number:
if (lookup_non_WS_type_and_value.call(this, 1, Delim, '/')) {
if (lookup_non_ws_type_and_value.call(this, 1, Delim, '/')) {
child = this.Ratio();
} else {
child = this.Number();

@ -84,7 +84,17 @@ function make_dirty(component, i) {
component.$$.dirty[(i / 31) | 0] |= 1 << i % 31;
}
/** @returns {void} */
// TODO: Document the other params
/**
* @param {SvelteComponent} component
* @param {import('./public.js').ComponentConstructorOptions} options
*
* @param {import('./utils.js')['not_equal']} not_equal Used to compare props and state values.
* @param {(target: Element | ShadowRoot) => void} [append_styles] Function that appends styles to the DOM when the component is first initialised.
* This will be the `add_css` function from the compiled component.
*
* @returns {void}
*/
export function init(
component,
options,
@ -92,7 +102,7 @@ export function init(
create_fragment,
not_equal,
props,
append_styles,
append_styles = null,
dirty = [-1]
) {
const parent_component = current_component;
@ -139,8 +149,9 @@ export function init(
if (options.target) {
if (options.hydrate) {
start_hydrating();
// TODO: what is the correct type here?
// @ts-expect-error
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
} else {

@ -1,5 +1,7 @@
import { ResizeObserverSingleton } from './ResizeObserverSingleton.js';
import { contenteditable_truthy_values, has_prop } from './utils.js';
import { ResizeObserverSingleton } from './ResizeObserverSingleton.js';
// Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM
// at the end of hydration without touching the remaining nodes.
let is_hydrating = false;
@ -50,14 +52,14 @@ function init_hydrate(target) {
let children = /** @type {ArrayLike<NodeEx2>} */ (target.childNodes);
// If target is <head>, there may be children without claim_order
if (target.nodeName === 'HEAD') {
const myChildren = [];
const my_children = [];
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (node.claim_order !== undefined) {
myChildren.push(node);
my_children.push(node);
}
}
children = myChildren;
children = my_children;
}
/*
* Reorder claimed children optimally.
@ -87,15 +89,15 @@ function init_hydrate(target) {
// Find the largest subsequence length such that it ends in a value less than our current value
// upper_bound returns first greater value, so we subtract one
// with fast path for when we are on the current longest subsequence
const seqLen =
const seq_len =
(longest > 0 && children[m[longest]].claim_order <= current
? longest + 1
: upper_bound(1, longest, (idx) => children[m[idx]].claim_order, current)) - 1;
p[i] = m[seqLen] + 1;
const newLen = seqLen + 1;
p[i] = m[seq_len] + 1;
const new_len = seq_len + 1;
// We can guarantee that current is the smallest value. Otherwise, we would have generated a longer sequence.
m[newLen] = i;
longest = Math.max(newLen, longest);
m[new_len] = i;
longest = Math.max(new_len, longest);
}
// The longest increasing subsequence of nodes (initially reversed)
@ -108,28 +110,28 @@ function init_hydrate(target) {
/**
* @type {NodeEx2[]}
*/
const toMove = [];
const to_move = [];
let last = children.length - 1;
for (let cur = m[longest] + 1; cur != 0; cur = p[cur - 1]) {
lis.push(children[cur - 1]);
for (; last >= cur; last--) {
toMove.push(children[last]);
to_move.push(children[last]);
}
last--;
}
for (; last >= 0; last--) {
toMove.push(children[last]);
to_move.push(children[last]);
}
lis.reverse();
// We sort the nodes being moved to guarantee that their insertion order matches the claim order
toMove.sort((a, b) => a.claim_order - b.claim_order);
to_move.sort((a, b) => a.claim_order - b.claim_order);
// Finally, we move the nodes
for (let i = 0, j = 0; i < toMove.length; i++) {
while (j < lis.length && toMove[i].claim_order >= lis[j].claim_order) {
for (let i = 0, j = 0; i < to_move.length; i++) {
while (j < lis.length && to_move[i].claim_order >= lis[j].claim_order) {
j++;
}
const anchor = j < lis.length ? lis[j] : null;
target.insertBefore(toMove[i], anchor);
target.insertBefore(to_move[i], anchor);
}
}
@ -624,26 +626,26 @@ function init_claim_info(nodes) {
* @template {ChildNodeEx} R
* @param {ChildNodeArray} nodes
* @param {(node: ChildNodeEx) => node is R} predicate
* @param {(node: ChildNodeEx) => ChildNodeEx | undefined} processNode
* @param {() => R} createNode
* @param {boolean} dontUpdateLastIndex
* @param {(node: ChildNodeEx) => ChildNodeEx | undefined} process_node
* @param {() => R} create_node
* @param {boolean} dont_update_last_index
* @returns {R}
*/
function claim_node(nodes, predicate, processNode, createNode, dontUpdateLastIndex = false) {
function claim_node(nodes, predicate, process_node, create_node, dont_update_last_index = false) {
// Try to find nodes in an order such that we lengthen the longest increasing subsequence
init_claim_info(nodes);
const resultNode = (() => {
const result_node = (() => {
// We first try to find an element after the previous one
for (let i = nodes.claim_info.last_index; i < nodes.length; i++) {
const node = nodes[i];
if (predicate(node)) {
const replacement = processNode(node);
const replacement = process_node(node);
if (replacement === undefined) {
nodes.splice(i, 1);
} else {
nodes[i] = replacement;
}
if (!dontUpdateLastIndex) {
if (!dont_update_last_index) {
nodes.claim_info.last_index = i;
}
return node;
@ -654,13 +656,13 @@ function claim_node(nodes, predicate, processNode, createNode, dontUpdateLastInd
for (let i = nodes.claim_info.last_index - 1; i >= 0; i--) {
const node = nodes[i];
if (predicate(node)) {
const replacement = processNode(node);
const replacement = process_node(node);
if (replacement === undefined) {
nodes.splice(i, 1);
} else {
nodes[i] = replacement;
}
if (!dontUpdateLastIndex) {
if (!dont_update_last_index) {
nodes.claim_info.last_index = i;
} else if (replacement === undefined) {
// Since we spliced before the last_index, we decrease it
@ -670,11 +672,11 @@ function claim_node(nodes, predicate, processNode, createNode, dontUpdateLastInd
}
}
// If we can't find any matching node, we create a new one
return createNode();
return create_node();
})();
resultNode.claim_order = nodes.claim_info.total_claimed;
result_node.claim_order = nodes.claim_info.total_claimed;
nodes.claim_info.total_claimed += 1;
return resultNode;
return result_node;
}
/**
@ -736,13 +738,13 @@ export function claim_text(nodes, data) {
(node) => node.nodeType === 3,
/** @param {Text} node */
(node) => {
const dataStr = '' + data;
if (node.data.startsWith(dataStr)) {
if (node.data.length !== dataStr.length) {
return node.splitText(dataStr.length);
const data_str = '' + data;
if (node.data.startsWith(data_str)) {
if (node.data.length !== data_str.length) {
return node.splitText(data_str.length);
}
} else {
node.data = dataStr;
node.data = data_str;
}
},
() => text(data),

@ -6,5 +6,5 @@
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '4.2.0';
export const VERSION = '4.2.1';
export const PUBLIC_VERSION = '4';

@ -1,6 +1,8 @@
{
"plugins": ["lube"],
"rules": {
"no-console": "off",
"@typescript-eslint/no-var-requires": "off"
"@typescript-eslint/no-var-requires": "off",
"lube/svelte-naming-convention": ["error", { "fixSameNames": true }]
}
}

@ -20,14 +20,14 @@ describe('compiler-errors', () => {
it_fn(dir, () => {
const cwd = path.resolve(`${__dirname}/samples/${dir}`);
const compileOptions = Object.assign({}, config.compileOptions || {}, {
const compile_options = Object.assign({}, config.compileOptions || {}, {
immutable: config.immutable,
accessors: 'accessors' in config ? config.accessors : true,
generate: 'dom'
});
try {
compile(fs.readFileSync(`${cwd}/main.svelte`, 'utf-8'), compileOptions);
compile(fs.readFileSync(`${cwd}/main.svelte`, 'utf-8'), compile_options);
} catch (error) {
if (typeof config.error === 'function') {
config.error(assert, error);

@ -2,11 +2,11 @@ export default {
compileOptions: {
filename: 'src/components/FooSwitcher.svelte',
cssHash({ hash, css, name, filename }) {
const minFilename = filename
const min_filename = filename
.split('/')
.map((i) => i.charAt(0).toLowerCase())
.join('');
return `sv-${name}-${minFilename}-${hash(css)}`;
return `sv-${name}-${min_filename}-${hash(css)}`;
}
}
};

@ -1,10 +1,11 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import glob from 'tiny-glob/sync';
import colors from 'kleur';
import { assert } from 'vitest';
import colors from 'kleur';
import { compile } from 'svelte/compiler';
import { fileURLToPath } from 'node:url';
import glob from 'tiny-glob/sync';
export function try_load_json(file) {
try {
@ -158,8 +159,8 @@ export function create_loader(compileOptions, cwd) {
)
.replace(
/^import (\w+, )?{([^}]+)} from ['"](.+)['"];?/gm,
(_, default_, names, source) => {
const d = default_ ? `default: ${default_}` : '';
(_, _default, names, source) => {
const d = _default ? `default: ${_default}` : '';
return `const { ${d} ${names.replaceAll(
' as ',
': '

@ -18,12 +18,12 @@ describe('hydration', async () => {
it_fn(dir, async () => {
const cwd = path.resolve(`${__dirname}/samples/${dir}`);
const compileOptions = Object.assign({}, config.compileOptions, {
const compile_options = Object.assign({}, config.compileOptions, {
accessors: 'accessors' in config ? config.accessors : true,
hydratable: true
});
const { default: SvelteComponent } = await create_loader(compileOptions, cwd)('main.svelte');
const { default: SvelteComponent } = await create_loader(compile_options, cwd)('main.svelte');
const target = window.document.body;
const head = window.document.head;

@ -2,12 +2,12 @@ export default {
props: {},
snapshot(target) {
const nullText = target.querySelectorAll('p')[0].textContent;
const undefinedText = target.querySelectorAll('p')[1].textContent;
const null_text = target.querySelectorAll('p')[0].textContent;
const undefined_text = target.querySelectorAll('p')[1].textContent;
return {
nullText,
undefinedText
nullText: null_text,
undefinedText: undefined_text
};
}
};

@ -53,11 +53,11 @@ function create_fragment(ctx) {
div7 = element("div");
t7 = space();
div8 = element("div");
toggle_class(div0, "update1", reactiveModuleVar);
toggle_class(div1, "update2", /*reactiveConst*/ ctx[0].x);
toggle_class(div2, "update3", nonReactiveGlobal && /*reactiveConst*/ ctx[0].x);
toggle_class(div3, "update4", /*$reactiveStoreVal*/ ctx[2]);
toggle_class(div4, "update5", /*$reactiveDeclaration*/ ctx[3]);
toggle_class(div0, "update2", /*reactiveConst*/ ctx[0].x);
toggle_class(div1, "update3", nonReactiveGlobal && /*reactiveConst*/ ctx[0].x);
toggle_class(div2, "update4", /*$reactiveStoreVal*/ ctx[2]);
toggle_class(div3, "update5", /*$reactiveDeclaration*/ ctx[3]);
toggle_class(div4, "update1", reassignedModuleVar);
toggle_class(div5, "static1", nonReactiveModuleVar);
toggle_class(div6, "static2", nonReactiveGlobal);
toggle_class(div7, "static3", nonReactiveModuleVar && nonReactiveGlobal);
@ -83,24 +83,20 @@ function create_fragment(ctx) {
insert(target, div8, anchor);
},
p(ctx, [dirty]) {
if (dirty & /*reactiveModuleVar*/ 0) {
toggle_class(div0, "update1", reactiveModuleVar);
}
if (dirty & /*reactiveConst*/ 1) {
toggle_class(div1, "update2", /*reactiveConst*/ ctx[0].x);
toggle_class(div0, "update2", /*reactiveConst*/ ctx[0].x);
}
if (dirty & /*nonReactiveGlobal, reactiveConst*/ 1) {
toggle_class(div2, "update3", nonReactiveGlobal && /*reactiveConst*/ ctx[0].x);
toggle_class(div1, "update3", nonReactiveGlobal && /*reactiveConst*/ ctx[0].x);
}
if (dirty & /*$reactiveStoreVal*/ 4) {
toggle_class(div3, "update4", /*$reactiveStoreVal*/ ctx[2]);
toggle_class(div2, "update4", /*$reactiveStoreVal*/ ctx[2]);
}
if (dirty & /*$reactiveDeclaration*/ 8) {
toggle_class(div4, "update5", /*$reactiveDeclaration*/ ctx[3]);
toggle_class(div3, "update5", /*$reactiveDeclaration*/ ctx[3]);
}
},
i: noop,
@ -130,7 +126,7 @@ function create_fragment(ctx) {
}
let nonReactiveModuleVar = Math.random();
let reactiveModuleVar = Math.random();
let reassignedModuleVar = Math.random();
function instance($$self, $$props, $$invalidate) {
let reactiveDeclaration;
@ -144,13 +140,13 @@ function instance($$self, $$props, $$invalidate) {
$$self.$$.on_destroy.push(() => $$unsubscribe_reactiveDeclaration());
nonReactiveGlobal = Math.random();
const reactiveConst = { x: Math.random() };
reactiveModuleVar += 1;
reassignedModuleVar += 1;
if (Math.random()) {
reactiveConst.x += 1;
}
$: $$subscribe_reactiveDeclaration($$invalidate(1, reactiveDeclaration = reactiveModuleVar * 2));
$: $$subscribe_reactiveDeclaration($$invalidate(1, reactiveDeclaration = reassignedModuleVar * 2));
return [reactiveConst, reactiveDeclaration, $reactiveStoreVal, $reactiveDeclaration];
}

@ -1,6 +1,6 @@
<script context="module">
let nonReactiveModuleVar = Math.random();
let reactiveModuleVar = Math.random();
let reassignedModuleVar = Math.random();
</script>
<script>
@ -9,22 +9,22 @@
nonReactiveGlobal = Math.random();
const reactiveConst = {x: Math.random()};
$: reactiveDeclaration = reactiveModuleVar * 2;
$: reactiveDeclaration = reassignedModuleVar * 2;
reactiveModuleVar += 1;
reassignedModuleVar += 1;
if (Math.random()) {
reactiveConst.x += 1;
}
</script>
<!--These should all get updaters because they have at least one reactive dependency-->
<div class:update1={reactiveModuleVar}></div>
<div class:update2={reactiveConst.x}></div>
<div class:update3={nonReactiveGlobal && reactiveConst.x}></div>
<div class:update4={$reactiveStoreVal}></div>
<div class:update5={$reactiveDeclaration}></div>
<!--These shouldn't get updates because they're purely non-reactive-->
<div class:update1={reassignedModuleVar}></div>
<div class:static1={nonReactiveModuleVar}></div>
<div class:static2={nonReactiveGlobal}></div>
<div class:static3={nonReactiveModuleVar && nonReactiveGlobal}></div>

@ -21,7 +21,7 @@ function create_dynamic_element_3(ctx) {
return {
c() {
svelte_element = element(static_value);
set_dynamic_element_data(static_value)(svelte_element, { static_value, ...static_obj });
set_dynamic_element_data(static_value)(svelte_element, { static_value, .../*static_obj*/ ctx[2] });
toggle_class(svelte_element, "foo", static_value);
},
m(target, anchor) {
@ -43,7 +43,7 @@ function create_dynamic_element_2(ctx) {
return {
c() {
svelte_element = element(/*dynamic_value*/ ctx[0]);
set_dynamic_element_data(/*dynamic_value*/ ctx[0])(svelte_element, { static_value, ...static_obj });
set_dynamic_element_data(/*dynamic_value*/ ctx[0])(svelte_element, { static_value, .../*static_obj*/ ctx[2] });
toggle_class(svelte_element, "foo", static_value);
},
m(target, anchor) {

@ -26,8 +26,8 @@ describe('parse', () => {
.trimEnd()
.replace(/\r/g, '');
const expectedOutput = try_load_json(`${__dirname}/samples/${dir}/output.json`);
const expectedError = try_load_json(`${__dirname}/samples/${dir}/error.json`);
const expected_output = try_load_json(`${__dirname}/samples/${dir}/output.json`);
const expected_error = try_load_json(`${__dirname}/samples/${dir}/error.json`);
try {
const { ast } = svelte.compile(
@ -42,16 +42,16 @@ describe('parse', () => {
JSON.stringify(ast, null, '\t')
);
assert.deepEqual(ast.html, expectedOutput.html);
assert.deepEqual(ast.css, expectedOutput.css);
assert.deepEqual(ast.instance, expectedOutput.instance);
assert.deepEqual(ast.module, expectedOutput.module);
assert.deepEqual(ast.html, expected_output.html);
assert.deepEqual(ast.css, expected_output.css);
assert.deepEqual(ast.instance, expected_output.instance);
assert.deepEqual(ast.module, expected_output.module);
} catch (err) {
if (err.name !== 'ParseError') throw err;
if (!expectedError) throw err;
if (!expected_error) throw err;
const { code, message, pos, start } = err;
assert.deepEqual({ code, message, pos, start }, expectedError);
assert.deepEqual({ code, message, pos, start }, expected_error);
}
});
});

@ -33,10 +33,10 @@ export function ok(condition, message) {
}
export function htmlEqual(actual, expected, message) {
return deepEqual(normalizeHtml(window, actual), normalizeHtml(window, expected), message);
return deepEqual(normalize_html(window, actual), normalize_html(window, expected), message);
}
function normalizeHtml(window, html) {
function normalize_html(window, html) {
try {
const node = window.document.createElement('div');
node.innerHTML = html
@ -44,7 +44,7 @@ function normalizeHtml(window, html) {
.replace(/>[\s\r\n]+</g, '><')
.trim();
normalizeStyles(node);
normalize_styles(node);
return node.innerHTML.replace(/<\/?noscript\/?>/g, '');
} catch (err) {
@ -52,14 +52,14 @@ function normalizeHtml(window, html) {
}
}
function normalizeStyles(node) {
function normalize_styles(node) {
if (node.nodeType === 1) {
if (node.hasAttribute('style')) {
node.style = node.style.cssText;
}
for (const child of node.childNodes) {
normalizeStyles(child);
normalize_styles(child);
}
}
}

@ -93,7 +93,7 @@ async function run_browser_test(dir) {
globalName: 'test'
});
function assertWarnings() {
function assert_warnings() {
if (config.warnings) {
assert.deepStrictEqual(
warnings.map((w) => ({
@ -112,7 +112,7 @@ async function run_browser_test(dir) {
}
}
assertWarnings();
assert_warnings();
try {
const page = await browser.newPage();
@ -191,7 +191,7 @@ async function run_custom_elements_test(dir) {
globalName: 'test'
});
function assertWarnings() {
function assert_warnings() {
if (expected_warnings) {
assert.deepStrictEqual(
warnings.map((w) => ({
@ -205,7 +205,7 @@ async function run_custom_elements_test(dir) {
);
}
}
assertWarnings();
assert_warnings();
const page = await browser.newPage();
page.on('console', (type) => {

@ -6,25 +6,25 @@ export default async function (target) {
target.innerHTML = '<custom-element red white></custom-element>';
await tick();
await tick();
const ceRoot = target.querySelector('custom-element').shadowRoot;
const div = ceRoot.querySelector('div');
const p = ceRoot.querySelector('p');
const button = ceRoot.querySelector('button');
const ce_root = target.querySelector('custom-element').shadowRoot;
const div = ce_root.querySelector('div');
const p = ce_root.querySelector('p');
const button = ce_root.querySelector('button');
assert.equal(getComputedStyle(div).color, 'rgb(255, 0, 0)');
assert.equal(getComputedStyle(p).color, 'rgb(255, 255, 255)');
const innerRoot = ceRoot.querySelector('my-widget').shadowRoot;
const innerDiv = innerRoot.querySelector('div');
const innerP = innerRoot.querySelector('p');
const inner_root = ce_root.querySelector('my-widget').shadowRoot;
const inner_div = inner_root.querySelector('div');
const inner_p = inner_root.querySelector('p');
assert.equal(getComputedStyle(innerDiv).color, 'rgb(255, 0, 0)');
assert.equal(getComputedStyle(innerP).color, 'rgb(255, 255, 255)');
assert.equal(getComputedStyle(inner_div).color, 'rgb(255, 0, 0)');
assert.equal(getComputedStyle(inner_p).color, 'rgb(255, 255, 255)');
button.click();
await tick();
await tick();
assert.equal(getComputedStyle(div).color, 'rgb(0, 0, 0)');
assert.equal(getComputedStyle(innerDiv).color, 'rgb(0, 0, 0)');
assert.equal(getComputedStyle(inner_div).color, 'rgb(0, 0, 0)');
}

@ -30,7 +30,7 @@ export default async function (target) {
const component = new SvelteComponent(options);
const waitUntil = async (fn, ms = 500) => {
const wait_until = async (fn, ms = 500) => {
const start = new Date().getTime();
do {
if (fn()) return;
@ -48,7 +48,7 @@ export default async function (target) {
component,
target,
window,
waitUntil
waitUntil: wait_until
});
component.$destroy();

@ -45,14 +45,14 @@ export default {
`
);
const circleColor1 = target.querySelector('#svg-1 circle');
const rectColor1 = target.querySelector('#svg-1 rect');
const circleColor2 = target.querySelector('#svg-2 circle');
const rectColor2 = target.querySelector('#svg-2 rect');
const circle_color1 = target.querySelector('#svg-1 circle');
const rect_color1 = target.querySelector('#svg-1 rect');
const circle_color2 = target.querySelector('#svg-2 circle');
const rect_color2 = target.querySelector('#svg-2 rect');
assert.htmlEqual(window.getComputedStyle(circleColor1).fill, 'rgb(255, 0, 0)');
assert.htmlEqual(window.getComputedStyle(rectColor1).fill, 'rgb(255, 255, 0)');
assert.htmlEqual(window.getComputedStyle(circleColor2).fill, 'rgb(0, 255, 255)');
assert.htmlEqual(window.getComputedStyle(rectColor2).fill, 'rgb(0, 0, 0)');
assert.htmlEqual(window.getComputedStyle(circle_color1).fill, 'rgb(255, 0, 0)');
assert.htmlEqual(window.getComputedStyle(rect_color1).fill, 'rgb(255, 255, 0)');
assert.htmlEqual(window.getComputedStyle(circle_color2).fill, 'rgb(0, 255, 255)');
assert.htmlEqual(window.getComputedStyle(rect_color2).fill, 'rgb(0, 0, 0)');
}
};

@ -14,14 +14,14 @@ export default {
</div>
`,
test({ target, window, assert }) {
const railColor1 = target.querySelector('#slider-1 p');
const trackColor1 = target.querySelector('#slider-1 span');
const railColor2 = target.querySelector('#slider-2 p');
const trackColor2 = target.querySelector('#slider-2 span');
const rail_color1 = target.querySelector('#slider-1 p');
const track_color1 = target.querySelector('#slider-1 span');
const rail_color2 = target.querySelector('#slider-2 p');
const track_color2 = target.querySelector('#slider-2 span');
assert.htmlEqual(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.htmlEqual(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.htmlEqual(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.htmlEqual(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.htmlEqual(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.htmlEqual(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.htmlEqual(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.htmlEqual(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
}
};

@ -18,31 +18,31 @@ export default {
`,
test({ target, window, assert, component }) {
function assert_slider_1() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider1');
assert.equal(railColor2.textContent, 'Slider1');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(rail_color1.textContent, 'Slider1');
assert.equal(rail_color2.textContent, 'Slider1');
}
function assert_slider_2() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider2');
assert.equal(railColor2.textContent, 'Slider2');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(rail_color1.textContent, 'Slider2');
assert.equal(rail_color2.textContent, 'Slider2');
}
assert_slider_1();

@ -20,31 +20,31 @@ export default {
`,
test({ target, window, assert, component }) {
function assert_slider_1() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider1');
assert.equal(railColor2.textContent, 'Slider1');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(rail_color1.textContent, 'Slider1');
assert.equal(rail_color2.textContent, 'Slider1');
}
function assert_slider_2() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider2');
assert.equal(railColor2.textContent, 'Slider2');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(rail_color1.textContent, 'Slider2');
assert.equal(rail_color2.textContent, 'Slider2');
}
assert_slider_1();

@ -22,26 +22,26 @@ export default {
</div>
`,
test({ target, window, assert }) {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const nestRailColor1 = target.querySelector('#nest-component1 p');
const nestTrackColor1 = target.querySelector('#nest-component1 span');
const nestRailColor2 = target.querySelector('#nest-component2 p');
const nestTrackColor2 = target.querySelector('#nest-component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
const nest_rail_color1 = target.querySelector('#nest-component1 p');
const nest_track_color1 = target.querySelector('#nest-component1 span');
const nest_rail_color2 = target.querySelector('#nest-component2 p');
const nest_track_color2 = target.querySelector('#nest-component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nestRailColor1).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nestTrackColor1).color, 'rgb(255, 0, 255)');
assert.equal(window.getComputedStyle(nestRailColor2).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nestTrackColor2).color, 'rgb(255, 255, 255)');
assert.equal(railColor1.textContent, 'Slider1');
assert.equal(railColor2.textContent, 'Slider2');
assert.equal(nestRailColor1.textContent, 'Slider1');
assert.equal(nestRailColor2.textContent, 'Slider2');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nest_rail_color1).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nest_track_color1).color, 'rgb(255, 0, 255)');
assert.equal(window.getComputedStyle(nest_rail_color2).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nest_track_color2).color, 'rgb(255, 255, 255)');
assert.equal(rail_color1.textContent, 'Slider1');
assert.equal(rail_color2.textContent, 'Slider2');
assert.equal(nest_rail_color1.textContent, 'Slider1');
assert.equal(nest_rail_color2.textContent, 'Slider2');
}
};

@ -25,51 +25,51 @@ export default {
`,
test({ target, window, assert, component }) {
function assert_slider_1() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const nestRailColor1 = target.querySelector('#nest-component1 p');
const nestTrackColor1 = target.querySelector('#nest-component1 span');
const nestRailColor2 = target.querySelector('#nest-component2 p');
const nestTrackColor2 = target.querySelector('#nest-component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
const nest_rail_color1 = target.querySelector('#nest-component1 p');
const nest_track_color1 = target.querySelector('#nest-component1 span');
const nest_rail_color2 = target.querySelector('#nest-component2 p');
const nest_track_color2 = target.querySelector('#nest-component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nestRailColor1).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nestTrackColor1).color, 'rgb(255, 0, 255)');
assert.equal(window.getComputedStyle(nestRailColor2).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nestTrackColor2).color, 'rgb(255, 0, 255)');
assert.equal(railColor1.textContent, 'Slider1');
assert.equal(railColor2.textContent, 'Slider1');
assert.equal(nestRailColor1.textContent, 'Slider1');
assert.equal(nestRailColor2.textContent, 'Slider1');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nest_rail_color1).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nest_track_color1).color, 'rgb(255, 0, 255)');
assert.equal(window.getComputedStyle(nest_rail_color2).color, 'rgb(255, 255, 0)');
assert.equal(window.getComputedStyle(nest_track_color2).color, 'rgb(255, 0, 255)');
assert.equal(rail_color1.textContent, 'Slider1');
assert.equal(rail_color2.textContent, 'Slider1');
assert.equal(nest_rail_color1.textContent, 'Slider1');
assert.equal(nest_rail_color2.textContent, 'Slider1');
}
function assert_slider_2() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');
const nestRailColor1 = target.querySelector('#nest-component1 p');
const nestTrackColor1 = target.querySelector('#nest-component1 span');
const nestRailColor2 = target.querySelector('#nest-component2 p');
const nestTrackColor2 = target.querySelector('#nest-component2 span');
const rail_color1 = target.querySelector('#component1 p');
const track_color1 = target.querySelector('#component1 span');
const rail_color2 = target.querySelector('#component2 p');
const track_color2 = target.querySelector('#component2 span');
const nest_rail_color1 = target.querySelector('#nest-component1 p');
const nest_track_color1 = target.querySelector('#nest-component1 span');
const nest_rail_color2 = target.querySelector('#nest-component2 p');
const nest_track_color2 = target.querySelector('#nest-component2 span');
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nestRailColor1).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nestTrackColor1).color, 'rgb(255, 255, 255)');
assert.equal(window.getComputedStyle(nestRailColor2).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nestTrackColor2).color, 'rgb(255, 255, 255)');
assert.equal(railColor1.textContent, 'Slider2');
assert.equal(railColor2.textContent, 'Slider2');
assert.equal(nestRailColor1.textContent, 'Slider2');
assert.equal(nestRailColor2.textContent, 'Slider2');
assert.equal(window.getComputedStyle(rail_color1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(track_color1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(rail_color2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(track_color2).color, 'rgb(0, 0, 255)');
assert.equal(window.getComputedStyle(nest_rail_color1).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nest_track_color1).color, 'rgb(255, 255, 255)');
assert.equal(window.getComputedStyle(nest_rail_color2).color, 'rgb(0, 255, 255)');
assert.equal(window.getComputedStyle(nest_track_color2).color, 'rgb(255, 255, 255)');
assert.equal(rail_color1.textContent, 'Slider2');
assert.equal(rail_color2.textContent, 'Slider2');
assert.equal(nest_rail_color1.textContent, 'Slider2');
assert.equal(nest_rail_color2.textContent, 'Slider2');
}
assert_slider_1();

@ -55,18 +55,18 @@ async function run_test(dir) {
const cwd = path.resolve(`${__dirname}/samples/${dir}`);
const compileOptions = Object.assign({}, config.compileOptions || {}, {
const compile_options = Object.assign({}, config.compileOptions || {}, {
hydratable: hydrate,
immutable: config.immutable,
accessors: 'accessors' in config ? config.accessors : true
});
const load = create_loader(compileOptions, cwd);
const load = create_loader(compile_options, cwd);
let mod;
let SvelteComponent;
let unintendedError = null;
let unintended_error = null;
if (config.expect_unhandled_rejections) {
listeners.forEach((listener) => {
@ -111,7 +111,7 @@ async function run_test(dir) {
let snapshot = undefined;
if (hydrate && from_ssr_html) {
const load_ssr = create_loader({ ...compileOptions, generate: 'ssr' }, cwd);
const load_ssr = create_loader({ ...compile_options, generate: 'ssr' }, cwd);
// ssr into target
if (config.before_test) config.before_test();
@ -152,14 +152,14 @@ async function run_test(dir) {
console.warn = warn;
if (config.error) {
unintendedError = true;
unintended_error = true;
assert.fail('Expected a runtime error');
}
if (config.warnings) {
assert.deepEqual(warnings, config.warnings);
} else if (warnings.length) {
unintendedError = true;
unintended_error = true;
assert.fail('Received unexpected warnings');
}
@ -183,7 +183,7 @@ async function run_test(dir) {
snapshot,
window,
raf,
compileOptions,
compileOptions: compile_options,
load
});
}
@ -201,7 +201,7 @@ async function run_test(dir) {
await test()
.catch((err) => {
if (config.error && !unintendedError) {
if (config.error && !unintended_error) {
if (typeof config.error === 'function') {
config.error(assert, err);
} else {
@ -217,7 +217,7 @@ async function run_test(dir) {
mkdirp(path.dirname(out)); // file could be in subdirectory, therefore don't use dir
const { js } = compile(fs.readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r/g, ''), {
...compileOptions,
...compile_options,
filename: file
});
fs.writeFileSync(out, js.code);

@ -10,9 +10,9 @@ export default {
`,
async test({ assert, target, window }) {
const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
const click_event = new window.MouseEvent('click');
await btn1.dispatchEvent(clickEvent);
await btn1.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -24,7 +24,7 @@ export default {
`
);
await btn2.dispatchEvent(clickEvent);
await btn2.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -36,7 +36,7 @@ export default {
`
);
await btn3.dispatchEvent(clickEvent);
await btn3.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -48,7 +48,7 @@ export default {
`
);
await btn4.dispatchEvent(clickEvent);
await btn4.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,

@ -12,9 +12,9 @@ export default {
async test({ assert, target, window }) {
const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
const click_event = new window.MouseEvent('click');
await btn1.dispatchEvent(clickEvent);
await btn1.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -27,7 +27,7 @@ export default {
`
);
await btn2.dispatchEvent(clickEvent);
await btn2.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -40,7 +40,7 @@ export default {
`
);
await btn3.dispatchEvent(clickEvent);
await btn3.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -53,7 +53,7 @@ export default {
`
);
await btn4.dispatchEvent(clickEvent);
await btn4.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,

@ -5,10 +5,10 @@ export default {
async test({ assert, target, window }) {
const button = target.querySelector('button');
const eventEnter = new window.MouseEvent('mouseenter');
const eventLeave = new window.MouseEvent('mouseleave');
const event_enter = new window.MouseEvent('mouseenter');
const event_leave = new window.MouseEvent('mouseleave');
await button.dispatchEvent(eventEnter);
await button.dispatchEvent(event_enter);
assert.htmlEqual(
target.innerHTML,
`
@ -17,7 +17,7 @@ export default {
`
);
await button.dispatchEvent(eventLeave);
await button.dispatchEvent(event_leave);
assert.htmlEqual(
target.innerHTML,
`

@ -7,7 +7,7 @@ export default {
const button = target.querySelector('button');
const enter = new window.MouseEvent('mouseenter');
const leave = new window.MouseEvent('mouseleave');
const ctrlPress = new window.KeyboardEvent('keydown', { ctrlKey: true });
const ctrl_press = new window.KeyboardEvent('keydown', { ctrlKey: true });
await button.dispatchEvent(enter);
assert.htmlEqual(
@ -18,7 +18,7 @@ export default {
`
);
await window.dispatchEvent(ctrlPress);
await window.dispatchEvent(ctrl_press);
assert.htmlEqual(
target.innerHTML,
`

@ -5,10 +5,10 @@ export default {
async test({ assert, target, window }) {
const button = target.querySelector('button');
const eventEnter = new window.MouseEvent('mouseenter');
const eventLeave = new window.MouseEvent('mouseleave');
const event_enter = new window.MouseEvent('mouseenter');
const event_leave = new window.MouseEvent('mouseleave');
await button.dispatchEvent(eventEnter);
await button.dispatchEvent(event_enter);
assert.htmlEqual(
target.innerHTML,
`
@ -17,7 +17,7 @@ export default {
`
);
await button.dispatchEvent(eventLeave);
await button.dispatchEvent(event_leave);
assert.htmlEqual(
target.innerHTML,
`

@ -1,8 +1,8 @@
const realPromise = Promise.resolve(42);
const real_promise = Promise.resolve(42);
const promise = () => {};
promise.then = realPromise.then.bind(realPromise);
promise.catch = realPromise.catch.bind(realPromise);
promise.then = real_promise.then.bind(real_promise);
promise.catch = real_promise.catch.bind(real_promise);
export default {
get props() {

@ -1,13 +1,13 @@
let fulfil;
const thePromise = new Promise((f) => {
const the_promise = new Promise((f) => {
fulfil = f;
});
const items = [
{
title: 'a title',
data: thePromise
data: the_promise
}
];
@ -23,7 +23,7 @@ export default {
test({ assert, target }) {
fulfil(42);
return thePromise.then(() => {
return the_promise.then(() => {
assert.htmlEqual(
target.innerHTML,
`

@ -28,10 +28,10 @@ export default {
assert.equal(component.clicked, 42);
const thePromise = Promise.resolve(43);
component.thePromise = thePromise;
const the_promise = Promise.resolve(43);
component.thePromise = the_promise;
return thePromise;
return the_promise;
})
.then(() => {
const { button } = component;

@ -1,12 +1,12 @@
let fulfil;
const thePromise = new Promise((f) => {
const the_promise = new Promise((f) => {
fulfil = f;
});
export default {
get props() {
return { show: true, thePromise };
return { show: true, thePromise: the_promise };
},
html: `
@ -16,7 +16,7 @@ export default {
test({ assert, component, target }) {
fulfil(42);
return thePromise.then(() => {
return the_promise.then(() => {
assert.htmlEqual(
target.innerHTML,
`
@ -35,7 +35,7 @@ export default {
component.show = true;
return thePromise.then(() => {
return the_promise.then(() => {
assert.htmlEqual(
target.innerHTML,
`

@ -1,12 +1,12 @@
let fulfil;
const thePromise = new Promise((f) => {
const the_promise = new Promise((f) => {
fulfil = f;
});
export default {
get props() {
return { thePromise };
return { thePromise: the_promise };
},
html: `
@ -16,7 +16,7 @@ export default {
test({ assert, target }) {
fulfil(42);
return thePromise.then(() => {
return the_promise.then(() => {
assert.htmlEqual(
target.innerHTML,
`

@ -1,12 +1,12 @@
let fulfil;
const thePromise = new Promise((f) => {
const the_promise = new Promise((f) => {
fulfil = f;
});
export default {
get props() {
return { thePromise };
return { thePromise: the_promise };
},
html: `
@ -16,7 +16,7 @@ export default {
async test({ assert, target }) {
fulfil([]);
await thePromise;
await the_promise;
assert.htmlEqual(
target.innerHTML,

@ -15,12 +15,12 @@ export default {
test({ assert, component, target, window }) {
const { cats } = component;
const newCats = cats.slice();
newCats.push({
const new_cats = cats.slice();
new_cats.push({
name: 'cat ' + cats.length,
checked: false
});
component.cats = newCats;
component.cats = new_cats;
let inputs = target.querySelectorAll('input');
assert.equal(inputs.length, 3);

@ -23,7 +23,7 @@ export default {
async test({ assert, target, window }) {
const inputs = target.querySelectorAll('input');
const checked = new Set();
const checkInbox = async (i) => {
const check_inbox = async (i) => {
checked.add(i);
inputs[i].checked = true;
await inputs[i].dispatchEvent(event);
@ -35,17 +35,17 @@ export default {
const event = new window.Event('change');
await checkInbox(2);
await check_inbox(2);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(12);
await check_inbox(12);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(8);
await check_inbox(8);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}

@ -1,8 +1,8 @@
export default {
async test({ assert, target, component, window }) {
const button = target.querySelector('button');
const clickEvent = new window.Event('click');
const changeEvent = new window.Event('change');
const click_event = new window.Event('click');
const change_event = new window.Event('change');
const [input1, input2] = target.querySelectorAll('input[type="checkbox"]');
function validate_inputs(v1, v2) {
@ -17,24 +17,24 @@ export default {
validate_inputs(true, true);
input1.checked = false;
await input1.dispatchEvent(changeEvent);
await input1.dispatchEvent(change_event);
assert.deepEqual(component.test, ['b']);
input2.checked = false;
await input2.dispatchEvent(changeEvent);
await input2.dispatchEvent(change_event);
assert.deepEqual(component.test, []);
input1.checked = true;
input2.checked = true;
await input1.dispatchEvent(changeEvent);
await input2.dispatchEvent(changeEvent);
await input1.dispatchEvent(change_event);
await input2.dispatchEvent(change_event);
assert.deepEqual(component.test, ['b', 'a']);
await button.dispatchEvent(clickEvent);
await button.dispatchEvent(click_event);
assert.deepEqual(component.test, ['b', 'a']); // should it be ['a'] only? valid arguments for both outcomes
input1.checked = false;
await input1.dispatchEvent(changeEvent);
await input1.dispatchEvent(change_event);
assert.deepEqual(component.test, []);
}
};

@ -1,8 +1,8 @@
export default {
async test({ assert, target, component, window }) {
const button = target.querySelector('button');
const clickEvent = new window.Event('click');
const changeEvent = new window.Event('change');
const click_event = new window.Event('click');
const change_event = new window.Event('change');
const [input1, input2] = target.querySelectorAll('input[type="radio"]');
function validate_inputs(v1, v2) {
@ -17,18 +17,18 @@ export default {
validate_inputs(false, true);
input1.checked = true;
await input1.dispatchEvent(changeEvent);
await input1.dispatchEvent(change_event);
assert.deepEqual(component.test, 'a');
input2.checked = true;
await input2.dispatchEvent(changeEvent);
await input2.dispatchEvent(change_event);
assert.deepEqual(component.test, 'b');
await button.dispatchEvent(clickEvent);
await button.dispatchEvent(click_event);
assert.deepEqual(component.test, 'b'); // should it be undefined? valid arguments for both outcomes
input1.checked = true;
await input1.dispatchEvent(changeEvent);
await input1.dispatchEvent(change_event);
assert.deepEqual(component.test, 'a');
}
};

@ -1,25 +1,25 @@
export default {
test({ assert, target, window, component }) {
const input = target.querySelector('input');
const inputEvent = new window.InputEvent('input');
const input_event = new window.InputEvent('input');
assert.equal(component.value, 5);
assert.equal(input.value, '5');
input.value = '5.';
input.dispatchEvent(inputEvent);
input.dispatchEvent(input_event);
// input type number has value === "" if ends with dot/comma
assert.equal(component.value, undefined);
assert.equal(input.value, '');
input.value = '5.5';
input.dispatchEvent(inputEvent);
input.dispatchEvent(input_event);
assert.equal(component.value, 5.5);
assert.equal(input.value, '5.5');
input.value = '5.50';
input.dispatchEvent(inputEvent);
input.dispatchEvent(input_event);
assert.equal(component.value, 5.5);
assert.equal(input.value, '5.50');

@ -9,13 +9,13 @@ const components = [
}
];
const selectedComponent = components[0];
const selected_component = components[0];
export default {
skip: true, // doesn't reflect real-world bug, maybe a JSDOM quirk
get props() {
return { components, selectedComponent };
return { components, selectedComponent: selected_component };
},
html: `

@ -5,13 +5,13 @@ export default {
`,
async test({ assert, component, target, window }) {
const [updateButton, button] = target.querySelectorAll('button');
const [update_button, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await button.dispatchEvent(event);
assert.equal(component.count, 1);
await updateButton.dispatchEvent(event);
await update_button.dispatchEvent(event);
await button.dispatchEvent(event);
assert.equal(component.count, 11);
}

@ -5,10 +5,10 @@ export default {
`,
async test({ assert, component, target, window }) {
const [updateButton, button] = target.querySelectorAll('button');
const [update_button, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await updateButton.dispatchEvent(event);
await update_button.dispatchEvent(event);
await button.dispatchEvent(event);
assert.equal(component.count, 10);

@ -3,7 +3,7 @@ export default {
ssrHtml: '<input value="Blub"> <input value="Blub"> <input value="Blub">',
async test({ assert, target, component, window }) {
const [input1, input2, inputFallback] = target.querySelectorAll('input');
const [input1, input2, input_fallback] = target.querySelectorAll('input');
assert.equal(component.getSubscriberCount(), 3);
@ -13,7 +13,7 @@ export default {
await input1.dispatchEvent(new window.Event('input'));
assert.equal(input1.value, 'ab');
assert.equal(input2.value, 'ab');
assert.equal(inputFallback.value, 'ab');
assert.equal(input_fallback.value, 'ab');
component.props = 'hello';

@ -6,9 +6,9 @@ export default {
async test({ assert, target, window }) {
const btn = target.querySelector('button');
const clickEvent = new window.MouseEvent('click');
const click_event = new window.MouseEvent('click');
await btn.dispatchEvent(clickEvent);
await btn.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -18,7 +18,7 @@ export default {
`
);
await btn.dispatchEvent(clickEvent);
await btn.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,

@ -1,6 +1,6 @@
let originalDivGetBoundingClientRect;
let originalSpanGetBoundingClientRect;
let originalParagraphGetBoundingClientRect;
let original_div_get_bounding_client_rect;
let original_span_get_bounding_client_rect;
let original_paragraph_get_bounding_client_rect;
export default {
skip_if_ssr: true,
@ -26,16 +26,16 @@ export default {
`,
before_test() {
originalDivGetBoundingClientRect = window.HTMLDivElement.prototype.getBoundingClientRect;
originalSpanGetBoundingClientRect = window.HTMLSpanElement.prototype.getBoundingClientRect;
originalParagraphGetBoundingClientRect =
original_div_get_bounding_client_rect = window.HTMLDivElement.prototype.getBoundingClientRect;
original_span_get_bounding_client_rect = window.HTMLSpanElement.prototype.getBoundingClientRect;
original_paragraph_get_bounding_client_rect =
window.HTMLParagraphElement.prototype.getBoundingClientRect;
window.HTMLDivElement.prototype.getBoundingClientRect = fakeGetBoundingClientRect;
window.HTMLSpanElement.prototype.getBoundingClientRect = fakeGetBoundingClientRect;
window.HTMLParagraphElement.prototype.getBoundingClientRect = fakeGetBoundingClientRect;
window.HTMLDivElement.prototype.getBoundingClientRect = fake_get_bounding_client_rect;
window.HTMLSpanElement.prototype.getBoundingClientRect = fake_get_bounding_client_rect;
window.HTMLParagraphElement.prototype.getBoundingClientRect = fake_get_bounding_client_rect;
function fakeGetBoundingClientRect() {
function fake_get_bounding_client_rect() {
const index = [...this.parentNode.children].indexOf(this);
const top = index * 30;
@ -48,10 +48,10 @@ export default {
}
},
after_test() {
window.HTMLDivElement.prototype.getBoundingClientRect = originalDivGetBoundingClientRect;
window.HTMLSpanElement.prototype.getBoundingClientRect = originalSpanGetBoundingClientRect;
window.HTMLDivElement.prototype.getBoundingClientRect = original_div_get_bounding_client_rect;
window.HTMLSpanElement.prototype.getBoundingClientRect = original_span_get_bounding_client_rect;
window.HTMLParagraphElement.prototype.getBoundingClientRect =
originalParagraphGetBoundingClientRect;
original_paragraph_get_bounding_client_rect;
},
async test({ assert, component, raf }) {

@ -0,0 +1,3 @@
export default {
html: '<div id="element" class="element-handler">this is div</div>'
};

@ -0,0 +1,7 @@
<script>
let props = {
id: "element",
class: "element-handler"
}
</script>
<svelte:element this={"div"} {...props}>this is div</svelte:element>

@ -13,10 +13,10 @@ export default {
assert.equal(input1.value, '');
assert.equal(input2.value, 'hello');
const inputEvent = new window.InputEvent('input');
const input_event = new window.InputEvent('input');
input2.value = 'world';
input2.dispatchEvent(inputEvent);
input2.dispatchEvent(input_event);
assert.equal(input2.value, 'world');
assert.equal(component.array[1].value, 'world');
}

@ -1,6 +1,6 @@
const VALUES = Array.from('abcdefghijklmnopqrstuvwxyz');
function toObjects(array) {
function to_objects(array) {
return array.split('').map((x) => ({ id: x }));
}
@ -17,7 +17,7 @@ function permute() {
export default {
get props() {
return { values: toObjects('abc') };
return { values: to_objects('abc') };
},
html: '(a)(b)(c)',
@ -29,7 +29,7 @@ export default {
.split('')
.map((x) => `(${x})`)
.join('');
component.values = toObjects(sequence);
component.values = to_objects(sequence);
assert.htmlEqual(
target.innerHTML,
expected,

@ -6,8 +6,8 @@ export default {
async test({ assert, target, window }) {
const button = target.querySelector('button');
const clickEvent = new window.MouseEvent('click');
await button.dispatchEvent(clickEvent);
const click_event = new window.MouseEvent('click');
await button.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,

@ -9,10 +9,10 @@ export default {
<button>Test</button>
`,
async test({ assert, target, window }) {
let [incrementBtn, ...buttons] = target.querySelectorAll('button');
let [increment_btn, ...buttons] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
await buttons[0].dispatchEvent(clickEvent);
const click_event = new window.MouseEvent('click');
await buttons[0].dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -27,7 +27,7 @@ export default {
`
);
await buttons[0].dispatchEvent(clickEvent);
await buttons[0].dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -42,8 +42,8 @@ export default {
`
);
await buttons[2].dispatchEvent(clickEvent);
await buttons[2].dispatchEvent(clickEvent);
await buttons[2].dispatchEvent(click_event);
await buttons[2].dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -58,7 +58,7 @@ export default {
`
);
await incrementBtn.dispatchEvent(clickEvent);
await increment_btn.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
@ -75,9 +75,9 @@ export default {
`
);
[incrementBtn, ...buttons] = target.querySelectorAll('button');
[increment_btn, ...buttons] = target.querySelectorAll('button');
await buttons[3].dispatchEvent(clickEvent);
await buttons[3].dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,

@ -9,7 +9,7 @@ export default {
`,
async test({ assert, target, window }) {
const [updateButton1, updateButton2, button] = target.querySelectorAll('button');
const [update_button1, update_button2, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
let err = '';
@ -32,7 +32,7 @@ export default {
`
);
await updateButton1.dispatchEvent(event);
await update_button1.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(
target.innerHTML,
@ -46,7 +46,7 @@ export default {
`
);
await updateButton2.dispatchEvent(event);
await update_button2.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(
target.innerHTML,

@ -4,7 +4,7 @@ export default {
<button>invalid</button>`,
async test({ assert, target, window }) {
const [buttonUndef, buttonNull, buttonInvalid] = target.querySelectorAll('button');
const [button_undef, button_null, button_invalid] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
let err = '';
@ -14,13 +14,13 @@ export default {
});
// All three should not throw if proper checking is done in runtime code
await buttonUndef.dispatchEvent(event);
await button_undef.dispatchEvent(event);
assert.equal(err, '', err);
await buttonNull.dispatchEvent(event);
await button_null.dispatchEvent(event);
assert.equal(err, '', err);
await buttonInvalid.dispatchEvent(event);
await button_invalid.dispatchEvent(event);
assert.equal(err, '', err);
}
};

@ -9,7 +9,7 @@ export default {
`,
async test({ assert, target, window }) {
const [updateButton1, updateButton2, button] = target.querySelectorAll('button');
const [update_button1, update_button2, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
let err = '';
@ -32,7 +32,7 @@ export default {
`
);
await updateButton1.dispatchEvent(event);
await update_button1.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(
target.innerHTML,
@ -46,7 +46,7 @@ export default {
`
);
await updateButton2.dispatchEvent(event);
await update_button2.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(
target.innerHTML,

@ -10,21 +10,21 @@ export default {
assert.equal(component.updated, 4);
const [item1, item2] = target.childNodes;
const [item1Btn1, item1Btn2] = item1.querySelectorAll('button');
const [item2Btn1, item2Btn2] = item2.querySelectorAll('button');
const [item1_btn1, item1_btn2] = item1.querySelectorAll('button');
const [item2_btn1, item2_btn2] = item2.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
const click_event = new window.MouseEvent('click');
await item1Btn1.dispatchEvent(clickEvent);
await item1_btn1.dispatchEvent(click_event);
assert.equal(component.getNormalCount(), 1);
await item1Btn2.dispatchEvent(clickEvent);
await item1_btn2.dispatchEvent(click_event);
assert.equal(component.getModifierCount(), 1);
await item2Btn1.dispatchEvent(clickEvent);
await item2_btn1.dispatchEvent(click_event);
assert.equal(component.getNormalCount(), 2);
await item2Btn2.dispatchEvent(clickEvent);
await item2_btn2.dispatchEvent(click_event);
assert.equal(component.getModifierCount(), 2);
}
};

@ -11,11 +11,11 @@ export default {
`,
test({ assert, component }) {
const visibleThings = component.visibleThings;
assert.deepEqual(visibleThings, ['first thing', 'second thing']);
const visible_things = component.visibleThings;
assert.deepEqual(visible_things, ['first thing', 'second thing']);
const snapshots = component.snapshots;
assert.deepEqual(snapshots, [visibleThings]);
assert.deepEqual(snapshots, [visible_things]);
// TODO minimise the number of recomputations during oncreate
// assert.equal(counter.count, 1);

@ -7,10 +7,14 @@ export default {
},
async test({ assert, target }) {
const firstSpanList = target.children[0];
assert.htmlEqualWithOptions(firstSpanList.innerHTML, expected, { withoutNormalizeHtml: true });
const first_span_list = target.children[0];
assert.htmlEqualWithOptions(first_span_list.innerHTML, expected, {
withoutNormalizeHtml: true
});
const secondSpanList = target.children[1];
assert.htmlEqualWithOptions(secondSpanList.innerHTML, expected, { withoutNormalizeHtml: true });
const second_span_list = target.children[1];
assert.htmlEqualWithOptions(second_span_list.innerHTML, expected, {
withoutNormalizeHtml: true
});
}
};

@ -2,9 +2,9 @@ export default {
async test({ assert, target, window }) {
const [btn1, btn2] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
const click_event = new window.MouseEvent('click');
await btn2.dispatchEvent(clickEvent);
await btn2.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
`
@ -17,7 +17,7 @@ export default {
`
);
await btn1.dispatchEvent(clickEvent);
await btn1.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
`
@ -30,7 +30,7 @@ export default {
`
);
await btn2.dispatchEvent(clickEvent);
await btn2.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
`
@ -43,7 +43,7 @@ export default {
`
);
await btn1.dispatchEvent(clickEvent);
await btn1.dispatchEvent(click_event);
assert.htmlEqual(
target.innerHTML,
`

@ -0,0 +1,20 @@
export default {
html: `
<p style="background-color: green; font-size: 12px;"></p>
`,
test({ assert, target, window, component }) {
const p = target.querySelector('p');
const styles = window.getComputedStyle(p);
assert.equal(styles.backgroundColor, 'green');
assert.equal(styles.fontSize, '12px');
{
component.modify = true;
const p = target.querySelector('p');
const styles = window.getComputedStyle(p);
assert.equal(styles.backgroundColor, 'green');
assert.equal(styles.fontSize, '50px');
}
}
};

@ -0,0 +1,12 @@
<script>
let settings = {
fontSize: 12,
bg: 'green'
};
export let modify = false;
$: if (modify) {
settings.fontSize = 50;
}
</script>
<p style:font-size="{settings.fontSize}px" style="background-color: {settings.bg}" />

@ -4,14 +4,14 @@ export default {
<div>&nbsp;hello&nbsp; &nbsp;hello</div>`,
test({ assert, target }) {
const divList = target.querySelectorAll('div');
assert.equal(divList[0].textContent.charCodeAt(0), 160);
assert.equal(divList[1].textContent.charCodeAt(0), 160);
assert.equal(divList[1].textContent.charCodeAt(6), 160);
assert.equal(divList[1].textContent.charCodeAt(7), 160);
assert.equal(divList[2].textContent.charCodeAt(0), 160);
assert.equal(divList[2].textContent.charCodeAt(6), 160);
assert.equal(divList[2].textContent.charCodeAt(7), 32); //normal space
assert.equal(divList[2].textContent.charCodeAt(8), 160);
const div_list = target.querySelectorAll('div');
assert.equal(div_list[0].textContent.charCodeAt(0), 160);
assert.equal(div_list[1].textContent.charCodeAt(0), 160);
assert.equal(div_list[1].textContent.charCodeAt(6), 160);
assert.equal(div_list[1].textContent.charCodeAt(7), 160);
assert.equal(div_list[2].textContent.charCodeAt(0), 160);
assert.equal(div_list[2].textContent.charCodeAt(6), 160);
assert.equal(div_list[2].textContent.charCodeAt(7), 32); //normal space
assert.equal(div_list[2].textContent.charCodeAt(8), 160);
}
};

@ -14,7 +14,7 @@ export default {
// it's okay not to remove the node during hydration
// will not be seen by user anyway
removeNoScript(target);
remove_no_script(target);
assert.htmlEqual(
target.innerHTML,
@ -26,7 +26,7 @@ export default {
}
};
function removeNoScript(target) {
function remove_no_script(target) {
target.querySelectorAll('noscript').forEach((elem) => {
elem.parentNode.removeChild(elem);
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save