pull/4742/head
pushkine 5 years ago
parent c225731d06
commit eaf599f910

1
.gitignore vendored

@ -12,6 +12,7 @@ node_modules
/dev /dev
/store /store
/easing /easing
/interpolate
/motion /motion
/transition /transition
/animate /animate

@ -0,0 +1 @@
export * from '../types/runtime/environment/index';

@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const is_browser = typeof window !== 'undefined';
const is_iframe = is_browser && window.self !== window.top;
const is_cors =
is_iframe &&
(() => {
try {
if (window.parent) void window.parent.document;
return false;
} catch (error) {
return true;
}
})();
const has_Symbol = typeof Symbol === 'function';
const globals = is_browser ? window : typeof globalThis !== 'undefined' ? globalThis : global;
exports.globals = globals;
exports.has_Symbol = has_Symbol;
exports.is_browser = is_browser;
exports.is_cors = is_cors;
exports.is_iframe = is_iframe;

@ -0,0 +1,16 @@
const is_browser = typeof window !== 'undefined';
const is_iframe = is_browser && window.self !== window.top;
const is_cors =
is_iframe &&
(() => {
try {
if (window.parent) void window.parent.document;
return false;
} catch (error) {
return true;
}
})();
const has_Symbol = typeof Symbol === 'function';
const globals = is_browser ? window : typeof globalThis !== 'undefined' ? globalThis : global;
export { globals, has_Symbol, is_browser, is_cors, is_iframe };

@ -0,0 +1,5 @@
{
"main": "./index",
"module": "./index.mjs",
"types": "./index.d.ts"
}

@ -18,9 +18,6 @@
"svelte", "svelte",
"README.md" "README.md"
], ],
"engines": {
"node": ">= 8"
},
"types": "types/runtime/index.d.ts", "types": "types/runtime/index.d.ts",
"scripts": { "scripts": {
"test": "mocha --opts mocha.opts", "test": "mocha --opts mocha.opts",
@ -64,16 +61,16 @@
"@rollup/plugin-typescript": "^4.1.1", "@rollup/plugin-typescript": "^4.1.1",
"@rollup/plugin-virtual": "^2.0.1", "@rollup/plugin-virtual": "^2.0.1",
"@types/mocha": "^7.0.2", "@types/mocha": "^7.0.2",
"@types/node": "^13.13.4", "@types/node": "^13.13.5",
"@typescript-eslint/eslint-plugin": "^2.30.0", "@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.30.0", "@typescript-eslint/parser": "^2.31.0",
"acorn": "^7.1.1", "acorn": "^7.2.0",
"agadoo": "^2.0.0", "agadoo": "^2.0.0",
"c8": "^7.1.1", "c8": "^7.1.2",
"code-red": "0.1.1", "code-red": "0.1.1",
"codecov": "^3.6.5", "codecov": "^3.6.5",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^6.8.0", "eslint": "^7.0.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-svelte3": "^2.7.3", "eslint-plugin-svelte3": "^2.7.3",
"estree-walker": "^2.0.1", "estree-walker": "^2.0.1",
@ -83,13 +80,13 @@
"locate-character": "^2.0.5", "locate-character": "^2.0.5",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"mocha": "^7.1.2", "mocha": "^7.1.2",
"periscopic": "^2.0.2", "periscopic": "^2.0.1",
"puppeteer": "^3.0.2", "puppeteer": "^3.0.4",
"rollup": "^2.7.6", "rollup": "^2.8.2",
"source-map": "^0.7.3", "source-map": "^0.7.3",
"source-map-support": "^0.5.19", "source-map-support": "^0.5.19",
"tiny-glob": "^0.2.6", "tiny-glob": "^0.2.6",
"tslib": "^1.11.1", "tslib": "^1.11.2",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"nyc": { "nyc": {

@ -53,3 +53,5 @@ declare var __VERSION__: string;
* instead of relying on hacks * instead of relying on hacks
*/ */
declare var __TEST__: boolean; declare var __TEST__: boolean;
declare var global: any;

@ -1,4 +1,5 @@
const now = (typeof process !== 'undefined' && process.hrtime) const now =
typeof process !== 'undefined' && process.hrtime
? () => { ? () => {
const t = process.hrtime(); const t = process.hrtime();
return t[0] * 1e3 + t[1] / 1e6; return t[0] * 1e3 + t[1] / 1e6;
@ -14,10 +15,13 @@ interface Timing {
function collapse_timings(timings) { function collapse_timings(timings) {
const result = {}; const result = {};
timings.forEach(timing => { timings.forEach((timing) => {
result[timing.label] = Object.assign({ result[timing.label] = Object.assign(
total: timing.end - timing.start {
}, timing.children && collapse_timings(timing.children)); total: timing.end - timing.start,
},
timing.children && collapse_timings(timing.children)
);
}); });
return result; return result;
} }
@ -40,7 +44,7 @@ export default class Stats {
label, label,
start: now(), start: now(),
end: null, end: null,
children: [] children: [],
}; };
this.current_children.push(timing); this.current_children.push(timing);
@ -62,12 +66,13 @@ export default class Stats {
} }
render() { render() {
const timings = Object.assign({ const timings = {
total: now() - this.start_time total: now() - this.start_time,
}, collapse_timings(this.timings)); ...collapse_timings(this.timings),
};
return { return {
timings timings,
}; };
} }
} }

@ -1,37 +1,37 @@
import { b, print, x } from 'code-red';
import {
AssignmentExpression,
ExpressionStatement,
Identifier,
ImportDeclaration,
Literal,
Node,
Program,
} from 'estree';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import { getLocator } from 'locate-character'; import { getLocator } from 'locate-character';
import { test } from '../config';
import { Ast, CompileOptions, CssResult, Var, Warning } from '../interfaces';
import Stats from '../Stats'; import Stats from '../Stats';
import { globals, reserved, is_valid } from '../utils/names'; import error from '../utils/error';
import { namespaces, valid_namespaces } from '../utils/namespaces'; import fuzzymatch from '../utils/fuzzymatch';
import get_code_frame from '../utils/get_code_frame';
import { globals, is_valid, reserved } from '../utils/names';
import { namespaces } from '../utils/namespaces';
import create_module from './create_module'; import create_module from './create_module';
import { create_scopes, extract_names, Scope, extract_identifiers } from './utils/scope';
import Stylesheet from './css/Stylesheet'; import Stylesheet from './css/Stylesheet';
import { test } from '../config';
import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports'; import internal_exports from './internal_exports';
import { Ast, CompileOptions, Var, Warning, CssResult } from '../interfaces'; import Fragment from './nodes/Fragment';
import error from '../utils/error';
import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference';
import is_used_as_reference from './utils/is_used_as_reference';
import is_reference from 'is-reference';
import TemplateScope from './nodes/shared/TemplateScope'; import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch';
import get_object from './utils/get_object';
import Slot from './nodes/Slot'; import Slot from './nodes/Slot';
import {
Node,
ImportDeclaration,
Identifier,
Program,
ExpressionStatement,
AssignmentExpression,
Literal,
} from 'estree';
import add_to_set from './utils/add_to_set'; import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles'; import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red'; import flatten_reference from './utils/flatten_reference';
import get_object from './utils/get_object';
import is_used_as_reference from './utils/is_used_as_reference';
import { is_reserved_keyword } from './utils/reserved_keywords'; import { is_reserved_keyword } from './utils/reserved_keywords';
import { create_scopes, extract_identifiers, extract_names, Scope } from './utils/scope';
interface ComponentOptions { interface ComponentOptions {
namespace?: string; namespace?: string;
@ -1343,8 +1343,8 @@ function process_component_options(component: Component, nodes) {
if (typeof ns !== 'string') component.error(attribute, { code, message }); if (typeof ns !== 'string') component.error(attribute, { code, message });
if (valid_namespaces.indexOf(ns) === -1) { if (!(ns in namespaces)) {
const match = fuzzymatch(ns, valid_namespaces); const match = fuzzymatch(ns, namespaces);
if (match) { if (match) {
component.error(attribute, { component.error(attribute, {
code: `invalid-namespace-property`, code: `invalid-namespace-property`,

@ -1,4 +1,3 @@
import { assign } from '../../runtime/internal/utils';
import Stats from '../Stats'; import Stats from '../Stats';
import parse from '../parse/index'; import parse from '../parse/index';
import render_dom from './render_dom/index'; import render_dom from './render_dom/index';
@ -9,6 +8,7 @@ import fuzzymatch from '../utils/fuzzymatch';
import get_name_from_filename from './utils/get_name_from_filename'; import get_name_from_filename from './utils/get_name_from_filename';
const valid_options = [ const valid_options = [
'version',
'format', 'format',
'name', 'name',
'filename', 'filename',
@ -26,13 +26,13 @@ const valid_options = [
'css', 'css',
'loopGuardTimeout', 'loopGuardTimeout',
'preserveComments', 'preserveComments',
'preserveWhitespace' 'preserveWhitespace',
]; ];
function validate_options(options: CompileOptions, warnings: Warning[]) { function validate_options(options: CompileOptions, warnings: Warning[]) {
const { name, filename, loopGuardTimeout, dev } = options; const { name, filename, loopGuardTimeout, dev } = options;
Object.keys(options).forEach(key => { Object.keys(options).forEach((key) => {
if (!valid_options.includes(key)) { if (!valid_options.includes(key)) {
const match = fuzzymatch(key, valid_options); const match = fuzzymatch(key, valid_options);
let message = `Unrecognized option '${key}'`; let message = `Unrecognized option '${key}'`;
@ -68,7 +68,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
} }
export default function compile(source: string, options: CompileOptions = {}) { export default function compile(source: string, options: CompileOptions = {}) {
options = assign({ generate: 'dom', dev: false }, options); options = { generate: 'dom', dev: false, ...options };
const stats = new Stats(); const stats = new Stats();
const warnings = []; const warnings = [];
@ -90,7 +90,8 @@ export default function compile(source: string, options: CompileOptions = {}) {
); );
stats.stop('create component'); stats.stop('create component');
const result = options.generate === false const result =
options.generate === false
? null ? null
: options.generate === 'ssr' : options.generate === 'ssr'
? render_ssr(component, options) ? render_ssr(component, options)

@ -19,10 +19,14 @@ import { INode } from './interfaces';
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/; const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;
const aria_attributes = 'activedescendant atomic autocomplete busy checked colindex controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowindex selected setsize sort valuemax valuemin valuenow valuetext'.split(' '); const aria_attributes = 'activedescendant atomic autocomplete busy checked colindex controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowindex selected setsize sort valuemax valuemin valuenow valuetext'.split(
' '
);
const aria_attribute_set = new Set(aria_attributes); const aria_attribute_set = new Set(aria_attributes);
const aria_roles = 'alert alertdialog application article banner button cell checkbox columnheader combobox command complementary composite contentinfo definition dialog directory document feed figure form grid gridcell group heading img input landmark link list listbox listitem log main marquee math menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search searchbox section sectionhead select separator slider spinbutton status structure switch tab table tablist tabpanel term textbox timer toolbar tooltip tree treegrid treeitem widget window'.split(' '); const aria_roles = 'alert alertdialog application article banner button cell checkbox columnheader combobox command complementary composite contentinfo definition dialog directory document feed figure form grid gridcell group heading img input landmark link list listbox listitem log main marquee math menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search searchbox section sectionhead select separator slider spinbutton status structure switch tab table tablist tabpanel term textbox timer toolbar tooltip tree treegrid treeitem widget window'.split(
' '
);
const aria_role_set = new Set(aria_roles); const aria_role_set = new Set(aria_roles);
const a11y_required_attributes = { const a11y_required_attributes = {
@ -35,13 +39,10 @@ const a11y_required_attributes = {
// iframe-has-title // iframe-has-title
iframe: ['title'], iframe: ['title'],
img: ['alt'], img: ['alt'],
object: ['title', 'aria-label', 'aria-labelledby'] object: ['title', 'aria-label', 'aria-labelledby'],
}; };
const a11y_distracting_elements = new Set([ const a11y_distracting_elements = new Set(['blink', 'marquee']);
'blink',
'marquee'
]);
const a11y_required_content = new Set([ const a11y_required_content = new Set([
// anchor-has-content // anchor-has-content
@ -53,35 +54,20 @@ const a11y_required_content = new Set([
'h3', 'h3',
'h4', 'h4',
'h5', 'h5',
'h6' 'h6',
]); ]);
const invisible_elements = new Set(['meta', 'html', 'script', 'style']); const invisible_elements = new Set(['meta', 'html', 'script', 'style']);
const valid_modifiers = new Set([ const valid_modifiers = new Set(['preventDefault', 'stopPropagation', 'capture', 'once', 'passive', 'self']);
'preventDefault',
'stopPropagation',
'capture',
'once',
'passive',
'self'
]);
const passive_events = new Set([ const passive_events = new Set(['wheel', 'touchstart', 'touchmove', 'touchend', 'touchcancel']);
'wheel',
'touchstart',
'touchmove',
'touchend',
'touchcancel'
]);
function get_namespace(parent: Element, element: Element, explicit_namespace: string) { function get_namespace(parent: Element, element: Element, explicit_namespace: string) {
const parent_element = parent.find_nearest(/^Element/); const parent_element = parent.find_nearest(/^Element/);
if (!parent_element) { if (!parent_element) {
return explicit_namespace || (svg.test(element.name) return explicit_namespace || (svg.test(element.name) ? namespaces.svg : null);
? namespaces.svg
: null);
} }
if (svg.test(element.name.toLowerCase())) return namespaces.svg; if (svg.test(element.name.toLowerCase())) return namespaces.svg;
@ -115,11 +101,11 @@ export default class Element extends Node {
if (this.name === 'textarea') { if (this.name === 'textarea') {
if (info.children.length > 0) { if (info.children.length > 0) {
const value_attribute = info.attributes.find(node => node.name === 'value'); const value_attribute = info.attributes.find((node) => node.name === 'value');
if (value_attribute) { if (value_attribute) {
component.error(value_attribute, { component.error(value_attribute, {
code: `textarea-duplicate-value`, code: `textarea-duplicate-value`,
message: `A <textarea> can have either a value attribute or (equivalently) child content, but not both` message: `A <textarea> can have either a value attribute or (equivalently) child content, but not both`,
}); });
} }
@ -128,7 +114,7 @@ export default class Element extends Node {
info.attributes.push({ info.attributes.push({
type: 'Attribute', type: 'Attribute',
name: 'value', name: 'value',
value: info.children value: info.children,
}); });
info.children = []; info.children = [];
@ -139,19 +125,19 @@ export default class Element extends Node {
// Special case — treat these the same way: // Special case — treat these the same way:
// <option>{foo}</option> // <option>{foo}</option>
// <option value={foo}>{foo}</option> // <option value={foo}>{foo}</option>
const value_attribute = info.attributes.find(attribute => attribute.name === 'value'); const value_attribute = info.attributes.find((attribute) => attribute.name === 'value');
if (!value_attribute) { if (!value_attribute) {
info.attributes.push({ info.attributes.push({
type: 'Attribute', type: 'Attribute',
name: 'value', name: 'value',
value: info.children, value: info.children,
synthetic: true synthetic: true,
}); });
} }
} }
const has_let = info.attributes.some(node => node.type === 'Let'); const has_let = info.attributes.some((node) => node.type === 'Let');
if (has_let) { if (has_let) {
scope = scope.child(); scope = scope.child();
} }
@ -160,7 +146,7 @@ export default class Element extends Node {
const order = ['Binding']; // everything else is -1 const order = ['Binding']; // everything else is -1
info.attributes.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type)); 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':
this.actions.push(new Action(component, this, scope, node)); this.actions.push(new Action(component, this, scope, node));
@ -191,14 +177,13 @@ export default class Element extends Node {
this.lets.push(l); this.lets.push(l);
const dependencies = new Set([l.name.name]); const dependencies = new Set([l.name.name]);
l.names.forEach(name => { l.names.forEach((name) => {
scope.add(name, dependencies, this); scope.add(name, dependencies, this);
}); });
break; break;
} }
case 'Transition': case 'Transition': {
{
const transition = new Transition(component, this, scope, node); const transition = new Transition(component, this, scope, node);
if (node.intro) this.intro = transition; if (node.intro) this.intro = transition;
if (node.outro) this.outro = transition; if (node.outro) this.outro = transition;
@ -227,7 +212,7 @@ export default class Element extends Node {
// no-distracting-elements // no-distracting-elements
this.component.warn(this, { this.component.warn(this, {
code: `a11y-distracting-elements`, code: `a11y-distracting-elements`,
message: `A11y: Avoid <${this.name}> elements` message: `A11y: Avoid <${this.name}> elements`,
}); });
} }
@ -249,24 +234,24 @@ export default class Element extends Node {
if (!is_figure_parent) { if (!is_figure_parent) {
this.component.warn(this, { this.component.warn(this, {
code: `a11y-structure`, code: `a11y-structure`,
message: `A11y: <figcaption> must be an immediate child of <figure>` message: `A11y: <figcaption> must be an immediate child of <figure>`,
}); });
} }
} }
if (this.name === 'figure') { if (this.name === 'figure') {
const children = this.children.filter(node => { const children = this.children.filter((node) => {
if (node.type === 'Comment') return false; if (node.type === 'Comment') return false;
if (node.type === 'Text') return /\S/.test(node.data); if (node.type === 'Text') return /\S/.test(node.data);
return true; return true;
}); });
const index = children.findIndex(child => (child as Element).name === 'figcaption'); const index = children.findIndex((child) => (child as Element).name === 'figcaption');
if (index !== -1 && (index !== 0 && index !== children.length - 1)) { if (index !== -1 && index !== 0 && index !== children.length - 1) {
this.component.warn(children[index], { this.component.warn(children[index], {
code: `a11y-structure`, code: `a11y-structure`,
message: `A11y: <figcaption> must be first or last child of <figure>` message: `A11y: <figcaption> must be first or last child of <figure>`,
}); });
} }
} }
@ -282,7 +267,7 @@ export default class Element extends Node {
const attribute_map = new Map(); const attribute_map = new Map();
this.attributes.forEach(attribute => { this.attributes.forEach((attribute) => {
if (attribute.is_spread) return; if (attribute.is_spread) return;
const name = attribute.name.toLowerCase(); const name = attribute.name.toLowerCase();
@ -293,7 +278,7 @@ export default class Element extends Node {
// aria-unsupported-elements // aria-unsupported-elements
component.warn(attribute, { component.warn(attribute, {
code: `a11y-aria-attributes`, code: `a11y-aria-attributes`,
message: `A11y: <${this.name}> should not have aria-* attributes` message: `A11y: <${this.name}> should not have aria-* attributes`,
}); });
} }
@ -305,14 +290,14 @@ export default class Element extends Node {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-unknown-aria-attribute`, code: `a11y-unknown-aria-attribute`,
message message,
}); });
} }
if (name === 'aria-hidden' && /^h[1-6]$/.test(this.name)) { if (name === 'aria-hidden' && /^h[1-6]$/.test(this.name)) {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-hidden`, code: `a11y-hidden`,
message: `A11y: <${this.name}> element should not be hidden` message: `A11y: <${this.name}> element should not be hidden`,
}); });
} }
} }
@ -323,7 +308,7 @@ export default class Element extends Node {
// aria-unsupported-elements // aria-unsupported-elements
component.warn(attribute, { component.warn(attribute, {
code: `a11y-misplaced-role`, code: `a11y-misplaced-role`,
message: `A11y: <${this.name}> should not have role attribute` message: `A11y: <${this.name}> should not have role attribute`,
}); });
} }
@ -337,7 +322,7 @@ export default class Element extends Node {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-unknown-role`, code: `a11y-unknown-role`,
message message,
}); });
} }
} }
@ -346,7 +331,7 @@ export default class Element extends Node {
if (name === 'accesskey') { if (name === 'accesskey') {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-accesskey`, code: `a11y-accesskey`,
message: `A11y: Avoid using accesskey` message: `A11y: Avoid using accesskey`,
}); });
} }
@ -354,7 +339,7 @@ export default class Element extends Node {
if (name === 'autofocus') { if (name === 'autofocus') {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-autofocus`, code: `a11y-autofocus`,
message: `A11y: Avoid using autofocus` message: `A11y: Avoid using autofocus`,
}); });
} }
@ -362,7 +347,7 @@ export default class Element extends Node {
if (name === 'scope' && this.name !== 'th') { if (name === 'scope' && this.name !== 'th') {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-misplaced-scope`, code: `a11y-misplaced-scope`,
message: `A11y: The scope attribute should only be used with <th> elements` message: `A11y: The scope attribute should only be used with <th> elements`,
}); });
} }
@ -373,12 +358,11 @@ export default class Element extends Node {
if (!isNaN(value) && +value > 0) { if (!isNaN(value) && +value > 0) {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-positive-tabindex`, code: `a11y-positive-tabindex`,
message: `A11y: avoid tabindex values above zero` message: `A11y: avoid tabindex values above zero`,
}); });
} }
} }
if (/(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/.test(name)) { if (/(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/.test(name)) {
component.error(attribute, { component.error(attribute, {
code: `illegal-attribute`, code: `illegal-attribute`,
@ -390,14 +374,14 @@ export default class Element extends Node {
if (!attribute.is_static) { if (!attribute.is_static) {
component.error(attribute, { component.error(attribute, {
code: `invalid-slot-attribute`, code: `invalid-slot-attribute`,
message: `slot attribute cannot have a dynamic value` message: `slot attribute cannot have a dynamic value`,
}); });
} }
if (component.slot_outlets.has(name)) { if (component.slot_outlets.has(name)) {
component.error(attribute, { component.error(attribute, {
code: `duplicate-slot-attribute`, code: `duplicate-slot-attribute`,
message: `Duplicate '${name}' slot` message: `Duplicate '${name}' slot`,
}); });
component.slot_outlets.add(name); component.slot_outlets.add(name);
@ -414,7 +398,7 @@ export default class Element extends Node {
if (name === 'is') { if (name === 'is') {
component.warn(attribute, { component.warn(attribute, {
code: 'avoid-is', code: 'avoid-is',
message: `The 'is' attribute is not supported cross-browser and should be avoided` message: `The 'is' attribute is not supported cross-browser and should be avoided`,
}); });
} }
@ -431,21 +415,19 @@ export default class Element extends Node {
if (value === '' || value === '#') { if (value === '' || value === '#') {
component.warn(attribute, { component.warn(attribute, {
code: `a11y-invalid-attribute`, code: `a11y-invalid-attribute`,
message: `A11y: '${value}' is not a valid ${attribute.name} attribute` message: `A11y: '${value}' is not a valid ${attribute.name} attribute`,
}); });
} }
} else { } else {
component.warn(this, { component.warn(this, {
code: `a11y-missing-attribute`, code: `a11y-missing-attribute`,
message: `A11y: <a> element should have an href attribute` message: `A11y: <a> element should have an href attribute`,
}); });
} }
} } else {
else {
const required_attributes = a11y_required_attributes[this.name]; const required_attributes = a11y_required_attributes[this.name];
if (required_attributes) { if (required_attributes) {
const has_attribute = required_attributes.some(name => attribute_map.has(name)); const has_attribute = required_attributes.some((name) => attribute_map.has(name));
if (!has_attribute) { if (!has_attribute) {
should_have_attribute(this, required_attributes); should_have_attribute(this, required_attributes);
@ -456,7 +438,7 @@ export default class Element extends Node {
const type = attribute_map.get('type'); const type = attribute_map.get('type');
if (type && type.get_static_value() === 'image') { if (type && type.get_static_value() === 'image') {
const required_attributes = ['alt', 'aria-label', 'aria-labelledby']; const required_attributes = ['alt', 'aria-label', 'aria-labelledby'];
const has_attribute = required_attributes.some(name => attribute_map.has(name)); const has_attribute = required_attributes.some((name) => attribute_map.has(name));
if (!has_attribute) { if (!has_attribute) {
should_have_attribute(this, required_attributes, 'input type="image"'); should_have_attribute(this, required_attributes, 'input type="image"');
@ -470,16 +452,14 @@ export default class Element extends Node {
const { component } = this; const { component } = this;
const check_type_attribute = () => { const check_type_attribute = () => {
const attribute = this.attributes.find( const attribute = this.attributes.find((attribute: Attribute) => attribute.name === 'type');
(attribute: Attribute) => attribute.name === 'type'
);
if (!attribute) return null; if (!attribute) return null;
if (!attribute.is_static) { if (!attribute.is_static) {
component.error(attribute, { component.error(attribute, {
code: `invalid-type`, code: `invalid-type`,
message: `'type' attribute cannot be dynamic if input uses two-way binding` message: `'type' attribute cannot be dynamic if input uses two-way binding`,
}); });
} }
@ -488,37 +468,31 @@ export default class Element extends Node {
if (value === true) { if (value === true) {
component.error(attribute, { component.error(attribute, {
code: `missing-type`, code: `missing-type`,
message: `'type' attribute must be specified` message: `'type' attribute must be specified`,
}); });
} }
return value; return value;
}; };
this.bindings.forEach(binding => { this.bindings.forEach((binding) => {
const { name } = binding; const { name } = binding;
if (name === 'value') { if (name === 'value') {
if ( if (this.name !== 'input' && this.name !== 'textarea' && this.name !== 'select') {
this.name !== 'input' &&
this.name !== 'textarea' &&
this.name !== 'select'
) {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'value' is not a valid binding on <${this.name}> elements` message: `'value' is not a valid binding on <${this.name}> elements`,
}); });
} }
if (this.name === 'select') { if (this.name === 'select') {
const attribute = this.attributes.find( const attribute = this.attributes.find((attribute: Attribute) => attribute.name === 'multiple');
(attribute: Attribute) => attribute.name === 'multiple'
);
if (attribute && !attribute.is_static) { if (attribute && !attribute.is_static) {
component.error(attribute, { component.error(attribute, {
code: `dynamic-multiple-attribute`, code: `dynamic-multiple-attribute`,
message: `'multiple' attribute cannot be dynamic if select uses two-way binding` message: `'multiple' attribute cannot be dynamic if select uses two-way binding`,
}); });
} }
} else { } else {
@ -528,7 +502,7 @@ export default class Element extends Node {
if (this.name !== 'input') { if (this.name !== 'input') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'${name}' is not a valid binding on <${this.name}> elements` message: `'${name}' is not a valid binding on <${this.name}> elements`,
}); });
} }
@ -543,7 +517,7 @@ export default class Element extends Node {
if (this.name !== 'input') { if (this.name !== 'input') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'group' is not a valid binding on <${this.name}> elements` message: `'group' is not a valid binding on <${this.name}> elements`,
}); });
} }
@ -552,14 +526,14 @@ export default class Element extends Node {
if (type !== 'checkbox' && type !== 'radio') { if (type !== 'checkbox' && type !== 'radio') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'group' binding can only be used with <input type="checkbox"> or <input type="radio">` message: `'group' binding can only be used with <input type="checkbox"> or <input type="radio">`,
}); });
} }
} else if (name === 'files') { } else if (name === 'files') {
if (this.name !== 'input') { if (this.name !== 'input') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'files' is not a valid binding on <${this.name}> elements` message: `'files' is not a valid binding on <${this.name}> elements`,
}); });
} }
@ -568,15 +542,14 @@ export default class Element extends Node {
if (type !== 'file') { if (type !== 'file') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'files' binding can only be used with <input type="file">` message: `'files' binding can only be used with <input type="file">`,
}); });
} }
} else if (name === 'open') { } else if (name === 'open') {
if (this.name !== 'details') { if (this.name !== 'details') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'${name}' binding can only be used with <details>` message: `'${name}' binding can only be used with <details>`,
}); });
} }
} else if ( } else if (
@ -594,59 +567,54 @@ export default class Element extends Node {
if (this.name !== 'audio' && this.name !== 'video') { if (this.name !== 'audio' && this.name !== 'video') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'${name}' binding can only be used with <audio> or <video>` message: `'${name}' binding can only be used with <audio> or <video>`,
}); });
} }
} else if ( } else if (name === 'videoHeight' || name === 'videoWidth') {
name === 'videoHeight' ||
name === 'videoWidth'
) {
if (this.name !== 'video') { if (this.name !== 'video') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'${name}' binding can only be used with <video>` message: `'${name}' binding can only be used with <video>`,
}); });
} }
} else if (dimensions.test(name)) { } else if (dimensions.test(name)) {
if (this.name === 'svg' && (name === 'offsetWidth' || name === 'offsetHeight')) { if (this.name === 'svg' && (name === 'offsetWidth' || name === 'offsetHeight')) {
component.error(binding, { component.error(binding, {
code: 'invalid-binding', code: 'invalid-binding',
message: `'${binding.name}' is not a valid binding on <svg>. Use '${name.replace('offset', 'client')}' instead` message: `'${binding.name}' is not a valid binding on <svg>. Use '${name.replace(
'offset',
'client'
)}' instead`,
}); });
} else if (svg.test(this.name)) { } else if (svg.test(this.name)) {
component.error(binding, { component.error(binding, {
code: 'invalid-binding', code: 'invalid-binding',
message: `'${binding.name}' is not a valid binding on SVG elements` message: `'${binding.name}' is not a valid binding on SVG elements`,
}); });
} else if (is_void(this.name)) { } else if (is_void(this.name)) {
component.error(binding, { component.error(binding, {
code: 'invalid-binding', code: 'invalid-binding',
message: `'${binding.name}' is not a valid binding on void elements like <${this.name}>. Use a wrapper element instead` message: `'${binding.name}' is not a valid binding on void elements like <${this.name}>. Use a wrapper element instead`,
}); });
} }
} else if ( } else if (name === 'textContent' || name === 'innerHTML') {
name === 'textContent' || const contenteditable = this.attributes.find((attribute: Attribute) => attribute.name === 'contenteditable');
name === 'innerHTML'
) {
const contenteditable = this.attributes.find(
(attribute: Attribute) => attribute.name === 'contenteditable'
);
if (!contenteditable) { if (!contenteditable) {
component.error(binding, { component.error(binding, {
code: `missing-contenteditable-attribute`, code: `missing-contenteditable-attribute`,
message: `'contenteditable' attribute is required for textContent and innerHTML two-way bindings` message: `'contenteditable' attribute is required for textContent and innerHTML two-way bindings`,
}); });
} else if (contenteditable && !contenteditable.is_static) { } else if (contenteditable && !contenteditable.is_static) {
component.error(contenteditable, { component.error(contenteditable, {
code: `dynamic-contenteditable-attribute`, code: `dynamic-contenteditable-attribute`,
message: `'contenteditable' attribute cannot be dynamic if element uses two-way binding` message: `'contenteditable' attribute cannot be dynamic if element uses two-way binding`,
}); });
} }
} else if (name !== 'this') { } else if (name !== 'this') {
component.error(binding, { component.error(binding, {
code: `invalid-binding`, code: `invalid-binding`,
message: `'${binding.name}' is not a valid binding` message: `'${binding.name}' is not a valid binding`,
}); });
} }
}); });
@ -658,7 +626,7 @@ export default class Element extends Node {
if (this.children.length === 0) { if (this.children.length === 0) {
this.component.warn(this, { this.component.warn(this, {
code: `a11y-missing-content`, code: `a11y-missing-content`,
message: `A11y: <${this.name}> element should have child content` message: `A11y: <${this.name}> element should have child content`,
}); });
} }
} }
@ -666,19 +634,19 @@ export default class Element extends Node {
validate_event_handlers() { validate_event_handlers() {
const { component } = this; const { component } = this;
this.handlers.forEach(handler => { this.handlers.forEach((handler) => {
if (handler.modifiers.has('passive') && handler.modifiers.has('preventDefault')) { if (handler.modifiers.has('passive') && handler.modifiers.has('preventDefault')) {
component.error(handler, { component.error(handler, {
code: 'invalid-event-modifier', code: 'invalid-event-modifier',
message: `The 'passive' and 'preventDefault' modifiers cannot be used together` message: `The 'passive' and 'preventDefault' modifiers cannot be used together`,
}); });
} }
handler.modifiers.forEach(modifier => { handler.modifiers.forEach((modifier) => {
if (!valid_modifiers.has(modifier)) { if (!valid_modifiers.has(modifier)) {
component.error(handler, { component.error(handler, {
code: 'invalid-event-modifier', code: 'invalid-event-modifier',
message: `Valid event modifiers are ${list(Array.from(valid_modifiers))}` message: `Valid event modifiers are ${list(Array.from(valid_modifiers))}`,
}); });
} }
@ -687,25 +655,16 @@ export default class Element extends Node {
if (handler.can_make_passive) { if (handler.can_make_passive) {
component.warn(handler, { component.warn(handler, {
code: 'redundant-event-modifier', code: 'redundant-event-modifier',
message: `Touch event handlers that don't use the 'event' object are passive by default` message: `Touch event handlers that don't use the 'event' object are passive by default`,
}); });
} }
} else { } else {
component.warn(handler, { component.warn(handler, {
code: 'redundant-event-modifier', code: 'redundant-event-modifier',
message: `The passive modifier only works with wheel and touch events` message: `The passive modifier only works with wheel and touch events`,
}); });
} }
} }
if (component.compile_options.legacy && (modifier === 'once' || modifier === 'passive')) {
// TODO this could be supported, but it would need a few changes to
// how event listeners work
component.error(handler, {
code: 'invalid-event-modifier',
message: `The '${modifier}' modifier cannot be used in legacy mode`
});
}
}); });
if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault')) { if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault')) {
@ -720,14 +679,14 @@ export default class Element extends Node {
} }
add_css_class() { add_css_class() {
if (this.attributes.some(attr => attr.is_spread)) { if (this.attributes.some((attr) => attr.is_spread)) {
this.needs_manual_style_scoping = true; this.needs_manual_style_scoping = true;
return; return;
} }
const { id } = this.component.stylesheet; const { id } = this.component.stylesheet;
const class_attribute = this.attributes.find(a => a.name === 'class'); const class_attribute = this.attributes.find((a) => a.name === 'class');
if (class_attribute && !class_attribute.is_true) { if (class_attribute && !class_attribute.is_true) {
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') { if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
@ -737,7 +696,7 @@ export default class Element extends Node {
new Text(this.component, this, this.scope, { new Text(this.component, this, this.scope, {
type: 'Text', type: 'Text',
data: ` ${id}`, data: ` ${id}`,
synthetic: true synthetic: true,
}) })
); );
} }
@ -746,26 +705,23 @@ export default class Element extends Node {
new Attribute(this.component, this, this.scope, { new Attribute(this.component, this, this.scope, {
type: 'Attribute', type: 'Attribute',
name: 'class', name: 'class',
value: [{ type: 'Text', data: id, synthetic: true }] value: [{ type: 'Text', data: id, synthetic: true }],
}) })
); );
} }
} }
} }
function should_have_attribute( function should_have_attribute(node, attributes: string[], name = node.name) {
node,
attributes: string[],
name = node.name
) {
const article = /^[aeiou]/.test(attributes[0]) ? 'an' : 'a'; const article = /^[aeiou]/.test(attributes[0]) ? 'an' : 'a';
const sequence = attributes.length > 1 ? const sequence =
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` : attributes.length > 1
attributes[0]; ? attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}`
: attributes[0];
node.component.warn(node, { node.component.warn(node, {
code: `a11y-missing-attribute`, code: `a11y-missing-attribute`,
message: `A11y: <${name}> element should have ${article} ${sequence} attribute` message: `A11y: <${name}> element should have ${article} ${sequence} attribute`,
}); });
} }

