diff --git a/src/Stats.ts b/src/Stats.ts
index bec51d5a2d..5236e6a85c 100644
--- a/src/Stats.ts
+++ b/src/Stats.ts
@@ -99,7 +99,7 @@ export default class Stats {
return {
timings,
warnings: this.warnings,
- vars: component.vars.filter(variable => !variable.global && !variable.implicit).map(variable => ({
+ vars: component.vars.filter(variable => !variable.global && !variable.implicit && !variable.internal).map(variable => ({
name: variable.name,
export_name: variable.export_name || null,
injected: variable.injected || false,
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
index e4fe24fd68..42d3f7a449 100644
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -429,28 +429,16 @@ export default class Component {
if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
- const { kind } = node.declaration;
-
if (node.declaration.type === 'VariableDeclaration') {
node.declaration.declarations.forEach(declarator => {
extractNames(declarator.id).forEach(name => {
const variable = this.var_lookup.get(name);
variable.export_name = name;
- if (kind !== 'const') variable.mutated = true;
});
});
} else {
const { name } = node.declaration.id;
- const kind = node.declaration.type === 'ClassDeclaration'
- ? 'class'
- : node.declaration.type === 'FunctionDeclaration'
- ? 'function'
- : null;
-
- // sanity check
- if (!kind) throw new Error(`Unknown declaration type ${node.declaration.type}`);
-
const variable = this.var_lookup.get(name);
variable.export_name = name;
}
@@ -636,13 +624,17 @@ export default class Component {
walk(this.ast.instance.content, {
enter(node, parent) {
- let names;
if (map.has(node)) {
scope = map.get(node);
}
+ let names;
+ let deep = false;
+
if (node.type === 'AssignmentExpression') {
- names = node.left.type === 'MemberExpression'
+ deep = node.left.type === 'MemberExpression';
+
+ names = deep
? [getObject(node.left).name]
: extractNames(node.left);
} else if (node.type === 'UpdateExpression') {
@@ -653,7 +645,7 @@ export default class Component {
names.forEach(name => {
if (scope.findOwner(name) === instance_scope) {
const variable = component.var_lookup.get(name);
- variable.mutated = true;
+ variable[deep ? 'mutated' : 'reassigned'] = true;
}
});
}
@@ -836,7 +828,7 @@ export default class Component {
this.ast.instance.content.body.forEach(node => {
if (node.type === 'VariableDeclaration') {
- if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.var_lookup.get(d.id.name).mutated)) {
+ if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.var_lookup.get(d.id.name).reassigned)) {
node.declarations.forEach(d => {
const variable = this.var_lookup.get(d.id.name);
variable.hoistable = true;
diff --git a/src/compile/nodes/Binding.ts b/src/compile/nodes/Binding.ts
index 85fcf7cf2d..213314c3cb 100644
--- a/src/compile/nodes/Binding.ts
+++ b/src/compile/nodes/Binding.ts
@@ -34,11 +34,11 @@ export default class Binding extends Node {
if (this.isContextual) {
scope.dependenciesForName.get(name).forEach(name => {
const variable = component.var_lookup.get(name);
- variable.mutated = true;
+ variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
});
} else {
const variable = component.var_lookup.get(name);
- variable.mutated = true;
+ variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
}
if (this.expression.node.type === 'MemberExpression') {
diff --git a/src/compile/nodes/EventHandler.ts b/src/compile/nodes/EventHandler.ts
index 797442893e..e426efa79c 100644
--- a/src/compile/nodes/EventHandler.ts
+++ b/src/compile/nodes/EventHandler.ts
@@ -44,7 +44,7 @@ export default class EventHandler extends Node {
component.add_var({
name,
- injected: true,
+ internal: true,
referenced: true
});
diff --git a/src/compile/nodes/shared/Expression.ts b/src/compile/nodes/shared/Expression.ts
index eea1235e42..ea2237543a 100644
--- a/src/compile/nodes/shared/Expression.ts
+++ b/src/compile/nodes/shared/Expression.ts
@@ -149,30 +149,33 @@ export default class Expression {
}
// track any assignments from template expressions as mutable
- let mutated;
+ let names;
+ let deep = false;
+
if (function_expression) {
if (node.type === 'AssignmentExpression') {
- mutated = node.left.type === 'MemberExpression'
+ deep = node.left.type === 'MemberExpression';
+ names = deep
? [getObject(node.left).name]
: extractNames(node.left);
} else if (node.type === 'UpdateExpression') {
const { name } = getObject(node.argument);
- mutated = [name];
+ names = [name];
}
}
- if (mutated) {
- mutated.forEach(name => {
+ if (names) {
+ names.forEach(name => {
if (template_scope.names.has(name)) {
template_scope.dependenciesForName.get(name).forEach(name => {
const variable = component.var_lookup.get(name);
- if (variable) variable.mutated = true;
+ if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
});
} else {
component.add_reference(name);
const variable = component.var_lookup.get(name);
- if (variable) variable.mutated = true;
+ if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
}
});
}
@@ -381,7 +384,7 @@ export default class Expression {
component.add_var({
name,
- injected: true,
+ internal: true,
hoistable: true,
referenced: true
});
@@ -394,7 +397,7 @@ export default class Expression {
component.add_var({
name,
- injected: true,
+ internal: true,
referenced: true
});
}
@@ -406,7 +409,7 @@ export default class Expression {
component.add_var({
name,
- injected: true,
+ internal: true,
referenced: true
});
diff --git a/src/compile/render-dom/wrappers/AwaitBlock.ts b/src/compile/render-dom/wrappers/AwaitBlock.ts
index 22247a14e1..e896510fa8 100644
--- a/src/compile/render-dom/wrappers/AwaitBlock.ts
+++ b/src/compile/render-dom/wrappers/AwaitBlock.ts
@@ -181,9 +181,11 @@ export default class AwaitBlockWrapper extends Wrapper {
}
const conditions = [];
- if (this.node.expression.dependencies.size > 0) {
+ const dependencies = this.node.expression.dynamic_dependencies();
+
+ if (dependencies.length > 0) {
conditions.push(
- `(${[...this.node.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})`
+ `(${dependencies.map(dep => `'${dep}' in changed`).join(' || ')})`
);
}
diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts
index 78ede56473..71d5bd2831 100644
--- a/src/compile/render-dom/wrappers/Element/index.ts
+++ b/src/compile/render-dom/wrappers/Element/index.ts
@@ -408,7 +408,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.add_var({
name: handler,
- injected: true,
+ internal: true,
referenced: true
});
@@ -513,7 +513,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.add_var({
name,
- injected: true,
+ internal: true,
referenced: true
});
diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts
index 7e2503025a..f4dfe6f123 100644
--- a/src/compile/render-dom/wrappers/InlineComponent/index.ts
+++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts
@@ -147,14 +147,16 @@ export default class InlineComponentWrapper extends Wrapper {
}
const fragment_dependencies = new Set();
- this.slots.forEach((slot, name) => {
+ this.slots.forEach(slot => {
slot.block.dependencies.forEach(name => {
const is_let = slot.scope.is_let(name);
const variable = renderer.component.var_lookup.get(name);
- if (is_let || variable.mutated) {
- fragment_dependencies.add(name);
- }
+ if (is_let) fragment_dependencies.add(name);
+
+ if (!variable) return;
+ if (variable.mutated || variable.reassigned) fragment_dependencies.add(name);
+ if (!variable.module && variable.writable && variable.export_name) fragment_dependencies.add(name);
});
});
@@ -241,7 +243,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.add_var({
name: fn,
- injected: true,
+ internal: true,
referenced: true
});
@@ -276,7 +278,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.add_var({
name,
- injected: true,
+ internal: true,
referenced: true
});
diff --git a/src/compile/render-dom/wrappers/Window.ts b/src/compile/render-dom/wrappers/Window.ts
index 16995b23ea..18a15dcd77 100644
--- a/src/compile/render-dom/wrappers/Window.ts
+++ b/src/compile/render-dom/wrappers/Window.ts
@@ -120,7 +120,7 @@ export default class WindowWrapper extends Wrapper {
component.add_var({
name: handler_name,
- injected: true,
+ internal: true,
referenced: true
});
diff --git a/src/interfaces.ts b/src/interfaces.ts
index a14cc1a58a..157ad56eb7 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -91,7 +91,8 @@ export interface Var {
// used internally, but not exposed
global?: boolean;
- implicit?: boolean;
+ implicit?: boolean; // logic-less template references
+ internal?: boolean; // event handlers, bindings
initialised?: boolean;
hoistable?: boolean;
}
\ No newline at end of file
diff --git a/test/stats/samples/mutated-vs-reassigned-bindings/_config.js b/test/stats/samples/mutated-vs-reassigned-bindings/_config.js
new file mode 100644
index 0000000000..a1d027cbb5
--- /dev/null
+++ b/test/stats/samples/mutated-vs-reassigned-bindings/_config.js
@@ -0,0 +1,26 @@
+export default {
+ test(assert, stats) {
+ assert.deepEqual(stats.vars, [
+ {
+ name: 'count',
+ export_name: null,
+ injected: false,
+ module: false,
+ mutated: false,
+ reassigned: true,
+ referenced: true,
+ writable: true
+ },
+ {
+ name: 'user',
+ export_name: null,
+ injected: false,
+ module: false,
+ mutated: true,
+ reassigned: false,
+ referenced: true,
+ writable: false
+ }
+ ]);
+ }
+};
\ No newline at end of file
diff --git a/test/stats/samples/mutated-vs-reassigned-bindings/input.html b/test/stats/samples/mutated-vs-reassigned-bindings/input.html
new file mode 100644
index 0000000000..c1186ad3a7
--- /dev/null
+++ b/test/stats/samples/mutated-vs-reassigned-bindings/input.html
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/stats/samples/mutated-vs-reassigned/_config.js b/test/stats/samples/mutated-vs-reassigned/_config.js
new file mode 100644
index 0000000000..a1d027cbb5
--- /dev/null
+++ b/test/stats/samples/mutated-vs-reassigned/_config.js
@@ -0,0 +1,26 @@
+export default {
+ test(assert, stats) {
+ assert.deepEqual(stats.vars, [
+ {
+ name: 'count',
+ export_name: null,
+ injected: false,
+ module: false,
+ mutated: false,
+ reassigned: true,
+ referenced: true,
+ writable: true
+ },
+ {
+ name: 'user',
+ export_name: null,
+ injected: false,
+ module: false,
+ mutated: true,
+ reassigned: false,
+ referenced: true,
+ writable: false
+ }
+ ]);
+ }
+};
\ No newline at end of file
diff --git a/test/stats/samples/mutated-vs-reassigned/input.html b/test/stats/samples/mutated-vs-reassigned/input.html
new file mode 100644
index 0000000000..6a5e604c26
--- /dev/null
+++ b/test/stats/samples/mutated-vs-reassigned/input.html
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/stats/samples/props/_config.js b/test/stats/samples/props/_config.js
index e966c7fcfb..cbec831f61 100644
--- a/test/stats/samples/props/_config.js
+++ b/test/stats/samples/props/_config.js
@@ -6,7 +6,7 @@ export default {
export_name: 'name',
injected: false,
module: false,
- mutated: true,
+ mutated: false,
reassigned: false,
referenced: true,
writable: true
@@ -16,7 +16,7 @@ export default {
export_name: 'cats',
injected: false,
module: false,
- mutated: true,
+ mutated: false,
reassigned: false,
referenced: true,
writable: true
@@ -36,8 +36,8 @@ export default {
export_name: null,
injected: false,
module: false,
- mutated: true,
- reassigned: false,
+ mutated: false,
+ reassigned: true,
referenced: true,
writable: true
}