some annoying typescript stuff

pull/3539/head
Richard Harris 6 years ago
parent 8d093c6200
commit 1091d23ed1

23
package-lock.json generated

@ -502,27 +502,20 @@
"dev": true
},
"code-red": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.8.tgz",
"integrity": "sha512-wvKPondTM1H6MoUnz5tj6z1xawzmaulkC0qLv5S1KLkShWgRybgIdvGyVr5pPQt2ovpdmh6BcpRXXoqs5X8tcA==",
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.9.tgz",
"integrity": "sha512-xQVOUxMYfFXgkW/VA1raehj+YH8CV98c8XG5cFhpeEYoO8fIjr4KM4E0Ov6lLC2rSu5qtMWmrcA1gveZwg8r1w==",
"dev": true,
"requires": {
"acorn": "^7.0.0",
"estree-walker": "^0.6.1",
"is-reference": "^1.1.3",
"periscopic": "^1.0.0",
"source-map": "^0.7.3"
},
"dependencies": {
"astring": {
"version": "github:Rich-Harris/astring#ff83f5e4e75b304cdd428ada4a71372276b0084d",
"from": "github:Rich-Harris/astring#ff83f5e4e75b304cdd428ada4a71372276b0084d"
},
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
"version": "github:Rich-Harris/astring#00fa527b66cc1b57efb2a4b9052dd4e190ae3590",
"from": "github:Rich-Harris/astring#00fa527b66cc1b57efb2a4b9052dd4e190ae3590"
},
"source-map": {
"version": "0.7.3",
@ -1212,9 +1205,9 @@
"dev": true
},
"estree-walker": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.7.0.tgz",
"integrity": "sha512-6BzTIhsjm2jnqLO+O23aaiDGr9jsrgwT1ObAIEebwvbJthCoF5T1MtybbCx540r2U5fsn/8IIv7ZWqMqTHuu6Q==",
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.8.1.tgz",
"integrity": "sha512-H6cJORkqvrNziu0KX2hqOMAlA2CiuAxHeGJXSIoKA/KLv229Dw806J3II6mKTm5xiDX1At1EXCfsOQPB+tMB+g==",
"dev": true
},
"esutils": {

@ -63,13 +63,13 @@
"acorn": "^7.0.0",
"agadoo": "^1.0.1",
"c8": "^5.0.1",
"code-red": "0.0.8",
"code-red": "0.0.9",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-svelte3": "^2.7.3",
"estree-walker": "^0.7.0",
"estree-walker": "^0.8.1",
"is-reference": "^1.1.3",
"jsdom": "^15.1.1",
"kleur": "^3.0.3",

@ -20,8 +20,9 @@ const ts_plugin = is_publish
const external = id => id.startsWith('svelte/');
const inlined_estree = fs.readFileSync('./node_modules/estree-walker/index.d.ts', 'utf-8').replace(/declare.*\{((.|[\n\r])+)\}/m, '$1');
fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';\n${inlined_estree}`);
// const inlined_estree = fs.readFileSync('./node_modules/estree-walker/index.d.ts', 'utf-8').replace(/declare.*\{((.|[\n\r])+)\}/m, '$1');
// fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';\n${inlined_estree}`);
fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';`);
export default [
/* runtime */

@ -15,7 +15,7 @@ import Stylesheet from './css/Stylesheet';
import { test } from '../config';
import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports';
import { Node, Ast, CompileOptions, Var, Warning, Identifier } from '../interfaces';
import { Ast, CompileOptions, Var, Warning } from '../interfaces';
import error from '../utils/error';
import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference';
@ -23,9 +23,8 @@ import is_reference from 'is-reference';
import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch';
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';
import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression } from 'estree';
import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x } from 'code-red';
@ -46,7 +45,7 @@ childKeys.Attribute = ['value'];
childKeys.ExportNamedDeclaration = ['declaration', 'specifiers'];
function remove_node(
body: Node,
body: Node[],
node: Node
) {
const i = body.indexOf(node);
@ -78,14 +77,14 @@ export default class Component {
vars: Var[] = [];
var_lookup: Map<string, Var> = new Map();
imports: Node[] = [];
imports: ImportDeclaration[] = [];
module_javascript: string;
javascript: string;
hoistable_nodes: Set<Node> = new Set();
node_for_declaration: Map<string, Node> = new Map();
partly_hoisted: Node[] = [];
fully_hoisted: Node[] = [];
partly_hoisted: (Node | Node[])[] = [];
fully_hoisted: (Node | Node[])[] = [];
reactive_declarations: Array<{
assignees: Set<string>;
dependencies: Set<string>;
@ -589,7 +588,7 @@ export default class Component {
walk(script.content, {
enter(node) {
if (node.type === 'LabeledStatement' && node.label.name === '$') {
component.warn(node, {
component.warn(node as any, {
code: 'module-script-reactive-declaration',
message: '$: has no effect in a module script',
});
@ -602,23 +601,25 @@ export default class Component {
scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node, {
this.error(node as any, {
code: 'illegal-declaration',
message: `The $ prefix is reserved, and cannot be used for variable and import names`,
});
}
const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
this.add_var({
name,
module: true,
hoistable: true,
writable: node.kind === 'var' || node.kind === 'let',
writable
});
});
globals.forEach((node, name) => {
if (name[0] === '$') {
this.error(node, {
this.error(node as any, {
code: 'illegal-subscription',
message: `Cannot reference store value inside <script context="module">`,
});
@ -644,7 +645,7 @@ export default class Component {
if (node.type !== 'LabeledStatement') return;
if (node.body.type !== 'ExpressionStatement') return;
const expression = unwrap_parens(node.body.expression);
const { expression } = node.body;
if (expression.type !== 'AssignmentExpression') return;
extract_names(expression.left).forEach(name => {
@ -662,17 +663,19 @@ export default class Component {
instance_scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node, {
this.error(node as any, {
code: 'illegal-declaration',
message: `The $ prefix is reserved, and cannot be used for variable and import names`,
});
}
const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
this.add_var({
name,
initialised: instance_scope.initialised_declarations.has(name),
hoistable: /^Import/.test(node.type),
writable: node.kind === 'var' || node.kind === 'let',
writable
});
this.node_for_declaration.set(name, node);
@ -696,7 +699,7 @@ export default class Component {
});
} else if (name[0] === '$') {
if (name === '$' || name[1] === '$') {
this.error(node, {
this.error(node as any, {
code: 'illegal-global',
message: `${name} is an illegal variable name`
});
@ -749,20 +752,12 @@ export default class Component {
scope = map.get(node);
}
let names;
let deep = false;
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
const names = extract_names(assignee);
if (node.type === 'AssignmentExpression') {
deep = node.left.type === 'MemberExpression';
const deep = assignee.type === 'MemberExpression';
names = deep
? [get_object(node.left).name]
: extract_names(node.left);
} else if (node.type === 'UpdateExpression') {
names = [get_object(node.argument).name];
}
if (names) {
names.forEach(name => {
if (scope.find_owner(name) === instance_scope) {
const variable = component.var_lookup.get(name);
@ -797,13 +792,13 @@ export default class Component {
node.label.name === '$' &&
parent.type !== 'Program'
) {
component.warn(node, {
component.warn(node as any, {
code: 'non-top-level-reactive-declaration',
message: '$: has no effect outside of the top-level',
});
}
if (is_reference(node as ESTreeNode, parent as ESTreeNode)) {
if (is_reference(node as Node, parent as Node)) {
const object = get_object(node);
const { name } = object;
@ -891,7 +886,7 @@ export default class Component {
if (variable.export_name) {
// TODO is this still true post-#3539?
component.error(declarator, {
component.error(declarator as any, {
code: 'destructured-prop',
message: `Cannot declare props in destructured declaration`,
});
@ -939,13 +934,13 @@ export default class Component {
}
},
leave(node, _parent, _key, index) {
leave(node, parent, _key, index) {
if (map.has(node)) {
scope = scope.parent;
}
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
_parent.body[index] = node.declaration;
(parent as Program).body[index] = node.declaration;
}
},
});
@ -975,14 +970,16 @@ export default class Component {
if (!d.init) return false;
if (d.init.type !== 'Literal') return false;
const v = this.var_lookup.get(d.id.name);
const { name } = d.id as Identifier;
const v = this.var_lookup.get(name);
if (v.reassigned) return false;
if (v.export_name) return false;
if (this.var_lookup.get(d.id.name).reassigned) return false;
if (this.var_lookup.get(name).reassigned) return false;
if (
this.vars.find(
variable => variable.name === d.id.name && variable.module
variable => variable.name === name && variable.module
)
)
return false;
@ -992,7 +989,7 @@ export default class Component {
if (all_hoistable) {
node.declarations.forEach(d => {
const variable = this.var_lookup.get(d.id.name);
const variable = this.var_lookup.get((d.id as Identifier).name);
variable.hoistable = true;
});
@ -1041,7 +1038,7 @@ export default class Component {
scope = map.get(node);
}
if (is_reference(node as ESTreeNode, parent as ESTreeNode)) {
if (is_reference(node as Node, parent as Node)) {
const { name } = flatten_reference(node);
const owner = scope.find_owner(name);
@ -1138,7 +1135,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 as ESTreeNode, parent as ESTreeNode)) {
} else if (is_reference(node as Node, parent as Node)) {
const identifier = get_object(node);
if (!assignee_nodes.has(identifier)) {
const { name } = identifier;
@ -1166,9 +1163,8 @@ export default class Component {
},
});
const expression =
node.body.expression && unwrap_parens(node.body.expression);
const declaration = expression && expression.left;
const { expression } = node.body as ExpressionStatement;
const declaration = expression && (expression as AssignmentExpression).left;
unsorted_reactive_declarations.push({
assignees,

@ -1,6 +1,7 @@
import list from '../utils/list';
import { ModuleFormat, Node, Identifier } from '../interfaces';
import { ModuleFormat } from '../interfaces';
import { b, x } from 'code-red';
import { Identifier, ImportDeclaration } from 'estree';
const wrappers = { esm, cjs };
@ -17,7 +18,7 @@ export default function create_module(
sveltePath = 'svelte',
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
imports: ImportDeclaration[],
module_exports: Export[]
) {
const internal_path = `${sveltePath}/internal`;
@ -45,7 +46,7 @@ function esm(
internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
imports: ImportDeclaration[],
module_exports: Export[]
) {
const import_declaration = {
@ -113,7 +114,7 @@ function cjs(
internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
imports: ImportDeclaration[],
module_exports: Export[]
) {
const internal_requires = {
@ -172,7 +173,7 @@ function cjs(
method: false,
shorthand: false,
computed: false,
key: s.imported || { type: 'Identifier', name: 'default' },
key: s.type === 'ImportSpecifier' ? s.imported : { type: 'Identifier', name: 'default' },
value: s.local,
kind: 'init'
}))

@ -1,17 +1,17 @@
import MagicString from 'magic-string';
import Stylesheet from './Stylesheet';
import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { Node } from '../../interfaces';
import { CssNode } from './interfaces';
import Component from '../Component';
export default class Selector {
node: Node;
node: CssNode;
stylesheet: Stylesheet;
blocks: Block[];
local_blocks: Block[];
used: boolean;
constructor(node: Node, stylesheet: Stylesheet) {
constructor(node: CssNode, stylesheet: Stylesheet) {
this.node = node;
this.stylesheet = stylesheet;
@ -28,8 +28,8 @@ export default class Selector {
this.used = this.blocks[0].global;
}
apply(node: Node, stack: Node[]) {
const to_encapsulate: Node[] = [];
apply(node: CssNode, stack: CssNode[]) {
const to_encapsulate: CssNode[] = [];
apply_selector(this.stylesheet, this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
@ -126,7 +126,7 @@ export default class Selector {
}
}
function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: Node, stack: Node[], to_encapsulate: any[]): boolean {
function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean {
const block = blocks.pop();
if (!block) return false;
@ -215,13 +215,13 @@ const operators = {
'*=': (value: string, flags: string) => new RegExp(value, flags)
};
function attribute_matches(node: Node, name: string, expected_value: string, operator: string, case_insensitive: boolean) {
function attribute_matches(node: CssNode, name: string, expected_value: string, operator: string, case_insensitive: boolean) {
const spread = node.attributes.find(attr => attr.type === 'Spread');
if (spread) return true;
if (node.bindings.some((binding: Node) => binding.name === name)) return true;
if (node.bindings.some((binding: CssNode) => binding.name === name)) return true;
const attr = node.attributes.find((attr: Node) => attr.name === name);
const attr = node.attributes.find((attr: CssNode) => attr.name === name);
if (!attr) return false;
if (attr.is_true) return operator === null;
if (attr.chunks.length > 1) return true;
@ -250,7 +250,7 @@ function class_matches(node, name: string) {
});
}
function unquote(value: Node) {
function unquote(value: CssNode) {
if (value.type === 'Identifier') return value.name;
const str = value.value;
if (str[0] === str[str.length - 1] && str[0] === "'" || str[0] === '"') {
@ -261,13 +261,13 @@ function unquote(value: Node) {
class Block {
global: boolean;
combinator: Node;
selectors: Node[]
combinator: CssNode;
selectors: CssNode[]
start: number;
end: number;
should_encapsulate: boolean;
constructor(combinator: Node) {
constructor(combinator: CssNode) {
this.combinator = combinator;
this.global = false;
this.selectors = [];
@ -278,7 +278,7 @@ class Block {
this.should_encapsulate = false;
}
add(selector: Node) {
add(selector: CssNode) {
if (this.selectors.length === 0) {
this.start = selector.start;
this.global = selector.type === 'PseudoClassSelector' && selector.name === 'global';
@ -289,12 +289,12 @@ class Block {
}
}
function group_selectors(selector: Node) {
function group_selectors(selector: CssNode) {
let block: Block = new Block(null);
const blocks = [block];
selector.children.forEach((child: Node) => {
selector.children.forEach((child: CssNode) => {
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
block = new Block(child);
blocks.push(block);

@ -2,20 +2,21 @@ import MagicString from 'magic-string';
import { walk } from 'estree-walker';
import Selector from './Selector';
import Element from '../nodes/Element';
import { Node, Ast } from '../../interfaces';
import { Ast, TemplateNode } from '../../interfaces';
import Component from '../Component';
import { CssNode } from './interfaces';
function remove_css_prefix(name: string): string {
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
}
const is_keyframes_node = (node: Node) =>
const is_keyframes_node = (node: CssNode) =>
remove_css_prefix(node.name) === 'keyframes';
const at_rule_has_declaration = ({ block }: Node): true =>
const at_rule_has_declaration = ({ block }: CssNode): true =>
block &&
block.children &&
block.children.find((node: Node) => node.type === 'Declaration');
block.children.find((node: CssNode) => node.type === 'Declaration');
function minify_declarations(
code: MagicString,
@ -48,14 +49,14 @@ function hash(str: string): string {
class Rule {
selectors: Selector[];
declarations: Declaration[];
node: Node;
node: CssNode;
parent: Atrule;
constructor(node: Node, stylesheet, parent?: Atrule) {
constructor(node: CssNode, stylesheet, parent?: Atrule) {
this.node = node;
this.parent = parent;
this.selectors = node.selector.children.map((node: Node) => new Selector(node, stylesheet));
this.declarations = node.block.children.map((node: Node) => new Declaration(node));
this.selectors = node.selector.children.map((node: CssNode) => new Selector(node, stylesheet));
this.declarations = node.block.children.map((node: CssNode) => new Declaration(node));
}
apply(node: Element, stack: Element[]) {
@ -117,16 +118,16 @@ class Rule {
}
class Declaration {
node: Node;
node: CssNode;
constructor(node: Node) {
constructor(node: CssNode) {
this.node = node;
}
transform(code: MagicString, keyframes: Map<string, string>) {
const property = this.node.property && remove_css_prefix(this.node.property.toLowerCase());
if (property === 'animation' || property === 'animation-name') {
this.node.value.children.forEach((block: Node) => {
this.node.value.children.forEach((block: CssNode) => {
if (block.type === 'Identifier') {
const name = block.name;
if (keyframes.has(name)) {
@ -155,11 +156,11 @@ class Declaration {
}
class Atrule {
node: Node;
node: CssNode;
children: Array<Atrule|Rule>;
declarations: Declaration[];
constructor(node: Node) {
constructor(node: CssNode) {
this.node = node;
this.children = [];
this.declarations = [];
@ -191,7 +192,7 @@ class Atrule {
let c = this.node.start + (expression_char === '(' ? 6 : 7);
if (this.node.expression.start > c) code.remove(c, this.node.expression.start);
this.node.expression.children.forEach((query: Node) => {
this.node.expression.children.forEach((query: CssNode) => {
// TODO minify queries
c = query.end;
});
@ -200,7 +201,7 @@ class Atrule {
} else if (this.node.name === 'supports') {
let c = this.node.start + 9;
if (this.node.expression.start - c > 1) code.overwrite(c, this.node.expression.start, ' ');
this.node.expression.children.forEach((query: Node) => {
this.node.expression.children.forEach((query: CssNode) => {
// TODO minify queries
c = query.end;
});
@ -240,7 +241,7 @@ class Atrule {
transform(code: MagicString, id: string, keyframes: Map<string, string>) {
if (is_keyframes_node(this.node)) {
this.node.expression.children.forEach(({ type, name, start, end }: Node) => {
this.node.expression.children.forEach(({ type, name, start, end }: CssNode) => {
if (type === 'Identifier') {
if (name.startsWith('-global-')) {
code.remove(start, start + 8);
@ -288,7 +289,7 @@ export default class Stylesheet {
children: Array<Rule|Atrule> = [];
keyframes: Map<string, string> = new Map();
nodes_with_css_class: Set<Node> = new Set();
nodes_with_css_class: Set<CssNode> = new Set();
constructor(source: string, ast: Ast, filename: string, dev: boolean) {
this.source = source;
@ -305,8 +306,8 @@ export default class Stylesheet {
let depth = 0;
let current_atrule: Atrule = null;
walk(ast.css, {
enter: (node: Node) => {
walk(ast.css as any, {
enter: (node: any) => {
if (node.type === 'Atrule') {
const atrule = new Atrule(node);
stack.push(atrule);
@ -318,7 +319,7 @@ export default class Stylesheet {
}
if (is_keyframes_node(node)) {
node.expression.children.forEach((expression: Node) => {
node.expression.children.forEach((expression: CssNode) => {
if (expression.type === 'Identifier' && !expression.name.startsWith('-global-')) {
this.keyframes.set(expression.name, `${this.id}-${expression.name}`);
}
@ -346,7 +347,7 @@ export default class Stylesheet {
depth += 1;
},
leave: (node: Node) => {
leave: (node: any) => {
if (node.type === 'Atrule') {
stack.pop();
current_atrule = stack[stack.length - 1];
@ -364,7 +365,7 @@ export default class Stylesheet {
if (!this.has_styles) return;
const stack: Element[] = [];
let parent: Node = node;
let parent: TemplateNode = node;
while (parent = parent.parent) {
if (parent.type === 'Element') stack.unshift(parent as Element);
}
@ -376,7 +377,7 @@ export default class Stylesheet {
}
reify() {
this.nodes_with_css_class.forEach((node: Node) => {
this.nodes_with_css_class.forEach((node: Element) => {
node.add_css_class();
});
}
@ -388,8 +389,8 @@ export default class Stylesheet {
const code = new MagicString(this.source);
walk(this.ast.css, {
enter: (node: Node) => {
walk(this.ast.css as any, {
enter: (node: any) => {
code.addSourcemapLocation(node.start);
code.addSourcemapLocation(node.end);
}

@ -1,4 +1,4 @@
import { Node } from '../../interfaces';
import { Node } from 'estree';
export const UNKNOWN = {};

@ -0,0 +1,6 @@
export type CssNode = {
type: string;
start: number;
end: number;
[prop_name: string]: any;
}

@ -1,49 +1,48 @@
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Expression from './shared/Expression';
import map_children from './shared/map_children';
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';
import { x } from 'code-red';
import { Node, Identifier } from 'estree';
interface Context {
key: INode;
key: Identifier;
name?: string;
tail: string;
modifier: (node: Node) => Node;
}
function unpack_destructuring(contexts: Context[], node: INode, tail: string) {
function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) {
if (!node) return;
if (node.type === 'Identifier' || node.type === 'RestIdentifier') {
if (node.type === 'Identifier' || (node as any).type === 'RestIdentifier') { // TODO is this right? not RestElement?
contexts.push({
key: node,
tail
key: node as Identifier,
modifier
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (element && element.type === 'RestIdentifier') {
unpack_destructuring(contexts, element, `${tail}.slice(${i})`);
if (element && (element as any).type === 'RestIdentifier') {
unpack_destructuring(contexts, element, node => x`${node}.slice(${i})` as Node);
} else {
unpack_destructuring(contexts, element, `${tail}[${i}]`);
unpack_destructuring(contexts, element, node => x`${node}[${i}]` as Node);
}
});
} else if (node.type === 'ObjectPattern') {
const used_properties = [];
node.properties.forEach((property) => {
if (property.kind === 'rest') {
if ((property as any).kind === 'rest') { // TODO is this right?
unpack_destructuring(
contexts,
property.value,
`@object_without_properties(${tail}, ${JSON.stringify(used_properties)})`
node => x`@object_without_properties(${node}, ${JSON.stringify(used_properties)})` as Node
);
} else {
used_properties.push(property.key.name);
used_properties.push((property.key as Identifier).name);
unpack_destructuring(contexts, property.value,`${tail}.${property.key.name}`);
unpack_destructuring(contexts, property.value, node => x`${node}.${(property.key as Identifier).name}` as Node);
}
});
}
@ -77,7 +76,7 @@ export default class EachBlock extends AbstractBlock {
this.scope = scope.child();
this.contexts = [];
unpack_destructuring(this.contexts, info.context, new_tail());
unpack_destructuring(this.contexts, info.context, node => node);
this.contexts.forEach(context => {
this.scope.add(context.key.name, this.expression.dependencies, this);

@ -4,7 +4,7 @@ import Component from '../Component';
import { b, x } from 'code-red';
import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names';
import { Identifier } from '../../interfaces';
import { Identifier } from 'estree';
export default class EventHandler extends Node {
type: 'EventHandler';
@ -32,16 +32,18 @@ export default class EventHandler extends Node {
} else if (info.expression.type === 'Identifier') {
let node = component.node_for_declaration.get(info.expression.name);
if (node && node.type === 'VariableDeclaration') {
if (node) {
if (node.type === 'VariableDeclaration') {
// for `const handleClick = () => {...}`, we want the [arrow] function expression node
const declarator = node.declarations.find(d => d.id.name === info.expression.name);
const declarator = node.declarations.find(d => (d.id as Identifier).name === info.expression.name);
node = declarator && declarator.init;
}
if (node && /Function/.test(node.type) && node.params.length === 0) {
if ((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) {
this.can_make_passive = true;
}
}
}
} else {
const id = component.get_unique_name(`${sanitize(this.name)}_handler`);

@ -20,7 +20,7 @@ export default class Let extends Node {
walk(info.expression, {
enter: node => {
if (!applicable.has(node.type)) {
component.error(node, {
component.error(node as any, {
code: 'invalid-let',
message: `let directive value must be an identifier or an object/array pattern`
});

@ -3,16 +3,16 @@ import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import flatten_reference from '../../utils/flatten_reference';
import { create_scopes, Scope, extract_names } from '../../utils/scope';
import { Node } from '../../../interfaces';
import { globals, sanitize } from '../../../utils/names';
import Wrapper from '../../render_dom/wrappers/shared/Wrapper';
import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object';
import Block from '../../render_dom/Block';
import { INode } from '../interfaces';
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
import { x, b } from 'code-red';
import { invalidate } from '../../utils/invalidate';
import { Node, BinaryExpression, LogicalExpression, FunctionExpression } from 'estree';
import { TemplateNode } from '../../../interfaces';
const binary_operators: Record<string, number> = {
'**': 15,
@ -53,8 +53,8 @@ const precedence: Record<string, (node?: Node) => number> = {
CallExpression: () => 19,
UpdateExpression: () => 17,
UnaryExpression: () => 16,
BinaryExpression: (node: Node) => binary_operators[node.operator],
LogicalExpression: (node: Node) => logical_operators[node.operator],
BinaryExpression: (node: BinaryExpression) => binary_operators[node.operator],
LogicalExpression: (node: LogicalExpression) => logical_operators[node.operator],
ConditionalExpression: () => 4,
AssignmentExpression: () => 3,
YieldExpression: () => 2,
@ -62,14 +62,13 @@ const precedence: Record<string, (node?: Node) => number> = {
SequenceExpression: () => 0
};
type Owner = Wrapper | INode;
type Owner = Wrapper | TemplateNode;
export default class Expression {
type: 'Expression' = 'Expression';
component: Component;
owner: Owner;
node: any;
snippet: string;
references: Set<string>;
dependencies: Set<string> = new Set();
contextual_dependencies: Set<string> = new Set();
@ -79,7 +78,7 @@ export default class Expression {
scope_map: WeakMap<Node, Scope>;
is_synthetic: boolean;
declarations: Node[] = [];
declarations: (Node | Node[])[] = [];
uses_context = false;
// todo: owner type
@ -297,7 +296,7 @@ export default class Expression {
if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(declaration);
node.name = id;
// node.name = id;
this.replace(id as any);
@ -312,7 +311,7 @@ export default class Expression {
else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);
node.name = id;
// node.name = id;
this.replace(x`#ctx.${id}` as any);
@ -325,18 +324,21 @@ export default class Expression {
else {
// we need a combo block/init recipe
node.params.unshift({
(node as FunctionExpression).params.unshift({
type: 'ObjectPattern',
properties: Array.from(contextual_dependencies).map(name => ({
type: 'Property',
kind: 'init',
method: false,
shorthand: false,
computed: false,
key: { type: 'Identifier', name },
value: { type: 'Identifier', name }
}))
});
component.partly_hoisted.push(declaration);
node.name = id;
// node.name = id;
this.replace(id as any);
@ -346,7 +348,7 @@ export default class Expression {
referenced: true
});
if (node.params.length > 0) {
if ((node as FunctionExpression).params.length > 0) {
declarations.push(b`
function ${id}(...args) {
return #ctx.${id}(#ctx, ...args);

@ -14,7 +14,7 @@ import Slot from '../Slot';
import Text from '../Text';
import Title from '../Title';
import Window from '../Window';
import { Node } from '../../../interfaces';
import { TemplateNode } from '../../../interfaces';
export type Children = ReturnType<typeof map_children>;
@ -40,7 +40,7 @@ function get_constructor(type) {
}
}
export default function map_children(component, parent, scope, children: Node[]) {
export default function map_children(component, parent, scope, children: TemplateNode[]) {
let last = null;
let ignores = [];

@ -1,7 +1,7 @@
import Renderer from './Renderer';
import Wrapper from './wrappers/shared/Wrapper';
import { b, x } from 'code-red';
import { Node, Identifier } from '../../interfaces';
import { Node, Identifier } from 'estree';
import { is_head } from './wrappers/shared/is_head';
export interface BlockOptions {
@ -14,9 +14,9 @@ export interface BlockOptions {
bindings?: Map<string, {
object: Identifier;
property: Identifier;
snippet: string;
snippet: Node;
store: string;
tail: string;
tail: Node;
}>;
dependencies?: Set<string>;
}
@ -38,24 +38,24 @@ export default class Block {
bindings: Map<string, {
object: Identifier;
property: Identifier;
snippet: string;
snippet: Node;
store: string;
tail: string
tail: Node;
}>;
chunks: {
init: Node[];
create: Node[];
claim: Node[];
hydrate: Node[];
mount: Node[];
measure: Node[];
fix: Node[];
animate: Node[];
intro: Node[];
update: Node[];
outro: Node[];
destroy: Node[];
init: (Node | Node[])[];
create: (Node | Node[])[];
claim: (Node | Node[])[];
hydrate: (Node | Node[])[];
mount: (Node | Node[])[];
measure: (Node | Node[])[];
fix: (Node | Node[])[];
animate: (Node | Node[])[];
intro: (Node | Node[])[];
update: (Node | Node[])[];
outro: (Node | Node[])[];
destroy: (Node | Node[])[];
};
event_listeners: Node[] = [];

@ -1,16 +1,17 @@
import Block from './Block';
import { CompileOptions, Node, Identifier } from '../../interfaces';
import { CompileOptions } from '../../interfaces';
import Component from '../Component';
import FragmentWrapper from './wrappers/Fragment';
import { x } from 'code-red';
import { Node, Identifier } from 'estree';
export default class Renderer {
component: Component; // TODO Maybe Renderer shouldn't know about Component?
options: CompileOptions;
blocks: Array<Block | string> = [];
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Node[] = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
meta_bindings: (Node | Node[])[] = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
binding_groups: string[] = [];
block: Block;
@ -49,14 +50,15 @@ export default class Renderer {
null
);
// TODO messy
this.blocks.forEach(block => {
if (typeof block !== 'string') {
if (block instanceof Block) {
block.assign_variable_names();
}
});
this.block.assign_variable_names();
this.fragment.render(this.block, null, x`nodes` as unknown as Identifier);
this.fragment.render(this.block, null, x`nodes` as Identifier);
}
}

@ -8,6 +8,7 @@ import add_to_set from '../utils/add_to_set';
import { extract_names } from '../utils/scope';
import { invalidate } from '../utils/invalidate';
import Block from './Block';
import { ClassDeclaration, FunctionExpression } from 'estree';
export default function dom(
component: Component,
@ -322,26 +323,26 @@ export default function dom(
const fixed_reactive_declarations = []; // not really 'reactive' but whatever
component.reactive_declarations
.forEach(d => {
const dependencies = Array.from(d.dependencies);
const uses_props = !!dependencies.find(n => n === '$$props');
const condition = !uses_props && dependencies
.filter(n => {
const variable = component.var_lookup.get(n);
return variable && (variable.writable || variable.mutated);
})
.map(n => `$$dirty.${n}`).join(' || ');
.forEach(_d => {
// const dependencies = Array.from(d.dependencies);
// const uses_props = !!dependencies.find(n => n === '$$props');
throw new Error(`bad`);
let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`;
if (condition) snippet = `if (${condition}) { ${snippet} }`;
// const condition = !uses_props && dependencies
// .filter(n => {
// const variable = component.var_lookup.get(n);
// return variable && (variable.writable || variable.mutated);
// })
// .map(n => `$$dirty.${n}`).join(' || ');
if (condition || uses_props) {
reactive_declarations.push(snippet);
} else {
fixed_reactive_declarations.push(snippet);
}
throw new Error(`bad`);
// let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`;
// if (condition) snippet = `if (${condition}) { ${snippet} }`;
// if (condition || uses_props) {
// reactive_declarations.push(snippet);
// } else {
// fixed_reactive_declarations.push(snippet);
// }
});
const injected = Array.from(component.injected_reactive_declaration_vars).filter(name => {
@ -396,7 +397,7 @@ export default function dom(
${unknown_props_check}
${component.slots.size && `let { $$slots = {}, $$scope } = $$props;`}
${component.slots.size && x`let { $$slots = {}, $$scope } = $$props;`}
${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`}
@ -404,9 +405,9 @@ export default function dom(
${set && b`$$self.$set = ${set};`}
${capture_state && `$$self.$capture_state = ${capture_state};`}
${capture_state && x`$$self.$capture_state = ${capture_state};`}
${inject_state && `$$self.$inject_state = ${inject_state};`}
${inject_state && x`$$self.$inject_state = ${inject_state};`}
${injected.length && `let ${injected.join(', ')};`}
@ -455,17 +456,18 @@ export default function dom(
}
}
}
`[0];
`[0] as ClassDeclaration;
if (props.length > 0) {
declaration.body.body.push({
type: 'MethodDefinition',
kind: 'get',
static: true,
computed: false,
key: { type: 'Identifier', name: 'observedAttributes' },
value: x`function() {
return [${props.map(prop => x`"${prop.export_name}"`)}];
}`
}` as FunctionExpression
})
}
@ -495,7 +497,7 @@ export default function dom(
${dev_props_check}
}
}
`[0];
`[0] as ClassDeclaration;
declaration.body.body.push(...accessors);

@ -8,8 +8,8 @@ import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
import { Identifier } from '../../../interfaces';
import { changed } from './shared/changed';
import { Identifier } from 'estree';
class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
@ -234,7 +234,7 @@ export default class AwaitBlockWrapper extends Wrapper {
`);
[this.pending, this.then, this.catch].forEach(branch => {
branch.fragment.render(branch.block, null, x`nodes` as unknown as Identifier);
branch.fragment.render(branch.block, null, x`nodes` as Identifier);
});
}
}

@ -2,7 +2,7 @@ import Block from '../Block';
import Wrapper from './shared/Wrapper';
import { b } from 'code-red';
import Body from '../../nodes/Body';
import { Identifier } from '../../../interfaces';
import { Identifier } from 'estree';
export default class BodyWrapper extends Wrapper {
node: Body;

@ -4,7 +4,7 @@ import Block from '../Block';
import DebugTag from '../../nodes/DebugTag';
// import add_to_set from '../../utils/add_to_set';
import { b } from 'code-red';
import { Identifier } from '../../../interfaces';
import { Identifier } from 'estree';
export default class DebugTagWrapper extends Wrapper {
node: DebugTag;

@ -6,8 +6,7 @@ import EachBlock from '../../nodes/EachBlock';
import FragmentWrapper from './Fragment';
import { b, x } from 'code-red';
import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
import { Identifier, Node } from '../../../interfaces';
import { Identifier, Node } from 'estree';
export class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
@ -61,7 +60,7 @@ export default class EachBlockWrapper extends Wrapper {
view_length: string;
}
context_props: string[];
context_props: (Node | Node[])[];
index_name: Identifier;
var: Identifier = { type: 'Identifier', name: 'each' };
@ -141,9 +140,9 @@ export default class EachBlockWrapper extends Wrapper {
this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value,
property: this.index_name,
snippet: attach_head(`${this.vars.each_block_value}[${this.index_name}]`, prop.tail),
snippet: prop.modifier(x`${this.vars.each_block_value}[${this.index_name}]` as Node),
store,
tail: attach_head(`[${this.index_name}]`, prop.tail)
tail: prop.modifier(x`[${this.index_name}]` as Node)
});
});
@ -189,7 +188,7 @@ export default class EachBlockWrapper extends Wrapper {
? !this.next.is_dom_node() :
!parent_node || !this.parent.is_dom_node();
this.context_props = this.node.contexts.map(prop => b`child_ctx.${prop.key.name} = ${attach_head('list[i]', prop.tail)};`);
this.context_props = this.node.contexts.map(prop => b`child_ctx.${prop.key.name} = ${prop.modifier(x`list[i]`)};`);
if (this.node.has_binding) this.context_props.push(b`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(b`child_ctx.${this.index_name} = i;`);
@ -243,7 +242,7 @@ export default class EachBlockWrapper extends Wrapper {
update_anchor_node as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node as unknown as Node
parent_node
);
}
@ -299,10 +298,10 @@ export default class EachBlockWrapper extends Wrapper {
`);
}
this.fragment.render(this.block, null, x`nodes` as unknown as Identifier);
this.fragment.render(this.block, null, x`nodes` as Identifier);
if (this.else) {
this.else.fragment.render(this.else.block, null, x`nodes` as unknown as Identifier);
this.else.fragment.render(this.else.block, null, x`nodes` as Identifier);
}
}

@ -3,12 +3,11 @@ import Binding from '../../../nodes/Binding';
import ElementWrapper from '../Element';
import get_object from '../../../utils/get_object';
import Block from '../../Block';
import Node from '../../../nodes/shared/Node';
import Renderer from '../../Renderer';
import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { Node as INode, Identifier } from '../../../../interfaces';
import { changed } from '../shared/changed';
import { Node, Identifier } from 'estree';
export default class BindingWrapper {
node: Binding;
@ -17,11 +16,11 @@ export default class BindingWrapper {
object: string;
handler: {
uses_context: boolean;
mutation: Node;
mutation: (Node | Node[]);
contextual_dependencies: Set<string>;
snippet?: Node;
};
snippet: INode;
snippet: Node;
is_readonly: boolean;
needs_lock: boolean;
@ -157,17 +156,24 @@ export default class BindingWrapper {
}
if (update_dom) {
block.chunks.update.push(
update_conditions.length
? b`if (${update_conditions.join(' && ')}) {
if (update_conditions.length > 0) {
const condition = update_conditions.reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
block.chunks.update.push(b`
if (${condition}) {
${update_dom}
}`
: update_dom
);
}
`);
} else {
block.chunks.update.push(update_dom);
}
}
if (this.node.name === 'innerHTML' || this.node.name === 'textContent') {
block.chunks.mount.push(b`if (${this.snippet} !== void 0) ${update_dom}`);
block.chunks.mount.push(b`
if (${this.snippet} !== void 0) {
${update_dom}
}`);
} else if (!/(currentTime|paused)/.test(this.node.name)) {
block.chunks.mount.push(update_dom);
}
@ -228,8 +234,8 @@ function get_binding_group(renderer: Renderer, value: Node) {
function mutate_store(store, value, tail) {
return tail
? `${store}.update($$value => ($$value${tail} = ${value}, $$value));`
: `${store}.set(${value});`;
? b`${store}.update($$value => ($$value${tail} = ${value}, $$value));`
: b`${store}.set(${value});`;
}
function get_event_handler(
@ -240,7 +246,7 @@ function get_event_handler(
snippet: Node
): {
uses_context: boolean;
mutation: Node;
mutation: (Node | Node[]);
contextual_dependencies: Set<string>;
snippet?: Node;
} {
@ -260,14 +266,14 @@ function get_event_handler(
if (binding.store) {
store = binding.store;
tail = `${binding.tail}${tail}`;
tail = x`${binding.tail}.${tail}`;
}
return {
uses_context: true,
mutation: store
? mutate_store(store, value, tail)
: b`${snippet}${tail} = ${value};`,
: b`${snippet}.${tail} = ${value};`,
contextual_dependencies: new Set([object.name, property.name])
};
}

@ -22,7 +22,7 @@ import { get_context_merger } from '../shared/get_context_merger';
import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed';
import { is_head } from '../shared/is_head';
import { Identifier } from '../../../../interfaces';
import { Identifier } from 'estree';
const events = [
{
@ -469,7 +469,7 @@ export default class ElementWrapper extends Wrapper {
} else {
block.chunks.init.push(b`
function ${handler}() {
${needs_lock && `${lock} = true;`}
${needs_lock && b`${lock} = true;`}
#ctx.${handler}.call(${this.var}, ${contextual_dependencies.size > 0 ? '#ctx' : null});
}
`);
@ -508,8 +508,8 @@ export default class ElementWrapper extends Wrapper {
});
const some_initial_state_is_undefined = group.bindings
.map(binding => `${binding.snippet} === void 0`)
.join(' || ');
.map(binding => x`${binding.snippet} === void 0`)
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
const should_initialise = (
this.node.name === 'select' ||
@ -524,7 +524,7 @@ export default class ElementWrapper extends Wrapper {
);
if (should_initialise) {
const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`;
const callback = has_local_function ? handler : x`() => ${callee}.call(${this.var})`;
block.chunks.hydrate.push(
b`if (${some_initial_state_is_undefined}) @add_render_callback(${callback});`
);

@ -8,9 +8,8 @@ import ElseBlock from '../../nodes/ElseBlock';
import FragmentWrapper from './Fragment';
import { b, x } from 'code-red';
import { walk } from 'estree-walker';
import { Identifier } from '../../../interfaces';
import Node from '../../nodes/shared/Node';
import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree';
function is_else_if(node: ElseBlock) {
return (
@ -230,7 +229,7 @@ export default class IfBlockWrapper extends Wrapper {
anchor as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node as unknown as Node
parent_node
);
}

@ -14,8 +14,8 @@ import EachBlock from '../../../nodes/EachBlock';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this';
import { Identifier } from '../../../../interfaces';
import { changed } from '../shared/changed';
import { Node, Identifier } from 'estree';
export default class InlineComponentWrapper extends Wrapper {
var: Identifier;
@ -116,8 +116,8 @@ export default class InlineComponentWrapper extends Wrapper {
const component_opts: any = x`{}`;
const statements: string[] = [];
const updates: string[] = [];
const statements: (Node | Node[])[] = [];
const updates: (Node | Node[])[] = [];
let props;
const name_changes = block.get_unique_name(`${name.name}_changes`);
@ -301,7 +301,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
if (non_let_dependencies.length > 0) {
updates.push(`if (${changed(non_let_dependencies)} ${name_changes}.$$scope = { changed: #changed, ctx: #ctx };`);
updates.push(b`if (${changed(non_let_dependencies)} ${name_changes}.$$scope = { changed: #changed, ctx: #ctx };`);
}
const munged_bindings = this.node.bindings.map(binding => {
@ -339,7 +339,7 @@ export default class InlineComponentWrapper extends Wrapper {
const contextual_dependencies = Array.from(binding.expression.contextual_dependencies);
const dependencies = Array.from(binding.expression.dependencies);
let lhs = component.source.slice(binding.expression.node.start, binding.expression.node.end).trim();
let lhs = binding.expression.node;
if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
// bind:x={y} — we can't just do `y = x`, we need to

@ -4,8 +4,8 @@ import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Identifier, Node } from '../../../interfaces';
import { x } from 'code-red';
import { Identifier } from 'estree';
export default class MustacheTagWrapper extends Tag {
var: Identifier = { type: 'Identifier', name: 't' };
@ -25,7 +25,7 @@ export default class MustacheTagWrapper extends Tag {
this.var,
x`@text(${init})`,
parent_nodes && x`@claim_text(${parent_nodes}, ${init})`,
parent_node as unknown as Node
parent_node
);
}
}

@ -5,8 +5,8 @@ import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Identifier, Node } from '../../../interfaces';
import { is_head } from './shared/is_head';
import { Identifier } from 'estree';
export default class RawMustacheTagWrapper extends Tag {
var: Identifier = { type: 'Identifier', name: 'raw' };
@ -56,7 +56,7 @@ export default class RawMustacheTagWrapper extends Tag {
block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}, ${parent_node ? null : 'anchor'});`);
if (needs_anchor) {
block.add_element(html_anchor, x`@empty()`, x`@empty()`, parent_node as unknown as Node);
block.add_element(html_anchor, x`@empty()`, x`@empty()`, parent_node);
}
if (!parent_node || in_head) {

@ -10,7 +10,7 @@ import get_slot_data from '../../utils/get_slot_data';
import { stringify_props } from '../../utils/stringify_props';
import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic';
import { Identifier } from '../../../interfaces';
import { Identifier } from 'estree';
export default class SlotWrapper extends Wrapper {
node: Slot;

@ -2,9 +2,8 @@ import Renderer from '../Renderer';
import Block from '../Block';
import Text from '../../nodes/Text';
import Wrapper from './shared/Wrapper';
import { Identifier } from '../../../interfaces';
import { x } from 'code-red';
import Node from '../../nodes/shared/Node';
import { Identifier } from 'estree';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
@ -74,7 +73,7 @@ export default class TextWrapper extends Wrapper {
this.var,
use_space ? x`@space()` : x`@text("${this.data}")`,
parent_nodes && (use_space ? x`@claim_space(${parent_nodes})` : x`@claim_text(${parent_nodes}, "${this.data}")`),
parent_node as unknown as Node
parent_node as Identifier
);
}
}

@ -6,7 +6,7 @@ import Title from '../../nodes/Title';
import { stringify } from '../../utils/stringify';
import add_to_set from '../../utils/add_to_set';
import Text from '../../nodes/Text';
import { Identifier } from '../../../interfaces';
import { Identifier } from 'estree';
export default class TitleWrapper extends Wrapper {
node: Title;

@ -5,9 +5,9 @@ import { b, x } from 'code-red';
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';
import { changed } from './shared/changed';
import { Identifier } from '../../../interfaces';
import { Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';
const associated_events = {
innerWidth: 'resize',
@ -35,7 +35,7 @@ const readonly = new Set([
export default class WindowWrapper extends Wrapper {
node: Window;
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: INode) {
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
super(renderer, block, parent, node);
}

@ -4,7 +4,7 @@ import Renderer from '../../Renderer';
import Block from '../../Block';
import MustacheTag from '../../../nodes/MustacheTag';
import RawMustacheTag from '../../../nodes/RawMustacheTag';
import { Node } from '../../../../interfaces';
import { Node } from 'estree';
export default class Tag extends Wrapper {
node: MustacheTag | RawMustacheTag;
@ -18,7 +18,7 @@ export default class Tag extends Wrapper {
rename_this_method(
block: Block,
update: ((value: Node) => Node)
update: ((value: Node) => (Node | Node[]))
) {
const dependencies = this.node.expression.dynamic_dependencies();
const snippet = this.node.expression.manipulate(block);

@ -1,13 +1,13 @@
import Renderer from '../../Renderer';
import Block from '../../Block';
import { INode } from '../../../nodes/interfaces';
import { Identifier, Node } from '../../../../interfaces';
import { x } from 'code-red';
import { TemplateNode } from '../../../../interfaces';
import { Identifier } from 'estree';
export default class Wrapper {
renderer: Renderer;
parent: Wrapper;
node: INode;
node: TemplateNode;
prev: Wrapper | null;
next: Wrapper | null;
@ -19,7 +19,7 @@ export default class Wrapper {
renderer: Renderer,
block: Block,
parent: Wrapper,
node: INode
node: TemplateNode
) {
this.node = node;
@ -57,7 +57,7 @@ export default class Wrapper {
anchor,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node as unknown as Node
parent_node as Identifier
);
}

@ -3,7 +3,7 @@ import { b, x } from 'code-red';
import Component from '../../../Component';
import Block from '../../Block';
import Binding from '../../../nodes/Binding';
import { Identifier } from '../../../../interfaces';
import { Identifier } from 'estree';
export default function bind_this(component: Component, block: Block, binding: Binding, variable: Identifier) {
const fn = component.get_unique_name(`${variable.name}_binding`);
@ -81,7 +81,7 @@ export default function bind_this(component: Component, block: Block, binding: B
);
block.chunks.destroy.push(b`${unassign}();`);
return `${assign}();`;
return b`${assign}();`;
}
component.partly_hoisted.push(b`

@ -5,7 +5,7 @@ import EachBlock from '../../nodes/EachBlock';
export default function(node: EachBlock, renderer: Renderer, options: RenderOptions) {
const snippet = snip(node.expression);
const { start, end } = node.context_node;
const { start, end } = node.context_node as any;
const ctx = node.index
? `([✂${start}-${end}✂], ${node.index})`

@ -3,8 +3,7 @@ import Component from '../Component';
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 { INode as TemplateNode } from '../nodes/interfaces'; // TODO
import Text from '../nodes/Text';
export default function ssr(
@ -65,33 +64,33 @@ export default function ssr(
})
: [];
const reactive_declarations = component.reactive_declarations.map(d => {
let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`;
if (d.declaration) {
const declared = extract_names(d.declaration);
const injected = declared.filter(name => {
return name[0] !== '$' && component.var_lookup.get(name).injected;
});
const self_dependencies = injected.filter(name => d.dependencies.has(name));
if (injected.length) {
// in some cases we need to do `let foo; [expression]`, in
// others we can do `let [expression]`
const separate = (
self_dependencies.length > 0 ||
declared.length > injected.length ||
d.node.body.expression.type === 'ParenthesizedExpression'
);
snippet = separate
? `let ${injected.join(', ')}; ${snippet}`
: `let ${snippet}`;
}
}
return snippet;
const reactive_declarations = component.reactive_declarations.map(_d => {
throw new Error('TODO');
// let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`;
// if (d.declaration) {
// const declared = extract_names(d.declaration);
// const injected = declared.filter(name => {
// return name[0] !== '$' && component.var_lookup.get(name).injected;
// });
// const self_dependencies = injected.filter(name => d.dependencies.has(name));
// if (injected.length) {
// // in some cases we need to do `let foo; [expression]`, in
// // others we can do `let [expression]`
// const separate = (
// self_dependencies.length > 0 ||
// declared.length > injected.length
// );
// snippet = separate
// ? `let ${injected.join(', ')}; ${snippet}`
// : `let ${snippet}`;
// }
// }
// return snippet;
});
const main = renderer.has_bindings
@ -153,7 +152,7 @@ export default function ssr(
`;
}
function trim(nodes: INode[]) {
function trim(nodes: TemplateNode[]) {
let start = 0;
for (; start < nodes.length; start += 1) {
const node = nodes[start] as Text;

@ -1,7 +1,11 @@
import { Node } from '../../interfaces';
import { Node, Identifier } from 'estree';
export default function flatten_reference(node: Node) {
if (node.type === 'Expression') throw new Error('bad');
// TODO temporary (#3539)
if ((node as any).type === 'Expression') {
throw new Error('bad');
}
const nodes = [];
const parts = [];
@ -9,7 +13,7 @@ export default function flatten_reference(node: Node) {
nodes.unshift(node.property);
if (!node.computed) {
parts.unshift(node.property.name);
parts.unshift((node.property as Identifier).name);
}
node = node.object;
@ -21,7 +25,7 @@ export default function flatten_reference(node: Node) {
nodes.unshift(node);
if (!node.computed) {
if (!(node as any).computed) {
parts.unshift(name);
}

@ -1,8 +1,6 @@
import { Node } from '../../interfaces';
import unwrap_parens from './unwrap_parens';
import { Node, Identifier } from 'estree';
export default function get_object(node: Node) {
node = unwrap_parens(node);
export default function get_object(node: Node): Identifier {
while (node.type === 'MemberExpression') node = node.object;
return node;
return node as Identifier;
}

@ -1,8 +1,8 @@
import Component from '../Component';
import { Node } from '../../interfaces';
import { nodes_match } from '../../utils/nodes_match';
import { Scope } from './scope';
import { x } from 'code-red';
import { Node } from 'estree';
export function invalidate(component: Component, scope: Scope, node: Node, names: Set<string>) {
const [head, ...tail] = Array.from(names).filter(name => {
@ -27,7 +27,7 @@ export function invalidate(component: Component, scope: Scope, node: Node, names
if (head) {
component.has_reactive_assignments = true;
if (node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return component.invalidate(head);
} else {

@ -1,9 +1,9 @@
import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import { Node } from '../../interfaces';
import { Node as ESTreeNode } from 'estree';
import { Node, VariableDeclaration, ClassDeclaration, VariableDeclarator, ObjectPattern, Property, RestElement, ArrayPattern, Identifier } from 'estree';
import get_object from './get_object';
// TODO replace this with periscopic?
export function create_scopes(expression: Node) {
const map = new WeakMap();
@ -16,7 +16,7 @@ export function create_scopes(expression: Node) {
node.specifiers.forEach(specifier => {
scope.declarations.set(specifier.local.name, specifier);
});
} else if (/Function/.test(node.type)) {
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
if (node.type === 'FunctionDeclaration') {
scope.declarations.set(node.id.name, node);
scope = new Scope(scope, false);
@ -24,7 +24,9 @@ export function create_scopes(expression: Node) {
} else {
scope = new Scope(scope, false);
map.set(node, scope);
if (node.id) scope.declarations.set(node.id.name, node);
if (node.type === 'FunctionExpression' && node.id) {
scope.declarations.set(node.id.name, node);
}
}
node.params.forEach((param) => {
@ -38,7 +40,7 @@ export function create_scopes(expression: Node) {
} else if (node.type === 'BlockStatement') {
scope = new Scope(scope, true);
map.set(node, scope);
} else if (/(Class|Variable)Declaration/.test(node.type)) {
} else if (node.type === 'ClassDeclaration' || node.type === 'VariableDeclaration') {
scope.add_declaration(node);
} else if (node.type === 'CatchClause') {
scope = new Scope(scope, true);
@ -47,7 +49,7 @@ export function create_scopes(expression: Node) {
extract_names(node.param).forEach(name => {
scope.declarations.set(name, node.param);
});
} else if (node.type === 'Identifier' && is_reference(node as ESTreeNode, parent as ESTreeNode)) {
} else if (node.type === 'Identifier' && is_reference(node as Node, parent as Node)) {
if (!scope.has(node.name) && !globals.has(node.name)) {
globals.set(node.name, node);
}
@ -80,16 +82,18 @@ export class Scope {
this.block = block;
}
add_declaration(node: Node) {
add_declaration(node: VariableDeclaration | ClassDeclaration) {
if (node.type === 'VariableDeclaration') {
if (node.kind === 'var' && this.block && this.parent) {
this.parent.add_declaration(node);
} else if (node.type === 'VariableDeclaration') {
node.declarations.forEach((declarator: Node) => {
} else {
node.declarations.forEach((declarator: VariableDeclarator) => {
extract_names(declarator.id).forEach(name => {
this.declarations.set(name, node);
if (declarator.init) this.initialised_declarations.add(name);
});
});
}
} else {
this.declarations.set(node.id.name, node);
}
@ -107,12 +111,12 @@ export class Scope {
}
}
export function extract_names(param: Node) {
return extract_identifiers(param).map(node => node.name);
export function extract_names(param: Node): string[] {
return extract_identifiers(param).map((node: any) => node.name);
}
export function extract_identifiers(param: Node) {
const nodes: Node[] = [];
export function extract_identifiers(param: Node): Identifier[] {
const nodes: Identifier[] = [];
extractors[param.type] && extractors[param.type](nodes, param);
return nodes;
}
@ -126,8 +130,8 @@ const extractors = {
nodes.push(get_object(param));
},
ObjectPattern(nodes: Node[], param: Node) {
param.properties.forEach((prop: Node) => {
ObjectPattern(nodes: Node[], param: ObjectPattern) {
param.properties.forEach((prop: Property | RestElement) => {
if (prop.type === 'RestElement') {
nodes.push(prop.argument);
} else {
@ -136,17 +140,17 @@ const extractors = {
});
},
ArrayPattern(nodes: Node[], param: Node) {
ArrayPattern(nodes: Node[], param: ArrayPattern) {
param.elements.forEach((element: Node) => {
if (element) extractors[element.type](nodes, element);
});
},
RestElement(nodes: Node[], param: Node) {
RestElement(nodes: Node[], param: any) {
extractors[param.argument.type](nodes, param.argument);
},
AssignmentPattern(nodes: Node[], param: Node) {
AssignmentPattern(nodes: Node[], param: any) {
extractors[param.left.type](nodes, param.left);
}
};

@ -1,7 +0,0 @@
export function new_tail(): string {
return '%%tail_head%%';
}
export function attach_head(head: string, tail: string): string {
return tail.replace('%%tail_head%%', head);
}

@ -1,6 +0,0 @@
import { Node } from '../../interfaces';
export default function unwrap_parens(node: Node) {
while (node.type === 'ParenthesizedExpression') node = node.expression;
return node;
}

@ -1,16 +1,18 @@
export interface Identifier {
type: 'Identifier';
name: string;
}
import { Node, Program } from "estree";
interface BaseNode {
start: number;
end: number;
type: string;
children?: Node[];
children?: TemplateNode[];
[prop_name: string]: any;
}
export interface Fragment extends BaseNode {
type: 'Fragment';
children: TemplateNode[];
}
export interface Text extends BaseNode {
type: 'Text';
data: string;
@ -32,7 +34,7 @@ export type DirectiveType = 'Action'
interface BaseDirective extends BaseNode {
type: DirectiveType;
expression: null|Node;
expression: null | Node;
name: string;
modifiers: string[];
}
@ -45,7 +47,7 @@ export interface Transition extends BaseDirective{
export type Directive = BaseDirective | Transition;
export type Node = Text
export type TemplateNode = Text
| MustacheTag
| BaseNode
| Directive
@ -64,11 +66,28 @@ export interface Parser {
meta_tags: {};
}
export interface Script extends BaseNode {
type: 'Script',
context: string;
content: Program;
}
export interface Style extends BaseNode {
type: 'Style';
attributes: any[]; // TODO
children: any[]; // TODO add CSS node types
content: {
start: number;
end: number;
styles: string;
}
}
export interface Ast {
html: Node;
css: Node;
instance: Node;
module: Node;
html: TemplateNode;
css: Style;
instance: Script;
module: Script;
}
export interface Warning {

@ -3,7 +3,7 @@ import fragment from './state/fragment';
import { whitespace } from '../utils/patterns';
import { reserved } from '../utils/names';
import full_char_code_at from '../utils/full_char_code_at';
import { Node, Ast, ParserOptions } from '../interfaces';
import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces';
import error from '../utils/error';
type ParserState = (parser: Parser) => (ParserState | void);
@ -14,11 +14,11 @@ export class Parser {
readonly customElement: boolean;
index = 0;
stack: Node[] = [];
stack: TemplateNode[] = [];
html: Node;
css: Node[] = [];
js: Node[] = [];
html: Fragment;
css: Style[] = [];
js: Script[] = [];
meta_tags = {};
constructor(template: string, options: ParserOptions) {
@ -207,7 +207,7 @@ export default function parse(
): Ast {
const parser = new Parser(template, options);
// TODO we way want to allow multiple <style> tags —
// TODO we may want to allow multiple <style> tags —
// one scoped, one global. for now, only allow one
if (parser.css.length > 1) {
parser.error({

@ -1,11 +1,12 @@
import * as acorn from '../acorn';
import repeat from '../../utils/repeat';
import { Parser } from '../index';
import { Node } from '../../interfaces';
import { Script } from '../../interfaces';
import { Node, Program } from 'estree';
const script_closing_tag = '</script>';
function get_context(parser: Parser, attributes: Node[], start: number) {
function get_context(parser: Parser, attributes: any[], start: number): string {
const context = attributes.find(attribute => attribute.name === 'context');
if (!context) return 'default';
@ -28,7 +29,7 @@ function get_context(parser: Parser, attributes: Node[], start: number) {
return value;
}
export default function read_script(parser: Parser, start: number, attributes: Node[]) {
export default function read_script(parser: Parser, start: number, attributes: Node[]) : Script {
const script_start = parser.index;
const script_end = parser.template.indexOf(script_closing_tag, script_start);
@ -41,7 +42,7 @@ export default function read_script(parser: Parser, start: number, attributes: N
repeat(' ', script_start) + parser.template.slice(script_start, script_end);
parser.index = script_end + script_closing_tag.length;
let ast;
let ast: Program;
try {
ast = acorn.parse(source);
@ -49,8 +50,11 @@ export default function read_script(parser: Parser, start: number, attributes: N
parser.acorn_error(err);
}
ast.start = script_start;
// TODO is this necessary?
(ast as any).start = script_start;
return {
type: 'Script',
start,
end: parser.index,
context: get_context(parser, attributes, start),

@ -1,9 +1,10 @@
import parse from 'css-tree/lib/parser/index.js';
import { walk } from 'estree-walker';
import { Parser } from '../index';
import { Node } from '../../interfaces';
import { Node } from 'estree';
import { Style } from '../../interfaces';
export default function read_style(parser: Parser, start: number, attributes: Node[]) {
export default function read_style(parser: Parser, start: number, attributes: Node[]): Style {
const content_start = parser.index;
const styles = parser.read_until(/<\/style>/);
const content_end = parser.index;
@ -30,7 +31,7 @@ export default function read_style(parser: Parser, start: number, attributes: No
// tidy up AST
walk(ast, {
enter: (node: Node) => {
enter: (node: any) => { // `any` because this isn't an ESTree node
// replace `ref:a` nodes
if (node.type === 'Selector') {
for (let i = 0; i < node.children.length; i += 1) {
@ -58,6 +59,7 @@ export default function read_style(parser: Parser, start: number, attributes: No
const end = parser.index;
return {
type: 'Style',
start,
end,
attributes,
@ -65,12 +67,12 @@ export default function read_style(parser: Parser, start: number, attributes: No
content: {
start: content_start,
end: content_end,
styles,
},
styles
}
};
}
function is_ref_selector(a: Node, b: Node) {
function is_ref_selector(a: any, b: any) { // TODO add CSS node types
if (!b) return false;
return (

@ -4,9 +4,9 @@ import { closing_tag_omitted } from '../utils/html';
import { whitespace } from '../../utils/patterns';
import { trim_start, trim_end } from '../../utils/trim';
import { Parser } from '../index';
import { Node } from '../../interfaces';
import { TemplateNode } from '../../interfaces';
function trim_whitespace(block: Node, trim_before: boolean, trim_after: boolean) {
function trim_whitespace(block: TemplateNode, trim_before: boolean, trim_after: boolean) {
if (!block.children || block.children.length === 0) return; // AwaitBlock
const first_child = block.children[0];
@ -175,7 +175,7 @@ export default function mustache(parser: Parser) {
parser.eat('}', true);
}
const then_block: Node = {
const then_block: TemplateNode = {
start,
end: null,
type: 'ThenBlock',
@ -200,7 +200,7 @@ export default function mustache(parser: Parser) {
parser.eat('}', true);
}
const catch_block: Node = {
const catch_block: TemplateNode = {
start,
end: null,
type: 'CatchBlock',
@ -232,7 +232,7 @@ export default function mustache(parser: Parser) {
const expression = read_expression(parser);
const block: Node = type === 'AwaitBlock' ?
const block: TemplateNode = type === 'AwaitBlock' ?
{
start,
end: null,

@ -4,7 +4,7 @@ import read_style from '../read/style';
import { decode_character_references, closing_tag_omitted } from '../utils/html';
import { is_void } from '../../utils/names';
import { Parser } from '../index';
import { Directive, DirectiveType, Node, Text } from '../../interfaces';
import { Directive, DirectiveType, TemplateNode, Text } from '../../interfaces';
import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
@ -112,7 +112,7 @@ export default function tag(parser: Parser) {
: name === 'title' && parent_is_head(parser.stack) ? 'Title'
: name === 'slot' && !parser.customElement ? 'Slot' : 'Element';
const element: Node = {
const element: TemplateNode = {
start,
end: null, // filled in later
type,
@ -406,7 +406,7 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
end: directive.end,
type: 'Identifier',
name: directive.name
};
} as any;
}
return directive;
@ -447,7 +447,7 @@ function read_attribute_value(parser: Parser) {
return value;
}
function read_sequence(parser: Parser, done: () => boolean): Node[] {
function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] {
let current_chunk: Text = {
start: parser.index,
end: null,
@ -464,7 +464,7 @@ function read_sequence(parser: Parser, done: () => boolean): Node[] {
}
}
const chunks: Node[] = [];
const chunks: TemplateNode[] = [];
while (parser.index < parser.template.length) {
const index = parser.index;

Loading…
Cancel
Save