diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index e7243bc6c5..bd781c05fc 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -25,6 +25,51 @@ interface Computation { deps: string[] } +function detectIndentation(str: string) { + const pattern = /^[\t\s]{1,4}/gm; + let match; + + while (match = pattern.exec(str)) { + if (match[0][0] === '\t') return '\t'; + if (match[0].length === 2) return ' '; + } + + return ' '; +} + +function getIndentationLevel(str: string, b: number) { + let a = b; + while (a > 0 && str[a - 1] !== '\n') a -= 1; + return /^\s*/.exec(str.slice(a, b))[0]; +} + +function getIndentExclusionRanges(node: Node) { + const ranges: Node[] = []; + walk(node, { + enter(node: Node) { + if (node.type === 'TemplateElement') ranges.push(node); + } + }); + return ranges; +} + +function removeIndentation( + code: MagicString, + start: number, + end: number, + indentationLevel: string, + ranges: Node[] +) { + const str = code.original.slice(start, end); + const pattern = new RegExp(`^${indentationLevel}`, 'gm'); + let match; + + while (match = pattern.exec(str)) { + // TODO bail if we're inside an exclusion range + code.remove(start + match.index, start + match.index + indentationLevel.length); + } +} + export default class Generator { ast: Parsed; parsed: Parsed; @@ -45,7 +90,6 @@ export default class Generator { importedComponents: Map; namespace: string; hasComponents: boolean; - hasJs: boolean; computations: Computation[]; templateProperties: Record; slots: Set; @@ -409,7 +453,7 @@ export default class Generator { } parseJs() { - const { source } = this; + const { code, source } = this; const { js } = this.parsed; const imports = this.imports; @@ -418,11 +462,14 @@ export default class Generator { const componentDefinition = new CodeBuilder(); let namespace = null; - let hasJs = !!js; if (js) { this.addSourcemapLocations(js.content); + const indentation = detectIndentation(source.slice(js.start, js.end)); + const indentationLevel = getIndentationLevel(source, js.content.body[0].start); + const indentExclusionRanges = getIndentExclusionRanges(js.content); + const scope = annotateWithScopes(js.content); scope.declarations.forEach(name => { this.userVars.add(name); @@ -500,6 +547,12 @@ export default class Generator { let name = this.getUniqueName(key); this.templateVars.set(disambiguator ? `${disambiguator}-${key}` : key, name); + // deindent + const indentationLevel = getIndentationLevel(source, node.start); + if (indentationLevel) { + removeIndentation(code, node.start, node.end, indentationLevel, indentExclusionRanges); + } + // TODO disambiguate between different categories, and ensure // no conflicts with existing aliases if (node.type === 'ArrowFunctionExpression') { @@ -624,35 +677,46 @@ export default class Generator { } } - // now that we've analysed the default export, we can determine whether or not we need to keep it - let hasDefaultExport = !!defaultExport; - if (defaultExport && defaultExport.declaration.properties.length === 0) { - hasDefaultExport = false; - removeNode(this.code, js.content, defaultExport); - } - // if we do need to keep it, then we need to replace `export default` - if (hasDefaultExport) { - this.code.overwrite( - defaultExport.start, - defaultExport.declaration.start, - `var ${this.alias('template')} = ` - ); + // if (defaultExport) { + // this.code.overwrite( + // defaultExport.start, + // defaultExport.declaration.start, + // `var ${this.alias('template')} = ` + // ); + // } + + if (indentationLevel) { + if (defaultExport) { + removeIndentation(code, js.content.start, defaultExport.start, indentationLevel, indentExclusionRanges); + removeIndentation(code, defaultExport.end, js.content.end, indentationLevel, indentExclusionRanges); + } else { + removeIndentation(code, js.content.start, js.content.end, indentationLevel, indentExclusionRanges); + } } if (js.content.body.length === 0) { // if there's no need to include user code, remove it altogether this.code.remove(js.content.start, js.content.end); - hasJs = false; } - this.javascript = hasDefaultExport ? - `[✂${js.content.start}-${defaultExport.start}✂]${componentDefinition}[✂${defaultExport.end}-${js.content.end}✂]` : - `[✂${js.content.start}-${js.content.end}✂]`; + let a = js.content.start; + while (/\s/.test(source[a])) a += 1; + + let b = js.content.end; + while (/\s/.test(source[b - 1])) b -= 1; + + if (defaultExport) { + this.javascript = ''; + if (a !== defaultExport.start) this.javascript += `[✂${a}-${defaultExport.start}✂]`; + if (!componentDefinition.isEmpty()) this.javascript += componentDefinition; + if (defaultExport.end !== b) this.javascript += `[✂${defaultExport.end}-${b}✂]`; + } else { + this.javascript = a === b ? null : `[✂${a}-${b}✂]`; + } } this.computations = computations; - this.hasJs = hasJs; this.namespace = namespace; this.templateProperties = templateProperties; } diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index eee9c5669c..e9fe9805c7 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -92,7 +92,6 @@ export default function dom( const { computations, - hasJs, name, templateProperties, namespace, @@ -338,7 +337,7 @@ export default function dom( } ` : (!sharedPath && `${name}.prototype._recompute = @noop;`)} - ${templateProperties.setup && `@template.setup(${name});`} + ${templateProperties.setup && `%setup(${name});`} `); const usedHelpers = new Set(); diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index 5085ee9969..67f647a755 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -219,7 +219,7 @@ export default function visitComponent( const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get(node.name) || - `@template.components.${node.name}`; + `@template.components.${node.name}`; // TODO this is out of date block.builders.init.addBlock(deindent` ${statements.join('\n')} diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts index 96f1b1fe40..58d410fbe6 100644 --- a/src/generators/server-side-rendering/index.ts +++ b/src/generators/server-side-rendering/index.ts @@ -82,7 +82,7 @@ export default function ssr( const generator = new SsrGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options); - const { computations, name, hasJs, templateProperties } = generator; + const { computations, name, templateProperties } = generator; // create main render() function const mainBlock = new Block({ diff --git a/src/generators/server-side-rendering/visitors/Component.ts b/src/generators/server-side-rendering/visitors/Component.ts index 7af91a0aad..8c527ac361 100644 --- a/src/generators/server-side-rendering/visitors/Component.ts +++ b/src/generators/server-side-rendering/visitors/Component.ts @@ -72,7 +72,7 @@ export default function visitComponent( const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get(node.name) || - `@template.components.${node.name}`; + `@template.components.${node.name}`; // TODO out of date bindings.forEach(binding => { block.addBinding(binding, expression); diff --git a/test/js/index.js b/test/js/index.js index 3096bdf4c4..07bdc183de 100644 --- a/test/js/index.js +++ b/test/js/index.js @@ -4,7 +4,7 @@ import * as path from "path"; import { rollup } from "rollup"; import { loadConfig, svelte } from "../helpers.js"; -describe.skip("js", () => { +describe("js", () => { fs.readdirSync("test/js/samples").forEach(dir => { if (dir[0] === ".") return; @@ -62,13 +62,13 @@ describe.skip("js", () => { ); assert.equal( - actual.trim().replace(/^\s+$/gm, ""), - expected.trim().replace(/^\s+$/gm, "") + actual.trim().replace(/^[ \t]+$/gm, ""), + expected.trim().replace(/^[ \t]+$/gm, "") ); assert.equal( - code.trim().replace(/^\s+$/gm, ""), - expectedBundle.trim().replace(/^\s+$/gm, "") + code.trim().replace(/^[ \t]+$/gm, ""), + expectedBundle.trim().replace(/^[ \t]+$/gm, "") ); }).catch(err => { if (err.loc) console.error(err.loc); diff --git a/test/js/samples/collapses-text-around-comments/expected-bundle.js b/test/js/samples/collapses-text-around-comments/expected-bundle.js index 5d44708a27..a69b1fcc77 100644 --- a/test/js/samples/collapses-text-around-comments/expected-bundle.js +++ b/test/js/samples/collapses-text-around-comments/expected-bundle.js @@ -190,13 +190,9 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - data: function () { - return { foo: 42 } - } - }; -}()); +function data() { + return { foo: 42 } +} function encapsulateStyles(node) { setAttribute(node, "svelte-3590263702", ""); @@ -244,7 +240,7 @@ function create_main_fragment(state, component) { function SvelteComponent(options) { init(this, options); - this._state = assign(template.data(), options.data); + this._state = assign(data(), options.data); if (!document.getElementById("svelte-3590263702-style")) add_css(); diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index 83841b4eb8..f19c316d12 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -1,13 +1,9 @@ /* generated by Svelte vX.Y.Z */ import { appendNode, assign, createElement, createText, detachNode, init, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; -var template = (function() { - return { - data: function () { - return { foo: 42 } - } - }; -}()); +function data() { + return { foo: 42 } +}; function encapsulateStyles(node) { setAttribute(node, "svelte-3590263702", ""); @@ -55,7 +51,7 @@ function create_main_fragment(state, component) { function SvelteComponent(options) { init(this, options); - this._state = assign(template.data(), options.data); + this._state = assign(data(), options.data); if (!document.getElementById("svelte-3590263702-style")) add_css(); diff --git a/test/js/samples/component-static/expected-bundle.js b/test/js/samples/component-static/expected-bundle.js index a9769110cb..fc1860c060 100644 --- a/test/js/samples/component-static/expected-bundle.js +++ b/test/js/samples/component-static/expected-bundle.js @@ -166,17 +166,11 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - components: { - Nested: window.Nested - } - }; -}()); +var Nested = window.Nested; function create_main_fragment(state, component) { - var nested = new template.components.Nested({ + var nested = new Nested({ _root: component._root, data: { foo: "bar" } }); diff --git a/test/js/samples/component-static/expected.js b/test/js/samples/component-static/expected.js index 8a22292037..593f8b6a89 100644 --- a/test/js/samples/component-static/expected.js +++ b/test/js/samples/component-static/expected.js @@ -1,17 +1,11 @@ /* generated by Svelte vX.Y.Z */ import { assign, callAll, init, noop, proto } from "svelte/shared.js"; -var template = (function() { - return { - components: { - Nested: window.Nested - } - }; -}()); +var Nested = window.Nested; function create_main_fragment(state, component) { - var nested = new template.components.Nested({ + var nested = new Nested({ _root: component._root, data: { foo: "bar" } }); diff --git a/test/js/samples/computed-collapsed-if/expected-bundle.js b/test/js/samples/computed-collapsed-if/expected-bundle.js index e6e9152b1e..013e7f1bbc 100644 --- a/test/js/samples/computed-collapsed-if/expected-bundle.js +++ b/test/js/samples/computed-collapsed-if/expected-bundle.js @@ -166,14 +166,13 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - computed: { - a: x => x * 2, - b: x => x * 3 - } - }; -}()); +function a(x) { + return x * 2; +} + +function b(x) { + return x * 3; +} function create_main_fragment(state, component) { @@ -207,8 +206,8 @@ assign(SvelteComponent.prototype, proto); SvelteComponent.prototype._recompute = function _recompute(changed, state) { if (changed.x) { - if (differs(state.a, (state.a = template.computed.a(state.x)))) changed.a = true; - if (differs(state.b, (state.b = template.computed.b(state.x)))) changed.b = true; + if (differs(state.a, (state.a = a(state.x)))) changed.a = true; + if (differs(state.b, (state.b = b(state.x)))) changed.b = true; } }; diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js index 7b9207a955..21cd8bc338 100644 --- a/test/js/samples/computed-collapsed-if/expected.js +++ b/test/js/samples/computed-collapsed-if/expected.js @@ -1,14 +1,13 @@ /* generated by Svelte vX.Y.Z */ import { assign, differs, init, noop, proto } from "svelte/shared.js"; -var template = (function() { - return { - computed: { - a: x => x * 2, - b: x => x * 3 - } - }; -}()); +function a(x) { + return x * 2; +} + +function b(x) { + return x * 3; +} function create_main_fragment(state, component) { @@ -42,8 +41,8 @@ assign(SvelteComponent.prototype, proto); SvelteComponent.prototype._recompute = function _recompute(changed, state) { if (changed.x) { - if (differs(state.a, (state.a = template.computed.a(state.x)))) changed.a = true; - if (differs(state.b, (state.b = template.computed.b(state.x)))) changed.b = true; + if (differs(state.a, (state.a = a(state.x)))) changed.a = true; + if (differs(state.b, (state.b = b(state.x)))) changed.b = true; } } export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/event-handlers-custom/expected-bundle.js b/test/js/samples/event-handlers-custom/expected-bundle.js index 7b896bf8f1..d6794a3cfb 100644 --- a/test/js/samples/event-handlers-custom/expected-bundle.js +++ b/test/js/samples/event-handlers-custom/expected-bundle.js @@ -178,20 +178,15 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - methods: { - foo ( bar ) { - console.log( bar ); - } - }, - events: { - foo ( node, callback ) { - // code goes here - } - } - }; -}()); +function foo( node, callback ) { + // code goes here +} + +var methods = { + foo ( bar ) { + console.log( bar ); + } +}; function create_main_fragment(state, component) { var button, foo_handler; @@ -204,7 +199,7 @@ function create_main_fragment(state, component) { }, hydrate: function() { - foo_handler = template.events.foo.call(component, button, function(event) { + foo_handler = foo.call(component, button, function(event) { var state = component.get(); component.foo( state.bar ); }); @@ -238,6 +233,6 @@ function SvelteComponent(options) { } } -assign(SvelteComponent.prototype, template.methods, proto); +assign(SvelteComponent.prototype, methods, proto); export default SvelteComponent; diff --git a/test/js/samples/event-handlers-custom/expected.js b/test/js/samples/event-handlers-custom/expected.js index 34832b6000..ebf6a8ee03 100644 --- a/test/js/samples/event-handlers-custom/expected.js +++ b/test/js/samples/event-handlers-custom/expected.js @@ -1,20 +1,15 @@ /* generated by Svelte vX.Y.Z */ import { assign, createElement, detachNode, init, insertNode, noop, proto } from "svelte/shared.js"; -var template = (function() { - return { - methods: { - foo ( bar ) { - console.log( bar ); - } - }, - events: { - foo ( node, callback ) { - // code goes here - } - } - }; -}()); +function foo( node, callback ) { + // code goes here +}; + +var methods = { + foo ( bar ) { + console.log( bar ); + } +}; function create_main_fragment(state, component) { var button, foo_handler; @@ -27,7 +22,7 @@ function create_main_fragment(state, component) { }, hydrate: function() { - foo_handler = template.events.foo.call(component, button, function(event) { + foo_handler = foo.call(component, button, function(event) { var state = component.get(); component.foo( state.bar ); }); @@ -61,5 +56,5 @@ function SvelteComponent(options) { } } -assign(SvelteComponent.prototype, template.methods, proto); +assign(SvelteComponent.prototype, methods, proto); export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/non-imported-component/expected-bundle.js b/test/js/samples/non-imported-component/expected-bundle.js index 2252eb1635..9a68b51d17 100644 --- a/test/js/samples/non-imported-component/expected-bundle.js +++ b/test/js/samples/non-imported-component/expected-bundle.js @@ -180,14 +180,6 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - components: { - NonImported - } - }; -}()); - function create_main_fragment(state, component) { var text; @@ -195,7 +187,7 @@ function create_main_fragment(state, component) { _root: component._root }); - var nonimported = new template.components.NonImported({ + var nonimported = new NonImported({ _root: component._root }); diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js index e424bb6ffd..6959e54da5 100644 --- a/test/js/samples/non-imported-component/expected.js +++ b/test/js/samples/non-imported-component/expected.js @@ -2,13 +2,7 @@ import { assign, callAll, createText, detachNode, init, insertNode, noop, proto } from "svelte/shared.js"; import Imported from 'Imported.html'; -var template = (function() { - return { - components: { - NonImported - } - }; -}()); + function create_main_fragment(state, component) { var text; @@ -17,7 +11,7 @@ function create_main_fragment(state, component) { _root: component._root }); - var nonimported = new template.components.NonImported({ + var nonimported = new NonImported({ _root: component._root }); diff --git a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js index bebd336b6c..bf7688dc3a 100644 --- a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js +++ b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js @@ -166,13 +166,9 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - // this test should be removed in v2 - oncreate () {}, - ondestroy () {} - }; -}()); +function oncreate() {} + +function ondestroy() {} function create_main_fragment(state, component) { @@ -193,14 +189,14 @@ function SvelteComponent(options) { init(this, options); this._state = options.data || {}; - this._handlers.destroy = [template.ondestroy]; + this._handlers.destroy = [ondestroy]; - var oncreate = template.oncreate.bind(this); + var _oncreate = oncreate.bind(this); if (!options._root) { - this._oncreate = [oncreate]; + this._oncreate = [_oncreate]; } else { - this._root._oncreate.push(oncreate); + this._root._oncreate.push(_oncreate); } this._fragment = create_main_fragment(this._state, this); diff --git a/test/js/samples/onrender-onteardown-rewritten/expected.js b/test/js/samples/onrender-onteardown-rewritten/expected.js index d29f9fd752..21a5d0ed86 100644 --- a/test/js/samples/onrender-onteardown-rewritten/expected.js +++ b/test/js/samples/onrender-onteardown-rewritten/expected.js @@ -1,13 +1,9 @@ /* generated by Svelte vX.Y.Z */ import { assign, callAll, init, noop, proto } from "svelte/shared.js"; -var template = (function() { - return { - // this test should be removed in v2 - oncreate () {}, - ondestroy () {} - }; -}()); +function oncreate() {}; + +function ondestroy() {}; function create_main_fragment(state, component) { @@ -28,14 +24,14 @@ function SvelteComponent(options) { init(this, options); this._state = options.data || {}; - this._handlers.destroy = [template.ondestroy] + this._handlers.destroy = [ondestroy] - var oncreate = template.oncreate.bind(this); + var _oncreate = oncreate.bind(this); if (!options._root) { - this._oncreate = [oncreate]; + this._oncreate = [_oncreate]; } else { - this._root._oncreate.push(oncreate); + this._root._oncreate.push(_oncreate); } this._fragment = create_main_fragment(this._state, this); diff --git a/test/js/samples/setup-method/expected-bundle.js b/test/js/samples/setup-method/expected-bundle.js index 85bc643281..f92175d261 100644 --- a/test/js/samples/setup-method/expected-bundle.js +++ b/test/js/samples/setup-method/expected-bundle.js @@ -166,24 +166,21 @@ var proto = { }; /* generated by Svelte vX.Y.Z */ -var template = (function() { - return { - methods: { - foo ( bar ) { - console.log( bar ); - } - }, - setup: (Component) => { - Component.SOME_CONSTANT = 42; - Component.factory = function (target) { - return new Component({ - target: target - }); - }; - Component.prototype.foo( 'baz' ); - } +var methods = { + foo ( bar ) { + console.log( bar ); + } +}; + +function setup(Component) { + Component.SOME_CONSTANT = 42; + Component.factory = function (target) { + return new Component({ + target: target + }); }; -}()); + Component.prototype.foo( 'baz' ); +} function create_main_fragment(state, component) { @@ -212,8 +209,8 @@ function SvelteComponent(options) { } } -assign(SvelteComponent.prototype, template.methods, proto); +assign(SvelteComponent.prototype, methods, proto); -template.setup(SvelteComponent); +setup(SvelteComponent); export default SvelteComponent; diff --git a/test/js/samples/setup-method/expected.js b/test/js/samples/setup-method/expected.js index b3bd9fcc8e..cfacd329dc 100644 --- a/test/js/samples/setup-method/expected.js +++ b/test/js/samples/setup-method/expected.js @@ -1,24 +1,21 @@ /* generated by Svelte vX.Y.Z */ import { assign, init, noop, proto } from "svelte/shared.js"; -var template = (function() { - return { - methods: { - foo ( bar ) { - console.log( bar ); - } - }, - setup: (Component) => { - Component.SOME_CONSTANT = 42; - Component.factory = function (target) { - return new Component({ - target: target - }); - } - Component.prototype.foo( 'baz' ); - } - }; -}()); +var methods = { + foo ( bar ) { + console.log( bar ); + } +}; + +function setup(Component) { + Component.SOME_CONSTANT = 42; + Component.factory = function (target) { + return new Component({ + target: target + }); + } + Component.prototype.foo( 'baz' ); +} function create_main_fragment(state, component) { @@ -47,7 +44,7 @@ function SvelteComponent(options) { } } -assign(SvelteComponent.prototype, template.methods, proto); +assign(SvelteComponent.prototype, methods, proto); -template.setup(SvelteComponent); +setup(SvelteComponent); export default SvelteComponent; \ No newline at end of file diff --git a/test/runtime/samples/setup/_config.js b/test/runtime/samples/setup/_config.js new file mode 100644 index 0000000000..47b0cf332e --- /dev/null +++ b/test/runtime/samples/setup/_config.js @@ -0,0 +1,5 @@ +export default { + test(assert, component) { + assert.ok(component.constructor.FOO); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/setup/main.html b/test/runtime/samples/setup/main.html new file mode 100644 index 0000000000..a1192323c9 --- /dev/null +++ b/test/runtime/samples/setup/main.html @@ -0,0 +1,7 @@ + \ No newline at end of file