a few more tests

pull/1864/head
Rich Harris 7 years ago
parent e272736d06
commit 80ea0572e2

@ -10,7 +10,7 @@ import namespaces from '../utils/namespaces';
import { removeNode } from '../utils/removeNode'; import { removeNode } from '../utils/removeNode';
import nodeToString from '../utils/nodeToString'; import nodeToString from '../utils/nodeToString';
import wrapModule from './wrapModule'; import wrapModule from './wrapModule';
import { createScopes } from '../utils/annotateWithScopes'; import { createScopes, extractNames } from '../utils/annotateWithScopes';
import getName from '../utils/getName'; import getName from '../utils/getName';
import Stylesheet from './css/Stylesheet'; import Stylesheet from './css/Stylesheet';
import { test } from '../config'; import { test } from '../config';
@ -92,6 +92,7 @@ function increaseIndentation(
// the wrong idea about the shape of each/if blocks // the wrong idea about the shape of each/if blocks
childKeys.EachBlock = childKeys.IfBlock = ['children', 'else']; childKeys.EachBlock = childKeys.IfBlock = ['children', 'else'];
childKeys.Attribute = ['value']; childKeys.Attribute = ['value'];
childKeys.ExportNamedDeclaration = ['declaration', 'specifiers'];
export default class Component { export default class Component {
stats: Stats; stats: Stats;
@ -104,7 +105,6 @@ export default class Component {
customElement: CustomElementOptions; customElement: CustomElementOptions;
tag: string; tag: string;
props: string[];
properties: Map<string, Node>; properties: Map<string, Node>;
@ -123,7 +123,7 @@ export default class Component {
hasComponents: boolean; hasComponents: boolean;
computations: Computation[]; computations: Computation[];
templateProperties: Record<string, Node>; templateProperties: Record<string, Node>;
javascript: [string, string]; javascript: string;
used: { used: {
components: Set<string>; components: Set<string>;
@ -135,6 +135,8 @@ export default class Component {
}; };
declarations: string[]; declarations: string[];
exports: Array<{ name: string, as: string }>;
props: string[];
refCallees: Node[]; refCallees: Node[];
@ -193,6 +195,7 @@ export default class Component {
}; };
this.declarations = []; this.declarations = [];
this.exports = [];
this.refs = new Set(); this.refs = new Set();
this.refCallees = []; this.refCallees = [];
@ -509,10 +512,15 @@ export default class Component {
const { code, source, imports } = this; const { code, source, imports } = this;
const indentationLevel = getIndentationLevel(source, js.content.body[0].start); const indent = code.getIndentString();
const indentExclusionRanges = getIndentExclusionRanges(js.content); code.indent(indent, {
exclude: [
[0, js.content.start],
[js.content.end, source.length]
]
});
const { scope, globals } = createScopes(js.content); let { scope, map, globals } = createScopes(js.content);
scope.declarations.forEach(name => { scope.declarations.forEach(name => {
this.userVars.add(name); this.userVars.add(name);
@ -533,6 +541,30 @@ export default class Component {
}) })
} }
if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') {
node.declaration.declarations.forEach(declarator => {
extractNames(declarator.id).forEach(name => {
this.exports.push({ name, as: name });
});
});
} else {
const { name } = node.declaration.id;
this.exports.push({ name, as: name });
}
code.remove(node.start, node.declaration.start);
} else {
node.specifiers.forEach(specifier => {
this.exports.push({
name: specifier.local.name,
as: specifier.exported.name
});
});
}
}
// imports need to be hoisted out of the IIFE // imports need to be hoisted out of the IIFE
// TODO hoist other stuff where possible // TODO hoist other stuff where possible
else if (node.type === 'ImportDeclaration') { else if (node.type === 'ImportDeclaration') {
@ -545,20 +577,32 @@ export default class Component {
} }
}); });
walk(js.content, {
enter(node) {
if (map.has(node)) {
scope = map.get(node);
}
if (node.type === 'AssignmentExpression') {
const { name } = flattenReference(node.left);
code.appendLeft(node.end, `; __make_dirty('${name}')`);
}
},
leave(node) {
if (map.has(node)) {
scope = scope.parent;
}
}
});
let a = js.content.start; let a = js.content.start;
while (/\s/.test(source[a])) a += 1; while (/\s/.test(source[a])) a += 1;
let b = js.content.end; let b = js.content.end;
while (/\s/.test(source[b - 1])) b -= 1; while (/\s/.test(source[b - 1])) b -= 1;
this.javascript = this.defaultExport this.javascript = a !== b ? `[✂${a}-${b}✂]` : '';
? [
a !== this.defaultExport.start ? `[✂${a}-${this.defaultExport.start}✂]` : '',
b !== this.defaultExport.end ?`[✂${this.defaultExport.end}-${b}✂]` : ''
]
: [
a !== b ? `[✂${a}-${b}✂]` : '',
''
];
} }
} }