@ -11,14 +11,17 @@ export interface BlockOptions {
renderer?: Renderer; renderer?: Renderer;
comment?: string; comment?: string;
key?: Identifier; key?: Identifier;
bindings?: Map<string, { bindings?: Map<
string,
{
object: Identifier; object: Identifier;
property: Identifier; property: Identifier;
snippet: Node; snippet: Node;
store: string; store: string;
tail: Node; tail: Node;
modifier: (node: Node) => Node; modifier: (node: Node) => Node;
}>; }
>;
dependencies?: Set<string>; dependencies?: Set<string>;
} }
@ -36,14 +39,17 @@ export default class Block {
dependencies: Set<string> = new Set(); dependencies: Set<string> = new Set();
bindings: Map<string, { bindings: Map<
string,
{
object: Identifier; object: Identifier;
property: Identifier; property: Identifier;
snippet: Node; snippet: Node;
store: string; store: string;
tail: Node; tail: Node;
modifier: (node: Node) => Node; modifier: (node: Node) => Node;
}>; }
>;
chunks: { chunks: {
declarations: Array<Node | Node[]>; declarations: Array<Node | Node[]>;
@ -157,7 +163,7 @@ export default class Block {
} }
add_dependencies(dependencies: Set<string>) { add_dependencies(dependencies: Set<string>) {
dependencies.forEach(dependency => { dependencies.forEach((dependency) => {
this.dependencies.add(dependency); this.dependencies.add(dependency);
}); });
@ -167,13 +173,7 @@ export default class Block {
} }
} }
add_element( add_element(id: Identifier, render_statement: Node, claim_statement: Node, parent_node: Node, no_detach?: boolean) {
id: Identifier,
render_statement: Node,
claim_statement: Node,
parent_node: Node,
no_detach?: boolean
) {
this.add_variable(id); this.add_variable(id);
this.chunks.create.push(b`${id} = ${render_statement};`); this.chunks.create.push(b`${id} = ${render_statement};`);
@ -207,13 +207,14 @@ export default class Block {
add_variable(id: Identifier, init?: Node) { add_variable(id: Identifier, init?: Node) {
if (this.variables.has(id.name)) { if (this.variables.has(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 });
} }
group_transition_out(fn) {
return this.has_outros ? b`@group_transition_out((#transition_out) => { ${fn(x`#transition_out`)} })` : fn(null);
}
alias(name: string) { alias(name: string) {
if (!this.aliases.has(name)) { if (!this.aliases.has(name)) {
@ -228,7 +229,7 @@ export default class Block {
} }
get_contents(key?: any) { get_contents(key?: any) {
const { dev,version } = this.renderer.options; const { dev, version } = this.renderer.options;
if (this.has_outros) { if (this.has_outros) {
this.add_variable({ type: 'Identifier', name: '#current' }); this.add_variable({ type: 'Identifier', name: '#current' });
@ -263,11 +264,8 @@ export default class Block {
if (this.chunks.create.length === 0 && this.chunks.hydrate.length === 0) { if (this.chunks.create.length === 0 && this.chunks.hydrate.length === 0) {
properties.create = noop; properties.create = noop;
} else { } else {
const hydrate = this.chunks.hydrate.length > 0 && ( const hydrate =
this.renderer.options.hydratable this.chunks.hydrate.length > 0 && (this.renderer.options.hydratable ? b`this.h();` : this.chunks.hydrate);
? b`this.h();`
: this.chunks.hydrate
);
properties.create = x`function #create() { properties.create = x`function #create() {
${this.chunks.create} ${this.chunks.create}
@ -393,23 +391,25 @@ export default class Block {
${this.chunks.declarations} ${this.chunks.declarations}
${Array.from(this.variables.values()).map(({ id, init }) => { ${Array.from(this.variables.values()).map(({ id, init }) => {
return init return init ? b`let ${id} = ${init}` : b`let ${id}`;
? b`let ${id} = ${init}`
: b`let ${id}`;
})} })}
${this.chunks.init} ${this.chunks.init}
${dev ${
dev
? b` ? b`
const ${block} = ${return_value}; const ${block} = ${return_value};
${version < 3.22 && b`@dispatch_dev$legacy("SvelteRegisterBlock", { ${
version < 3.22 &&
b`@dispatch_dev$legacy("SvelteRegisterBlock", {
block: ${block}, block: ${block},
id: ${this.name || 'create_fragment'}.name, id: ${this.name || 'create_fragment'}.name,
type: "${this.type}", type: "${this.type}",
source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}",
ctx: #ctx ctx: #ctx
});`} });`
}
return ${block};` return ${block};`
: b` : b`
return ${return_value};` return ${return_value};`
@ -420,7 +420,8 @@ export default class Block {
} }
has_content(): boolean { has_content(): boolean {
return !!this.first || return (
!!this.first ||
this.event_listeners.length > 0 || this.event_listeners.length > 0 ||
this.chunks.intro.length > 0 || this.chunks.intro.length > 0 ||
this.chunks.outro.length > 0 || this.chunks.outro.length > 0 ||
@ -430,7 +431,8 @@ export default class Block {
this.chunks.mount.length > 0 || this.chunks.mount.length > 0 ||
this.chunks.update.length > 0 || this.chunks.update.length > 0 ||
this.chunks.destroy.length > 0 || this.chunks.destroy.length > 0 ||
this.has_animation; this.has_animation
);
} }
render() { render() {
@ -454,7 +456,7 @@ export default class Block {
if (this.event_listeners.length > 0) { if (this.event_listeners.length > 0) {
const dispose: Identifier = { const dispose: Identifier = {
type: 'Identifier', type: 'Identifier',
name: `#dispose${chunk}` name: `#dispose${chunk}`,
}; };
this.add_variable(dispose); this.add_variable(dispose);
@ -467,20 +469,16 @@ export default class Block {
` `
); );
this.chunks.destroy.push( this.chunks.destroy.push(b`${dispose}();`);
b`${dispose}();`
);
} else { } else {
this.chunks.mount.push(b` this.chunks.mount.push(b`
if (#remount) @run_all(${dispose}); if (#remount) for(let #i=0;#i<${dispose}.length;#i++){ ${dispose}[#i];}
${dispose} = [ ${dispose} = [
${this.event_listeners} ${this.event_listeners}
]; ];
`); `);
this.chunks.destroy.push( this.chunks.destroy.push(b`for(let #i=0;#i<${dispose}.length;#i++){ ${dispose}[#i];}`);
b`@run_all(${dispose});`
);
} }
} }
} }

@ -8,10 +8,7 @@ import { invalidate } from './invalidate';
import Block from './Block'; import Block from './Block';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree'; import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
export default function dom( export default function dom(component: Component, options: CompileOptions): { js: Node[]; css: CssResult } {
component: Component,
options: CompileOptions
): { js: Node[]; css: CssResult } {
const { name } = component; const { name } = component;
const renderer = new Renderer(component, options); const renderer = new Renderer(component, options);
@ -30,17 +27,14 @@ export default function dom(
} }
const css = component.stylesheet.render(options.filename, !options.customElement); const css = component.stylesheet.render(options.filename, !options.customElement);
const styles = component.stylesheet.has_styles && options.dev const styles =
component.stylesheet.has_styles && options.dev
? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */` ? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
: css.code; : css.code;
const add_css = component.get_unique_name('add_css'); const add_css = component.get_unique_name('add_css');
const should_add_css = ( const should_add_css = !options.customElement && !!styles && options.css !== false;
!options.customElement &&
!!styles &&
options.css !== false
);
if (should_add_css) { if (should_add_css) {
body.push(b` body.push(b`
@ -57,12 +51,14 @@ export default function dom(
// TODO the deconflicted names of blocks are reversed... should set them here // TODO the deconflicted names of blocks are reversed... should set them here
const blocks = renderer.blocks.slice().reverse(); const blocks = renderer.blocks.slice().reverse();
body.push(...blocks.map(block => { body.push(
...blocks.map((block) => {
// TODO this is a horrible mess — renderer.blocks // TODO this is a horrible mess — renderer.blocks
// contains a mixture of Blocks and Nodes // contains a mixture of Blocks and Nodes
if ((block as Block).render) return (block as Block).render(); if ((block as Block).render) return (block as Block).render();
return block; return block;
})); })
);
if (options.dev && !options.hydratable) { if (options.dev && !options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
@ -72,30 +68,44 @@ export default function dom(
const uses_props = component.var_lookup.has('$$props'); const uses_props = component.var_lookup.has('$$props');
const uses_rest = component.var_lookup.has('$$restProps'); const uses_rest = component.var_lookup.has('$$restProps');
const $$props = uses_props || uses_rest ? `$$new_props` : `$$props`; const uses_any = uses_props || uses_rest;
const props = component.vars.filter(variable => !variable.module && variable.export_name); const $$props = uses_any ? `$$new_props` : `$$props`;
const writable_props = props.filter(variable => variable.writable); const props = component.vars.filter((variable) => !variable.module && variable.export_name);
const writable_props = props.filter((variable) => variable.writable);
const omit_props_names = component.get_unique_name('omit_props_names');
const compute_rest = x`@compute_rest_props($$props, ${omit_props_names.name})`; const compute_rest = b`for (#k in $$props){ if (!#keys.has(#k) && $$props[#k][0] !== '$'){ $$restProps[#k] = $$props[#k];}}`;
const rest = uses_rest ? b` const rest = uses_rest
const ${omit_props_names.name} = [${props.map(prop => `"${prop.export_name}"`).join(',')}]; ? b`
let $$restProps = ${compute_rest}; let #k;
` : null; const #keys = new Set([${props.map((prop) => `"${prop.export_name}"`).join(',')}]);
let $$restProps = {};
const set = (uses_props || uses_rest || writable_props.length > 0 || component.slots.size > 0) ${compute_rest};`
? x` : null;
${$$props} => {
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`)} const set =
${uses_rest && !uses_props && x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`} uses_any || writable_props.length || component.slots.size
${uses_rest && renderer.invalidate('$$restProps', x`$$restProps = ${compute_rest}`)} ? x`(${$$props}) => {
${writable_props.map(prop => ${
b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};` uses_any &&
b`
${!uses_rest && x`let #k;`}
for (#k in $$new_props) if ($$new_props[#k][0] !== '$') $$props[k] = $$new_props[k];
${uses_rest && compute_rest}
${renderer.invalidate(uses_props ? '$$props' : '$$restProps')}
`
}
${writable_props.map(
(prop) =>
b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(
prop.name,
x`${prop.name} = ${$$props}.${prop.export_name}`
)};`
)} )}
${component.slots.size > 0 && ${
b`if ('$$scope' in ${$$props}) ${renderer.invalidate('$$scope', x`$$scope = ${$$props}.$$scope`)};`} component.slots.size &&
b`if ('$$scope' in ${$$props}) ${renderer.invalidate('$$scope', x`$$scope = ${$$props}.$$scope`)};`
} }
` }`
: null; : null;
const accessors = []; const accessors = [];
@ -106,7 +116,7 @@ export default function dom(
let capture_state: Expression; let capture_state: Expression;
let props_inject: Node[] | Node; let props_inject: Node[] | Node;
props.forEach(prop => { props.forEach((prop) => {
const variable = component.var_lookup.get(prop.name); const variable = component.var_lookup.get(prop.name);
if (!variable.writable || component.component_options.accessors) { if (!variable.writable || component.component_options.accessors) {
@ -116,7 +126,7 @@ export default function dom(
key: { type: 'Identifier', name: prop.export_name }, key: { type: 'Identifier', name: prop.export_name },
value: x`function() { value: x`function() {
return ${prop.hoistable ? prop.name : x`this.$$.ctx[${renderer.context_lookup.get(prop.name).index}]`} return ${prop.hoistable ? prop.name : x`this.$$.ctx[${renderer.context_lookup.get(prop.name).index}]`}
}` }`,
}); });
} else if (component.compile_options.dev) { } else if (component.compile_options.dev) {
accessors.push({ accessors.push({
@ -125,7 +135,7 @@ export default function dom(
key: { type: 'Identifier', name: prop.export_name }, key: { type: 'Identifier', name: prop.export_name },
value: x`function() { value: x`function() {
throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'"); throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}` }`,
}); });
} }
@ -138,7 +148,7 @@ export default function dom(
value: x`function(${prop.name}) { value: x`function(${prop.name}) {
this.$set({ ${prop.export_name}: ${prop.name} }); this.$set({ ${prop.export_name}: ${prop.name} });
@flush(); @flush();
}` }`,
}); });
} else if (component.compile_options.dev) { } else if (component.compile_options.dev) {
accessors.push({ accessors.push({
@ -147,7 +157,7 @@ export default function dom(
key: { type: 'Identifier', name: prop.export_name }, key: { type: 'Identifier', name: prop.export_name },
value: x`function(value) { value: x`function(value) {
throw new @_Error("<${component.tag}>: Cannot set read-only property '${prop.export_name}'"); throw new @_Error("<${component.tag}>: Cannot set read-only property '${prop.export_name}'");
}` }`,
}); });
} }
} else if (component.compile_options.dev) { } else if (component.compile_options.dev) {
@ -157,40 +167,42 @@ export default function dom(
key: { type: 'Identifier', name: prop.export_name }, key: { type: 'Identifier', name: prop.export_name },
value: x`function(value) { value: x`function(value) {
throw new @_Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'"); throw new @_Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}` }`,
}); });
} }
}); });
if (component.compile_options.dev) { if (component.compile_options.dev) {
// checking that expected ones were passed // checking that expected ones were passed
const expected = props.filter(prop => prop.writable && !prop.initialised); const expected = props.filter((prop) => prop.writable && !prop.initialised);
if (expected.length) { if (expected.length) {
dev_props_check = b` dev_props_check = b`
const { ctx: #ctx } = this.$$; const { ctx: #ctx } = this.$$;
const props = ${options.customElement ? x`this.attributes` : x`options.props || {}`}; const props = ${options.customElement ? x`this.attributes` : x`options.props || {}`};
${expected.map(prop => b` ${expected.map(
(prop) => b`
if (${renderer.reference(prop.name)} === undefined && !('${prop.export_name}' in props)) { if (${renderer.reference(prop.name)} === undefined && !('${prop.export_name}' in props)) {
@_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'");
}`)} }`
)}
`; `;
} }
const capturable_vars = component.vars.filter(v => !v.internal && !v.global && !v.name.startsWith('$$')); const capturable_vars = component.vars.filter((v) => !v.internal && !v.global && !v.name.startsWith('$$'));
if (capturable_vars.length > 0) { if (capturable_vars.length > 0) {
capture_state = x`() => ({ ${capturable_vars.map(prop => p`${prop.name}`)} })`; capture_state = x`() => ({ ${capturable_vars.map((prop) => p`${prop.name}`)} })`;
} }
const injectable_vars = capturable_vars.filter(v => !v.module && v.writable && v.name[0] !== '$'); const injectable_vars = capturable_vars.filter((v) => !v.module && v.writable && v.name[0] !== '$');
if (uses_props || injectable_vars.length > 0) { if (uses_props || injectable_vars.length > 0) {
inject_state = x` inject_state = x`
${$$props} => { ${$$props} => {
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)} ${uses_props && renderer.invalidate('$$props', x`$$props = { ...$$props, ...$$new_props }`)}
${injectable_vars.map( ${injectable_vars.map(
v => b`if ('${v.name}' in $$props) ${renderer.invalidate(v.name, x`${v.name} = ${$$props}.${v.name}`)};` (v) => b`if ('${v.name}' in $$props) ${renderer.invalidate(v.name, x`${v.name} = ${$$props}.${v.name}`)};`
)} )}
} }
`; `;
@ -242,16 +254,17 @@ export default function dom(
this.replace(invalidate(renderer, scope, node, names, execution_context === null)); this.replace(invalidate(renderer, scope, node, names, execution_context === null));
} }
} },
}); });
component.rewrite_props(({ name, reassigned, export_name }) => { component.rewrite_props(({ name, reassigned, export_name }) => {
const value = `$${name}`; const value = `$${name}`;
const i = renderer.context_lookup.get(`$${name}`).index; const i = renderer.context_lookup.get(`$${name}`).index;
const insert = (reassigned || export_name) const insert =
? b`${`$$subscribe_${name}`}()` reassigned || export_name
: b`@component_subscribe($$self, ${name}, #value => $$invalidate(${i}, ${value} = #value))`; ? b`${`$$subscribe_${name}`}();`
: b`$$self.$$.on_destroy.push(@subscribe(${name}, #value => $$invalidate(${i}, ${value} = #value)));`;
if (component.compile_options.dev) { if (component.compile_options.dev) {
return b`@validate_store(${name}, '${name}'); ${insert}`; return b`@validate_store(${name}, '${name}'); ${insert}`;
@ -262,11 +275,8 @@ export default function dom(
} }
const args = [x`$$self`]; const args = [x`$$self`];
const has_invalidate = props.length > 0 || const has_invalidate =
component.has_reactive_assignments || props.length > 0 || component.has_reactive_assignments || component.slots.size > 0 || capture_state || inject_state;
component.slots.size > 0 ||
capture_state ||
inject_state;
if (has_invalidate) { if (has_invalidate) {
args.push(x`$$props`, x`$$invalidate`); args.push(x`$$props`, x`$$invalidate`);
} else if (component.compile_options.dev) { } else if (component.compile_options.dev) {
@ -289,7 +299,7 @@ export default function dom(
${component.fully_hoisted} ${component.fully_hoisted}
`); `);
const filtered_props = props.filter(prop => { const filtered_props = props.filter((prop) => {
const variable = component.var_lookup.get(prop.name); const variable = component.var_lookup.get(prop.name);
if (variable.hoistable) return false; if (variable.hoistable) return false;
@ -297,7 +307,7 @@ export default function dom(
return true; return true;
}); });
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$'); const reactive_stores = component.vars.filter((variable) => variable.name[0] === '$' && variable.name[1] !== '$');
const instance_javascript = component.extract_javascript(component.ast.instance); const instance_javascript = component.extract_javascript(component.ast.instance);
@ -312,7 +322,7 @@ export default function dom(
} }
const initial_context = renderer.context.slice(0, i + 1); const initial_context = renderer.context.slice(0, i + 1);
const has_definition = ( const has_definition =
component.compile_options.dev || component.compile_options.dev ||
(instance_javascript && instance_javascript.length > 0) || (instance_javascript && instance_javascript.length > 0) ||
filtered_props.length > 0 || filtered_props.length > 0 ||
@ -321,39 +331,40 @@ export default function dom(
initial_context.length > 0 || initial_context.length > 0 ||
component.reactive_declarations.length > 0 || component.reactive_declarations.length > 0 ||
capture_state || capture_state ||
inject_state inject_state;
);
const definition = has_definition const definition = has_definition ? component.alias('instance') : { type: 'Literal', value: null };
? component.alias('instance')
: { type: 'Literal', value: null };
const reactive_store_subscriptions = reactive_stores const reactive_store_subscriptions = reactive_stores
.filter(store => { .filter((store) => {
const variable = component.var_lookup.get(store.name.slice(1)); const variable = component.var_lookup.get(store.name.slice(1));
return !variable || variable.hoistable; return !variable || variable.hoistable;
}) })
.map(({ name }) => b` .map(
({ name }) => b`
${component.compile_options.dev && b`@validate_store(${name.slice(1)}, '${name.slice(1)}');`} ${component.compile_options.dev && b`@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
@component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate(${renderer.context_lookup.get(name).index}, ${name} = $$value)); $$self.$$.on_destroy.push(@subscribe(${name.slice(1)}, #value => $$invalidate(${
`); renderer.context_lookup.get(name).index
}, ${name} = #value));
`
);
const resubscribable_reactive_store_unsubscribers = reactive_stores const resubscribable_reactive_store_unsubscribers = reactive_stores
.filter(store => { .filter((store) => {
const variable = component.var_lookup.get(store.name.slice(1)); const variable = component.var_lookup.get(store.name.slice(1));
return variable && (variable.reassigned || variable.export_name); return variable && (variable.reassigned || variable.export_name);
}) })
.map(({ name }) => b`$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`); .map(({ name }) => b`$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`);
if (has_definition) { if (has_definition) {
const reactive_declarations: (Node | Node[]) = []; const reactive_declarations: Node | Node[] = [];
const fixed_reactive_declarations = []; // not really 'reactive' but whatever const fixed_reactive_declarations = []; // not really 'reactive' but whatever
component.reactive_declarations.forEach(d => { component.reactive_declarations.forEach((d) => {
const dependencies = Array.from(d.dependencies); const dependencies = Array.from(d.dependencies);
const uses_rest_or_props = !!dependencies.find(n => n === '$$props' || n === '$$restProps'); const uses_rest_or_props = !!dependencies.find((n) => n === '$$props' || n === '$$restProps');
const writable = dependencies.filter(n => { const writable = dependencies.filter((n) => {
const variable = component.var_lookup.get(n); const variable = component.var_lookup.get(n);
return variable && (variable.export_name || variable.mutated || variable.reassigned); return variable && (variable.export_name || variable.mutated || variable.reassigned);
}); });
@ -371,12 +382,12 @@ export default function dom(
} }
}); });
const injected = Array.from(component.injected_reactive_declaration_vars).filter(name => { const injected = Array.from(component.injected_reactive_declaration_vars).filter((name) => {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
return variable.injected && variable.name[0] !== '$'; return variable.injected && variable.name[0] !== '$';
}); });
const reactive_store_declarations = reactive_stores.map(variable => { const reactive_store_declarations = reactive_stores.map((variable) => {
const $name = variable.name; const $name = variable.name;
const name = $name.slice(1); const name = $name.slice(1);
@ -395,19 +406,24 @@ export default function dom(
let unknown_props_check; let unknown_props_check;
if (component.compile_options.dev && !(uses_props || uses_rest)) { if (component.compile_options.dev && !(uses_props || uses_rest)) {
unknown_props_check = b` unknown_props_check = b`
const writable_props = [${writable_props.map(prop => x`'${prop.export_name}'`)}]; const writable_props = [${writable_props.map((prop) => x`'${prop.export_name}'`)}];
@_Object.keys($$props).forEach(key => { @_Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$') @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$') @_console.warn(\`<${
component.tag
}> was created with unknown prop '\${key}'\`);
}); });
`; `;
} }
const return_value = { const return_value = {
type: 'ArrayExpression', type: 'ArrayExpression',
elements: initial_context.map(member => ({ elements: initial_context.map(
(member) =>
({
type: 'Identifier', type: 'Identifier',
name: member.name name: member.name,
}) as Expression) } as Expression)
),
}; };
body.push(b` body.push(b`
@ -425,9 +441,14 @@ export default function dom(
${unknown_props_check} ${unknown_props_check}
${component.slots.size || component.compile_options.dev ? b`let { $$slots = {}, $$scope } = $$props;` : null} ${component.slots.size || component.compile_options.dev ? b`let { $$slots = {}, $$scope } = $$props;` : null}
${component.compile_options.dev && b`@validate_slots('${component.tag}', $$slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`} ${
component.compile_options.dev &&
b`@validate_slots('${component.tag}', $$slots, [${[...component.slots.keys()]
.map((key) => `'${key}'`)
.join(',')}]);`
}
${renderer.binding_groups.length > 0 && b`const $$binding_groups = [${renderer.binding_groups.map(_ => x`[]`)}];`} ${renderer.binding_groups.length && b`const $$binding_groups = [${renderer.binding_groups.map((_) => x`[]`)}];`}
${component.partly_hoisted} ${component.partly_hoisted}
@ -437,19 +458,15 @@ export default function dom(
${inject_state && b`$$self.$inject_state = ${inject_state};`} ${inject_state && b`$$self.$inject_state = ${inject_state};`}
${injected.map(name => b`let ${name};`)} ${injected.map((name) => b`let ${name};`)}
${/* before reactive declarations */ props_inject} ${/* before reactive declarations */ props_inject}
${reactive_declarations.length > 0 && b` ${reactive_declarations.length && b`$$self.$$.update = () => {${reactive_declarations}};`}
$$self.$$.update = () => {
${reactive_declarations}
};
`}
${fixed_reactive_declarations} ${fixed_reactive_declarations}
${uses_props && b`$$props = @exclude_internal_props($$props);`} ${uses_props && b`let #k;for (#k in $$props) if ($$props[#k][0] === '$') delete $$props[#k];`}
return ${return_value}; return ${return_value};
} }
@ -457,7 +474,9 @@ export default function dom(
} }
const prop_indexes = x`{ const prop_indexes = x`{
${props.filter(v => v.export_name && !v.module).map(v => p`${v.export_name}: ${renderer.context_lookup.get(v.name).index}`)} ${props
.filter((v) => v.export_name && !v.module)
.map((v) => p`${v.export_name}: ${renderer.context_lookup.get(v.name).index}`)}
}` as ObjectExpression; }` as ObjectExpression;
let dirty; let dirty;
@ -474,9 +493,16 @@ export default function dom(
constructor(options) { constructor(options) {
super(); super();
${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}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty}); @init(this, { target: this.shadowRoot }, ${definition}, ${
has_create_fragment ? 'create_fragment' : 'null'
}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check} ${dev_props_check}
@ -485,11 +511,14 @@ export default function dom(
@insert(options.target, this, options.anchor); @insert(options.target, this, options.anchor);
} }
${(props.length > 0 || uses_props || uses_rest) && b` ${
(props.length > 0 || uses_props || uses_rest) &&
b`
if (options.props) { if (options.props) {
this.$set(options.props); this.$set(options.props);
@flush(); @flush();
}`} }`
}
} }
} }
} }
@ -503,8 +532,8 @@ export default function dom(
computed: false, computed: false,
key: { type: 'Identifier', name: 'observedAttributes' }, key: { type: 'Identifier', name: 'observedAttributes' },
value: x`function() { value: x`function() {
return [${props.map(prop => x`"${prop.export_name}"`)}]; return [${props.map((prop) => x`"${prop.export_name}"`)}];
}` as FunctionExpression }` as FunctionExpression,
}); });
} }
@ -520,7 +549,7 @@ export default function dom(
} else { } else {
const superclass = { const superclass = {
type: 'Identifier', type: 'Identifier',
name: options.dev ? '@SvelteComponentDev' : '@SvelteComponent' name: options.dev ? '@SvelteComponentDev' : '@SvelteComponent',
}; };
const declaration = b` const declaration = b`
@ -528,8 +557,14 @@ 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}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty}); @init(this, options, ${definition}, ${
${options.dev && options.version < 3.22 && b`@dispatch_dev$legacy("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} has_create_fragment ? 'create_fragment' : 'null'
}, ${not_equal}, ${prop_indexes}, ${dirty});
${
options.dev &&
options.version < 3.22 &&
b`@dispatch_dev$legacy("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`
}
${dev_props_check} ${dev_props_check}
} }

@ -5,27 +5,32 @@ import { Node, Expression } from 'estree';
import Renderer from './Renderer'; import Renderer from './Renderer';
import { Var } from '../../interfaces'; import { Var } from '../../interfaces';
export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: Set<string>, main_execution_context: boolean = false) { export function invalidate(
renderer: Renderer,
scope: Scope,
node: Node,
names: Set<string>,
main_execution_context: boolean = false
) {
const { component } = renderer; const { component } = renderer;
const [head, ...tail] = Array.from(names) const [head, ...tail] = Array.from(names)
.filter(name => { .filter((name) => {
const owner = scope.find_owner(name); const owner = scope.find_owner(name);
return !owner || owner === component.instance_scope; return !owner || owner === component.instance_scope;
}) })
.map(name => component.var_lookup.get(name)) .map((name) => component.var_lookup.get(name))
.filter(variable => { .filter((variable) => {
return variable && ( return (
variable &&
!variable.hoistable && !variable.hoistable &&
!variable.global && !variable.global &&
!variable.module && !variable.module &&
( (variable.referenced ||
variable.referenced ||
variable.subscribable || variable.subscribable ||
variable.is_reactive_dependency || variable.is_reactive_dependency ||
variable.export_name || variable.export_name ||
variable.name[0] === '$' variable.name[0] === '$')
)
); );
}) as Var[]; }) as Var[];
@ -40,22 +45,26 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
if (head) { if (head) {
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) { if (
!tail.length &&
node.type === 'AssignmentExpression' &&
node.operator === '=' &&
nodes_match(node.left, node.right)
) {
return get_invalidated(head, node); return get_invalidated(head, node);
} else { } else {
const is_store_value = head.name[0] === '$' && head.name[1] !== '$'; const is_store_value = head.name[0] === '$' && head.name[1] !== '$';
const extra_args = tail.map(variable => get_invalidated(variable)); const extra_args = tail.map((variable) => get_invalidated(variable));
const pass_value = ( const pass_value =
extra_args.length > 0 || extra_args.length > 0 ||
(node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
(node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')) (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier'));
);
if (pass_value) { if (pass_value) {
extra_args.unshift({ extra_args.unshift({
type: 'Identifier', type: 'Identifier',
name: head.name name: head.name,
}); });
} }

@ -33,7 +33,7 @@ class AwaitBlockBranch extends Wrapper {
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component), comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.get_unique_name(`create_${status}_block`), name: this.renderer.component.get_unique_name(`create_${status}_block`),
type: status type: status,
}); });
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(
@ -111,18 +111,10 @@ export default class AwaitBlockWrapper extends Wrapper {
let has_intros = false; let has_intros = false;
let has_outros = false; let has_outros = false;
['pending', 'then', 'catch'].forEach(status => { ['pending', 'then', 'catch'].forEach((status) => {
const child = this.node[status]; const child = this.node[status];
const branch = new AwaitBlockBranch( const branch = new AwaitBlockBranch(status, renderer, block, this, child, strip_whitespace, next_sibling);
status,
renderer,
block,
this,
child,
strip_whitespace,
next_sibling
);
renderer.blocks.push(branch.block); renderer.blocks.push(branch.block);
@ -138,7 +130,7 @@ export default class AwaitBlockWrapper extends Wrapper {
this[status] = branch; this[status] = branch;
}); });
['pending', 'then', 'catch'].forEach(status => { ['pending', 'then', 'catch'].forEach((status) => {
this[status].block.has_update_method = is_dynamic; this[status].block.has_update_method = is_dynamic;
this[status].block.has_intro_method = has_intros; this[status].block.has_intro_method = has_intros;
this[status].block.has_outro_method = has_outros; this[status].block.has_outro_method = has_outros;
@ -149,11 +141,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} }
} }
render( render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
block: Block,
parent_node: Identifier,
parent_nodes: Identifier
) {
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes); const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const update_mount_node = this.get_update_mount_node(anchor); const update_mount_node = this.get_update_mount_node(anchor);
@ -222,9 +210,7 @@ export default class AwaitBlockWrapper extends Wrapper {
${promise} !== (${promise} = ${snippet}) && ${promise} !== (${promise} = ${snippet}) &&
@handle_promise(${promise}, ${info})`; @handle_promise(${promise}, ${info})`;
block.chunks.update.push( block.chunks.update.push(b`${info}.ctx = #ctx;`);
b`${info}.ctx = #ctx;`
);
if (this.pending.block.has_update_method) { if (this.pending.block.has_update_method) {
block.chunks.update.push(b` block.chunks.update.push(b`
@ -256,8 +242,7 @@ export default class AwaitBlockWrapper extends Wrapper {
if (this.pending.block.has_outro_method) { if (this.pending.block.has_outro_method) {
block.chunks.outro.push(b` block.chunks.outro.push(b`
for (let #i = 0; #i < 3; #i += 1) { for (let #i = 0; #i < 3; #i += 1) {
const block = ${info}.blocks[#i]; @transition_out(${info}.blocks[#i]);
@transition_out(block);
} }
`); `);
} }
@ -268,7 +253,7 @@ export default class AwaitBlockWrapper extends Wrapper {
${info} = null; ${info} = null;
`); `);
[this.pending, this.then, this.catch].forEach(branch => { [this.pending, this.then, this.catch].forEach((branch) => {
branch.render(branch.block, null, x`#nodes` as Identifier); branch.render(branch.block, null, x`#nodes` as Identifier);
}); });
this.then.render_destructure(block, this.value, this.node.value, value_index); this.then.render_destructure(block, this.value, this.node.value, value_index);

@ -407,33 +407,38 @@ export default class EachBlockWrapper extends Wrapper {
const dynamic = this.block.has_update_method; const dynamic = this.block.has_update_method;
const destroy = this.node.has_animation // const destroy = this.node.has_animation
? this.block.has_outros // ? this.block.has_outros
? `@fix_and_outro_and_destroy_block` // ? `@fix_and_outro_and_destroy_block`
: `@fix_and_destroy_block` // : `@fix_and_destroy_block`
: this.block.has_outros // : this.block.has_outros
? `@outro_and_destroy_block` // ? `@outro_and_destroy_block`
: `@destroy_block`; // : `@destroy_block`;
if (this.dependencies.size) { if (this.dependencies.size) {
this.updates.push(b` this.updates.push(
b`
const ${this.vars.each_block_value} = ${snippet}; const ${this.vars.each_block_value} = ${snippet};
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`} ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}`,
this.block.group_transition_out(
${this.block.has_outros && b`@group_outros();`} (transition_out) => b`
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].r();}`} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].r();}`}
${ ${
this.renderer.options.dev && this.renderer.options.dev &&
b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});` b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`
} }
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${ ${iterations} = @update_keyed_each(${iterations}, #dirty, #ctx, ${bit_state([
dynamic,
this.node.has_animation,
this.block.has_outros,
])}, ${get_key}, ${
this.vars.each_block_value this.vars.each_block_value
}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${ }, ${lookup}, ${update_mount_node}, ${create_each_block}, ${update_anchor_node}, ${
this.vars.get_each_context this.vars.get_each_context
}); }, ${transition_out});
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].a();}`} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].a();}`}`
${this.block.has_outros && b`@check_outros();`} )
`); );
} }
if (this.block.has_outros) { if (this.block.has_outros) {
@ -533,33 +538,21 @@ export default class EachBlockWrapper extends Wrapper {
const start = this.block.has_update_method ? 0 : `#old_length`; const start = this.block.has_update_method ? 0 : `#old_length`;
let remove_old_blocks; const remove_old_blocks = this.block.group_transition_out((transition_out) =>
transition_out
if (this.block.has_outros) { ? b`
const out = block.get_unique_name('out');
block.chunks.init.push(b`
const ${out} = i => @transition_out(${iterations}[i], 1, 1, () => {
${iterations}[i] = null;
});
`);
remove_old_blocks = b`
@group_outros();
for (#i = ${data_length}; #i < ${view_length}; #i += 1) { for (#i = ${data_length}; #i < ${view_length}; #i += 1) {
${out}(#i); ${transition_out}(${iterations}[#i], () => { ${iterations}[#i] = null; });
} }`
@check_outros(); : b`
`; for (${this.block.has_update_method ? null : x`#i = ${data_length}`};
} else { #i < ${this.block.has_update_method ? view_length : '#old_length'};
remove_old_blocks = b` #i += 1 ) {
for (${this.block.has_update_method ? null : x`#i = ${data_length}`}; #i < ${
this.block.has_update_method ? view_length : '#old_length'
}; #i += 1) {
${iterations}[#i].d(1); ${iterations}[#i].d(1);
} }
${!fixed_length && b`${view_length} = ${data_length};`} ${!fixed_length && b`${view_length} = ${data_length};`}
`; `
} );
// We declare `i` as block scoped here, as the `remove_old_blocks` code // We declare `i` as block scoped here, as the `remove_old_blocks` code
// may rely on continuing where this iteration stopped. // may rely on continuing where this iteration stopped.
@ -593,3 +586,4 @@ export default class EachBlockWrapper extends Wrapper {
block.chunks.destroy.push(b`@destroy_each(${iterations}, detaching);`); block.chunks.destroy.push(b`@destroy_each(${iterations}, detaching);`);
} }
} }
const bit_state = (arr) => arr.reduce((state, bool, index) => (bool ? (state |= 1 << index) : state), 0);

@ -29,7 +29,7 @@ export default class AttributeWrapper {
select = select.parent; select = select.parent;
if (select && select.select_binding_dependencies) { if (select && select.select_binding_dependencies) {
select.select_binding_dependencies.forEach(prop => { select.select_binding_dependencies.forEach((prop) => {
this.node.dependencies.forEach((dependency: string) => { this.node.dependencies.forEach((dependency: string) => {
this.parent.renderer.component.indirect_dependencies.get(prop).add(dependency); this.parent.renderer.component.indirect_dependencies.get(prop).add(dependency);
}); });
@ -42,13 +42,11 @@ export default class AttributeWrapper {
is_indirectly_bound_value() { is_indirectly_bound_value() {
const element = this.parent; const element = this.parent;
const name = fix_attribute_casing(this.node.name); const name = fix_attribute_casing(this.node.name);
return name === 'value' && return (
name === 'value' &&
(element.node.name === 'option' || // TODO check it's actually bound (element.node.name === 'option' || // TODO check it's actually bound
(element.node.name === 'input' && (element.node.name === 'input' && element.node.bindings.some((binding) => /checked|group/.test(binding.name))))
element.node.bindings.some( );
(binding) =>
/checked|group/.test(binding.name)
)));
} }
render(block: Block) { render(block: Block) {
@ -59,9 +57,7 @@ export default class AttributeWrapper {
const is_indirectly_bound_value = this.is_indirectly_bound_value(); const is_indirectly_bound_value = this.is_indirectly_bound_value();
const property_name = is_indirectly_bound_value const property_name = is_indirectly_bound_value ? '__value' : metadata && metadata.property_name;
? '__value'
: metadata && metadata.property_name;
// xlink is a special case... we could maybe extend this to generic // xlink is a special case... we could maybe extend this to generic
// namespaced attributes but I'm not sure that's applicable in // namespaced attributes but I'm not sure that's applicable in
@ -72,34 +68,25 @@ export default class AttributeWrapper {
? '@xlink_attr' ? '@xlink_attr'
: '@attr'; : '@attr';
const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input';
const dependencies = this.node.get_dependencies(); const dependencies = this.node.get_dependencies();
const value = this.get_value(block); const value = this.get_value(block);
const is_src = this.node.name === 'src'; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750 const is_src = this.node.name === 'src'; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
const is_select_value_attribute = const is_select_value_attribute = name === 'value' && element.node.name === 'select';
name === 'value' && element.node.name === 'select';
const is_input_value = name === 'value' && element.node.name === 'input'; const is_input_value = name === 'value' && element.node.name === 'input';
const should_cache = is_src || this.node.should_cache() || is_select_value_attribute; // TODO is this necessary? const should_cache = is_src || this.node.should_cache() || is_select_value_attribute; // TODO is this necessary?
const last = should_cache && block.get_unique_name( const last =
`${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` should_cache && block.get_unique_name(`${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`);
);
if (should_cache) block.add_variable(last); if (should_cache) block.add_variable(last);
let updater; let updater;
const init = should_cache ? x`${last} = ${value}` : value; const init = should_cache ? x`${last} = ${value}` : value;
if (is_legacy_input_type) { if (is_select_value_attribute) {
block.chunks.hydrate.push(
b`@set_input_type(${element.var}, ${init});`
);
updater = b`@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) {
// annoying special case // annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple'); const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i'); const i = block.get_unique_name('i');
@ -127,21 +114,15 @@ export default class AttributeWrapper {
${updater} ${updater}
`); `);
} else if (is_src) { } else if (is_src) {
block.chunks.hydrate.push( block.chunks.hydrate.push(b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${last});`);
b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${last});`
);
updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`; updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
} else if (property_name) { } else if (property_name) {
block.chunks.hydrate.push( block.chunks.hydrate.push(b`${element.var}.${property_name} = ${init};`);
b`${element.var}.${property_name} = ${init};`
);
updater = block.renderer.options.dev updater = block.renderer.options.dev
? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? last : value});` ? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? last : value});`
: b`${element.var}.${property_name} = ${should_cache ? last : value};`; : b`${element.var}.${property_name} = ${should_cache ? last : value};`;
} else { } else {
block.chunks.hydrate.push( block.chunks.hydrate.push(b`${method}(${element.var}, "${name}", ${init});`);
b`${method}(${element.var}, "${name}", ${init});`
);
updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`; updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
} }
@ -157,7 +138,7 @@ export default class AttributeWrapper {
if (is_input_value) { if (is_input_value) {
const type = element.node.get_static_attribute_value('type'); const type = element.node.get_static_attribute_value('type');
if (type === null || type === "" || type === "text" || type === "email" || type === "password") { if (type === null || type === '' || type === 'text' || type === 'email' || type === 'password') {
condition = x`${condition} && ${element.var}.${property_name} !== ${should_cache ? last : value}`; condition = x`${condition} && ${element.var}.${property_name} !== ${should_cache ? last : value}`;
} }
} }
@ -210,7 +191,8 @@ export default class AttributeWrapper {
: (this.node.chunks[0] as Expression).manipulate(block); : (this.node.chunks[0] as Expression).manipulate(block);
} }
let value = this.node.name === 'class' let value =
this.node.name === 'class'
? this.get_class_name_text(block) ? this.get_class_name_text(block)
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`); : this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
@ -228,7 +210,7 @@ export default class AttributeWrapper {
if (scoped_css && rendered.length === 2) { if (scoped_css && rendered.length === 2) {
// we have a situation like class={possiblyUndefined} // we have a situation like class={possiblyUndefined}
rendered[0] = x`@null_to_empty(${rendered[0]})`; rendered[0] = x`${rendered[0]} ?? ""`;
} }
return rendered.reduce((lhs, rhs) => x`${lhs} + ${rhs}`); return rendered.reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
@ -250,11 +232,11 @@ export default class AttributeWrapper {
const value = this.node.chunks; const value = this.node.chunks;
if (value.length === 0) return `=""`; if (value.length === 0) return `=""`;
return `="${value.map(chunk => { return `="${value
return chunk.type === 'Text' .map((chunk) => {
? chunk.data.replace(/"/g, '\\"') return chunk.type === 'Text' ? chunk.data.replace(/"/g, '\\"') : `\${${chunk.manipulate()}}`;
: `\${${chunk.manipulate()}}`; })
}).join('')}"`; .join('')}"`;
} }
} }
@ -270,16 +252,7 @@ const attribute_lookup = {
default: { applies_to: ['track'] }, default: { applies_to: ['track'] },
defer: { applies_to: ['script'] }, defer: { applies_to: ['script'] },
disabled: { disabled: {
applies_to: [ applies_to: ['button', 'fieldset', 'input', 'keygen', 'optgroup', 'option', 'select', 'textarea'],
'button',
'fieldset',
'input',
'keygen',
'optgroup',
'option',
'select',
'textarea',
],
}, },
formnovalidate: { property_name: 'formNoValidate', applies_to: ['button', 'input'] }, formnovalidate: { property_name: 'formNoValidate', applies_to: ['button', 'input'] },
hidden: {}, hidden: {},
@ -297,21 +270,11 @@ const attribute_lookup = {
reversed: { applies_to: ['ol'] }, reversed: { applies_to: ['ol'] },
selected: { applies_to: ['option'] }, selected: { applies_to: ['option'] },
value: { value: {
applies_to: [ applies_to: ['button', 'option', 'input', 'li', 'meter', 'progress', 'param', 'select', 'textarea'],
'button',
'option',
'input',
'li',
'meter',
'progress',
'param',
'select',
'textarea',
],
}, },
}; };
Object.keys(attribute_lookup).forEach(name => { Object.keys(attribute_lookup).forEach((name) => {
const metadata = attribute_lookup[name]; const metadata = attribute_lookup[name];
if (!metadata.property_name) metadata.property_name = name; if (!metadata.property_name) metadata.property_name = name;
}); });
@ -342,5 +305,5 @@ const boolean_attribute = new Set([
'readonly', 'readonly',
'required', 'required',
'reversed', 'reversed',
'selected' 'selected',
]); ]);

@ -27,11 +27,13 @@ export default class EventHandlerWrapper {
} }
get_snippet(block) { get_snippet(block) {
const snippet = this.node.expression ? this.node.expression.manipulate(block) : block.renderer.reference(this.node.handler_name); const snippet = this.node.expression
? this.node.expression.manipulate(block)
: block.renderer.reference(this.node.handler_name);
if (this.node.reassigned) { if (this.node.reassigned) {
block.maintain_context = true; block.maintain_context = true;
return x`function () { if (@is_function(${snippet})) ${snippet}.apply(this, arguments); }`; return x`function () { if ("function" === typeof (${snippet})) ${snippet}.apply(this, arguments); }`;
} }
return snippet; return snippet;
} }
@ -45,11 +47,9 @@ export default class EventHandlerWrapper {
const args = []; const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); const opts = ['passive', 'once', 'capture'].filter((mod) => this.node.modifiers.has(mod));
if (opts.length) { if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture') args.push(opts.length === 1 && opts[0] === 'capture' ? TRUE : x`{ ${opts.map((opt) => p`${opt}: true`)} }`);
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
} else if (block.renderer.options.dev) { } else if (block.renderer.options.dev) {
args.push(FALSE); args.push(FALSE);
} }
@ -59,8 +59,6 @@ export default class EventHandlerWrapper {
args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE); args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE);
} }
block.event_listeners.push( block.event_listeners.push(x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})`);
x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})`
);
} }
} }

@ -372,7 +372,9 @@ export default class ElementWrapper extends Wrapper {
if (renderer.options.dev) { if (renderer.options.dev) {
const loc = renderer.locate(this.node.start); const loc = renderer.locate(this.node.start);
block.chunks.hydrate.push( block.chunks.hydrate.push(
b`@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${this.node.start});` b`@add_location_dev$legacy(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${
this.node.start
});`
); );
} }
} }
@ -387,7 +389,7 @@ export default class ElementWrapper extends Wrapper {
get_render_statement(block: Block) { get_render_statement(block: Block) {
const { name, namespace } = this.node; const { name, namespace } = this.node;
if (namespace === 'http://www.w3.org/2000/svg') { if (namespace === namespaces.svg) {
return x`@svg_element("${name}")`; return x`@svg_element("${name}")`;
} }
@ -654,11 +656,10 @@ export default class ElementWrapper extends Wrapper {
}); });
block.chunks.init.push(b` block.chunks.init.push(b`
let ${levels} = [${initial_props}]; const ${levels} = [${initial_props}];
const ${data} = ${levels}[0]||{};
let ${data} = {}; for (let #i = 1; #i < ${levels}.length; #i += 1) {
for (let #i = 0; #i < ${levels}.length; #i += 1) { ${data} = { ...${data}, ...${levels}[#i] };
${data} = @assign(${data}, ${levels}[#i]);
} }
`); `);
@ -669,15 +670,14 @@ export default class ElementWrapper extends Wrapper {
block.chunks.update.push(b`${fn}(${this.var}, @get_spread_update(${levels}, [${updates}]));`); block.chunks.update.push(b`${fn}(${this.var}, @get_spread_update(${levels}, [${updates}]));`);
} }
add_bidi_transition(block: Block, intro: Transition) { add_bidi_transition(block: Block, intro: Transition) {
const name = block.get_unique_name(`${this.var.name}_bidi_transition`); const name = block.get_unique_name(`${this.var.name}_transition`);
const snippet = intro.expression ? intro.expression.manipulate(block) : x`{}`; const snippet = intro.expression ? intro.expression.manipulate(block) : null;
block.add_variable(name); block.add_variable(name);
const fn = this.renderer.reference(intro.name); const fn = this.renderer.reference(intro.name);
let intro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, true, ${snippet});`; let intro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, true, ${snippet});`;
let outro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, false, ${snippet});`; let outro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, false, ${snippet});`;
if (intro.is_local) { if (intro.is_local) {

@ -16,7 +16,6 @@ import Window from './Window';
import { INode } from '../../nodes/interfaces'; import { INode } from '../../nodes/interfaces';
import Renderer from '../Renderer'; import Renderer from '../Renderer';
import Block from '../Block'; import Block from '../Block';
import { trim_start, trim_end } from '../../../utils/trim';
import { link } from '../../../utils/link'; import { link } from '../../../utils/link';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
@ -36,14 +35,17 @@ const wrappers = {
Slot, Slot,
Text, Text,
Title, Title,
Window Window,
}; };
function trimmable_at(child: INode, next_sibling: Wrapper): boolean { function trimmable_at(child: INode, next_sibling: Wrapper): boolean {
// Whitespace is trimmable if one of the following is true: // Whitespace is trimmable if one of the following is true:
// The child and its sibling share a common nearest each block (not at an each block boundary) // The child and its sibling share a common nearest each block (not at an each block boundary)
// The next sibling's previous node is an each block // The next sibling's previous node is an each block
return (next_sibling.node.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/)) || next_sibling.node.prev.type === 'EachBlock'; return (
next_sibling.node.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/) ||
next_sibling.node.prev.type === 'EachBlock'
);
} }
export default class FragmentWrapper { export default class FragmentWrapper {
@ -87,12 +89,14 @@ export default class FragmentWrapper {
// We want to remove trailing whitespace inside an element/component/block, // We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling // *unless* there is no whitespace between this node and its next sibling
if (this.nodes.length === 0) { if (this.nodes.length === 0) {
const should_trim = ( const should_trim = next_sibling
next_sibling ? (next_sibling.node.type === 'Text' && /^\s/.test(next_sibling.node.data) && trimmable_at(child, next_sibling)) : !child.has_ancestor('EachBlock') ? next_sibling.node.type === 'Text' &&
); /^\s/.test(next_sibling.node.data) &&
trimmable_at(child, next_sibling)
: !child.has_ancestor('EachBlock');
if (should_trim) { if (should_trim) {
data = trim_end(data); data = data.trimRight();
if (!data) continue; if (!data) continue;
} }
} }
@ -108,7 +112,7 @@ export default class FragmentWrapper {
this.nodes.unshift(wrapper); this.nodes.unshift(wrapper);
link(last_child, last_child = wrapper); link(last_child, (last_child = wrapper));
} else { } else {
const Wrapper = wrappers[child.type]; const Wrapper = wrappers[child.type];
if (!Wrapper) continue; if (!Wrapper) continue;
@ -116,7 +120,7 @@ export default class FragmentWrapper {
const wrapper = new Wrapper(renderer, block, parent, child, strip_whitespace, last_child || next_sibling); const wrapper = new Wrapper(renderer, block, parent, child, strip_whitespace, last_child || next_sibling);
this.nodes.unshift(wrapper); this.nodes.unshift(wrapper);
link(last_child, last_child = wrapper); link(last_child, (last_child = wrapper));
} }
} }
@ -124,7 +128,7 @@ export default class FragmentWrapper {
const first = this.nodes[0] as Text; const first = this.nodes[0] as Text;
if (first && first.node.type === 'Text') { if (first && first.node.type === 'Text') {
first.data = trim_start(first.data); first.data = first.data.trimLeft();
if (!first.data) { if (!first.data) {
first.var = null; first.var = null;
this.nodes.shift(); this.nodes.shift();

@ -22,29 +22,22 @@ export default class HeadWrapper extends Wrapper {
this.can_use_innerhtml = false; this.can_use_innerhtml = false;
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling);
renderer,
block,
node.children,
this,
strip_whitespace,
next_sibling
);
} }
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
let nodes; let nodes;
if (this.renderer.options.hydratable && this.fragment.nodes.length) { if (this.renderer.options.hydratable && this.fragment.nodes.length) {
nodes = block.get_unique_name('head_nodes'); nodes = block.get_unique_name('head_nodes');
block.chunks.claim.push(b`const ${nodes} = @query_selector_all('[data-svelte="${this.node.id}"]', @_document.head);`); block.chunks.claim.push(
b`const ${nodes} = Array.from((@_document.head||document.body).querySelectorAll('[data-svelte="${this.node.id}"]'));`
);
} }
this.fragment.render(block, x`@_document.head` as unknown as Identifier, nodes); this.fragment.render(block, (x`@_document.head` as unknown) as Identifier, nodes);
if (nodes && this.renderer.options.hydratable) { if (nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(b`${nodes}.forEach(@detach);`);
b`${nodes}.forEach(@detach);`
);
} }
} }
} }

@ -12,9 +12,7 @@ import { is_head } from './shared/is_head';
import { Identifier, Node, UnaryExpression } from 'estree'; import { Identifier, Node, UnaryExpression } from 'estree';
function is_else_if(node: ElseBlock) { function is_else_if(node: ElseBlock) {
return ( return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
node && node.children.length === 1 && node.children[0].type === 'IfBlock'
);
} }
class IfBlockBranch extends Wrapper { class IfBlockBranch extends Wrapper {
@ -37,7 +35,7 @@ class IfBlockBranch extends Wrapper {
) { ) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
const { expression } = (node as IfBlock); const { expression } = node as IfBlock;
const is_else = !expression; const is_else = !expression;
if (expression) { if (expression) {
@ -51,12 +49,12 @@ class IfBlockBranch extends Wrapper {
if (node.type === 'CallExpression' || node.type === 'NewExpression') { if (node.type === 'CallExpression' || node.type === 'NewExpression') {
should_cache = true; should_cache = true;
} }
} },
}); });
if (should_cache) { if (should_cache) {
this.condition = block.get_unique_name(`show_if`); this.condition = block.get_unique_name(`show_if`);
this.snippet = (expression.manipulate(block) as Node); this.snippet = expression.manipulate(block) as Node;
} else { } else {
this.condition = expression.manipulate(block); this.condition = expression.manipulate(block);
} }
@ -64,10 +62,8 @@ class IfBlockBranch extends Wrapper {
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, parent.renderer.component), comment: create_debugging_comment(node, parent.renderer.component),
name: parent.renderer.component.get_unique_name( name: parent.renderer.component.get_unique_name(is_else ? `create_else_block` : `create_if_block`),
is_else ? `create_else_block` : `create_if_block` type: (node as IfBlock).expression ? 'if' : 'else',
),
type: (node as IfBlock).expression ? 'if' : 'else'
}); });
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling);
@ -104,14 +100,7 @@ export default class IfBlockWrapper extends Wrapper {
let has_outros = false; let has_outros = false;
const create_branches = (node: IfBlock) => { const create_branches = (node: IfBlock) => {
const branch = new IfBlockBranch( const branch = new IfBlockBranch(renderer, block, this, node, strip_whitespace, next_sibling);
renderer,
block,
this,
node,
strip_whitespace,
next_sibling
);
this.branches.push(branch); this.branches.push(branch);
@ -135,14 +124,7 @@ export default class IfBlockWrapper extends Wrapper {
if (is_else_if(node.else)) { if (is_else_if(node.else)) {
create_branches(node.else.children[0] as IfBlock); create_branches(node.else.children[0] as IfBlock);
} else if (node.else) { } else if (node.else) {
const branch = new IfBlockBranch( const branch = new IfBlockBranch(renderer, block, this, node.else, strip_whitespace, next_sibling);
renderer,
block,
this,
node.else,
strip_whitespace,
next_sibling
);
this.branches.push(branch); this.branches.push(branch);
@ -160,7 +142,7 @@ export default class IfBlockWrapper extends Wrapper {
create_branches(this.node); create_branches(this.node);
blocks.forEach(block => { blocks.forEach((block) => {
block.has_update_method = is_dynamic; block.has_update_method = is_dynamic;
block.has_intro_method = has_intros; block.has_intro_method = has_intros;
block.has_outro_method = has_outros; block.has_outro_method = has_outros;
@ -169,11 +151,7 @@ export default class IfBlockWrapper extends Wrapper {
renderer.blocks.push(...blocks); renderer.blocks.push(...blocks);
} }
render( render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
block: Block,
parent_node: Identifier,
parent_nodes: Identifier
) {
const name = this.var; const name = this.var;
const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node(); const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node();
@ -181,7 +159,7 @@ export default class IfBlockWrapper extends Wrapper {
? block.get_unique_name(`${this.var.name}_anchor`) ? block.get_unique_name(`${this.var.name}_anchor`)
: (this.next && this.next.var) || 'null'; : (this.next && this.next.var) || 'null';
const has_else = !(this.branches[this.branches.length - 1].condition); const has_else = !this.branches[this.branches.length - 1].condition;
const if_exists_condition = has_else ? null : name; const if_exists_condition = has_else ? null : name;
const dynamic = this.branches[0].block.has_update_method; // can use [0] as proxy for all, since they necessarily have the same value const dynamic = this.branches[0].block.has_update_method; // can use [0] as proxy for all, since they necessarily have the same value
@ -194,7 +172,7 @@ export default class IfBlockWrapper extends Wrapper {
const detaching = parent_node && !is_head(parent_node) ? null : 'detaching'; const detaching = parent_node && !is_head(parent_node) ? null : 'detaching';
if (this.node.else) { if (this.node.else) {
this.branches.forEach(branch => { this.branches.forEach((branch) => {
if (branch.snippet) block.add_variable(branch.condition); if (branch.snippet) block.add_variable(branch.condition);
}); });
@ -221,13 +199,9 @@ export default class IfBlockWrapper extends Wrapper {
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.claim.push( block.chunks.claim.push(b`if (${if_exists_condition}) ${name}.l(${parent_nodes});`);
b`if (${if_exists_condition}) ${name}.l(${parent_nodes});`
);
} else { } else {
block.chunks.claim.push( block.chunks.claim.push(b`${name}.l(${parent_nodes});`);
b`${name}.l(${parent_nodes});`
);
} }
} }
@ -236,16 +210,11 @@ export default class IfBlockWrapper extends Wrapper {
} }
if (needs_anchor) { if (needs_anchor) {
block.add_element( block.add_element(anchor as Identifier, x`@empty()`, parent_nodes && x`@empty()`, parent_node);
anchor as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node
);
} }
this.branches.forEach(branch => { this.branches.forEach((branch) => {
branch.fragment.render(branch.block, null, x`#nodes` as unknown as Identifier); branch.fragment.render(branch.block, null, (x`#nodes` as unknown) as Identifier);
}); });
} }
@ -266,23 +235,26 @@ export default class IfBlockWrapper extends Wrapper {
if (this.needs_update) { if (this.needs_update) {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ dependencies, condition, snippet, block }) => condition ${this.branches.map(({ dependencies, condition, snippet, block }) =>
condition
? b` ? b`
${snippet && ( ${
dependencies.length > 0 snippet &&
(dependencies.length > 0
? b`if (${condition} == null || ${block.renderer.dirty(dependencies)}) ${condition} = !!${snippet}` ? b`if (${condition} == null || ${block.renderer.dirty(dependencies)}) ${condition} = !!${snippet}`
: b`if (${condition} == null) ${condition} = !!${snippet}` : b`if (${condition} == null) ${condition} = !!${snippet}`)
)} }
if (${condition}) return ${block.name};` if (${condition}) return ${block.name};`
: b`return ${block.name};`)} : b`return ${block.name};`
)}
} }
`); `);
} else { } else {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet, block }) => condition ${this.branches.map(({ condition, snippet, block }) =>
? b`if (${snippet || condition}) return ${block.name};` condition ? b`if (${snippet || condition}) return ${block.name};` : b`return ${block.name};`
: b`return ${block.name};`)} )}
} }
`); `);
} }
@ -296,13 +268,9 @@ export default class IfBlockWrapper extends Wrapper {
const anchor_node = parent_node ? 'null' : 'anchor'; const anchor_node = parent_node ? 'null' : 'anchor';
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.mount.push( block.chunks.mount.push(b`if (${if_exists_condition}) ${name}.m(${initial_mount_node}, ${anchor_node});`);
b`if (${if_exists_condition}) ${name}.m(${initial_mount_node}, ${anchor_node});`
);
} else { } else {
block.chunks.mount.push( block.chunks.mount.push(b`${name}.m(${initial_mount_node}, ${anchor_node});`);
b`${name}.m(${initial_mount_node}, ${anchor_node});`
);
} }
if (this.needs_update) { if (this.needs_update) {
@ -371,42 +339,47 @@ export default class IfBlockWrapper extends Wrapper {
const if_blocks = block.get_unique_name(`if_blocks`); const if_blocks = block.get_unique_name(`if_blocks`);
const if_current_block_type_index = has_else const if_current_block_type_index = has_else
? nodes => nodes ? (nodes) => nodes
: nodes => b`if (~${current_block_type_index}) { ${nodes} }`; : (nodes) => b`if (~${current_block_type_index}) { ${nodes} }`;
block.add_variable(current_block_type_index); block.add_variable(current_block_type_index);
block.add_variable(name); block.add_variable(name);
block.chunks.init.push(b` block.chunks.init.push(b`
const ${if_block_creators} = [ const ${if_block_creators} = [
${this.branches.map(branch => branch.block.name)} ${this.branches.map((branch) => branch.block.name)}
]; ];
const ${if_blocks} = []; const ${if_blocks} = [];
${this.needs_update ${
this.needs_update
? b` ? b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ dependencies, condition, snippet }, i) => condition ${this.branches.map(({ dependencies, condition, snippet }, i) =>
condition
? b` ? b`
${snippet && ( ${
dependencies.length > 0 snippet &&
(dependencies.length > 0
? b`if (${block.renderer.dirty(dependencies)}) ${condition} = !!${snippet}` ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = !!${snippet}`
: b`if (${condition} == -1) ${condition} = !!${snippet}` : b`if (${condition} == -1) ${condition} = !!${snippet}`)
)} }
if (${condition}) return ${i};` if (${condition}) return ${i};`
: b`return ${i};`)} : b`return ${i};`
)}
${!has_else && b`return -1;`} ${!has_else && b`return -1;`}
} }
` `
: b` : b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet }, i) => condition ${this.branches.map(({ condition, snippet }, i) =>
? b`if (${snippet || condition}) return ${i};` condition ? b`if (${snippet || condition}) return ${i};` : b`return ${i};`
: b`return ${i};`)} )}
${!has_else && b`return -1;`} ${!has_else && b`return -1;`}
} }
`} `
}
`); `);
if (has_else) { if (has_else) {
@ -434,13 +407,10 @@ export default class IfBlockWrapper extends Wrapper {
if (this.needs_update) { if (this.needs_update) {
const update_mount_node = this.get_update_mount_node(anchor); const update_mount_node = this.get_update_mount_node(anchor);
const destroy_old_block = b` const destroy_old_block = block.group_transition_out(
@group_outros(); (transition_out) =>
@transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => { b`${transition_out}(${if_blocks}[${previous_block_index}], () => {${if_blocks}[${previous_block_index}] = null;})`
${if_blocks}[${previous_block_index}] = null; );
});
@check_outros();
`;
const create_new_block = b` const create_new_block = b`
${name} = ${if_blocks}[${current_block_type_index}]; ${name} = ${if_blocks}[${current_block_type_index}];
@ -455,7 +425,6 @@ export default class IfBlockWrapper extends Wrapper {
const change_block = has_else const change_block = has_else
? b` ? b`
${destroy_old_block} ${destroy_old_block}
${create_new_block} ${create_new_block}
` `
: b` : b`
@ -521,9 +490,7 @@ export default class IfBlockWrapper extends Wrapper {
const initial_mount_node = parent_node || '#target'; const initial_mount_node = parent_node || '#target';
const anchor_node = parent_node ? 'null' : 'anchor'; const anchor_node = parent_node ? 'null' : 'anchor';
block.chunks.mount.push( block.chunks.mount.push(b`if (${name}) ${name}.m(${initial_mount_node}, ${anchor_node});`);
b`if (${name}) ${name}.m(${initial_mount_node}, ${anchor_node});`
);
if (branch.dependencies.length > 0) { if (branch.dependencies.length > 0) {
const update_mount_node = this.get_update_mount_node(anchor); const update_mount_node = this.get_update_mount_node(anchor);
@ -546,7 +513,9 @@ export default class IfBlockWrapper extends Wrapper {
`; `;
if (branch.snippet) { if (branch.snippet) {
block.chunks.update.push(b`if (${block.renderer.dirty(branch.dependencies)}) ${branch.condition} = ${branch.snippet}`); block.chunks.update.push(
b`if (${block.renderer.dirty(branch.dependencies)}) ${branch.condition} = ${branch.snippet}`
);
} }
// no `p()` here — we don't want to update outroing nodes, // no `p()` here — we don't want to update outroing nodes,
@ -556,11 +525,7 @@ export default class IfBlockWrapper extends Wrapper {
if (${branch.condition}) { if (${branch.condition}) {
${enter} ${enter}
} else if (${name}) { } else if (${name}) {
@group_outros(); ${block.group_transition_out((transition_out) => b`${transition_out}(${name},() => {${name} = null;})`)}
@transition_out(${name}, 1, 1, () => {
${name} = null;
});
@check_outros();
} }
`); `);
} else { } else {

@ -240,12 +240,10 @@ export default class InlineComponentWrapper extends Wrapper {
if (attr.is_spread) { if (attr.is_spread) {
const value = attr.expression.manipulate(block); const value = attr.expression.manipulate(block);
initial_props.push(value); initial_props.push(value);
change_object =
let value_object = value; attr.expression.node.type !== 'ObjectExpression'
if (attr.expression.node.type !== 'ObjectExpression') { ? x`(typeof ${value} === 'object' && ${value} !== null ? ${value} : {})`
value_object = x`@get_spread_object(${value})`; : value;
}
change_object = value_object;
} else { } else {
const obj = x`{ ${name}: ${attr.get_value(block)} }`; const obj = x`{ ${name}: ${attr.get_value(block)} }`;
initial_props.push(obj); initial_props.push(obj);
@ -261,7 +259,7 @@ export default class InlineComponentWrapper extends Wrapper {
statements.push(b` statements.push(b`
for (let #i = 0; #i < ${levels}.length; #i += 1) { for (let #i = 0; #i < ${levels}.length; #i += 1) {
${props} = @assign(${props}, ${levels}[#i]); ${props} = Object.assign(${props}, ${levels}[#i]);
} }
`); `);
@ -417,12 +415,11 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${switch_value} !== (${switch_value} = ${snippet})) { if (${switch_value} !== (${switch_value} = ${snippet})) {
if (${name}) { if (${name}) {
@group_outros();
const old_component = ${name}; const old_component = ${name};
@transition_out(old_component.$$.fragment, 1, 0, () => { ${block.group_transition_out(
@destroy_component(old_component, 1); (transition_out) =>
}); b`${transition_out}(old_component.$$.fragment, () => { @destroy_component(old_component, 1); }, 0)`
@check_outros(); )}
} }
if (${switch_value}) { if (${switch_value}) {

@ -2,12 +2,8 @@ import { b, x } from 'code-red';
import Block from '../../Block'; import Block from '../../Block';
import Action from '../../../nodes/Action'; import Action from '../../../nodes/Action';
export default function add_actions( export default function add_actions(block: Block, target: string, actions: Action[]) {
block: Block, actions.forEach((action) => add_action(block, target, action));
target: string,
actions: Action[]
) {
actions.forEach(action => add_action(block, target, action));
} }
export function add_action(block: Block, target: string, action: Action) { export function add_action(block: Block, target: string, action: Action) {
@ -20,27 +16,21 @@ export function add_action(block: Block, target: string, action: Action) {
dependencies = expression.dynamic_dependencies(); dependencies = expression.dynamic_dependencies();
} }
const id = block.get_unique_name( const id = block.get_unique_name(`${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action`);
`${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action`
);
block.add_variable(id); block.add_variable(id);
const fn = block.renderer.reference(action.name); const fn = block.renderer.reference(action.name);
block.event_listeners.push( block.event_listeners.push(x`@action_destroyer(${id} = ${fn}.call(null, ${target}, ${snippet}))`);
x`@action_destroyer(${id} = ${fn}.call(null, ${target}, ${snippet}))`
);
if (dependencies && dependencies.length > 0) { if (dependencies && dependencies.length > 0) {
let condition = x`${id} && @is_function(${id}.update)`; let condition = x`${id} && "function" === typeof ${id}.update`;
if (dependencies.length > 0) { if (dependencies.length > 0) {
condition = x`${condition} && ${block.renderer.dirty(dependencies)}`; condition = x`${condition} && ${block.renderer.dirty(dependencies)}`;
} }
block.chunks.update.push( block.chunks.update.push(b`if (${condition}) ${id}.update.call(null, ${snippet});`);
b`if (${condition}) ${id}.update.call(null, ${snippet});`
);
} }
} }

@ -10,46 +10,42 @@ function get_prop_value(attribute) {
if (attribute.chunks.length === 0) return x`''`; if (attribute.chunks.length === 0) return x`''`;
return attribute.chunks return attribute.chunks
.map(chunk => { .map((chunk) => {
if (chunk.type === 'Text') return string_literal(chunk.data); if (chunk.type === 'Text') return string_literal(chunk.data);
return chunk.node; return chunk.node;
}) })
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`); .reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
} }
export default function(node: InlineComponent, renderer: Renderer, options: RenderOptions) { export default function (node: InlineComponent, renderer: Renderer, options: RenderOptions) {
const binding_props = []; const binding_props = [];
const binding_fns = []; const binding_fns = [];
node.bindings.forEach(binding => { node.bindings.forEach((binding) => {
renderer.has_bindings = true; renderer.has_bindings = true;
// TODO this probably won't work for contextual bindings // TODO this probably won't work for contextual bindings
const snippet = binding.expression.node; const snippet = binding.expression.node;
binding_props.push(p`${binding.name}: ${snippet}`); binding_props.push(p`${binding.name}: ${snippet}`);
binding_fns.push(p`${binding.name}: $$value => { ${snippet} = $$value; $$settled = false }`); binding_fns.push(p`${binding.name}: (#value) => { ${snippet} = #value; $$settled = false }`);
}); });
const uses_spread = node.attributes.find(attr => attr.is_spread); const uses_spread = node.attributes.find((attr) => attr.is_spread);
let props; let props;
if (uses_spread) { if (uses_spread) {
props = x`@_Object.assign(${ props = x`{${node.attributes
node.attributes .map((attribute) => {
.map(attribute => { if (attribute.is_spread) return x`...${attribute.expression.node}`;
if (attribute.is_spread) { else return x`${attribute.name}: ${get_prop_value(attribute)}`;
return attribute.expression.node;
} else {
return x`{ ${attribute.name}: ${get_prop_value(attribute)} }`;
}
}) })
.concat(binding_props.map(p => x`{ ${p} }`)) .concat(binding_props.map((p) => x`${p}`))
})`; .join()}}`;
} else { } else {
props = x`{ props = x`{
${node.attributes.map(attribute => p`${attribute.name}: ${get_prop_value(attribute)}`)}, ${node.attributes.map((attribute) => p`${attribute.name}: ${get_prop_value(attribute)}`)},
${binding_props} ${binding_props}
}`; }`;
} }
@ -58,13 +54,12 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
${binding_fns} ${binding_fns}
}`; }`;
const expression = ( const expression =
node.name === 'svelte:self' node.name === 'svelte:self'
? renderer.name ? renderer.name
: node.name === 'svelte:component' : node.name === 'svelte:component'
? x`(${node.expression.node}) || @missing_component` ? x`(${node.expression.node}) || @missing_component`
: node.name.split('.').reduce(((lhs, rhs) => x`${lhs}.${rhs}`) as any) : node.name.split('.').reduce(((lhs, rhs) => x`${lhs}.${rhs}`) as any);
);
const slot_fns = []; const slot_fns = [];
@ -75,20 +70,16 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
renderer.push(); renderer.push();
renderer.render(children, Object.assign({}, options, { renderer.render(children, { ...options, ...slot_scopes });
slot_scopes
}));
slot_scopes.set('default', { slot_scopes.set('default', {
input: get_slot_scope(node.lets), input: get_slot_scope(node.lets),
output: renderer.pop() output: renderer.pop(),
}); });
slot_scopes.forEach(({ input, output }, name) => { slot_scopes.forEach(({ input, output }, name) => {
if (!is_empty_template_literal(output)) { if (!is_empty_template_literal(output)) {
slot_fns.push( slot_fns.push(p`${name}: (${input}) => ${output}`);
p`${name}: (${input}) => ${output}`
);
} }
}); });
} }
@ -97,13 +88,15 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
${slot_fns} ${slot_fns}
}`; }`;
renderer.add_expression(x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`); renderer.add_expression(
x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`
);
} }
function is_empty_template_literal(template_literal) { function is_empty_template_literal(template_literal) {
return ( return (
template_literal.expressions.length === 0 && template_literal.expressions.length === 0 &&
template_literal.quasis.length === 1 && template_literal.quasis.length === 1 &&
template_literal.quasis[0].value.raw === "" template_literal.quasis[0].value.raw === ''
); );
} }

