diff --git a/src/compile/Component.ts b/src/compile/Component.ts index 05bab19cc5..4a249d2287 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -5,7 +5,6 @@ import { walk, childKeys } from 'estree-walker'; import { getLocator } from 'locate-character'; import Stats from '../Stats'; import deindent from '../utils/deindent'; -import CodeBuilder from '../utils/CodeBuilder'; import reservedNames from '../utils/reservedNames'; import namespaces from '../utils/namespaces'; import { removeNode } from '../utils/removeNode'; @@ -13,13 +12,11 @@ import nodeToString from '../utils/nodeToString'; import wrapModule from './wrapModule'; import annotateWithScopes from '../utils/annotateWithScopes'; import getName from '../utils/getName'; -import Stylesheet from '../css/Stylesheet'; +import Stylesheet from './css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; import shared from './shared'; -import { DomTarget } from './dom'; -import { SsrTarget } from './ssr'; -import { Node, GenerateOptions, ShorthandImport, Ast, CompileOptions, CustomElementOptions } from '../interfaces'; +import { Node, ShorthandImport, Ast, CompileOptions, CustomElementOptions } from '../interfaces'; import error from '../utils/error'; import getCodeFrame from '../utils/getCodeFrame'; import checkForComputedKeys from './validate/js/utils/checkForComputedKeys'; @@ -101,7 +98,6 @@ export default class Component { name: string; options: CompileOptions; fragment: Fragment; - target: DomTarget | SsrTarget; customElement: CustomElementOptions; tag: string; @@ -124,7 +120,6 @@ export default class Component { hasComponents: boolean; computations: Computation[]; templateProperties: Record; - slots: Set; javascript: [string, string]; used: { @@ -142,13 +137,11 @@ export default class Component { code: MagicString; - bindingGroups: string[]; indirectDependencies: Map>; expectedProperties: Set; refs: Set; file: string; - fileVar: string; locate: (c: number) => { line: number, column: number }; stylesheet: Stylesheet; @@ -168,15 +161,13 @@ export default class Component { source: string, name: string, options: CompileOptions, - stats: Stats, - target: DomTarget | SsrTarget + stats: Stats ) { this.stats = stats; this.ast = ast; this.source = source; this.options = options; - this.target = target; this.imports = []; this.shorthandImports = []; @@ -188,7 +179,6 @@ export default class Component { this.transitions = new Set(); this.actions = new Set(); this.importedComponents = new Map(); - this.slots = new Set(); this.used = { components: new Set(), @@ -204,7 +194,6 @@ export default class Component { this.refs = new Set(); this.refCallees = []; - this.bindingGroups = []; this.indirectDependencies = new Map(); this.file = options.filename && ( @@ -229,8 +218,6 @@ export default class Component { this.aliases = new Map(); this.usedNames = new Set(); - this.fileVar = options.dev && this.getUniqueName('file'); - this.computations = []; this.templateProperties = {}; this.properties = new Map(); @@ -319,7 +306,11 @@ export default class Component { return this.aliases.get(name); } - generate(result: string, options: CompileOptions, { banner = '', name, format }: GenerateOptions ) { + generate(result: string, options: CompileOptions, { + banner = '', + name, + format + }) { const pattern = /\[✂(\d+)-(\d+)$/; const helpers = new Set(); diff --git a/src/css/Selector.ts b/src/compile/css/Selector.ts similarity index 100% rename from src/css/Selector.ts rename to src/compile/css/Selector.ts diff --git a/src/css/Stylesheet.ts b/src/compile/css/Stylesheet.ts similarity index 97% rename from src/css/Stylesheet.ts rename to src/compile/css/Stylesheet.ts index 3edb6fd856..cf7fcf061e 100644 --- a/src/css/Stylesheet.ts +++ b/src/compile/css/Stylesheet.ts @@ -2,12 +2,12 @@ import MagicString from 'magic-string'; import { walk } from 'estree-walker'; import { getLocator } from 'locate-character'; import Selector from './Selector'; -import getCodeFrame from '../utils/getCodeFrame'; -import hash from '../utils/hash'; -import removeCSSPrefix from '../utils/removeCSSPrefix'; -import Element from '../compile/nodes/Element'; -import { Node, Ast, Warning } from '../interfaces'; -import Component from '../compile/Component'; +import getCodeFrame from '../../utils/getCodeFrame'; +import hash from '../../utils/hash'; +import removeCSSPrefix from '../../utils/removeCSSPrefix'; +import Element from '../nodes/Element'; +import { Node, Ast, Warning } from '../../interfaces'; +import Component from '../Component'; const isKeyframesNode = (node: Node) => removeCSSPrefix(node.name) === 'keyframes' diff --git a/src/css/gatherPossibleValues.ts b/src/compile/css/gatherPossibleValues.ts similarity index 100% rename from src/css/gatherPossibleValues.ts rename to src/compile/css/gatherPossibleValues.ts diff --git a/src/compile/index.ts b/src/compile/index.ts index ccf917772b..239d09d2de 100644 --- a/src/compile/index.ts +++ b/src/compile/index.ts @@ -1,13 +1,13 @@ import { assign } from '../shared'; import Stats from '../Stats'; import parse from '../parse/index'; -import generate, { DomTarget } from './dom/index'; -import generateSSR, { SsrTarget } from './ssr/index'; +import renderDOM from './render-dom/index'; +import renderSSR from './render-ssr/index'; import { CompileOptions, Warning, Ast } from '../interfaces'; import Component from './Component'; function normalize_options(options: CompileOptions): CompileOptions { - let normalized = assign({ generate: 'dom' }, options); + let normalized = assign({ generate: 'dom', dev: false }, options); const { onwarn, onerror } = normalized; normalized.onwarn = onwarn @@ -74,10 +74,7 @@ export default function compile(source: string, options: CompileOptions) { source, options.name || 'SvelteComponent', options, - stats, - - // TODO make component generator-agnostic, to allow e.g. WebGL generator - options.generate === 'ssr' ? new SsrTarget() : new DomTarget() + stats ); stats.stop('create component'); @@ -85,9 +82,11 @@ export default function compile(source: string, options: CompileOptions) { return { ast, stats: stats.render(null), js: null, css: null }; } - const compiler = options.generate === 'ssr' ? generateSSR : generate; + if (options.generate === 'ssr') { + return renderSSR(component, options); + } - return compiler(component, options); + return renderDOM(component, options); } catch (err) { options.onerror(err); return; diff --git a/src/compile/nodes/Attribute.ts b/src/compile/nodes/Attribute.ts index caf5f7ed2d..83c845176d 100644 --- a/src/compile/nodes/Attribute.ts +++ b/src/compile/nodes/Attribute.ts @@ -1,19 +1,11 @@ -import deindent from '../../utils/deindent'; import { escape, escapeTemplate, stringify } from '../../utils/stringify'; -import fixAttributeCasing from '../../utils/fixAttributeCasing'; import addToSet from '../../utils/addToSet'; import Component from '../Component'; import Node from './shared/Node'; import Element from './Element'; import Text from './Text'; -import Block from '../dom/Block'; import Expression from './shared/Expression'; -export interface StyleProp { - key: string; - value: Node[]; -} - export default class Attribute extends Node { type: 'Attribute'; start: number; @@ -108,237 +100,6 @@ export default class Attribute extends Node { : ''; } - render(block: Block) { - const node = this.parent; - const name = fixAttributeCasing(this.name); - - if (name === 'style') { - const styleProps = optimizeStyle(this.chunks); - if (styleProps) { - this.renderStyle(block, styleProps); - return; - } - } - - let metadata = node.namespace ? null : attributeLookup[name]; - if (metadata && metadata.appliesTo && !~metadata.appliesTo.indexOf(node.name)) - metadata = null; - - const isIndirectlyBoundValue = - name === 'value' && - (node.name === 'option' || // TODO check it's actually bound - (node.name === 'input' && - node.bindings.find( - (binding: Binding) => - /checked|group/.test(binding.name) - ))); - - const propertyName = isIndirectlyBoundValue - ? '__value' - : metadata && metadata.propertyName; - - // xlink is a special case... we could maybe extend this to generic - // namespaced attributes but I'm not sure that's applicable in - // HTML5? - const method = /-/.test(node.name) - ? '@setCustomElementData' - : name.slice(0, 6) === 'xlink:' - ? '@setXlinkAttribute' - : '@setAttribute'; - - const isLegacyInputType = this.component.options.legacy && name === 'type' && this.parent.name === 'input'; - - const isDataSet = /^data-/.test(name) && !this.component.options.legacy && !node.namespace; - const camelCaseName = isDataSet ? name.replace('data-', '').replace(/(-\w)/g, function (m) { - return m[1].toUpperCase(); - }) : name; - - if (this.isDynamic) { - let value; - - // TODO some of this code is repeated in Tag.ts — would be good to - // DRY it out if that's possible without introducing crazy indirection - if (this.chunks.length === 1) { - // single {tag} — may be a non-string - value = this.chunks[0].snippet; - } else { - // '{foo} {bar}' — treat as string concatenation - value = - (this.chunks[0].type === 'Text' ? '' : `"" + `) + - this.chunks - .map((chunk: Node) => { - if (chunk.type === 'Text') { - return stringify(chunk.data); - } else { - return chunk.getPrecedence() <= 13 - ? `(${chunk.snippet})` - : chunk.snippet; - } - }) - .join(' + '); - } - - const isSelectValueAttribute = - name === 'value' && node.name === 'select'; - - const shouldCache = this.shouldCache || isSelectValueAttribute; - - const last = shouldCache && block.getUniqueName( - `${node.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` - ); - - if (shouldCache) block.addVariable(last); - - let updater; - const init = shouldCache ? `${last} = ${value}` : value; - - if (isLegacyInputType) { - block.builders.hydrate.addLine( - `@setInputType(${node.var}, ${init});` - ); - updater = `@setInputType(${node.var}, ${shouldCache ? last : value});`; - } else if (isSelectValueAttribute) { - // annoying special case - const isMultipleSelect = node.getStaticAttributeValue('multiple'); - const i = block.getUniqueName('i'); - const option = block.getUniqueName('option'); - - const ifStatement = isMultipleSelect - ? deindent` - ${option}.selected = ~${last}.indexOf(${option}.__value);` - : deindent` - if (${option}.__value === ${last}) { - ${option}.selected = true; - break; - }`; - - updater = deindent` - for (var ${i} = 0; ${i} < ${node.var}.options.length; ${i} += 1) { - var ${option} = ${node.var}.options[${i}]; - - ${ifStatement} - } - `; - - block.builders.mount.addBlock(deindent` - ${last} = ${value}; - ${updater} - `); - } else if (propertyName) { - block.builders.hydrate.addLine( - `${node.var}.${propertyName} = ${init};` - ); - updater = `${node.var}.${propertyName} = ${shouldCache ? last : value};`; - } else if (isDataSet) { - block.builders.hydrate.addLine( - `${node.var}.dataset.${camelCaseName} = ${init};` - ); - updater = `${node.var}.dataset.${camelCaseName} = ${shouldCache ? last : value};`; - } else { - block.builders.hydrate.addLine( - `${method}(${node.var}, "${name}", ${init});` - ); - updater = `${method}(${node.var}, "${name}", ${shouldCache ? last : value});`; - } - - if (this.dependencies.size || isSelectValueAttribute) { - const dependencies = Array.from(this.dependencies); - const changedCheck = ( - (block.hasOutros ? `!#current || ` : '') + - dependencies.map(dependency => `changed.${dependency}`).join(' || ') - ); - - const updateCachedValue = `${last} !== (${last} = ${value})`; - - const condition = shouldCache ? - ( dependencies.length ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue ) : - changedCheck; - - block.builders.update.addConditional( - condition, - updater - ); - } - } else { - const value = this.getValue(); - - const statement = ( - isLegacyInputType - ? `@setInputType(${node.var}, ${value});` - : propertyName - ? `${node.var}.${propertyName} = ${value};` - : isDataSet - ? `${node.var}.dataset.${camelCaseName} = ${value};` - : `${method}(${node.var}, "${name}", ${value});` - ); - - block.builders.hydrate.addLine(statement); - - // special case – autofocus. has to be handled in a bit of a weird way - if (this.isTrue && name === 'autofocus') { - block.autofocus = node.var; - } - } - - if (isIndirectlyBoundValue) { - const updateValue = `${node.var}.value = ${node.var}.__value;`; - - block.builders.hydrate.addLine(updateValue); - if (this.isDynamic) block.builders.update.addLine(updateValue); - } - } - - renderStyle( - block: Block, - styleProps: StyleProp[] - ) { - styleProps.forEach((prop: StyleProp) => { - let value; - - if (isDynamic(prop.value)) { - const propDependencies = new Set(); - let shouldCache; - - value = - ((prop.value.length === 1 || prop.value[0].type === 'Text') ? '' : `"" + `) + - prop.value - .map((chunk: Node) => { - if (chunk.type === 'Text') { - return stringify(chunk.data); - } else { - const { dependencies, snippet } = chunk; - - dependencies.forEach(d => { - propDependencies.add(d); - }); - - return chunk.getPrecedence() <= 13 ? `(${snippet})` : snippet; - } - }) - .join(' + '); - - if (propDependencies.size) { - const dependencies = Array.from(propDependencies); - const condition = ( - (block.hasOutros ? `!#current || ` : '') + - dependencies.map(dependency => `changed.${dependency}`).join(' || ') - ); - - block.builders.update.addConditional( - condition, - `@setStyle(${this.parent.var}, "${prop.key}", ${value});` - ); - } - } else { - value = stringify(prop.value[0].data); - } - - block.builders.hydrate.addLine( - `@setStyle(${this.parent.var}, "${prop.key}", ${value});` - ); - }); - } - stringifyForSsr() { return this.chunks .map((chunk: Node) => { @@ -350,353 +111,4 @@ export default class Attribute extends Node { }) .join(''); } -} - -// source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes -const attributeLookup = { - accept: { appliesTo: ['form', 'input'] }, - 'accept-charset': { propertyName: 'acceptCharset', appliesTo: ['form'] }, - accesskey: { propertyName: 'accessKey' }, - action: { appliesTo: ['form'] }, - align: { - appliesTo: [ - 'applet', - 'caption', - 'col', - 'colgroup', - 'hr', - 'iframe', - 'img', - 'table', - 'tbody', - 'td', - 'tfoot', - 'th', - 'thead', - 'tr', - ], - }, - allowfullscreen: { propertyName: 'allowFullscreen', appliesTo: ['iframe'] }, - alt: { appliesTo: ['applet', 'area', 'img', 'input'] }, - async: { appliesTo: ['script'] }, - autocomplete: { appliesTo: ['form', 'input'] }, - autofocus: { appliesTo: ['button', 'input', 'keygen', 'select', 'textarea'] }, - autoplay: { appliesTo: ['audio', 'video'] }, - autosave: { appliesTo: ['input'] }, - bgcolor: { - propertyName: 'bgColor', - appliesTo: [ - 'body', - 'col', - 'colgroup', - 'marquee', - 'table', - 'tbody', - 'tfoot', - 'td', - 'th', - 'tr', - ], - }, - border: { appliesTo: ['img', 'object', 'table'] }, - buffered: { appliesTo: ['audio', 'video'] }, - challenge: { appliesTo: ['keygen'] }, - charset: { appliesTo: ['meta', 'script'] }, - checked: { appliesTo: ['command', 'input'] }, - cite: { appliesTo: ['blockquote', 'del', 'ins', 'q'] }, - class: { propertyName: 'className' }, - code: { appliesTo: ['applet'] }, - codebase: { propertyName: 'codeBase', appliesTo: ['applet'] }, - color: { appliesTo: ['basefont', 'font', 'hr'] }, - cols: { appliesTo: ['textarea'] }, - colspan: { propertyName: 'colSpan', appliesTo: ['td', 'th'] }, - content: { appliesTo: ['meta'] }, - contenteditable: { propertyName: 'contentEditable' }, - contextmenu: {}, - controls: { appliesTo: ['audio', 'video'] }, - coords: { appliesTo: ['area'] }, - data: { appliesTo: ['object'] }, - datetime: { propertyName: 'dateTime', appliesTo: ['del', 'ins', 'time'] }, - default: { appliesTo: ['track'] }, - defer: { appliesTo: ['script'] }, - dir: {}, - dirname: { propertyName: 'dirName', appliesTo: ['input', 'textarea'] }, - disabled: { - appliesTo: [ - 'button', - 'command', - 'fieldset', - 'input', - 'keygen', - 'optgroup', - 'option', - 'select', - 'textarea', - ], - }, - download: { appliesTo: ['a', 'area'] }, - draggable: {}, - dropzone: {}, - enctype: { appliesTo: ['form'] }, - for: { propertyName: 'htmlFor', appliesTo: ['label', 'output'] }, - form: { - appliesTo: [ - 'button', - 'fieldset', - 'input', - 'keygen', - 'label', - 'meter', - 'object', - 'output', - 'progress', - 'select', - 'textarea', - ], - }, - formaction: { appliesTo: ['input', 'button'] }, - headers: { appliesTo: ['td', 'th'] }, - height: { - appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'], - }, - hidden: {}, - high: { appliesTo: ['meter'] }, - href: { appliesTo: ['a', 'area', 'base', 'link'] }, - hreflang: { appliesTo: ['a', 'area', 'link'] }, - 'http-equiv': { propertyName: 'httpEquiv', appliesTo: ['meta'] }, - icon: { appliesTo: ['command'] }, - id: {}, - indeterminate: { appliesTo: ['input'] }, - ismap: { propertyName: 'isMap', appliesTo: ['img'] }, - itemprop: {}, - keytype: { appliesTo: ['keygen'] }, - kind: { appliesTo: ['track'] }, - label: { appliesTo: ['track'] }, - lang: {}, - language: { appliesTo: ['script'] }, - loop: { appliesTo: ['audio', 'bgsound', 'marquee', 'video'] }, - low: { appliesTo: ['meter'] }, - manifest: { appliesTo: ['html'] }, - max: { appliesTo: ['input', 'meter', 'progress'] }, - maxlength: { propertyName: 'maxLength', appliesTo: ['input', 'textarea'] }, - media: { appliesTo: ['a', 'area', 'link', 'source', 'style'] }, - method: { appliesTo: ['form'] }, - min: { appliesTo: ['input', 'meter'] }, - multiple: { appliesTo: ['input', 'select'] }, - muted: { appliesTo: ['audio', 'video'] }, - name: { - appliesTo: [ - 'button', - 'form', - 'fieldset', - 'iframe', - 'input', - 'keygen', - 'object', - 'output', - 'select', - 'textarea', - 'map', - 'meta', - 'param', - ], - }, - novalidate: { propertyName: 'noValidate', appliesTo: ['form'] }, - open: { appliesTo: ['details'] }, - optimum: { appliesTo: ['meter'] }, - pattern: { appliesTo: ['input'] }, - ping: { appliesTo: ['a', 'area'] }, - placeholder: { appliesTo: ['input', 'textarea'] }, - poster: { appliesTo: ['video'] }, - preload: { appliesTo: ['audio', 'video'] }, - radiogroup: { appliesTo: ['command'] }, - readonly: { propertyName: 'readOnly', appliesTo: ['input', 'textarea'] }, - rel: { appliesTo: ['a', 'area', 'link'] }, - required: { appliesTo: ['input', 'select', 'textarea'] }, - reversed: { appliesTo: ['ol'] }, - rows: { appliesTo: ['textarea'] }, - rowspan: { propertyName: 'rowSpan', appliesTo: ['td', 'th'] }, - sandbox: { appliesTo: ['iframe'] }, - scope: { appliesTo: ['th'] }, - scoped: { appliesTo: ['style'] }, - seamless: { appliesTo: ['iframe'] }, - selected: { appliesTo: ['option'] }, - shape: { appliesTo: ['a', 'area'] }, - size: { appliesTo: ['input', 'select'] }, - sizes: { appliesTo: ['link', 'img', 'source'] }, - span: { appliesTo: ['col', 'colgroup'] }, - spellcheck: {}, - src: { - appliesTo: [ - 'audio', - 'embed', - 'iframe', - 'img', - 'input', - 'script', - 'source', - 'track', - 'video', - ], - }, - srcdoc: { appliesTo: ['iframe'] }, - srclang: { appliesTo: ['track'] }, - srcset: { appliesTo: ['img'] }, - start: { appliesTo: ['ol'] }, - step: { appliesTo: ['input'] }, - style: { propertyName: 'style.cssText' }, - summary: { appliesTo: ['table'] }, - tabindex: { propertyName: 'tabIndex' }, - target: { appliesTo: ['a', 'area', 'base', 'form'] }, - title: {}, - type: { - appliesTo: [ - 'button', - 'command', - 'embed', - 'object', - 'script', - 'source', - 'style', - 'menu', - ], - }, - usemap: { propertyName: 'useMap', appliesTo: ['img', 'input', 'object'] }, - value: { - appliesTo: [ - 'button', - 'option', - 'input', - 'li', - 'meter', - 'progress', - 'param', - 'select', - 'textarea', - ], - }, - volume: { appliesTo: ['audio', 'video'] }, - width: { - appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'], - }, - wrap: { appliesTo: ['textarea'] }, -}; - -Object.keys(attributeLookup).forEach(name => { - const metadata = attributeLookup[name]; - if (!metadata.propertyName) metadata.propertyName = name; -}); - -function optimizeStyle(value: Node[]) { - let expectingKey = true; - let i = 0; - - const props: { key: string, value: Node[] }[] = []; - let chunks = value.slice(); - - while (chunks.length) { - const chunk = chunks[0]; - - if (chunk.type !== 'Text') return null; - - const keyMatch = /^\s*([\w-]+):\s*/.exec(chunk.data); - if (!keyMatch) return null; - - const key = keyMatch[1]; - - const offset = keyMatch.index + keyMatch[0].length; - const remainingData = chunk.data.slice(offset); - - if (remainingData) { - chunks[0] = { - start: chunk.start + offset, - end: chunk.end, - type: 'Text', - data: remainingData - }; - } else { - chunks.shift(); - } - - const result = getStyleValue(chunks); - if (!result) return null; - - props.push({ key, value: result.value }); - chunks = result.chunks; - } - - return props; -} - -function getStyleValue(chunks: Node[]) { - const value: Node[] = []; - - let inUrl = false; - let quoteMark = null; - let escaped = false; - - while (chunks.length) { - const chunk = chunks.shift(); - - if (chunk.type === 'Text') { - let c = 0; - while (c < chunk.data.length) { - const char = chunk.data[c]; - - if (escaped) { - escaped = false; - } else if (char === '\\') { - escaped = true; - } else if (char === quoteMark) { - quoteMark === null; - } else if (char === '"' || char === "'") { - quoteMark = char; - } else if (char === ')' && inUrl) { - inUrl = false; - } else if (char === 'u' && chunk.data.slice(c, c + 4) === 'url(') { - inUrl = true; - } else if (char === ';' && !inUrl && !quoteMark) { - break; - } - - c += 1; - } - - if (c > 0) { - value.push({ - type: 'Text', - start: chunk.start, - end: chunk.start + c, - data: chunk.data.slice(0, c) - }); - } - - while (/[;\s]/.test(chunk.data[c])) c += 1; - const remainingData = chunk.data.slice(c); - - if (remainingData) { - chunks.unshift({ - start: chunk.start + c, - end: chunk.end, - type: 'Text', - data: remainingData - }); - - break; - } - } - - else { - value.push(chunk); - } - } - - return { - chunks, - value - }; -} - -function isDynamic(value: Node[]) { - return value.length > 1 || value[0].type !== 'Text'; -} +} \ No newline at end of file diff --git a/src/compile/nodes/AwaitBlock.ts b/src/compile/nodes/AwaitBlock.ts index df30f828a3..4d0559711f 100644 --- a/src/compile/nodes/AwaitBlock.ts +++ b/src/compile/nodes/AwaitBlock.ts @@ -1,12 +1,8 @@ -import deindent from '../../utils/deindent'; import Node from './shared/Node'; -import Block from '../dom/Block'; import PendingBlock from './PendingBlock'; import ThenBlock from './ThenBlock'; import CatchBlock from './CatchBlock'; -import createDebuggingComment from '../../utils/createDebuggingComment'; import Expression from './shared/Expression'; -import { SsrTarget } from '../ssr'; export default class AwaitBlock extends Node { expression: Expression; @@ -30,187 +26,4 @@ export default class AwaitBlock extends Node { this.then = new ThenBlock(component, this, scope.add(this.value, deps), info.then); this.catch = new CatchBlock(component, this, scope.add(this.error, deps), info.catch); } - - init( - block: Block, - stripWhitespace: boolean, - nextSibling: Node - ) { - this.cannotUseInnerHTML(); - - this.var = block.getUniqueName('await_block'); - block.addDependencies(this.expression.dependencies); - - let isDynamic = false; - let hasIntros = false; - let hasOutros = false; - - ['pending', 'then', 'catch'].forEach(status => { - const child = this[status]; - - child.block = block.child({ - comment: createDebuggingComment(child, this.component), - name: this.component.getUniqueName(`create_${status}_block`) - }); - - child.initChildren(child.block, stripWhitespace, nextSibling); - this.component.target.blocks.push(child.block); - - if (child.block.dependencies.size > 0) { - isDynamic = true; - block.addDependencies(child.block.dependencies); - } - - if (child.block.hasIntros) hasIntros = true; - if (child.block.hasOutros) hasOutros = true; - }); - - this.pending.block.hasUpdateMethod = isDynamic; - this.then.block.hasUpdateMethod = isDynamic; - this.catch.block.hasUpdateMethod = isDynamic; - - this.pending.block.hasIntroMethod = hasIntros; - this.then.block.hasIntroMethod = hasIntros; - this.catch.block.hasIntroMethod = hasIntros; - - this.pending.block.hasOutroMethod = hasOutros; - this.then.block.hasOutroMethod = hasOutros; - this.catch.block.hasOutroMethod = hasOutros; - - if (hasOutros && this.component.options.nestedTransitions) block.addOutro(); - } - - build( - block: Block, - parentNode: string, - parentNodes: string - ) { - const name = this.var; - - const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes); - const updateMountNode = this.getUpdateMountNode(anchor); - - const { snippet } = this.expression; - - const info = block.getUniqueName(`info`); - const promise = block.getUniqueName(`promise`); - - block.addVariable(promise); - - block.maintainContext = true; - - const infoProps = [ - block.alias('component') === 'component' ? 'component' : `component: #component`, - 'ctx', - 'current: null', - this.pending.block.name && `pending: ${this.pending.block.name}`, - this.then.block.name && `then: ${this.then.block.name}`, - this.catch.block.name && `catch: ${this.catch.block.name}`, - this.then.block.name && `value: '${this.value}'`, - this.catch.block.name && `error: '${this.error}'`, - this.pending.block.hasOutroMethod && `blocks: Array(3)` - ].filter(Boolean); - - block.builders.init.addBlock(deindent` - let ${info} = { - ${infoProps.join(',\n')} - }; - `); - - block.builders.init.addBlock(deindent` - @handlePromise(${promise} = ${snippet}, ${info}); - `); - - block.builders.create.addBlock(deindent` - ${info}.block.c(); - `); - - if (parentNodes) { - block.builders.claim.addBlock(deindent` - ${info}.block.l(${parentNodes}); - `); - } - - const initialMountNode = parentNode || '#target'; - const anchorNode = parentNode ? 'null' : 'anchor'; - - const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod; - - block.builders.mount.addBlock(deindent` - ${info}.block.${hasTransitions ? 'i' : 'm'}(${initialMountNode}, ${info}.anchor = ${anchorNode}); - ${info}.mount = () => ${updateMountNode}; - `); - - const conditions = []; - if (this.expression.dependencies.size > 0) { - conditions.push( - `(${[...this.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})` - ); - } - - conditions.push( - `${promise} !== (${promise} = ${snippet})`, - `@handlePromise(${promise}, ${info})` - ); - - block.builders.update.addLine( - `${info}.ctx = ctx;` - ); - - if (this.pending.block.hasUpdateMethod) { - block.builders.update.addBlock(deindent` - if (${conditions.join(' && ')}) { - // nothing - } else { - ${info}.block.p(changed, @assign(@assign({}, ctx), ${info}.resolved)); - } - `); - } else { - block.builders.update.addBlock(deindent` - ${conditions.join(' && ')} - `); - } - - if (this.pending.block.hasOutroMethod && this.component.options.nestedTransitions) { - const countdown = block.getUniqueName('countdown'); - block.builders.outro.addBlock(deindent` - const ${countdown} = @callAfter(#outrocallback, 3); - for (let #i = 0; #i < 3; #i += 1) { - const block = ${info}.blocks[#i]; - if (block) block.o(${countdown}); - else ${countdown}(); - } - `); - } - - block.builders.destroy.addBlock(deindent` - ${info}.block.d(${parentNode ? '' : 'detach'}); - ${info} = null; - `); - - [this.pending, this.then, this.catch].forEach(status => { - status.children.forEach(child => { - child.build(status.block, null, 'nodes'); - }); - }); - } - - ssr() { - const target: SsrTarget = this.component.target; - const { snippet } = this.expression; - - target.append('${(function(__value) { if(@isPromise(__value)) return `'); - - this.pending.children.forEach((child: Node) => { - child.ssr(); - }); - - target.append('`; return function(ctx) { return `'); - - this.then.children.forEach((child: Node) => { - child.ssr(); - }); - - target.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`); - } } diff --git a/src/compile/nodes/Binding.ts b/src/compile/nodes/Binding.ts index 40ad3bce5f..1d8aa911e1 100644 --- a/src/compile/nodes/Binding.ts +++ b/src/compile/nodes/Binding.ts @@ -1,22 +1,6 @@ import Node from './shared/Node'; -import Element from './Element'; import getObject from '../../utils/getObject'; -import getTailSnippet from '../../utils/getTailSnippet'; -import flattenReference from '../../utils/flattenReference'; -import Component from '../Component'; -import Block from '../dom/Block'; import Expression from './shared/Expression'; -import { dimensions } from '../../utils/patterns'; - -const readOnlyMediaAttributes = new Set([ - 'duration', - 'buffered', - 'seekable', - 'played' -]); - -// TODO a lot of this element-specific stuff should live in Element — -// Binding should ideally be agnostic between Element and InlineComponent export default class Binding extends Node { name: string; @@ -54,263 +38,4 @@ export default class Binding extends Node { this.obj = obj; this.prop = prop; } - - munge( - block: Block - ) { - const node: Element = this.parent; - - const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(node.getStaticAttributeValue('type')); - const isReadOnly = ( - (node.isMediaNode() && readOnlyMediaAttributes.has(this.name)) || - dimensions.test(this.name) - ); - - let updateConditions: string[] = []; - - const { name } = getObject(this.value.node); - const { snippet } = this.value; - - // special case: if you have e.g. `` - // and `selected` is an object chosen with a - if (binding.name === 'group') { - const bindingGroup = getBindingGroup(component, binding.value.node); - if (type === 'checkbox') { - return `@getBindingGroupValue(#component._bindingGroups[${bindingGroup}])`; - } - - return `${node.var}.__value`; - } - - // - if (type === 'range' || type === 'number') { - return `@toNumber(${node.var}.${binding.name})`; - } - - if ((binding.name === 'buffered' || binding.name === 'seekable' || binding.name === 'played')) { - return `@timeRangesToArray(${node.var}.${binding.name})` - } - - // everything else - return `${node.var}.${binding.name}`; -} - -function isComputed(node: Node) { - while (node.type === 'MemberExpression') { - if (node.computed) return true; - node = node.object; - } - - return false; } diff --git a/src/compile/nodes/CatchBlock.ts b/src/compile/nodes/CatchBlock.ts index 4fb9ae43f1..a8c55860f5 100644 --- a/src/compile/nodes/CatchBlock.ts +++ b/src/compile/nodes/CatchBlock.ts @@ -1,5 +1,5 @@ import Node from './shared/Node'; -import Block from '../dom/Block'; +import Block from '../render-dom/Block'; import mapChildren from './shared/mapChildren'; export default class CatchBlock extends Node { diff --git a/src/compile/nodes/Comment.ts b/src/compile/nodes/Comment.ts index 03ea0be9e6..97a28f25a1 100644 --- a/src/compile/nodes/Comment.ts +++ b/src/compile/nodes/Comment.ts @@ -8,11 +8,4 @@ export default class Comment extends Node { super(component, parent, scope, info); this.data = info.data; } - - ssr() { - // Allow option to preserve comments, otherwise ignore - if (this.component.options.preserveComments) { - this.component.target.append(``); - } - } } \ No newline at end of file diff --git a/src/compile/nodes/DebugTag.ts b/src/compile/nodes/DebugTag.ts index 9768b5c121..4d2c3f6ae4 100644 --- a/src/compile/nodes/DebugTag.ts +++ b/src/compile/nodes/DebugTag.ts @@ -1,10 +1,5 @@ import Node from './shared/Node'; -import Tag from './shared/Tag'; -import Block from '../dom/Block'; import Expression from './shared/Expression'; -import deindent from '../../utils/deindent'; -import addToSet from '../../utils/addToSet'; -import { stringify } from '../../utils/stringify'; export default class DebugTag extends Node { expressions: Expression[]; @@ -16,74 +11,4 @@ export default class DebugTag extends Node { return new Expression(component, parent, scope, node); }); } - - build( - block: Block, - parentNode: string, - parentNodes: string, - ) { - if (!this.component.options.dev) return; - - const { code } = this.component; - - if (this.expressions.length === 0) { - // Debug all - code.overwrite(this.start + 1, this.start + 7, 'debugger', { - storeName: true - }); - const statement = `[✂${this.start + 1}-${this.start + 7}✂];`; - - block.builders.create.addLine(statement); - block.builders.update.addLine(statement); - } else { - const { code } = this.component; - code.overwrite(this.start + 1, this.start + 7, 'log', { - storeName: true - }); - const log = `[✂${this.start + 1}-${this.start + 7}✂]`; - - const dependencies = new Set(); - this.expressions.forEach(expression => { - addToSet(dependencies, expression.dependencies); - }); - - const condition = [...dependencies].map(d => `changed.${d}`).join(' || '); - - const identifiers = this.expressions.map(e => e.node.name).join(', '); - - block.builders.update.addBlock(deindent` - if (${condition}) { - const { ${identifiers} } = ctx; - console.${log}({ ${identifiers} }); - debugger; - } - `); - - block.builders.create.addBlock(deindent` - { - const { ${identifiers} } = ctx; - console.${log}({ ${identifiers} }); - debugger; - } - `); - } - } - - ssr() { - if (!this.component.options.dev) return; - - const filename = this.component.file || null; - const { line, column } = this.component.locate(this.start + 1); - - const obj = this.expressions.length === 0 - ? `ctx` - : `{ ${this.expressions - .map(e => e.node.name) - .map(name => `${name}: ctx.${name}`) - .join(', ')} }`; - - const str = '${@debug(' + `${filename && stringify(filename)}, ${line}, ${column}, ${obj})}`; - - this.component.target.append(str); - } } \ No newline at end of file diff --git a/src/compile/nodes/EachBlock.ts b/src/compile/nodes/EachBlock.ts index 60e814afa4..afc4f8655a 100644 --- a/src/compile/nodes/EachBlock.ts +++ b/src/compile/nodes/EachBlock.ts @@ -1,8 +1,6 @@ -import deindent from '../../utils/deindent'; import Node from './shared/Node'; import ElseBlock from './ElseBlock'; -import Block from '../dom/Block'; -import createDebuggingComment from '../../utils/createDebuggingComment'; +import Block from '../render-dom/Block'; import Expression from './shared/Expression'; import mapChildren from './shared/mapChildren'; import TemplateScope from './shared/TemplateScope'; @@ -78,472 +76,4 @@ export default class EachBlock extends Node { ? new ElseBlock(component, this, this.scope, info.else) : null; } - - init( - block: Block, - stripWhitespace: boolean, - nextSibling: Node - ) { - this.cannotUseInnerHTML(); - - this.var = block.getUniqueName(`each`); - this.iterations = block.getUniqueName(`${this.var}_blocks`); - this.get_each_context = this.component.getUniqueName(`get_${this.var}_context`); - - const { dependencies } = this.expression; - block.addDependencies(dependencies); - - this.block = block.child({ - comment: createDebuggingComment(this, this.component), - name: this.component.getUniqueName('create_each_block'), - key: this.key, - - bindings: new Map(block.bindings) - }); - - this.each_block_value = this.component.getUniqueName('each_value'); - - const indexName = this.index || this.component.getUniqueName(`${this.context}_index`); - - this.contexts.forEach(prop => { - this.block.bindings.set(prop.key.name, `ctx.${this.each_block_value}[ctx.${indexName}]${prop.tail}`); - }); - - if (this.index) { - this.block.getUniqueName(this.index); // this prevents name collisions (#1254) - } - - this.contextProps = this.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`); - - // TODO only add these if necessary - this.contextProps.push( - `child_ctx.${this.each_block_value} = list;`, - `child_ctx.${indexName} = i;` - ); - - this.component.target.blocks.push(this.block); - this.initChildren(this.block, stripWhitespace, nextSibling); - block.addDependencies(this.block.dependencies); - this.block.hasUpdateMethod = this.block.dependencies.size > 0; - - if (this.else) { - this.else.block = block.child({ - comment: createDebuggingComment(this.else, this.component), - name: this.component.getUniqueName(`${this.block.name}_else`), - }); - - this.component.target.blocks.push(this.else.block); - this.else.initChildren( - this.else.block, - stripWhitespace, - nextSibling - ); - this.else.block.hasUpdateMethod = this.else.block.dependencies.size > 0; - } - - if (this.block.hasOutros || (this.else && this.else.block.hasOutros)) { - block.addOutro(); - } - } - - build( - block: Block, - parentNode: string, - parentNodes: string - ) { - if (this.children.length === 0) return; - - const { component } = this; - - const each = this.var; - - const create_each_block = this.block.name; - const iterations = this.iterations; - - const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode(); - const anchor = needsAnchor - ? block.getUniqueName(`${each}_anchor`) - : (this.next && this.next.var) || 'null'; - - // hack the sourcemap, so that if data is missing the bug - // is easy to find - let c = this.start + 2; - while (component.source[c] !== 'e') c += 1; - component.code.overwrite(c, c + 4, 'length'); - const length = `[✂${c}-${c+4}✂]`; - - const mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm'; - const vars = { - each, - create_each_block, - length, - iterations, - anchor, - mountOrIntro, - }; - - const { snippet } = this.expression; - - block.builders.init.addLine(`var ${this.each_block_value} = ${snippet};`); - - this.component.target.blocks.push(deindent` - function ${this.get_each_context}(ctx, list, i) { - const child_ctx = Object.create(ctx); - ${this.contextProps} - return child_ctx; - } - `); - - if (this.key) { - this.buildKeyed(block, parentNode, parentNodes, snippet, vars); - } else { - this.buildUnkeyed(block, parentNode, parentNodes, snippet, vars); - } - - if (needsAnchor) { - block.addElement( - anchor, - `@createComment()`, - parentNodes && `@createComment()`, - parentNode - ); - } - - if (this.else) { - const each_block_else = component.getUniqueName(`${each}_else`); - const mountOrIntro = (this.else.block.hasIntroMethod || this.else.block.hasOutroMethod) ? 'i' : 'm'; - - block.builders.init.addLine(`var ${each_block_else} = null;`); - - // TODO neaten this up... will end up with an empty line in the block - block.builders.init.addBlock(deindent` - if (!${this.each_block_value}.${length}) { - ${each_block_else} = ${this.else.block.name}(#component, ctx); - ${each_block_else}.c(); - } - `); - - block.builders.mount.addBlock(deindent` - if (${each_block_else}) { - ${each_block_else}.${mountOrIntro}(${parentNode || '#target'}, null); - } - `); - - const initialMountNode = parentNode || `${anchor}.parentNode`; - - if (this.else.block.hasUpdateMethod) { - block.builders.update.addBlock(deindent` - if (!${this.each_block_value}.${length} && ${each_block_else}) { - ${each_block_else}.p(changed, ctx); - } else if (!${this.each_block_value}.${length}) { - ${each_block_else} = ${this.else.block.name}(#component, ctx); - ${each_block_else}.c(); - ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${anchor}); - } else if (${each_block_else}) { - ${each_block_else}.d(1); - ${each_block_else} = null; - } - `); - } else { - block.builders.update.addBlock(deindent` - if (${this.each_block_value}.${length}) { - if (${each_block_else}) { - ${each_block_else}.d(1); - ${each_block_else} = null; - } - } else if (!${each_block_else}) { - ${each_block_else} = ${this.else.block.name}(#component, ctx); - ${each_block_else}.c(); - ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${anchor}); - } - `); - } - - block.builders.destroy.addBlock(deindent` - if (${each_block_else}) ${each_block_else}.d(${parentNode ? '' : 'detach'}); - `); - } - - this.children.forEach((child: Node) => { - child.build(this.block, null, 'nodes'); - }); - - if (this.else) { - this.else.children.forEach((child: Node) => { - child.build(this.else.block, null, 'nodes'); - }); - } - } - - buildKeyed( - block: Block, - parentNode: string, - parentNodes: string, - snippet: string, - { - each, - create_each_block, - length, - anchor, - mountOrIntro, - } - ) { - const get_key = block.getUniqueName('get_key'); - const blocks = block.getUniqueName(`${each}_blocks`); - const lookup = block.getUniqueName(`${each}_lookup`); - - block.addVariable(blocks, '[]'); - block.addVariable(lookup, `@blankObject()`); - - if (this.children[0].isDomNode()) { - this.block.first = this.children[0].var; - } else { - this.block.first = this.block.getUniqueName('first'); - this.block.addElement( - this.block.first, - `@createComment()`, - parentNodes && `@createComment()`, - null - ); - } - - block.builders.init.addBlock(deindent` - const ${get_key} = ctx => ${this.key.snippet}; - - for (var #i = 0; #i < ${this.each_block_value}.${length}; #i += 1) { - let child_ctx = ${this.get_each_context}(ctx, ${this.each_block_value}, #i); - let key = ${get_key}(child_ctx); - ${blocks}[#i] = ${lookup}[key] = ${create_each_block}(#component, key, child_ctx); - } - `); - - const initialMountNode = parentNode || '#target'; - const updateMountNode = this.getUpdateMountNode(anchor); - const anchorNode = parentNode ? 'null' : 'anchor'; - - block.builders.create.addBlock(deindent` - for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].c(); - `); - - if (parentNodes) { - block.builders.claim.addBlock(deindent` - for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].l(${parentNodes}); - `); - } - - block.builders.mount.addBlock(deindent` - for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].${mountOrIntro}(${initialMountNode}, ${anchorNode}); - `); - - const dynamic = this.block.hasUpdateMethod; - - const rects = block.getUniqueName('rects'); - const destroy = this.block.hasAnimation - ? `@fixAndOutroAndDestroyBlock` - : this.block.hasOutros - ? `@outroAndDestroyBlock` - : `@destroyBlock`; - - block.builders.update.addBlock(deindent` - const ${this.each_block_value} = ${snippet}; - - ${this.block.hasOutros && `@groupOutros();`} - ${this.block.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].r();`} - ${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.get_each_context}); - ${this.block.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].a();`} - `); - - if (this.block.hasOutros && this.component.options.nestedTransitions) { - const countdown = block.getUniqueName('countdown'); - block.builders.outro.addBlock(deindent` - const ${countdown} = @callAfter(#outrocallback, ${blocks}.length); - for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].o(${countdown}); - `); - } - - block.builders.destroy.addBlock(deindent` - for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].d(${parentNode ? '' : 'detach'}); - `); - } - - buildUnkeyed( - block: Block, - parentNode: string, - parentNodes: string, - snippet: string, - { - create_each_block, - length, - iterations, - anchor, - mountOrIntro, - } - ) { - block.builders.init.addBlock(deindent` - var ${iterations} = []; - - for (var #i = 0; #i < ${this.each_block_value}.${length}; #i += 1) { - ${iterations}[#i] = ${create_each_block}(#component, ${this.get_each_context}(ctx, ${this.each_block_value}, #i)); - } - `); - - const initialMountNode = parentNode || '#target'; - const updateMountNode = this.getUpdateMountNode(anchor); - const anchorNode = parentNode ? 'null' : 'anchor'; - - block.builders.create.addBlock(deindent` - for (var #i = 0; #i < ${iterations}.length; #i += 1) { - ${iterations}[#i].c(); - } - `); - - if (parentNodes) { - block.builders.claim.addBlock(deindent` - for (var #i = 0; #i < ${iterations}.length; #i += 1) { - ${iterations}[#i].l(${parentNodes}); - } - `); - } - - block.builders.mount.addBlock(deindent` - for (var #i = 0; #i < ${iterations}.length; #i += 1) { - ${iterations}[#i].${mountOrIntro}(${initialMountNode}, ${anchorNode}); - } - `); - - const allDependencies = new Set(this.block.dependencies); - const { dependencies } = this.expression; - dependencies.forEach((dependency: string) => { - allDependencies.add(dependency); - }); - - const outroBlock = this.block.hasOutros && block.getUniqueName('outroBlock') - if (outroBlock) { - block.builders.init.addBlock(deindent` - function ${outroBlock}(i, detach, fn) { - if (${iterations}[i]) { - ${iterations}[i].o(() => { - if (detach) { - ${iterations}[i].d(detach); - ${iterations}[i] = null; - } - if (fn) fn(); - }); - } - } - `); - } - - // TODO do this for keyed blocks as well - const condition = Array.from(allDependencies) - .map(dependency => `changed.${dependency}`) - .join(' || '); - - if (condition !== '') { - const forLoopBody = this.block.hasUpdateMethod - ? (this.block.hasIntros || this.block.hasOutros) - ? deindent` - if (${iterations}[#i]) { - ${iterations}[#i].p(changed, child_ctx); - } else { - ${iterations}[#i] = ${create_each_block}(#component, child_ctx); - ${iterations}[#i].c(); - } - ${iterations}[#i].i(${updateMountNode}, ${anchor}); - ` - : deindent` - if (${iterations}[#i]) { - ${iterations}[#i].p(changed, child_ctx); - } else { - ${iterations}[#i] = ${create_each_block}(#component, child_ctx); - ${iterations}[#i].c(); - ${iterations}[#i].m(${updateMountNode}, ${anchor}); - } - ` - : deindent` - ${iterations}[#i] = ${create_each_block}(#component, child_ctx); - ${iterations}[#i].c(); - ${iterations}[#i].${mountOrIntro}(${updateMountNode}, ${anchor}); - `; - - const start = this.block.hasUpdateMethod ? '0' : `${iterations}.length`; - - let destroy; - - if (this.block.hasOutros) { - destroy = deindent` - @groupOutros(); - for (; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 1); - `; - } else { - destroy = deindent` - for (; #i < ${iterations}.length; #i += 1) { - ${iterations}[#i].d(1); - } - ${iterations}.length = ${this.each_block_value}.${length}; - `; - } - - block.builders.update.addBlock(deindent` - if (${condition}) { - ${this.each_block_value} = ${snippet}; - - for (var #i = ${start}; #i < ${this.each_block_value}.${length}; #i += 1) { - const child_ctx = ${this.get_each_context}(ctx, ${this.each_block_value}, #i); - - ${forLoopBody} - } - - ${destroy} - } - `); - } - - if (outroBlock && this.component.options.nestedTransitions) { - const countdown = block.getUniqueName('countdown'); - block.builders.outro.addBlock(deindent` - ${iterations} = ${iterations}.filter(Boolean); - const ${countdown} = @callAfter(#outrocallback, ${iterations}.length); - for (let #i = 0; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 0, ${countdown});` - ); - } - - block.builders.destroy.addBlock(`@destroyEach(${iterations}, detach);`); - } - - remount(name: string) { - // TODO consider keyed blocks - return `for (var #i = 0; #i < ${this.iterations}.length; #i += 1) ${this.iterations}[#i].m(${name}._slotted.default, null);`; - } - - ssr() { - const { component } = this; - const { snippet } = this.expression; - - const props = this.contexts.map(prop => `${prop.key.name}: item${prop.tail}`); - - const getContext = this.index - ? `(item, i) => Object.assign({}, ctx, { ${props.join(', ')}, ${this.index}: i })` - : `item => Object.assign({}, ctx, { ${props.join(', ')} })`; - - const open = `\${ ${this.else ? `${snippet}.length ? ` : ''}@each(${snippet}, ${getContext}, ctx => \``; - component.target.append(open); - - this.children.forEach((child: Node) => { - child.ssr(); - }); - - const close = `\`)`; - component.target.append(close); - - if (this.else) { - component.target.append(` : \``); - this.else.children.forEach((child: Node) => { - child.ssr(); - }); - component.target.append(`\``); - } - - component.target.append('}'); - } } diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index 1206ad939b..c060edc1f8 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -1,14 +1,6 @@ -import deindent from '../../utils/deindent'; -import { stringify, escapeHTML } from '../../utils/stringify'; -import flattenReference from '../../utils/flattenReference'; import isVoidElementName from '../../utils/isVoidElementName'; -import validCalleeObjects from '../../utils/validCalleeObjects'; -import reservedNames from '../../utils/reservedNames'; -import fixAttributeCasing from '../../utils/fixAttributeCasing'; -import { quoteNameIfNecessary, quotePropIfNecessary } from '../../utils/quoteIfNecessary'; -import Component from '../Component'; +import { quotePropIfNecessary } from '../../utils/quoteIfNecessary'; import Node from './shared/Node'; -import Block from '../dom/Block'; import Attribute from './Attribute'; import Binding from './Binding'; import EventHandler from './EventHandler'; @@ -23,47 +15,6 @@ import { dimensions } from '../../utils/patterns'; import fuzzymatch from '../validate/utils/fuzzymatch'; import Ref from './Ref'; -// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 -const booleanAttributes = new Set([ - 'async', - 'autocomplete', - 'autofocus', - 'autoplay', - 'border', - 'challenge', - 'checked', - 'compact', - 'contenteditable', - 'controls', - 'default', - 'defer', - 'disabled', - 'formnovalidate', - 'frameborder', - 'hidden', - 'indeterminate', - 'ismap', - 'loop', - 'multiple', - 'muted', - 'nohref', - 'noresize', - 'noshade', - 'novalidate', - 'nowrap', - 'open', - 'readonly', - 'required', - 'reversed', - 'scoped', - 'scrolling', - 'seamless', - 'selected', - 'sortable', - 'spellcheck', - 'translate' -]); - 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|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/; const ariaAttributes = 'activedescendant atomic autocomplete busy checked controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription selected setsize sort valuemax valuemin valuenow valuetext'.split(' '); @@ -113,7 +64,6 @@ export default class Element extends Node { actions: Action[]; bindings: Binding[]; classes: Class[]; - classDependencies: string[]; handlers: EventHandler[]; intro?: Transition; outro?: Transition; @@ -144,7 +94,6 @@ export default class Element extends Node { this.actions = []; this.bindings = []; this.classes = []; - this.classDependencies = []; this.handlers = []; this.intro = null; @@ -614,737 +563,6 @@ export default class Element extends Node { } } - init( - block: Block, - stripWhitespace: boolean, - nextSibling: Node - ) { - if (this.name === 'slot' || this.name === 'option' || this.component.options.dev) { - this.cannotUseInnerHTML(); - } - - this.var = block.getUniqueName( - this.name.replace(/[^a-zA-Z0-9_$]/g, '_') - ); - - this.attributes.forEach(attr => { - if ( - attr.chunks && - attr.chunks.length && - (attr.chunks.length > 1 || attr.chunks[0].type !== 'Text') - ) { - this.parent.cannotUseInnerHTML(); - } - if (attr.dependencies.size) { - block.addDependencies(attr.dependencies); - - // special case —