Merge branch 'master' into master

pull/4272/head
pushkin 6 years ago committed by GitHub
commit 63217887f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -23,3 +23,9 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- run: 'npm i && npm run lint'
Unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- run: 'npm i && npm run test:unit'

@ -1,5 +1,37 @@
# Svelte changelog
## Unreleased
* Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325))
## 3.18.1
* Fix code generation error with adjacent inline and block comments ([#4312](https://github.com/sveltejs/svelte/issues/4312))
* Fix detection of unused CSS selectors that begin with a `:global()` but contain a scoped portion ([#4314](https://github.com/sveltejs/svelte/issues/4314))
## 3.18.0
* Fix infinite loop when instantiating another component during `onMount` ([#3218](https://github.com/sveltejs/svelte/issues/3218))
* Make autosubscribing to a nullish store a no-op ([#2181](https://github.com/sveltejs/svelte/issues/2181))
## 3.17.3
* Fix updating a `<slot>` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292))
* Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298))
* Add dev mode check to disallow duplicate keys in a keyed `{#each}` ([#4301](https://github.com/sveltejs/svelte/issues/4301))
* Fix hydration of `<title>` when starting from SSR-generated code with `hydratable: true` ([#4310](https://github.com/sveltejs/svelte/issues/4310))
## 3.17.2
* Fix removing attributes during hydration ([#1733](https://github.com/sveltejs/svelte/issues/1733))
* Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012))
* Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173))
* Fix `~=` and class selector matching against values separated by any whitespace characters ([#4242](https://github.com/sveltejs/svelte/issues/4242))
* Fix code generation for `await`ed expressions that need parentheses ([#4267](https://github.com/sveltejs/svelte/issues/4267))
* Preserve JavaScript comments from the original component source where possible ([#4268](https://github.com/sveltejs/svelte/issues/4268))
* Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276))
* Correctly apply event modifiers to `<svelte:body>` events ([#4278](https://github.com/sveltejs/svelte/issues/4278))
## 3.17.1
* Only attach SSR mode markers to a component's `<head>` elements when compiling with `hydratable: true` ([#4258](https://github.com/sveltejs/svelte/issues/4258))

@ -7,10 +7,6 @@
<img src="https://img.shields.io/npm/v/svelte.svg" alt="npm version">
</a>
<a href="https://packagephobia.now.sh/result?p=svelte">
<img src="https://packagephobia.now.sh/badge?p=svelte" alt="install size">
</a>
<a href="https://github.com/sveltejs/svelte/actions">
<img src="https://github.com/sveltejs/svelte/workflows/CI/badge.svg?branch=master"
alt="build status">

8
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.17.1",
"version": "3.18.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -597,9 +597,9 @@
"dev": true
},
"code-red": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.28.tgz",
"integrity": "sha512-k9L7Sp85HNt3f/CvfrZKXoZOaO0tWOJCw2kU5GKc/c5pDj52OgBa0J+krMRmYtnGzS2dk4Xrn0EjjsJaM51hWQ==",
"version": "0.0.32",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.32.tgz",
"integrity": "sha512-mE+EZc2vJ4HxiejW5S2CvcVDKtopFEmrqAd9DTBDLCNjLgxekPP8wLi/ZiwDTwZwwW3dzeetaubLaMlIvkhVNw==",
"dev": true,
"requires": {
"acorn": "^7.1.0",

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.17.1",
"version": "3.18.1",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
@ -70,7 +70,7 @@
"acorn": "^7.1.0",
"agadoo": "^1.1.0",
"c8": "^5.0.1",
"code-red": "0.0.28",
"code-red": "0.0.32",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",

@ -1,3 +1,6 @@
# IMPORTANT: Don't use this Dockerfile in your own Sapper projects without also looking at the .dockerignore file.
# Without an appropriate .dockerignore, this Dockerfile will copy a large number of unneeded files into your image.
FROM mhart/alpine-node:12
# install dependencies

@ -14,9 +14,9 @@ sapper:
docker:
@echo "\n~> building docker image"
@gcloud builds submit -t $(IMAGE)
@gcloud builds submit --project $(PROJECT) -t $(IMAGE)
deploy: sapper docker
@echo "\n~> deploying $(SERVICE) to Cloud Run servers"
@gcloud beta run deploy $(SERVICE) --allow-unauthenticated --platform managed --region us-central1 --image $(IMAGE) --memory=512Mi
@gcloud run deploy $(SERVICE) --project $(PROJECT) --allow-unauthenticated --platform managed --region us-central1 --image $(IMAGE) --memory=512Mi

@ -159,6 +159,6 @@ In Vue, meanwhile, we have a default export with a `data` function that returns
## Death to boilerplate
These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](https://svelte.dev/tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change).
These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change).
How? By choosing a different set of constraints. Because [Svelte is a compiler](blog/frameworks-without-the-framework), we're not bound to the peculiarities of JavaScript: we can *design* a component authoring experience, rather than having to fit it around the semantics of the language. Paradoxically, this results in *more* idiomatic code — for example using variables naturally rather than via proxies or hooks — while delivering significantly more performant apps.

@ -2,8 +2,8 @@
title: Before we begin
---
> Temporary note: This document is a work-in-progress. Please forgive any missing or misleading parts, and don't be shy about asking for help in the [Discord chatroom](chat). The [tutorial](tutorial) is more complete; start there.
This page contains detailed API reference documentation. It's intended to be a resource for people who already have some familiarity with Svelte.
If that's not you (yet), you may prefer to visit the [interactive tutorial](tutorial) or the [examples](examples) before consulting this reference.
Don't be shy about asking for help in the [Discord chatroom](chat).

@ -197,7 +197,7 @@ You can `export` bindings from this block, and they will become exports of the c
You cannot `export default`, since the default export is the component itself.
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](https://svelte.dev/docs#svelte_store).
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](docs#svelte_store).
```html
<script context="module">

@ -76,7 +76,7 @@ The following options can be passed to the compiler. None are required:
| `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance.
| `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`**
| `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out.
| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than optimised by Svelte.
| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than removed or collapsed to a single space where possible.
| `outputFilename` | `null` | A `string` used for your JavaScript sourcemap.
| `cssOutputFilename` | `null` | A `string` used for your CSS sourcemap.
| `sveltePath` | `"svelte"` | The location of the `svelte` package. Any imports from `svelte` or `svelte/[module]` will be modified accordingly.

@ -11,7 +11,7 @@
<svelte:head>
<title>Blog • Svelte</title>
<link rel="alternate" type="application/rss+xml" title="Svelte blog" href="blog/rss.xml">
<link rel="alternate" type="application/rss+xml" title="Svelte blog" href="https://svelte.dev/blog/rss.xml">
<meta name="twitter:title" content="Svelte blog">
<meta name="twitter:description" content="Articles about Svelte and UI development">

@ -15,6 +15,7 @@
<meta name='twitter:site' content='@sveltejs'>
<meta name='twitter:creator' content='@sveltejs'>
<meta name='twitter:image' content='https://svelte.dev/images/twitter-card.png'>
<meta name='og:image' content='https://svelte.dev/images/twitter-card.png'>
<!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is

@ -32,7 +32,7 @@ export default class Selector {
}
this.local_blocks = this.blocks.slice(0, i);
this.used = this.blocks[0].global;
this.used = this.local_blocks.length === 0;
}
apply(node: Element, stack: Element[]) {
@ -171,7 +171,7 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
if (ancestor_block.global) {
continue;
}
for (const stack_node of stack) {
if (block_might_apply_to_node(ancestor_block, stack_node) !== BlockAppliesToNode.NotPossible) {
to_encapsulate.push({ node: stack_node, block: ancestor_block });
@ -256,7 +256,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
}
switch (operator) {
case '=': return value === expected_value;
case '~=': return ` ${value} `.includes(` ${expected_value} `);
case '~=': return value.split(/\s/).includes(expected_value);
case '|=': return `${value}-`.startsWith(`${expected_value}-`);
case '^=': return value.startsWith(expected_value);
case '$=': return value.endsWith(expected_value);
@ -295,7 +295,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
// impossible to find out all combinations
if (current_possible_values.has(UNKNOWN)) return true;
if (prev_values.length > 0) {
const start_with_space = [];
const remaining = [];

@ -50,6 +50,13 @@ export default class Binding extends Node {
message: 'Cannot bind to a variable declared with the let: directive'
});
} else if (this.is_contextual) {
if (scope.is_await(name)) {
component.error(this, {
code: 'invalid-binding',
message: 'Cannot bind to a variable declared with {#await ... then} or {:catch} blocks'
});
}
scope.dependencies_for_name.get(name).forEach(name => {
const variable = component.var_lookup.get(name);
if (variable) {

@ -151,6 +151,11 @@ export default class Element extends Node {
}
}
const has_let = info.attributes.some(node => node.type === 'Let');
if (has_let) {
scope = scope.child();
}
// Binding relies on Attribute, defer its evaluation
const order = ['Binding']; // everything else is -1
info.attributes.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type));
@ -181,9 +186,16 @@ export default class Element extends Node {
this.handlers.push(new EventHandler(component, this, scope, node));
break;
case 'Let':
this.lets.push(new Let(component, this, scope, node));
case 'Let': {
const l = new Let(component, this, scope, node);
this.lets.push(l);
const dependencies = new Set([l.name.name]);
l.names.forEach(name => {
scope.add(name, dependencies, this);
});
break;
}
case 'Transition':
{
@ -202,20 +214,7 @@ export default class Element extends Node {
}
});
if (this.lets.length > 0) {
this.scope = scope.child();
this.lets.forEach(l => {
const dependencies = new Set([l.name.name]);
l.names.forEach(name => {
this.scope.add(name, dependencies, this);
});
});
} else {
this.scope = scope;
}
this.scope = scope;
this.children = map_children(component, this, this.scope, info.children);
this.validate();

@ -42,4 +42,9 @@ export default class TemplateScope {
const owner = this.get_owner(name);
return owner && (owner.type === 'Element' || owner.type === 'InlineComponent');
}
is_await(name: string) {
const owner = this.get_owner(name);
return owner && (owner.type === 'ThenBlock' || owner.type === 'CatchBlock');
}
}

@ -160,6 +160,9 @@ export default class Block {
});
this.has_update_method = true;
if (this.parent) {
this.parent.add_dependencies(dependencies);
}
}
add_element(

@ -1,26 +1,23 @@
import Block from '../Block';
import Wrapper from './shared/Wrapper';
import { b } from 'code-red';
import { x } from 'code-red';
import Body from '../../nodes/Body';
import { Identifier } from 'estree';
import EventHandler from './Element/EventHandler';
import add_event_handlers from './shared/add_event_handlers';
import { TemplateNode } from '../../../interfaces';
import Renderer from '../Renderer';
export default class BodyWrapper extends Wrapper {
node: Body;
handlers: EventHandler[];
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
this.node.handlers
.map(handler => new EventHandler(handler, this))
.forEach(handler => {
const snippet = handler.get_snippet(block);
block.chunks.init.push(b`
@_document.body.addEventListener("${handler.node.name}", ${snippet});
`);
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
super(renderer, block, parent, node);
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
}
block.chunks.destroy.push(b`
@_document.body.removeEventListener("${handler.node.name}", ${snippet});
`);
});
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
add_event_handlers(block, x`@_document.body`, this.handlers);
}
}

@ -374,6 +374,7 @@ export default class EachBlockWrapper extends Wrapper {
block.chunks.init.push(b`
const ${get_key} = #ctx => ${this.node.key.manipulate(block)};
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
for (let #i = 0; #i < ${data_length}; #i += 1) {
let child_ctx = ${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx);
@ -416,6 +417,7 @@ export default class EachBlockWrapper extends Wrapper {
${this.block.has_outros && b`@group_outros();`}
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context});
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`}
${this.block.has_outros && b`@check_outros();`}

@ -2,6 +2,7 @@ import EventHandler from '../../../nodes/EventHandler';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import { b, x, p } from 'code-red';
import { Expression } from 'estree';
const TRUE = x`true`;
const FALSE = x`false`;
@ -35,7 +36,7 @@ export default class EventHandlerWrapper {
return snippet;
}
render(block: Block, target: string) {
render(block: Block, target: string | Expression) {
let snippet = this.get_snippet(block);
if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;

@ -1,9 +1,10 @@
import Block from '../../Block';
import EventHandler from '../Element/EventHandler';
import { Expression } from 'estree';
export default function add_event_handlers(
block: Block,
target: string,
target: string | Expression,
handlers: EventHandler[]
) {
handlers.forEach(handler => add_event_handler(block, target, handler));
@ -11,7 +12,7 @@ export default function add_event_handlers(
export function add_event_handler(
block: Block,
target: string,
target: string | Expression,
handler: EventHandler
) {
handler.render(block, target);

@ -5,11 +5,7 @@ import { x } from 'code-red';
export default function(node: Title, renderer: Renderer, options: RenderOptions) {
renderer.push();
renderer.add_string('<title');
if (options.hydratable && options.head_id) {
renderer.add_string(` data-svelte="${options.head_id}"`);
}
renderer.add_string('>');
renderer.add_string(`<title>`);
renderer.render(node.children, options);

@ -10,7 +10,7 @@ describe('get_name_from_filename', () => {
assert.equal(get_name_from_filename('path/to/Widget/index.svelte'), 'Widget');
});
it('handles unusual filenames', () => {
assert.equal(get_name_from_filename('path/to/[...parts].svelte'), 'Parts');
it('handles Windows filenames', () => {
assert.equal(get_name_from_filename('path\\to\\Widget.svelte'), 'Widget');
});
});

@ -1,14 +1,13 @@
import * as acorn from 'acorn';
import { Node } from 'acorn';
import * as code_red from 'code-red';
const Parser = acorn.Parser;
export const parse = (source: string) => Parser.parse(source, {
export const parse = (source: string): Node => code_red.parse(source, {
sourceType: 'module',
ecmaVersion: 11,
locations: true
});
export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
export const parse_expression_at = (source: string, index: number): Node => code_red.parseExpressionAt(source, index, {
ecmaVersion: 11,
locations: true
});

@ -288,6 +288,16 @@ function read_tag_name(parser: Parser) {
function read_attribute(parser: Parser, unique_names: Set<string>) {
const start = parser.index;
function check_unique(name: string) {
if (unique_names.has(name)) {
parser.error({
code: `duplicate-attribute`,
message: 'Attributes need to be unique'
}, start);
}
unique_names.add(name);
}
if (parser.eat('{')) {
parser.allow_whitespace();
@ -310,6 +320,8 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
parser.allow_whitespace();
parser.eat('}', true);
check_unique(name);
return {
start,
end: parser.index,
@ -341,17 +353,6 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
const colon_index = name.indexOf(':');
const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
if (unique_names.has(name)) {
parser.error({
code: `duplicate-attribute`,
message: 'Attributes need to be unique'
}, start);
}
if (type !== "EventHandler") {
unique_names.add(name);
}
let value: any[] | true = true;
if (parser.eat('=')) {
parser.allow_whitespace();
@ -367,6 +368,12 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
if (type) {
const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
if (type === 'Binding' && directive_name !== 'this') {
check_unique(directive_name);
} else if (type !== 'EventHandler') {
check_unique(name);
}
if (type === 'Ref') {
parser.error({
code: `invalid-ref-directive`,
@ -410,6 +417,8 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
return directive;
}
check_unique(name);
return {
start,
end,

@ -5,6 +5,8 @@ export const globals = new Set([
'alert',
'Array',
'Boolean',
'clearInterval',
'clearTimeout',
'confirm',
'console',
'Date',
@ -16,6 +18,9 @@ export const globals = new Set([
'Error',
'EvalError',
'Event',
'fetch',
'global',
'globalThis',
'history',
'Infinity',
'InternalError',
@ -41,11 +46,14 @@ export const globals = new Set([
'RegExp',
'sessionStorage',
'Set',
'setInterval',
'setTimeout',
'String',
'SyntaxError',
'TypeError',
'undefined',
'URIError',
'URL',
'window'
]);

@ -152,11 +152,16 @@ export function claim_element(nodes, name, attributes, svg) {
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.nodeName === name) {
for (let j = 0; j < node.attributes.length; j += 1) {
let j = 0;
while (j < node.attributes.length) {
const attribute = node.attributes[j];
if (!attributes[attribute.name]) node.removeAttribute(attribute.name);
if (attributes[attribute.name]) {
j++;
} else {
node.removeAttribute(attribute.name);
}
}
return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes
return nodes.splice(i, 1)[0];
}
}

@ -108,9 +108,13 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
return new_blocks;
}
export function measure(blocks) {
const rects = {};
let i = blocks.length;
while (i--) rects[blocks[i].key] = blocks[i].node.getBoundingClientRect();
return rects;
export function validate_each_keys(ctx, list, get_context, get_key) {
const keys = new Set();
for (let i = 0; i < list.length; i++) {
const key = get_key(get_context(ctx, list, i));
if (keys.has(key)) {
throw new Error(`Cannot have duplicate keys in a keyed each`);
}
keys.add(key);
}
}

@ -31,8 +31,8 @@ export function add_flush_callback(fn) {
flush_callbacks.push(fn);
}
const seen_callbacks = new Set();
export function flush() {
const seen_callbacks = new Set();
do {
// first, call beforeUpdate functions
@ -52,10 +52,10 @@ export function flush() {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
callback();
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
@ -67,6 +67,7 @@ export function flush() {
}
update_scheduled = false;
seen_callbacks.clear();
}
function update($$) {

@ -43,13 +43,16 @@ export function not_equal(a, b) {
}
export function validate_store(store, name) {
if (!store || typeof store.subscribe !== 'function') {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
export function subscribe(store, callback) {
const unsub = store.subscribe(callback);
export function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}

@ -1,4 +1,4 @@
import { run_all, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal';
import { run_all, subscribe, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal';
/** Callback to inform of a value updates. */
type Subscriber<T> = (value: T) => void;
@ -173,7 +173,8 @@ export function derived<T>(stores: Stores, fn: Function, initial_value?: T): Rea
}
};
const unsubscribers = stores_array.map((store, i) => store.subscribe(
const unsubscribers = stores_array.map((store, i) => subscribe(
store,
(value) => {
values[i] = value;
pending &= ~(1 << i);

@ -0,0 +1 @@
.foo.svelte-xyz{color:red}[class~="bar"].svelte-xyz{background:blue}

@ -0,0 +1,13 @@
<div class="
foo
bar
"></div>
<style>
.foo {
color: red;
}
[class~="bar"] {
background: blue;
}
</style>

@ -0,0 +1,24 @@
export default {
warnings: [{
code: 'css-unused-selector',
end: {
character: 27,
column: 19,
line: 2
},
frame: `
1: <style>
2: :global(.foo) .bar {
^
3: color: red;
4: }
`,
message: 'Unused CSS selector',
pos: 9,
start: {
character: 9,
column: 1,
line: 2
}
}]
};

@ -0,0 +1,5 @@
<style>
:global(.foo) .bar {
color: red;
}
</style>

@ -1 +1 @@
<div class='foo'></div>
<div class='foo' title='bar'></div>

@ -1,4 +1,4 @@
<title data-svelte="svelte-1s8aodm">Some Title</title>
<title>Some Title</title>
<link rel="canonical" href="/" data-svelte="svelte-1s8aodm">
<meta name="description" content="some description" data-svelte="svelte-1s8aodm">
<meta name="keywords" content="some keywords" data-svelte="svelte-1s8aodm">

@ -43,7 +43,7 @@ function handleFoo(bar) {
function foo(node, callback) {
}
} // code goes here
function instance($$self, $$props, $$invalidate) {
let { bar } = $$props;

@ -63,4 +63,4 @@ class Component extends SvelteComponent {
}
}
export default Component;
export default Component;

@ -46,4 +46,4 @@ class Component extends SvelteComponent {
}
}
export default Component;
export default Component;

@ -103,7 +103,7 @@ class Component extends SvelteComponentDev {
});
const { ctx } = this.$$;
const props = options.props || ({});
const props = options.props || {};
if (/*name*/ ctx[0] === undefined && !("name" in props)) {
console.warn("<Component> was created without expected prop 'name'");

@ -210,7 +210,7 @@ class Component extends SvelteComponentDev {
});
const { ctx } = this.$$;
const props = options.props || ({});
const props = options.props || {};
if (/*things*/ ctx[0] === undefined && !("things" in props)) {
console.warn("<Component> was created without expected prop 'things'");

@ -198,7 +198,7 @@ class Component extends SvelteComponentDev {
});
const { ctx } = this.$$;
const props = options.props || ({});
const props = options.props || {};
if (/*things*/ ctx[0] === undefined && !("things" in props)) {
console.warn("<Component> was created without expected prop 'things'");

@ -107,7 +107,7 @@ class Component extends SvelteComponentDev {
});
const { ctx } = this.$$;
const props = options.props || ({});
const props = options.props || {};
if (/*foo*/ ctx[0] === undefined && !("foo" in props)) {
console.warn("<Component> was created without expected prop 'foo'");

@ -63,11 +63,11 @@ function create_fragment(ctx) {
function handleTouchstart() {
}
} // ...
function handleClick() {
}
} // ...
class Component extends SvelteComponent {
constructor(options) {

@ -13,7 +13,7 @@ function foo() {
function swipe(node, callback) {
}
} // TODO implement
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
onMount(() => {

@ -0,0 +1,10 @@
{
"code": "duplicate-attribute",
"message": "Attributes need to be unique",
"start": {
"line": 1,
"column": 17,
"character": 17
},
"pos": 17
}

@ -0,0 +1,10 @@
{
"code": "duplicate-attribute",
"message": "Attributes need to be unique",
"start": {
"line": 1,
"column": 17,
"character": 17
},
"pos": 17
}

@ -41,7 +41,15 @@
}
},
"body": [],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Line",
"value": " TODO write some code",
"start": 10,
"end": 33
}
]
}
}
}

@ -134,7 +134,15 @@
"kind": "let"
}
],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Block",
"value": "\n\ttrailing multiline comment\n",
"start": 32,
"end": 67
}
]
}
}
}

@ -134,7 +134,15 @@
"kind": "let"
}
],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Line",
"value": " trailing line comment",
"start": 32,
"end": 56
}
]
}
}
}

@ -0,0 +1,5 @@
<script>
export let x;
</script>
<slot name="foo" reflected={x}/>

@ -0,0 +1,22 @@
export default {
html: `
<span slot="foo" class="1">1</span>
0
`,
async test({ assert, target, component, window }) {
component.x = 2;
assert.htmlEqual(target.innerHTML, `
<span slot="foo" class="2">2</span>
0
`);
const span = target.querySelector('span');
await span.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<span slot="foo" class="2">2</span>
2
`);
}
};

@ -0,0 +1,17 @@
<script>
import A from './A.svelte';
export let x = 1;
let y = 0;
</script>
<A {x}>
<span
on:click={() => y = reflected}
slot="foo"
let:reflected
class={reflected}
>
{reflected}
</span>
</A>
{ y }

@ -0,0 +1,6 @@
<script>
let val;
</script>
<input bind:value={val} />
<slot {val}></slot>

@ -0,0 +1,30 @@
export default {
html: `
<input>
`,
async test({ assert, target, snapshot, component, window }) {
const input = target.querySelector('input');
input.value = 'a';
await input.dispatchEvent(new window.Event('input'));
assert.htmlEqual(
target.innerHTML,
`
<input>
Display: a
`
);
input.value = 'abc';
await input.dispatchEvent(new window.Event('input'));
assert.htmlEqual(
target.innerHTML,
`
<input>
Display: abc
`
);
},
};

@ -0,0 +1,10 @@
<script>
import Input from "./Input.svelte";
import Display from "./Display.svelte";
</script>
<Input let:val={foo}>
{#if foo}
<Display>{foo}</Display>
{/if}
</Input>

@ -0,0 +1,11 @@
export default {
async test({ assert, component, window }) {
const event = new window.MouseEvent('click');
await window.document.body.dispatchEvent(event);
assert.equal(component.count, 1);
await window.document.body.dispatchEvent(event);
assert.equal(component.count, 1);
}
};

@ -0,0 +1,5 @@
<script>
export let count = 0;
</script>
<svelte:body on:click|once="{() => count += 1}"/>

@ -0,0 +1,7 @@
export default {
compileOptions: {
dev: true
},
error: `Cannot have duplicate keys in a keyed each`
};

@ -0,0 +1,7 @@
<script>
const array = [1, 2, 3, 1];
</script>
{#each array as item (item)}
{item}
{/each}

@ -0,0 +1,6 @@
export default {
test({ assert, component }) {
const { count } = component;
assert.deepEqual(count, 1);
}
};

@ -0,0 +1,16 @@
<script>
import { onMount } from 'svelte';
import Child from './Child.svelte';
let root;
export let count = 0;
onMount(() => {
if (count < 5) {
count++;
new Child({ target: root });
}
});
</script>
<div bind:this={root}></div>

@ -0,0 +1,13 @@
import { writable } from '../../../../store';
export default {
html: `
<p>undefined</p>
`,
async test({ assert, component, target }) {
component.store = writable('foo');
assert.htmlEqual(target.innerHTML, `
<p>foo</p>
`);
}
};

@ -0,0 +1,5 @@
<script>
export let store;
</script>
<p>{$store}</p>

@ -1,4 +1,4 @@
<title data-svelte="svelte-1s8aodm">Some Title</title>
<title>Some Title</title>
<link rel="canonical" href="/" data-svelte="svelte-1s8aodm">
<meta name="description" content="some description" data-svelte="svelte-1s8aodm">
<meta name="keywords" content="some keywords" data-svelte="svelte-1s8aodm">

@ -116,6 +116,15 @@ describe('store', () => {
});
});
const fake_observable = {
subscribe(fn) {
fn(42);
return {
unsubscribe: () => {}
};
}
};
describe('derived', () => {
it('maps a single store', () => {
const a = writable(1);
@ -346,6 +355,11 @@ describe('store', () => {
b.set(2);
assert.deepEqual(get(c), 'two 2');
});
it('works with RxJS-style observables', () => {
const d = derived(fake_observable, _ => _);
assert.equal(get(d), 42);
});
});
describe('get', () => {
@ -355,16 +369,7 @@ describe('store', () => {
});
it('works with RxJS-style observables', () => {
const observable = {
subscribe(fn) {
fn(42);
return {
unsubscribe: () => {}
};
}
};
assert.equal(get(observable), 42);
assert.equal(get(fake_observable), 42);
});
});
});

@ -0,0 +1,9 @@
[
{
"code": "invalid-binding",
"message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks",
"pos": 79,
"start": { "line": 6, "column": 9, "character": 79 },
"end": { "line": 6, "column": 27, "character": 97 }
}
]

@ -0,0 +1,7 @@
<script>
let promise = 0;
</script>
{#await promise}
{:catch error}
<input bind:value={error} />
{/await}

@ -0,0 +1,9 @@
[
{
"code": "invalid-binding",
"message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks",
"pos": 78,
"start": { "line": 6, "column": 9, "character": 78 },
"end": { "line": 6, "column": 19, "character": 88 }
}
]

@ -0,0 +1,7 @@
<script>
let promise = 0;
</script>
{#await promise}
{:then value}
<input bind:value />
{/await}

@ -0,0 +1,9 @@
[
{
"code": "invalid-binding",
"message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks",
"pos": 75,
"start": { "line": 5, "column": 9, "character": 75 },
"end": { "line": 5, "column": 19, "character": 85 }
}
]

@ -0,0 +1,6 @@
<script>
let promise = 0;
</script>
{#await promise then value}
<input bind:value />
{/await}
Loading…
Cancel
Save