Events for DOM methods

pull/7738/head
Timothy Johnson 6 years ago
parent 8658c5e5fb
commit ad7c62d983

@ -6,7 +6,12 @@ import Stats from '../Stats';
import { globals, reserved } from '../utils/names'; import { globals, reserved } from '../utils/names';
import { namespaces, valid_namespaces } from '../utils/namespaces'; import { namespaces, valid_namespaces } from '../utils/namespaces';
import create_module from './create_module'; import create_module from './create_module';
import { create_scopes, extract_names, Scope, extract_identifiers } from './utils/scope'; import {
create_scopes,
extract_names,
Scope,
extract_identifiers,
} from './utils/scope';
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';
@ -39,7 +44,13 @@ childKeys.EachBlock = childKeys.IfBlock = ['children', 'else'];
childKeys.Attribute = ['value']; childKeys.Attribute = ['value'];
childKeys.ExportNamedDeclaration = ['declaration', 'specifiers']; childKeys.ExportNamedDeclaration = ['declaration', 'specifiers'];
function remove_node(code: MagicString, start: number, end: number, body: Node, node: Node) { function remove_node(
code: MagicString,
start: number,
end: number,
body: Node,
node: Node
) {
const i = body.indexOf(node); const i = body.indexOf(node);
if (i === -1) throw new Error('node not in list'); if (i === -1) throw new Error('node not in list');
@ -97,7 +108,12 @@ export default class Component {
node_for_declaration: Map<string, Node> = new Map(); node_for_declaration: Map<string, Node> = new Map();
partly_hoisted: string[] = []; partly_hoisted: string[] = [];
fully_hoisted: string[] = []; fully_hoisted: string[] = [];
reactive_declarations: Array<{ assignees: Set<string>; dependencies: Set<string>; node: Node; declaration: Node }> = []; reactive_declarations: Array<{
assignees: Set<string>;
dependencies: Set<string>;
node: Node;
declaration: Node;
}> = [];
reactive_declaration_nodes: Set<Node> = new Set(); reactive_declaration_nodes: Set<Node> = new Set();
has_reactive_assignments = false; has_reactive_assignments = false;
injected_reactive_declaration_vars: Set<string> = new Set(); injected_reactive_declaration_vars: Set<string> = new Set();
@ -110,7 +126,10 @@ export default class Component {
locate: (c: number) => { line: number; column: number }; locate: (c: number) => { line: number; column: number };
// TODO this does the same as component.locate! remove one or the other // TODO this does the same as component.locate! remove one or the other
locator: (search: number, startIndex?: number) => { locator: (
search: number,
startIndex?: number
) => {
line: number; line: number;
column: number; column: number;
}; };
@ -140,27 +159,46 @@ export default class Component {
this.source = source; this.source = source;
this.compile_options = compile_options; this.compile_options = compile_options;
this.file = compile_options.filename && ( this.file =
compile_options.filename &&
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
typeof process !== 'undefined' ? compile_options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : compile_options.filename (typeof process !== 'undefined'
); ? compile_options.filename
.replace(process.cwd(), '')
.replace(/^[\/\\]/, '')
: compile_options.filename);
this.locate = getLocator(this.source); this.locate = getLocator(this.source);
this.code = new MagicString(source); this.code = new MagicString(source);
// styles // styles
this.stylesheet = new Stylesheet(source, ast, compile_options.filename, compile_options.dev); this.stylesheet = new Stylesheet(
source,
ast,
compile_options.filename,
compile_options.dev
);
this.stylesheet.validate(this); this.stylesheet.validate(this);
this.component_options = process_component_options(this, this.ast.html.children); this.component_options = process_component_options(
this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace; this,
this.ast.html.children
);
this.namespace =
namespaces[this.component_options.namespace] ||
this.component_options.namespace;
if (compile_options.customElement) { if (compile_options.customElement) {
if (this.component_options.tag === undefined && compile_options.tag === undefined) { if (
const svelteOptions = ast.html.children.find(child => child.name === 'svelte:options') || { start: 0, end: 0 }; this.component_options.tag === undefined &&
compile_options.tag === undefined
) {
const svelteOptions = ast.html.children.find(
child => child.name === 'svelte:options'
) || { start: 0, end: 0 };
this.warn(svelteOptions, { this.warn(svelteOptions, {
code: 'custom-element-no-tag', code: 'custom-element-no-tag',
message: `No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/>` message: `No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/>`,
}); });
} }
this.tag = this.component_options.tag || compile_options.tag; this.tag = this.component_options.tag || compile_options.tag;
@ -195,7 +233,7 @@ export default class Component {
this.add_var({ this.add_var({
name, name,
injected: true, injected: true,
referenced: true referenced: true,
}); });
} else if (name[0] === '$') { } else if (name[0] === '$') {
this.add_var({ this.add_var({
@ -203,7 +241,7 @@ export default class Component {
injected: true, injected: true,
referenced: true, referenced: true,
mutated: true, mutated: true,
writable: true writable: true,
}); });
const subscribable_name = name.slice(1); const subscribable_name = name.slice(1);
@ -253,35 +291,52 @@ export default class Component {
const { compile_options, name } = this; const { compile_options, name } = this;
const { format = 'esm' } = compile_options; const { format = 'esm' } = compile_options;
const banner = `/* ${this.file ? `${this.file} ` : ``}generated by Svelte v${"__VERSION__"} */`; const banner = `/* ${
this.file ? `${this.file} ` : ``
}generated by Svelte v${'__VERSION__'} */`;
result = result result = result
.replace(/__svelte:self__/g, this.name) .replace(/__svelte:self__/g, this.name)
.replace(compile_options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (_match: string, sigil: string, name: string) => { .replace(
if (sigil === '@') { compile_options.generate === 'ssr'
if (name[0] === '_') { ? /(@+|#+)(\w*(?:-\w*)?)/g
return this.global(name.slice(1)); : /(@+)(\w*(?:-\w*)?)/g,
} (_match: string, sigil: string, name: string) => {
if (sigil === '@') {
if (name[0] === '_') {
return this.global(name.slice(1));
}
if (!internal_exports.has(name)) { if (!internal_exports.has(name)) {
throw new Error(`compiler error: this shouldn't happen! generated code is trying to use inexistent internal '${name}'`); throw new Error(
} `compiler error: this shouldn't happen! generated code is trying to use inexistent internal '${name}'`
);
}
if (compile_options.dev) {
if (internal_exports.has(`${name}_dev`)) name = `${name}_dev`;
else if (internal_exports.has(`${name}Dev`))
name = `${name}Dev`;
}
if (compile_options.dev && internal_exports.has(`${name}Dev`)) { return this.helper(name);
name = `${name}Dev`;
} }
return this.helper(name); return sigil.slice(1) + name;
} }
);
return sigil.slice(1) + name; const referenced_globals = Array.from(
}); this.globals,
([name, alias]) => name !== alias && { name, alias }
const referenced_globals = Array.from(this.globals, ([name, alias]) => name !== alias && ({ name, alias })).filter(Boolean); ).filter(Boolean);
if (referenced_globals.length) { if (referenced_globals.length) {
this.helper('globals'); this.helper('globals');
} }
const imported_helpers = Array.from(this.helpers, ([name, alias]) => ({ name, alias })); const imported_helpers = Array.from(this.helpers, ([name, alias]) => ({
name,
alias,
}));
const module = create_module( const module = create_module(
result, result,
@ -292,10 +347,12 @@ export default class Component {
imported_helpers, imported_helpers,
referenced_globals, referenced_globals,
this.imports, this.imports,
this.vars.filter(variable => variable.module && variable.export_name).map(variable => ({ this.vars
name: variable.name, .filter(variable => variable.module && variable.export_name)
as: variable.export_name .map(variable => ({
})), name: variable.name,
as: variable.export_name,
})),
this.source this.source
); );
@ -339,16 +396,16 @@ export default class Component {
add_string(final_chunk); add_string(final_chunk);
css = compile_options.customElement ? css = compile_options.customElement
{ code: null, map: null } : ? { code: null, map: null }
this.stylesheet.render(compile_options.cssOutputFilename, true); : this.stylesheet.render(compile_options.cssOutputFilename, true);
js = { js = {
code: compiled.toString(), code: compiled.toString(),
map: compiled.generateMap({ map: compiled.generateMap({
includeContent: true, includeContent: true,
file: compile_options.outputFilename, file: compile_options.outputFilename,
}) }),
}; };
} }
@ -357,17 +414,19 @@ export default class Component {
css, css,
ast: this.ast, ast: this.ast,
warnings: this.warnings, warnings: this.warnings,
vars: this.vars.filter(v => !v.global && !v.internal).map(v => ({ vars: this.vars
name: v.name, .filter(v => !v.global && !v.internal)
export_name: v.export_name || null, .map(v => ({
injected: v.injected || false, name: v.name,
module: v.module || false, export_name: v.export_name || null,
mutated: v.mutated || false, injected: v.injected || false,
reassigned: v.reassigned || false, module: v.module || false,
referenced: v.referenced || false, mutated: v.mutated || false,
writable: v.writable || false reassigned: v.reassigned || false,
})), referenced: v.referenced || false,
stats: this.stats.render() writable: v.writable || false,
})),
stats: this.stats.render(),
}; };
} }
@ -402,8 +461,7 @@ export default class Component {
let alias = name; let alias = name;
for ( for (
let i = 1; let i = 1;
this.used_names.has(alias) || this.used_names.has(alias) || local_used_names.has(alias);
local_used_names.has(alias);
alias = `${name}_${i++}` alias = `${name}_${i++}`
); );
local_used_names.add(alias); local_used_names.add(alias);
@ -428,7 +486,7 @@ export default class Component {
source: this.source, source: this.source,
start: pos.start, start: pos.start,
end: pos.end, end: pos.end,
filename: this.compile_options.filename filename: this.compile_options.filename,
}); });
} }
@ -459,7 +517,8 @@ export default class Component {
end, end,
pos: pos.start, pos: pos.start,
filename: this.compile_options.filename, filename: this.compile_options.filename,
toString: () => `${warning.message} (${start.line + 1}:${start.column})\n${frame}`, toString: () =>
`${warning.message} (${start.line + 1}:${start.column})\n${frame}`,
}); });
} }
@ -482,7 +541,7 @@ export default class Component {
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
this.error(node, { this.error(node, {
code: `default-export`, code: `default-export`,
message: `A component cannot have a default export` message: `A component cannot have a default export`,
}); });
} }
@ -490,7 +549,7 @@ export default class Component {
if (node.source) { if (node.source) {
this.error(node, { this.error(node, {
code: `not-implemented`, code: `not-implemented`,
message: `A component currently cannot have an export ... from` message: `A component currently cannot have an export ... from`,
}); });
} }
if (node.declaration) { if (node.declaration) {
@ -530,7 +589,8 @@ export default class Component {
if (this.hoistable_nodes.has(node)) return false; if (this.hoistable_nodes.has(node)) return false;
if (this.reactive_declaration_nodes.has(node)) return false; if (this.reactive_declaration_nodes.has(node)) return false;
if (node.type === 'ImportDeclaration') return false; if (node.type === 'ImportDeclaration') return false;
if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) return false; if (node.type === 'ExportDeclaration' && node.specifiers.length > 0)
return false;
return true; return true;
}); });
@ -543,8 +603,11 @@ export default class Component {
let result = ''; let result = '';
script.content.body.forEach((node) => { script.content.body.forEach(node => {
if (this.hoistable_nodes.has(node) || this.reactive_declaration_nodes.has(node)) { if (
this.hoistable_nodes.has(node) ||
this.reactive_declaration_nodes.has(node)
) {
if (a !== b) result += `[✂${a}-${b}✂]`; if (a !== b) result += `[✂${a}-${b}✂]`;
a = node.end; a = node.end;
} }
@ -572,10 +635,10 @@ export default class Component {
if (node.type === 'LabeledStatement' && node.label.name === '$') { if (node.type === 'LabeledStatement' && node.label.name === '$') {
component.warn(node, { component.warn(node, {
code: 'module-script-reactive-declaration', code: 'module-script-reactive-declaration',
message: '$: has no effect in a module script' message: '$: has no effect in a module script',
}); });
} }
} },
}); });
this.add_sourcemap_locations(script.content); this.add_sourcemap_locations(script.content);
@ -587,7 +650,7 @@ export default class Component {
if (name[0] === '$') { if (name[0] === '$') {
this.error(node, { this.error(node, {
code: 'illegal-declaration', code: 'illegal-declaration',
message: `The $ prefix is reserved, and cannot be used for variable and import names` message: `The $ prefix is reserved, and cannot be used for variable and import names`,
}); });
} }
@ -595,7 +658,7 @@ export default class Component {
name, name,
module: true, module: true,
hoistable: true, hoistable: true,
writable: node.kind === 'var' || node.kind === 'let' writable: node.kind === 'var' || node.kind === 'let',
}); });
}); });
@ -603,12 +666,12 @@ export default class Component {
if (name[0] === '$') { if (name[0] === '$') {
this.error(node, { this.error(node, {
code: 'illegal-subscription', code: 'illegal-subscription',
message: `Cannot reference store value inside <script context="module">` message: `Cannot reference store value inside <script context="module">`,
}); });
} else { } else {
this.add_var({ this.add_var({
name, name,
global: true global: true,
}); });
} }
}); });
@ -640,7 +703,9 @@ export default class Component {
}); });
}); });
const { scope: instance_scope, map, globals } = create_scopes(script.content); const { scope: instance_scope, map, globals } = create_scopes(
script.content
);
this.instance_scope = instance_scope; this.instance_scope = instance_scope;
this.instance_scope_map = map; this.instance_scope_map = map;
@ -648,7 +713,7 @@ export default class Component {
if (name[0] === '$') { if (name[0] === '$') {
this.error(node, { this.error(node, {
code: 'illegal-declaration', code: 'illegal-declaration',
message: `The $ prefix is reserved, and cannot be used for variable and import names` message: `The $ prefix is reserved, and cannot be used for variable and import names`,
}); });
} }
@ -656,7 +721,7 @@ export default class Component {
name, name,
initialised: instance_scope.initialised_declarations.has(name), initialised: instance_scope.initialised_declarations.has(name),
hoistable: /^Import/.test(node.type), hoistable: /^Import/.test(node.type),
writable: node.kind === 'var' || node.kind === 'let' writable: node.kind === 'var' || node.kind === 'let',
}); });
this.node_for_declaration.set(name, node); this.node_for_declaration.set(name, node);
@ -671,19 +736,19 @@ export default class Component {
injected: true, injected: true,
writable: true, writable: true,
reassigned: true, reassigned: true,
initialised: true initialised: true,
}); });
} else if (name === '$$props') { } else if (name === '$$props') {
this.add_var({ this.add_var({
name, name,
injected: true injected: true,
}); });
} else if (name[0] === '$') { } else if (name[0] === '$') {
this.add_var({ this.add_var({
name, name,
injected: true, injected: true,
mutated: true, mutated: true,
writable: true writable: true,
}); });
this.add_reference(name.slice(1)); this.add_reference(name.slice(1));
@ -693,7 +758,7 @@ export default class Component {
} else { } else {
this.add_var({ this.add_var({
name, name,
global: true global: true,
}); });
} }
}); });
@ -753,7 +818,7 @@ export default class Component {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
} },
}); });
} }
@ -769,10 +834,14 @@ export default class Component {
scope = map.get(node); scope = map.get(node);
} }
if (node.type === 'LabeledStatement' && node.label.name === '$' && parent.type !== 'Program') { if (
node.type === 'LabeledStatement' &&
node.label.name === '$' &&
parent.type !== 'Program'
) {
component.warn(node, { component.warn(node, {
code: 'non-top-level-reactive-declaration', code: 'non-top-level-reactive-declaration',
message: '$: has no effect outside of the top-level' message: '$: has no effect outside of the top-level',
}); });
} }
@ -790,7 +859,7 @@ export default class Component {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
} },
}); });
} }
@ -805,7 +874,13 @@ export default class Component {
return `${name.slice(1)}.set(${name})`; return `${name.slice(1)}.set(${name})`;
} }
if (variable && !variable.referenced && !variable.is_reactive_dependency && !variable.export_name && !name.startsWith('$$')) { if (
variable &&
!variable.referenced &&
!variable.is_reactive_dependency &&
!variable.export_name &&
!name.startsWith('$$')
) {
return value || name; return value || name;
} }
@ -817,7 +892,9 @@ export default class Component {
const deps = new Set([name]); const deps = new Set([name]);
deps.forEach(name => { deps.forEach(name => {
const reactive_declarations = this.reactive_declarations.filter(x => x.assignees.has(name)); const reactive_declarations = this.reactive_declarations.filter(x =>
x.assignees.has(name)
);
reactive_declarations.forEach(declaration => { reactive_declarations.forEach(declaration => {
declaration.dependencies.forEach(name => { declaration.dependencies.forEach(name => {
deps.add(name); deps.add(name);
@ -825,7 +902,9 @@ export default class Component {
}); });
}); });
return Array.from(deps).map(n => `$$invalidate('${n}', ${n})`).join(', '); return Array.from(deps)
.map(n => `$$invalidate('${n}', ${n})`)
.join(', ');
} }
rewrite_props(get_insert: (variable: Var) => string) { rewrite_props(get_insert: (variable: Var) => string) {
@ -861,7 +940,7 @@ export default class Component {
if (variable.export_name) { if (variable.export_name) {
component.error(declarator, { component.error(declarator, {
code: 'destructured-prop', code: 'destructured-prop',
message: `Cannot declare props in destructured declaration` message: `Cannot declare props in destructured declaration`,
}); });
} }
@ -872,7 +951,11 @@ export default class Component {
if (inserts.length > 0) { if (inserts.length > 0) {
if (next) { if (next) {
code.overwrite(declarator.end, next.start, `; ${inserts.join('; ')}; ${node.kind} `); code.overwrite(
declarator.end,
next.start,
`; ${inserts.join('; ')}; ${node.kind} `
);
} else { } else {
code.appendLeft(declarator.end, `; ${inserts.join('; ')}`); code.appendLeft(declarator.end, `; ${inserts.join('; ')}`);
} }
@ -894,7 +977,11 @@ export default class Component {
: null; : null;
if (!current_group || (current_group.insert && insert)) { if (!current_group || (current_group.insert && insert)) {
current_group = { kind: node.kind, declarators: [declarator], insert }; current_group = {
kind: node.kind,
declarators: [declarator],
insert,
};
coalesced_declarations.push(current_group); coalesced_declarations.push(current_group);
} else if (insert) { } else if (insert) {
current_group.insert = insert; current_group.insert = insert;
@ -903,17 +990,28 @@ export default class Component {
current_group.declarators.push(declarator); current_group.declarators.push(declarator);
} }
if (variable.writable && variable.name !== variable.export_name) { if (
code.prependRight(declarator.id.start, `${variable.export_name}: `); variable.writable &&
variable.name !== variable.export_name
) {
code.prependRight(
declarator.id.start,
`${variable.export_name}: `
);
} }
if (next) { if (next) {
const next_variable = component.var_lookup.get(next.id.name); const next_variable = component.var_lookup.get(next.id.name);
const new_declaration = !next_variable.export_name const new_declaration =
|| (current_group.insert && next_variable.subscribable); !next_variable.export_name ||
(current_group.insert && next_variable.subscribable);
if (new_declaration) { if (new_declaration) {
code.overwrite(declarator.end, next.start, ` ${node.kind} `); code.overwrite(
declarator.end,
next.start,
` ${node.kind} `
);
} }
} }
} else { } else {
@ -923,7 +1021,11 @@ export default class Component {
const insert = get_insert(variable); const insert = get_insert(variable);
if (next) { if (next) {
code.overwrite(declarator.end, next.start, `; ${insert}; ${node.kind} `); code.overwrite(
declarator.end,
next.start,
`; ${insert}; ${node.kind} `
);
} else { } else {
code.appendLeft(declarator.end, `; ${insert}`); code.appendLeft(declarator.end, `; ${insert}`);
} }
@ -942,7 +1044,7 @@ export default class Component {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
} },
}); });
coalesced_declarations.forEach(group => { coalesced_declarations.forEach(group => {
@ -965,11 +1067,11 @@ export default class Component {
}); });
if (combining) { if (combining) {
const insert = group.insert const insert = group.insert ? `; ${group.insert}` : '';
? `; ${group.insert}`
: '';
const suffix = `${writable ? ` } = $$props` : ``}${insert}` + (code.original[c] === ';' ? `` : `;`); const suffix =
`${writable ? ` } = $$props` : ``}${insert}` +
(code.original[c] === ';' ? `` : `;`);
code.appendLeft(c, suffix); code.appendLeft(c, suffix);
} }
}); });
@ -981,7 +1083,11 @@ export default class Component {
// reference instance variables other than other // reference instance variables other than other
// hoistable functions. TODO others? // hoistable functions. TODO others?
const { hoistable_nodes, var_lookup, injected_reactive_declaration_vars } = this; const {
hoistable_nodes,
var_lookup,
injected_reactive_declaration_vars,
} = this;
const top_level_function_declarations = new Map(); const top_level_function_declarations = new Map();
@ -996,7 +1102,12 @@ export default class Component {
if (v.export_name) return false; if (v.export_name) return false;
if (this.var_lookup.get(d.id.name).reassigned) return false; if (this.var_lookup.get(d.id.name).reassigned) return false;
if (this.vars.find(variable => variable.name === d.id.name && variable.module)) return false; if (
this.vars.find(
variable => variable.name === d.id.name && variable.module
)
)
return false;
return true; return true;
}); });
@ -1012,7 +1123,11 @@ export default class Component {
} }
} }
if (node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'FunctionDeclaration') { if (
node.type === 'ExportNamedDeclaration' &&
node.declaration &&
node.declaration.type === 'FunctionDeclaration'
) {
top_level_function_declarations.set(node.declaration.id.name, node); top_level_function_declarations.set(node.declaration.id.name, node);
} }
@ -1061,18 +1176,21 @@ export default class Component {
if (variable.hoistable) return; if (variable.hoistable) return;
if (top_level_function_declarations.has(name)) { if (top_level_function_declarations.has(name)) {
const other_declaration = top_level_function_declarations.get(name); const other_declaration = top_level_function_declarations.get(
name
);
if (walking.has(other_declaration)) { if (walking.has(other_declaration)) {
hoistable = false; hoistable = false;
} else if (other_declaration.type === 'ExportNamedDeclaration' && walking.has(other_declaration.declaration)) { } else if (
other_declaration.type === 'ExportNamedDeclaration' &&
walking.has(other_declaration.declaration)
) {
hoistable = false; hoistable = false;
} else if (!is_hoistable(other_declaration)) { } else if (!is_hoistable(other_declaration)) {
hoistable = false; hoistable = false;
} }
} } else {
else {
hoistable = false; hoistable = false;
} }
} }
@ -1085,7 +1203,7 @@ export default class Component {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
} },
}); });
checked.add(fn_declaration); checked.add(fn_declaration);
@ -1144,7 +1262,8 @@ export default class Component {
const owner = scope.find_owner(name); const owner = scope.find_owner(name);
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (variable) variable.is_reactive_dependency = true; if (variable) variable.is_reactive_dependency = true;
const is_writable_or_mutated = variable && (variable.writable || variable.mutated); const is_writable_or_mutated =
variable && (variable.writable || variable.mutated);
if ( if (
(!owner || owner === component.instance_scope) && (!owner || owner === component.instance_scope) &&
(name[0] === '$' || is_writable_or_mutated) (name[0] === '$' || is_writable_or_mutated)
@ -1161,15 +1280,21 @@ export default class Component {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
} },
}); });
add_indentation(this.code, node.body, 2); add_indentation(this.code, node.body, 2);
const expression = node.body.expression && unwrap_parens(node.body.expression); const expression =
node.body.expression && unwrap_parens(node.body.expression);
const declaration = expression && expression.left; const declaration = expression && expression.left;
unsorted_reactive_declarations.push({ assignees, dependencies, node, declaration }); unsorted_reactive_declarations.push({
assignees,
dependencies,
node,
declaration,
});
} }
}); });
@ -1192,7 +1317,7 @@ export default class Component {
if (seen.has(declaration)) { if (seen.has(declaration)) {
this.error(declaration.node, { this.error(declaration.node, {
code: 'cyclical-reactive-declaration', code: 'cyclical-reactive-declaration',
message: 'Cyclical dependency detected' message: 'Cyclical dependency detected',
}); });
} }
@ -1205,9 +1330,10 @@ export default class Component {
declaration.dependencies.forEach(name => { declaration.dependencies.forEach(name => {
if (declaration.assignees.has(name)) return; if (declaration.assignees.has(name)) return;
const earlier_declarations = lookup.get(name); const earlier_declarations = lookup.get(name);
if (earlier_declarations) earlier_declarations.forEach(declaration => { if (earlier_declarations)
add_declaration(declaration); earlier_declarations.forEach(declaration => {
}); add_declaration(declaration);
});
}); });
this.reactive_declarations.push(declaration); this.reactive_declarations.push(declaration);
@ -1246,11 +1372,12 @@ export default class Component {
if (globals.has(name)) return; if (globals.has(name)) return;
let message = `'${name}' is not defined`; let message = `'${name}' is not defined`;
if (!this.ast.instance) message += `. Consider adding a <script> block with 'export let ${name}' to declare a prop`; if (!this.ast.instance)
message += `. Consider adding a <script> block with 'export let ${name}' to declare a prop`;
this.warn(node, { this.warn(node, {
code: 'missing-declaration', code: 'missing-declaration',
message message,
}); });
} }
} }
@ -1258,10 +1385,11 @@ export default class Component {
function process_component_options(component: Component, nodes) { function process_component_options(component: Component, nodes) {
const component_options: ComponentOptions = { const component_options: ComponentOptions = {
immutable: component.compile_options.immutable || false, immutable: component.compile_options.immutable || false,
accessors: 'accessors' in component.compile_options accessors:
? component.compile_options.accessors 'accessors' in component.compile_options
: !!component.compile_options.customElement, ? component.compile_options.accessors
preserveWhitespace: !!component.compile_options.preserveWhitespace : !!component.compile_options.customElement,
preserveWhitespace: !!component.compile_options.preserveWhitespace,
}; };
const node = nodes.find(node => node.name === 'svelte:options'); const node = nodes.find(node => node.name === 'svelte:options');
@ -1296,12 +1424,13 @@ function process_component_options(component: Component, nodes) {
const message = `'tag' must be a string literal`; const message = `'tag' must be a string literal`;
const tag = get_value(attribute, code, message); const tag = get_value(attribute, code, message);
if (typeof tag !== 'string' && tag !== null) component.error(attribute, { code, message }); if (typeof tag !== 'string' && tag !== null)
component.error(attribute, { code, message });
if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
component.error(attribute, { component.error(attribute, {
code: `invalid-tag-property`, code: `invalid-tag-property`,
message: `tag name must be two or more words joined by the '-' character` message: `tag name must be two or more words joined by the '-' character`,
}); });
} }
@ -1314,19 +1443,20 @@ function process_component_options(component: Component, nodes) {
const message = `The 'namespace' attribute must be a string literal representing a valid namespace`; const message = `The 'namespace' attribute must be a string literal representing a valid namespace`;
const ns = get_value(attribute, code, message); const ns = get_value(attribute, code, message);
if (typeof ns !== 'string') component.error(attribute, { code, message }); if (typeof ns !== 'string')
component.error(attribute, { code, message });
if (valid_namespaces.indexOf(ns) === -1) { if (valid_namespaces.indexOf(ns) === -1) {
const match = fuzzymatch(ns, valid_namespaces); const match = fuzzymatch(ns, valid_namespaces);
if (match) { if (match) {
component.error(attribute, { component.error(attribute, {
code: `invalid-namespace-property`, code: `invalid-namespace-property`,
message: `Invalid namespace '${ns}' (did you mean '${match}'?)` message: `Invalid namespace '${ns}' (did you mean '${match}'?)`,
}); });
} else { } else {
component.error(attribute, { component.error(attribute, {
code: `invalid-namespace-property`, code: `invalid-namespace-property`,
message: `Invalid namespace '${ns}'` message: `Invalid namespace '${ns}'`,
}); });
} }
} }
@ -1337,13 +1467,13 @@ function process_component_options(component: Component, nodes) {
case 'accessors': case 'accessors':
case 'immutable': case 'immutable':
case 'preserveWhitespace': case 'preserveWhitespace': {
{
const code = `invalid-${name}-value`; const code = `invalid-${name}-value`;
const message = `${name} attribute must be true or false`; const message = `${name} attribute must be true or false`;
const value = get_value(attribute, code, message); const value = get_value(attribute, code, message);
if (typeof value !== 'boolean') component.error(attribute, { code, message }); if (typeof value !== 'boolean')
component.error(attribute, { code, message });
component_options[name] = value; component_options[name] = value;
break; break;
@ -1352,15 +1482,13 @@ function process_component_options(component: Component, nodes) {
default: default:
component.error(attribute, { component.error(attribute, {
code: `invalid-options-attribute`, code: `invalid-options-attribute`,
message: `<svelte:options> unknown attribute` message: `<svelte:options> unknown attribute`,
}); });
} }
} } else {
else {
component.error(attribute, { component.error(attribute, {
code: `invalid-options-attribute`, code: `invalid-options-attribute`,
message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes` message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes`,
}); });
} }
}); });

