From 4e28c9b82c086afb753364a2bf1e80e4239dcbe7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 27 Jun 2017 11:03:09 -0400 Subject: [PATCH] hack sourcemap to mitigate confusion caused by missing data for each block (#681) --- src/generators/dom/visitors/EachBlock.ts | 28 +++++++++++++------ test/sourcemaps/index.js | 5 ++-- test/sourcemaps/samples/each-block/input.html | 3 ++ test/sourcemaps/samples/each-block/test.js | 17 +++++++++++ 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 test/sourcemaps/samples/each-block/input.html create mode 100644 test/sourcemaps/samples/each-block/test.js diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 82a117eaae..0e1e08c394 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -20,11 +20,19 @@ export default function visitEachBlock( ? block.getUniqueName(`${each_block}_anchor`) : (node.next && node.next._state.name) || 'null'; + // hack the sourcemap, so that if data is missing the bug + // is easy to find + let c = node.start + 3; + while (generator.source[c] !== 'e') c += 1; + generator.code.overwrite(c, c + 4, 'length'); + const length = `[✂${c}-${c+4}✂]`; + const mountOrIntro = node._block.hasIntroMethod ? 'intro' : 'mount'; const vars = { each_block, create_each_block, each_block_value, + length, iterations, params, anchor, @@ -62,7 +70,7 @@ export default function visitEachBlock( // TODO neaten this up... will end up with an empty line in the block block.builders.init.addBlock(deindent` - if ( !${each_block_value}.length ) { + if ( !${each_block_value}.${length} ) { ${each_block_else} = ${node.else._block.name}( ${params}, #component ); ${each_block_else}.create(); } @@ -79,9 +87,9 @@ export default function visitEachBlock( if (node.else._block.hasUpdateMethod) { block.builders.update.addBlock(deindent` - if ( !${each_block_value}.length && ${each_block_else} ) { + if ( !${each_block_value}.${length} && ${each_block_else} ) { ${each_block_else}.update( changed, ${params} ); - } else if ( !${each_block_value}.length ) { + } else if ( !${each_block_value}.${length} ) { ${each_block_else} = ${node.else._block.name}( ${params}, #component ); ${each_block_else}.create(); ${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} ); @@ -93,7 +101,7 @@ export default function visitEachBlock( `); } else { block.builders.update.addBlock(deindent` - if ( ${each_block_value}.length ) { + if ( ${each_block_value}.${length} ) { if ( ${each_block_else} ) { ${each_block_else}.unmount(); ${each_block_else}.destroy(); @@ -137,6 +145,7 @@ function keyed( each_block, create_each_block, each_block_value, + length, params, anchor, mountOrIntro, @@ -168,7 +177,7 @@ function keyed( } block.builders.init.addBlock(deindent` - for ( var #i = 0; #i < ${each_block_value}.length; #i += 1 ) { + for ( var #i = 0; #i < ${each_block_value}.${length}; #i += 1 ) { var ${key} = ${each_block_value}[#i].${node.key}; var ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component, ${key} ); @@ -268,7 +277,7 @@ function keyed( var discard_pile = []; - for ( #i = 0; #i < ${each_block_value}.length; #i += 1 ) { + for ( #i = 0; #i < ${each_block_value}.${length}; #i += 1 ) { var ${key} = ${each_block_value}[#i].${node.key}; var ${iteration} = ${lookup}[${key}]; @@ -357,6 +366,7 @@ function unkeyed( { create_each_block, each_block_value, + length, iterations, params, anchor, @@ -366,7 +376,7 @@ function unkeyed( block.builders.init.addBlock(deindent` var ${iterations} = []; - for ( var #i = 0; #i < ${each_block_value}.length; #i += 1 ) { + for ( var #i = 0; #i < ${each_block_value}.${length}; #i += 1 ) { ${iterations}[#i] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component ); } `); @@ -453,14 +463,14 @@ function unkeyed( ${iterations}[#i].unmount(); ${iterations}[#i].destroy(); } - ${iterations}.length = ${each_block_value}.length; + ${iterations}.length = ${each_block_value}.${length}; `; block.builders.update.addBlock(deindent` var ${each_block_value} = ${snippet}; if ( ${condition} ) { - for ( var #i = ${start}; #i < ${each_block_value}.length; #i += 1 ) { + for ( var #i = ${start}; #i < ${each_block_value}.${length}; #i += 1 ) { ${forLoopBody} } diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.js index f4aaabb069..2209ad0f7d 100644 --- a/test/sourcemaps/index.js +++ b/test/sourcemaps/index.js @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as path from "path"; import assert from "assert"; -import { svelte, exists } from "../helpers.js"; +import { svelte } from "../helpers.js"; import { SourceMapConsumer } from "source-map"; import { getLocator } from "locate-character"; @@ -9,7 +9,8 @@ describe("sourcemaps", () => { fs.readdirSync("test/sourcemaps/samples").forEach(dir => { if (dir[0] === ".") return; - const solo = exists(`test/sourcemaps/samples/${dir}/solo`); + // add .solo to a sample directory name to only run that test + const solo = /\.solo/.test(dir); if (solo && process.env.CI) { throw new Error("Forgot to remove `solo: true` from test"); diff --git a/test/sourcemaps/samples/each-block/input.html b/test/sourcemaps/samples/each-block/input.html new file mode 100644 index 0000000000..8144479672 --- /dev/null +++ b/test/sourcemaps/samples/each-block/input.html @@ -0,0 +1,3 @@ +{{#each foo as bar}} + {{bar}} +{{/each}} \ No newline at end of file diff --git a/test/sourcemaps/samples/each-block/test.js b/test/sourcemaps/samples/each-block/test.js new file mode 100644 index 0000000000..add51d628b --- /dev/null +++ b/test/sourcemaps/samples/each-block/test.js @@ -0,0 +1,17 @@ +export function test ({ assert, smc, locateInSource, locateInGenerated }) { + const expected = locateInSource( 'each' ); + + const loc = locateInGenerated( 'length' ); + + const actual = smc.originalPositionFor({ + line: loc.line + 1, + column: loc.column + }); + + assert.deepEqual( actual, { + source: 'input.html', + name: null, + line: expected.line + 1, + column: expected.column + }); +}