diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 1a074e7825..e7187e309d 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -243,15 +243,17 @@ export default class Block { `); } - if (this.builders.claim.isEmpty()) { - properties.addBlock(`claim: ${this.generator.helper('noop')},`); - } else { - properties.addBlock(deindent` - claim: function ( nodes ) { - ${this.builders.claim} - ${!this.builders.hydrate.isEmpty() && `this.hydrate();`} - }, - `); + if (this.generator.hydratable) { + if (this.builders.claim.isEmpty()) { + properties.addBlock(`claim: ${this.generator.helper('noop')},`); + } else { + properties.addBlock(deindent` + claim: function ( nodes ) { + ${this.builders.claim} + ${!this.builders.hydrate.isEmpty() && `this.hydrate();`} + }, + `); + } } if (!this.builders.hydrate.isEmpty()) { diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index 5ba3d8ea3a..2c54e573a1 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -18,6 +18,8 @@ export class DomGenerator extends Generator { readonly: Set; metaBindings: string[]; + hydratable: boolean; + hasIntroTransitions: boolean; hasOutroTransitions: boolean; hasComplexBindings: boolean; @@ -34,6 +36,8 @@ export class DomGenerator extends Generator { this.readonly = new Set(); + this.hydratable = options.hydratable; + // initial values for e.g. window.innerWidth, if there's a <:Window> meta tag this.metaBindings = []; } @@ -236,9 +240,15 @@ export default function dom( )}( this._state, this ); if ( options.target ) { - var nodes = ${generator.helper('children')}( options.target ); - this._fragment.claim( nodes ); - nodes.forEach( ${generator.helper('detachNode')} ); + ${generator.hydratable ? + deindent` + var nodes = ${generator.helper('children')}( options.target ); + options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create(); + nodes.forEach( ${generator.helper('detachNode')} ); + ` : + deindent` + this._fragment.create(); + `} this._fragment.mount( options.target, null ); } diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 75ec6ad83e..5e16abb3eb 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -52,10 +52,13 @@ export default function visitElement( block.addVariable(name); block.builders.create.addLine(`${name} = ${getRenderStatement(generator, childState.namespace, node.name)};`); - block.builders.claim.addBlock(deindent` - ${name} = ${getClaimStatement(generator, childState.namespace, state.parentNodes, node)}; - var ${childState.parentNodes} = ${generator.helper('children')}( ${name} ); - `); + + if (generator.hydratable) { + block.builders.claim.addBlock(deindent` + ${name} = ${getClaimStatement(generator, childState.namespace, state.parentNodes, node)}; + var ${childState.parentNodes} = ${generator.helper('children')}( ${name} ); + `); + } if (state.parentNode) { block.builders.mount.addLine(`${block.generator.helper('appendNode')}( ${name}, ${state.parentNode} );`); @@ -63,24 +66,6 @@ export default function visitElement( block.builders.mount.addLine(`${block.generator.helper('insertNode')}( ${name}, ${block.target}, anchor );`); } - // block.addVariable(name); - - // block.builders.create.addLine( - // `${name} = ${getRenderStatement( - // generator, - // childState.namespace, - // node.name - // )};` - // ); - - // block.builders.claim.addLine( - // `${name} = ${getClaimStatement( - // generator, - // childState.namespace, - // node.name - // )};` - // ); - // add CSS encapsulation attribute if (generator.cssId && (!generator.cascade || state.isTopLevel)) { block.builders.hydrate.addLine( diff --git a/src/generators/dom/visitors/MustacheTag.ts b/src/generators/dom/visitors/MustacheTag.ts index f7747eedb2..7ab106a650 100644 --- a/src/generators/dom/visitors/MustacheTag.ts +++ b/src/generators/dom/visitors/MustacheTag.ts @@ -19,7 +19,7 @@ export default function visitMustacheTag( block.addElement( name, `${generator.helper('createText')}( ${value} = ${snippet} )`, - `${generator.helper('claimText')}( ${state.parentNodes}, ${value} = ${snippet} )`, + generator.hydratable ? `${generator.helper('claimText')}( ${state.parentNodes}, ${value} = ${snippet} )` : '', state.parentNode, true ); diff --git a/src/generators/dom/visitors/Text.ts b/src/generators/dom/visitors/Text.ts index ae0337990c..a14c25208c 100644 --- a/src/generators/dom/visitors/Text.ts +++ b/src/generators/dom/visitors/Text.ts @@ -13,7 +13,7 @@ export default function visitText( block.addElement( node._state.name, `${generator.helper('createText')}( ${JSON.stringify(node.data)} )`, - `${generator.helper('claimText')}( ${state.parentNodes}, ${JSON.stringify(node.data)} )`, + generator.hydratable ? `${generator.helper('claimText')}( ${state.parentNodes}, ${JSON.stringify(node.data)} )` : '', state.parentNode, node.usedAsAnchor ); diff --git a/src/interfaces.ts b/src/interfaces.ts index fd12939860..fbb745b7b8 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -41,6 +41,7 @@ export interface CompileOptions { dev?: boolean; shared?: boolean | string; cascade?: boolean; + hydratable?: boolean; onerror?: (error: Error) => void; onwarn?: (warning: Warning) => void; diff --git a/test/hydration/index.js b/test/hydration/index.js index 696de66739..1e291ed6b1 100644 --- a/test/hydration/index.js +++ b/test/hydration/index.js @@ -27,7 +27,7 @@ describe('hydration', () => { require.extensions['.html'] = function(module, filename) { const options = Object.assign( - { filename, name: getName(filename) }, + { filename, name: getName(filename), hydratable: true }, compileOptions ); let { code } = svelte.compile(fs.readFileSync(filename, 'utf-8'), options); @@ -76,6 +76,7 @@ describe('hydration', () => { const component = new SvelteComponent({ target, + hydrate: true, data: config.data }); diff --git a/test/runtime/index.js b/test/runtime/index.js index 3e16e0208e..6c599ce96d 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -49,7 +49,7 @@ describe("runtime", () => { const failed = new Set(); - function runTest(dir, shared) { + function runTest(dir, shared, hydrate) { if (dir[0] === ".") return; const config = loadConfig(`./runtime/samples/${dir}/_config.js`); @@ -61,13 +61,14 @@ describe("runtime", () => { (config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers)`, () => { if (failed.has(dir)) { // this makes debugging easier, by only printing compiled output once - throw new Error('skipping inline helpers test'); + throw new Error('skipping test, already failed'); } const cwd = path.resolve(`test/runtime/samples/${dir}`); compileOptions = config.compileOptions || {}; compileOptions.shared = shared; + compileOptions.hydratable = hydrate; compileOptions.dev = config.dev; // check that no ES2015+ syntax slipped in @@ -157,6 +158,7 @@ describe("runtime", () => { const component = new SvelteComponent({ target, + hydrate, data: config.data }); @@ -208,8 +210,9 @@ describe("runtime", () => { const shared = path.resolve("shared.js"); fs.readdirSync("test/runtime/samples").forEach(dir => { - runTest(dir, shared); - runTest(dir, null); + runTest(dir, shared, false); + runTest(dir, shared, true); + runTest(dir, null, false); }); it("fails if options.target is missing in dev mode", () => {