Merge pull request #864 from sveltejs/no-template-iife

Remove template IIFE
pull/867/head
Rich Harris 8 years ago committed by GitHub
commit 262456d41c

@ -1,6 +1,8 @@
import MagicString, { Bundle } from 'magic-string';
import { walk } from 'estree-walker';
import { getLocator } from 'locate-character';
import deindent from '../utils/deindent';
import CodeBuilder from '../utils/CodeBuilder';
import getCodeFrame from '../utils/getCodeFrame';
import isReference from '../utils/isReference';
import flattenReference from '../utils/flattenReference';
@ -23,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;
@ -43,10 +90,10 @@ export default class Generator {
importedComponents: Map<string, string>;
namespace: string;
hasComponents: boolean;
hasJs: boolean;
computations: Computation[];
templateProperties: Record<string, Node>;
slots: Set<string>;
javascript: string;
code: MagicString;
@ -59,7 +106,8 @@ export default class Generator {
stylesheet: Stylesheet;
importedNames: Set<string>;
userVars: Set<string>;
templateVars: Map<string, string>;
aliases: Map<string, string>;
usedNames: Set<string>;
@ -68,7 +116,8 @@ export default class Generator {
source: string,
name: string,
stylesheet: Stylesheet,
options: CompileOptions
options: CompileOptions,
dom: boolean
) {
this.ast = clone(parsed);
@ -101,11 +150,12 @@ export default class Generator {
// allow compiler to deconflict user's `import { get } from 'whatever'` and
// Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`;
this.importedNames = new Set();
this.userVars = new Set();
this.templateVars = new Map();
this.aliases = new Map();
this.usedNames = new Set();
this.parseJs();
this.parseJs(dom);
this.name = this.alias(name);
if (options.customElement === true) {
@ -198,7 +248,11 @@ export default class Generator {
usedContexts.add(name);
} else if (helpers.has(name)) {
code.prependRight(node.start, `${self.alias('template')}.helpers.`);
let object = node;
while (object.type === 'MemberExpression') object = object.object;
const alias = self.templateVars.get(`helpers-${name}`);
if (alias !== name) code.overwrite(object.start, object.end, alias);
} else if (indexes.has(name)) {
const context = indexes.get(name);
usedContexts.add(context); // TODO is this right?
@ -367,7 +421,7 @@ export default class Generator {
for (
let i = 1;
reservedNames.has(alias) ||
this.importedNames.has(alias) ||
this.userVars.has(alias) ||
this.usedNames.has(alias);
alias = `${name}_${i++}`
);
@ -383,7 +437,7 @@ export default class Generator {
}
reservedNames.forEach(add);
this.importedNames.forEach(add);
this.userVars.forEach(add);
return (name: string) => {
if (test) name = `${name}$`;
@ -399,30 +453,40 @@ export default class Generator {
};
}
parseJs() {
const { source } = this;
parseJs(dom: boolean) {
const { code, source } = this;
const { js } = this.parsed;
const imports = this.imports;
const computations: Computation[] = [];
const templateProperties: Record<string, Node> = {};
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);
});
const body = js.content.body.slice(); // slice, because we're going to be mutating the original
// imports need to be hoisted out of the IIFE
for (let i = 0; i < body.length; i += 1) {
const node = body[i];
if (node.type === 'ImportDeclaration') {
removeNode(this.code, js.content, node);
removeNode(code, js.content, node);
imports.push(node);
node.specifiers.forEach((specifier: Node) => {
this.importedNames.add(specifier.local.name);
this.userVars.add(specifier.local.name);
});
}
}
@ -435,176 +499,197 @@ export default class Generator {
defaultExport.declaration.properties.forEach((prop: Node) => {
templateProperties[prop.key.name] = prop;
});
}
['helpers', 'events', 'components', 'transitions'].forEach(key => {
if (templateProperties[key]) {
templateProperties[key].value.properties.forEach((prop: Node) => {
this[key].add(prop.key.name);
['helpers', 'events', 'components', 'transitions'].forEach(key => {
if (templateProperties[key]) {
templateProperties[key].value.properties.forEach((prop: Node) => {
this[key].add(prop.key.name);
});
}
});
const addArrowFunctionExpression = (name: string, node: Node) => {
const { body, params } = node;
const paramString = params.length ?
`[✂${params[0].start}-${params[params.length - 1].end}✂]` :
``;
if (body.type === 'BlockStatement') {
componentDefinition.addBlock(deindent`
function ${name}(${paramString}) [${body.start}-${body.end}]
`);
} else {
componentDefinition.addBlock(deindent`
function ${name}(${paramString}) {
return [${body.start}-${body.end}];
}
`);
}
};
const addFunctionExpression = (name: string, node: Node) => {
let c = node.start;
while (this.source[c] !== '(') c += 1;
componentDefinition.addBlock(deindent`
function ${name}[${c}-${node.end}];
`);
};
const addValue = (name: string, node: Node) => {
componentDefinition.addBlock(deindent`
var ${name} = [${node.start}-${node.end}];
`);
};
const addDeclaration = (key: string, node: Node, disambiguator?: string) => {
const qualified = disambiguator ? `${disambiguator}-${key}` : key;
if (node.type === 'Identifier' && node.name === key) {
this.templateVars.set(qualified, key);
return;
}
let name = this.getUniqueName(key);
this.templateVars.set(qualified, name);
// deindent
const indentationLevel = getIndentationLevel(source, node.start);
if (indentationLevel) {
removeIndentation(code, node.start, node.end, indentationLevel, indentExclusionRanges);
}
if (node.type === 'ArrowFunctionExpression') {
addArrowFunctionExpression(name, node);
} else if (node.type === 'FunctionExpression') {
addFunctionExpression(name, node);
} else {
addValue(name, node);
}
};
if (templateProperties.components) {
templateProperties.components.value.properties.forEach((property: Node) => {
addDeclaration(property.key.name, property.value, 'components');
});
}
});
if (templateProperties.computed) {
const dependencies = new Map();
if (templateProperties.computed) {
const dependencies = new Map();
templateProperties.computed.value.properties.forEach((prop: Node) => {
const key = prop.key.name;
const value = prop.value;
templateProperties.computed.value.properties.forEach((prop: Node) => {
const key = prop.key.name;
const value = prop.value;
const deps = value.params.map(
(param: Node) =>
param.type === 'AssignmentPattern' ? param.left.name : param.name
);
dependencies.set(key, deps);
});
const deps = value.params.map(
(param: Node) =>
param.type === 'AssignmentPattern' ? param.left.name : param.name
);
dependencies.set(key, deps);
});
const visited = new Set();
const visited = new Set();
const visit = function visit(key: string) {
if (!dependencies.has(key)) return; // not a computation
const visit = (key: string) => {
if (!dependencies.has(key)) return; // not a computation
if (visited.has(key)) return;
visited.add(key);
if (visited.has(key)) return;
visited.add(key);
const deps = dependencies.get(key);
deps.forEach(visit);
const deps = dependencies.get(key);
deps.forEach(visit);
computations.push({ key, deps });
}
computations.push({ key, deps });
templateProperties.computed.value.properties.forEach((prop: Node) =>
visit(prop.key.name)
);
}
const prop = templateProperties.computed.value.properties.find((prop: Node) => prop.key.name === key);
addDeclaration(key, prop.value, 'computed');
};
if (templateProperties.namespace) {
const ns = templateProperties.namespace.value.value;
namespace = namespaces[ns] || ns;
templateProperties.computed.value.properties.forEach((prop: Node) =>
visit(prop.key.name)
);
}
removeObjectKey(this.code, defaultExport.declaration, 'namespace');
}
if (templateProperties.data) {
addDeclaration('data', templateProperties.data.value);
}
if (templateProperties.components) {
let hasNonImportedComponent = false;
templateProperties.components.value.properties.forEach(
(property: Node) => {
const key = property.key.name;
const value = source.slice(
property.value.start,
property.value.end
);
if (this.importedNames.has(value)) {
this.importedComponents.set(key, value);
} else {
hasNonImportedComponent = true;
}
}
);
if (hasNonImportedComponent) {
// remove the specific components that were imported, as we'll refer to them directly
Array.from(this.importedComponents.keys()).forEach(key => {
removeObjectKey(
this.code,
templateProperties.components.value,
key
);
if (templateProperties.events && dom) {
templateProperties.events.value.properties.forEach((property: Node) => {
addDeclaration(property.key.name, property.value, 'events');
});
} else {
// remove the entire components portion of the export
removeObjectKey(this.code, defaultExport.declaration, 'components');
}
}
// Remove these after version 2
if (templateProperties.onrender) {
const { key } = templateProperties.onrender;
this.code.overwrite(key.start, key.end, 'oncreate', {
storeName: true,
contentOnly: false,
});
templateProperties.oncreate = templateProperties.onrender;
}
if (templateProperties.helpers) {
templateProperties.helpers.value.properties.forEach((property: Node) => {
addDeclaration(property.key.name, property.value, 'helpers');
});
}
if (templateProperties.onteardown) {
const { key } = templateProperties.onteardown;
this.code.overwrite(key.start, key.end, 'ondestroy', {
storeName: true,
contentOnly: false,
});
templateProperties.ondestroy = templateProperties.onteardown;
}
if (templateProperties.methods && dom) {
addDeclaration('methods', templateProperties.methods.value);
}
if (templateProperties.tag) {
this.tag = templateProperties.tag.value.value;
removeObjectKey(this.code, defaultExport.declaration, 'tag');
}
if (templateProperties.namespace) {
const ns = templateProperties.namespace.value.value;
namespace = namespaces[ns] || ns;
}
if (templateProperties.props) {
this.props = templateProperties.props.value.elements.map((element: Node) => element.value);
removeObjectKey(this.code, defaultExport.declaration, 'props');
}
if (templateProperties.onrender) templateProperties.oncreate = templateProperties.onrender; // remove after v2
if (templateProperties.oncreate && dom) {
addDeclaration('oncreate', templateProperties.oncreate.value);
}
// 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 (templateProperties.onteardown) templateProperties.ondestroy = templateProperties.onteardown; // remove after v2
if (templateProperties.ondestroy && dom) {
addDeclaration('ondestroy', templateProperties.ondestroy.value);
}
// if we do need to keep it, then we need to generate a return statement
if (hasDefaultExport) {
const finalNode = body[body.length - 1];
if (defaultExport === finalNode) {
// export is last property, we can just return it
this.code.overwrite(
defaultExport.start,
defaultExport.declaration.start,
`return `
);
} else {
const { declarations } = annotateWithScopes(js);
let template = 'template';
for (
let i = 1;
declarations.has(template);
template = `template_${i++}`
);
if (templateProperties.props) {
this.props = templateProperties.props.value.elements.map((element: Node) => element.value);
}
this.code.overwrite(
defaultExport.start,
defaultExport.declaration.start,
`var ${template} = `
);
if (templateProperties.setup) {
addDeclaration('setup', templateProperties.setup.value);
}
let i = defaultExport.start;
while (/\s/.test(source[i - 1])) i--;
if (templateProperties.tag) {
this.tag = templateProperties.tag.value.value;
}
const indentation = source.slice(i, defaultExport.start);
this.code.appendLeft(
finalNode.end,
`\n\n${indentation}return ${template};`
);
if (templateProperties.transitions) {
templateProperties.transitions.value.properties.forEach((property: Node) => {
addDeclaration(property.key.name, property.value, 'transitions');
});
}
}
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);
}
}
// user code gets wrapped in an IIFE
if (js.content.body.length) {
const prefix = hasDefaultExport
? `var ${this.alias('template')} = (function() {`
: `(function() {`;
this.code
.prependRight(js.content.start, prefix)
.appendLeft(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 {
// 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 = a === b ? null : `[✂${a}-${b}✂]`;
}
}
this.computations = computations;
this.hasJs = hasJs;
this.namespace = namespace;
this.templateProperties = templateProperties;
}

@ -39,7 +39,7 @@ export class DomGenerator extends Generator {
stylesheet: Stylesheet,
options: CompileOptions
) {
super(parsed, source, name, stylesheet, options);
super(parsed, source, name, stylesheet, options, true);
this.blocks = [];
this.readonly = new Set();
@ -60,7 +60,7 @@ export class DomGenerator extends Generator {
}
reservedNames.forEach(add);
this.importedNames.forEach(add);
this.userVars.forEach(add);
for (const name in shared) {
localUsedNames.add(test ? `${name}$` : name);
}
@ -92,7 +92,6 @@ export default function dom(
const {
computations,
hasJs,
name,
templateProperties,
namespace,
@ -127,7 +126,7 @@ export default function dom(
const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`;
const statement = `if (@differs(state.${key}, (state.${key} = @template.computed.${key}(${deps
const statement = `if (@differs(state.${key}, (state.${key} = %computed-${key}(${deps
.map(dep => `state.${dep}`)
.join(', ')})))) changed.${key} = true;`;
@ -135,8 +134,8 @@ export default function dom(
});
}
if (hasJs) {
builder.addBlock(`[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`);
if (generator.javascript) {
builder.addBlock(generator.javascript);
}
if (generator.needsEncapsulateHelper) {
@ -173,7 +172,7 @@ export default function dom(
const prototypeBase =
`${name}.prototype` +
(templateProperties.methods ? `, @template.methods` : '');
(templateProperties.methods ? `, %methods` : '');
const proto = sharedPath
? `@proto`
: deindent`
@ -192,7 +191,7 @@ export default function dom(
@init(this, options);
${generator.usesRefs && `this.refs = {};`}
this._state = ${templateProperties.data
? `@assign(@template.data(), options.data)`
? `@assign(%data(), options.data)`
: `options.data || {}`};
${generator.metaBindings}
${computations.length && `this._recompute({ ${Array.from(computationDeps).map(dep => `${dep}: 1`).join(', ')} }, this._state);`}
@ -204,7 +203,7 @@ export default function dom(
${generator.bindingGroups.length &&
`this._bindingGroups = [${Array(generator.bindingGroups.length).fill('[]').join(', ')}];`}
${templateProperties.ondestroy && `this._handlers.destroy = [@template.ondestroy]`}
${templateProperties.ondestroy && `this._handlers.destroy = [%ondestroy]`}
${generator.slots.size && `this._slotted = options.slots || {};`}
@ -217,16 +216,16 @@ export default function dom(
`if (!document.getElementById("${generator.stylesheet.id}-style")) @add_css();`)
}
${templateProperties.oncreate && `var oncreate = @template.oncreate.bind(this);`}
${templateProperties.oncreate && `var _oncreate = %oncreate.bind(this);`}
${(templateProperties.oncreate || generator.hasComponents || generator.hasComplexBindings || generator.hasIntroTransitions) && deindent`
if (!options._root) {
this._oncreate = [${templateProperties.oncreate && `oncreate`}];
this._oncreate = [${templateProperties.oncreate && `_oncreate`}];
${(generator.hasComponents || generator.hasComplexBindings) && `this._beforecreate = [];`}
${(generator.hasComponents || generator.hasIntroTransitions) && `this._aftercreate = [];`}
} ${templateProperties.oncreate && deindent`
else {
this._root._oncreate.push(oncreate);
this._root._oncreate.push(_oncreate);
}
`}
`}
@ -338,22 +337,28 @@ export default function dom(
}
` : (!sharedPath && `${name}.prototype._recompute = @noop;`)}
${templateProperties.setup && `@template.setup(${name});`}
${templateProperties.setup && `%setup(${name});`}
`);
const usedHelpers = new Set();
let result = builder
.toString()
.replace(/(@+)(\w*)/g, (match: string, sigil: string, name: string) => {
if (sigil !== '@') return sigil.slice(1) + name;
.replace(/(%+|@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => {
if (sigil === '@') {
if (name in shared) {
if (options.dev && `${name}Dev` in shared) name = `${name}Dev`;
usedHelpers.add(name);
}
return generator.alias(name);
}
if (name in shared) {
if (options.dev && `${name}Dev` in shared) name = `${name}Dev`;
usedHelpers.add(name);
if (sigil === '%') {
return generator.templateVars.get(name);
}
return generator.alias(name);
return sigil.slice(1) + name;
});
let helpers;

@ -216,10 +216,7 @@ export default function visitComponent(
}
}
const expression = node.name === ':Self'
? generator.name
: generator.importedComponents.get(node.name) ||
`@template.components.${node.name}`;
const expression = node.name === ':Self' ? generator.name : `%components-${node.name}`;
block.builders.init.addBlock(deindent`
${statements.join('\n')}

@ -79,7 +79,7 @@ export default function visitEventHandler(
block.addVariable(handlerName);
block.builders.hydrate.addBlock(deindent`
${handlerName} = @template.events.${name}.call(#component, ${state.parentNode}, function(event) {
${handlerName} = %events-${name}.call(#component, ${state.parentNode}, function(event) {
${handlerBody}
});
`);

@ -20,7 +20,7 @@ export default function addTransitions(
block.addVariable(name);
const fn = `@template.transitions.${intro.name}`;
const fn = `%transitions-${intro.name}`;
block.builders.intro.addBlock(deindent`
#component._root._aftercreate.push(function() {
@ -48,7 +48,7 @@ export default function addTransitions(
? block.contextualise(intro.expression).snippet
: '{}';
const fn = `@template.transitions.${intro.name}`; // TODO add built-in transitions?
const fn = `%transitions-${intro.name}`; // TODO add built-in transitions?
if (outro) {
block.builders.intro.addBlock(deindent`
@ -73,7 +73,7 @@ export default function addTransitions(
? block.contextualise(outro.expression).snippet
: '{}';
const fn = `@template.transitions.${outro.name}`;
const fn = `%transitions-${outro.name}`;
// TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM

@ -21,42 +21,14 @@ export class SsrGenerator extends Generator {
stylesheet: Stylesheet,
options: CompileOptions
) {
super(parsed, source, name, stylesheet, options);
super(parsed, source, name, stylesheet, options, false);
this.bindings = [];
this.renderCode = '';
this.appendTargets = [];
// in an SSR context, we don't need to include events, methods, oncreate or ondestroy
const { templateProperties, defaultExport } = this;
preprocess(this, parsed.html);
this.stylesheet.warnOnUnusedSelectors(options.onwarn);
if (templateProperties.oncreate)
removeNode(
this.code,
defaultExport.declaration,
templateProperties.oncreate
);
if (templateProperties.ondestroy)
removeNode(
this.code,
defaultExport.declaration,
templateProperties.ondestroy
);
if (templateProperties.methods)
removeNode(
this.code,
defaultExport.declaration,
templateProperties.methods
);
if (templateProperties.events)
removeNode(
this.code,
defaultExport.declaration,
templateProperties.events
);
}
append(code: string) {
@ -80,7 +52,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({
@ -99,24 +71,24 @@ export default function ssr(
generator.stylesheet.render(options.filename, true);
const result = deindent`
${hasJs && `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`}
${generator.javascript}
var ${name} = {};
${options.filename && `${name}.filename = ${stringify(options.filename)}`};
${name}.data = function() {
return ${templateProperties.data ? `@template.data()` : `{}`};
return ${templateProperties.data ? `%data()` : `{}`};
};
${name}.render = function(state, options) {
${templateProperties.data
? `state = Object.assign(@template.data(), state || {});`
? `state = Object.assign(%data(), state || {});`
: `state = state || {};`}
${computations.map(
({ key, deps }) =>
`state.${key} = @template.computed.${key}(${deps.map(dep => `state.${dep}`).join(', ')});`
`state.${key} = %computed-${key}(${deps.map(dep => `state.${dep}`).join(', ')});`
)}
${generator.bindings.length &&
@ -159,10 +131,8 @@ export default function ssr(
});
}
${templateProperties.components.value.properties.map(prop => {
const { name } = prop.key;
const expression = generator.importedComponents.get(name) || `@template.components.${name}`;
return `addComponent(${expression});`;
${templateProperties.components.value.properties.map((prop: Node) => {
return `addComponent(%components-${prop.key.name});`;
})}
`}
@ -184,8 +154,10 @@ export default function ssr(
function __escape(html) {
return String(html).replace(/["'&<>]/g, match => escaped[match]);
}
`.replace(/(@+|#+)(\w*)/g, (match: string, sigil: string, name: string) => {
return sigil === '@' ? generator.alias(name) : sigil.slice(1) + name;
`.replace(/(@+|#+|%+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => {
if (sigil === '@') return generator.alias(name);
if (sigil === '%') return generator.templateVars.get(name);
return sigil.slice(1) + name;
});
return generator.generate(result, options, { name, format });

@ -69,10 +69,7 @@ export default function visitComponent(
)
.join(', ');
const expression = node.name === ':Self'
? generator.name
: generator.importedComponents.get(node.name) ||
`@template.components.${node.name}`;
const expression = node.name === ':Self' ? generator.name : `%components-${node.name}`;
bindings.forEach(binding => {
block.addBinding(binding, expression);

@ -3,7 +3,7 @@ export function stringify(data: string, options = {}) {
}
export function escape(data: string, { onlyEscapeAtSymbol = false } = {}) {
return data.replace(onlyEscapeAtSymbol ? /(@+)/g : /(@+|#+)/g, (match: string) => {
return data.replace(onlyEscapeAtSymbol ? /(%+|@+)/g : /(%+|@+|#+)/g, (match: string) => {
return match + match[0];
});
}

@ -101,34 +101,44 @@ describe('css', () => {
if (expected.html !== null) {
const window = env();
const Component = eval(
`(function () { ${dom.code}; return SvelteComponent; }())`
);
const target = window.document.querySelector('main');
new Component({ target, data: config.data });
const html = target.innerHTML;
fs.writeFileSync(`test/css/samples/${dir}/_actual.html`, html);
// dom
assert.equal(
normalizeHtml(window, html.replace(/svelte-\d+/g, 'svelte-xyz')),
normalizeHtml(window, expected.html)
);
try {
const Component = eval(
`(function () { ${dom.code}; return SvelteComponent; }())`
);
const target = window.document.querySelector('main');
new Component({ target, data: config.data });
const html = target.innerHTML;
fs.writeFileSync(`test/css/samples/${dir}/_actual.html`, html);
assert.equal(
normalizeHtml(window, html.replace(/svelte-\d+/g, 'svelte-xyz')),
normalizeHtml(window, expected.html)
);
} catch (err) {
console.log(dom.code);
throw err;
}
// ssr
const component = eval(
`(function () { ${ssr.code}; return SvelteComponent; }())`
);
assert.equal(
normalizeHtml(
window,
component.render(config.data).replace(/svelte-\d+/g, 'svelte-xyz')
),
normalizeHtml(window, expected.html)
);
try {
const component = eval(
`(function () { ${ssr.code}; return SvelteComponent; }())`
);
assert.equal(
normalizeHtml(
window,
component.render(config.data).replace(/svelte-\d+/g, 'svelte-xyz')
),
normalizeHtml(window, expected.html)
);
} catch (err) {
console.log(ssr.code);
throw err;
}
}
});
});
@ -140,4 +150,4 @@ function read(file) {
} catch (err) {
return null;
}
}
}

@ -62,13 +62,13 @@ describe("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);

@ -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();

@ -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();

@ -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" }
});

@ -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" }
});

@ -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;
}
};

@ -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;

@ -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) {
},
h: function hydrate() {
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;

@ -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) {
},
h: function hydrate() {
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;

@ -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
});

@ -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
});

@ -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);

@ -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);

@ -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;

@ -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;

@ -0,0 +1,5 @@
export default {
options: {
generate: 'ssr'
}
};

@ -0,0 +1,23 @@
var SvelteComponent = {};
SvelteComponent.data = function() {
return {};
};
SvelteComponent.render = function(state, options) {
state = state || {};
return ``.trim();
};
SvelteComponent.renderCss = function() {
var components = [];
return {
css: components.map(x => x.css).join('\n'),
map: null,
components
};
};
module.exports = SvelteComponent;

@ -0,0 +1,37 @@
"use strict";
var SvelteComponent = {};;
SvelteComponent.data = function() {
return {};
};
SvelteComponent.render = function(state, options) {
state = state || {};
return ``.trim();
};
SvelteComponent.renderCss = function() {
var components = [];
return {
css: components.map(x => x.css).join('\n'),
map: null,
components
};
};
var escaped = {
'"': '&quot;',
"'": '&#39;',
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};
function __escape(html) {
return String(html).replace(/["'&<>]/g, match => escaped[match]);
}
module.exports = SvelteComponent;

@ -0,0 +1,23 @@
<script>
export default {
oncreate() {
console.log('oncreate');
},
ondestroy() {
console.log('ondestroy');
},
methods: {
foo() {
console.log('foo');
}
},
events: {
swipe(node, callback) {
// TODO implement
}
}
};
</script>

@ -0,0 +1,3 @@
export default {
html: `<p>ab</p>`
};

@ -0,0 +1,26 @@
<p>{{value}}</p>
<script>
function throwError() {
throw new Error('nope');
}
const createElement = throwError;
const createElement$ = throwError;
export default {
data() {
return {
value: template() + template$()
};
}
};
function template() {
return 'a';
}
function template$() {
return 'b';
}
</script>

@ -1,3 +1,7 @@
export default {
html: `@@x`
html: `
@@x
%1
%%2
`
};

@ -0,0 +1,5 @@
export default {
test(assert, component) {
assert.ok(component.constructor.FOO);
}
};

@ -0,0 +1,7 @@
<script>
export default {
setup(Component) {
Component.FOO = true;
}
};
</script>
Loading…
Cancel
Save