|
|
|
@ -24,13 +24,13 @@ import unwrap_parens from './utils/unwrap_parens';
|
|
|
|
import Slot from './nodes/Slot';
|
|
|
|
import Slot from './nodes/Slot';
|
|
|
|
import { Node as ESTreeNode } from 'estree';
|
|
|
|
import { Node as ESTreeNode } from 'estree';
|
|
|
|
|
|
|
|
|
|
|
|
type ComponentOptions = {
|
|
|
|
interface ComponentOptions {
|
|
|
|
namespace?: string;
|
|
|
|
namespace?: string;
|
|
|
|
tag?: string;
|
|
|
|
tag?: string;
|
|
|
|
immutable?: boolean;
|
|
|
|
immutable?: boolean;
|
|
|
|
accessors?: boolean;
|
|
|
|
accessors?: boolean;
|
|
|
|
preserveWhitespace?: boolean;
|
|
|
|
preserveWhitespace?: boolean;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We need to tell estree-walker that it should always
|
|
|
|
// We need to tell estree-walker that it should always
|
|
|
|
// look for an `else` block, otherwise it might get
|
|
|
|
// look for an `else` block, otherwise it might get
|
|
|
|
@ -67,6 +67,120 @@ function remove_node(code: MagicString, start: number, end: number, body: Node,
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function process_component_options(component: Component, nodes) {
|
|
|
|
|
|
|
|
const component_options: ComponentOptions = {
|
|
|
|
|
|
|
|
immutable: component.compile_options.immutable || false,
|
|
|
|
|
|
|
|
accessors: 'accessors' in component.compile_options
|
|
|
|
|
|
|
|
? component.compile_options.accessors
|
|
|
|
|
|
|
|
: !!component.compile_options.customElement,
|
|
|
|
|
|
|
|
preserveWhitespace: !!component.compile_options.preserveWhitespace
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const node = nodes.find(node => node.name === 'svelte:options');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function get_value(attribute, code, message) {
|
|
|
|
|
|
|
|
const { value } = attribute;
|
|
|
|
|
|
|
|
const chunk = value[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!chunk) return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value.length > 1) {
|
|
|
|
|
|
|
|
component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (chunk.type === 'Text') return chunk.data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (chunk.expression.type !== 'Literal') {
|
|
|
|
|
|
|
|
component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return chunk.expression.value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
|
|
|
|
node.attributes.forEach(attribute => {
|
|
|
|
|
|
|
|
if (attribute.type === 'Attribute') {
|
|
|
|
|
|
|
|
const { name } = attribute;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (name) {
|
|
|
|
|
|
|
|
case 'tag': {
|
|
|
|
|
|
|
|
const code = 'invalid-tag-attribute';
|
|
|
|
|
|
|
|
const message = `'tag' must be a string literal`;
|
|
|
|
|
|
|
|
const tag = get_value(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)) {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-tag-property`,
|
|
|
|
|
|
|
|
message: `tag name must be two or more words joined by the '-' character`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options.tag = tag;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'namespace': {
|
|
|
|
|
|
|
|
const code = 'invalid-namespace-attribute';
|
|
|
|
|
|
|
|
const message = `The 'namespace' attribute must be a string literal representing a valid namespace`;
|
|
|
|
|
|
|
|
const ns = get_value(attribute, code, message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof ns !== 'string') component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (valid_namespaces.indexOf(ns) === -1) {
|
|
|
|
|
|
|
|
const match = fuzzymatch(ns, valid_namespaces);
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-namespace-property`,
|
|
|
|
|
|
|
|
message: `Invalid namespace '${ns}' (did you mean '${match}'?)`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-namespace-property`,
|
|
|
|
|
|
|
|
message: `Invalid namespace '${ns}'`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options.namespace = ns;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'accessors':
|
|
|
|
|
|
|
|
case 'immutable':
|
|
|
|
|
|
|
|
case 'preserveWhitespace':
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const code = `invalid-${name}-value`;
|
|
|
|
|
|
|
|
const message = `${name} attribute must be true or false`;
|
|
|
|
|
|
|
|
const value = get_value(attribute, code, message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof value !== 'boolean') component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options[name] = value;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-options-attribute`,
|
|
|
|
|
|
|
|
message: `<svelte:options> unknown attribute`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-options-attribute`,
|
|
|
|
|
|
|
|
message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return component_options;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default class Component {
|
|
|
|
export default class Component {
|
|
|
|
stats: Stats;
|
|
|
|
stats: Stats;
|
|
|
|
warnings: Warning[];
|
|
|
|
warnings: Warning[];
|
|
|
|
@ -97,7 +211,7 @@ 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();
|
|
|
|
@ -106,12 +220,12 @@ export default class Component {
|
|
|
|
indirect_dependencies: Map<string, Set<string>> = new Map();
|
|
|
|
indirect_dependencies: Map<string, Set<string>> = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
file: string;
|
|
|
|
file: string;
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
stylesheet: Stylesheet;
|
|
|
|
stylesheet: Stylesheet;
|
|
|
|
@ -140,6 +254,7 @@ export default class Component {
|
|
|
|
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
|
|
|
|
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);
|
|
|
|
@ -283,7 +398,7 @@ export default class Component {
|
|
|
|
this.source
|
|
|
|
this.source
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const parts = module.split('✂]');
|
|
|
|
const parts = module.split('âÃÂÃÂ]');
|
|
|
|
const final_chunk = parts.pop();
|
|
|
|
const final_chunk = parts.pop();
|
|
|
|
|
|
|
|
|
|
|
|
const compiled = new Bundle({ separator: '' });
|
|
|
|
const compiled = new Bundle({ separator: '' });
|
|
|
|
@ -296,7 +411,7 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
const { filename } = compile_options;
|
|
|
|
const { filename } = compile_options;
|
|
|
|
|
|
|
|
|
|
|
|
// special case — the source file doesn't actually get used anywhere. we need
|
|
|
|
// special case âÃÂàthe source file doesn't actually get used anywhere. we need
|
|
|
|
// to add an empty file to populate map.sources and map.sourcesContent
|
|
|
|
// to add an empty file to populate map.sources and map.sourcesContent
|
|
|
|
if (!parts.length) {
|
|
|
|
if (!parts.length) {
|
|
|
|
compiled.addSource({
|
|
|
|
compiled.addSource({
|
|
|
|
@ -305,7 +420,7 @@ export default class Component {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const pattern = /\[✂(\d+)-(\d+)$/;
|
|
|
|
const pattern = /\[âÃÂÃÂ(\d+)-(\d+)$/;
|
|
|
|
|
|
|
|
|
|
|
|
parts.forEach((str: string) => {
|
|
|
|
parts.forEach((str: string) => {
|
|
|
|
const chunk = str.replace(pattern, '');
|
|
|
|
const chunk = str.replace(pattern, '');
|
|
|
|
@ -398,12 +513,12 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
error(
|
|
|
|
error(
|
|
|
|
pos: {
|
|
|
|
pos: {
|
|
|
|
start: number,
|
|
|
|
start: number;
|
|
|
|
end: number
|
|
|
|
end: number;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
e : {
|
|
|
|
e: {
|
|
|
|
code: string,
|
|
|
|
code: string;
|
|
|
|
message: string
|
|
|
|
message: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
error(e.message, {
|
|
|
|
error(e.message, {
|
|
|
|
@ -418,12 +533,12 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
warn(
|
|
|
|
warn(
|
|
|
|
pos: {
|
|
|
|
pos: {
|
|
|
|
start: number,
|
|
|
|
start: number;
|
|
|
|
end: number
|
|
|
|
end: number;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
warning: {
|
|
|
|
warning: {
|
|
|
|
code: string,
|
|
|
|
code: string;
|
|
|
|
message: string
|
|
|
|
message: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if (!this.locator) {
|
|
|
|
if (!this.locator) {
|
|
|
|
@ -527,9 +642,9 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
let result = '';
|
|
|
|
let result = '';
|
|
|
|
|
|
|
|
|
|
|
|
script.content.body.forEach((node, i) => {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -541,7 +656,7 @@ export default class Component {
|
|
|
|
b = script.content.end;
|
|
|
|
b = script.content.end;
|
|
|
|
while (/\s/.test(this.source[b - 1])) b -= 1;
|
|
|
|
while (/\s/.test(this.source[b - 1])) b -= 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (a < b) result += `[✂${a}-${b}✂]`;
|
|
|
|
if (a < b) result += `[âÃÂÃÂ${a}-${b}âÃÂÃÂ]`;
|
|
|
|
|
|
|
|
|
|
|
|
return result || null;
|
|
|
|
return result || null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -564,7 +679,7 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
this.add_sourcemap_locations(script.content);
|
|
|
|
this.add_sourcemap_locations(script.content);
|
|
|
|
|
|
|
|
|
|
|
|
let { scope, globals } = create_scopes(script.content);
|
|
|
|
const { scope, globals } = create_scopes(script.content);
|
|
|
|
this.module_scope = scope;
|
|
|
|
this.module_scope = scope;
|
|
|
|
|
|
|
|
|
|
|
|
scope.declarations.forEach((node, name) => {
|
|
|
|
scope.declarations.forEach((node, name) => {
|
|
|
|
@ -588,7 +703,7 @@ export default class Component {
|
|
|
|
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,
|
|
|
|
@ -624,7 +739,7 @@ export default class Component {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let { 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;
|
|
|
|
|
|
|
|
|
|
|
|
@ -705,7 +820,7 @@ export default class Component {
|
|
|
|
let scope = instance_scope;
|
|
|
|
let scope = instance_scope;
|
|
|
|
|
|
|
|
|
|
|
|
walk(this.ast.instance.content, {
|
|
|
|
walk(this.ast.instance.content, {
|
|
|
|
enter(node, parent) {
|
|
|
|
enter(node) {
|
|
|
|
if (map.has(node)) {
|
|
|
|
if (map.has(node)) {
|
|
|
|
scope = map.get(node);
|
|
|
|
scope = map.get(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -738,7 +853,7 @@ export default class Component {
|
|
|
|
scope = scope.parent;
|
|
|
|
scope = scope.parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extract_reactive_store_references() {
|
|
|
|
extract_reactive_store_references() {
|
|
|
|
@ -786,7 +901,7 @@ export default class Component {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (name[0] === '$' && name[1] !== '$') {
|
|
|
|
if (name[0] === '$' && name[1] !== '$') {
|
|
|
|
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('$$')) {
|
|
|
|
@ -888,13 +1003,13 @@ export default class Component {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (variable.writable && variable.name !== variable.export_name) {
|
|
|
|
if (variable.writable && variable.name !== variable.export_name) {
|
|
|
|
code.prependRight(declarator.id.start, `${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 = !next_variable.export_name
|
|
|
|
|| (current_group.insert && next_variable.subscribable)
|
|
|
|
|| (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} `);
|
|
|
|
@ -904,7 +1019,7 @@ export default class Component {
|
|
|
|
current_group = null;
|
|
|
|
current_group = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (variable.subscribable) {
|
|
|
|
if (variable.subscribable) {
|
|
|
|
let 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} `);
|
|
|
|
@ -975,9 +1090,9 @@ export default class Component {
|
|
|
|
if (!d.init) return false;
|
|
|
|
if (!d.init) return false;
|
|
|
|
if (d.init.type !== 'Literal') return false;
|
|
|
|
if (d.init.type !== 'Literal') return false;
|
|
|
|
|
|
|
|
|
|
|
|
const v = this.var_lookup.get(d.id.name)
|
|
|
|
const v = this.var_lookup.get(d.id.name);
|
|
|
|
if (v.reassigned) return false
|
|
|
|
if (v.reassigned) return false;
|
|
|
|
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;
|
|
|
|
@ -992,7 +1107,7 @@ export default class Component {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
hoistable_nodes.add(node);
|
|
|
|
hoistable_nodes.add(node);
|
|
|
|
this.fully_hoisted.push(`[✂${node.start}-${node.end}✂]`);
|
|
|
|
this.fully_hoisted.push(`[âÃÂÃÂ${node.start}-${node.end}âÃÂÃÂ]`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -1006,7 +1121,7 @@ export default class Component {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const checked = new Set();
|
|
|
|
const checked = new Set();
|
|
|
|
let walking = new Set();
|
|
|
|
const walking = new Set();
|
|
|
|
|
|
|
|
|
|
|
|
const is_hoistable = fn_declaration => {
|
|
|
|
const is_hoistable = fn_declaration => {
|
|
|
|
if (fn_declaration.type === 'ExportNamedDeclaration') {
|
|
|
|
if (fn_declaration.type === 'ExportNamedDeclaration') {
|
|
|
|
@ -1015,7 +1130,7 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
const instance_scope = this.instance_scope;
|
|
|
|
const instance_scope = this.instance_scope;
|
|
|
|
let scope = this.instance_scope;
|
|
|
|
let scope = this.instance_scope;
|
|
|
|
let map = this.instance_scope_map;
|
|
|
|
const map = this.instance_scope_map;
|
|
|
|
|
|
|
|
|
|
|
|
let hoistable = true;
|
|
|
|
let hoistable = true;
|
|
|
|
|
|
|
|
|
|
|
|
@ -1051,7 +1166,7 @@ export default class Component {
|
|
|
|
hoistable = false;
|
|
|
|
hoistable = false;
|
|
|
|
} else if (!is_hoistable(other_declaration)) {
|
|
|
|
} else if (!is_hoistable(other_declaration)) {
|
|
|
|
hoistable = false;
|
|
|
|
hoistable = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
else {
|
|
|
|
@ -1084,7 +1199,7 @@ export default class Component {
|
|
|
|
|
|
|
|
|
|
|
|
remove_indentation(this.code, node);
|
|
|
|
remove_indentation(this.code, node);
|
|
|
|
|
|
|
|
|
|
|
|
this.fully_hoisted.push(`[✂${node.start}-${node.end}✂]`);
|
|
|
|
this.fully_hoisted.push(`[âÃÂÃÂ${node.start}-${node.end}âÃÂÃÂ]`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1103,7 +1218,7 @@ export default class Component {
|
|
|
|
const dependencies = new Set();
|
|
|
|
const dependencies = new Set();
|
|
|
|
|
|
|
|
|
|
|
|
let scope = this.instance_scope;
|
|
|
|
let scope = this.instance_scope;
|
|
|
|
let map = this.instance_scope_map;
|
|
|
|
const map = this.instance_scope_map;
|
|
|
|
|
|
|
|
|
|
|
|
walk(node.body, {
|
|
|
|
walk(node.body, {
|
|
|
|
enter(node, parent) {
|
|
|
|
enter(node, parent) {
|
|
|
|
@ -1236,115 +1351,3 @@ export default class Component {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function process_component_options(component: Component, nodes) {
|
|
|
|
|
|
|
|
const component_options: ComponentOptions = {
|
|
|
|
|
|
|
|
immutable: component.compile_options.immutable || false,
|
|
|
|
|
|
|
|
accessors: 'accessors' in component.compile_options
|
|
|
|
|
|
|
|
? component.compile_options.accessors
|
|
|
|
|
|
|
|
: !!component.compile_options.customElement,
|
|
|
|
|
|
|
|
preserveWhitespace: !!component.compile_options.preserveWhitespace
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const node = nodes.find(node => node.name === 'svelte:options');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function get_value(attribute, code, message) {
|
|
|
|
|
|
|
|
const { value } = attribute;
|
|
|
|
|
|
|
|
const chunk = value[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!chunk) return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value.length > 1) {
|
|
|
|
|
|
|
|
component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (chunk.type === 'Text') return chunk.data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (chunk.expression.type !== 'Literal') {
|
|
|
|
|
|
|
|
component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return chunk.expression.value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
|
|
|
|
node.attributes.forEach(attribute => {
|
|
|
|
|
|
|
|
if (attribute.type === 'Attribute') {
|
|
|
|
|
|
|
|
const { name } = attribute;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (name) {
|
|
|
|
|
|
|
|
case 'tag': {
|
|
|
|
|
|
|
|
const code = 'invalid-tag-attribute';
|
|
|
|
|
|
|
|
const message = `'tag' must be a string literal`;
|
|
|
|
|
|
|
|
const tag = get_value(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)) {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-tag-property`,
|
|
|
|
|
|
|
|
message: `tag name must be two or more words joined by the '-' character`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options.tag = tag;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'namespace': {
|
|
|
|
|
|
|
|
const code = 'invalid-namespace-attribute';
|
|
|
|
|
|
|
|
const message = `The 'namespace' attribute must be a string literal representing a valid namespace`;
|
|
|
|
|
|
|
|
const ns = get_value(attribute, code, message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof ns !== 'string') component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (valid_namespaces.indexOf(ns) === -1) {
|
|
|
|
|
|
|
|
const match = fuzzymatch(ns, valid_namespaces);
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-namespace-property`,
|
|
|
|
|
|
|
|
message: `Invalid namespace '${ns}' (did you mean '${match}'?)`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-namespace-property`,
|
|
|
|
|
|
|
|
message: `Invalid namespace '${ns}'`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options.namespace = ns;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'accessors':
|
|
|
|
|
|
|
|
case 'immutable':
|
|
|
|
|
|
|
|
case 'preserveWhitespace':
|
|
|
|
|
|
|
|
const code = `invalid-${name}-value`;
|
|
|
|
|
|
|
|
const message = `${name} attribute must be true or false`
|
|
|
|
|
|
|
|
const value = get_value(attribute, code, message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof value !== 'boolean') component.error(attribute, { code, message });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
component_options[name] = value;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-options-attribute`,
|
|
|
|
|
|
|
|
message: `<svelte:options> unknown attribute`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
component.error(attribute, {
|
|
|
|
|
|
|
|
code: `invalid-options-attribute`,
|
|
|
|
|
|
|
|
message: `<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes`
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return component_options;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|