@ -11,20 +11,31 @@ export default function add_event_handlers(
if (handler.modifiers.has('preventDefault')) snippet = `@prevent_default(${snippet})`; if (handler.modifiers.has('preventDefault')) snippet = `@prevent_default(${snippet})`;
if (handler.modifiers.has('stopPropagation')) snippet = `@stop_propagation(${snippet})`; if (handler.modifiers.has('stopPropagation')) snippet = `@stop_propagation(${snippet})`;
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod)); let opts_string = '';
if (opts.length) { if (block.renderer.options.dev) {
const opts_string = (opts.length === 1 && opts[0] === 'capture') if (handler.modifiers.has('stopPropagation')) {
? 'true' opts_string = ', true';
: `{ ${opts.map(opt => `${opt}: true`).join(', ')} }`; }
block.event_listeners.push( if (handler.modifiers.has('preventDefault')) {
`@listen(${target}, "${handler.name}", ${snippet}, ${opts_string})` opts_string = ', true' + opts_string;
); } else if (opts_string) {
} else { opts_string = ', false' + opts_string;
block.event_listeners.push( }
`@listen(${target}, "${handler.name}", ${snippet})`
);
} }
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
if (opts.length) {
opts_string = (opts.length === 1 && opts[0] === 'capture')
? ', true'
: `, { ${opts.map(opt => `${opt}: true`).join(', ')} }`;
} else if (opts_string) {
opts_string = ', false' + opts_string;
}
block.event_listeners.push(
`@listen(${target}, "${handler.name}", ${snippet}${opts_string})`
);
}); });
} }

