Merge remote-tracking branch 'upstream/master' into hmr-capture-state

pull/3822/head
rixo 6 years ago
commit 1f6d759dfb

@ -1,5 +1,12 @@
# Svelte changelog # Svelte changelog
## 3.16.6
* Fix CSS specificity bug when encapsulating styles ([#1277](https://github.com/sveltejs/svelte/issues/1277))
* Apply directives in the order they're given ([#2446](https://github.com/sveltejs/svelte/issues/2446))
* Fix destructuring in `let:` directives ([#2751](https://github.com/sveltejs/svelte/issues/2751))
* Preserve whitespace around `<tspan>`s in `<svg>`s ([#3998](https://github.com/sveltejs/svelte/issues/3998))
## 3.16.5 ## 3.16.5
* Better fix for cascading invalidations and fix some regressions ([#4098](https://github.com/sveltejs/svelte/issues/4098), [#4114](https://github.com/sveltejs/svelte/issues/4114), [#4120](https://github.com/sveltejs/svelte/issues/4120)) * Better fix for cascading invalidations and fix some regressions ([#4098](https://github.com/sveltejs/svelte/issues/4098), [#4114](https://github.com/sveltejs/svelte/issues/4114), [#4120](https://github.com/sveltejs/svelte/issues/4120))

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.16.5", "version": "3.16.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.16.5", "version": "3.16.6",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",

@ -489,7 +489,7 @@ A `spring` store gradually changes to its target value based on its `stiffness`
--- ---
As with [`tweened`](#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect. As with [`tweened`](docs#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect.
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`. Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.

@ -23,7 +23,9 @@ import { pannable } from './pannable.js';
on:panstart={handlePanStart} on:panstart={handlePanStart}
on:panmove={handlePanMove} on:panmove={handlePanMove}
on:panend={handlePanEnd} on:panend={handlePanEnd}
style="transform: translate({$coords.x}px,{$coords.y}px)" style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div> ></div>
``` ```

@ -63,6 +63,7 @@
<a target="_blank" rel="noopener" href="https://godaddy.com"><img src="organisations/godaddy.svg" alt="GoDaddy logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="https://godaddy.com"><img src="organisations/godaddy.svg" alt="GoDaddy logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://www.grainger.com"><img src="organisations/grainger.svg" alt="Grainger logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="https://www.grainger.com"><img src="organisations/grainger.svg" alt="Grainger logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="http://healthtree.org/"><img src="organisations/healthtree.png" alt="HealthTree logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="http://healthtree.org/"><img src="organisations/healthtree.png" alt="HealthTree logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://iota.org"><img src="organisations/iota.svg" alt="IOTA logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://itslearning.com"><img src="organisations/itslearning.svg" alt="itslearning logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="https://itslearning.com"><img src="organisations/itslearning.svg" alt="itslearning logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://jacoux.com"><img src="organisations/jacoux.png" alt="Jacoux logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="https://jacoux.com"><img src="organisations/jacoux.png" alt="Jacoux logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://jingmnt.co.za"><img src="organisations/jingmnt.png" alt="Jingmnt logo" loading="lazy"></a> <a target="_blank" rel="noopener" href="https://jingmnt.co.za"><img src="organisations/jingmnt.png" alt="Jingmnt logo" loading="lazy"></a>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

@ -14,7 +14,7 @@ import Stylesheet from './css/Stylesheet';
import { test } from '../config'; import { test } from '../config';
import Fragment from './nodes/Fragment'; import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports'; import internal_exports from './internal_exports';
import { Ast, CompileOptions, Var, Warning } from '../interfaces'; import { Ast, CompileOptions, Var, Warning, CssResult } from '../interfaces';
import error from '../utils/error'; import error from '../utils/error';
import get_code_frame from '../utils/get_code_frame'; import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference'; import flatten_reference from './utils/flatten_reference';
@ -226,7 +226,7 @@ export default class Component {
return alias; return alias;
} }
generate(result?: Node[]) { generate(result?: { js: Node[]; css: CssResult }) {
let js = null; let js = null;
let css = null; let css = null;
@ -236,7 +236,7 @@ export default class Component {
const banner = `${this.file ? `${this.file} ` : ``}generated by Svelte v${'__VERSION__'}`; const banner = `${this.file ? `${this.file} ` : ``}generated by Svelte v${'__VERSION__'}`;
const program: any = { type: 'Program', body: result }; const program: any = { type: 'Program', body: result.js };
walk(program, { walk(program, {
enter: (node, parent, key) => { enter: (node, parent, key) => {
@ -310,7 +310,7 @@ export default class Component {
css = compile_options.customElement css = compile_options.customElement
? { code: null, map: null } ? { code: null, map: null }
: this.stylesheet.render(compile_options.cssOutputFilename, true); : result.css;
js = print(program, { js = print(program, {
sourceMapSource: compile_options.filename sourceMapSource: compile_options.filename

@ -63,9 +63,13 @@ export default class Selector {
}); });
} }
transform(code: MagicString, attr: string) { transform(code: MagicString, attr: string, max_amount_class_specificity_increased: number) {
const amount_class_specificity_to_increase = max_amount_class_specificity_increased - this.blocks.filter(block => block.should_encapsulate).length;
attr = attr.repeat(amount_class_specificity_to_increase + 1);
function encapsulate_block(block: Block) { function encapsulate_block(block: Block) {
let i = block.selectors.length; let i = block.selectors.length;
while (i--) { while (i--) {
const selector = block.selectors[i]; const selector = block.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') { if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') {
@ -131,6 +135,16 @@ export default class Selector {
} }
} }
} }
get_amount_class_specificity_increased() {
let count = 0;
for (const block of this.blocks) {
if (block.should_encapsulate) {
count ++;
}
}
return count;
}
} }
function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean { function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean {

@ -95,12 +95,12 @@ class Rule {
code.remove(c, this.node.block.end - 1); code.remove(c, this.node.block.end - 1);
} }
transform(code: MagicString, id: string, keyframes: Map<string, string>) { transform(code: MagicString, id: string, keyframes: Map<string, string>, max_amount_class_specificity_increased: number) {
if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node)) return true; if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node)) return true;
const attr = `.${id}`; const attr = `.${id}`;
this.selectors.forEach(selector => selector.transform(code, attr)); this.selectors.forEach(selector => selector.transform(code, attr, max_amount_class_specificity_increased));
this.declarations.forEach(declaration => declaration.transform(code, keyframes)); this.declarations.forEach(declaration => declaration.transform(code, keyframes));
} }
@ -115,6 +115,10 @@ class Rule {
if (!selector.used) handler(selector); if (!selector.used) handler(selector);
}); });
} }
get_max_amount_class_specificity_increased() {
return Math.max(...this.selectors.map(selector => selector.get_amount_class_specificity_increased()));
}
} }
class Declaration { class Declaration {
@ -239,7 +243,7 @@ class Atrule {
} }
} }
transform(code: MagicString, id: string, keyframes: Map<string, string>) { transform(code: MagicString, id: string, keyframes: Map<string, string>, max_amount_class_specificity_increased: number) {
if (is_keyframes_node(this.node)) { if (is_keyframes_node(this.node)) {
this.node.expression.children.forEach(({ type, name, start, end }: CssNode) => { this.node.expression.children.forEach(({ type, name, start, end }: CssNode) => {
if (type === 'Identifier') { if (type === 'Identifier') {
@ -258,7 +262,7 @@ class Atrule {
} }
this.children.forEach(child => { this.children.forEach(child => {
child.transform(code, id, keyframes); child.transform(code, id, keyframes, max_amount_class_specificity_increased);
}); });
} }
@ -275,6 +279,10 @@ class Atrule {
child.warn_on_unused_selector(handler); child.warn_on_unused_selector(handler);
}); });
} }
get_max_amount_class_specificity_increased() {
return Math.max(...this.children.map(rule => rule.get_max_amount_class_specificity_increased()));
}
} }
export default class Stylesheet { export default class Stylesheet {
@ -397,8 +405,9 @@ export default class Stylesheet {
}); });
if (should_transform_selectors) { if (should_transform_selectors) {
const max = Math.max(...this.children.map(rule => rule.get_max_amount_class_specificity_increased()));
this.children.forEach((child: (Atrule|Rule)) => { this.children.forEach((child: (Atrule|Rule)) => {
child.transform(code, this.id, this.keyframes); child.transform(code, this.id, this.keyframes, max);
}); });
} }

@ -90,11 +90,11 @@ export default function compile(source: string, options: CompileOptions = {}) {
); );
stats.stop('create component'); stats.stop('create component');
const js = options.generate === false const result = options.generate === false
? null ? null
: options.generate === 'ssr' : options.generate === 'ssr'
? render_ssr(component, options) ? render_ssr(component, options)
: render_dom(component, options); : render_dom(component, options);
return component.generate(js); return component.generate(result);
} }

@ -1,7 +1,7 @@
import { b, x, p } from 'code-red'; import { b, x, p } from 'code-red';
import Component from '../Component'; import Component from '../Component';
import Renderer from './Renderer'; import Renderer from './Renderer';
import { CompileOptions } from '../../interfaces'; import { CompileOptions, CssResult } from '../../interfaces';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { extract_names, Scope } from '../utils/scope'; import { extract_names, Scope } from '../utils/scope';
import { invalidate } from './invalidate'; import { invalidate } from './invalidate';
@ -11,7 +11,7 @@ import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression
export default function dom( export default function dom(
component: Component, component: Component,
options: CompileOptions options: CompileOptions
) { ): { js: Node[]; css: CssResult } {
const { name } = component; const { name } = component;
const renderer = new Renderer(component, options); const renderer = new Renderer(component, options);
@ -530,7 +530,7 @@ export default function dom(
body.push(declaration); body.push(declaration);
} }
return flatten(body, []); return { js: flatten(body, []), css };
} }
function flatten(nodes: any[], target: any[]) { function flatten(nodes: any[], target: any[]) {

@ -377,8 +377,7 @@ export default class ElementWrapper extends Wrapper {
} }
this.add_attributes(block); this.add_attributes(block);
this.add_bindings(block); this.add_directives_in_order(block);
this.add_event_handlers(block);
this.add_transitions(block); this.add_transitions(block);
this.add_animation(block); this.add_animation(block);
this.add_actions(block); this.add_actions(block);
@ -436,29 +435,62 @@ export default class ElementWrapper extends Wrapper {
return x`@claim_element(${nodes}, "${name}", { ${attributes} }, ${svg})`; return x`@claim_element(${nodes}, "${name}", { ${attributes} }, ${svg})`;
} }
add_bindings(block: Block) { add_directives_in_order (block: Block) {
interface BindingGroup {
events: string[];
bindings: Binding[];
}
const bindingGroups = events
.map(event => ({
events: event.event_names,
bindings: this.bindings
.filter(binding => binding.node.name !== 'this')
.filter(binding => event.filter(this.node, binding.node.name))
}))
.filter(group => group.bindings.length);
const this_binding = this.bindings.find(b => b.node.name === 'this');
function getOrder (item: EventHandler | BindingGroup | Binding) {
if (item instanceof EventHandler) {
return item.node.start;
} else if (item instanceof Binding) {
return item.node.start;
} else {
return item.bindings[0].node.start;
}
}
const ordered: Array<EventHandler | BindingGroup | Binding> = [].concat(bindingGroups, this.event_handlers, this_binding).filter(Boolean);
ordered.sort((a, b) => getOrder(a) - getOrder(b));
ordered.forEach(bindingGroupOrEventHandler => {
if (bindingGroupOrEventHandler instanceof EventHandler) {
add_event_handlers(block, this.var, [bindingGroupOrEventHandler]);
} else if (bindingGroupOrEventHandler instanceof Binding) {
this.add_this_binding(block, bindingGroupOrEventHandler);
} else {
this.add_bindings(block, bindingGroupOrEventHandler);
}
});
}
add_bindings(block: Block, bindingGroup) {
const { renderer } = this; const { renderer } = this;
if (this.bindings.length === 0) return; if (bindingGroup.bindings.length === 0) return;
renderer.component.has_reactive_assignments = true; renderer.component.has_reactive_assignments = true;
const lock = this.bindings.some(binding => binding.needs_lock) ? const lock = bindingGroup.bindings.some(binding => binding.needs_lock) ?
block.get_unique_name(`${this.var.name}_updating`) : block.get_unique_name(`${this.var.name}_updating`) :
null; null;
if (lock) block.add_variable(lock, x`false`); if (lock) block.add_variable(lock, x`false`);
const groups = events [bindingGroup].forEach(group => {
.map(event => ({
events: event.event_names,
bindings: this.bindings
.filter(binding => binding.node.name !== 'this')
.filter(binding => event.filter(this.node, binding.node.name))
}))
.filter(group => group.bindings.length);
groups.forEach(group => {
const handler = renderer.component.get_unique_name(`${this.var.name}_${group.events.join('_')}_handler`); const handler = renderer.component.get_unique_name(`${this.var.name}_${group.events.join('_')}_handler`);
renderer.add_to_context(handler.name); renderer.add_to_context(handler.name);
@ -586,13 +618,15 @@ export default class ElementWrapper extends Wrapper {
if (lock) { if (lock) {
block.chunks.update.push(b`${lock} = false;`); block.chunks.update.push(b`${lock} = false;`);
} }
}
const this_binding = this.bindings.find(b => b.node.name === 'this'); add_this_binding(block: Block, this_binding: Binding) {
if (this_binding) { const { renderer } = this;
const binding_callback = bind_this(renderer.component, block, this_binding.node, this.var);
block.chunks.mount.push(binding_callback); renderer.component.has_reactive_assignments = true;
}
const binding_callback = bind_this(renderer.component, block, this_binding.node, this.var);
block.chunks.mount.push(binding_callback);
} }
add_attributes(block: Block) { add_attributes(block: Block) {

@ -27,6 +27,11 @@ function should_skip(node: Text) {
if (parent_element.type === 'Head') return true; if (parent_element.type === 'Head') return true;
if (parent_element.type === 'InlineComponent') return parent_element.children.length === 1 && node === parent_element.children[0]; if (parent_element.type === 'InlineComponent') return parent_element.children.length === 1 && node === parent_element.children[0];
// svg namespace exclusions
if (/svg$/.test(parent_element.namespace)) {
if (node.prev && node.prev.type === "Element" && node.prev.name === "tspan") return false;
}
return parent_element.namespace || elements_without_text.has(parent_element.name); return parent_element.namespace || elements_without_text.has(parent_element.name);
} }

@ -7,7 +7,7 @@ import { BinaryExpression } from 'estree';
export function get_slot_definition(block: Block, scope: TemplateScope, lets: Let[]) { export function get_slot_definition(block: Block, scope: TemplateScope, lets: Let[]) {
if (lets.length === 0) return { block, scope }; if (lets.length === 0) return { block, scope };
const input = { const context_input = {
type: 'ObjectPattern', type: 'ObjectPattern',
properties: lets.map(l => ({ properties: lets.map(l => ({
type: 'Property', type: 'Property',
@ -17,10 +17,41 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
})) }))
}; };
const properties = [];
const value_map = new Map();
lets.forEach(l => {
let value;
if (l.names.length > 1) {
// more than one, probably destructuring
const unique_name = block.get_unique_name(l.names.join('_')).name;
value_map.set(l.value, unique_name);
value = { type: 'Identifier', name: unique_name };
} else {
value = l.value || l.name;
}
properties.push({
type: 'Property',
kind: 'init',
key: l.name,
value,
});
});
const changes_input = {
type: 'ObjectPattern',
properties,
};
const names: Set<string> = new Set(); const names: Set<string> = new Set();
const names_lookup: Map<string, string> = new Map();
lets.forEach(l => { lets.forEach(l => {
l.names.forEach(name => { l.names.forEach(name => {
names.add(name); names.add(name);
if (value_map.has(l.value)) {
names_lookup.set(name, value_map.get(l.value));
}
}); });
}); });
@ -43,8 +74,10 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
const i = context_lookup.get(name).index.value as number; const i = context_lookup.get(name).index.value as number;
const g = Math.floor(i / 31); const g = Math.floor(i / 31);
const lookup_name = names_lookup.has(name) ? names_lookup.get(name) : name;
if (!grouped[g]) grouped[g] = []; if (!grouped[g]) grouped[g] = [];
grouped[g].push({ name, n: i % 31 }); grouped[g].push({ name: lookup_name, n: i % 31 });
}); });
const elements = []; const elements = [];
@ -65,8 +98,9 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
return Array.from(names) return Array.from(names)
.map(name => { .map(name => {
const lookup_name = names_lookup.has(name) ? names_lookup.get(name) : name;
const i = context_lookup.get(name).index.value as number; const i = context_lookup.get(name).index.value as number;
return x`${name} ? ${1 << i} : 0`; return x`${lookup_name} ? ${1 << i} : 0`;
}) })
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`) as BinaryExpression; .reduce((lhs, rhs) => x`${lhs} | ${rhs}`) as BinaryExpression;
} }
@ -75,7 +109,7 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
return { return {
block, block,
scope, scope,
get_context: x`${input} => ${context}`, get_context: x`${context_input} => ${context}`,
get_changes: x`${input} => ${changes}` get_changes: x`${changes_input} => ${changes}`
}; };
} }

@ -1,17 +1,17 @@
import { b } from 'code-red'; import { b } from 'code-red';
import Component from '../Component'; import Component from '../Component';
import { CompileOptions } from '../../interfaces'; import { CompileOptions, CssResult } from '../../interfaces';
import { string_literal } from '../utils/stringify'; import { string_literal } from '../utils/stringify';
import Renderer from './Renderer'; import Renderer from './Renderer';
import { INode as TemplateNode } from '../nodes/interfaces'; // TODO import { INode as TemplateNode } from '../nodes/interfaces'; // TODO
import Text from '../nodes/Text'; import Text from '../nodes/Text';
import { extract_names } from '../utils/scope'; import { extract_names } from '../utils/scope';
import { LabeledStatement, Statement, ExpressionStatement, AssignmentExpression } from 'estree'; import { LabeledStatement, Statement, ExpressionStatement, AssignmentExpression, Node } from 'estree';
export default function ssr( export default function ssr(
component: Component, component: Component,
options: CompileOptions options: CompileOptions
) { ): {js: Node[]; css: CssResult} {
const renderer = new Renderer({ const renderer = new Renderer({
name: component.name name: component.name
}); });
@ -145,7 +145,7 @@ export default function ssr(
main main
].filter(Boolean); ].filter(Boolean);
return b` const js = b`
${css.code ? b` ${css.code ? b`
const #css = { const #css = {
code: "${css.code}", code: "${css.code}",
@ -160,6 +160,8 @@ export default function ssr(
${blocks} ${blocks}
}); });
`; `;
return {js, css};
} }
function trim(nodes: TemplateNode[]) { function trim(nodes: TemplateNode[]) {

@ -1,4 +1,5 @@
import { Node, Program } from "estree"; import { Node, Program } from "estree";
import { SourceMap } from 'magic-string';
interface BaseNode { interface BaseNode {
start: number; start: number;
@ -161,3 +162,8 @@ export interface Var {
subscribable?: boolean; subscribable?: boolean;
is_reactive_dependency?: boolean; is_reactive_dependency?: boolean;
} }
export interface CssResult {
code: string;
map: SourceMap;
}

@ -0,0 +1 @@
a.svelte-xyz b c span.svelte-xyz{color:red;font-size:2em;font-family:'Comic Sans MS'}.foo.svelte-xyz.svelte-xyz{color:green}

@ -0,0 +1,12 @@
<a class="svelte-xyz">
<b>
<c>
<span class="svelte-xyz">
Big red Comic Sans
</span>
<span class="foo svelte-xyz">
Big red Comic Sans
</span>
</c>
</b>
</a>

@ -0,0 +1,24 @@
<!-- svelte-ignore a11y-missing-attribute -->
<a>
<b>
<c>
<span>
Big red Comic Sans
</span>
<span class='foo'>
Big red Comic Sans
</span>
</c>
</b>
</a>
<style>
a b c span {
color: red;
font-size: 2em;
font-family: 'Comic Sans MS';
}
.foo {
color: green;
}
</style>

@ -29,26 +29,26 @@ function create_fragment(ctx) {
audio_updating = true; audio_updating = true;
} }
/*audio_timeupdate_handler*/ ctx[10].call(audio); /*audio_timeupdate_handler*/ ctx[12].call(audio);
} }
return { return {
c() { c() {
audio = element("audio"); audio = element("audio");
if (/*buffered*/ ctx[0] === void 0) add_render_callback(() => /*audio_progress_handler*/ ctx[10].call(audio));
if (/*buffered*/ ctx[0] === void 0 || /*seekable*/ ctx[1] === void 0) add_render_callback(() => /*audio_loadedmetadata_handler*/ ctx[11].call(audio));
if (/*played*/ ctx[2] === void 0 || /*currentTime*/ ctx[3] === void 0 || /*ended*/ ctx[9] === void 0) add_render_callback(audio_timeupdate_handler); if (/*played*/ ctx[2] === void 0 || /*currentTime*/ ctx[3] === void 0 || /*ended*/ ctx[9] === void 0) add_render_callback(audio_timeupdate_handler);
if (/*duration*/ ctx[4] === void 0) add_render_callback(() => /*audio_durationchange_handler*/ ctx[11].call(audio)); if (/*duration*/ ctx[4] === void 0) add_render_callback(() => /*audio_durationchange_handler*/ ctx[13].call(audio));
if (/*buffered*/ ctx[0] === void 0) add_render_callback(() => /*audio_progress_handler*/ ctx[13].call(audio));
if (/*buffered*/ ctx[0] === void 0 || /*seekable*/ ctx[1] === void 0) add_render_callback(() => /*audio_loadedmetadata_handler*/ ctx[14].call(audio));
if (/*seeking*/ ctx[8] === void 0) add_render_callback(() => /*audio_seeking_seeked_handler*/ ctx[17].call(audio)); if (/*seeking*/ ctx[8] === void 0) add_render_callback(() => /*audio_seeking_seeked_handler*/ ctx[17].call(audio));
if (/*ended*/ ctx[9] === void 0) add_render_callback(() => /*audio_ended_handler*/ ctx[18].call(audio)); if (/*ended*/ ctx[9] === void 0) add_render_callback(() => /*audio_ended_handler*/ ctx[18].call(audio));
dispose = [ dispose = [
listen(audio, "progress", /*audio_progress_handler*/ ctx[10]),
listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[11]),
listen(audio, "timeupdate", audio_timeupdate_handler), listen(audio, "timeupdate", audio_timeupdate_handler),
listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[11]), listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[13]),
listen(audio, "play", /*audio_play_pause_handler*/ ctx[12]), listen(audio, "play", /*audio_play_pause_handler*/ ctx[14]),
listen(audio, "pause", /*audio_play_pause_handler*/ ctx[12]), listen(audio, "pause", /*audio_play_pause_handler*/ ctx[14]),
listen(audio, "progress", /*audio_progress_handler*/ ctx[13]),
listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[14]),
listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[15]), listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[15]),
listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[16]), listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[16]),
listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[17]), listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[17]),
@ -72,6 +72,8 @@ function create_fragment(ctx) {
audio.currentTime = /*currentTime*/ ctx[3]; audio.currentTime = /*currentTime*/ ctx[3];
} }
audio_updating = false;
if (dirty & /*paused*/ 32 && audio_is_paused !== (audio_is_paused = /*paused*/ ctx[5])) { if (dirty & /*paused*/ 32 && audio_is_paused !== (audio_is_paused = /*paused*/ ctx[5])) {
audio[audio_is_paused ? "pause" : "play"](); audio[audio_is_paused ? "pause" : "play"]();
} }
@ -83,8 +85,6 @@ function create_fragment(ctx) {
if (dirty & /*playbackRate*/ 128 && !isNaN(/*playbackRate*/ ctx[7])) { if (dirty & /*playbackRate*/ 128 && !isNaN(/*playbackRate*/ ctx[7])) {
audio.playbackRate = /*playbackRate*/ ctx[7]; audio.playbackRate = /*playbackRate*/ ctx[7];
} }
audio_updating = false;
}, },
i: noop, i: noop,
o: noop, o: noop,
@ -107,6 +107,18 @@ function instance($$self, $$props, $$invalidate) {
let { seeking } = $$props; let { seeking } = $$props;
let { ended } = $$props; let { ended } = $$props;
function audio_progress_handler() {
buffered = time_ranges_to_array(this.buffered);
$$invalidate(0, buffered);
}
function audio_loadedmetadata_handler() {
buffered = time_ranges_to_array(this.buffered);
seekable = time_ranges_to_array(this.seekable);
$$invalidate(0, buffered);
$$invalidate(1, seekable);
}
function audio_timeupdate_handler() { function audio_timeupdate_handler() {
played = time_ranges_to_array(this.played); played = time_ranges_to_array(this.played);
currentTime = this.currentTime; currentTime = this.currentTime;
@ -126,18 +138,6 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(5, paused); $$invalidate(5, paused);
} }
function audio_progress_handler() {
buffered = time_ranges_to_array(this.buffered);
$$invalidate(0, buffered);
}
function audio_loadedmetadata_handler() {
buffered = time_ranges_to_array(this.buffered);
seekable = time_ranges_to_array(this.seekable);
$$invalidate(0, buffered);
$$invalidate(1, seekable);
}
function audio_volumechange_handler() { function audio_volumechange_handler() {
volume = this.volume; volume = this.volume;
$$invalidate(6, volume); $$invalidate(6, volume);
@ -182,11 +182,11 @@ function instance($$self, $$props, $$invalidate) {
playbackRate, playbackRate,
seeking, seeking,
ended, ended,
audio_progress_handler,
audio_loadedmetadata_handler,
audio_timeupdate_handler, audio_timeupdate_handler,
audio_durationchange_handler, audio_durationchange_handler,
audio_play_pause_handler, audio_play_pause_handler,
audio_progress_handler,
audio_loadedmetadata_handler,
audio_volumechange_handler, audio_volumechange_handler,
audio_ratechange_handler, audio_ratechange_handler,
audio_seeking_seeked_handler, audio_seeking_seeked_handler,

@ -17,8 +17,8 @@ import {
function create_fragment(ctx) { function create_fragment(ctx) {
let video; let video;
let video_updating = false; let video_updating = false;
let video_resize_listener;
let video_animationframe; let video_animationframe;
let video_resize_listener;
let dispose; let dispose;
function video_timeupdate_handler() { function video_timeupdate_handler() {
@ -29,23 +29,23 @@ function create_fragment(ctx) {
video_updating = true; video_updating = true;
} }
/*video_timeupdate_handler*/ ctx[5].call(video); /*video_timeupdate_handler*/ ctx[4].call(video);
} }
return { return {
c() { c() {
video = element("video"); video = element("video");
add_render_callback(() => /*video_elementresize_handler*/ ctx[4].call(video)); if (/*videoHeight*/ ctx[1] === void 0 || /*videoWidth*/ ctx[2] === void 0) add_render_callback(() => /*video_resize_handler*/ ctx[5].call(video));
if (/*videoHeight*/ ctx[1] === void 0 || /*videoWidth*/ ctx[2] === void 0) add_render_callback(() => /*video_resize_handler*/ ctx[6].call(video)); add_render_callback(() => /*video_elementresize_handler*/ ctx[6].call(video));
dispose = [ dispose = [
listen(video, "timeupdate", video_timeupdate_handler), listen(video, "timeupdate", video_timeupdate_handler),
listen(video, "resize", /*video_resize_handler*/ ctx[6]) listen(video, "resize", /*video_resize_handler*/ ctx[5])
]; ];
}, },
m(target, anchor) { m(target, anchor) {
insert(target, video, anchor); insert(target, video, anchor);
video_resize_listener = add_resize_listener(video, /*video_elementresize_handler*/ ctx[4].bind(video)); video_resize_listener = add_resize_listener(video, /*video_elementresize_handler*/ ctx[6].bind(video));
}, },
p(ctx, [dirty]) { p(ctx, [dirty]) {
if (!video_updating && dirty & /*currentTime*/ 1 && !isNaN(/*currentTime*/ ctx[0])) { if (!video_updating && dirty & /*currentTime*/ 1 && !isNaN(/*currentTime*/ ctx[0])) {
@ -70,11 +70,6 @@ function instance($$self, $$props, $$invalidate) {
let { videoWidth } = $$props; let { videoWidth } = $$props;
let { offsetWidth } = $$props; let { offsetWidth } = $$props;
function video_elementresize_handler() {
offsetWidth = this.offsetWidth;
$$invalidate(3, offsetWidth);
}
function video_timeupdate_handler() { function video_timeupdate_handler() {
currentTime = this.currentTime; currentTime = this.currentTime;
$$invalidate(0, currentTime); $$invalidate(0, currentTime);
@ -87,6 +82,11 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(2, videoWidth); $$invalidate(2, videoWidth);
} }
function video_elementresize_handler() {
offsetWidth = this.offsetWidth;
$$invalidate(3, offsetWidth);
}
$$self.$set = $$props => { $$self.$set = $$props => {
if ("currentTime" in $$props) $$invalidate(0, currentTime = $$props.currentTime); if ("currentTime" in $$props) $$invalidate(0, currentTime = $$props.currentTime);
if ("videoHeight" in $$props) $$invalidate(1, videoHeight = $$props.videoHeight); if ("videoHeight" in $$props) $$invalidate(1, videoHeight = $$props.videoHeight);
@ -99,9 +99,9 @@ function instance($$self, $$props, $$invalidate) {
videoHeight, videoHeight,
videoWidth, videoWidth,
offsetWidth, offsetWidth,
video_elementresize_handler,
video_timeupdate_handler, video_timeupdate_handler,
video_resize_handler video_resize_handler,
video_elementresize_handler
]; ];
} }

@ -0,0 +1,37 @@
export default {
props: {
value: ''
},
html: `
<input>
<p></p>
`,
ssrHtml: `
<input>
<p></p>
`,
async test({ assert, component, target, window }) {
const input = target.querySelector('input');
const event = new window.Event('input');
input.value = 'h';
await input.dispatchEvent(event);
assert.equal(input.value, 'H');
assert.htmlEqual(target.innerHTML, `
<input>
<p>H</p>
`);
input.value = 'he';
await input.dispatchEvent(event);
assert.equal(input.value, 'HE');
assert.htmlEqual(target.innerHTML, `
<input>
<p>HE</p>
`);
},
};

@ -0,0 +1,10 @@
<script>
export let value;
function uppercase(event) {
event.target.value = event.target.value.toUpperCase()
}
</script>
<input on:input={uppercase} bind:value>
<p>{value}</p>

@ -0,0 +1,5 @@
<script>
export let props;
</script>
<slot value={props} data={Array.isArray(props) ? props[0] : props.a} />

@ -0,0 +1,68 @@
export default {
html: `
<div>
hello world 0 hello
<button>Increment</button>
</div>
<div>
hello world 0 hello
<button>Increment</button>
</div>
<div>
hello world 0 hello
<button>Increment</button>
</div>
`,
async test({ assert, component, target, window }) {
const [button1, button2, button3] = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await button1.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
hello world 1 hello
<button>Increment</button>
</div>
<div>
hello world 0 hello
<button>Increment</button>
</div>
<div>
hello world 0 hello
<button>Increment</button>
</div>
`);
await button2.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
hello world 1 hello
<button>Increment</button>
</div>
<div>
hello world 1 hello
<button>Increment</button>
</div>
<div>
hello world 0 hello
<button>Increment</button>
</div>
`);
await button3.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>
hello world 1 hello
<button>Increment</button>
</div>
<div>
hello world 1 hello
<button>Increment</button>
</div>
<div>
hello world 1 hello
<button>Increment</button>
</div>
`);
}
};

@ -0,0 +1,28 @@
<script>
import Nested from "./Nested.svelte";
let c = 0, d = 0, e = 0;
</script>
<div>
<Nested props={['hello', 'world']} let:value={pair} let:data={foo}>
{pair[0]} {pair[1]} {c} {foo}
</Nested>
<button on:click={() => { c += 1; }}>Increment</button>
</div>
<div>
<Nested props={['hello', 'world']} let:value={[a, b]} let:data={foo}>
{a} {b} {d} {foo}
</Nested>
<button on:click={() => { d += 1; }}>Increment</button>
</div>
<div>
<Nested props={{ a: 'hello', b: 'world' }} let:value={{ a, b }} let:data={foo}>
{a} {b} {e} {foo}
</Nested>
<button on:click={() => { e += 1; }}>Increment</button>
</div>

@ -0,0 +1,3 @@
export default {
html: `<svg><text x=0 y=50><tspan>foo</tspan> bar<tspan>foo</tspan> bar</text></svg>`,
};

@ -0,0 +1 @@
<svg><text x=0 y=50><tspan>foo</tspan> {"bar"}<tspan>foo</tspan> bar</text></svg>

After

Width:  |  Height:  |  Size: 81 B

@ -1,13 +1,16 @@
import * as assert from "assert"; import * as assert from "assert";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import * as glob from 'tiny-glob/sync.js';
import { import {
showOutput, showOutput,
loadConfig, loadConfig,
loadSvelte,
setupHtmlEqual, setupHtmlEqual,
tryToLoadJson, tryToLoadJson,
shouldUpdateExpected shouldUpdateExpected,
mkdirp
} from "../helpers.js"; } from "../helpers.js";
function tryToReadFile(file) { function tryToReadFile(file) {
@ -20,6 +23,7 @@ function tryToReadFile(file) {
} }
const sveltePath = process.cwd().split('\\').join('/'); const sveltePath = process.cwd().split('\\').join('/');
let compile = null;
describe("ssr", () => { describe("ssr", () => {
before(() => { before(() => {
@ -28,6 +32,8 @@ describe("ssr", () => {
sveltePath sveltePath
}); });
compile = loadSvelte(true).compile;
return setupHtmlEqual(); return setupHtmlEqual();
}); });
@ -142,6 +148,33 @@ describe("ssr", () => {
require("../../register")(compileOptions); require("../../register")(compileOptions);
glob('**/*.svelte', { cwd }).forEach(file => {
if (file[0] === '_') return;
const dir = `${cwd}/_output/ssr`;
const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;
if (fs.existsSync(out)) {
fs.unlinkSync(out);
}
mkdirp(dir);
try {
const { js } = compile(
fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
{
...compileOptions,
filename: file
}
);
fs.writeFileSync(out, js.code);
} catch (err) {
// do nothing
}
});
try { try {
if (config.before_test) config.before_test(); if (config.before_test) config.before_test();

@ -27,7 +27,8 @@ describe("validate", () => {
const { warnings } = svelte.compile(input, { const { warnings } = svelte.compile(input, {
dev: config.dev, dev: config.dev,
legacy: config.legacy, legacy: config.legacy,
generate: false generate: false,
customElement: config.customElement
}); });
assert.deepEqual(warnings.map(w => ({ assert.deepEqual(warnings.map(w => ({

@ -0,0 +1,15 @@
[{
"code": "missing-custom-element-compile-options",
"message": "The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?",
"start": {
"line": 1,
"column": 16,
"character": 16
},
"end": {
"line": 1,
"column": 36,
"character": 36
},
"pos": 16
}]

@ -0,0 +1,3 @@
export default {
customElement: true
};
Loading…
Cancel
Save