remove <:Document>, implement <:Head> on client-side

pull/1024/head
Rich Harris 8 years ago
parent a02c3dba28
commit 50654fab88

@ -727,9 +727,9 @@ export default class Generator {
} else if (node.name === ':Window') { // TODO do this in parse? } else if (node.name === ':Window') { // TODO do this in parse?
node.type = 'Window'; node.type = 'Window';
node.__proto__ = nodes.Window.prototype; node.__proto__ = nodes.Window.prototype;
} else if (node.name === ':Document') { // TODO do this in parse? } else if (node.name === ':Head') { // TODO do this in parse?
node.type = 'Document'; node.type = 'Head';
node.__proto__ = nodes.Document.prototype; node.__proto__ = nodes.Head.prototype;
} else if (node.type === 'Element' && node.name === 'slot' && !generator.customElement) { } else if (node.type === 'Element' && node.name === 'slot' && !generator.customElement) {
node.type = 'Slot'; node.type = 'Slot';
node.__proto__ = nodes.Slot.prototype; node.__proto__ = nodes.Slot.prototype;

@ -132,9 +132,9 @@ export default class Block {
) { ) {
this.addVariable(name); this.addVariable(name);
this.builders.create.addLine(`${name} = ${renderStatement};`); this.builders.create.addLine(`${name} = ${renderStatement};`);
this.builders.claim.addLine(`${name} = ${claimStatement};`); this.builders.claim.addLine(`${name} = ${claimStatement || renderStatement};`);
if (parentNode) { if (parentNode && parentNode !== 'document.head') {
this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`); this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`);
} else { } else {
this.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`); this.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`);

@ -68,7 +68,7 @@ export default class AwaitBlock extends Node {
) { ) {
const name = this.var; const name = this.var;
const anchor = this.getOrCreateAnchor(block, parentNode); const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor); const updateMountNode = this.getUpdateMountNode(anchor);
const params = block.params.join(', '); const params = block.params.join(', ');
@ -143,9 +143,11 @@ export default class AwaitBlock extends Node {
${await_block}.c(); ${await_block}.c();
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
${await_block}.l(${parentNodes}); block.builders.claim.addBlock(deindent`
`); ${await_block}.l(${parentNodes});
`);
}
const initialMountNode = parentNode || '#target'; const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor'; const anchorNode = parentNode ? 'null' : 'anchor';

@ -248,7 +248,7 @@ export default class Component extends Node {
block.contextualise(this.expression); block.contextualise(this.expression);
const { dependencies, snippet } = this.metadata; const { dependencies, snippet } = this.metadata;
const anchor = this.getOrCreateAnchor(block, parentNode); const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const params = block.params.join(', '); const params = block.params.join(', ');
@ -281,9 +281,11 @@ export default class Component extends Node {
`if (${name}) ${name}._fragment.c();` `if (${name}) ${name}._fragment.c();`
); );
block.builders.claim.addLine( if (parentNodes) {
`if (${name}) ${name}._fragment.l(${parentNodes});` block.builders.claim.addLine(
); `if (${name}) ${name}._fragment.l(${parentNodes});`
);
}
block.builders.mount.addLine( block.builders.mount.addLine(
`if (${name}) ${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` `if (${name}) ${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});`
@ -350,9 +352,11 @@ export default class Component extends Node {
block.builders.create.addLine(`${name}._fragment.c();`); block.builders.create.addLine(`${name}._fragment.c();`);
block.builders.claim.addLine( if (parentNodes) {
`${name}._fragment.l(${parentNodes});` block.builders.claim.addLine(
); `${name}._fragment.l(${parentNodes});`
);
}
block.builders.mount.addLine( block.builders.mount.addLine(
`${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` `${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});`

@ -154,7 +154,7 @@ export default class EachBlock extends Node {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }
@ -263,7 +263,7 @@ export default class EachBlock extends Node {
this.block.addElement( this.block.addElement(
this.block.first, this.block.first,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
null null
); );
} }
@ -293,13 +293,15 @@ export default class EachBlock extends Node {
} }
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
var ${iteration} = ${head}; block.builders.claim.addBlock(deindent`
while (${iteration}) { var ${iteration} = ${head};
${iteration}.l(${parentNodes}); while (${iteration}) {
${iteration} = ${iteration}.next; ${iteration}.l(${parentNodes});
} ${iteration} = ${iteration}.next;
`); }
`);
}
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
var ${iteration} = ${head}; var ${iteration} = ${head};
@ -481,11 +483,13 @@ export default class EachBlock extends Node {
} }
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
for (var #i = 0; #i < ${iterations}.length; #i += 1) { block.builders.claim.addBlock(deindent`
${iterations}[#i].l(${parentNodes}); for (var #i = 0; #i < ${iterations}.length; #i += 1) {
} ${iterations}[#i].l(${parentNodes});
`); }
`);
}
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
for (var #i = 0; #i < ${iterations}.length; #i += 1) { for (var #i = 0; #i < ${iterations}.length; #i += 1) {

@ -163,7 +163,7 @@ export default class Element extends Node {
const childState = { const childState = {
parentNode: this.var, parentNode: this.var,
parentNodes: block.getUniqueName(`${this.var}_nodes`) parentNodes: parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null
}; };
const name = this.var; const name = this.var;
@ -175,22 +175,25 @@ export default class Element extends Node {
parentNode; parentNode;
block.addVariable(name); block.addVariable(name);
const renderStatement = getRenderStatement(this.generator, this.namespace, this.name);
block.builders.create.addLine( block.builders.create.addLine(
`${name} = ${getRenderStatement( `${name} = ${renderStatement};`
this.generator,
this.namespace,
this.name
)};`
); );
if (this.generator.hydratable) { if (this.generator.hydratable) {
block.builders.claim.addBlock(deindent` if (parentNodes) {
${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)}; block.builders.claim.addBlock(deindent`
var ${childState.parentNodes} = @children(${name}); ${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)};
`); var ${childState.parentNodes} = @children(${name});
`);
} else {
block.builders.claim.addLine(
`${name} = ${renderStatement};`
);
}
} }
if (initialMountNode) { if (initialMountNode && initialMountNode !== 'document.head') {
block.builders.mount.addLine( block.builders.mount.addLine(
`@appendNode(${name}, ${initialMountNode});` `@appendNode(${name}, ${initialMountNode});`
); );
@ -394,9 +397,11 @@ export default class Element extends Node {
block.builders.mount.addBlock(this.initialUpdate); block.builders.mount.addBlock(this.initialUpdate);
} }
block.builders.claim.addLine( if (childState.parentNodes) {
`${childState.parentNodes}.forEach(@detachNode);` block.builders.claim.addLine(
); `${childState.parentNodes}.forEach(@detachNode);`
);
}
function toHTML(node: Element | Text) { function toHTML(node: Element | Text) {
if (node.type === 'Text') return node.data; if (node.type === 'Text') return node.data;

@ -4,10 +4,18 @@ import Node from './shared/Node';
import Block from '../dom/Block'; import Block from '../dom/Block';
import Attribute from './Attribute'; import Attribute from './Attribute';
export default class Document extends Node { export default class Head extends Node {
type: 'Document'; type: 'Head';
attributes: Attribute[]; attributes: Attribute[];
init(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
this.initChildren(block, true, null);
}
build( build(
block: Block, block: Block,
parentNode: string, parentNode: string,
@ -15,12 +23,10 @@ export default class Document extends Node {
) { ) {
const { generator } = this; const { generator } = this;
this.var = 'document'; this.var = 'document.head';
this.attributes.forEach((attribute: Attribute) => { this.children.forEach((child: Node) => {
if (attribute.name === 'title') { child.build(block, 'document.head', null);
attribute.render(block);
}
}); });
} }
} }

@ -133,15 +133,17 @@ export default class IfBlock extends Node {
block.builders.create.addLine(`${if_name}${name}.c();`); block.builders.create.addLine(`${if_name}${name}.c();`);
block.builders.claim.addLine( if (parentNodes) {
`${if_name}${name}.l(${parentNodes});` block.builders.claim.addLine(
); `${if_name}${name}.l(${parentNodes});`
);
}
if (needsAnchor) { if (needsAnchor) {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }

@ -22,7 +22,7 @@ export default class MustacheTag extends Tag {
block.addElement( block.addElement(
this.var, this.var,
`@createText(${init})`, `@createText(${init})`,
`@claimText(${parentNodes}, ${init})`, parentNodes && `@claimText(${parentNodes}, ${init})`,
parentNode parentNode
); );
} }

@ -61,7 +61,7 @@ export default class RawMustacheTag extends Tag {
block.addElement( block.addElement(
anchorBefore, anchorBefore,
`@createElement('noscript')`, `@createElement('noscript')`,
`@createElement('noscript')`, parentNodes && `@createElement('noscript')`,
parentNode parentNode
); );
} }
@ -70,7 +70,7 @@ export default class RawMustacheTag extends Tag {
block.addElement( block.addElement(
anchorAfter, anchorAfter,
`@createElement('noscript')`, `@createElement('noscript')`,
`@createElement('noscript')`, parentNodes && `@createElement('noscript')`,
parentNode parentNode
); );
} }

@ -53,7 +53,7 @@ export default class Text extends Node {
block.addElement( block.addElement(
this.var, this.var,
`@createText(${stringify(this.data)})`, `@createText(${stringify(this.data)})`,
`@claimText(${parentNodes}, ${stringify(this.data)})`, parentNodes && `@claimText(${parentNodes}, ${stringify(this.data)})`,
parentNode parentNode
); );
} }

@ -5,12 +5,12 @@ import Binding from './Binding';
import CatchBlock from './CatchBlock'; import CatchBlock from './CatchBlock';
import Comment from './Comment'; import Comment from './Comment';
import Component from './Component'; import Component from './Component';
import Document from './Document';
import EachBlock from './EachBlock'; import EachBlock from './EachBlock';
import Element from './Element'; import Element from './Element';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import EventHandler from './EventHandler'; import EventHandler from './EventHandler';
import Fragment from './Fragment'; import Fragment from './Fragment';
import Head from './Head';
import IfBlock from './IfBlock'; import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag'; import MustacheTag from './MustacheTag';
import PendingBlock from './PendingBlock'; import PendingBlock from './PendingBlock';
@ -29,12 +29,12 @@ const nodes: Record<string, any> = {
CatchBlock, CatchBlock,
Comment, Comment,
Component, Component,
Document,
EachBlock, EachBlock,
Element, Element,
ElseBlock, ElseBlock,
EventHandler, EventHandler,
Fragment, Fragment,
Head,
IfBlock, IfBlock,
MustacheTag, MustacheTag,
PendingBlock, PendingBlock,

@ -138,7 +138,7 @@ export default class Node {
if (this.parent) return this.parent.findNearest(selector); if (this.parent) return this.parent.findNearest(selector);
} }
getOrCreateAnchor(block: Block, parentNode: string) { getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
// TODO use this in EachBlock and IfBlock — tricky because // TODO use this in EachBlock and IfBlock — tricky because
// children need to be created first // children need to be created first
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode(); const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
@ -150,7 +150,7 @@ export default class Node {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }

@ -8,9 +8,11 @@ export default function visitDocument(
block: Block, block: Block,
node: Node node: Node
) { ) {
const title = node.attributes.find(attribute => attribute.type === 'Attribute' && attribute.name === 'title'); throw new Error('TODO');
if (title) { // const title = node.attributes.find(attribute => attribute.type === 'Attribute' && attribute.name === 'title');
generator.append('${(__result.title = `' + stringifyAttributeValue(block, title.value) + '`, "")}');
} // if (title) {
// generator.append('${(__result.title = `' + stringifyAttributeValue(block, title.value) + '`, "")}');
// }
} }

@ -1,9 +1,9 @@
import AwaitBlock from './AwaitBlock'; import AwaitBlock from './AwaitBlock';
import Comment from './Comment'; import Comment from './Comment';
import Component from './Component'; import Component from './Component';
import Document from './Document';
import EachBlock from './EachBlock'; import EachBlock from './EachBlock';
import Element from './Element'; import Element from './Element';
import Head from './Head';
import IfBlock from './IfBlock'; import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag'; import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag'; import RawMustacheTag from './RawMustacheTag';
@ -15,9 +15,9 @@ export default {
AwaitBlock, AwaitBlock,
Comment, Comment,
Component, Component,
Document,
EachBlock, EachBlock,
Element, Element,
Head,
IfBlock, IfBlock,
MustacheTag, MustacheTag,
RawMustacheTag, RawMustacheTag,

@ -19,7 +19,7 @@ const COMPONENT = ':Component';
const metaTags = new Set([ const metaTags = new Set([
':Window', ':Window',
':Document' ':Head'
]); ]);
const specials = new Map([ const specials = new Map([
@ -88,21 +88,24 @@ export default function tag(parser: Parser) {
const name = readTagName(parser); const name = readTagName(parser);
if (metaTags.has(name)) { if (metaTags.has(name)) {
if (name in parser.metaTags) { if (isClosingTag) {
if (isClosingTag && parser.current().children.length) { if (name === ':Window' && parser.current().children.length) {
parser.error( parser.error(
`<${name}> cannot have children`, `<:Window> cannot have children`,
parser.current().children[0].start parser.current().children[0].start
); );
} }
} else {
if (name in parser.metaTags) {
parser.error(`A component can only have one <${name}> tag`, start);
}
parser.error(`A component can only have one <${name}> tag`, start); if (parser.stack.length > 1) {
} console.log(parser.stack);
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
parser.metaTags[name] = true; }
if (parser.stack.length > 1) { parser.metaTags[name] = true;
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
} }
} }

@ -1,6 +1,6 @@
import validateElement from './validateElement'; import validateElement from './validateElement';
import validateWindow from './validateWindow'; import validateWindow from './validateWindow';
import validateDocument from './validateDocument'; import validateHead from './validateHead';
import a11y from './a11y'; import a11y from './a11y';
import fuzzymatch from '../utils/fuzzymatch' import fuzzymatch from '../utils/fuzzymatch'
import flattenReference from '../../utils/flattenReference'; import flattenReference from '../../utils/flattenReference';
@ -9,7 +9,7 @@ import { Node } from '../../interfaces';
const meta = new Map([ const meta = new Map([
[':Window', validateWindow], [':Window', validateWindow],
[':Document', validateDocument] [':Head', validateHead]
]); ]);
export default function validateHtml(validator: Validator, html: Node) { export default function validateHtml(validator: Validator, html: Node) {

@ -1,42 +0,0 @@
import flattenReference from '../../utils/flattenReference';
import fuzzymatch from '../utils/fuzzymatch';
import list from '../../utils/list';
import validateEventHandler from './validateEventHandler';
import { Validator } from '../index';
import { Node } from '../../interfaces';
const descriptions = {
Bindings: 'two-way bindings',
EventHandler: 'event handlers',
Transition: 'transitions',
Ref: 'refs'
};
export default function validateWindow(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) {
node.attributes.forEach((attribute: Node) => {
if (attribute.type === 'Attribute') {
if (attribute.name !== 'title') {
validator.error(
`<:Document> can only have a 'title' attribute`,
attribute.start
);
}
}
else {
const description = descriptions[attribute.type];
if (description) {
validator.error(
`<:Document> does not support ${description}`,
attribute.start
);
} else {
// future-proofing
validator.error(
`<:Document> can only have a 'title' attribute`,
attribute.start
);
}
}
});
}

@ -0,0 +1,8 @@
import { Validator } from '../index';
import { Node } from '../../interfaces';
export default function validateHead(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) {
if (node.attributes.length) {
validator.error(`<:Head> should not have any attributes or directives`, node.start);
}
}

@ -1 +0,0 @@
<:Document title='a {{adjective}} title'/>

@ -1,4 +1,6 @@
export default { export default {
solo: true,
data: { data: {
adjective: 'custom' adjective: 'custom'
}, },

@ -0,0 +1,4 @@
<:Head>
<title>a {{adjective}} title</title>
<meta name='twitter:creator' content='@sveltejs'>
</:Head>

@ -0,0 +1,4 @@
<:Head>
<title>changed</title>
<meta name='twitter:creator' content='@sveltejs'>
</:Head>

@ -1 +0,0 @@
<:Document title='a {{adjective}} title'/>

@ -0,0 +1,3 @@
<:Head>
<title>a {{adjective}} title</title>
</:Head>
Loading…
Cancel
Save