Merge branch 'master' into less-invalidation

pull/2865/head
Rich Harris 6 years ago committed by GitHub
commit 52733f3040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

18
.gitignore vendored

@ -1,16 +1,17 @@
.idea
.DS_Store
.nyc_output
node_modules
*.map
/src/compile/internal-exports.ts
/compiler.js
/index.js
/internal.*
/store.*
/easing.js
/motion.*
/transition.js
/animate.js
/compiler.*js
/index.*js
/internal.*js
/store.*js
/easing.*js
/motion.*js
/transition.*js
/animate.*js
/scratch/
/coverage/
/coverage.lcov/
@ -20,6 +21,7 @@ node_modules
/test/sourcemaps/samples/*/output.css.map
/yarn-error.log
_actual*.*
/dist
/site/cypress/screenshots/
/site/__sapper__/

@ -1,5 +1,14 @@
# Svelte changelog
## 3.4.3
* Add type declaration files for everything ([#2842](https://github.com/sveltejs/svelte/pull/2842))
* Prevent `svelte/store` being bundled ([#2786](https://github.com/sveltejs/svelte/issues/2786))
* Warn on unknown props in dev mode ([#2840](https://github.com/sveltejs/svelte/pull/2840))
* Treat `requestAnimationFrame` as a no-op on the server ([#2856](https://github.com/sveltejs/svelte/pull/2856))
* Add `raw` property to AST's `Text` nodes ([#2714](https://github.com/sveltejs/svelte/issues/2714))
* Add `<details bind:open>` ([#2854](https://github.com/sveltejs/svelte/issues/2854))
## 3.4.2
* Use empty string for empty data attributes ([#2804](https://github.com/sveltejs/svelte/pull/2804))

1
animate.d.ts vendored

@ -0,0 +1 @@
export * from './dist/animate';

1
compiler.d.ts vendored

@ -0,0 +1 @@
export * from './dist/compiler';

1
easing.d.ts vendored

@ -0,0 +1 @@
export * from './dist/easing';

1
index.d.ts vendored

@ -0,0 +1 @@
export * from './dist/index';

@ -1,10 +0,0 @@
export {
onMount,
onDestroy,
beforeUpdate,
afterUpdate,
setContext,
getContext,
tick,
createEventDispatcher
} from './internal';

1
internal.d.ts vendored

@ -0,0 +1 @@
export * from './dist/internal';

1
motion.d.ts vendored

@ -0,0 +1 @@
export * from './dist/motion';

833
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,10 +1,11 @@
{
"name": "svelte",
"version": "3.4.2",
"version": "3.4.3",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
"files": [
"dist",
"compiler.js",
"register.js",
"index.*",
@ -25,14 +26,14 @@
"coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html",
"codecov": "codecov",
"precodecov": "npm run coverage",
"lint": "eslint src test/*.js",
"build": "rollup -c",
"prepare": "npm run build && npm run tsd",
"dev": "rollup -cw",
"pretest": "npm run build",
"posttest": "agadoo src/internal/index.js",
"prepublishOnly": "export PUBLISH=true && npm run lint && npm test",
"tsd": "tsc -d src/store.ts --outDir ."
"posttest": "agadoo internal.mjs",
"prepublishOnly": "export PUBLISH=true && npm test",
"tsd": "tsc -p . --emitDeclarationOnly",
"typecheck": "tsc -p . --noEmit"
},
"repository": {
"type": "git",
@ -60,9 +61,6 @@
"c8": "^3.4.0",
"codecov": "^3.0.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^5.3.0",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-import": "^2.11.0",
"estree-walker": "^0.6.0",
"is-reference": "^1.1.1",
"jsdom": "^12.2.0",
@ -84,7 +82,7 @@
"tiny-glob": "^0.2.1",
"ts-node": "^8.0.2",
"tslib": "^1.8.0",
"typescript": "^3.0.1"
"typescript": "^3.4.0"
},
"nyc": {
"include": [

@ -9,10 +9,19 @@ import pkg from './package.json';
const is_publish = !!process.env.PUBLISH;
const tsPlugin = is_publish
? typescript({
include: 'src/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
});
export default [
/* internal.[m]js */
{
input: `src/internal/index.js`,
input: `src/internal/index.ts`,
output: [
{
file: `internal.mjs`,
@ -26,7 +35,10 @@ export default [
}
],
external: id => id.startsWith('svelte/'),
plugins: [{
plugins: [
tsPlugin,
{
generateBundle(options, bundle) {
const mod = bundle['internal.mjs'];
if (mod) {
@ -38,7 +50,7 @@ export default [
/* compiler.js */
{
input: 'src/index.ts',
input: 'src/compiler.ts',
plugins: [
replace({
__VERSION__: pkg.version
@ -48,15 +60,7 @@ export default [
include: ['node_modules/**']
}),
json(),
is_publish
? typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
tsPlugin
],
output: {
file: 'compiler.js',
@ -71,7 +75,7 @@ export default [
/* motion.mjs */
{
input: `src/motion/index.js`,
input: `src/motion/index.ts`,
output: [
{
file: `motion.mjs`,
@ -84,46 +88,30 @@ export default [
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
plugins: [
tsPlugin
],
external: id => id.startsWith('svelte/')
},
/* store.mjs */
{
input: `src/store.ts`,
// everything else
...['index', 'easing', 'transition', 'animate', 'store'].map(name => ({
input: `src/${name}.ts`,
output: [
{
file: `store.mjs`,
file: `${name}.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `store.js`,
file: `${name}.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
plugins: [
is_publish
? typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
tsPlugin
],
external: id => id.startsWith('svelte/')
},
// everything else
...['index', 'easing', 'transition', 'animate'].map(name => ({
input: `${name}.mjs`,
output: {
file: `${name}.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
external: id => id !== `${name}.mjs`
}))
];

@ -30,10 +30,7 @@
$: selected = filteredPeople[i];
$: {
first = selected ? selected.first : '';
last = selected ? selected.last : '';
}
$: reset_inputs(selected);
function create() {
people = people.concat({ first, last });
@ -53,7 +50,8 @@
}
function reset_inputs(person) {
({ first, last } = person);
first = person ? person.first : '';
last = person ? person.last : '';
}
</script>

@ -2,7 +2,13 @@
title: In and out
---
Instead of the `transition` directive, an element can have an `in` or an `out` directive, or both together:
Instead of the `transition` directive, an element can have an `in` or an `out` directive, or both together. Import `fade` alongside `fly`...
```js
import { fade, fly } from 'svelte/transition';
```
...then replace the `transition` directive with separate `in` and `out` directives:
```html
<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>

@ -1,5 +1,5 @@
import { cubicOut } from './easing';
import { is_function } from './internal';
import { cubicOut } from 'svelte/easing';
import { is_function } from 'svelte/internal';
export function flip(node, animation, params) {
const style = getComputedStyle(node);

@ -1,4 +1,5 @@
import MagicString, { Bundle } from 'magic-string';
// @ts-ignore
import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character';
import Stats from '../Stats';
@ -21,6 +22,7 @@ import { remove_indentation, add_indentation } from '../utils/indentation';
import get_object from './utils/get_object';
import unwrap_parens from './utils/unwrap_parens';
import Slot from './nodes/Slot';
import { Node as ESTreeNode } from 'estree';
type ComponentOptions = {
namespace?: string;
@ -758,7 +760,7 @@ export default class Component {
});
}
if (is_reference(node, parent)) {
if (is_reference(node as ESTreeNode, parent as ESTreeNode)) {
const object = get_object(node);
const { name } = object;
@ -776,7 +778,7 @@ export default class Component {
});
}
invalidate(name, value) {
invalidate(name, value?) {
const variable = this.var_lookup.get(name);
if (variable && (variable.subscribable && variable.reassigned)) {
@ -1026,7 +1028,7 @@ export default class Component {
scope = map.get(node);
}
if (is_reference(node, parent)) {
if (is_reference(node as ESTreeNode, parent as ESTreeNode)) {
const { name } = flatten_reference(node);
const owner = scope.find_owner(name);
@ -1117,7 +1119,7 @@ export default class Component {
} else if (node.type === 'UpdateExpression') {
const identifier = get_object(node.argument);
assignees.add(identifier.name);
} else if (is_reference(node, parent)) {
} else if (is_reference(node as ESTreeNode, parent as ESTreeNode)) {
const identifier = get_object(node);
if (!assignee_nodes.has(identifier)) {
const { name } = identifier;

@ -1,4 +1,4 @@
import { assign } from '../internal';
import { assign } from '../internal/index';
import Stats from '../Stats';
import parse from '../parse/index';
import render_dom from './render-dom/index';
@ -55,7 +55,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
}
}
function get_name(filename) {
function get_name(filename: string) {
if (!filename) return null;
const parts = filename.split(/[\/\\]/);

@ -67,6 +67,7 @@ export default class Attribute extends Node {
this.should_cache = this.is_dynamic
? this.chunks.length === 1
// @ts-ignore todo: probably error
? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name)
: true
: false;
@ -91,8 +92,10 @@ export default class Attribute extends Node {
if (this.chunks.length === 0) return `""`;
if (this.chunks.length === 1) {
return this.chunks[0].type === 'Text'
? stringify(this.chunks[0].data)
? stringify((this.chunks[0] as Text).data)
// @ts-ignore todo: probably error
: this.chunks[0].render(block);
}
@ -102,6 +105,7 @@ export default class Attribute extends Node {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
// @ts-ignore todo: probably error
return chunk.get_precedence() <= 13 ? `(${chunk.render()})` : chunk.render();
}
})
@ -114,7 +118,8 @@ export default class Attribute extends Node {
return this.is_true
? true
: this.chunks[0]
? this.chunks[0].data
// method should be called only when `is_static = true`
? (this.chunks[0] as Text).data
: '';
}
}

@ -5,6 +5,7 @@ import CatchBlock from './CatchBlock';
import Expression from './shared/Expression';
export default class AwaitBlock extends Node {
type: 'AwaitBlock';
expression: Expression;
value: string;
error: string;

@ -3,13 +3,24 @@ import get_object from '../utils/get_object';
import Expression from './shared/Expression';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import {dimensions} from "../../utils/patterns";
// TODO this should live in a specific binding
const read_only_media_attributes = new Set([
'duration',
'buffered',
'seekable',
'played'
]);
export default class Binding extends Node {
type: 'Binding';
name: string;
expression: Expression;
is_contextual: boolean;
obj: string;
prop: string;
is_readonly: boolean;
constructor(component: Component, parent, scope: TemplateScope, info) {
super(component, parent, scope, info);
@ -63,5 +74,17 @@ export default class Binding extends Node {
this.obj = obj;
this.prop = prop;
const type = parent.get_static_attribute_value('type');
this.is_readonly = (
dimensions.test(this.name) ||
(parent.is_media_node && parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
(parent.name === 'input' && type === 'file') // TODO others?
);
}
is_readonly_media_attribute() {
return read_only_media_attributes.has(this.name);
}
}

@ -3,6 +3,7 @@ import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class CatchBlock extends AbstractBlock {
type: 'CatchBlock';
scope: TemplateScope;
constructor(component, parent, scope, info) {

@ -2,6 +2,7 @@ import Node from './shared/Node';
import Expression from './shared/Expression';
export default class DebugTag extends Node {
type: 'DebugTag';
expressions: Expression[];
constructor(component, parent, scope, info) {

@ -6,8 +6,15 @@ import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import { Node as INode } from '../../interfaces';
import { new_tail } from '../utils/tail';
import Element from './Element';
function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) {
type Context = {
key: INode,
name?: string,
tail: string
};
function unpack_destructuring(contexts: Array<Context>, node: INode, tail: string) {
if (!node) return;
if (node.type === 'Identifier' || node.type === 'RestIdentifier') {
@ -53,7 +60,7 @@ export default class EachBlock extends AbstractBlock {
context: string;
key: Expression;
scope: TemplateScope;
contexts: Array<{ name: string, tail: string }>;
contexts: Array<Context>;
has_animation: boolean;
has_binding = false;
@ -82,7 +89,7 @@ export default class EachBlock extends AbstractBlock {
if (this.index) {
// index can only change if this is a keyed each block
const dependencies = this.key ? this.expression.dependencies : [];
const dependencies = this.key ? this.expression.dependencies : new Set([]);
this.scope.add(this.index, dependencies, this);
}
@ -92,8 +99,8 @@ export default class EachBlock extends AbstractBlock {
if (this.has_animation) {
if (this.children.length !== 1) {
const child = this.children.find(child => !!child.animation);
component.error(child.animation, {
const child = this.children.find(child => !!(child as Element).animation);
component.error((child as Element).animation, {
code: `invalid-animation`,
message: `An element that use the animate directive must be the sole child of a keyed each block`
});

@ -15,6 +15,7 @@ import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
import Let from './Let';
import TemplateScope from './shared/TemplateScope';
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)$/;
@ -101,7 +102,7 @@ export default class Element extends Node {
intro?: Transition = null;
outro?: Transition = null;
animation?: Animation = null;
children: Node[];
children: INode[];
namespace: string;
constructor(component, parent, scope, info: any) {
@ -136,7 +137,7 @@ export default class Element extends Node {
// Special case — treat these the same way:
// <option>{foo}</option>
// <option value={foo}>{foo}</option>
const value_attribute = info.attributes.find((attribute: Node) => attribute.name === 'value');
const value_attribute = info.attributes.find(attribute => attribute.name === 'value');
if (!value_attribute) {
info.attributes.push({
@ -228,7 +229,7 @@ export default class Element extends Node {
let is_figure_parent = false;
while (parent) {
if (parent.name === 'figure') {
if ((parent as Element).name === 'figure') {
is_figure_parent = true;
break;
}
@ -253,7 +254,7 @@ export default class Element extends Node {
return true;
});
const index = children.findIndex(child => child.name === 'figcaption');
const index = children.findIndex(child => (child as Element).name === 'figcaption');
if (index !== -1 && (index !== 0 && index !== children.length - 1)) {
this.component.warn(children[index], {
@ -320,7 +321,9 @@ export default class Element extends Node {
}
const value = attribute.get_static_value();
// @ts-ignore
if (value && !aria_role_set.has(value)) {
// @ts-ignore
const match = fuzzymatch(value, aria_roles);
let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`;
@ -359,6 +362,7 @@ export default class Element extends Node {
// tabindex-no-positive
if (name === 'tabindex') {
const value = attribute.get_static_value();
// @ts-ignore todo is tabindex=true correct case?
if (!isNaN(value) && +value > 0) {
component.warn(attribute, {
code: `a11y-positive-tabindex`,
@ -544,7 +548,7 @@ export default class Element extends Node {
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') {
component.error(binding, {
code: `invalid-binding`,
@ -560,6 +564,14 @@ export default class Element extends Node {
message: `'files' binding can only be used with <input type="file">`
});
}
} else if (name === 'open') {
if (this.name !== 'details') {
component.error(binding, {
code: `invalid-binding`,
message: `'${name}' binding can only be used with <details>`
});
}
} else if (
name === 'currentTime' ||
name === 'duration' ||

@ -1,10 +1,11 @@
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component';
export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock';
constructor(component, parent, scope, info) {
constructor(component: Component, parent, scope, info) {
super(component, parent, scope, info);
this.children = map_children(component, this, scope, info.children);

@ -5,6 +5,7 @@ import deindent from '../utils/deindent';
import Block from '../render-dom/Block';
export default class EventHandler extends Node {
type: 'EventHandler';
name: string;
modifiers: Set<string>;
expression: Expression;

@ -3,10 +3,12 @@ import Component from '../Component';
import map_children from './shared/map_children';
import Block from '../render-dom/Block';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
export default class Fragment extends Node {
type: 'Fragment';
block: Block;
children: Node[];
children: INode[];
scope: TemplateScope;
constructor(component: Component, info: any) {

@ -1,5 +1,4 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children';
export default class Head extends Node {

@ -7,6 +7,7 @@ import Expression from './shared/Expression';
import Component from '../Component';
import Let from './Let';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
export default class InlineComponent extends Node {
type: 'InlineComponent';
@ -16,7 +17,7 @@ export default class InlineComponent extends Node {
bindings: Binding[] = [];
handlers: EventHandler[] = [];
lets: Let[] = [];
children: Node[];
children: INode[];
scope: TemplateScope;
constructor(component: Component, parent, scope, info) {

@ -1,3 +1,5 @@
import Tag from './shared/Tag';
export default class MustacheTag extends Tag {}
export default class MustacheTag extends Tag {
type: 'MustacheTag';
}

@ -2,7 +2,7 @@ import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class PendingBlock extends AbstractBlock {
type: 'PendingBlock';
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = map_children(component, parent, scope, info.children);

@ -1,3 +1,5 @@
import Tag from './shared/Tag';
export default class RawMustacheTag extends Tag {}
export default class RawMustacheTag extends Tag {
type: 'RawMustacheTag'
}

@ -1,17 +1,17 @@
import Node from './shared/Node';
import Element from './Element';
import Attribute from './Attribute';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
export default class Slot extends Element {
type: 'Element';
name: string;
children: Node[];
children: INode[];
slot_name: string;
values: Map<string, Attribute> = new Map();
constructor(component: Component, parent: Node, scope: TemplateScope, info: any) {
constructor(component: Component, parent: INode, scope: TemplateScope, info: any) {
super(component, parent, scope, info);
info.attributes.forEach(attr => {

@ -1,13 +1,14 @@
import Node from './shared/Node';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
export default class Text extends Node {
type: 'Text';
data: string;
use_space = false;
constructor(component: Component, parent: Node, scope: TemplateScope, info: any) {
constructor(component: Component, parent: INode, scope: TemplateScope, info: any) {
super(component, parent, scope, info);
this.data = info.data;

@ -3,6 +3,7 @@ import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class ThenBlock extends AbstractBlock {
type: 'ThenBlock';
scope: TemplateScope;
constructor(component, parent, scope, info) {

@ -1,12 +1,13 @@
import Node from './shared/Node';
import map_children from './shared/map_children';
import map_children, { Children } from './shared/map_children';
import Component from '../Component';
export default class Title extends Node {
type: 'Title';
children: any[]; // TODO
children: Children;
should_cache: boolean;
constructor(component, parent, scope, info) {
constructor(component: Component, parent, scope, info) {
super(component, parent, scope, info);
this.children = map_children(component, parent, scope, info.children);

@ -0,0 +1,64 @@
import Tag from './shared/Tag';
import Action from './Action';
import Animation from './Animation';
import Attribute from './Attribute';
import AwaitBlock from './AwaitBlock';
import Binding from './Binding';
import Body from './Body';
import CatchBlock from './CatchBlock';
import Class from './Class';
import Comment from './Comment';
import DebugTag from './DebugTag';
import EachBlock from './EachBlock';
import Element from './Element';
import ElseBlock from './ElseBlock';
import EventHandler from './EventHandler';
import Fragment from './Fragment';
import Head from './Head';
import IfBlock from './IfBlock';
import InlineComponent from './InlineComponent';
import Let from './Let';
import MustacheTag from './MustacheTag';
import Options from './Options';
import PendingBlock from './PendingBlock';
import RawMustacheTag from './RawMustacheTag';
import Slot from './Slot';
import Text from './Text';
import ThenBlock from './ThenBlock';
import Title from './Title';
import Transition from './Transition';
import Window from './Window';
// note: to write less types each of types in union below should have type defined as literal
// https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
export type INode = Action
| Animation
| Attribute
| AwaitBlock
| Binding
| Body
| CatchBlock
| Class
| Comment
| DebugTag
| EachBlock
| Element
| ElseBlock
| EventHandler
| Fragment
| Head
| IfBlock
| InlineComponent
| Let
| MustacheTag
| Options
| PendingBlock
| RawMustacheTag
| Slot
| Tag
| Text
| ThenBlock
| Title
| Transition
| Window;

@ -1,10 +1,11 @@
import Block from '../../render-dom/Block';
import Component from './../../Component';
import Node from './Node';
import { INode } from '../interfaces';
export default class AbstractBlock extends Node {
block: Block;
children: Node[];
children: INode[];
constructor(component: Component, parent, scope, info: any) {
super(component, parent, scope, info);

@ -12,6 +12,7 @@ import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object';
import { nodes_match } from '../../../utils/nodes_match';
import Block from '../../render-dom/Block';
import { INode } from '../interfaces';
const binary_operators: Record<string, number> = {
'**': 15,
@ -61,10 +62,12 @@ const precedence: Record<string, (node?: Node) => number> = {
SequenceExpression: () => 0
};
type Owner = Wrapper | INode;
export default class Expression {
type = 'Expression';
type: 'Expression' = 'Expression';
component: Component;
owner: Wrapper;
owner: Owner;
node: any;
snippet: string;
references: Set<string>;
@ -81,7 +84,8 @@ export default class Expression {
rendered: string;
constructor(component: Component, owner: Wrapper, template_scope: TemplateScope, info) {
// todo: owner type
constructor(component: Component, owner: Owner, template_scope: TemplateScope, info) {
// TODO revert to direct property access in prod?
Object.defineProperties(this, {
component: {
@ -92,6 +96,7 @@ export default class Expression {
this.node = info;
this.template_scope = template_scope;
this.owner = owner;
// @ts-ignore
this.is_synthetic = owner.is_synthetic;
const { dependencies, contextual_dependencies } = this;
@ -218,7 +223,7 @@ export default class Expression {
}
// TODO move this into a render-dom wrapper?
render(block: Block) {
render(block?: Block) {
if (this.rendered) return this.rendered;
const {

@ -1,15 +1,17 @@
import Attribute from './../Attribute';
import Component from './../../Component';
import { INode } from '../interfaces';
import Text from '../Text';
export default class Node {
readonly start: number;
readonly end: number;
readonly component: Component;
readonly parent: Node;
readonly parent: INode;
readonly type: string;
prev?: Node;
next?: Node;
prev?: INode;
next?: INode;
can_use_innerhtml: boolean;
var: string;
@ -45,7 +47,7 @@ export default class Node {
}
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
const attribute = this.attributes && this.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
@ -55,7 +57,7 @@ export default class Node {
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
return attribute.chunks[0].data;
return (attribute.chunks[0] as Text).data;
}
return null;

@ -2,6 +2,7 @@ import Node from './Node';
import Expression from './Expression';
export default class Tag extends Node {
type: 'MustacheTag' | 'RawMustacheTag';
expression: Expression;
should_cache: boolean;

@ -2,6 +2,7 @@ import EachBlock from '../EachBlock';
import ThenBlock from '../ThenBlock';
import CatchBlock from '../CatchBlock';
import InlineComponent from '../InlineComponent';
import Element from '../Element';
type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element;

@ -14,9 +14,11 @@ import Slot from '../Slot';
import Text from '../Text';
import Title from '../Title';
import Window from '../Window';
import Node from './Node';
import { Node } from '../../../interfaces';
function get_constructor(type): typeof Node {
export type Children = ReturnType<typeof map_children>;
function get_constructor(type) {
switch (type) {
case 'AwaitBlock': return AwaitBlock;
case 'Body': return Body;
@ -38,7 +40,7 @@ function get_constructor(type): typeof Node {
}
}
export default function map_children(component, parent, scope, children: any[]) {
export default function map_children(component, parent, scope, children: Node[]) {
let last = null;
return children.map(child => {
const constructor = get_constructor(child.type);

@ -9,7 +9,7 @@ export interface BlockOptions {
renderer?: Renderer;
comment?: string;
key?: string;
bindings?: Map<string, () => { object: string, property: string, snippet: string }>;
bindings?: Map<string, { object: string, property: string, snippet: string }>;
dependencies?: Set<string>;
}

@ -3,7 +3,6 @@ import { CompileOptions } from '../../interfaces';
import Component from '../Component';
import FragmentWrapper from './wrappers/Fragment';
import CodeBuilder from '../utils/CodeBuilder';
import SlotWrapper from './wrappers/Slot';
export default class Renderer {
component: Component; // TODO Maybe Renderer shouldn't know about Component?
@ -18,6 +17,7 @@ export default class Renderer {
fragment: FragmentWrapper;
file_var: string;
locate: (c: number) => { line: number; column: number; };
constructor(component: Component, options: CompileOptions) {
this.component = component;

@ -134,7 +134,6 @@ export default function dom(
});
if (component.compile_options.dev) {
// TODO check no uunexpected props were passed, as well as
// checking that expected ones were passed
const expected = props.filter(prop => !prop.initialised);
@ -397,6 +396,16 @@ export default function dom(
return $name;
});
let unknown_props_check;
if (component.compile_options.dev && writable_props.length) {
unknown_props_check = deindent`
const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`).join(', ')}];
Object.keys($$props).forEach(key => {
if (!writable_props.includes(key)) console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`);
});
`;
}
builder.add_block(deindent`
function ${definition}(${args.join(', ')}) {
${reactive_store_declarations.length > 0 && `let ${reactive_store_declarations.join(', ')};`}
@ -407,6 +416,8 @@ export default function dom(
${component.javascript}
${unknown_props_check}
${component.slots.size && `let { $$slots = {}, $$scope } = $$props;`}
${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`}

@ -8,7 +8,7 @@ import deindent from '../../utils/deindent';
import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
class ElseBlockWrapper extends Wrapper {
export class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
block: Block;
fragment: FragmentWrapper;
@ -83,6 +83,7 @@ export default class EachBlockWrapper extends Wrapper {
this.block = block.child({
comment: create_debugging_comment(this.node, this.renderer.component),
name: renderer.component.get_unique_name('create_each_block'),
// @ts-ignore todo: probably error
key: node.key as string,
bindings: new Map(block.bindings)
@ -310,7 +311,9 @@ export default class EachBlockWrapper extends Wrapper {
}
block.builders.init.add_block(deindent`
const ${get_key} = ctx => ${this.node.key.render()};
const ${get_key} = ctx => ${
// @ts-ignore todo: probably error
this.node.key.render()};
for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) {
let child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i);

@ -4,6 +4,7 @@ import fix_attribute_casing from './fix_attribute_casing';
import ElementWrapper from './index';
import { stringify } from '../../../utils/stringify';
import deindent from '../../../utils/deindent';
import Expression from '../../../nodes/shared/Expression';
export default class AttributeWrapper {
node: Attribute;
@ -21,7 +22,9 @@ export default class AttributeWrapper {
// special case — <option value={foo}> — see below
if (this.parent.node.name === 'option' && node.name === 'value') {
let select: ElementWrapper = this.parent;
while (select && (select.node.type !== 'Element' || select.node.name !== 'select')) select = select.parent;
while (select && (select.node.type !== 'Element' || select.node.name !== 'select'))
// @ts-ignore todo: doublecheck this, but looks to be correct
select = select.parent;
if (select && select.select_binding_dependencies) {
select.select_binding_dependencies.forEach(prop => {
@ -47,7 +50,7 @@ export default class AttributeWrapper {
(element.node.name === 'option' || // TODO check it's actually bound
(element.node.name === 'input' &&
element.node.bindings.find(
(binding: Binding) =>
(binding) =>
/checked|group/.test(binding.name)
)));
@ -78,13 +81,13 @@ export default class AttributeWrapper {
// DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) {
// single {tag} — may be a non-string
value = this.node.chunks[0].render(block);
value = (this.node.chunks[0] as Expression).render(block);
} else {
// '{foo} {bar}' — treat as string concatenation
value =
(this.node.chunks[0].type === 'Text' ? '' : `"" + `) +
this.node.chunks
.map((chunk: Node) => {
.map((chunk) => {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {

@ -9,14 +9,6 @@ import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { Node as INode } from '../../../../interfaces';
// TODO this should live in a specific binding
const read_only_media_attributes = new Set([
'duration',
'buffered',
'seekable',
'played'
]);
function get_tail(node: INode) {
const end = node.end;
while (node.type === 'MemberExpression') node = node.object;
@ -74,13 +66,7 @@ export default class BindingWrapper {
this.snippet = this.node.expression.render(block);
const type = parent.node.get_static_attribute_value('type');
this.is_readonly = (
dimensions.test(this.node.name) ||
(parent.node.is_media_node() && read_only_media_attributes.has(this.node.name)) ||
(parent.node.name === 'input' && type === 'file') // TODO others?
);
this.is_readonly = this.node.is_readonly;
this.needs_lock = this.node.name === 'currentTime'; // TODO others?
}
@ -101,7 +87,7 @@ export default class BindingWrapper {
}
is_readonly_media_attribute() {
return read_only_media_attributes.has(this.node.name);
return this.node.is_readonly_media_attribute()
}
render(block: Block, lock: string) {

@ -1,14 +1,15 @@
import Attribute from '../../../nodes/Attribute';
import Block from '../../Block';
import AttributeWrapper from './Attribute';
import Node from '../../../nodes/shared/Node';
import ElementWrapper from '.';
import { stringify } from '../../../utils/stringify';
import add_to_set from '../../../utils/add_to_set';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
export interface StyleProp {
key: string;
value: Node[];
value: (Text|Expression)[];
}
export default class StyleAttributeWrapper extends AttributeWrapper {
@ -28,7 +29,7 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
value =
((prop.value.length === 1 || prop.value[0].type === 'Text') ? '' : `"" + `) +
prop.value
.map((chunk: Node) => {
.map((chunk) => {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
@ -54,7 +55,7 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
);
}
} else {
value = stringify(prop.value[0].data);
value = stringify((prop.value[0] as Text).data);
}
block.builders.hydrate.add_line(
@ -64,8 +65,8 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
}
}
function optimize_style(value: Node[]) {
const props: { key: string, value: Node[] }[] = [];
function optimize_style(value: (Text|Expression)[]) {
const props: StyleProp[] = [];
let chunks = value.slice();
while (chunks.length) {
@ -87,7 +88,7 @@ function optimize_style(value: Node[]) {
end: chunk.end,
type: 'Text',
data: remaining_data
};
} as Text;
} else {
chunks.shift();
}
@ -101,8 +102,8 @@ function optimize_style(value: Node[]) {
return props;
}
function get_style_value(chunks: Node[]) {
const value: Node[] = [];
function get_style_value(chunks: (Text | Expression)[]) {
const value: (Text|Expression)[] = [];
let in_url = false;
let quote_mark = null;
@ -141,7 +142,7 @@ function get_style_value(chunks: Node[]) {
start: chunk.start,
end: chunk.start + c,
data: chunk.data.slice(0, c)
});
} as Text);
}
while (/[;\s]/.test(chunk.data[c])) c += 1;
@ -153,7 +154,7 @@ function get_style_value(chunks: Node[]) {
end: chunk.end,
type: 'Text',
data: remaining_data
});
} as Text);
break;
}
@ -170,6 +171,6 @@ function get_style_value(chunks: Node[]) {
};
}
function is_dynamic(value: Node[]) {
function is_dynamic(value: (Text|Expression)[]) {
return value.length > 1 || value[0].type !== 'Text';
}

@ -20,20 +20,19 @@ import add_event_handlers from '../shared/add_event_handlers';
import add_actions from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_context_merger } from '../shared/get_context_merger';
import Slot from '../../../nodes/Slot';
const events = [
{
event_names: ['input'],
filter: (node: Element, name: string) =>
node.name === 'textarea' ||
node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type'))
node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type') as string)
},
{
event_names: ['change'],
filter: (node: Element, name: string) =>
node.name === 'select' ||
node.name === 'input' && /radio|checkbox/.test(node.get_static_attribute_value('type'))
node.name === 'input' && /radio|checkbox/.test(node.get_static_attribute_value('type') as string)
},
{
event_names: ['change', 'input'],
@ -91,6 +90,12 @@ const events = [
name === 'playbackRate'
},
// details event
{
event_names: ['toggle'],
filter: (node: Element, name: string) =>
node.name === 'details'
},
];
export default class ElementWrapper extends Wrapper {
@ -135,7 +140,7 @@ export default class ElementWrapper extends Wrapper {
}
if (owner && owner.node.type === 'InlineComponent') {
const name = attribute.get_static_value();
const name = attribute.get_static_value() as string;
if (!(owner as InlineComponentWrapper).slots.has(name)) {
const child_block = block.child({
@ -275,6 +280,7 @@ export default class ElementWrapper extends Wrapper {
if (!this.node.namespace && this.can_use_innerhtml && this.fragment.nodes.length > 0) {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.builders.create.add_line(
// @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead?
`${node}.textContent = ${stringify(this.fragment.nodes[0].data)};`
);
} else {
@ -324,7 +330,7 @@ export default class ElementWrapper extends Wrapper {
function to_html(wrapper: ElementWrapper | TextWrapper) {
if (wrapper.node.type === 'Text') {
const { parent } = wrapper.node;
const parent = wrapper.node.parent as Element;
const raw = parent && (
parent.name === 'script' ||
@ -349,7 +355,7 @@ export default class ElementWrapper extends Wrapper {
if (is_void(wrapper.node.name)) return open + '>';
return `${open}>${wrapper.fragment.nodes.map(to_html).join('')}</${wrapper.node.name}>`;
return `${open}>${(wrapper as ElementWrapper).fragment.nodes.map(to_html).join('')}</${wrapper.node.name}>`;
}
if (renderer.options.dev) {
@ -376,8 +382,8 @@ export default class ElementWrapper extends Wrapper {
get_claim_statement(nodes: string) {
const attributes = this.node.attributes
.filter((attr: Node) => attr.type === 'Attribute')
.map((attr: Node) => `${quote_name_if_necessary(attr.name)}: true`)
.filter((attr) => attr.type === 'Attribute')
.map((attr) => `${quote_name_if_necessary(attr.name)}: true`)
.join(', ');
const name = this.node.namespace
@ -455,7 +461,7 @@ export default class ElementWrapper extends Wrapper {
function ${handler}() {
${animation_frame && deindent`
cancelAnimationFrame(${animation_frame});
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`}
if (!${this.var}.paused) ${animation_frame} = @raf(${handler});`}
${needs_lock && `${lock} = true;`}
ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''});
}
@ -553,12 +559,13 @@ export default class ElementWrapper extends Wrapper {
}
add_attributes(block: Block) {
// @ts-ignore todo:
if (this.node.attributes.find(attr => attr.type === 'Spread')) {
this.add_spread_attributes(block);
return;
}
this.attributes.forEach((attribute: Attribute) => {
this.attributes.forEach((attribute) => {
if (attribute.node.name === 'class' && attribute.node.is_dynamic) {
this.class_dependencies.push(...attribute.node.dependencies);
}
@ -814,27 +821,28 @@ export default class ElementWrapper extends Wrapper {
});
}
add_css_class(class_name = this.component.stylesheet.id) {
const class_attribute = this.attributes.find(a => a.name === 'class');
if (class_attribute && !class_attribute.is_true) {
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
(class_attribute.chunks[0] as Text).data += ` ${class_name}`;
} else {
(class_attribute.chunks as Node[]).push(
new Text(this.component, this, this.scope, {
type: 'Text',
data: ` ${class_name}`
})
);
}
} else {
this.attributes.push(
new Attribute(this.component, this, this.scope, {
type: 'Attribute',
name: 'class',
value: [{ type: 'Text', data: class_name }]
})
);
}
}
// todo: looks to be dead code copypasted from Element.add_css_class in src/compile/nodes/Element.ts
// add_css_class(class_name = this.component.stylesheet.id) {
// const class_attribute = this.attributes.find(a => a.name === 'class');
// if (class_attribute && !class_attribute.is_true) {
// if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
// (class_attribute.chunks[0] as Text).data += ` ${class_name}`;
// } else {
// (class_attribute.chunks as Node[]).push(
// new Text(this.component, this, this.scope, {
// type: 'Text',
// data: ` ${class_name}`
// })
// );
// }
// } else {
// this.attributes.push(
// new Attribute(this.component, this, this.scope, {
// type: 'Attribute',
// name: 'class',
// value: [{ type: 'Text', data: class_name }]
// })
// );
// }
// }
}

@ -13,7 +13,7 @@ import Slot from './Slot';
import Text from './Text';
import Title from './Title';
import Window from './Window';
import Node from '../../nodes/shared/Node';
import { INode } from '../../nodes/interfaces';
import TextWrapper from './Text';
import Renderer from '../Renderer';
import Block from '../Block';
@ -49,7 +49,7 @@ export default class FragmentWrapper {
constructor(
renderer: Renderer,
block: Block,
nodes: Node[],
nodes: INode[],
parent: Wrapper,
strip_whitespace: boolean,
next_sibling: Wrapper
@ -85,6 +85,7 @@ export default class FragmentWrapper {
// *unless* there is no whitespace between this node and its next sibling
if (this.nodes.length === 0) {
const should_trim = (
// @ts-ignore todo: probably error, should it be next_sibling.node.data?
next_sibling ? (next_sibling.node.type === 'Text' && /^\s/.test(next_sibling.data)) : !child.has_ancestor('EachBlock')
);
@ -96,6 +97,7 @@ export default class FragmentWrapper {
// glue text nodes (which could e.g. be separated by comments) together
if (last_child && last_child.node.type === 'Text') {
// @ts-ignore todo: probably error, should it be last_child.node.data?
last_child.data = data + last_child.data;
continue;
}

@ -96,7 +96,7 @@ export default class IfBlockWrapper extends Wrapper {
if (branch.block.has_outros) has_outros = true;
if (is_else_if(node.else)) {
create_branches(node.else.children[0]);
create_branches(node.else.children[0] as IfBlock);
} else if (node.else) {
const branch = new IfBlockBranch(
renderer,

@ -142,7 +142,7 @@ export default class InlineComponentWrapper extends Wrapper {
if (this.fragment) {
const default_slot = this.slots.get('default');
this.fragment.nodes.forEach((child: Wrapper) => {
this.fragment.nodes.forEach((child) => {
child.render(default_slot.block, null, 'nodes');
});
}

@ -1,13 +1,14 @@
import Renderer from '../Renderer';
import Block from '../Block';
import Node from '../../nodes/shared/Node';
import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
export default class MustacheTagWrapper extends Tag {
var = 't';
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: Node) {
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: MustacheTag | RawMustacheTag) {
super(renderer, block, parent, node);
this.cannot_use_innerhtml();
}

@ -1,9 +1,10 @@
import Renderer from '../Renderer';
import Block from '../Block';
import Node from '../../nodes/shared/Node';
import Tag from './shared/Tag';
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import deindent from '../../utils/deindent';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
export default class RawMustacheTagWrapper extends Tag {
var = 'raw';
@ -12,7 +13,7 @@ export default class RawMustacheTagWrapper extends Tag {
renderer: Renderer,
block: Block,
parent: Wrapper,
node: Node
node: MustacheTag | RawMustacheTag
) {
super(renderer, block, parent, node);
this.cannot_use_innerhtml();

@ -9,7 +9,6 @@ import add_to_set from '../../utils/add_to_set';
import get_slot_data from '../../utils/get_slot_data';
import { stringify_props } from '../../utils/stringify_props';
import Expression from '../../nodes/shared/Expression';
import Attribute from '../../nodes/Attribute';
export default class SlotWrapper extends Wrapper {
node: Slot;

@ -4,6 +4,7 @@ import Block from '../Block';
import Title from '../../nodes/Title';
import { stringify } from '../../utils/stringify';
import add_to_set from '../../utils/add_to_set';
import Text from '../../nodes/Text';
export default class TitleWrapper extends Wrapper {
node: Title;
@ -31,6 +32,7 @@ export default class TitleWrapper extends Wrapper {
// DRY it out if that's possible without introducing crazy indirection
if (this.node.children.length === 1) {
// single {tag} — may be a non-string
// @ts-ignore todo: check this
const { expression } = this.node.children[0];
value = expression.render(block);
add_to_set(all_dependencies, expression.dependencies);
@ -39,16 +41,18 @@ export default class TitleWrapper extends Wrapper {
value =
(this.node.children[0].type === 'Text' ? '' : `"" + `) +
this.node.children
.map((chunk: Node) => {
.map((chunk) => {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
// @ts-ignore todo: check this
const snippet = chunk.expression.render(block);
// @ts-ignore todo: check this
chunk.expression.dependencies.forEach(d => {
all_dependencies.add(d);
});
// @ts-ignore todo: check this
return chunk.expression.get_precedence() <= 13 ? `(${snippet})` : snippet;
}
})
@ -88,7 +92,7 @@ export default class TitleWrapper extends Wrapper {
);
}
} else {
const value = stringify(this.node.children[0].data);
const value = stringify((this.node.children[0] as Text).data);
block.builders.hydrate.add_line(`document.title = ${value};`);
}
}

@ -6,6 +6,7 @@ import deindent from '../../utils/deindent';
import add_event_handlers from './shared/add_event_handlers';
import Window from '../../nodes/Window';
import add_actions from './shared/add_actions';
import { INode } from '../../nodes/interfaces';
const associated_events = {
innerWidth: 'resize',
@ -33,7 +34,7 @@ const readonly = new Set([
export default class WindowWrapper extends Wrapper {
node: Window;
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: Node) {
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: INode) {
super(renderer, block, parent, node);
}

@ -1,11 +1,11 @@
import Renderer from '../../Renderer';
import Node from '../../../nodes/shared/Node';
import Block from '../../Block';
import { INode } from '../../../nodes/interfaces';
export default class Wrapper {
renderer: Renderer;
parent: Wrapper;
node: Node;
node: INode;
prev: Wrapper | null;
next: Wrapper | null;
@ -17,7 +17,7 @@ export default class Wrapper {
renderer: Renderer,
block: Block,
parent: Wrapper,
node: Node
node: INode
) {
this.node = node;
@ -75,4 +75,8 @@ export default class Wrapper {
this.node.type === 'MustacheTag'
);
}
render(block: Block, parent_node: string, parent_nodes: string){
throw Error('Wrapper class is not renderable');
}
}

@ -1,8 +1,8 @@
import Component from '../../../Component';
import { Node } from '../../../../interfaces';
import { INode } from '../../../nodes/interfaces';
export default function create_debugging_comment(
node: Node,
node: INode,
component: Component
) {
const { locate, source } = component;
@ -19,6 +19,7 @@ export default function create_debugging_comment(
d = node.children.length ? node.children[0].start : node.start;
while (source[d - 1] !== '>') d -= 1;
} else {
// @ts-ignore
d = node.expression ? node.expression.node.end : c;
while (source[d] !== '}') d += 1;
while (source[d] === '}') d += 1;

@ -12,6 +12,7 @@ import Tag from './handlers/Tag';
import Text from './handlers/Text';
import Title from './handlers/Title';
import { AppendTarget, CompileOptions } from '../../interfaces';
import { INode } from '../nodes/interfaces';
type Handler = (node: any, renderer: Renderer, options: CompileOptions) => void;
@ -36,6 +37,10 @@ const handlers: Record<string, Handler> = {
Window: noop
};
export interface RenderOptions extends CompileOptions{
locate: (c: number) => { line: number; column: number; };
};
export default class Renderer {
has_bindings = false;
code = '';
@ -51,7 +56,7 @@ export default class Renderer {
}
}
render(nodes, options) {
render(nodes: INode[], options: RenderOptions) {
nodes.forEach(node => {
const handler = handlers[node.type];

@ -1,8 +1,8 @@
import Renderer from '../Renderer';
import { CompileOptions } from '../../../interfaces';
import Renderer, { RenderOptions } from '../Renderer';
import { snip } from '../../utils/snip';
import AwaitBlock from '../../nodes/AwaitBlock';
export default function(node, renderer: Renderer, options: CompileOptions) {
export default function(node: AwaitBlock, renderer: Renderer, options: RenderOptions) {
renderer.append('${(function(__value) { if(@is_promise(__value)) return `');
renderer.render(node.pending.children, options);

@ -1,7 +1,7 @@
import Renderer from '../Renderer';
import { CompileOptions } from '../../../interfaces';
import Renderer, { RenderOptions } from '../Renderer';
import Comment from '../../nodes/Comment';
export default function(node, renderer: Renderer, options: CompileOptions) {
export default function(node: Comment, renderer: Renderer, options: RenderOptions) {
if (options.preserveComments) {
renderer.append(`<!--${node.data}-->`);
}

@ -1,9 +1,10 @@
import { stringify } from '../../utils/stringify';
export default function(node, renderer, options) {
import DebugTag from '../../nodes/DebugTag';
import Renderer, { RenderOptions } from '../Renderer';
export default function(node: DebugTag, renderer: Renderer, options: RenderOptions) {
if (!options.dev) return;
const filename = options.file || null;
const filename = options.filename || null;
const { line, column } = options.locate(node.start + 1);
const obj = node.expressions.length === 0

@ -1,6 +1,8 @@
import { snip } from '../../utils/snip';
import Renderer, { RenderOptions } from '../Renderer';
import EachBlock from '../../nodes/EachBlock';
export default function(node, renderer, options) {
export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) {
const snippet = snip(node.expression);
const { start, end } = node.context_node;

@ -5,6 +5,9 @@ import Node from '../../nodes/shared/Node';
import { snip } from '../../utils/snip';
import { stringify_attribute } from '../../utils/stringify_attribute';
import { get_slot_scope } from './shared/get_slot_scope';
import Renderer, { RenderOptions } from '../Renderer';
import Element from '../../nodes/Element';
import Text from '../../nodes/Text';
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
const boolean_attributes = new Set([
@ -47,15 +50,17 @@ const boolean_attributes = new Set([
'translate'
]);
export default function(node, renderer, options) {
export default function(node: Element, renderer: Renderer, options: RenderOptions & {
slot_scopes: Map<any, any>;
}) {
let opening_tag = `<${node.name}`;
let textarea_contents; // awkward special case
const slot = node.get_static_attribute_value('slot');
const component = node.find_nearest(/InlineComponent/);
if (slot && component) {
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
const slot_name = slot.chunks[0].data;
const slot = node.attributes.find((attribute) => attribute.name === 'slot');
const slot_name = (slot.chunks[0] as Text).data;
const target = renderer.targets[renderer.targets.length - 1];
target.slot_stack.push(slot_name);
target.slots[slot_name] = '';
@ -135,6 +140,10 @@ export default function(node, renderer, options) {
node.bindings.forEach(binding => {
const { name, expression } = binding;
if (binding.is_readonly) {
return;
}
if (name === 'group') {
// TODO server-render group bindings
} else {

@ -1,4 +1,7 @@
export default function(node, renderer, options) {
import Renderer, { RenderOptions } from '../Renderer';
import Head from '../../nodes/Head';
export default function(node: Head, renderer: Renderer, options: RenderOptions) {
renderer.append('${($$result.head += `');
renderer.render(node.children, options);

@ -1,5 +1,7 @@
import { snip } from '../../utils/snip';
import Renderer, { RenderOptions } from '../Renderer';
import RawMustacheTag from '../../nodes/RawMustacheTag';
export default function(node, renderer, options) {
export default function(node: RawMustacheTag, renderer: Renderer, options: RenderOptions) {
renderer.append('${' + snip(node.expression) + '}');
}

@ -1,6 +1,7 @@
import { snip } from '../../utils/snip';
export default function(node, renderer, options) {
import IfBlock from '../../nodes/IfBlock';
import Renderer, { RenderOptions } from '../Renderer';
export default function(node: IfBlock, renderer: Renderer, options: RenderOptions) {
const snippet = snip(node.expression);
renderer.append('${ ' + snippet + ' ? `');

@ -1,14 +1,17 @@
import { escape, escape_template, stringify } from '../../utils/stringify';
import { quote_name_if_necessary } from '../../../utils/names';
import { snip } from '../../utils/snip';
import Renderer from '../Renderer';
import Renderer, { RenderOptions } from '../Renderer';
import { stringify_props } from '../../utils/stringify_props';
import { get_slot_scope } from './shared/get_slot_scope';
import { AppendTarget } from '../../../interfaces';
import InlineComponent from '../../nodes/InlineComponent';
import { INode } from '../../nodes/interfaces';
import Text from '../../nodes/Text';
function stringify_attribute(chunk: Node) {
function stringify_attribute(chunk: INode) {
if (chunk.type === 'Text') {
return escape_template(escape(chunk.data));
return escape_template(escape((chunk as Text).data));
}
return '${@escape(' + snip(chunk) + ')}';
@ -30,7 +33,7 @@ function get_attribute_value(attribute) {
return '`' + attribute.chunks.map(stringify_attribute).join('') + '`';
}
export default function(node, renderer: Renderer, options) {
export default function(node: InlineComponent, renderer: Renderer, options: RenderOptions) {
const binding_props = [];
const binding_fns = [];

@ -1,7 +1,9 @@
import { quote_prop_if_necessary } from '../../../utils/names';
import get_slot_data from '../../utils/get_slot_data';
import Renderer, { RenderOptions } from '../Renderer';
import Slot from '../../nodes/Slot';
export default function(node, renderer, options) {
export default function(node: Slot, renderer: Renderer, options: RenderOptions) {
const prop = quote_prop_if_necessary(node.slot_name);
const slot_data = get_slot_data(node.values, true);

@ -1,6 +1,6 @@
import { snip } from '../../utils/snip';
export default function(node, renderer, options) {
import Renderer, { RenderOptions } from '../Renderer';
export default function(node, renderer: Renderer, options: RenderOptions) {
const snippet = snip(node.expression);
renderer.append(

@ -1,11 +1,14 @@
import { escape_html, escape_template, escape } from '../../utils/stringify';
import Renderer, { RenderOptions } from '../Renderer';
import Text from '../../nodes/Text';
import Element from '../../nodes/Element';
export default function(node, renderer, options) {
export default function(node: Text, renderer: Renderer, options: RenderOptions) {
let text = node.data;
if (
!node.parent ||
node.parent.type !== 'Element' ||
(node.parent.name !== 'script' && node.parent.name !== 'style')
((node.parent as Element).name !== 'script' && (node.parent as Element).name !== 'style')
) {
// unless this Text node is inside a <script> or <style> element, escape &,<,>
text = escape_html(text);

@ -1,4 +1,7 @@
export default function(node, renderer, options) {
import Renderer, { RenderOptions } from '../Renderer';
import Title from '../../nodes/Title';
export default function(node: Title, renderer: Renderer, options: RenderOptions) {
renderer.append(`<title>`);
renderer.render(node.children, options);

@ -4,6 +4,8 @@ import { CompileOptions } from '../../interfaces';
import { stringify } from '../utils/stringify';
import Renderer from './Renderer';
import { extract_names } from '../utils/scope';
import { INode } from '../nodes/interfaces';
import Text from '../nodes/Text';
export default function ssr(
component: Component,
@ -151,10 +153,10 @@ export default function ssr(
`).trim();
}
function trim(nodes) {
function trim(nodes: INode[]) {
let start = 0;
for (; start < nodes.length; start += 1) {
const node = nodes[start];
const node = nodes[start] as Text;
if (node.type !== 'Text') break;
node.data = node.data.replace(/^\s+/, '');
@ -163,7 +165,7 @@ function trim(nodes) {
let end = nodes.length;
for (; end > start; end -= 1) {
const node = nodes[end - 1];
const node = nodes[end - 1] as Text;
if (node.type !== 'Text') break;
node.data = node.data.replace(/\s+$/, '');

@ -1,4 +1,5 @@
export default function add_to_set(a: Set<any>, b: Set<any>) {
export default function add_to_set<T>(a: Set<T>, b: Set<T> | Array<T>) {
// @ts-ignore
b.forEach(item => {
a.add(item);
});

@ -1,6 +1,7 @@
import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import { Node } from '../../interfaces';
import { Node as ESTreeNode } from 'estree';
export function create_scopes(expression: Node) {
const map = new WeakMap();
@ -9,7 +10,7 @@ export function create_scopes(expression: Node) {
let scope = new Scope(null, false);
walk(expression, {
enter(node: Node, parent: Node) {
enter(node, parent) {
if (node.type === 'ImportDeclaration') {
node.specifiers.forEach(specifier => {
scope.declarations.set(specifier.local.name, specifier);
@ -25,7 +26,7 @@ export function create_scopes(expression: Node) {
if (node.id) scope.declarations.set(node.id.name, node);
}
node.params.forEach((param: Node) => {
node.params.forEach((param) => {
extract_names(param).forEach(name => {
scope.declarations.set(name, node);
});
@ -38,7 +39,7 @@ export function create_scopes(expression: Node) {
map.set(node, scope);
} else if (/(Class|Variable)Declaration/.test(node.type)) {
scope.add_declaration(node);
} else if (node.type === 'Identifier' && is_reference(node, parent)) {
} else if (node.type === 'Identifier' && is_reference(node as ESTreeNode, parent as ESTreeNode)) {
if (!scope.has(node.name) && !globals.has(node.name)) {
globals.set(node.name, node);
}

@ -1,11 +1,10 @@
import Attribute from '../nodes/Attribute';
import Node from '../nodes/shared/Node';
import { escape_template, escape } from './stringify';
import { snip } from './snip';
export function stringify_attribute(attribute: Attribute, is_ssr: boolean) {
return attribute.chunks
.map((chunk: Node) => {
.map((chunk) => {
if (chunk.type === 'Text') {
return escape_template(escape(chunk.data).replace(/"/g, '&quot;'));
}

@ -0,0 +1,6 @@
export { default as compile } from './compile/index';
export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
export const VERSION = '__VERSION__';

@ -3,7 +3,7 @@ Adapted from https://github.com/mattdesl
Distributed under MIT License https://github.com/mattdesl/eases/blob/master/LICENSE.md
*/
export { identity as linear } from './internal';
export { identity as linear } from 'svelte/internal';
export function backInOut(t) {
const s = 1.70158 * 1.525;

@ -1,6 +1,10 @@
export { default as compile } from './compile/index';
export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
export const VERSION = '__VERSION__';
export {
onMount,
onDestroy,
beforeUpdate,
afterUpdate,
setContext,
getContext,
tick,
createEventDispatcher
} from 'svelte/internal';

@ -1,10 +1,51 @@
export interface Node {
interface BaseNode {
start: number;
end: number;
type: string;
children?: Node[];
[prop_name: string]: any;
}
export interface Text extends BaseNode {
type: 'Text',
data: string;
}
export interface MustacheTag extends BaseNode {
type: 'MustacheTag',
expresion: Node;
}
export type DirectiveType = 'Action'
| 'Animation'
| 'Binding'
| 'Class'
| 'EventHandler'
| 'Let'
| 'Ref'
| 'Transition';
interface BaseDirective extends BaseNode {
type: DirectiveType;
expression: null|Node;
name: string;
modifiers: string[]
}
export interface Transition extends BaseDirective{
type: 'Transition',
intro: boolean;
outro: boolean;
}
export type Directive = BaseDirective | Transition;
export type Node = Text
| MustacheTag
| BaseNode
| Directive
| Transition;
export interface Parser {
readonly template: string;
readonly filename?: string;

@ -1,7 +1,23 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler.js';
import { current_component, set_current_component } from './lifecycle.js';
import { blank_object, is_function, run, run_all, noop } from './utils.js';
import { children } from './dom.js';
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler';
import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils';
import { children } from './dom';
interface T$$ {
dirty: null;
ctx: null|any;
bound: any;
update: () => void;
callbacks: any;
after_render: any[];
props: any;
fragment: null|any;
not_equal: any;
before_render: any[];
context: Map<any, any>;
on_mount: any[];
on_destroy: any[]
}
export function bind(component, name, callback) {
if (component.$$.props.indexOf(name) === -1) return;
@ -59,7 +75,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
const props = options.props || {};
const $$ = component.$$ = {
const $$: T$$ = component.$$ = {
fragment: null,
ctx: null,
@ -99,9 +115,9 @@ export function init(component, options, instance, create_fragment, not_equal, p
if (options.target) {
if (options.hydrate) {
$$.fragment.l(children(options.target));
$$.fragment!.l(children(options.target));
} else {
$$.fragment.c();
$$.fragment!.c();
}
if (options.intro && component.$$.fragment.i) component.$$.fragment.i();
@ -115,13 +131,16 @@ export function init(component, options, instance, create_fragment, not_equal, p
export let SvelteElement;
if (typeof HTMLElement !== 'undefined') {
SvelteElement = class extends HTMLElement {
$$: T$$;
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
@ -153,6 +172,8 @@ if (typeof HTMLElement !== 'undefined') {
}
export class SvelteComponent {
$$: T$$;
$destroy() {
destroy(this, true);
this.$destroy = noop;

@ -1,6 +1,6 @@
import { identity as linear, noop, now } from './utils.js';
import { loop } from './loop.js';
import { create_rule, delete_rule } from './style_manager.js';
import { identity as linear, noop, now } from './utils';
import { loop } from './loop';
import { create_rule, delete_rule } from './style_manager';
export function create_animation(node, from, fn, params) {
if (!from) return noop;
@ -22,16 +22,15 @@ export function create_animation(node, from, fn, params) {
let started = false;
let name;
const css_text = node.style.cssText;
function start() {
if (css) {
if (delay) node.style.cssText = css_text; // TODO create delayed animation instead?
name = create_rule(node, 0, 1, duration, 0, easing, css);
name = create_rule(node, 0, 1, duration, delay, easing, css);
}
if (!delay) {
started = true;
}
}
function stop() {
if (css) delete_rule(node, name);
@ -40,7 +39,7 @@ export function create_animation(node, from, fn, params) {
loop(now => {
if (!started && now >= start_time) {
start();
started = true;
}
if (started && now >= end) {
@ -61,11 +60,7 @@ export function create_animation(node, from, fn, params) {
return true;
});
if (delay) {
if (css) node.style.cssText += css(0, 1);
} else {
start();
}
tick(0, 1);

@ -1,11 +1,11 @@
import { assign, is_promise } from './utils.js';
import { check_outros, group_outros, on_outro } from './transitions.js';
import { flush } from '../internal/scheduler.js';
import { assign, is_promise } from './utils';
import { check_outros, group_outros, on_outro } from './transitions';
import { flush } from '../internal/scheduler';
export function handle_promise(promise, info) {
const token = info.token = {};
function update(type, index, key, value) {
function update(type, index, key?, value?) {
if (info.token !== token) return;
info.resolved = key && { [key]: value };

@ -1,28 +1,28 @@
export function append(target, node) {
export function append(target:Node, node:Node) {
target.appendChild(node);
}
export function insert(target, node, anchor) {
export function insert(target: Node, node: Node, anchor?:Node) {
target.insertBefore(node, anchor || null);
}
export function detach(node) {
export function detach(node: Node) {
node.parentNode.removeChild(node);
}
export function detach_between(before, after) {
export function detach_between(before: Node, after: Node) {
while (before.nextSibling && before.nextSibling !== after) {
before.parentNode.removeChild(before.nextSibling);
}
}
export function detach_before(after) {
export function detach_before(after:Node) {
while (after.previousSibling) {
after.parentNode.removeChild(after.previousSibling);
}
}
export function detach_after(before) {
export function detach_after(before:Node) {
while (before.nextSibling) {
before.parentNode.removeChild(before.nextSibling);
}
@ -34,25 +34,30 @@ export function destroy_each(iterations, detaching) {
}
}
export function element(name) {
return document.createElement(name);
export function element<K extends keyof HTMLElementTagNameMap>(name: K) {
return document.createElement<K>(name);
}
export function object_without_properties(obj, exclude) {
const target = {};
export function object_without_properties<T,K extends keyof T>(obj:T, exclude: K[]) {
const target = {} as Pick<T, Exclude<keyof T, K>>;
for (const k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) {
if (
Object.prototype.hasOwnProperty.call(obj, k)
// @ts-ignore
&& exclude.indexOf(k) === -1
) {
// @ts-ignore
target[k] = obj[k];
}
}
return target;
}
export function svg_element(name) {
export function svg_element(name:string):SVGElement {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
export function text(data) {
export function text(data:string) {
return document.createTextNode(data);
}
@ -64,7 +69,7 @@ export function empty() {
return text('');
}
export function listen(node, event, handler, options) {
export function listen(node: Node, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
@ -72,6 +77,7 @@ export function listen(node, event, handler, options) {
export function prevent_default(fn) {
return function(event) {
event.preventDefault();
// @ts-ignore
return fn.call(this, event);
};
}
@ -79,16 +85,17 @@ export function prevent_default(fn) {
export function stop_propagation(fn) {
return function(event) {
event.stopPropagation();
// @ts-ignore
return fn.call(this, event);
};
}
export function attr(node, attribute, value) {
export function attr(node: Element, attribute: string, value?: string) {
if (value == null) node.removeAttribute(attribute);
else node.setAttribute(attribute, value);
}
export function set_attributes(node, attributes) {
export function set_attributes(node: HTMLElement, attributes: { [x: string]: string; }) {
for (const key in attributes) {
if (key === 'style') {
node.style.cssText = attributes[key];
@ -243,8 +250,8 @@ export function toggle_class(element, name, toggle) {
element.classList[toggle ? 'add' : 'remove'](name);
}
export function custom_event(type, detail) {
const e = document.createEvent('CustomEvent');
export function custom_event<T=any>(type: string, detail?: T) {
const e: CustomEvent<T> = document.createEvent('CustomEvent');
e.initCustomEvent(type, false, false, detail);
return e;
}

@ -1,12 +0,0 @@
export * from './animations.js';
export * from './await-block.js';
export * from './dom.js';
export * from './keyed-each.js';
export * from './lifecycle.js';
export * from './loop.js';
export * from './scheduler.js';
export * from './spread.js';
export * from './ssr.js';
export * from './transitions.js';
export * from './utils.js';
export * from './Component.js';

@ -0,0 +1,12 @@
export * from './animations';
export * from './await-block';
export * from './dom';
export * from './keyed-each';
export * from './lifecycle';
export * from './loop';
export * from './scheduler';
export * from './spread';
export * from './ssr';
export * from './transitions';
export * from './utils';
export * from './Component';

@ -1,4 +1,4 @@
import { on_outro } from './transitions.js';
import { on_outro } from './transitions';
export function destroy_block(block, lookup) {
block.d(1);

@ -1,4 +1,6 @@
import { now } from './utils.js';
import { now, raf } from './utils';
export interface Task { abort(): void; promise: Promise<undefined> }
const tasks = new Set();
let running = false;
@ -12,7 +14,7 @@ function run_tasks() {
});
running = tasks.size > 0;
if (running) requestAnimationFrame(run_tasks);
if (running) raf(run_tasks);
}
export function clear_loops() {
@ -21,16 +23,16 @@ export function clear_loops() {
running = false;
}
export function loop(fn) {
export function loop(fn: (number)=>void): Task {
let task;
if (!running) {
running = true;
requestAnimationFrame(run_tasks);
raf(run_tasks);
}
return {
promise: new Promise(fulfil => {
promise: new Promise<undefined>(fulfil => {
tasks.add(task = [fn, fulfil]);
}),
abort() {

@ -1,5 +1,5 @@
import { run_all } from './utils.js';
import { set_current_component } from './lifecycle.js';
import { run_all } from './utils';
import { set_current_component } from './lifecycle';
export const dirty_components = [];
export const intros = { enabled: false };

@ -1,5 +1,5 @@
import { set_current_component, current_component } from './lifecycle.js';
import { run_all, blank_object } from './utils.js';
import { set_current_component, current_component } from './lifecycle';
import { run_all, blank_object } from './utils';
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;
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2

@ -1,4 +1,5 @@
import { element } from './dom.js';
import { element } from './dom';
import { raf } from './utils';
let stylesheet;
let active = 0;
@ -43,7 +44,7 @@ export function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
return name;
}
export function delete_rule(node, name) {
export function delete_rule(node, name?) {
node.style.animation = (node.style.animation || '')
.split(', ')
.filter(name
@ -56,7 +57,7 @@ export function delete_rule(node, name) {
}
export function clear_rules() {
requestAnimationFrame(() => {
raf(() => {
if (active) return;
let i = stylesheet.cssRules.length;
while (i--) stylesheet.deleteRule(i);

@ -1,8 +1,8 @@
import { identity as linear, noop, now, run_all } from './utils.js';
import { loop } from './loop.js';
import { create_rule, delete_rule } from './style_manager.js';
import { custom_event } from './dom.js';
import { add_render_callback } from './scheduler.js';
import { identity as linear, noop, now, run_all } from './utils';
import { loop } from './loop';
import { create_rule, delete_rule } from './style_manager';
import { custom_event } from './dom';
import { add_render_callback } from './scheduler';
let promise;
@ -229,6 +229,7 @@ export function create_bidirectional_transition(node, fn, params, intro) {
};
if (!b) {
// @ts-ignore todo: improve typings
program.group = outros;
outros.remaining += 1;
}

@ -80,11 +80,19 @@ export function exclude_internal_props(props) {
return result;
}
export let now = typeof window !== 'undefined'
const is_client = typeof window !== 'undefined';
export let now: () => number = is_client
? () => window.performance.now()
: () => Date.now();
export let raf = is_client ? requestAnimationFrame : noop;
// used internally for testing
export function set_now(fn) {
now = fn;
}
export function set_raf(fn) {
raf = fn;
}

@ -1,2 +0,0 @@
export * from './spring.js';
export * from './tweened.js';

@ -0,0 +1,2 @@
export * from './spring';
export * from './tweened';

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

Loading…
Cancel
Save