@ -9,7 +9,7 @@ export function get_class_attribute_value(attribute: Attribute): ESTreeExpressio
// handle special case — `class={possiblyUndefined}` with scoped CSS // handle special case — `class={possiblyUndefined}` with scoped CSS
if (attribute.chunks.length === 2 && (attribute.chunks[1] as Text).synthetic) { if (attribute.chunks.length === 2 && (attribute.chunks[1] as Text).synthetic) {
const value = (attribute.chunks[0] as Expression).node; const value = (attribute.chunks[0] as Expression).node;
return x`@escape(@null_to_empty(${value})) + "${(attribute.chunks[1] as Text).data}"`; return x`@escape(${value} ?? "") + "${(attribute.chunks[1] as Text).data}"`;
} }
return get_attribute_value(attribute); return get_attribute_value(attribute);
@ -21,7 +21,7 @@ export function get_attribute_value(attribute: Attribute): ESTreeExpression {
return attribute.chunks return attribute.chunks
.map((chunk) => { .map((chunk) => {
return chunk.type === 'Text' return chunk.type === 'Text'
? string_literal(chunk.data.replace(/"/g, '&quot;')) as ESTreeExpression ? (string_literal(chunk.data.replace(/"/g, '&quot;')) as ESTreeExpression)
: x`@escape(${chunk.node})`; : x`@escape(${chunk.node})`;
}) })
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`); .reduce((lhs, rhs) => x`${lhs} + ${rhs}`);

@ -1,5 +1,4 @@
import { INode } from '../../../nodes/interfaces'; import { INode } from '../../../nodes/interfaces';
import { trim_end, trim_start } from '../../../../utils/trim';
import { link } from '../../../../utils/link'; import { link } from '../../../../utils/link';
// similar logic from `compile/render_dom/wrappers/Fragment` // similar logic from `compile/render_dom/wrappers/Fragment`
@ -21,13 +20,11 @@ export default function remove_whitespace_children(children: INode[], next?: INo
if (nodes.length === 0) { if (nodes.length === 0) {
const should_trim = next const should_trim = next
? next.type === 'Text' && ? next.type === 'Text' && /^\s/.test(next.data) && trimmable_at(child, next)
/^\s/.test(next.data) &&
trimmable_at(child, next)
: !child.has_ancestor('EachBlock'); : !child.has_ancestor('EachBlock');
if (should_trim) { if (should_trim) {
data = trim_end(data); data = data.trimRight();
if (!data) continue; if (!data) continue;
} }
} }
@ -39,16 +36,16 @@ export default function remove_whitespace_children(children: INode[], next?: INo
} }
nodes.unshift(child); nodes.unshift(child);
link(last_child, last_child = child); link(last_child, (last_child = child));
} else { } else {
nodes.unshift(child); nodes.unshift(child);
link(last_child, last_child = child); link(last_child, (last_child = child));
} }
} }
const first = nodes[0]; const first = nodes[0];
if (first && first.type === 'Text') { if (first && first.type === 'Text') {
first.data = trim_start(first.data); first.data = first.data.trimLeft();
if (!first.data) { if (!first.data) {
first.var = null; first.var = null;
nodes.shift(); nodes.shift();
@ -67,7 +64,6 @@ function trimmable_at(child: INode, next_sibling: INode): boolean {
// The child and its sibling share a common nearest each block (not at an each block boundary) // The child and its sibling share a common nearest each block (not at an each block boundary)
// The next sibling's previous node is an each block // The next sibling's previous node is an each block
return ( return (
next_sibling.find_nearest(/EachBlock/) === next_sibling.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/) || next_sibling.prev.type === 'EachBlock'
child.find_nearest(/EachBlock/) || next_sibling.prev.type === 'EachBlock'
); );
} }

@ -8,101 +8,91 @@ import Text from '../nodes/Text';
import { extract_names } from '../utils/scope'; import { extract_names } from '../utils/scope';
import { LabeledStatement, Statement, ExpressionStatement, AssignmentExpression, Node } from 'estree'; import { LabeledStatement, Statement, ExpressionStatement, AssignmentExpression, Node } from 'estree';
export default function ssr( export default function ssr(component: Component, options: CompileOptions): { js: Node[]; css: CssResult } {
component: Component,
options: CompileOptions
): {js: Node[]; css: CssResult} {
const renderer = new Renderer({ const renderer = new Renderer({
name: component.name name: component.name,
}); });
const { name } = component; const { name } = component;
// create $$render function // create $$render function
renderer.render(trim(component.fragment.children), Object.assign({ renderer.render(trim(component.fragment.children), { locate: component.locate, ...options });
locate: component.locate
}, options));
// TODO put this inside the Renderer class // TODO put this inside the Renderer class
const literal = renderer.pop(); const literal = renderer.pop();
// TODO concatenate CSS maps // TODO concatenate CSS maps
const css = options.customElement ? const css = options.customElement ? { code: null, map: null } : component.stylesheet.render(options.filename, true);
{ code: null, map: null } :
component.stylesheet.render(options.filename, true);
const uses_rest = component.var_lookup.has('$$restProps'); const uses_rest = component.var_lookup.has('$$restProps');
const props = component.vars.filter(variable => !variable.module && variable.export_name); const props = component.vars.filter((variable) => !variable.module && variable.export_name);
const rest = uses_rest ? b`let $$restProps = @compute_rest_props($$props, [${props.map(prop => `"${prop.export_name}"`).join(',')}]);` : null; const rest = uses_rest
? b`
let #k;
const #keys = new Set([${props.map((prop) => `"${prop.export_name}"`).join(',')}]);
let $$restProps = {};
for (#k in $$props){ if (!#keys.has(#k) && $$props[#k][0] !== '$'){ $$restProps[#k] = $$props[#k];}}`
: null;
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$'); const reactive_stores = component.vars.filter((variable) => variable.name[0] === '$' && variable.name[1] !== '$');
const reactive_store_values = reactive_stores const reactive_store_values = reactive_stores
.map(({ name }) => { .map(({ name }) => {
const store_name = name.slice(1); const store_name = name.slice(1);
const store = component.var_lookup.get(store_name); const store = component.var_lookup.get(store_name);
if (store && store.hoistable) return null; if (store && store.hoistable) return null;
const assignment = b`${store_name}.subscribe(#v=>{${name}=#v})();`;
const assignment = b`${name} = @get_store_value(${store_name});`;
return component.compile_options.dev return component.compile_options.dev
? b`@validate_store(${store_name}, '${store_name}'); ${assignment}` ? b`@validate_store(${store_name}, '${store_name}'); ${assignment}`
: assignment; : assignment;
}) })
.filter(Boolean); .filter(Boolean);
component.rewrite_props(({ name }) => { component.rewrite_props(
const value = `$${name}`; ({ name }) =>
b`${
let insert = b`${value} = @get_store_value(${name})`; component.compile_options.dev && b`@validate_store(${name}, '${name}');`
if (component.compile_options.dev) { }${name}.subscribe((#v)=>{$${name}=#v})()`
insert = b`@validate_store(${name}, '${name}'); ${insert}`; );
}
return insert;
});
const instance_javascript = component.extract_javascript(component.ast.instance); const instance_javascript = component.extract_javascript(component.ast.instance);
// TODO only do this for props with a default value // TODO only do this for props with a default value
const parent_bindings = instance_javascript const parent_bindings = instance_javascript
? component.vars ? component.vars
.filter(variable => !variable.module && variable.export_name) .filter((variable) => !variable.module && variable.export_name)
.map(prop => { .map((prop) => {
return b`if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`; return b`if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`;
}) })
: []; : [];
const reactive_declarations = component.reactive_declarations.map(d => { const reactive_declarations = component.reactive_declarations.map((d) => {
const body: Statement = (d.node as LabeledStatement).body; const body: Statement = (d.node as LabeledStatement).body;
let statement = b`${body}`; let statement = b`${body}`;
if (d.declaration) { if (d.declaration) {
const declared = extract_names(d.declaration); const declared = extract_names(d.declaration);
const injected = declared.filter(name => { const injected = declared.filter((name) => {
return name[0] !== '$' && component.var_lookup.get(name).injected; return name[0] !== '$' && component.var_lookup.get(name).injected;
}); });
const self_dependencies = injected.filter(name => d.dependencies.has(name)); const self_dependencies = injected.filter((name) => d.dependencies.has(name));
if (injected.length) { if (injected.length) {
// in some cases we need to do `let foo; [expression]`, in // in some cases we need to do `let foo; [expression]`, in
// others we can do `let [expression]` // others we can do `let [expression]`
const separate = ( const separate = self_dependencies.length > 0 || declared.length > injected.length;
self_dependencies.length > 0 ||
declared.length > injected.length
);
const { left, right } = (body as ExpressionStatement).expression as AssignmentExpression; const { left, right } = (body as ExpressionStatement).expression as AssignmentExpression;
statement = separate statement = separate
? b` ? b`
${injected.map(name => b`let ${name};`)} ${injected.map((name) => b`let ${name};`)}
${statement}` ${statement}`
: b` : b`let ${left} = ${right}`;
let ${left} = ${right}`;
} }
} else { // TODO do not add label if it's not referenced } else {
// TODO do not add label if it's not referenced
statement = b`$: { ${statement} }`; statement = b`$: { ${statement} }`;
} }
@ -138,24 +128,25 @@ export default function ssr(
...reactive_stores.map(({ name }) => { ...reactive_stores.map(({ name }) => {
const store_name = name.slice(1); const store_name = name.slice(1);
const store = component.var_lookup.get(store_name); const store = component.var_lookup.get(store_name);
if (store && store.hoistable) { return b`let ${name};${store && store.hoistable && b`${store_name}.subscribe((#v)=>{${name}=#v})()`}`;
return b`let ${name} = @get_store_value(${store_name});`;
}
return b`let ${name};`;
}), }),
instance_javascript, instance_javascript,
...parent_bindings, ...parent_bindings,
css.code && b`$$result.css.add(#css);`, css.code && b`$$result.css.add(#css);`,
main main,
].filter(Boolean); ].filter(Boolean);
const js = b` const js = b`
${css.code ? b` ${
css.code
? b`
const #css = { const #css = {
code: "${css.code}", code: "${css.code}",
map: ${css.map ? string_literal(css.map.toString()) : 'null'} map: ${css.map ? string_literal(css.map.toString()) : 'null'}
};` : null} };`
: null
}
${component.extract_javascript(component.ast.module)} ${component.extract_javascript(component.ast.module)}
@ -166,7 +157,7 @@ export default function ssr(
}); });
`; `;
return {js, css}; return { js, css };
} }
function trim(nodes: TemplateNode[]) { function trim(nodes: TemplateNode[]) {

@ -5,13 +5,13 @@ import Block from '../render_dom/Block';
export default function get_slot_data(values: Map<string, Attribute>, block: Block = null) { export default function get_slot_data(values: Map<string, Attribute>, block: Block = null) {
return { return {
type: 'ObjectExpression', type: 'Expression',
properties: Array.from(values.values()) properties: Array.from(values.values())
.filter(attribute => attribute.name !== 'name') .filter((attribute) => attribute.name !== 'name')
.map(attribute => { .map((attribute) => {
const value = get_value(block, attribute); const value = get_value(block, attribute);
return p`${attribute.name}: ${value}`; return p`${attribute.name}: ${value}`;
}) }),
}; };
} }
@ -20,7 +20,7 @@ function get_value(block: Block, attribute: Attribute) {
if (attribute.chunks.length === 0) return x`""`; if (attribute.chunks.length === 0) return x`""`;
let value = attribute.chunks let value = attribute.chunks
.map(chunk => chunk.type === 'Text' ? string_literal(chunk.data) : (block ? chunk.manipulate(block) : chunk.node)) .map((chunk) => (chunk.type === 'Text' ? string_literal(chunk.data) : block ? chunk.manipulate(block) : chunk.node))
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`); .reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
if (attribute.chunks.length > 1 && attribute.chunks[0].type !== 'Text') { if (attribute.chunks.length > 1 && attribute.chunks[0].type !== 'Text') {

@ -102,11 +102,14 @@ export interface Warning {
export type ModuleFormat = 'esm' | 'cjs'; export type ModuleFormat = 'esm' | 'cjs';
export interface CompileOptions { export interface CompileOptions {
/* bundler */
filename?: string;
version?: number;
format?: ModuleFormat; format?: ModuleFormat;
/* Component class name */
name?: string; name?: string;
filename?: string;
generate?: string | false; generate?: string | false;
version?: number;
outputFilename?: string; outputFilename?: string;
cssOutputFilename?: string; cssOutputFilename?: string;
@ -116,7 +119,6 @@ export interface CompileOptions {
accessors?: boolean; accessors?: boolean;
immutable?: boolean; immutable?: boolean;
hydratable?: boolean; hydratable?: boolean;
legacy?: boolean;
customElement?: boolean; customElement?: boolean;
tag?: string; tag?: string;
css?: boolean; css?: boolean;

@ -2,7 +2,6 @@ import read_context from '../read/context';
import read_expression from '../read/expression'; import read_expression from '../read/expression';
import { closing_tag_omitted } from '../utils/html'; import { closing_tag_omitted } from '../utils/html';
import { whitespace } from '../../utils/patterns'; import { whitespace } from '../../utils/patterns';
import { trim_start, trim_end } from '../../utils/trim';
import { to_string } from '../utils/node'; import { to_string } from '../utils/node';
import { Parser } from '../index'; import { Parser } from '../index';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
@ -14,12 +13,12 @@ function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after:
const last_child = block.children[block.children.length - 1]; const last_child = block.children[block.children.length - 1];
if (first_child.type === 'Text' && trim_before) { if (first_child.type === 'Text' && trim_before) {
first_child.data = trim_start(first_child.data); first_child.data = first_child.data.trimLeft();
if (!first_child.data) block.children.shift(); if (!first_child.data) block.children.shift();
} }
if (last_child.type === 'Text' && trim_after) { if (last_child.type === 'Text' && trim_after) {
last_child.data = trim_end(last_child.data); last_child.data = last_child.data.trimRight();
if (!last_child.data) block.children.pop(); if (!last_child.data) block.children.pop();
} }
@ -49,7 +48,12 @@ export default function mustache(parser: Parser) {
block = parser.current(); block = parser.current();
} }
if (block.type === 'ElseBlock' || block.type === 'PendingBlock' || block.type === 'ThenBlock' || block.type === 'CatchBlock') { if (
block.type === 'ElseBlock' ||
block.type === 'PendingBlock' ||
block.type === 'ThenBlock' ||
block.type === 'CatchBlock'
) {
block.end = start; block.end = start;
parser.stack.pop(); parser.stack.pop();
block = parser.current(); block = parser.current();
@ -66,7 +70,7 @@ export default function mustache(parser: Parser) {
} else { } else {
parser.error({ parser.error({
code: `unexpected-block-close`, code: `unexpected-block-close`,
message: `Unexpected block closing tag` message: `Unexpected block closing tag`,
}); });
} }
@ -98,7 +102,7 @@ export default function mustache(parser: Parser) {
if (parser.eat('if')) { if (parser.eat('if')) {
parser.error({ parser.error({
code: 'invalid-elseif', code: 'invalid-elseif',
message: `'elseif' should be 'else if'` message: `'elseif' should be 'else if'`,
}); });
} }
@ -110,9 +114,9 @@ export default function mustache(parser: Parser) {
if (block.type !== 'IfBlock') { if (block.type !== 'IfBlock') {
parser.error({ parser.error({
code: `invalid-elseif-placement`, code: `invalid-elseif-placement`,
message: parser.stack.some(block => block.type === 'IfBlock') message: parser.stack.some((block) => block.type === 'IfBlock')
? `Expected to close ${to_string(block)} before seeing {:else if ...} block` ? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
: `Cannot have an {:else if ...} block outside an {#if ...} block` : `Cannot have an {:else if ...} block outside an {#if ...} block`,
}); });
} }
@ -148,9 +152,9 @@ export default function mustache(parser: Parser) {
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error({ parser.error({
code: `invalid-else-placement`, code: `invalid-else-placement`,
message: parser.stack.some(block => block.type === 'IfBlock' || block.type === 'EachBlock') message: parser.stack.some((block) => block.type === 'IfBlock' || block.type === 'EachBlock')
? `Expected to close ${to_string(block)} before seeing {:else} block` ? `Expected to close ${to_string(block)} before seeing {:else} block`
: `Cannot have an {:else} block outside an {#if ...} or {#each ...} block` : `Cannot have an {:else} block outside an {#if ...} or {#each ...} block`,
}); });
} }
@ -174,18 +178,18 @@ export default function mustache(parser: Parser) {
if (block.type !== 'PendingBlock') { if (block.type !== 'PendingBlock') {
parser.error({ parser.error({
code: `invalid-then-placement`, code: `invalid-then-placement`,
message: parser.stack.some(block => block.type === 'PendingBlock') message: parser.stack.some((block) => block.type === 'PendingBlock')
? `Expected to close ${to_string(block)} before seeing {:then} block` ? `Expected to close ${to_string(block)} before seeing {:then} block`
: `Cannot have an {:then} block outside an {#await ...} block` : `Cannot have an {:then} block outside an {#await ...} block`,
}); });
} }
} else { } else {
if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
parser.error({ parser.error({
code: `invalid-catch-placement`, code: `invalid-catch-placement`,
message: parser.stack.some(block => block.type === 'ThenBlock' || block.type === 'PendingBlock') message: parser.stack.some((block) => block.type === 'ThenBlock' || block.type === 'PendingBlock')
? `Expected to close ${to_string(block)} before seeing {:catch} block` ? `Expected to close ${to_string(block)} before seeing {:catch} block`
: `Cannot have an {:catch} block outside an {#await ...} block` : `Cannot have an {:catch} block outside an {#await ...} block`,
}); });
} }
} }
@ -196,7 +200,7 @@ export default function mustache(parser: Parser) {
if (!parser.eat('}')) { if (!parser.eat('}')) {
parser.require_whitespace(); parser.require_whitespace();
await_block[is_then ? 'value': 'error'] = parser.read_destructure_pattern(); await_block[is_then ? 'value' : 'error'] = parser.read_destructure_pattern();
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
} }
@ -204,9 +208,9 @@ export default function mustache(parser: Parser) {
const new_block: TemplateNode = { const new_block: TemplateNode = {
start, start,
end: null, end: null,
type: is_then ? 'ThenBlock': 'CatchBlock', type: is_then ? 'ThenBlock' : 'CatchBlock',
children: [], children: [],
skip: false skip: false,
}; };
await_block[is_then ? 'then' : 'catch'] = new_block; await_block[is_then ? 'then' : 'catch'] = new_block;
@ -224,7 +228,7 @@ export default function mustache(parser: Parser) {
} else { } else {
parser.error({ parser.error({
code: `expected-block-type`, code: `expected-block-type`,
message: `Expected if, each or await` message: `Expected if, each or await`,
}); });
} }
@ -232,8 +236,9 @@ export default function mustache(parser: Parser) {
const expression = read_expression(parser); const expression = read_expression(parser);
const block: TemplateNode = type === 'AwaitBlock' ? const block: TemplateNode =
{ type === 'AwaitBlock'
? {
start, start,
end: null, end: null,
type, type,
@ -245,24 +250,24 @@ export default function mustache(parser: Parser) {
end: null, end: null,
type: 'PendingBlock', type: 'PendingBlock',
children: [], children: [],
skip: true skip: true,
}, },
then: { then: {
start: null, start: null,
end: null, end: null,
type: 'ThenBlock', type: 'ThenBlock',
children: [], children: [],
skip: true skip: true,
}, },
catch: { catch: {
start: null, start: null,
end: null, end: null,
type: 'CatchBlock', type: 'CatchBlock',
children: [], children: [],
skip: true skip: true,
}, },
} : }
{ : {
start, start,
end: null, end: null,
type, type,
@ -284,9 +289,10 @@ export default function mustache(parser: Parser) {
if (parser.eat(',')) { if (parser.eat(',')) {
parser.allow_whitespace(); parser.allow_whitespace();
block.index = parser.read_identifier(); block.index = parser.read_identifier();
if (!block.index) parser.error({ if (!block.index)
parser.error({
code: `expected-name`, code: `expected-name`,
message: `Expected name` message: `Expected name`,
}); });
parser.allow_whitespace(); parser.allow_whitespace();
@ -361,16 +367,17 @@ export default function mustache(parser: Parser) {
} else { } else {
const expression = read_expression(parser); const expression = read_expression(parser);
identifiers = expression.type === 'SequenceExpression' identifiers = expression.type === 'SequenceExpression' ? expression.expressions : [expression];
? expression.expressions
: [expression];
identifiers.forEach(node => { identifiers.forEach((node) => {
if (node.type !== 'Identifier') { if (node.type !== 'Identifier') {
parser.error({ parser.error(
{
code: 'invalid-debug-args', code: 'invalid-debug-args',
message: '{@debug ...} arguments must be identifiers, not arbitrary expressions' message: '{@debug ...} arguments must be identifiers, not arbitrary expressions',
}, node.start); },
node.start
);
} }
}); });
@ -382,7 +389,7 @@ export default function mustache(parser: Parser) {
start, start,
end: parser.index, end: parser.index,
type: 'DebugTag', type: 'DebugTag',
identifiers identifiers,
}); });
} else { } else {
const expression = read_expression(parser); const expression = read_expression(parser);

@ -35,10 +35,7 @@ const windows_1252 = [
376, 376,
]; ];
const entity_pattern = new RegExp( const entity_pattern = new RegExp(`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}))(?:;|\\b)`, 'g');
`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}))(?:;|\\b)`,
'g'
);
export function decode_character_references(html: string) { export function decode_character_references(html: string) {
return html.replace(entity_pattern, (match, entity) => { return html.replace(entity_pattern, (match, entity) => {
@ -61,8 +58,6 @@ export function decode_character_references(html: string) {
}); });
} }
const NUL = 0;
// some code points are verboten. If we were inserting HTML, the browser would replace the illegal // some code points are verboten. If we were inserting HTML, the browser would replace the illegal
// code points with alternatives in some cases - since we're bypassing that mechanism, we need // code points with alternatives in some cases - since we're bypassing that mechanism, we need
// to replace them ourselves // to replace them ourselves
@ -92,7 +87,7 @@ function validate_code(code: number) {
// UTF-16 surrogate halves // UTF-16 surrogate halves
if (code <= 57343) { if (code <= 57343) {
return NUL; return 0;
} }
// rest of the basic multilingual plane // rest of the basic multilingual plane
@ -110,7 +105,7 @@ function validate_code(code: number) {
return code; return code;
} }
return NUL; return 0;
} }
// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission // based on http://developers.whatwg.org/syntax.html#syntax-tag-omission

@ -1,4 +1,4 @@
export default function fuzzymatch(name: string, names: string[]) { export default function fuzzymatch(name: string, names: string[] | object) {
const set = new FuzzySet(names); const set = new FuzzySet(names);
const matches = set.get(name); const matches = set.get(name);
@ -13,8 +13,7 @@ const GRAM_SIZE_UPPER = 3;
// return an edit distance from 0 to 1 // return an edit distance from 0 to 1
function _distance(str1: string, str2: string) { function _distance(str1: string, str2: string) {
if (str1 === null && str2 === null) if (str1 === null && str2 === null) throw 'Trying to compare two null values';
throw 'Trying to compare two null values';
if (str1 === null || str2 === null) return 0; if (str1 === null || str2 === null) return 0;
str1 = String(str1); str1 = String(str1);
str2 = String(str2); str2 = String(str2);
@ -96,15 +95,16 @@ class FuzzySet {
match_dict = {}; match_dict = {};
items = {}; items = {};
constructor(arr: string[]) { constructor(arr: string[] | object) {
// initialization // initialization
for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) { for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
this.items[i] = []; this.items[i] = [];
} }
let k;
// add all the items to the set // add all the items to the set
for (let i = 0; i < arr.length; ++i) { for (k in arr) {
this.add(arr[i]); this.add(arr[k]);
} }
} }
@ -156,11 +156,7 @@ class FuzzySet {
let results = []; let results = [];
// start with high gram size and if there are no results, go to lower gram sizes // start with high gram size and if there are no results, go to lower gram sizes
for ( for (let gram_size = GRAM_SIZE_UPPER; gram_size >= GRAM_SIZE_LOWER; --gram_size) {
let gram_size = GRAM_SIZE_UPPER;
gram_size >= GRAM_SIZE_LOWER;
--gram_size
) {
results = this.__get(value, gram_size); results = this.__get(value, gram_size);
if (results) { if (results) {
return results; return results;
@ -204,10 +200,7 @@ class FuzzySet {
// build a results list of [score, str] // build a results list of [score, str]
for (const match_index in matches) { for (const match_index in matches) {
match_score = matches[match_index]; match_score = matches[match_index];
results.push([ results.push([match_score / (vector_normal * items[match_index][0]), items[match_index][1]]);
match_score / (vector_normal * items[match_index][0]),
items[match_index][1],
]);
} }
results.sort(sort_descending); results.sort(sort_descending);
@ -216,10 +209,7 @@ class FuzzySet {
const end_index = Math.min(50, results.length); const end_index = Math.min(50, results.length);
// truncate somewhat arbitrarily to 50 // truncate somewhat arbitrarily to 50
for (let i = 0; i < end_index; ++i) { for (let i = 0; i < end_index; ++i) {
new_results.push([ new_results.push([_distance(results[i][1], normalized_value), results[i][1]]);
_distance(results[i][1], normalized_value),
results[i][1],
]);
} }
results = new_results; results = new_results;
results.sort(sort_descending); results.sort(sort_descending);

@ -1,4 +1,2 @@
export function link<T extends { next?: T; prev?: T }>(next: T, prev: T) { export const link = <T extends { next?: T; prev?: T }>(next: T, prev: T) =>
prev.next = next; void ((prev.next = next) && (next.prev = prev));
if (next) next.prev = prev;
}

@ -1,6 +1,2 @@
export default function list(items: string[], conjunction = 'or') { export default (items: string[], conjunction = 'or') =>
if (items.length === 1) return items[0]; items.length === 1 ? items[0] : items.slice(0, -1).join(', ') + `${conjunction} ${items[items.length - 1]}`;
return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[
items.length - 1
]}`;
}

@ -54,7 +54,7 @@ export const globals = new Set([
'undefined', 'undefined',
'URIError', 'URIError',
'URL', 'URL',
'window' 'window',
]); ]);
export const reserved = new Set([ export const reserved = new Set([

@ -1,23 +1,14 @@
export const html = 'http://www.w3.org/1999/xhtml'; export enum namespaces {
export const mathml = 'http://www.w3.org/1998/Math/MathML'; svg = 'http://www.w3.org/2000/svg',
export const svg = 'http://www.w3.org/2000/svg'; xmlns = 'http://www.w3.org/2000/xmlns',
export const xlink = 'http://www.w3.org/1999/xlink'; xlink = 'http://www.w3.org/1999/xlink',
export const xml = 'http://www.w3.org/XML/1998/namespace'; html = 'http://www.w3.org/1999/xhtml',
export const xmlns = 'http://www.w3.org/2000/xmlns'; mathml = 'http://www.w3.org/1998/Math/MathML',
xml = 'http://www.w3.org/XML/1998/namespace',
export const valid_namespaces = [ 'http://www.w3.org/2000/svg' = 'svg',
'html', 'http://www.w3.org/2000/xmlns' = 'xmlns',
'mathml', 'http://www.w3.org/1999/xlink' = 'xlink',
'svg', 'http://www.w3.org/1999/xhtml' = 'html',
'xlink', 'http://www.w3.org/1998/Math/MathML' = 'mathml',
'xml', 'http://www.w3.org/XML/1998/namespace' = 'xml',
'xmlns', }
html,
mathml,
svg,
xlink,
xml,
xmlns,
];
export const namespaces: Record<string, string> = { html, mathml, svg, xlink, xml, xmlns };

@ -17,12 +17,8 @@ export function nodes_match(a, b) {
while (i--) { while (i--) {
const key = a_keys[i]; const key = a_keys[i];
if (b_keys[i] !== key) return false; if (b_keys[i] !== key) return false;
else if (key === 'start' || key === 'end') continue;
if (key === 'start' || key === 'end') continue; else if (!nodes_match(a[key], b[key])) return false;
if (!nodes_match(a[key], b[key])) {
return false;
}
} }
return true; return true;

@ -1,31 +1,31 @@
import { dev$assert } from 'svelte/internal'; import { dev$assert } from 'svelte/internal';
export { identity as linear } from 'svelte/internal'; export const linear = (t: number) => t;
export const quadIn = (t: number) => t ** 2; export const quadIn = (t: number) => t ** 2;
export const quadOut = (t: number) => 1.0 - (1.0 - t) ** 2; export const quadOut = (t: number) => 1.0 - (1.0 - t) ** 2;
export const quadInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - 2 * (1.0 - t) ** 2 : (2 * t) ** 2); export const quadInOut = (t: number) => 0.5 * (t >= 0.5 ? 2.0 - 2 * (1.0 - t) ** 2 : (2 * t) ** 2);
export const cubicIn = (t: number) => t ** 3; export const cubicIn = (t: number) => t ** 3;
export const cubicOut = (t: number) => 1.0 - (1.0 - t) ** 3; export const cubicOut = (t: number) => 1.0 - (1.0 - t) ** 3;
export const cubicInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 3 : (2 * t) ** 3); export const cubicInOut = (t: number) => 0.5 * (t >= 0.5 ? 2.0 - (2 * (1.0 - t)) ** 3 : (2 * t) ** 3);
export const quartIn = (t: number) => t ** 4; export const quartIn = (t: number) => t ** 4;
export const quartOut = (t: number) => 1.0 - (1.0 - t) ** 4; export const quartOut = (t: number) => 1.0 - (1.0 - t) ** 4;
export const quartInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 4 : (2 * t) ** 4); export const quartInOut = (t: number) => 0.5 * (t >= 0.5 ? 2.0 - (2 * (1.0 - t)) ** 4 : (2 * t) ** 4);
export const easeIn = quartIn; export const easeIn = quartIn;
export const easeOut = quartOut; export const easeOut = quartOut;
export const easeInOut = quartInOut; export const easeInOut = quartInOut;
export const quintIn = (t: number) => t ** 5; export const quintIn = (t: number) => t ** 5;
export const quintOut = (t: number) => 1.0 - (1.0 - t) ** 5; export const quintOut = (t: number) => 1.0 - (1.0 - t) ** 5;
export const quintInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 5 : (2 * t) ** 5); export const quintInOut = (t: number) => 0.5 * (t >= 0.5 ? 2.0 - (2 * (1.0 - t)) ** 5 : (2 * t) ** 5);
export const backIn = (t: number) => t * t * (2.6 * t - 1.6); export const backIn = (t: number) => t * t * (2.6 * t - 1.6);
export const backOut = (t: number) => 1 - (t = 1.0 - t) * t * (2.6 * t - 1.6); export const backOut = (t: number) => 1.0 - (t = 1.0 - t) * t * (2.6 * t - 1.6);
export const backInOut = (t: number) => export const backInOut = (t: number) =>
0.5 * (t >= 0.5 ? 2 - (t = 2 * (1 - t)) * t * (2.6 * t - 1.6) : (t = 2 * t) * t * (2.6 * t - 1.6)); 0.5 * (t >= 0.5 ? 2 - (t = 2 * (1.0 - t)) * t * (2.6 * t - 1.6) : (t = 2 * t) * t * (2.6 * t - 1.6));
export const expoIn = (t: number) => (t ? Math.pow(2.0, 10.0 * (t - 1.0)) : t); export const expoIn = (t: number) => (t ? Math.pow(2.0, 10.0 * (t - 1.0)) : t);
export const expoOut = (t: number) => (t ? 1.0 - Math.pow(2.0, -10.0 * t) : t); export const expoOut = (t: number) => (t ? 1.0 - Math.pow(2.0, -10.0 * t) : t);
export const expoInOut = (t: number) => export const expoInOut = (t: number) =>
!t || t === 1.0 ? t : t < 0.5 ? 0.5 * Math.pow(2.0, 20.0 * t - 10.0) : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0; !t || t === 1.0 ? t : t < 0.5 ? 0.5 * Math.pow(2.0, 20.0 * t - 10.0) : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;
export const elasticIn = (t: number) => Math.sin((13.0 * t * Math.PI) / 2) * Math.pow(2.0, 10.0 * (t - 1.0)); export const elasticIn = (t: number) => Math.sin((13.0 * t * Math.PI) / 2.0) * Math.pow(2.0, 10.0 * (t - 1.0));
export const elasticOut = (t: number) => Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2) * Math.pow(2.0, -10.0 * t) + 1.0; export const elasticOut = (t: number) => Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2.0) * Math.pow(2.0, -10.0 * t) + 1.0;
export const elasticInOut = (t: number) => export const elasticInOut = (t: number) =>
t < 0.5 t < 0.5
? 0.5 * Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) * Math.pow(2.0, 10.0 * (2.0 * t - 1.0)) ? 0.5 * Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) * Math.pow(2.0, 10.0 * (2.0 * t - 1.0))
@ -43,9 +43,9 @@ export const bounceInOut = (t: number) =>
t < 0.5 ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0)) : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5; t < 0.5 ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0)) : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
export const sineIn = (t: number) => (1e-14 > Math.abs((t = Math.cos(t * Math.PI * 0.5))) ? 1.0 : 1.0 - t); export const sineIn = (t: number) => (1e-14 > Math.abs((t = Math.cos(t * Math.PI * 0.5))) ? 1.0 : 1.0 - t);
export const sineOut = (t: number) => Math.sin((t * Math.PI) / 2); export const sineOut = (t: number) => Math.sin((t * Math.PI) / 2);
export const sineInOut = (t: number) => -0.5 * (Math.cos(Math.PI * t) - 1); export const sineInOut = (t: number) => -0.5 * (Math.cos(Math.PI * t) - 1.0);
export const circIn = (t: number) => 1 - Math.sin(Math.acos(t)); export const circIn = (t: number) => 1.0 - Math.sin(Math.acos(t));
export const circOut = (t: number) => Math.sin(Math.acos(1 - t)); export const circOut = (t: number) => Math.sin(Math.acos(1.0 - t));
export const circInOut = (t: number) => export const circInOut = (t: number) =>
0.5 * (t >= 0.5 ? 2.0 - Math.sin(Math.acos(1.0 - 2.0 * (1.0 - t))) : Math.sin(Math.acos(1.0 - 2.0 * t))); 0.5 * (t >= 0.5 ? 2.0 - Math.sin(Math.acos(1.0 - 2.0 * (1.0 - t))) : Math.sin(Math.acos(1.0 - 2.0 * t)));
export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => { export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {

@ -0,0 +1,14 @@
export const is_browser = typeof window !== 'undefined';
export const is_iframe = is_browser && window.self !== window.top;
export const is_cors =
is_iframe &&
(() => {
try {
if (window.parent) void window.parent.document;
return false;
} catch (error) {
return true;
}
})();
export const has_Symbol = typeof Symbol === 'function';
export const globals = is_browser ? window : typeof globalThis !== 'undefined' ? globalThis : global;

@ -1,49 +1,38 @@
import { add_render_callback, flush, schedule_update } from './scheduler'; import { add_render_callback, flush, schedule_update } from './scheduler';
import { current_component, set_current_component } from './lifecycle'; import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run_all, noop } from './utils'; import { noop } from './utils';
import { children, detach } from './dom'; import { children, detach } from './dom';
import { transition_in } from './transitions'; import { transition_in } from './transitions';
type binary = 0 | 1;
export interface Fragment { export interface Fragment {
key: string | null; key: string | null;
first: null; first: null;
/** /* create */ c: () => void;
* create /* claim */ l: (nodes: any) => void;
* run once
* runs hydrate if exists
*/
c: () => void;
/**
* claim
* runs hydrate if exists
* */
l: (nodes: any) => void;
/* hydrate */ h: () => void; /* hydrate */ h: () => void;
/* mount */ m: (target: HTMLElement, anchor: any, is_remount: boolean) => void; /* mount */ m: (target: HTMLElement, anchor: any, is_remount: binary) => void;
/* update */ p: (ctx: any, dirty: any) => void; /* update */ p: (ctx: any, dirty: any) => void;
/* measure */ r: () => void; /* measure */ r: () => void;
/* fix */ f: () => void; /* fix */ f: () => void;
/* animate */ a: () => void; /* animate */ a: () => void;
/* intro */ i: (local: 0 | 1) => void; /* intro */ i: (local: binary) => void;
/* outro */ o: (local: 0 | 1) => void; /* outro */ o: (local: binary) => void;
/* destroy */ d: (detaching: 0 | 1) => void; /* destroy */ d: (detaching: binary) => void;
} }
// eslint-disable-next-line @typescript-eslint/class-name-casing // eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ { export interface T$$ {
dirty: number[]; dirty: number[];
ctx: null | any; ctx: null | any;
bound: any; bound: any;
update: () => void; update: () => void;
callbacks: any; callbacks: any;
after_update: any[];
props: Record<string, 0 | string>; props: Record<string, 0 | string>;
fragment: null | false | Fragment; fragment: null | false | Fragment;
not_equal: any;
before_update: any[];
context: Map<any, any>; context: Map<any, any>;
on_mount: any[]; on_mount: any[];
on_destroy: any[]; on_destroy: any[];
before_update: any[];
after_update: any[];
} }
export function bind({ $$: { props, bound, ctx } }, name: string, callback: (prop: any) => void) { export function bind({ $$: { props, bound, ctx } }, name: string, callback: (prop: any) => void) {
@ -52,22 +41,24 @@ export function bind({ $$: { props, bound, ctx } }, name: string, callback: (pro
bound[index] = callback; bound[index] = callback;
callback(ctx[index]); callback(ctx[index]);
} }
export function mount_component({ $$: { fragment, on_mount, on_destroy, after_update } }, target, anchor) { export function mount_component({ $$ }, target, anchor) {
if (fragment) fragment.m(target, anchor); if ($$.fragment) $$.fragment.m(target, anchor);
add_render_callback(() => { add_render_callback(() => {
for (let i = 0, res; i < on_mount.length; i++) let { on_mount, on_destroy, after_update } = $$,
if (is_function((res = on_mount[i]()))) i = 0,
if (on_destroy) on_destroy.push(res); res;
for (; i < on_mount.length; i++)
if ('function' === typeof (res = on_mount[i]()))
if ($$.on_destroy) on_destroy.push(res);
else res(); // component already destroyed else res(); // component already destroyed
on_mount.length = 0; for (i = on_mount.length = 0; i < after_update.length; i++) after_update[i]();
for (let i = 0; i < after_update.length; i++) after_update[i]();
}); });
} }
export function destroy_component({ $$ }, detaching: 0 | 1) { export function destroy_component({ $$ }, detaching: 0 | 1) {
if ($$.fragment === null) return; if (null === $$.fragment) return;
run_all($$.on_destroy); for (let i = 0, { on_destroy } = $$; i < on_destroy.length; i++) on_destroy[i]();
if ($$.fragment) $$.fragment.d(detaching); if ($$.fragment) $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ // TODO null out other refs, including component.$$
@ -93,21 +84,20 @@ export function init(
fragment: null, fragment: null,
ctx: null, ctx: null,
// state /* state */
props, props,
update: noop, update: noop,
not_equal, bound: Object.create(null),
bound: blank_object(),
// lifecycle /* lifecycle */
on_mount: [], on_mount: [],
on_destroy: [], on_destroy: [],
before_update: [], before_update: [],
after_update: [], after_update: [],
context: new Map(parent_component ? parent_component.$$.context : []), context: new Map(parent_component ? parent_component.$$.context : []),
// everything else /* everything else */
callbacks: blank_object(), callbacks: Object.create(null),
dirty, dirty,
}); });
@ -131,7 +121,9 @@ export function init(
ready = true; ready = true;
run_all($$.before_update); for (let i = 0, { before_update } = $$; i < before_update.length; i++) {
before_update[i]();
}
// false when empty // false when empty
$$.fragment = create_fragment ? create_fragment($$.ctx) : false; $$.fragment = create_fragment ? create_fragment($$.ctx) : false;

@ -1,10 +1,10 @@
import { is_promise } from './utils'; import { is_promise } from './utils';
import { check_outros, group_outros, transition_in, transition_out } from './transitions'; import { transition_in, group_transition_out } from './transitions';
import { flush } from './scheduler'; import { flush } from './scheduler';
import { get_current_component, set_current_component } from './lifecycle'; import { get_current_component, set_current_component } from './lifecycle';
export function handle_promise(promise, info) { export function handle_promise(promise, info) {
const token = info.token = {}; const token = (info.token = {});
function update(type, index, key?, value?) { function update(type, index, key?, value?) {
if (info.token !== token) return; if (info.token !== token) return;
@ -26,11 +26,11 @@ export function handle_promise(promise, info) {
if (info.blocks) { if (info.blocks) {
info.blocks.forEach((block, i) => { info.blocks.forEach((block, i) => {
if (i !== index && block) { if (i !== index && block) {
group_outros(); group_transition_out((transition_out) => {
transition_out(block, 1, 1, () => { transition_out(block, () => {
info.blocks[i] = null; info.blocks[i] = null;
}); });
check_outros(); });
} }
}); });
} else { } else {
@ -54,15 +54,18 @@ export function handle_promise(promise, info) {
if (is_promise(promise)) { if (is_promise(promise)) {
const current_component = get_current_component(); const current_component = get_current_component();
promise.then(value => { promise.then(
(value) => {
set_current_component(current_component); set_current_component(current_component);
update(info.then, 1, info.value, value); update(info.then, 1, info.value, value);
set_current_component(null); set_current_component(null);
}, error => { },
(error) => {
set_current_component(current_component); set_current_component(current_component);
update(info.catch, 2, info.error, error); update(info.catch, 2, info.error, error);
set_current_component(null); set_current_component(null);
}); }
);
// if we previously had a then/catch block, destroy it // if we previously had a then/catch block, destroy it
if (info.current !== info.pending) { if (info.current !== info.pending) {

@ -1,9 +1,15 @@
import { custom_event, append, insert, detach, listen, attr } from './dom'; import { custom_event, append, insert, detach, listen, attr } from './dom';
let inited import { now } from './environment';
let inited;
export function add_location_dev$legacy(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char },
};
}
export function dispatch_dev$legacy<T = any>(type: string, detail?: T) { export function dispatch_dev$legacy<T = any>(type: string, detail?: T) {
if(!inited && `__SVELTE_DEVTOOLS_GLOBAL_HOOK__` in window) { if (!inited && `__SVELTE_DEVTOOLS_GLOBAL_HOOK__` in window) {
inited = true inited = true;
throw new Error(`You must specify the version`) throw new Error(`You must specify the version`);
} }
document.dispatchEvent(custom_event(type, { version: __VERSION__, ...detail })); document.dispatchEvent(custom_event(type, { version: __VERSION__, ...detail }));
} }
@ -88,3 +94,16 @@ export function set_data_dev$legacy(text, data) {
dispatch_dev$legacy('SvelteDOMSetData', { node: text, data }); dispatch_dev$legacy('SvelteDOMSetData', { node: text, data });
text.data = data; text.data = data;
} }
export function loop_guard_dev$legacy(timeout) {
const start = now();
return () => {
if (now() - start > timeout) {
throw new Error(`Infinite loop detected`);
}
};
}
export function validate_store_dev$legacy(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}

@ -0,0 +1,148 @@
import { SvelteComponent } from './Component';
const dev$context = {
block: null,
component: null,
};
abstract class Dev$ {
self;
parent_block = dev$context.block;
parent_component = dev$context.component;
constructor(self) {
this.self = self;
}
abstract add(type, payload): void;
abstract subscribers: {}[];
dispatch(type, payload: any) {
this.add(type, payload);
this.subscribers.forEach((subscriber) => {
if (type in subscriber) subscriber[type](payload);
});
}
subscribe(subscriber) {
this.subscribers.push(subscriber);
return () => {
const index = this.subscribers.indexOf(subscriber);
if (~index) this.subscribers.splice(index, 1);
};
}
}
class Dev$Component extends Dev$ {
self: SvelteComponent;
block;
add(type, payload) {}
}
class Dev$Block extends Dev$ {
self;
parent_block;
parent_component;
elements: Dev$Element[] = [];
}
class Dev$Element extends Dev$ {
component;
node;
parent;
children = [];
attributes: {};
eventListeners: {};
style: {};
class: {};
add(type, payload) {}
subscribers: Dev$ElementSubscriber[] = [];
dispatch(type, payload: any) {
this.add(type, payload);
this.subscribers.forEach((subscriber) => {
if (type in subscriber) subscriber[type](payload);
});
}
subscribe(subscriber) {
this.subscribers.push(subscriber);
return () => {
const index = this.subscribers.indexOf(subscriber);
if (~index) this.subscribers.splice(index, 1);
};
}
}
interface Dev$ElementSubscriber {
onMount(payload): void;
onUnmount(payload): void;
setAttribute(payload): void;
removeAttribute(payload): void;
addEventListener(payload): void;
removeEventListener(payload): void;
addClass(payload): void;
removeClass(payload): void;
addStyle(payload): void;
removeStyle(payload): void;
}
const dev$dispatch =
__DEV__ &&
!__TEST__ &&
typeof window !== 'undefined' &&
(function dev$create_hook() {
const subscribers = [];
const rootComponents = [];
const hook = {
version: __VERSION__,
has_subscribers: false,
subscribe(listener) {
this.has_subscribers = true;
subscribers.push(listener);
if (rootComponents.length) rootComponents.forEach((component) => listener.addRootComponent(component));
else listener.ping(`init-no-components`);
return () => {
const index = subscribers.indexOf(listener);
if (~index) {
subscribers.splice(index, 1);
this.has_subscribers = !!subscribers.length;
return true;
}
return false;
};
},
};
Object.defineProperty(window, `__SVELTE_DEVTOOLS_GLOBAL_HOOK__`, {
enumerable: false,
get() {
return hook;
},
});
return function update(target_type, event_type, ...values) {
if (!hook.has_subscribers) return;
subscribers.forEach((listener) => {
if (target_type in listener && event_type in listener[target_type]) {
listener[target_type][event_type](...values);
}
});
};
})();
export function dev$element(element: Element | Node | EventTarget, event: keyof ElementEventsMap, payload?: any) {
if (__DEV__) {
dev$dispatch(`element`, event, element, payload);
}
}
export function dev$block(event: keyof BlockEventsMap, payload) {}
export function dev$tracing(type, value: any) {}
export function dev$assert(truthy: any, else_throw: string) {
if (__DEV__ && !truthy) {
throw new Error(else_throw);
}
}
interface ElementEventsMap {
onMount;
onDestroy;
setAttribute;
removeAttribute;
addEventListener;
removeEventListener;
addClass;
removeClass;
}
interface BlockEventsMap {
'create';
'claim';
'claim.failed';
}

@ -1,214 +0,0 @@
import { custom_event, insert, detach, attr } from './dom';
import { SvelteComponent } from './Component';
import {
get_current_component,
beforeUpdate,
onMount,
afterUpdate,
onDestroy,
createEventDispatcher,
setContext,
getContext,
} from './lifecycle';
import { writable } from 'svelte/store';
export const [
beforeUpdate_dev,
onMount_dev,
afterUpdate_dev,
onDestroy_dev,
createEventDispatcher_dev,
setContext_dev,
getContext_dev,
] = [beforeUpdate, onMount, afterUpdate, onDestroy, createEventDispatcher, setContext, getContext].map(
(fn) => (a?, b?) => {
if (!get_current_component()) throw new Error(`${fn.name} cannot be called outside of component initialization`);
return fn(a, b);
}
);
const dev$hook =
__DEV__ &&
!__TEST__ &&
typeof window !== 'undefined' &&
(() => {
const subscribers = [];
function subscribe(listener) {
subscribers.push(listener);
return () => subscribers.splice(subscribers.indexOf(listener), 1);
}
const components = [];
const hook = writable(components);
Object.defineProperty(window, `__SVELTE_DEVTOOLS_GLOBAL_HOOK__`, {
enumerable: false,
get() {
return {
version: __VERSION__,
components,
};
},
});
return function update(type, value) {
subscribers.forEach((listener) => {
listener(type, value);
});
};
})();
export function dev$dispatch(type: string, value: any) {
dev$hook.update((o) => {
return o;
});
}
export function dev$element(element: Element, event: keyof ElementEventsMap, payload?: any) {
if (__DEV__) {
}
}
export function dev$block(event: keyof BlockEventsMap, payload) {}
export function dev$tracing(type, value: any) {}
export function dev$assert(truthy: boolean, else_throw: string) {
if (__DEV__ && !truthy) {
throw new Error(else_throw);
}
}
interface SvelteDevEvent extends CustomEvent {
__VERSION__: string;
}
interface ElementEventsMap {
mount;
unmount;
setAttribute;
removeAttribute;
addEventListener;
removeEventListener;
addClass;
removeClass;
}
interface BlockEventsMap {
'create';
'claim';
'claim.failed';
}
interface SvelteDevErrorsMap {}
export interface SvelteDOM {}
export function dispatch_dev<T = any>(type: string, detail?: T) {
document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }));
}
export function insert_dev(target: Node, node: Node, anchor?: Node) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
export function detach_dev(node: Node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
export function detach_between_dev(before: Node, after: Node) {
while (before.nextSibling && before.nextSibling !== after) {
detach_dev(before.nextSibling);
}
}
export function detach_before_dev(after: Node) {
while (after.previousSibling) {
detach_dev(after.previousSibling);
}
}
export function detach_after_dev(before: Node) {
while (before.nextSibling) {
detach_dev(before.nextSibling);
}
}
export function attr_dev(node: Element, attribute: string, value?: string) {
attr(node, attribute, value);
if (value == null) dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
export function prop_dev(node: Element, property: string, value?: any) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
export function dataset_dev(node: HTMLElement, property: string, value?: any) {
node.dataset[property] = value;
dispatch_dev('SvelteDOMSetDataset', { node, property, value });
}
export function set_data_dev(text, data) {
data = '' + data;
if (text.data === data) return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
export function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
export function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void;
$destroy(): void;
[accessor: string]: any;
}
export class SvelteComponentDev extends SvelteComponent {
constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`);
}
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
};
}
$capture_state() {}
$inject_state() {}
}
export function loop_guard(timeout) {
const start = Date.now();
return () => {
if (Date.now() - start > timeout) {
throw new Error(`Infinite loop detected`);
}
};
}

@ -0,0 +1,56 @@
import { SvelteComponent } from './Component';
import { now, has_Symbol } from './environment';
import { dev$assert } from './dev.tools';
export const dev$is_array_like = (arg) =>
dev$assert(
typeof arg === 'string' || (typeof arg === 'object' && 'length' in arg),
`{#each} only iterates over Array-like Objects.${
has_Symbol && Symbol.iterator in arg
? ' Consider using a [...spread] to convert this iterable into an Array instead.'
: ''
}`
);
export const dev$known_slots = (name, slot, keys) =>
Object.keys(slot).forEach((key) => dev$assert(keys.includes(key), `<${name}> received an unexpected slot "${key}".`));
export const dev$is_valid_store = (store, name) =>
dev$assert(
typeof store === 'object' && (store === null || typeof store.subscribe === 'function'),
`Could not subscribe to $${name}. A valid store is an object with a .subscribe method, consider setting ${name} to null if this is expected.`
);
export function dev$loop_guard(loopGuardTimeout) {
const start = now();
return () => dev$assert(now() - start < loopGuardTimeout, `Infinite loop detected`);
}
type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void;
$destroy(): void;
[accessor: string]: any;
}
export class SvelteComponentDev extends SvelteComponent {
constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) throw new Error(`'target' is a required option`);
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
};
}
$capture_state() {}
$inject_state() {}
}

@ -1,18 +1,18 @@
import { has_prop } from './utils'; import { dev$element, dev$block } from './dev.tools';
import { dispatch_dev, dev$dispatch, dev$element, dev$block } from './dev'; import { is_client, is_cors } from './environment';
export function append(target: Node, node: Node) { export function append(target: Node, node: Node) {
dev$element(node, `mount`, { target }); dev$element(node, `onMount`, { target });
target.appendChild(node); target.appendChild(node);
} }
export function insert(target: Node, node: Node, anchor?: Node) { export function insert(target: Node, node: Node, anchor?: Node) {
dev$element(node, `mount`, { target, anchor }); dev$element(node, `onMount`, { target, anchor });
target.insertBefore(node, anchor || null); target.insertBefore(node, anchor || null);
} }
export function detach(node: Node) { export function detach(node: Node) {
dev$element(node, `unmount`); dev$element(node, `onDestroy`);
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
} }
@ -31,18 +31,10 @@ export function element_is<K extends keyof HTMLElementTagNameMap>(name: K, is: s
if (__DEV__) return dev$create(document.createElement<K>(name)); if (__DEV__) return dev$create(document.createElement<K>(name));
return document.createElement<K>(name, { is }); return document.createElement<K>(name, { is });
} }
export function object_without_properties<T, K extends keyof T>(obj: T, exclude: K[]) { export function object_without_properties<T, K extends string[]>(obj: T, excluded: K) {
const target = {} as Pick<T, Exclude<keyof T, K>>; const target = {} as Pick<T, Exclude<keyof T, keyof K>>;
for (const k in obj) { let key;
if ( for (key in obj) if (!~excluded.indexOf(key)) target[key] = obj[key];
has_prop(obj, k) &&
// @ts-ignore
exclude.indexOf(k) === -1
) {
// @ts-ignore
target[k] = obj[k];
}
}
return target; return target;
} }
@ -116,7 +108,7 @@ export function attr(node: Element, name: string, value?: any) {
} }
export function set_attributes(node: HTMLElement, attributes: { [x: string]: any }) { export function set_attributes(node: HTMLElement, attributes: { [x: string]: any }) {
// @ts-ignore #3687 // @ts-ignore
const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); const descriptors = Object.getOwnPropertyDescriptors(node.__proto__);
let name; let name;
for (name in attributes) { for (name in attributes) {
@ -264,26 +256,6 @@ export function select_multiple_value(select) {
return [].map.call(select.querySelectorAll(':checked'), (option) => option.__value); return [].map.call(select.querySelectorAll(':checked'), (option) => option.__value);
} }
// unfortunately this can't be a constant as that wouldn't be tree-shakeable
// so we cache the result instead
let crossorigin: boolean;
export function is_crossorigin() {
if (crossorigin === undefined) {
crossorigin = false;
try {
if (typeof window !== 'undefined' && window.parent) {
void window.parent.document;
}
} catch (error) {
crossorigin = true;
}
}
return crossorigin;
}
export function add_resize_listener(node: HTMLElement, fn: () => void) { export function add_resize_listener(node: HTMLElement, fn: () => void) {
const computed_style = getComputedStyle(node); const computed_style = getComputedStyle(node);
const z_index = (parseInt(computed_style.zIndex) || 0) - 1; const z_index = (parseInt(computed_style.zIndex) || 0) - 1;
@ -304,7 +276,7 @@ export function add_resize_listener(node: HTMLElement, fn: () => void) {
let unsubscribe: () => void; let unsubscribe: () => void;
if (is_crossorigin()) { if (is_cors) {
iframe.src = `data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>`; iframe.src = `data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>`;
unsubscribe = listen(window, 'message', (event: MessageEvent) => { unsubscribe = listen(window, 'message', (event: MessageEvent) => {
if (event.source === iframe.contentWindow) fn(); if (event.source === iframe.contentWindow) fn();
@ -334,11 +306,6 @@ export function custom_event<T = any>(type: string, detail?: T) {
event.initCustomEvent(type, false, false, detail); event.initCustomEvent(type, false, false, detail);
return event; return event;
} }
export function query_selector_all(selector: string, parent: HTMLElement = document.body) {
return Array.from(parent.querySelectorAll(selector));
}
export class HtmlTag { export class HtmlTag {
e: HTMLElement; e: HTMLElement;
n: ChildNode[]; n: ChildNode[];
@ -374,3 +341,18 @@ export class HtmlTag {
this.n.forEach(detach); this.n.forEach(detach);
} }
} }
export const hasOwnProperty = Object.prototype.hasOwnProperty;
const nodeProto = Node.prototype;
export const insertBefore = nodeProto.insertBefore;
export const removeChild = nodeProto.removeChild;
export const replaceChild = nodeProto.replaceChild;
export const cloneNode = nodeProto.cloneNode;
const elementProto = Element.prototype;
export const setAttribute = elementProto.setAttribute;
export const setAttributeNS = elementProto.setAttributeNS;
export const removeAttribute = elementProto.removeAttribute;
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
export const setClassName = getOwnPropertyDescriptor(elementProto, 'className').set;
export const getStyle = getOwnPropertyDescriptor(HTMLElement.prototype, 'style').get;
export const svg_getStyle = getOwnPropertyDescriptor(SVGElement.prototype, 'style').get;

@ -1,16 +1,30 @@
import { noop } from './utils'; import { noop } from './utils';
export const resolved_promise = Promise.resolve(); export const resolved_promise = Promise.resolve();
export const is_client = typeof window !== 'undefined'; export const is_client = typeof window !== 'undefined';
export const is_iframe = is_client && window.location !== window.parent.location; export const is_iframe = !is_client && window.self !== window.top;
export const is_cors =
is_iframe &&
(() => {
try {
if (window.parent) void window.parent.document;
return false;
} catch (error) {
return true;
}
})();
export const globals = ((is_client
? window
: typeof globalThis !== 'undefined'
? globalThis
: global) as unknown) as typeof globalThis;
export const has_Symbol = typeof Symbol === 'function';
export let now = is_client ? performance.now.bind(performance) : Date.now.bind(Date); export let now = is_client ? performance.now.bind(performance) : Date.now.bind(Date);
export let raf = is_client ? window.requestAnimationFrame : noop; export let raf = is_client ? requestAnimationFrame : noop;
/* tests only */
export const set_now = (fn) => void (now = fn);
// used internally for testing export const set_raf = (fn) => void (raf = fn);
export function set_now(fn) {
now = fn;
}
export function set_raf(fn) {
raf = fn;
}

@ -1,7 +0,0 @@
declare const global: any;
export const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global) as unknown as typeof globalThis;

@ -2,10 +2,10 @@ export * from './animations';
export * from './await_block'; export * from './await_block';
export * from './Component'; export * from './Component';
export * from './dev.legacy'; export * from './dev.legacy';
export * from './dev'; export * from './dev.utils';
export * from './dev.tools';
export * from './dom'; export * from './dom';
export * from './environment'; export * from './environment';
export * from './globals';
export * from './keyed_each'; export * from './keyed_each';
export * from './lifecycle'; export * from './lifecycle';
export * from './loop'; export * from './loop';

@ -1,27 +1,18 @@
import { transition_in, transition_out } from './transitions'; import { transition_in } from './transitions';
export function update_keyed_each(
export function destroy_block(block, lookup) { old_blocks,
block.d(1); dirty,
lookup.delete(block.key); ctx,
} state,
get_key,
export function outro_and_destroy_block(block, lookup) { list,
transition_out(block, 1, 1, () => { lookup,
lookup.delete(block.key); node,
}); create_each_block,
} next,
get_context,
export function fix_and_destroy_block(block, lookup) { transition_out?
block.f(); ) {
destroy_block(block, lookup);
}
export function fix_and_outro_and_destroy_block(block, lookup) {
block.f();
outro_and_destroy_block(block, lookup);
}
export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) {
let o = old_blocks.length; let o = old_blocks.length;
let n = list.length; let n = list.length;
@ -42,11 +33,11 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
if (!block) { if (!block) {
block = create_each_block(key, child_ctx); block = create_each_block(key, child_ctx);
block.c(); block.c();
} else if (dynamic) { } else if (state & 1) {
block.p(child_ctx, dirty); block.p(child_ctx, dirty);
} }
new_lookup.set(key, new_blocks[i] = block); new_lookup.set(key, (new_blocks[i] = block));
if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key]));
} }
@ -54,13 +45,18 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
const will_move = new Set(); const will_move = new Set();
const did_move = new Set(); const did_move = new Set();
function insert(block) { const insert = (block) => {
transition_in(block, 1); transition_in(block, 1);
block.m(node, next, lookup.has(block.key)); block.m(node, next, lookup.has(block.key));
lookup.set(block.key, block); lookup.set(block.key, block);
next = block.first; next = block.first;
n--; n--;
} };
const destroy = (block) => {
if (state & 2) block.f();
if (state & 4) transition_out(block, () => lookup.delete(block.key));
else block.d(1), lookup.delete(block.key);
};
while (o && n) { while (o && n) {
const new_block = new_blocks[n - 1]; const new_block = new_blocks[n - 1];
@ -73,25 +69,17 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
next = new_block.first; next = new_block.first;
o--; o--;
n--; n--;
} } else if (!new_lookup.has(old_key)) {
else if (!new_lookup.has(old_key)) {
// remove old block // remove old block
destroy(old_block, lookup); destroy(old_block);
o--; o--;
} } else if (!lookup.has(new_key) || will_move.has(new_key)) {
else if (!lookup.has(new_key) || will_move.has(new_key)) {
insert(new_block); insert(new_block);
} } else if (did_move.has(old_key)) {
else if (did_move.has(old_key)) {
o--; o--;
} else if (deltas.get(new_key) > deltas.get(old_key)) { } else if (deltas.get(new_key) > deltas.get(old_key)) {
did_move.add(new_key); did_move.add(new_key);
insert(new_block); insert(new_block);
} else { } else {
will_move.add(old_key); will_move.add(old_key);
o--; o--;
@ -100,7 +88,7 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
while (o--) { while (o--) {
const old_block = old_blocks[o]; const old_block = old_blocks[o];
if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); if (!new_lookup.has(old_block.key)) destroy(old_block);
} }
while (n) insert(new_blocks[n - 1]); while (n) insert(new_blocks[n - 1]);

@ -1,12 +1,13 @@
import { custom_event } from './dom'; import { custom_event } from './dom';
import { dev$assert, SvelteComponentDev } from './dev'; import { SvelteComponentDev } from './dev.utils';
import { SvelteComponent } from './Component'; import { SvelteComponent } from './Component';
import { dev$assert } from './dev.tools';
export let current_component: SvelteComponentDev | SvelteComponent | null; export let current_component: SvelteComponentDev | SvelteComponent | null;
export const set_current_component = (component) => (current_component = component); export const set_current_component = (component) => (current_component = component);
const dev$guard = (name: string) => const dev$on_init_only = (name: string) =>
dev$assert(!!current_component, `${name} cannot be called outside of component initialization`); dev$assert(!!current_component, `${name} cannot be called outside of component initialization`);
export function get_current_component() { export function get_current_component() {
@ -14,27 +15,27 @@ export function get_current_component() {
} }
export function beforeUpdate(fn) { export function beforeUpdate(fn) {
dev$guard(`beforeUpdate`); dev$on_init_only(`beforeUpdate`);
return current_component.$$.before_update.push(fn); return current_component.$$.before_update.push(fn);
} }
export function onMount(fn) { export function onMount(fn) {
dev$guard(`onMount`); dev$on_init_only(`onMount`);
return current_component.$$.on_mount.push(fn); return current_component.$$.on_mount.push(fn);
} }
export function afterUpdate(fn) { export function afterUpdate(fn) {
dev$guard(`afterUpdate`); dev$on_init_only(`afterUpdate`);
return current_component.$$.after_update.push(fn); return current_component.$$.after_update.push(fn);
} }
export function onDestroy(fn) { export function onDestroy(fn) {
dev$guard(`onDestroy`); dev$on_init_only(`onDestroy`);
return current_component.$$.on_destroy.push(fn); return current_component.$$.on_destroy.push(fn);
} }
// todo : deprecate
export function createEventDispatcher() { export function createEventDispatcher() {
dev$guard(`createEventDispatcher`); dev$on_init_only(`createEventDispatcher`);
const component = current_component; const component = current_component;
return (type: string, detail?: any) => { return (type: string, detail?: any) => {
const callbacks = component.$$.callbacks[type]; const callbacks = component.$$.callbacks[type];
@ -49,12 +50,12 @@ export function createEventDispatcher() {
} }
export function setContext(key, context) { export function setContext(key, context) {
dev$guard(`setContext`); dev$on_init_only(`setContext`);
current_component.$$.context.set(key, context); current_component.$$.context.set(key, context);
} }
export function getContext(key) { export function getContext(key) {
dev$guard(`getContext`); dev$on_init_only(`getContext`);
return current_component.$$.context.get(key); return current_component.$$.context.get(key);
} }

@ -17,7 +17,6 @@ const run = (t: number) => {
} }
running_frame.length = 0; running_frame.length = 0;
if (next_frame_length) raf(run); if (next_frame_length) raf(run);
else console.log('ended loop');
}; };
type TimeoutTask = { timestamp: number; callback: (now: number) => void }; type TimeoutTask = { timestamp: number; callback: (now: number) => void };

@ -1,73 +1,102 @@
import { set_current_component } from './lifecycle'; import { set_current_component } from './lifecycle';
import { resolved_promise } from './environment'; import { resolved_promise } from './environment';
import { T$$ } from './Component';
const dirty_components = [];
let update_scheduled = false; let update_scheduled = false;
export const schedule_update = (component) => { let is_flushing = false;
dirty_components.push(component);
if (!update_scheduled) (update_scheduled = true), resolved_promise.then(flush);
};
export const tick = () =>
update_scheduled ? resolved_promise : ((update_scheduled = true), resolved_promise.then(flush));
// todo : remove binding_callbacks export
const dirty_components = [];
export const binding_callbacks = []; export const binding_callbacks = [];
const render_callbacks = []; const render_callbacks = [];
const seen_callbacks = new Set();
export const add_render_callback = (fn) =>
void (!seen_callbacks.has(fn) && (seen_callbacks.add(fn), render_callbacks.push(fn)));
const flush_callbacks = [];
export const add_flush_callback = (fn) => void flush_callbacks.push(fn);
const measure_callbacks = []; const measure_callbacks = [];
export const add_measure_callback = (fn) => void measure_callbacks.push(fn); const flush_callbacks = [];
let flushing = false; // todo : remove add_flush_callback
export function flush() { export const add_flush_callback = Array.prototype.push.bind(flush_callbacks);
if (flushing) return; export const add_measure_callback = Array.prototype.push.bind(measure_callbacks);
else flushing = true;
const seen_render_callbacks = new Set();
export const add_render_callback = (fn) => {
if (!seen_render_callbacks.has(fn)) {
seen_render_callbacks.add(fn);
render_callbacks.push(fn);
}
};
export const schedule_update = (component) => {
dirty_components.push(component);
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
};
export const tick = () => {
if (!update_scheduled) {
update_scheduled = true;
return resolved_promise.then(flush);
}
return resolved_promise;
};
export const flush = () => {
if (is_flushing) return;
else is_flushing = true;
let i = 0, let i = 0,
j = 0, j = 0,
$$, $$: T$$,
dirty,
before_update, before_update,
dirty; after_update;
// RUN LOGIC, CANCEL STYLES
do { do {
// update components + beforeUpdate for (; i < dirty_components.length; i++) {
for (i = 0; i < dirty_components.length; i++) {
({ $$ } = set_current_component(dirty_components[i])); ({ $$ } = set_current_component(dirty_components[i]));
if ($$.fragment === null) continue;
// todo : is this check still necessary ?
if (null === $$.fragment) continue;
/* run reactive statements */
$$.update(); $$.update();
for (j = 0, { before_update } = $$; j < before_update.length; j++) before_update[j]();
({ dirty } = $$); /* run beforeUpdate */
$$.dirty = [-1]; for (j = 0, { before_update } = $$; j < before_update.length; j++) {
if ($$.fragment) $$.fragment.p($$.ctx, dirty); before_update[j]();
render_callbacks.push(...$$.after_update); }
/* update blocks */
({ dirty } = $$).dirty = [-1];
if (false !== $$.fragment) $$.fragment.p($$.ctx, dirty);
/* schedule afterUpdate */
for (j = 0, { after_update } = $$; j < after_update.length; j++) {
add_render_callback(after_update[j]);
}
} }
dirty_components.length = 0; dirty_components.length = 0;
// update bindings in reverse order // update bindings [ in reverse order (#3145); is there a better way ? ]
i = binding_callbacks.length; i = binding_callbacks.length;
while (i--) binding_callbacks[i](); while (i--) binding_callbacks[i]();
binding_callbacks.length = 0; binding_callbacks.length = i = 0;
// afterUpdate // run afterUpdates
for (i = 0; i < render_callbacks.length; i++) render_callbacks[i](); // todo : remove every non afterUpdate callback from render_callbacks
render_callbacks.length = 0; for (; i < render_callbacks.length; i++) render_callbacks[i]();
seen_callbacks.clear(); render_callbacks.length = i = 0;
} while (dirty_components.length); } while (dirty_components.length);
seen_render_callbacks.clear();
update_scheduled = false; update_scheduled = false;
// STYLE MEASURE CHANGES // measurement callbacks for animations
for (i = 0; i < measure_callbacks.length; i++) flush_callbacks.push(measure_callbacks[i]()); for (i = 0; i < measure_callbacks.length; i++) {
measure_callbacks.length = 0; flush_callbacks.push(measure_callbacks[i]());
}
measure_callbacks.length = i = 0;
// APPLY STYLE CHANGES // apply styles
for (i = 0; i < flush_callbacks.length; i++) flush_callbacks[i](); // todo : remove every non style callback from flush_callbacks
for (; i < flush_callbacks.length; i++) flush_callbacks[i]();
flush_callbacks.length = 0; flush_callbacks.length = 0;
flushing = false; is_flushing = false;
} };

@ -1,41 +1,16 @@
export function get_spread_update(levels, updates) { export function get_spread_update(levels, updates: any[]) {
const update = {}; const update = {};
const to_null_out = {}; const to_null_out = {};
const accounted_for = { $$scope: 1 }; const accounted_for = { $$scope: 1 };
let i = levels.length,
let i = levels.length; key,
while (i--) { n;
const o = levels[i]; while (i--)
const n = updates[i]; if ((n = updates[i])) {
for (key in levels[i]) if (!(key in n)) to_null_out[key] = 1;
if (n) { for (key in n) if (!accounted_for[key]) (update[key] = n[key]), (accounted_for[key] = 1);
for (const key in o) {
if (!(key in n)) to_null_out[key] = 1;
}
for (const key in n) {
if (!accounted_for[key]) {
update[key] = n[key];
accounted_for[key] = 1;
}
}
levels[i] = n; levels[i] = n;
} else { } else for (key in levels[i]) accounted_for[key] = 1;
for (const key in o) { for (key in to_null_out) if (!(key in update)) update[key] = undefined;
accounted_for[key] = 1;
}
}
}
for (const key in to_null_out) {
if (!(key in update)) update[key] = undefined;
}
return update; return update;
} }
export function get_spread_object(spread_props) {
return typeof spread_props === 'object' && spread_props !== null ? spread_props : {};
}

@ -1,5 +1,4 @@
import { set_current_component, current_component } from './lifecycle'; import { set_current_component, current_component } from './lifecycle';
import { run_all, blank_object } from './utils';
import { boolean_attributes } from '../../compiler/compile/render_ssr/handlers/shared/boolean_attributes'; import { boolean_attributes } from '../../compiler/compile/render_ssr/handlers/shared/boolean_attributes';
export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
@ -17,13 +16,13 @@ export function spread(args, classes_to_add) {
} }
let str = ''; let str = '';
Object.keys(attributes).forEach(name => { Object.keys(attributes).forEach((name) => {
if (invalid_attribute_name_character.test(name)) return; if (invalid_attribute_name_character.test(name)) return;
const value = attributes[name]; const value = attributes[name];
if (value === true) str += " " + name; if (value === true) str += ' ' + name;
else if (boolean_attributes.has(name.toLowerCase())) { else if (boolean_attributes.has(name.toLowerCase())) {
if (value) str += " " + name; if (value) str += ' ' + name;
} else if (value != null) { } else if (value != null) {
str += ` ${name}="${String(value).replace(/"/g, '&#34;').replace(/'/g, '&#39;')}"`; str += ` ${name}="${String(value).replace(/"/g, '&#34;').replace(/'/g, '&#39;')}"`;
} }
@ -37,11 +36,11 @@ export const escaped = {
"'": '&#39;', "'": '&#39;',
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;' '>': '&gt;',
}; };
export function escape(html) { export function escape(html) {
return String(html).replace(/["'&<>]/g, match => escaped[match]); return String(html).replace(/["'&<>]/g, (match) => escaped[match]);
} }
export function each(items, fn) { export function each(items, fn) {
@ -53,13 +52,15 @@ export function each(items, fn) {
} }
export const missing_component = { export const missing_component = {
$$render: () => '' $$render: () => '',
}; };
export function validate_component(component, name) { export function validate_component(component, name) {
if (!component || !component.$$render) { if (!component || !component.$$render) {
if (name === 'svelte:component') name += ' this={...}'; if (name === 'svelte:component') name += ' this={...}';
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`); throw new Error(
`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`
);
} }
return component; return component;
@ -85,7 +86,7 @@ export function create_ssr_component(fn) {
on_mount: [], on_mount: [],
before_update: [], before_update: [],
after_update: [], after_update: [],
callbacks: blank_object() callbacks: Object.create(null),
}; };
set_current_component({ $$ }); set_current_component({ $$ });
@ -111,25 +112,29 @@ export function create_ssr_component(fn) {
const html = $$render(result, props, {}, options); const html = $$render(result, props, {}, options);
run_all(on_destroy); for (let i = 0; i < on_destroy.length; i++) on_destroy[i]();
return { return {
html, html,
css: { css: {
code: Array.from(result.css).map(css => css.code).join('\n'), code: Array.from(result.css)
map: null // TODO .map((css) => css.code)
.join('\n'),
map: null, // TODO
}, },
head: result.title + result.head head: result.title + result.head,
}; };
}, },
$$render $$render,
}; };
} }
export function add_attribute(name, value, boolean) { export function add_attribute(name, value, boolean) {
if (value == null || (boolean && !value)) return ''; if (value == null || (boolean && !value)) return '';
return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `"${value}"`}`}`; return ` ${name}${
value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `"${value}"`}`
}`;
} }
export function add_classes(classes) { export function add_classes(classes) {

@ -1,5 +1,5 @@
import { element } from './dom'; import { element } from './dom';
import { raf, now } from './environment'; import { raf } from './environment';
const enum SVELTE { const enum SVELTE {
RULE = `__svelte_`, RULE = `__svelte_`,
STYLESHEET = `__svelte_stylesheet`, STYLESHEET = `__svelte_stylesheet`,

@ -7,44 +7,54 @@ import { add_measure_callback } from './scheduler';
import { animate_css } from './style_manager'; import { animate_css } from './style_manager';
type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig; type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig;
type StopResetReverse = (reset_reverse?: 1 | -1) => StopResetReverse; export type StopResetReverse = (reset_reverse?: 1 | -1) => StopResetReverse;
function startStopDispatcher(node: Element, is_intro: boolean) { export const transition_in = (block: Fragment, local?) => {
node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}start`));
return () => node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}end`));
}
const outroing = new Set();
let transition_group;
export const group_outros = () => void (transition_group = { p: transition_group, c: [], r: 0 });
export const check_outros = () => {
if (!transition_group.r) for (let i = 0; i < transition_group.c.length; i++) transition_group.c[i]();
transition_group = transition_group.p;
};
export const transition_in = (block: Fragment, local?: 0 | 1) => {
if (!block || !block.i) return; if (!block || !block.i) return;
outroing.delete(block); outroing.delete(block);
block.i(local); block.i(local);
}; };
export const transition_out = (block: Fragment, local?: 0 | 1, detach?: 0 | 1, callback?: () => void) => { export const transition_out = (block: Fragment, local) => {
if (!block || !block.o || outroing.has(block)) return;
outroing.add(block);
block.o(local);
};
let transition_group;
const outroing = new Set();
const check_transition_group = (group, decrement = true) => {
if (decrement) group.r--;
if (!group.r) for (let i = 0; i < group.c.length; i++) group.c[i]();
};
export const group_transition_out = (fn) => {
const c = [];
const current_group = (transition_group = { p: transition_group, c, r: 0 });
fn((block, callback, detach = true) => {
if (!block || !block.o || outroing.has(block)) return; if (!block || !block.o || outroing.has(block)) return;
outroing.add(block); outroing.add(block);
transition_group.c.push(() => { c.push(() => {
if (!outroing.has(block)) return; if (outroing.has(block)) {
else outroing.delete(block); outroing.delete(block);
if (!callback) return;
if (detach) block.d(1); if (detach) block.d(1);
// callback always ?
callback(); callback();
}
}); });
block.o(local); block.o(1);
});
check_transition_group(current_group, false);
transition_group = transition_group.p;
}; };
// todo : deprecate
function startStopDispatcher(node: Element, is_intro: boolean) {
node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}start`));
return () => node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}end`));
}
/* todo: deprecate */ /* todo: deprecate */
const swap = (fn, is_intro) => (is_intro ? (t) => fn(t, 1 - t) : (t) => fn(1 - t, t)); const swap = (fn, is_intro) =>
fn.length === 1 ? (is_intro ? fn : (t) => fn(1 - t)) : is_intro ? (t) => fn(t, 1 - t) : (t) => fn(1 - t, t);
const mirrored = (fn, is_intro, easing) => { const mirrored = (fn, is_intro, easing) => {
const run = swap(fn, is_intro); const run = swap(fn, is_intro);
@ -78,7 +88,7 @@ export const run_transition = (
let start_time = 0; let start_time = 0;
let end_time = 0; let end_time = 0;
const group = transition_group; const current_group = transition_group;
if (!is_intro) transition_group.r++; if (!is_intro) transition_group.r++;
const start = ({ delay = 0, duration = 300, easing, tick, css, strategy = 'reverse' }: TransitionConfig) => { const start = ({ delay = 0, duration = 300, easing, tick, css, strategy = 'reverse' }: TransitionConfig) => {
@ -115,13 +125,7 @@ export const run_transition = (
} }
} }
if (!is_intro) { if (!is_intro) check_transition_group(current_group);
if (!--group.r) {
for (let i = 0; i < group.c.length; i++) {
group.c[i]();
}
}
}
if (is_bidirectional) { if (is_bidirectional) {
if (-1 === t) { if (-1 === t) {

@ -1,52 +1,11 @@
export function noop() {} export function noop() {}
export const is_promise = <T = any>(value: any): value is PromiseLike<T> =>
value && typeof value === 'object' && typeof value.then === 'function';
export const identity = (x) => x; export const safe_not_equal = (a, b) =>
a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
export function assign<T, S>(tar: T, src: S): T & S { export const not_equal = (a, b) => (a != a ? b == b : a !== b);
// @ts-ignore
for (const k in src) tar[k] = src[k];
return tar as T & S;
}
export function is_promise<T = any>(value: any): value is PromiseLike<T> {
return value && typeof value === 'object' && typeof value.then === 'function';
}
export function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char },
};
}
export function run(fn) {
return fn();
}
export function blank_object() {
return Object.create(null);
}
export function run_all(fns) {
fns.forEach(run);
}
export function is_function(thing: any): thing is Function {
return typeof thing === 'function';
}
export function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
}
export function not_equal(a, b) {
return a != a ? b == b : a !== b;
}
export function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
export function subscribe(store, subscriber, invalidator?) { export function subscribe(store, subscriber, invalidator?) {
if (store == null) return noop; if (store == null) return noop;
@ -54,16 +13,6 @@ export function subscribe(store, subscriber, invalidator?) {
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
} }
export function get_store_value(store) {
let value;
subscribe(store, (_) => (value = _))();
return value;
}
export function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
export function create_slot(definition, ctx, $$scope, fn) { export function create_slot(definition, ctx, $$scope, fn) {
if (definition) { if (definition) {
const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
@ -72,45 +21,18 @@ export function create_slot(definition, ctx, $$scope, fn) {
} }
export function get_slot_context(definition, ctx, $$scope, fn) { export function get_slot_context(definition, ctx, $$scope, fn) {
return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx; return definition[1] && fn ? Object.assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
} }
export function get_slot_changes(definition, $$scope, dirty, fn) { export function get_slot_changes(definition, $$scope, dirty, fn) {
if (definition[2] && fn) { if (!definition[2] || !fn) return $$scope.dirty;
const lets = definition[2](fn(dirty)); const lets = definition[2](fn(dirty));
if ($$scope.dirty === void 0) return lets;
if ($$scope.dirty === undefined) { else if (typeof lets === 'object') {
return lets; const merged = new Array(Math.max($$scope.dirty.length, lets.length));
} for (let i = 0; i < merged.length; i += 1) merged[i] = $$scope.dirty[i] | lets[i];
if (typeof lets === 'object') {
const merged = [];
const len = Math.max($$scope.dirty.length, lets.length);
for (let i = 0; i < len; i += 1) {
merged[i] = $$scope.dirty[i] | lets[i];
}
return merged; return merged;
} } else return $$scope.dirty | lets;
return $$scope.dirty | lets;
}
return $$scope.dirty;
}
export function exclude_internal_props(props) {
const result = {};
for (const k in props) if (k[0] !== '$') result[k] = props[k];
return result;
}
export function compute_rest_props(props, keys) {
const rest = {};
keys = new Set(keys);
let k: string;
for (k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k];
return rest;
} }
export function once(fn) { export function once(fn) {
@ -121,18 +43,8 @@ export function once(fn) {
fn.call(this, ...args); fn.call(this, ...args);
}; };
} }
export const set_store_value = (store, ret, value) => (store.set(value || ret), ret);
export function null_to_empty(value) {
return value == null ? '' : value;
}
export function set_store_value(store, ret, value = ret) {
store.set(value);
return ret;
}
export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
export function action_destroyer(action_result) { export function action_destroyer(action_result) {
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; return action_result && 'function' === typeof action_result.destroy ? action_result.destroy : noop;
} }

@ -1,14 +1,14 @@
import { get_store_value, Writable, StartStopNotifier, Derived } from 'svelte/internal'; import { Writable, StartStopNotifier, Derived } from 'svelte/internal';
export { get_store_value as get }; export const get = (store) => (store.subscribe((v) => void (store = v))(), store);
export function readable<T>(value: T, start: StartStopNotifier<T>) { export const readable = <T>(value: T, start: StartStopNotifier<T>) => {
const store = new Writable(value, start); const store = new Writable(value, start);
return { subscribe: store.subscribe.bind(store) }; return { subscribe: store.subscribe.bind(store) };
} };
export function writable<T>(value: T, start: StartStopNotifier<T>) { export const writable = <T>(value: T, start: StartStopNotifier<T>) => {
const store = new Writable(value, start); const store = new Writable(value, start);
return { set: store.set.bind(store), update: store.update.bind(store), subscribe: store.subscribe.bind(store) }; return { set: store.set.bind(store), update: store.update.bind(store), subscribe: store.subscribe.bind(store) };
} };
export function derived(stores, deriver, initial_value?) { export const derived = (stores, deriver, initial_value?) => {
const store = new Derived(stores, deriver, initial_value); const store = new Derived(stores, deriver, initial_value);
return { subscribe: store.subscribe.bind(store) }; return { subscribe: store.subscribe.bind(store) };
} };

@ -2,21 +2,21 @@ import { cubicOut, cubicInOut } from 'svelte/easing';
import { run_duration } from 'svelte/internal'; import { run_duration } from 'svelte/internal';
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
interface BasicConfig { interface AnyConfig {
delay?: number; delay?: number;
duration?: number; duration?: number;
easing?: EasingFunction; easing?: EasingFunction;
strategy?: 'reverse' | 'mirror'; strategy?: 'reverse' | 'mirror';
} }
interface TimeableConfig extends Omit<BasicConfig, 'duration'> { interface TimeableConfig extends Omit<AnyConfig, 'duration'> {
duration?: number | ((len: number) => number); duration?: number | ((len: number) => number);
} }
export interface TransitionConfig extends BasicConfig { export interface TransitionConfig extends AnyConfig {
css?: (t: number, u?: number) => string; css?: (t: number, u?: number) => string;
tick?: (t: number, u?: number) => void; tick?: (t: number, u?: number) => void;
} }
interface BlurParams extends BasicConfig { interface BlurParams extends AnyConfig {
amount: number; amount: number;
opacity: number; opacity: number;
} }
@ -37,12 +37,12 @@ export function blur(
}; };
} }
export function fade(node: Element, { delay = 0, duration = 400, easing }: BasicConfig): TransitionConfig { export function fade(node: Element, { delay = 0, duration = 400, easing }: AnyConfig): TransitionConfig {
const o = +getComputedStyle(node).opacity; const o = +getComputedStyle(node).opacity;
return { delay, duration, easing, css: (t) => `opacity: ${t * o};` }; return { delay, duration, easing, css: (t) => `opacity: ${t * o};` };
} }
interface FlyParams extends BasicConfig { interface FlyParams extends AnyConfig {
x: number; x: number;
y: number; y: number;
opacity: number; opacity: number;
@ -64,7 +64,7 @@ export function fly(
}; };
} }
export function slide(node: Element, { delay = 0, duration = 400, easing = cubicOut }: BasicConfig): TransitionConfig { export function slide(node: Element, { delay = 0, duration = 400, easing = cubicOut }: AnyConfig): TransitionConfig {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const opacity = +style.opacity; const opacity = +style.opacity;
const height = parseFloat(style.height); const height = parseFloat(style.height);
@ -91,7 +91,7 @@ export function slide(node: Element, { delay = 0, duration = 400, easing = cubic
}; };
} }
interface ScaleParams extends BasicConfig { interface ScaleParams extends AnyConfig {
start: number; start: number;
opacity: number; opacity: number;
} }

@ -3,8 +3,8 @@
"include": [".", "../ambient.ts"], "include": [".", "../ambient.ts"],
"compilerOptions": { "compilerOptions": {
"lib": ["es2015", "dom", "dom.iterable"], "lib": ["ESNext", "dom", "dom.iterable"],
"target": "es2015", "target": "ESNext",
"types": [], "types": [],
"baseUrl": ".", "baseUrl": ".",

Loading…
Cancel
Save