fix compile/nodes typings

pull/2838/head
Bogdan Savluk 5 years ago
parent 6fdaa803c7
commit b7ec99e8c7

@ -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;

@ -5,6 +5,7 @@ import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
export default class Binding extends Node {
type: 'Binding';
name: string;
expression: Expression;
is_contextual: boolean;

@ -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) {
@ -11,4 +12,4 @@ export default class DebugTag extends Node {
return new Expression(component, parent, scope, node);
});
}
}
}

@ -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;
}
@ -249,11 +250,11 @@ export default class Element extends Node {
if (this.name === 'figure') {
const children = this.children.filter(node => {
if (node.type === 'Comment') return false;
if (node.type === 'Text') return /\S/.test(node.data);
if (node.type === 'Text') return /\S/.test((node as Text).data );
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`,
@ -387,7 +391,7 @@ export default class Element extends Node {
let ancestor = this.parent;
do {
if (ancestor.type === 'InlineComponent') break;
if (ancestor.type === 'Element' && /-/.test(ancestor.name)) break;
if (ancestor.type === 'Element' && /-/.test((ancestor as Element).name)) break;
if (ancestor.type === 'IfBlock' || ancestor.type === 'EachBlock') {
const type = ancestor.type === 'IfBlock' ? 'if' : 'each';

@ -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;
@ -65,4 +66,4 @@ export default class EventHandler extends Node {
// this.component.add_reference(this.handler_name);
return `ctx.${this.handler_name}`;
}
}
}

@ -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) {
@ -16,4 +18,4 @@ export default class Fragment extends Node {
this.scope = scope;
this.children = map_children(component, this, scope, info.children);
}
}
}

@ -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 => {
@ -68,4 +68,4 @@ export default class Slot extends Element {
component.slots.set(this.slot_name, this);
}
}
}

@ -1,20 +1,22 @@
import Node from './shared/Node';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
import Element from './Element';
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;
if (!component.component_options.preserveWhitespace && !/\S/.test(info.data)) {
let node = parent;
while (node) {
if (node.type === 'Element' && node.name === 'pre') {
if (node.type === 'Element' && (node as Element).name === 'pre') {
return;
}
node = node.parent;
@ -23,4 +25,4 @@ export default class Text extends Node {
this.use_space = true;
}
}
}
}

@ -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,14 @@
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
should_cache: boolean;
children: Children;
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);
@ -33,4 +35,4 @@ export default class Title extends Node {
)
: true;
}
}
}

@ -0,0 +1,62 @@
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';
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;
@ -510,4 +515,4 @@ function is_contextual(component: Component, scope: TemplateScope, name: string)
// assume contextual
return true;
}
}

@ -1,21 +1,23 @@
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;
attributes: Attribute[];
constructor(component: Component, parent, scope, info: any) {
constructor(component: Component, parent: any, scope: any, info: { start: number; end: number; type: string; }) {
this.start = info.start;
this.end = info.end;
this.type = info.type;
@ -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;
@ -14,4 +15,4 @@ export default class Tag extends Node {
(this.expression.dependencies.size && scope.names.has(info.expression.name))
);
}
}
}

@ -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;
@ -41,4 +42,4 @@ export default class TemplateScope {
const owner = this.get_owner(name);
return owner && (owner.type === 'Element' || owner.type === 'InlineComponent');
}
}
}

@ -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);

@ -1,5 +1,6 @@
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);
});
}
}

@ -9,7 +9,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 +25,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);
});

@ -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;'));
}
@ -15,4 +14,4 @@ export function stringify_attribute(attribute: Attribute, is_ssr: boolean) {
: '${' + snip(chunk) + '}';
})
.join('');
}
}

Loading…
Cancel
Save