@ -13,7 +13,6 @@ export default function dom(
const format = options.format || 'es'; const format = options.format || 'es';
const { const {
computations,
name, name,
templateProperties templateProperties
} = component; } = component;
@ -110,9 +109,16 @@ export default function dom(
} else { } else {
builder.addBlock(deindent` builder.addBlock(deindent`
class ${name} extends @SvelteComponent { class ${name} extends @SvelteComponent {
__init() { __init(__set_inject_props, __set_inject_refs, __make_dirty) {
${component.javascript} ${component.javascript}
__set_inject_props(props => {
// TODO only do this for export let|var
${(component.exports.map(name =>
`if ('${name.as}' in props) ${name.as} = props.${name.as};`
))}
});
return () => ({ ${(component.declarations).join(', ')} }); return () => ({ ${(component.declarations).join(', ')} });
} }

@ -1,9 +1,11 @@
import { schedule_update } from './scheduler'; import { schedule_update, flush } from './scheduler';
export class SvelteComponent { export class SvelteComponent {
constructor(options) { constructor(options) {
this.__get_state = this.__init( this.__get_state = this.__init(
fn => this.__inject_props = fn fn => this.__inject_props = fn,
fn => this.__inject_refs = fn,
key => this.__make_dirty(key)
); );
this.__dirty = null; this.__dirty = null;
@ -14,6 +16,7 @@ export class SvelteComponent {
if (options.target) { if (options.target) {
this.__mount(options.target); this.__mount(options.target);
flush();
} }
} }
@ -25,10 +28,12 @@ export class SvelteComponent {
this.__destroy(true); this.__destroy(true);
} }
__make_dirty() { __make_dirty(key) {
if (this.__dirty) return; if (!this.__dirty) {
this.__dirty = {}; schedule_update(this);
schedule_update(this); this.__dirty = {};
}
this.__dirty[key] = true;
} }
__mount(target, anchor) { __mount(target, anchor) {
@ -39,8 +44,7 @@ export class SvelteComponent {
__set(key, value) { __set(key, value) {
this.__inject_props({ [key]: value }); this.__inject_props({ [key]: value });
this.__make_dirty(); this.__make_dirty(key);
this.__dirty[key] = true;
} }
__update() { __update() {

@ -398,7 +398,7 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
end, end,
type, type,
name, name,
expression: value[0].expression expression: value[0] && value[0].expression
}; };
} }

@ -83,7 +83,7 @@ export class Scope {
} }
} }
function extractNames(param: Node) { export function extractNames(param: Node) {
const names: string[] = []; const names: string[] = [];
extractors[param.type](names, param); extractors[param.type](names, param);
return names; return names;

@ -46,16 +46,18 @@ describe.only("runtime", () => {
const failed = new Set(); const failed = new Set();
function runTest(dir, shared, hydrate) { function runTest(dir, internal, hydrate) {
if (dir[0] === ".") return; if (dir[0] === ".") return;
const { flush } = require(internal);
const config = loadConfig(`./runtime/samples/${dir}/_config.js`); const config = loadConfig(`./runtime/samples/${dir}/_config.js`);
if (config.solo && process.env.CI) { if (config.solo && process.env.CI) {
throw new Error("Forgot to remove `solo: true` from test"); throw new Error("Forgot to remove `solo: true` from test");
} }
(config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers${hydrate ? ', hydration' : ''})`, () => { (config.skip ? it.skip : config.solo ? it.only : it)(`${dir} ${hydrate ? '(with hydration)' : ''}`, () => {
if (failed.has(dir)) { if (failed.has(dir)) {
// this makes debugging easier, by only printing compiled output once // this makes debugging easier, by only printing compiled output once
throw new Error('skipping test, already failed'); throw new Error('skipping test, already failed');
@ -67,7 +69,7 @@ describe.only("runtime", () => {
global.document.title = ''; global.document.title = '';
compileOptions = config.compileOptions || {}; compileOptions = config.compileOptions || {};
compileOptions.shared = shared; compileOptions.shared = internal;
compileOptions.hydratable = hydrate; compileOptions.hydratable = hydrate;
compileOptions.store = !!config.store; compileOptions.store = !!config.store;
compileOptions.immutable = config.immutable; compileOptions.immutable = config.immutable;
@ -114,7 +116,7 @@ describe.only("runtime", () => {
try { try {
SvelteComponent = require(`./samples/${dir}/main.html`); SvelteComponent = require(`./samples/${dir}/main.html`);
} catch (err) { } catch (err) {
showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile); // eslint-disable-line no-console showOutput(cwd, { internal, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile); // eslint-disable-line no-console
throw err; throw err;
} }
@ -134,8 +136,7 @@ describe.only("runtime", () => {
const options = Object.assign({}, { const options = Object.assign({}, {
target, target,
hydrate, hydrate,
data: config.data, props: config.props,
store: (config.store !== true && config.store),
intro: config.intro intro: config.intro
}, config.options || {}); }, config.options || {});
@ -178,7 +179,7 @@ describe.only("runtime", () => {
} else { } else {
failed.add(dir); failed.add(dir);
showOutput(cwd, { showOutput(cwd, {
shared, internal,
format: 'cjs', format: 'cjs',
hydratable: hydrate, hydratable: hydrate,
store: !!compileOptions.store, store: !!compileOptions.store,
@ -190,7 +191,9 @@ describe.only("runtime", () => {
} }
}) })
.then(() => { .then(() => {
if (config.show) showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile); if (config.show) showOutput(cwd, { internal, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile);
flush();
}); });
}); });
} }

@ -1,5 +1,5 @@
export default { export default {
data: { props: {
target: 'World!', target: 'World!',
display: true, display: true,
}, },
@ -8,13 +8,13 @@ export default {
<h1></h1> <h1></h1>
`, `,
test ( assert, component, target, window ) { test(assert, component, target, window) {
const header = target.querySelector( 'h1' ); const header = target.querySelector('h1');
const eventClick = new window.MouseEvent( 'click' ); const click = new window.MouseEvent('click');
header.dispatchEvent( eventClick ); header.dispatchEvent(click);
assert.htmlEqual( target.innerHTML, ` assert.htmlEqual(target.innerHTML, `
<h1>Hello World!</h1> <h1>Hello World!</h1>
` ); `);
} },
}; };

@ -3,7 +3,6 @@
export let target; export let target;
function insert(node, text) { function insert(node, text) {
function onClick() { function onClick() {
node.textContent = text; node.textContent = text;
} }

@ -1,10 +1,11 @@
export default { export default {
test ( assert, component, target, window ) { async test(assert, component, target, window) {
const button = target.querySelector( 'button' ); const button = target.querySelector('button');
const click = new window.MouseEvent( 'click' ); const click = new window.MouseEvent('click');
assert.htmlEqual( target.innerHTML, `<button>0</button>` ); assert.htmlEqual(target.innerHTML, `<button>1</button>`);
button.dispatchEvent( click ); await button.dispatchEvent(click);
assert.htmlEqual( target.innerHTML, `<button>1</button>` ); await Promise.resolve();
} assert.htmlEqual(target.innerHTML, `<button>2</button>`);
},
}; };

@ -1,10 +1,10 @@
<script> <script>
export let x; export let x = 0;
function foo(node) { function foo(node) {
let x = 0; const handler = () => {
x += 1;
const handler = () => x = x++; }
node.addEventListener('click', handler); node.addEventListener('click', handler);
handler(); handler();

@ -3,27 +3,27 @@ export default {
<button>action</button> <button>action</button>
`, `,
test ( assert, component, target, window ) { test (assert, component, target, window) {
const button = target.querySelector( 'button' ); const button = target.querySelector('button');
const eventEnter = new window.MouseEvent( 'mouseenter' ); const eventEnter = new window.MouseEvent('mouseenter');
const eventLeave = new window.MouseEvent( 'mouseleave' ); const eventLeave = new window.MouseEvent('mouseleave');
const ctrlPress = new window.KeyboardEvent( 'keydown', { ctrlKey: true } ); const ctrlPress = new window.KeyboardEvent('keydown', { ctrlKey: true });
button.dispatchEvent( eventEnter ); button.dispatchEvent(eventEnter);
assert.htmlEqual( target.innerHTML, ` assert.htmlEqual(target.innerHTML, `
<button>action</button> <button>action</button>
<div class="tooltip">Perform an Action</div> <div class="tooltip">Perform an Action</div>
` ); `);
window.dispatchEvent( ctrlPress ); window.dispatchEvent(ctrlPress);
assert.htmlEqual( target.innerHTML, ` assert.htmlEqual(target.innerHTML, `
<button>action</button> <button>action</button>
<div class="tooltip">Perform an augmented Action</div> <div class="tooltip">Perform an augmented Action</div>
` ); `);
button.dispatchEvent( eventLeave ); button.dispatchEvent(eventLeave);
assert.htmlEqual( target.innerHTML, ` assert.htmlEqual(target.innerHTML, `
<button>action</button> <button>action</button>
` ); `);
} }
}; };

@ -1,5 +1,5 @@
<script> <script>
export let tooltip = 'Perform an Action'; export let text = 'Perform an Action';
function checkForCtrl(event) { function checkForCtrl(event) {
if (event.ctrlKey) { if (event.ctrlKey) {
@ -40,5 +40,5 @@
} }
</script> </script>
<button use:tooltip="{tooltip}">action</button> <button use:tooltip="{text}">action</button>
<svelte:window on:keydown="{checkForCtrl}" on:keyup="{checkForCtrl}"/> <svelte:window on:keydown="{checkForCtrl}" on:keyup="{checkForCtrl}"/>
Loading…
Cancel
Save