merge master -> composition

pull/1998/head
Richard Harris 7 years ago
commit ac4e1235b1

@ -33,7 +33,6 @@
"plugin:import/errors", "plugin:import/errors",
"plugin:import/warnings" "plugin:import/warnings"
], ],
"plugins": ["svelte3"],
"parserOptions": { "parserOptions": {
"ecmaVersion": 6, "ecmaVersion": 6,
"sourceType": "module" "sourceType": "module"

1
.gitignore vendored

@ -2,6 +2,7 @@
.nyc_output .nyc_output
node_modules node_modules
*.map *.map
/src/compile/internal-exports.ts
/cli/ /cli/
/compiler.js /compiler.js
/index.js /index.js

3381
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -23,18 +23,17 @@
"scripts": { "scripts": {
"test": "mocha --opts mocha.opts", "test": "mocha --opts mocha.opts",
"quicktest": "mocha --opts mocha.opts", "quicktest": "mocha --opts mocha.opts",
"precoverage": "export COVERAGE=true && nyc mocha --opts mocha.coverage.opts", "precoverage": "c8 mocha --opts mocha.coverage.opts",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov", "coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html",
"codecov": "codecov", "codecov": "codecov",
"precodecov": "npm run coverage", "precodecov": "npm run coverage",
"lint": "eslint src test/*.js", "lint": "eslint src test/*.js",
"build": "rollup -c", "build": "rollup -c",
"prepare": "npm run build", "prepare": "npm run build",
"dev": "rollup -c -w", "dev": "rollup -cw",
"pretest": "npm run build", "pretest": "npm run build",
"posttest": "agadoo src/internal/index.js", "posttest": "agadoo src/internal/index.js",
"prepublishOnly": "npm run lint && npm test", "prepublishOnly": "export PUBLISH=true && npm run lint && npm test"
"prettier": "prettier --write \"src/**/*.ts\""
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -58,28 +57,28 @@
"acorn": "^6.0.5", "acorn": "^6.0.5",
"acorn-dynamic-import": "^4.0.0", "acorn-dynamic-import": "^4.0.0",
"agadoo": "^1.0.1", "agadoo": "^1.0.1",
"c8": "^3.4.0",
"codecov": "^3.0.0", "codecov": "^3.0.0",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^5.3.0", "eslint": "^5.3.0",
"eslint-plugin-html": "^4.0.3", "eslint-plugin-html": "^5.0.0",
"eslint-plugin-import": "^2.11.0", "eslint-plugin-import": "^2.11.0",
"estree-walker": "^0.6.0", "estree-walker": "^0.6.0",
"is-reference": "^1.1.1", "is-reference": "^1.1.1",
"jsdom": "^11.8.0", "jsdom": "^12.2.0",
"kleur": "^3.0.0", "kleur": "^3.0.0",
"locate-character": "^2.0.5", "locate-character": "^2.0.5",
"magic-string": "^0.25.0", "magic-string": "^0.25.0",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"nightmare": "^3.0.1", "nightmare": "^3.0.1",
"node-resolve": "^1.3.3", "node-resolve": "^1.3.3",
"nyc": "^12.0.2", "rollup": "^1.1.2",
"prettier": "^1.12.1",
"rollup": "^0.63.5",
"rollup-plugin-commonjs": "^9.1.0", "rollup-plugin-commonjs": "^9.1.0",
"rollup-plugin-json": "^3.0.0", "rollup-plugin-json": "^3.0.0",
"rollup-plugin-node-resolve": "^3.3.0", "rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-replace": "^2.0.0", "rollup-plugin-replace": "^2.0.0",
"rollup-plugin-typescript": "^0.8.1", "rollup-plugin-sucrase": "^2.0.0",
"rollup-plugin-typescript": "^1.0.0",
"rollup-plugin-virtual": "^1.0.1", "rollup-plugin-virtual": "^1.0.1",
"rollup-watch": "^4.3.1", "rollup-watch": "^4.3.1",
"sade": "^1.4.0", "sade": "^1.4.0",
@ -88,7 +87,7 @@
"source-map": "0.6", "source-map": "0.6",
"source-map-support": "^0.5.4", "source-map-support": "^0.5.4",
"tiny-glob": "^0.2.1", "tiny-glob": "^0.2.1",
"ts-node": "^7.0.0", "ts-node": "^8.0.2",
"tslib": "^1.8.0", "tslib": "^1.8.0",
"typescript": "^3.0.1" "typescript": "^3.0.1"
}, },

@ -1,11 +1,41 @@
import fs from 'fs';
import replace from 'rollup-plugin-replace'; import replace from 'rollup-plugin-replace';
import resolve from 'rollup-plugin-node-resolve'; import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'; import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json'; import json from 'rollup-plugin-json';
import sucrase from 'rollup-plugin-sucrase';
import typescript from 'rollup-plugin-typescript'; import typescript from 'rollup-plugin-typescript';
import pkg from './package.json'; import pkg from './package.json';
const is_publish = !!process.env.PUBLISH;
export default [ export default [
/* internal.[m]js */
{
input: `src/internal/index.js`,
output: [
{
file: `internal.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `internal.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
external: id => id.startsWith('svelte/'),
plugins: [{
generateBundle(options, bundle) {
const mod = bundle['internal.mjs'];
if (mod) {
fs.writeFileSync('src/compile/internal-exports.ts', `// This file is automatically generated\nexport default new Set(${JSON.stringify(mod.exports)});`);
}
}
}]
},
/* compiler.js */ /* compiler.js */
{ {
input: 'src/index.ts', input: 'src/index.ts',
@ -13,21 +43,32 @@ export default [
replace({ replace({
__VERSION__: pkg.version __VERSION__: pkg.version
}), }),
resolve(), resolve({
commonjs(), extensions: ['.js', '.ts']
}),
commonjs({
include: ['node_modules/**']
}),
json(), json(),
typescript({ is_publish
include: 'src/**', ? typescript({
exclude: 'src/internal/**', include: 'src/**',
typescript: require('typescript') exclude: 'src/internal/**',
}) typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
], ],
output: { output: {
file: 'compiler.js', file: 'compiler.js',
format: 'umd', format: is_publish ? 'umd' : 'cjs',
name: 'svelte', name: 'svelte',
sourcemap: true sourcemap: true,
} },
external: is_publish
? []
: id => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree')
}, },
/* cli/*.js */ /* cli/*.js */
@ -48,27 +89,26 @@ export default [
typescript({ typescript({
typescript: require('typescript') typescript: require('typescript')
}) })
], ]
experimentalCodeSplitting: true
}, },
/* internal.[m]js, motion.mjs */ /* motion.mjs */
...['internal', 'motion'].map(name => ({ {
input: `src/${name}/index.js`, input: `src/motion/index.js`,
output: [ output: [
{ {
file: `${name}.mjs`, file: `motion.mjs`,
format: 'esm', format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.') paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}, },
{ {
file: `${name}.js`, file: `motion.js`,
format: 'cjs', format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.') paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
} }
], ],
external: id => id.startsWith('svelte/') external: id => id.startsWith('svelte/')
})), },
// everything else // everything else
...['index', 'store', 'easing', 'transition'].map(name => ({ ...['index', 'store', 'easing', 'transition'].map(name => ({

@ -10,7 +10,7 @@ import { createScopes, extractNames, Scope } from '../utils/annotateWithScopes';
import Stylesheet from './css/Stylesheet'; import Stylesheet from './css/Stylesheet';
import { test } from '../config'; import { test } from '../config';
import Fragment from './nodes/Fragment'; import Fragment from './nodes/Fragment';
import * as internal from '../internal/index'; import internal_exports from './internal-exports';
import { Node, Ast, CompileOptions } from '../interfaces'; import { Node, Ast, CompileOptions } from '../interfaces';
import error from '../utils/error'; import error from '../utils/error';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
@ -160,9 +160,12 @@ export default class Component {
: this.name; : this.name;
this.walk_module_js(); this.walk_module_js();
this.walk_instance_js(); this.walk_instance_js_pre_template();
this.fragment = new Fragment(this, ast.html); this.fragment = new Fragment(this, ast.html);
this.walk_instance_js_post_template();
if (!options.customElement) this.stylesheet.reify(); if (!options.customElement) this.stylesheet.reify();
this.stylesheet.warnOnUnusedSelectors(stats); this.stylesheet.warnOnUnusedSelectors(stats);
@ -212,8 +215,8 @@ export default class Component {
// TODO use same regex for both // TODO use same regex for both
result = result.replace(options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => { result = result.replace(options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => {
if (sigil === '@') { if (sigil === '@') {
if (name in internal) { if (internal_exports.has(name)) {
if (options.dev && `${name}Dev` in internal) name = `${name}Dev`; if (options.dev && internal_exports.has(`${name}Dev`)) name = `${name}Dev`;
helpers.add(name); helpers.add(name);
} }
@ -510,7 +513,7 @@ export default class Component {
this.module_javascript = this.extract_javascript(script); this.module_javascript = this.extract_javascript(script);
} }
walk_instance_js() { walk_instance_js_pre_template() {
const script = this.instance_script; const script = this.instance_script;
if (!script) return; if (!script) return;
@ -545,6 +548,12 @@ export default class Component {
this.track_mutations(); this.track_mutations();
this.extract_imports_and_exports(script.content, this.imports, this.props); this.extract_imports_and_exports(script.content, this.imports, this.props);
}
walk_instance_js_post_template() {
const script = this.instance_script;
if (!script) return;
this.hoist_instance_declarations(); this.hoist_instance_declarations();
this.extract_reactive_declarations(); this.extract_reactive_declarations();
this.extract_reactive_store_references(); this.extract_reactive_store_references();
@ -756,12 +765,13 @@ export default class Component {
// hoistable functions. TODO others? // hoistable functions. TODO others?
const { hoistable_names, hoistable_nodes, imported_declarations, instance_scope: scope } = this; const { hoistable_names, hoistable_nodes, imported_declarations, instance_scope: scope } = this;
const template_scope = this.fragment.scope;
const top_level_function_declarations = new Map(); const top_level_function_declarations = new Map();
this.instance_script.content.body.forEach(node => { this.instance_script.content.body.forEach(node => {
if (node.type === 'VariableDeclaration') { if (node.type === 'VariableDeclaration') {
if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.mutable_props.has(d.id.name))) { if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.mutable_props.has(d.id.name) && !template_scope.containsMutable([d.id.name]))) {
node.declarations.forEach(d => { node.declarations.forEach(d => {
hoistable_names.add(d.id.name); hoistable_names.add(d.id.name);
}); });

@ -42,12 +42,6 @@ export default class Wrapper {
if (this.parent) this.parent.cannotUseInnerHTML(); if (this.parent) this.parent.cannotUseInnerHTML();
} }
// TODO do we still need equivalent method on Node?
findNearest(pattern) {
if (pattern.test(this.node.type)) return this;
return this.parent && this.parent.findNearest(pattern);
}
getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) { getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
// TODO use this in EachBlock and IfBlock — tricky because // TODO use this in EachBlock and IfBlock — tricky because
// children need to be created first // children need to be created first
@ -81,8 +75,4 @@ export default class Wrapper {
this.node.type === 'MustacheTag' this.node.type === 'MustacheTag'
); );
} }
render(block: Block, parentNode: string, parentNodes: string) {
throw new Error(`render method not implemented by subclass ${this.node.type}`);
}
} }

@ -7,11 +7,6 @@ export function assign(tar, src) {
return tar; return tar;
} }
export function assignTrue(tar, src) {
for (var k in src) tar[k] = 1;
return tar;
}
export function isPromise(value) { export function isPromise(value) {
return value && typeof value.then === 'function'; return value && typeof value.then === 'function';
} }
@ -22,12 +17,6 @@ export function addLoc(element, file, line, column, char) {
}; };
} }
export function exclude(src, prop) {
const tar = {};
for (const k in src) k === prop || (tar[k] = src[k]);
return tar;
}
export function run(fn) { export function run(fn) {
return fn(); return fn();
} }

@ -199,10 +199,6 @@ export class Parser {
return this.template.slice(start); return this.template.slice(start);
} }
remaining() {
return this.template.slice(this.index);
}
requireWhitespace() { requireWhitespace() {
if (!whitespace.test(this.template[this.index])) { if (!whitespace.test(this.template[this.index])) {
this.error({ this.error({

@ -41,18 +41,10 @@ export default class CodeBuilder {
if (line && !whitespace.test(line)) this.current.children.push(this.last = { type: 'line', line }); if (line && !whitespace.test(line)) this.current.children.push(this.last = { type: 'line', line });
} }
addLineAtStart(line: string) {
if (line && !whitespace.test(line)) this.root.children.unshift({ type: 'line', line });
}
addBlock(block: string) { addBlock(block: string) {
if (block && !whitespace.test(block)) this.current.children.push(this.last = { type: 'line', line: block, block: true }); if (block && !whitespace.test(block)) this.current.children.push(this.last = { type: 'line', line: block, block: true });
} }
addBlockAtStart(block: string) {
if (block && !whitespace.test(block)) this.root.children.unshift({ type: 'line', line: block, block: true });
}
isEmpty() { return !findLine(this.root); } isEmpty() { return !findLine(this.root); }
pushCondition(condition: string) { pushCondition(condition: string) {

@ -124,67 +124,4 @@ describe('CodeBuilder', () => {
` `
); );
}); });
it('adds a line at start', () => {
const builder = new CodeBuilder();
builder.addLine('// second');
builder.addLineAtStart('// first');
assert.equal(
builder.toString(),
deindent`
// first
// second
`
);
});
it('adds a line at start before a block', () => {
const builder = new CodeBuilder();
builder.addBlock('// second');
builder.addLineAtStart('// first');
assert.equal(
builder.toString(),
deindent`
// first
// second
`
);
});
it('adds a block at start', () => {
const builder = new CodeBuilder();
builder.addLine('// second');
builder.addBlockAtStart('// first');
assert.equal(
builder.toString(),
deindent`
// first
// second
`
);
});
it('adds a block at start before a block', () => {
const builder = new CodeBuilder();
builder.addBlock('// second');
builder.addBlockAtStart('// first');
assert.equal(
builder.toString(),
deindent`
// first
// second
`
);
});
}); });

@ -10,11 +10,11 @@ export default async function replaceAsync(
replacements.push( replacements.push(
func(...args).then( func(...args).then(
res => res =>
<Replacement>{ <Replacement>({
offset: args[args.length - 2], offset: args[args.length - 2],
length: args[0].length, length: args[0].length,
replacement: res, replacement: res,
} })
) )
); );
return ''; return '';

@ -89,8 +89,8 @@ describe('custom-elements', function() {
] ]
}) })
.then(bundle => bundle.generate({ format: 'iife', name: 'test' })) .then(bundle => bundle.generate({ format: 'iife', name: 'test' }))
.then(generated => { .then(result => {
bundle = generated.code; bundle = result.output[0].code;
const nightmare = new Nightmare({ show: false }); const nightmare = new Nightmare({ show: false });

@ -11,7 +11,7 @@ function create_fragment(ctx) {
text1 = createText("\n\n"); text1 = createText("\n\n");
p = createElement("p"); p = createElement("p");
text2 = createText("x: "); text2 = createText("x: ");
text3 = createText(x); text3 = createText(ctx.x);
dispose = addListener(button, "click", ctx.click_handler); dispose = addListener(button, "click", ctx.click_handler);
}, },
@ -25,7 +25,7 @@ function create_fragment(ctx) {
p(changed, ctx) { p(changed, ctx) {
if (changed.x) { if (changed.x) {
setData(text3, x); setData(text3, ctx.x);
} }
}, },
@ -44,15 +44,14 @@ function create_fragment(ctx) {
}; };
} }
let x = 0;
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let x = 0;
function click_handler() { function click_handler() {
if (true) { x += 1; $$invalidate('x', x); } if (true) { x += 1; $$invalidate('x', x); }
} }
return { click_handler }; return { x, click_handler };
} }
class SvelteComponent extends SvelteComponent_1 { class SvelteComponent extends SvelteComponent_1 {

@ -248,7 +248,7 @@ describe("runtime", () => {
}); });
return eval( return eval(
`(function () { ${result.code}; return App; }())` `(function () { ${result.output[0].code}; return App; }())`
); );
} }

@ -0,0 +1,5 @@
<script>
let count = 0;
</script>
<button on:click="{() => count += 1}">{count}</button>

@ -0,0 +1,14 @@
export default {
async test({ assert, target }) {
const btns = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await btns[0].dispatchEvent(event);
await btns[0].dispatchEvent(event);
await btns[1].dispatchEvent(event);
await btns[1].dispatchEvent(event);
await btns[1].dispatchEvent(event);
assert.equal(btns[1].innerHTML, '3');
}
};

@ -0,0 +1,6 @@
<script>
import Widget from './Widget.html';
</script>
<Widget />
<Widget />
Loading…
Cancel
Save