From ff214ef8abb76944af7dda3bec96eb9fa1c360e9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 15 Apr 2018 23:27:44 -0400 Subject: [PATCH] SSR tests --- src/generators/Generator.ts | 9 +-- src/generators/dom/index.ts | 6 +- src/generators/server-side-rendering/index.ts | 2 +- src/interfaces.ts | 2 - src/parse/index.ts | 4 -- src/parse/read/directives.ts | 6 +- src/parse/read/expression.ts | 2 +- src/parse/state/fragment.ts | 2 +- src/parse/state/mustache.ts | 60 +++++-------------- src/parse/state/tag.ts | 46 ++++---------- src/parse/state/text.ts | 2 +- src/server-side-rendering/register.js | 21 ++++--- src/validate/html/validateElement.ts | 3 +- src/validate/index.ts | 2 - src/validate/js/propValidators/computed.ts | 41 ++++--------- test/server-side-rendering/index.js | 4 +- .../samples/attribute-dynamic/main.html | 2 +- .../component-binding-renamed/main.html | 2 +- .../samples/component-binding/main.html | 2 +- .../component-data-dynamic/Widget.html | 8 +-- .../samples/component-data-dynamic/main.html | 2 +- .../samples/component-data-empty/Widget.html | 2 +- .../samples/component-data-static/Widget.html | 4 +- .../component-refs-and-attributes/Widget.html | 2 +- .../component-refs-and-attributes/main.html | 2 +- .../samples/component-yield/Widget.html | 2 +- .../samples/component-yield/main.html | 2 +- .../samples/computed/main.html | 8 +-- .../samples/default-data-override/main.html | 2 +- .../samples/default-data/main.html | 2 +- .../samples/dynamic-text-escaped/main.html | 2 +- .../samples/dynamic-text/main.html | 2 +- .../samples/each-block/main.html | 6 +- .../samples/entities/main.html | 2 +- .../samples/head-title/main.html | 6 +- .../samples/helpers/main.html | 2 +- .../samples/if-block-false/main.html | 4 +- .../samples/if-block-true/main.html | 4 +- .../samples/import-non-component/main.html | 4 +- .../samples/raw-mustaches/main.html | 2 +- .../samples/styles-nested/One.html | 2 +- .../samples/styles-nested/Two.html | 2 +- .../samples/textarea-children/main.html | 2 +- .../samples/textarea-value/main.html | 2 +- .../samples/triple/main.html | 2 +- 45 files changed, 103 insertions(+), 195 deletions(-) diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 7565336abb..68c721fb2a 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -84,7 +84,6 @@ export default class Generator { source: string; name: string; options: CompileOptions; - v2: boolean; customElement: CustomElementOptions; tag: string; @@ -133,8 +132,6 @@ export default class Generator { stats.start('compile'); this.stats = stats; - this.v2 = options.parser === 'v2'; - this.ast = clone(parsed); this.parsed = parsed; @@ -560,9 +557,7 @@ export default class Generator { const key = getName(prop.key); const value = prop.value; - const deps = this.v2 - ? value.params[0].properties.map(prop => prop.key.name) - : value.params.map(param => param.type === 'AssignmentPattern' ? param.left.name : param.name); + const deps = value.params[0].properties.map(prop => prop.key.name); deps.forEach(dep => { this.expectedProperties.add(dep); @@ -621,12 +616,10 @@ export default class Generator { this.namespace = namespaces[ns] || ns; } - if (templateProperties.onrender) templateProperties.oncreate = templateProperties.onrender; // remove after v2 if (templateProperties.oncreate && dom) { addDeclaration('oncreate', templateProperties.oncreate.value); } - if (templateProperties.onteardown) templateProperties.ondestroy = templateProperties.onteardown; // remove after v2 if (templateProperties.ondestroy && dom) { addDeclaration('ondestroy', templateProperties.ondestroy.value); } diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index c4ee996f88..e3263e2552 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -99,11 +99,7 @@ export default function dom( const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`; - const call = generator.v2 - ? `%computed-${key}(state)` - : `%computed-${key}(${deps.map(dep => `state.${dep}`).join(', ')})`; - - const statement = `if (this._differs(state.${key}, (state.${key} = ${call}))) changed.${key} = true;`; + const statement = `if (this._differs(state.${key}, (state.${key} = %computed-${key}(state)))) changed.${key} = true;`; computationBuilder.addConditional(condition, statement); }); diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts index dc4b49df51..a0eade1664 100644 --- a/src/generators/server-side-rendering/index.ts +++ b/src/generators/server-side-rendering/index.ts @@ -137,7 +137,7 @@ export default function ssr( ${computations.map( ({ key, deps }) => - `state.${key} = %computed-${key}(${deps.map(dep => `state.${dep}`).join(', ')});` + `state.${key} = %computed-${key}(state);` )} ${generator.bindings.length && diff --git a/src/interfaces.ts b/src/interfaces.ts index ddafe52119..6ed7e31dde 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -65,8 +65,6 @@ export interface CompileOptions { onerror?: (error: Error) => void; onwarn?: (warning: Warning) => void; - - parser?: 'v2'; } export interface GenerateOptions { diff --git a/src/parse/index.ts b/src/parse/index.ts index 9b3994c831..f3d724c847 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -13,13 +13,11 @@ import error from '../utils/error'; interface ParserOptions { filename?: string; bind?: boolean; - parser?: 'v2'; } type ParserState = (parser: Parser) => (ParserState | void); export class Parser { - readonly v2: boolean; readonly template: string; readonly filename?: string; @@ -34,8 +32,6 @@ export class Parser { allowBindings: boolean; constructor(template: string, options: ParserOptions) { - this.v2 = options.parser === 'v2'; - if (typeof template !== 'string') { throw new TypeError('Template must be a string'); } diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts index 6d8fa70fc5..4fb093d13b 100644 --- a/src/parse/read/directives.ts +++ b/src/parse/read/directives.ts @@ -165,10 +165,10 @@ export function readDirective( // assume the mistake was wrapping the directive arguments. // this could yield false positives! but hopefully not too many let message = 'directive values should not be wrapped'; - const expressionEnd = parser.template.indexOf((parser.v2 ? '}' : '}}'), expressionStart); + const expressionEnd = parser.template.indexOf('}', expressionStart); if (expressionEnd !== -1) { - const value = parser.template.slice(expressionStart + (parser.v2 ? 1 : 2), expressionEnd); - message += ` — use '${value}', not '${parser.v2 ? `{${value}}` : `{{${value}}}`}'`; + const value = parser.template.slice(expressionStart + 1, expressionEnd); + message += ` — use '${value}', not '{${value}}'`; } parser.error({ code: `invalid-directive-value`, diff --git a/src/parse/read/expression.ts b/src/parse/read/expression.ts index 606f8f92a7..938009d816 100644 --- a/src/parse/read/expression.ts +++ b/src/parse/read/expression.ts @@ -6,7 +6,7 @@ const literals = new Map([['true', true], ['false', false], ['null', null]]); export default function readExpression(parser: Parser) { const start = parser.index; - const name = parser.readUntil(parser.v2 ? /\s*}/ : /\s*}}/); + const name = parser.readUntil(/\s*}/); if (name && /^[a-z]+$/.test(name)) { const end = start + name.length; diff --git a/src/parse/state/fragment.ts b/src/parse/state/fragment.ts index b2e6cb4c32..41275a1738 100644 --- a/src/parse/state/fragment.ts +++ b/src/parse/state/fragment.ts @@ -8,7 +8,7 @@ export default function fragment(parser: Parser) { return tag; } - if (parser.match(parser.v2 ? '{' : '{{')) { + if (parser.match('{')) { return mustache; } diff --git a/src/parse/state/mustache.ts b/src/parse/state/mustache.ts index 922de7b532..ed15da9702 100644 --- a/src/parse/state/mustache.ts +++ b/src/parse/state/mustache.ts @@ -32,7 +32,7 @@ function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) { export default function mustache(parser: Parser) { const start = parser.index; - parser.index += parser.v2 ? 1 : 2; + parser.index += 1; parser.allowWhitespace(); @@ -64,7 +64,7 @@ export default function mustache(parser: Parser) { parser.eat(expected, true); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); while (block.elseif) { block.end = parser.index; @@ -86,7 +86,7 @@ export default function mustache(parser: Parser) { block.end = parser.index; parser.stack.pop(); - } else if (parser.eat(parser.v2 ? ':elseif' : 'elseif')) { + } else if (parser.eat(':elseif')) { const block = parser.current(); if (block.type !== 'IfBlock') parser.error({ @@ -99,7 +99,7 @@ export default function mustache(parser: Parser) { const expression = readExpression(parser); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); block.else = { start: parser.index, @@ -118,7 +118,7 @@ export default function mustache(parser: Parser) { }; parser.stack.push(block.else.children[0]); - } else if (parser.eat(parser.v2 ? ':else' : 'else')) { + } else if (parser.eat(':else')) { const block = parser.current(); if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { parser.error({ @@ -128,7 +128,7 @@ export default function mustache(parser: Parser) { } parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); block.else = { start: parser.index, @@ -138,7 +138,7 @@ export default function mustache(parser: Parser) { }; parser.stack.push(block.else); - } else if (parser.eat(parser.v2 ? ':then' : 'then')) { + } else if (parser.eat(':then')) { // TODO DRY out this and the next section const pendingBlock = parser.current(); if (pendingBlock.type === 'PendingBlock') { @@ -150,7 +150,7 @@ export default function mustache(parser: Parser) { awaitBlock.value = parser.readIdentifier(); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); const thenBlock: Node = { start, @@ -162,7 +162,7 @@ export default function mustache(parser: Parser) { awaitBlock.then = thenBlock; parser.stack.push(thenBlock); } - } else if (parser.eat(parser.v2 ? ':catch' : 'catch')) { + } else if (parser.eat(':catch')) { const thenBlock = parser.current(); if (thenBlock.type === 'ThenBlock') { thenBlock.end = start; @@ -173,7 +173,7 @@ export default function mustache(parser: Parser) { awaitBlock.error = parser.readIdentifier(); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); const catchBlock: Node = { start, @@ -336,7 +336,7 @@ export default function mustache(parser: Parser) { parser.allowWhitespace(); } - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); parser.current().children.push(block); parser.stack.push(block); @@ -346,44 +346,12 @@ export default function mustache(parser: Parser) { childBlock.start = parser.index; parser.stack.push(childBlock); } - } else if (parser.eat('yield')) { - // {{yield}} - // TODO deprecate - parser.allowWhitespace(); - - if (parser.v2) { - const expressionEnd = parser.index; - - parser.eat('}', true); - parser.current().children.push({ - start, - end: parser.index, - type: 'MustacheTag', - expression: { - start: expressionEnd - 5, - end: expressionEnd, - type: 'Identifier', - name: 'yield' - } - }); - } else { - parser.eat('}}', true); - - parser.current().children.push({ - start, - end: parser.index, - type: 'Element', - name: 'slot', - attributes: [], - children: [] - }); - } - } else if (parser.eat(parser.v2 ? '@html' : '{')) { + } else if (parser.eat('@html')) { // {{{raw}}} mustache const expression = readExpression(parser); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}}', true); + parser.eat('}', true); parser.current().children.push({ start, @@ -395,7 +363,7 @@ export default function mustache(parser: Parser) { const expression = readExpression(parser); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); parser.current().children.push({ start, diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index 27b180195d..e1f6af0ad4 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -11,8 +11,6 @@ import { Node } from '../../interfaces'; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const metaTags = new Map([ - [':Window', 'Window'], - [':Head', 'Head'], ['svelte:window', 'Window'], ['svelte:head', 'Head'] ]); @@ -34,6 +32,9 @@ const specials = new Map([ ], ]); +const SELF = 'svelte:self'; +const COMPONENT = 'svelte:component'; + // based on http://developers.whatwg.org/syntax.html#syntax-tag-omission const disallowedContents = new Map([ ['li', new Set(['li'])], @@ -198,7 +199,7 @@ export default function tag(parser: Parser) { parser.allowWhitespace(); } - if (parser.v2 && name === 'svelte:component') { + if (name === 'svelte:component') { // TODO post v2, treat this just as any other attribute const index = element.attributes.findIndex(attr => attr.name === 'this'); if (!~index) { @@ -264,21 +265,11 @@ export default function tag(parser: Parser) { element.end = parser.index; } else if (name === 'style') { // special case - if (parser.v2) { - const start = parser.index; - const data = parser.readUntil(/<\/style>/); - const end = parser.index; - element.children.push({ start, end, type: 'Text', data }); - parser.eat('', true); - } else { - element.children = readSequence( - parser, - () => - parser.template.slice(parser.index, parser.index + 8) === '' - ); - parser.read(/<\/style>/); - element.end = parser.index; - } + const start = parser.index; + const data = parser.readUntil(/<\/style>/); + const end = parser.index; + element.children.push({ start, end, type: 'Text', data }); + parser.eat('', true); } else { parser.stack.push(element); } @@ -287,10 +278,6 @@ export default function tag(parser: Parser) { function readTagName(parser: Parser) { const start = parser.index; - // TODO hoist these back to the top, post-v2 - const SELF = parser.v2 ? 'svelte:self' : ':Self'; - const COMPONENT = parser.v2 ? 'svelte:component' : ':Component'; - if (parser.eat(SELF)) { // check we're inside a block, otherwise this // will cause infinite recursion @@ -334,14 +321,14 @@ function readTagName(parser: Parser) { function readAttribute(parser: Parser, uniqueNames: Set) { const start = parser.index; - if (parser.eat(parser.v2 ? '{' : '{{')) { + if (parser.eat('{')) { parser.allowWhitespace(); if (parser.eat('...')) { const expression = readExpression(parser); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); return { start, @@ -350,13 +337,6 @@ function readAttribute(parser: Parser, uniqueNames: Set) { expression }; } else { - if (!parser.v2) { - parser.error({ - code: `expected-spread`, - message: 'Expected spread operator (...)' - }); - } - const valueStart = parser.index; const name = parser.readIdentifier(); @@ -449,7 +429,7 @@ function readSequence(parser: Parser, done: () => boolean) { }); return chunks; - } else if (parser.eat(parser.v2 ? '{' : '{{')) { + } else if (parser.eat('{')) { if (currentChunk.data) { currentChunk.end = index; chunks.push(currentChunk); @@ -457,7 +437,7 @@ function readSequence(parser: Parser, done: () => boolean) { const expression = readExpression(parser); parser.allowWhitespace(); - parser.eat(parser.v2 ? '}' : '}}', true); + parser.eat('}', true); chunks.push({ start: index, diff --git a/src/parse/state/text.ts b/src/parse/state/text.ts index 29da8f928a..72fea496ce 100644 --- a/src/parse/state/text.ts +++ b/src/parse/state/text.ts @@ -9,7 +9,7 @@ export default function text(parser: Parser) { while ( parser.index < parser.template.length && !parser.match('<') && - !parser.match(parser.v2 ? '{' : '{{') + !parser.match('{') ) { data += parser.template[parser.index++]; } diff --git a/src/server-side-rendering/register.js b/src/server-side-rendering/register.js index 75b10d29f9..fe1fe74c5f 100644 --- a/src/server-side-rendering/register.js +++ b/src/server-side-rendering/register.js @@ -2,29 +2,28 @@ import * as fs from 'fs'; import * as path from 'path'; import { compile } from '../index.ts'; -let compileOptions = {}; +let compileOptions = { + extensions: ['.html'] +}; function capitalise(name) { return name[0].toUpperCase() + name.slice(1); } export default function register(options) { - const { extensions } = options; - - if (extensions) { - _deregister('.html'); - extensions.forEach(_register); + if (options.extensions) { + compileOptions.extensions.forEach(deregisterExtension); + options.extensions.forEach(registerExtension); } - // TODO make this the default and remove in v2 - if (options) compileOptions = options; + compileOptions = options; } -function _deregister(extension) { +function deregisterExtension(extension) { delete require.extensions[extension]; } -function _register(extension) { +function registerExtension(extension) { require.extensions[extension] = function(module, filename) { const name = path.basename(filename) .slice(0, -path.extname(filename).length) @@ -43,4 +42,4 @@ function _register(extension) { }; } -_register('.html'); +registerExtension('.html'); \ No newline at end of file diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 820de23ea1..078f3f6458 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -19,8 +19,7 @@ export default function validateElement( } if (!isComponent && /^[A-Z]/.test(node.name[0])) { - // TODO upgrade to validator.error in v2 - validator.warn(node, { + validator.error(node, { code: `missing-component`, message: `${node.name} component is not defined` }); diff --git a/src/validate/index.ts b/src/validate/index.ts index 390dbf0714..9734124d26 100644 --- a/src/validate/index.ts +++ b/src/validate/index.ts @@ -11,7 +11,6 @@ import { Node, Parsed, CompileOptions, Warning } from '../interfaces'; export class Validator { readonly source: string; readonly filename: string; - readonly v2: boolean; readonly stats: Stats; options: CompileOptions; @@ -41,7 +40,6 @@ export class Validator { this.filename = options.filename; this.options = options; - this.v2 = options.parser === 'v2'; this.namespace = null; this.defaultExport = null; diff --git a/src/validate/js/propValidators/computed.ts b/src/validate/js/propValidators/computed.ts index 9d32553b94..d918fe3e8b 100644 --- a/src/validate/js/propValidators/computed.ts +++ b/src/validate/js/propValidators/computed.ts @@ -75,36 +75,19 @@ export default function computed(validator: Validator, prop: Node) { }); } - if (validator.v2) { - if (params.length > 1) { - validator.error(computation.value, { - code: `invalid-computed-arguments`, - message: `Computed properties must take a single argument` - }); - } - - const param = params[0]; - if (param.type !== 'ObjectPattern') { - // TODO in v2, allow the entire object to be passed in - validator.error(computation.value, { - code: `invalid-computed-argument`, - message: `Computed property argument must be a destructured object pattern` - }); - } - } else { - params.forEach((param: Node) => { - const valid = - param.type === 'Identifier' || - (param.type === 'AssignmentPattern' && - param.left.type === 'Identifier'); + if (params.length > 1) { + validator.error(computation.value, { + code: `invalid-computed-arguments`, + message: `Computed properties must take a single argument` + }); + } - if (!valid) { - // TODO change this for v2 - validator.error(param, { - code: `invalid-computed-arguments`, - message: `Computed properties cannot use destructuring in function parameters` - }); - } + const param = params[0]; + if (param.type !== 'ObjectPattern') { + // TODO post-v2, allow the entire object to be passed in + validator.error(computation.value, { + code: `invalid-computed-argument`, + message: `Computed property argument must be a destructured object pattern` }); } }); diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 524b9486ff..1987d8215d 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -110,9 +110,7 @@ describe("ssr", () => { delete require.cache[resolved]; }); - const compileOptions = Object.assign(config.compileOptions || {}, { - store: !!config.store - }); + const compileOptions = config.compileOptions || {}; require("../../ssr/register")(compileOptions); diff --git a/test/server-side-rendering/samples/attribute-dynamic/main.html b/test/server-side-rendering/samples/attribute-dynamic/main.html index 18732ab91a..1528a49478 100644 --- a/test/server-side-rendering/samples/attribute-dynamic/main.html +++ b/test/server-side-rendering/samples/attribute-dynamic/main.html @@ -1 +1 @@ -
{{color}} {{font}}
+
{color} {font}
diff --git a/test/server-side-rendering/samples/component-binding-renamed/main.html b/test/server-side-rendering/samples/component-binding-renamed/main.html index 7445660354..7f5e588534 100644 --- a/test/server-side-rendering/samples/component-binding-renamed/main.html +++ b/test/server-side-rendering/samples/component-binding-renamed/main.html @@ -1,4 +1,4 @@ -{{y}}{{y}} +{y}{y} diff --git a/test/server-side-rendering/samples/default-data-override/main.html b/test/server-side-rendering/samples/default-data-override/main.html index 3605487855..1014c2df07 100644 --- a/test/server-side-rendering/samples/default-data-override/main.html +++ b/test/server-side-rendering/samples/default-data-override/main.html @@ -1,4 +1,4 @@ -

{{foo}}

+

{foo}