Merge pull request #3 from sveltejs/master

Sync Fork from Upstream Repo
pull/3941/head
Stefan Hagen 6 years ago committed by GitHub
commit c289c491b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

37
package-lock.json generated

@ -30,6 +30,16 @@
"integrity": "sha512-KioOCsSvSvXx6xUNLiJz+P+VMb7NRcePjoefOr74Y5P6lEKsiOn35eZyZzgpK4XCNJdXTDR7+zykj0lwxRvZ2g==", "integrity": "sha512-KioOCsSvSvXx6xUNLiJz+P+VMb7NRcePjoefOr74Y5P6lEKsiOn35eZyZzgpK4XCNJdXTDR7+zykj0lwxRvZ2g==",
"dev": true "dev": true
}, },
"@rollup/plugin-replace": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.2.1.tgz",
"integrity": "sha512-dgq5ijT8fK18KTb1inenZ61ivTayV7pvbz2+ivT+VN20BOgJVM1fqoBETqGHKgFVm/J9BhR82mQyAtxfpPv1lQ==",
"dev": true,
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
}
},
"@types/eslint-visitor-keys": { "@types/eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -499,6 +509,17 @@
"is-reference": "^1.1.4", "is-reference": "^1.1.4",
"periscopic": "^1.0.2", "periscopic": "^1.0.2",
"sourcemap-codec": "^1.4.6" "sourcemap-codec": "^1.4.6"
},
"dependencies": {
"periscopic": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.1.0.tgz",
"integrity": "sha512-sUdDgd8G35JjpBqHGnuc2MECoyUryHGfjtsKFPS6N8MEGHtxoIML8yEWydL1zf+W8EoChX4L7A9AvVRJuM6Lqg==",
"dev": true,
"requires": {
"is-reference": "^1.1.4"
}
}
} }
}, },
"codecov": { "codecov": {
@ -2713,9 +2734,9 @@
"dev": true "dev": true
}, },
"periscopic": { "periscopic": {
"version": "1.0.2", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.0.2.tgz", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.0.tgz",
"integrity": "sha512-KpKBKadLf8THXOxswQBhOY8E1lVVhfUidacPtQBrq7KDXaNkQLUPiTmXagzqpJGECP3/0gDXYFO6CZHVbGvOSw==", "integrity": "sha512-2YVtztswd6ud5b0+IDD26UhEm1QvlTsR3s7G59CD0txGyhfvQ39YhNuueSmzT5VzHUxiIH0E8S04JSQpXMJ8/g==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-reference": "^1.1.4" "is-reference": "^1.1.4"
@ -3109,16 +3130,6 @@
"rollup-pluginutils": "^2.8.1" "rollup-pluginutils": "^2.8.1"
} }
}, },
"rollup-plugin-replace": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz",
"integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==",
"dev": true,
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
}
},
"rollup-plugin-sucrase": { "rollup-plugin-sucrase": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-sucrase/-/rollup-plugin-sucrase-2.1.0.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-sucrase/-/rollup-plugin-sucrase-2.1.0.tgz",

@ -60,6 +60,7 @@
"@types/node": "^8.10.53", "@types/node": "^8.10.53",
"@typescript-eslint/eslint-plugin": "^1.13.0", "@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^2.1.0", "@typescript-eslint/parser": "^2.1.0",
"@rollup/plugin-replace": "^2.2.1",
"acorn": "^7.1.0", "acorn": "^7.1.0",
"agadoo": "^1.1.0", "agadoo": "^1.1.0",
"c8": "^5.0.1", "c8": "^5.0.1",
@ -76,12 +77,12 @@
"locate-character": "^2.0.5", "locate-character": "^2.0.5",
"magic-string": "^0.25.3", "magic-string": "^0.25.3",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"periscopic": "^2.0.0",
"puppeteer": "^1.19.0", "puppeteer": "^1.19.0",
"rollup": "^1.21.4", "rollup": "^1.21.4",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-json": "^4.0.0", "rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-sucrase": "^2.1.0", "rollup-plugin-sucrase": "^2.1.0",
"rollup-plugin-typescript": "^1.0.1", "rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-virtual": "^1.0.1", "rollup-plugin-virtual": "^1.0.1",

@ -1,5 +1,5 @@
import fs from 'fs'; import fs from 'fs';
import replace from 'rollup-plugin-replace'; import replace from '@rollup/plugin-replace';
import resolve from 'rollup-plugin-node-resolve'; import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'; import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json'; import json from 'rollup-plugin-json';

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 103 124">
<path style="fill:black" d='M96.33,20.61C85.38,4.93,63.74.28,48.09,10.25L20.61,27.77A31.46,31.46,0,0,0,6.37,48.88,33.22,33.22,0,0,0,9.64,70.2,31.52,31.52,0,0,0,4.93,82a33.61,33.61,0,0,0,5.73,25.41c11,15.68,32.6,20.33,48.25,10.36l27.48-17.52a31.48,31.48,0,0,0,14.24-21.11A33.22,33.22,0,0,0,97.36,57.8,31.52,31.52,0,0,0,102.07,46a33.57,33.57,0,0,0-5.74-25.41
M45.41,108.86A21.81,21.81,0,0,1,22,100.18,20.2,20.2,0,0,1,18.53,84.9a19,19,0,0,1,.65-2.57l.52-1.58,1.41,1a35.32,35.32,0,0,0,10.75,5.37l1,.31-.1,1a6.2,6.2,0,0,0,1.11,4.08A6.57,6.57,0,0,0,41,95.19a6,6,0,0,0,1.68-.74L70.11,76.94a5.76,5.76,0,0,0,2.59-3.83,6.09,6.09,0,0,0-1-4.6,6.58,6.58,0,0,0-7.06-2.62,6.21,6.21,0,0,0-1.69.74L52.43,73.31a19.88,19.88,0,0,1-5.58,2.45,21.82,21.82,0,0,1-23.43-8.68A20.2,20.2,0,0,1,20,51.8a19,19,0,0,1,8.56-12.7L56,21.59a19.88,19.88,0,0,1,5.58-2.45A21.81,21.81,0,0,1,85,27.82,20.2,20.2,0,0,1,88.47,43.1a19,19,0,0,1-.65,2.57l-.52,1.58-1.41-1a35.32,35.32,0,0,0-10.75-5.37l-1-.31.1-1a6.2,6.2,0,0,0-1.11-4.08,6.57,6.57,0,0,0-7.06-2.62,6,6,0,0,0-1.68.74L36.89,51.06a5.71,5.71,0,0,0-2.58,3.83,6,6,0,0,0,1,4.6,6.58,6.58,0,0,0,7.06,2.62,6.21,6.21,0,0,0,1.69-.74l10.48-6.68a19.88,19.88,0,0,1,5.58-2.45,21.82,21.82,0,0,1,23.43,8.68A20.2,20.2,0,0,1,87,76.2a19,19,0,0,1-8.56,12.7L51,106.41a19.88,19.88,0,0,1-5.58,2.45' />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -3,6 +3,7 @@ import Stylesheet from './Stylesheet';
import { gather_possible_values, UNKNOWN } from './gather_possible_values'; import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { CssNode } from './interfaces'; import { CssNode } from './interfaces';
import Component from '../Component'; import Component from '../Component';
import Element from '../nodes/Element';
enum BlockAppliesToNode { enum BlockAppliesToNode {
NotPossible, NotPossible,
@ -34,8 +35,8 @@ export default class Selector {
this.used = this.blocks[0].global; this.used = this.blocks[0].global;
} }
apply(node: CssNode, stack: CssNode[]) { apply(node: Element, stack: Element[]) {
const to_encapsulate: CssNode[] = []; const to_encapsulate: any[] = [];
apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate); apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
@ -132,7 +133,7 @@ export default class Selector {
} }
} }
function apply_selector(blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean { function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean {
const block = blocks.pop(); const block = blocks.pop();
if (!block) return false; if (!block) return false;
@ -259,16 +260,84 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
const attr = node.attributes.find((attr: CssNode) => attr.name === name); const attr = node.attributes.find((attr: CssNode) => attr.name === name);
if (!attr) return false; if (!attr) return false;
if (attr.is_true) return operator === null; if (attr.is_true) return operator === null;
if (attr.chunks.length > 1) return true;
if (!expected_value) return true; if (!expected_value) return true;
const value = attr.chunks[0]; if (attr.chunks.length === 1) {
const value = attr.chunks[0];
if (!value) return false; if (!value) return false;
if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data); if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data);
}
const possible_values = new Set(); const possible_values = new Set();
gather_possible_values(value.node, possible_values);
let prev_values = [];
for (const chunk of attr.chunks) {
const current_possible_values = new Set();
if (chunk.type === 'Text') {
current_possible_values.add(chunk.data);
} else {
gather_possible_values(chunk.node, current_possible_values);
}
// 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 = [];
current_possible_values.forEach((current_possible_value: string) => {
if (/^\s/.test(current_possible_value)) {
start_with_space.push(current_possible_value);
} else {
remaining.push(current_possible_value);
}
});
if (remaining.length > 0) {
if (start_with_space.length > 0) {
prev_values.forEach(prev_value => possible_values.add(prev_value));
}
const combined = [];
prev_values.forEach((prev_value: string) => {
remaining.forEach((value: string) => {
combined.push(prev_value + value);
});
});
prev_values = combined;
start_with_space.forEach((value: string) => {
if (/\s$/.test(value)) {
possible_values.add(value);
} else {
prev_values.push(value);
}
});
continue;
} else {
prev_values.forEach(prev_value => possible_values.add(prev_value));
prev_values = [];
}
}
current_possible_values.forEach((current_possible_value: string) => {
if (/\s$/.test(current_possible_value)) {
possible_values.add(current_possible_value);
} else {
prev_values.push(current_possible_value);
}
});
if (prev_values.length < current_possible_values.size) {
prev_values.push(' ');
}
if (prev_values.length > 20) {
// might grow exponentially, bail out
return true;
}
}
prev_values.forEach(prev_value => possible_values.add(prev_value));
if (possible_values.has(UNKNOWN)) return true; if (possible_values.has(UNKNOWN)) return true;
for (const value of possible_values) { for (const value of possible_values) {

@ -151,6 +151,10 @@ export default class Element extends Node {
} }
} }
// 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));
info.attributes.forEach(node => { info.attributes.forEach(node => {
switch (node.type) { switch (node.type) {
case 'Action': case 'Action':

@ -1,8 +1,6 @@
import Node from './shared/Node'; import Node from './shared/Node';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import Component from '../Component'; import Component from '../Component';
import { b, x } from 'code-red';
import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names'; import { sanitize } from '../../utils/names';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
@ -14,6 +12,7 @@ export default class EventHandler extends Node {
handler_name: Identifier; handler_name: Identifier;
uses_context = false; uses_context = false;
can_make_passive = false; can_make_passive = false;
reassigned?: boolean;
constructor(component: Component, parent, template_scope, info) { constructor(component: Component, parent, template_scope, info) {
super(component, parent, template_scope, info); super(component, parent, template_scope, info);
@ -22,7 +21,7 @@ export default class EventHandler extends Node {
this.modifiers = new Set(info.modifiers); this.modifiers = new Set(info.modifiers);
if (info.expression) { if (info.expression) {
this.expression = new Expression(component, this, template_scope, info.expression, true); this.expression = new Expression(component, this, template_scope, info.expression);
this.uses_context = this.expression.uses_context; this.uses_context = this.expression.uses_context;
if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) { if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) {
@ -42,34 +41,12 @@ export default class EventHandler extends Node {
if (node && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) { if (node && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) {
this.can_make_passive = true; this.can_make_passive = true;
} }
this.reassigned = component.var_lookup.get(info.expression.name).reassigned;
} }
} }
} else { } else {
const id = component.get_unique_name(`${sanitize(this.name)}_handler`); this.handler_name = component.get_unique_name(`${sanitize(this.name)}_handler`);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
component.partly_hoisted.push(b`
function ${id}(event) {
@bubble($$self, event);
}
`);
this.handler_name = id;
} }
} }
// TODO move this? it is specific to render-dom
render(block: Block) {
if (this.expression) {
return this.expression.manipulate(block);
}
// this.component.add_reference(this.handler_name);
return x`#ctx.${this.handler_name}`;
}
} }

@ -203,13 +203,11 @@ export default class Block {
} }
add_variable(id: Identifier, init?: Node) { add_variable(id: Identifier, init?: Node) {
this.variables.forEach(v => { if (this.variables.has(id.name)) {
if (v.id.name === id.name) { throw new Error(
throw new Error( `Variable '${id.name}' already initialised with a different value`
`Variable '${id.name}' already initialised with a different value` );
); }
}
});
this.variables.set(id.name, { id, init }); this.variables.set(id.name, { id, init });
} }
@ -376,6 +374,8 @@ export default class Block {
d: ${properties.destroy} d: ${properties.destroy}
}`; }`;
const block = dev && this.get_unique_name('block');
const body = b` const body = b`
${Array.from(this.variables.values()).map(({ id, init }) => { ${Array.from(this.variables.values()).map(({ id, init }) => {
return init return init
@ -387,9 +387,15 @@ export default class Block {
${dev ${dev
? b` ? b`
const block = ${return_value}; const ${block} = ${return_value};
@dispatch_dev("SvelteRegisterBlock", { block, id: ${this.name || 'create_fragment'}.name, type: "${this.type}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", ctx: #ctx }); @dispatch_dev("SvelteRegisterBlock", {
return block;` block: ${block},
id: ${this.name || 'create_fragment'}.name,
type: "${this.type}",
source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}",
ctx: #ctx
});
return ${block};`
: b` : b`
return ${return_value};` return ${return_value};`
} }
@ -398,6 +404,21 @@ export default class Block {
return body; return body;
} }
has_content() {
return this.renderer.options.dev ||
this.first ||
this.event_listeners.length > 0 ||
this.chunks.intro.length > 0 ||
this.chunks.outro.length > 0 ||
this.chunks.create.length > 0 ||
this.chunks.hydrate.length > 0 ||
this.chunks.claim.length > 0 ||
this.chunks.mount.length > 0 ||
this.chunks.update.length > 0 ||
this.chunks.destroy.length > 0 ||
this.has_animation;
}
render() { render() {
const key = this.key && this.get_unique_name('key'); const key = this.key && this.get_unique_name('key');

@ -241,11 +241,16 @@ export default function dom(
args.push(x`$$props`, x`$$invalidate`); args.push(x`$$props`, x`$$invalidate`);
} }
body.push(b` const has_create_fragment = block.has_content();
function create_fragment(#ctx) { if (has_create_fragment) {
${block.get_contents()} body.push(b`
} function create_fragment(#ctx) {
${block.get_contents()}
}
`);
}
body.push(b`
${component.extract_javascript(component.ast.module)} ${component.extract_javascript(component.ast.module)}
${component.fully_hoisted} ${component.fully_hoisted}
@ -437,7 +442,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`} ${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot }, ${definition}, create_fragment, ${not_equal}, ${prop_names}); @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${dev_props_check} ${dev_props_check}
@ -489,7 +494,7 @@ export default function dom(
constructor(options) { constructor(options) {
super(${options.dev && `options`}); super(${options.dev && `options`});
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} ${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
@init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names}); @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
${dev_props_check} ${dev_props_check}

@ -3,21 +3,24 @@ import Wrapper from './shared/Wrapper';
import { b } from 'code-red'; import { b } from 'code-red';
import Body from '../../nodes/Body'; import Body from '../../nodes/Body';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import EventHandler from './Element/EventHandler';
export default class BodyWrapper extends Wrapper { export default class BodyWrapper extends Wrapper {
node: Body; node: Body;
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
this.node.handlers.forEach(handler => { this.node.handlers
const snippet = handler.render(block); .map(handler => new EventHandler(handler, this))
.forEach(handler => {
const snippet = handler.get_snippet(block);
block.chunks.init.push(b` block.chunks.init.push(b`
@_document.body.addEventListener("${handler.name}", ${snippet}); @_document.body.addEventListener("${handler.node.name}", ${snippet});
`); `);
block.chunks.destroy.push(b` block.chunks.destroy.push(b`
@_document.body.removeEventListener("${handler.name}", ${snippet}); @_document.body.removeEventListener("${handler.node.name}", ${snippet});
`); `);
}); });
} }
} }

@ -0,0 +1,69 @@
import EventHandler from '../../../nodes/EventHandler';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import { b, x, p } from 'code-red';
const TRUE = x`true`;
const FALSE = x`false`;
export default class EventHandlerWrapper {
node: EventHandler;
parent: Wrapper;
constructor(node: EventHandler, parent: Wrapper) {
this.node = node;
this.parent = parent;
if (!node.expression) {
this.parent.renderer.component.add_var({
name: node.handler_name.name,
internal: true,
referenced: true,
});
this.parent.renderer.component.partly_hoisted.push(b`
function ${node.handler_name.name}(event) {
@bubble($$self, event);
}
`);
}
}
get_snippet(block) {
const snippet = this.node.expression ? this.node.expression.manipulate(block) : x`#ctx.${this.node.handler_name}`;
if (this.node.reassigned) {
block.maintain_context = true;
return x`function () { ${snippet}.apply(this, arguments); }`;
}
return snippet;
}
render(block: Block, target: string) {
let snippet = this.get_snippet(block);
if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`;
const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod));
if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture')
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
} else if (block.renderer.options.dev) {
args.push(FALSE);
}
if (block.renderer.options.dev) {
args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE);
args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE);
}
block.event_listeners.push(
x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})`
);
}
}

@ -24,6 +24,7 @@ import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed'; import { changed } from '../shared/changed';
import { is_head } from '../shared/is_head'; import { is_head } from '../shared/is_head';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import EventHandler from './EventHandler';
const events = [ const events = [
{ {
@ -113,6 +114,7 @@ export default class ElementWrapper extends Wrapper {
fragment: FragmentWrapper; fragment: FragmentWrapper;
attributes: AttributeWrapper[]; attributes: AttributeWrapper[];
bindings: Binding[]; bindings: Binding[];
event_handlers: EventHandler[];
class_dependencies: string[]; class_dependencies: string[];
slot_block: Block; slot_block: Block;
@ -194,6 +196,8 @@ export default class ElementWrapper extends Wrapper {
// e.g. <audio bind:paused bind:currentTime> // e.g. <audio bind:paused bind:currentTime>
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this)); this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
this.event_handlers = this.node.handlers.map(event_handler => new EventHandler(event_handler, this));
if (node.intro || node.outro) { if (node.intro || node.outro) {
if (node.intro) block.add_intro(node.intro.is_local); if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local); if (node.outro) block.add_outro(node.outro.is_local);
@ -643,7 +647,7 @@ export default class ElementWrapper extends Wrapper {
} }
add_event_handlers(block: Block) { add_event_handlers(block: Block) {
add_event_handlers(block, this.var, this.node.handlers); add_event_handlers(block, this.var, this.event_handlers);
} }
add_transitions( add_transitions(
@ -894,7 +898,7 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
attr.node.chunks.forEach(chunk => { attr.node.chunks.forEach(chunk => {
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
state.quasi.value.raw += chunk.data; state.quasi.value.raw += escape_html(chunk.data);
} else { } else {
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block)); literal.expressions.push(chunk.manipulate(block));

@ -16,6 +16,7 @@ import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this'; import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed'; import { changed } from '../shared/changed';
import { Node, Identifier, ObjectExpression } from 'estree'; import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier; var: Identifier;
@ -365,7 +366,8 @@ export default class InlineComponentWrapper extends Wrapper {
}); });
const munged_handlers = this.node.handlers.map(handler => { const munged_handlers = this.node.handlers.map(handler => {
let snippet = handler.render(block); const event_handler = new EventHandler(handler, this);
let snippet = event_handler.get_snippet(block);
if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`; if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`;
return b`${name}.$on("${handler.name}", ${snippet});`; return b`${name}.$on("${handler.name}", ${snippet});`;
@ -396,12 +398,12 @@ export default class InlineComponentWrapper extends Wrapper {
`); `);
block.chunks.create.push( block.chunks.create.push(
b`if (${name}) ${name}.$$.fragment.c();` b`if (${name}) @create_component(${name}.$$.fragment);`
); );
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`if (${name}) ${name}.$$.fragment.l(${parent_nodes});` b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
); );
} }
@ -437,7 +439,7 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_bindings} ${munged_bindings}
${munged_handlers} ${munged_handlers}
${name}.$$.fragment.c(); @create_component(${name}.$$.fragment);
@transition_in(${name}.$$.fragment, 1); @transition_in(${name}.$$.fragment, 1);
@mount_component(${name}, ${update_mount_node}, ${anchor}); @mount_component(${name}, ${update_mount_node}, ${anchor});
} else { } else {
@ -472,11 +474,11 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers} ${munged_handlers}
`); `);
block.chunks.create.push(b`${name}.$$.fragment.c();`); block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`${name}.$$.fragment.l(${parent_nodes});` b`@claim_component(${name}.$$.fragment, ${parent_nodes});`
); );
} }

@ -8,6 +8,7 @@ import add_actions from './shared/add_actions';
import { changed } from './shared/changed'; import { changed } from './shared/changed';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces'; import { TemplateNode } from '../../../interfaces';
import EventHandler from './Element/EventHandler';
const associated_events = { const associated_events = {
innerWidth: 'resize', innerWidth: 'resize',
@ -34,9 +35,11 @@ const readonly = new Set([
export default class WindowWrapper extends Wrapper { export default class WindowWrapper extends Wrapper {
node: Window; node: Window;
handlers: EventHandler[];
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) { constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
} }
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
@ -47,7 +50,7 @@ export default class WindowWrapper extends Wrapper {
const bindings: Record<string, string> = {}; const bindings: Record<string, string> = {};
add_actions(component, block, '@_window', this.node.actions); add_actions(component, block, '@_window', this.node.actions);
add_event_handlers(block, '@_window', this.node.handlers); add_event_handlers(block, '@_window', this.handlers);
this.node.bindings.forEach(binding => { this.node.bindings.forEach(binding => {
// in dev mode, throw if read-only values are written to // in dev mode, throw if read-only values are written to

@ -1,39 +1,10 @@
import Block from '../../Block'; import Block from '../../Block';
import EventHandler from '../../../nodes/EventHandler'; import EventHandler from '../Element/EventHandler';
import { x, p } from 'code-red';
const TRUE = x`true`;
const FALSE = x`false`;
export default function add_event_handlers( export default function add_event_handlers(
block: Block, block: Block,
target: string, target: string,
handlers: EventHandler[] handlers: EventHandler[]
) { ) {
handlers.forEach(handler => { handlers.forEach(handler => handler.render(block, target));
let snippet = handler.render(block);
if (handler.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
if (handler.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
if (handler.modifiers.has('self')) snippet = x`@self(${snippet})`;
const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture')
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
} else if (block.renderer.options.dev) {
args.push(FALSE);
}
if (block.renderer.options.dev) {
args.push(handler.modifiers.has('stopPropagation') ? TRUE : FALSE);
args.push(handler.modifiers.has('preventDefault') ? TRUE : FALSE);
}
block.event_listeners.push(
x`@listen(${target}, "${handler.name}", ${snippet}, ${args})`
);
});
} }

@ -1,7 +1,7 @@
export default function get_name_from_filename(filename: string) { export default function get_name_from_filename(filename: string) {
if (!filename) return null; if (!filename) return null;
// eslint-disable-next-line no-useless-escape
const parts = filename.split(/[\/\\]/); const parts = filename.split(/[/\\]/).map(encodeURI);
if (parts.length > 1) { if (parts.length > 1) {
const index_match = parts[parts.length - 1].match(/^index(\.\w+)/); const index_match = parts[parts.length - 1].match(/^index(\.\w+)/);
@ -12,6 +12,7 @@ export default function get_name_from_filename(filename: string) {
} }
const base = parts.pop() const base = parts.pop()
.replace(/%/g, 'u')
.replace(/\.[^.]+$/, "") .replace(/\.[^.]+$/, "")
.replace(/[^a-zA-Z_$0-9]+/g, '_') .replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '') .replace(/^_/, '')

@ -1,156 +1,8 @@
import { walk } from 'estree-walker'; import { Node } from 'estree';
import is_reference from 'is-reference'; import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic';
import { Node, VariableDeclaration, ClassDeclaration, VariableDeclarator, ObjectPattern, Property, RestElement, ArrayPattern, Identifier } from 'estree';
import get_object from './get_object';
// TODO replace this with periscopic?
export function create_scopes(expression: Node) { export function create_scopes(expression: Node) {
const map = new WeakMap(); return analyze(expression);
const globals: Map<string, Node> = new Map();
let scope = new Scope(null, false);
walk(expression, {
enter(node, parent) {
if (node.type === 'ImportDeclaration') {
node.specifiers.forEach(specifier => {
scope.declarations.set(specifier.local.name, specifier);
});
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
if (node.type === 'FunctionDeclaration') {
scope.declarations.set(node.id.name, node);
scope = new Scope(scope, false);
map.set(node, scope);
} else {
scope = new Scope(scope, false);
map.set(node, scope);
if (node.type === 'FunctionExpression' && node.id) {
scope.declarations.set(node.id.name, node);
}
}
node.params.forEach((param) => {
extract_names(param).forEach(name => {
scope.declarations.set(name, node);
});
});
} else if (/For(?:In|Of)?Statement/.test(node.type)) {
scope = new Scope(scope, true);
map.set(node, scope);
} else if (node.type === 'BlockStatement') {
scope = new Scope(scope, true);
map.set(node, scope);
} else if (node.type === 'ClassDeclaration' || node.type === 'VariableDeclaration') {
scope.add_declaration(node);
} else if (node.type === 'CatchClause') {
scope = new Scope(scope, true);
map.set(node, scope);
extract_names(node.param).forEach(name => {
scope.declarations.set(name, node.param);
});
} else if (node.type === 'Identifier' && is_reference(node as Node, parent as Node)) {
if (!scope.has(node.name) && !globals.has(node.name)) {
globals.set(node.name, node);
}
}
},
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
}
});
scope.declarations.forEach((_node, name) => {
globals.delete(name);
});
return { map, scope, globals };
} }
export class Scope { export { Scope, extract_names, extract_identifiers };
parent: Scope;
block: boolean;
declarations: Map<string, Node> = new Map();
initialised_declarations: Set<string> = new Set();
constructor(parent: Scope, block: boolean) {
this.parent = parent;
this.block = block;
}
add_declaration(node: VariableDeclaration | ClassDeclaration) {
if (node.type === 'VariableDeclaration') {
if (node.kind === 'var' && this.block && this.parent) {
this.parent.add_declaration(node);
} else {
node.declarations.forEach((declarator: VariableDeclarator) => {
extract_names(declarator.id).forEach(name => {
this.declarations.set(name, node);
if (declarator.init) this.initialised_declarations.add(name);
});
});
}
} else {
this.declarations.set(node.id.name, node);
}
}
find_owner(name: string): Scope {
if (this.declarations.has(name)) return this;
return this.parent && this.parent.find_owner(name);
}
has(name: string): boolean {
return (
this.declarations.has(name) || (this.parent && this.parent.has(name))
);
}
}
export function extract_names(param: Node): string[] {
return extract_identifiers(param).map((node: any) => node.name);
}
export function extract_identifiers(param: Node): Identifier[] {
const nodes: Identifier[] = [];
extractors[param.type] && extractors[param.type](nodes, param);
return nodes;
}
const extractors = {
Identifier(nodes: Node[], param: Node) {
nodes.push(param);
},
MemberExpression(nodes: Node[], param: Node) {
nodes.push(get_object(param));
},
ObjectPattern(nodes: Node[], param: ObjectPattern) {
param.properties.forEach((prop: Property | RestElement) => {
if (prop.type === 'RestElement') {
nodes.push(prop.argument);
} else {
extractors[prop.value.type](nodes, prop.value);
}
});
},
ArrayPattern(nodes: Node[], param: ArrayPattern) {
param.elements.forEach((element: Node) => {
if (element) extractors[element.type](nodes, element);
});
},
RestElement(nodes: Node[], param: any) {
extractors[param.argument.type](nodes, param.argument);
},
AssignmentPattern(nodes: Node[], param: any) {
extractors[param.left.type](nodes, param.left);
}
};

@ -12,13 +12,15 @@ export function escape(data: string, { only_escape_at_symbol = false } = {}) {
} }
const escaped = { const escaped = {
'"': '&quot;',
"'": '&#39;',
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
}; };
export function escape_html(html) { export function escape_html(html) {
return String(html).replace(/[&<>]/g, match => escaped[match]); return String(html).replace(/["'&<>]/g, match => escaped[match]);
} }
export function escape_template(str) { export function escape_template(str) {

@ -4,6 +4,21 @@ import { blank_object, is_function, run, run_all, noop, has_prop } from './utils
import { children } from './dom'; import { children } from './dom';
import { transition_in } from './transitions'; import { transition_in } from './transitions';
interface Fragment {
key: string|null;
first: null;
/* create */ c: () => void;
/* claim */ l: (nodes: any) => void;
/* hydrate */ h: () => void;
/* mount */ m: (target: HTMLElement, anchor: any) => void;
/* update */ p: (changed: any, ctx: any) => void;
/* measure */ r: () => void;
/* fix */ f: () => void;
/* animate */ a: () => void;
/* intro */ i: (local: any) => void;
/* outro */ o: (local: any) => void;
/* destroy */ d: (detaching: 0|1) => void;
}
// eslint-disable-next-line @typescript-eslint/class-name-casing // eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ { interface T$$ {
dirty: null; dirty: null;
@ -13,7 +28,7 @@ interface T$$ {
callbacks: any; callbacks: any;
after_update: any[]; after_update: any[];
props: Record<string, 0 | string>; props: Record<string, 0 | string>;
fragment: null|any; fragment: null|false|Fragment;
not_equal: any; not_equal: any;
before_update: any[]; before_update: any[];
context: Map<any, any>; context: Map<any, any>;
@ -29,10 +44,18 @@ export function bind(component, name, callback) {
} }
} }
export function create_component(block) {
block && block.c();
}
export function claim_component(block, parent_nodes) {
block && block.l(parent_nodes);
}
export function mount_component(component, target, anchor) { export function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_update } = component.$$; const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment.m(target, anchor); fragment && fragment.m(target, anchor);
// onMount happens before the initial afterUpdate // onMount happens before the initial afterUpdate
add_render_callback(() => { add_render_callback(() => {
@ -51,15 +74,16 @@ export function mount_component(component, target, anchor) {
} }
export function destroy_component(component, detaching) { export function destroy_component(component, detaching) {
if (component.$$.fragment) { const $$ = component.$$;
run_all(component.$$.on_destroy); if ($$.fragment !== null) {
run_all($$.on_destroy);
component.$$.fragment.d(detaching); $$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to // TODO null out other refs, including component.$$ (but need to
// preserve final state?) // preserve final state?)
component.$$.on_destroy = component.$$.fragment = null; $$.on_destroy = $$.fragment = null;
component.$$.ctx = {}; $$.ctx = {};
} }
} }
@ -115,15 +139,17 @@ export function init(component, options, instance, create_fragment, not_equal, p
$$.update(); $$.update();
ready = true; ready = true;
run_all($$.before_update); run_all($$.before_update);
$$.fragment = create_fragment($$.ctx);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) { if (options.target) {
if (options.hydrate) { if (options.hydrate) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.l(children(options.target)); $$.fragment && $$.fragment!.l(children(options.target));
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.c(); $$.fragment && $$.fragment!.c();
} }
if (options.intro) transition_in(component.$$.fragment); if (options.intro) transition_in(component.$$.fragment);

@ -70,10 +70,10 @@ export function flush() {
} }
function update($$) { function update($$) {
if ($$.fragment) { if ($$.fragment !== null) {
$$.update($$.dirty); $$.update($$.dirty);
run_all($$.before_update); run_all($$.before_update);
$$.fragment.p($$.dirty, $$.ctx); $$.fragment && $$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null; $$.dirty = null;
$$.after_update.forEach(add_render_callback); $$.after_update.forEach(add_render_callback);

@ -1,4 +1,4 @@
import { cubicOut, cubicInOut } from 'svelte/easing'; import { cubicOut, cubicInOut, linear } from 'svelte/easing';
import { assign, is_function } from 'svelte/internal'; import { assign, is_function } from 'svelte/internal';
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
@ -43,17 +43,20 @@ export function blur(node: Element, {
interface FadeParams { interface FadeParams {
delay: number; delay: number;
duration: number; duration: number;
easing: EasingFunction;
} }
export function fade(node: Element, { export function fade(node: Element, {
delay = 0, delay = 0,
duration = 400 duration = 400,
easing = linear
}: FadeParams): TransitionConfig { }: FadeParams): TransitionConfig {
const o = +getComputedStyle(node).opacity; const o = +getComputedStyle(node).opacity;
return { return {
delay, delay,
duration, duration,
easing,
css: t => `opacity: ${t * o}` css: t => `opacity: ${t * o}`
}; };
} }

@ -0,0 +1,143 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
` 9: <style>
10: .foo {color: red;}
11: .fooaa {color: red;}
^
12: .foobb {color: red;}
13: .foocc {color: red;}`,
start: { line: 11, column: 2, character: 206 },
end: { line: 11, column: 8, character: 212 },
pos: 206,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`10: .foo {color: red;}
11: .fooaa {color: red;}
12: .foobb {color: red;}
^
13: .foocc {color: red;}
14: .foodd {color: red;}`,
start: { line: 12, column: 2, character: 229 },
end: { line: 12, column: 8, character: 235 },
pos: 229,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`12: .foobb {color: red;}
13: .foocc {color: red;}
14: .foodd {color: red;}
^
15: .aa {color: red;}
16: .bb {color: red;}`,
start: { line: 14, column: 2, character: 275 },
end: { line: 14, column: 8, character: 281 },
pos: 275,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`18: .dd {color: red;}
19: .aabar {color: red;}
20: .bbbar {color: red;}
^
21: .ccbar {color: red;}
22: .ddbar {color: red;}`,
start: { line: 20, column: 2, character: 401 },
end: { line: 20, column: 8, character: 407 },
pos: 401,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`19: .aabar {color: red;}
20: .bbbar {color: red;}
21: .ccbar {color: red;}
^
22: .ddbar {color: red;}
23: .fooaabar {color: red;}`,
start: { line: 21, column: 2, character: 424 },
end: { line: 21, column: 8, character: 430 },
pos: 424,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`20: .bbbar {color: red;}
21: .ccbar {color: red;}
22: .ddbar {color: red;}
^
23: .fooaabar {color: red;}
24: .foobbbar {color: red;}`,
start: { line: 22, column: 2, character: 447 },
end: { line: 22, column: 8, character: 453 },
pos: 447,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`21: .ccbar {color: red;}
22: .ddbar {color: red;}
23: .fooaabar {color: red;}
^
24: .foobbbar {color: red;}
25: .fooccbar {color: red;}`,
start: { line: 23, column: 2, character: 470 },
end: { line: 23, column: 11, character: 479 },
pos: 470,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`22: .ddbar {color: red;}
23: .fooaabar {color: red;}
24: .foobbbar {color: red;}
^
25: .fooccbar {color: red;}
26: .fooddbar {color: red;}`,
start: { line: 24, column: 2, character: 496 },
end: { line: 24, column: 11, character: 505 },
pos: 496,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`23: .fooaabar {color: red;}
24: .foobbbar {color: red;}
25: .fooccbar {color: red;}
^
26: .fooddbar {color: red;}
27: .baz {color: red;}`,
start: { line: 25, column: 2, character: 522 },
end: { line: 25, column: 11, character: 531 },
pos: 522,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`26: .fooddbar {color: red;}
27: .baz {color: red;}
28: .unused {color: red;}
^
29: </style>`,
start: { line: 28, column: 2, character: 595 },
end: { line: 28, column: 9, character: 602 },
pos: 595,
},
],
};

@ -0,0 +1 @@
.foo.svelte-xyz{color:red}.foocc.svelte-xyz{color:red}.aa.svelte-xyz{color:red}.bb.svelte-xyz{color:red}.cc.svelte-xyz{color:red}.dd.svelte-xyz{color:red}.aabar.svelte-xyz{color:red}.fooddbar.svelte-xyz{color:red}.baz.svelte-xyz{color:red}

@ -0,0 +1,29 @@
<script>
export let a, b, c;
</script>
<div class="foo{a ? ' aa' : b ? ' bb ' : c ? 'cc ' : 'dd'}bar baz {a ? ' aa' : b ? ' bb ' : c ? 'cc ' : 'dd'}">
some stuff
</div>
<style>
.foo {color: red;}
.fooaa {color: red;}
.foobb {color: red;}
.foocc {color: red;}
.foodd {color: red;}
.aa {color: red;}
.bb {color: red;}
.cc {color: red;}
.dd {color: red;}
.aabar {color: red;}
.bbbar {color: red;}
.ccbar {color: red;}
.ddbar {color: red;}
.fooaabar {color: red;}
.foobbbar {color: red;}
.fooccbar {color: red;}
.fooddbar {color: red;}
.baz {color: red;}
.unused {color: red;}
</style>

@ -0,0 +1,3 @@
export default {
warnings: [],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue}.hover.unused.svelte-xyz{color:blue}.unused.svelte-xyz{color:blue}

@ -0,0 +1,18 @@
<script>
export let active;
export let hover;
</script>
<div class="thing {active ? 'active' : hover}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.hover { color: blue; }
.hover.unused { color: blue; }
.unused {color: blue;}
</style>

@ -0,0 +1,25 @@
export default {
warnings: [
{
code: 'css-unused-selector',
end: {
character: 205,
column: 9,
line: 14,
},
frame: `
12: .thing.active {color: blue;}
13:
14: .unused {color: blue;}
^
15: </style>`,
message: 'Unused CSS selector',
pos: 198,
start: {
character: 198,
column: 2,
line: 14,
},
},
],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}

@ -0,0 +1,15 @@
<script>
export let active;
</script>
<div class="thing {active ? 'active' : ''}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.unused {color: blue;}
</style>

@ -0,0 +1,31 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame: `
13: .thing.active {color: blue;}
14: .hover { color: blue; }
15: .hover.unused { color: blue; }
^
16:
17: .unused {color: blue;}`,
start: { line: 15, column: 2, character: 261 },
end: { line: 15, column: 15, character: 274 },
pos: 261,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame: `
15: .hover.unused { color: blue; }
16:
17: .unused {color: blue;}
^
18: </style>`,
start: { line: 17, column: 2, character: 295 },
end: { line: 17, column: 9, character: 302 },
pos: 295,
},
],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue}

@ -0,0 +1,18 @@
<script>
export let active;
export let hover;
</script>
<div class="thing {active ? 'active' : hover ? 'hover' : ''}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.hover { color: blue; }
.hover.unused { color: blue; }
.unused {color: blue;}
</style>

@ -0,0 +1,82 @@
import {
SvelteComponent,
attr,
detach,
element,
init,
insert,
listen,
noop,
run_all,
safe_not_equal,
space
} from "svelte/internal";
function create_fragment(ctx) {
let input0;
let t;
let input1;
let dispose;
return {
c() {
input0 = element("input");
t = space();
input1 = element("input");
attr(input0, "type", "file");
attr(input1, "type", "file");
dispose = [
listen(input0, "change", ctx.input0_change_handler),
listen(input1, "change", ctx.input1_change_handler)
];
},
m(target, anchor) {
insert(target, input0, anchor);
insert(target, t, anchor);
insert(target, input1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(input0);
if (detaching) detach(t);
if (detaching) detach(input1);
run_all(dispose);
}
};
}
function instance($$self, $$props, $$invalidate) {
let { files } = $$props;
function input0_change_handler() {
files = this.files;
$$invalidate("files", files);
}
function input1_change_handler() {
files = this.files;
$$invalidate("files", files);
}
$$self.$set = $$props => {
if ("files" in $$props) $$invalidate("files", files = $$props.files);
};
return {
files,
input0_change_handler,
input1_change_handler
};
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { files: 0 });
}
}
export default Component;

@ -0,0 +1,6 @@
<script>
export let files;
</script>
<input type="file" bind:files>
<input bind:files type="file">

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
detach, detach,
element, element,
@ -28,9 +29,9 @@ function create_fragment(ctx) {
return { return {
c() { c() {
foo.$$.fragment.c(); create_component(foo.$$.fragment);
t0 = space(); t0 = space();
bar.$$.fragment.c(); create_component(bar.$$.fragment);
t1 = space(); t1 = space();
input = element("input"); input = element("input");
dispose = listen(input, "input", ctx.input_input_handler); dispose = listen(input, "input", ctx.input_input_handler);

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +16,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -2,24 +2,12 @@ import {
SvelteComponent, SvelteComponent,
component_subscribe, component_subscribe,
init, init,
noop,
safe_not_equal, safe_not_equal,
set_store_value set_store_value
} from "svelte/internal"; } from "svelte/internal";
import { count } from "./store.js"; import { count } from "./store.js";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let $count; let $count;
component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value)); component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value));
@ -34,7 +22,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { increment: 0 }); init(this, options, instance, null, safe_not_equal, { increment: 0 });
} }
get increment() { get increment() {

@ -1,15 +1,4 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { x } = $$props; let { x } = $$props;
@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { x: 0, a: 0, b: 0 }); init(this, options, instance, null, safe_not_equal, { x: 0, a: 0, b: 0 });
} }
get a() { get a() {

@ -1,17 +1,6 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import { onMount } from "svelte"; import { onMount } from "svelte";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { foo = "bar" } = $$props; let { foo = "bar" } = $$props;
@ -29,7 +18,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { foo: 0 }); init(this, options, instance, null, safe_not_equal, { foo: 0 });
} }
} }

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -17,7 +18,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
lazyload.$$.fragment.c(); create_component(lazyload.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(lazyload, target, anchor); mount_component(lazyload, target, anchor);

@ -0,0 +1,15 @@
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function instance($$self) {
const a = 1 + 2;
return {};
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, null, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,3 @@
<script>
const a = 1 + 2;
</script>

@ -0,0 +1,107 @@
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
listen,
noop,
run_all,
safe_not_equal,
set_data,
space,
text
} from "svelte/internal";
function create_fragment(ctx) {
let p0;
let button0;
let t1;
let button1;
let t3;
let p1;
let t4;
let t5;
let button2;
let dispose;
return {
c() {
p0 = element("p");
button0 = element("button");
button0.textContent = "set handler 1";
t1 = space();
button1 = element("button");
button1.textContent = "set handler 2";
t3 = space();
p1 = element("p");
t4 = text(ctx.number);
t5 = space();
button2 = element("button");
button2.textContent = "click";
dispose = [
listen(button0, "click", ctx.updateHandler1),
listen(button1, "click", ctx.updateHandler2),
listen(button2, "click", function () {
ctx.clickHandler.apply(this, arguments);
})
];
},
m(target, anchor) {
insert(target, p0, anchor);
append(p0, button0);
append(p0, t1);
append(p0, button1);
insert(target, t3, anchor);
insert(target, p1, anchor);
append(p1, t4);
insert(target, t5, anchor);
insert(target, button2, anchor);
},
p(changed, new_ctx) {
ctx = new_ctx;
if (changed.number) set_data(t4, ctx.number);
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(p0);
if (detaching) detach(t3);
if (detaching) detach(p1);
if (detaching) detach(t5);
if (detaching) detach(button2);
run_all(dispose);
}
};
}
function instance($$self, $$props, $$invalidate) {
let clickHandler;
let number = 0;
function updateHandler1() {
$$invalidate("clickHandler", clickHandler = () => $$invalidate("number", number = 1));
}
function updateHandler2() {
$$invalidate("clickHandler", clickHandler = () => $$invalidate("number", number = 2));
}
return {
clickHandler,
number,
updateHandler1,
updateHandler2
};
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,23 @@
<script>
let clickHandler;
let number = 0;
function updateHandler1(){
clickHandler = () => number = 1;
}
function updateHandler2(){
clickHandler = () => number = 2;
}
</script>
<p>
<button on:click={updateHandler1}>set handler 1</button>
<button on:click={updateHandler2}>set handler 2</button>
</p>
<p>{ number }</p>
<button on:click={clickHandler}>click</button>

@ -1,5 +1,6 @@
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
detach, detach,
init, init,
@ -22,9 +23,9 @@ function create_fragment(ctx) {
return { return {
c() { c() {
imported.$$.fragment.c(); create_component(imported.$$.fragment);
t = space(); t = space();
nonimported.$$.fragment.c(); create_component(nonimported.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(imported, target, anchor); mount_component(imported, target, anchor);

@ -1,15 +1,4 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { x } = $$props; let { x } = $$props;
@ -36,7 +25,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { x: 0 }); init(this, options, instance, null, safe_not_equal, { x: 0 });
} }
} }

@ -1,15 +1,4 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { a = 1 } = $$props; let { a = 1 } = $$props;
@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { a: 0, b: 0 }); init(this, options, instance, null, safe_not_equal, { a: 0, b: 0 });
} }
} }

@ -1,16 +1,4 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
const SOME_CONSTANT = 42; const SOME_CONSTANT = 42;
function foo(bar) { function foo(bar) {
@ -20,7 +8,7 @@ function foo(bar) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, null, create_fragment, safe_not_equal, { foo: 0 }); init(this, options, null, null, safe_not_equal, { foo: 0 });
} }
get foo() { get foo() {

@ -1,3 +1,8 @@
export default { export default {
html: `<span title='"foo"'>foo</span>` html: `
<span title='"foo"'>
foo
<span title='"bar"'>bar</span>
</span>
`
}; };

@ -1 +1,4 @@
<span title='"foo"'>foo</span> <span title='"foo"'>
foo
<span title='"bar"'>bar</span>
</span>

@ -0,0 +1,18 @@
export default {
html: `
<button>update handler</button>
<button>0</button>
`,
async test({ assert, component, target, window }) {
const [updateButton, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await button.dispatchEvent(event);
assert.equal(component.count, 1);
await updateButton.dispatchEvent(event);
await button.dispatchEvent(event);
assert.equal(component.count, 11);
}
};

@ -0,0 +1,11 @@
<script>
import Button from './Button.svelte';
export let count = 0;
let clickHandler = () => count += 1;
function updateHandler(){
clickHandler = () => count += 10;
}
</script>
<button on:click={updateHandler}>update handler</button>
<Button on:click={clickHandler}>{count}</Button>

@ -0,0 +1,18 @@
export default {
html: `
<button>update handler</button>
<button>0</button>
`,
async test({ assert, component, target, window }) {
const [updateButton, button] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await updateButton.dispatchEvent(event);
await button.dispatchEvent(event);
assert.equal(component.count, 10);
await button.dispatchEvent(event);
assert.equal(component.count, 10);
}
};

@ -0,0 +1,11 @@
<script>
import Button from './Button.svelte';
export let count = 0;
let clickHandler = () => count += 1;
function updateHandler(){
clickHandler = () => count += 10;
}
</script>
<button on:click={updateHandler}>update handler</button>
<Button on:click|once={clickHandler}>{count}</Button>

@ -0,0 +1,3 @@
export default {
html: '',
};

@ -0,0 +1,3 @@
<script>
const a = 1 + 2;
</script>

@ -0,0 +1,50 @@
export default {
html: `
<p>
<button>set handler 1</button>
<button>set handler 2</button>
</p>
<p>0</p>
<button>click</button>
`,
async test({ assert, component, target, window }) {
const [updateButton1, updateButton2, button] = target.querySelectorAll(
'button'
);
const event = new window.MouseEvent('click');
await button.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<p>
<button>set handler 1</button>
<button>set handler 2</button>
</p>
<p>0</p>
<button>click</button>
`);
await updateButton1.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<p>
<button>set handler 1</button>
<button>set handler 2</button>
</p>
<p>1</p>
<button>click</button>
`);
await updateButton2.dispatchEvent(event);
await button.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<p>
<button>set handler 1</button>
<button>set handler 2</button>
</p>
<p>2</p>
<button>click</button>
`);
},
};

@ -0,0 +1,23 @@
<script>
let clickHandler;
let number = 0;
function updateHandler1(){
clickHandler = () => number = 1;
}
function updateHandler2(){
clickHandler = () => number = 2;
}
</script>
<p>
<button on:click={updateHandler1}>set handler 1</button>
<button on:click={updateHandler2}>set handler 2</button>
</p>
<p>{ number }</p>
<button on:click={clickHandler}>click</button>
Loading…
Cancel
Save