@ -198,20 +198,3 @@ export class SvelteComponent {
// overridden by instance, if it has props // overridden by instance, if it has props
} }
} }
export class SvelteComponentDev extends SvelteComponent {
constructor(options) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`);
}
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
};
}
}

@ -0,0 +1,85 @@
import { custom_event, append, insert, detach, listen, attr } from './dom';
import { SvelteComponent } from './Component';
export function dispatch_dev<T=any>(type: string, detail?: T) {
document.dispatchEvent(custom_event(type, detail));
}
export function append_dev(target: Node, node: Node) {
dispatch_dev("SvelteDOMInsert", { target, node });
append(target, node);
}
export function insert_dev(target: Node, node: Node, anchor?: Node) {
dispatch_dev("SvelteDOMInsert", { target, node, anchor });
insert(target, node, anchor);
}
export function detach_dev(node: Node) {
dispatch_dev("SvelteDOMRemove", { node });
detach(node);
}
export function detach_between_dev(before: Node, after: Node) {
while (before.nextSibling && before.nextSibling !== after) {
detach_dev(before.nextSibling);
}
}
export function detach_before_dev(after: Node) {
while (after.previousSibling) {
detach_dev(after.previousSibling);
}
}
export function detach_after_dev(before: Node) {
while (before.nextSibling) {
detach_dev(before.nextSibling);
}
}
export function listen_dev(node: Node, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions, has_prevent_default?: boolean, has_stop_propagation?: boolean) {
const modifiers = options === true ? [ "capture" ] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default) modifiers.push('preventDefault');
if (has_stop_propagation) modifiers.push('stopPropagation');
dispatch_dev("SvelteDOMAddEventListener", { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev("SvelteDOMRemoveEventListener", { event, handler, modifiers });
dispose();
};
}
export function attr_dev(node: Element, attribute: string, value?: string) {
attr(node, attribute, value);
if (value == null) dispatch_dev("SvelteDOMRemoveAttribute", { node, attribute });
else dispatch_dev("SvelteDOMSetAttribute", { node, attribute, value });
}
export function set_data_dev(text, data) {
data = '' + data;
if (text.data === data) return;
dispatch_dev("SvelteDOMSetData", { node: text, data });
text.data = data;
}
export class SvelteComponentDev extends SvelteComponent {
constructor(options) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`);
}
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
};
}
}

@ -256,7 +256,3 @@ export function custom_event<T=any>(type: string, detail?: T) {
e.initCustomEvent(type, false, false, detail); e.initCustomEvent(type, false, false, detail);
return e; return e;
} }
export function dispatch_dev(type: string, detail?: T) {
document.dispatchEvent(custom_event(type, detail))
}

@ -12,3 +12,4 @@ export * from './ssr';
export * from './transitions'; export * from './transitions';
export * from './utils'; export * from './utils';
export * from './Component'; export * from './Component';
export * from './dev';

Loading…
Cancel
Save