Skip to content

[chore] move compiler warnings/errors to dedicate files #6503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 31 additions & 113 deletions src/compiler/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { apply_preprocessor_sourcemap } from '../utils/mapped_code';
import Element from './nodes/Element';
import { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping/dist/types/types';
import { clone } from '../utils/clone';
import compiler_warnings from './compiler_warnings';
import compiler_errors from './compiler_errors';

interface ComponentOptions {
namespace?: string;
Expand Down Expand Up @@ -161,10 +163,7 @@ export default class Component {
const svelteOptions = ast.html.children.find(
child => child.name === 'svelte:options'
) || { start: 0, end: 0 };
this.warn(svelteOptions, {
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}/>'
});
this.warn(svelteOptions, compiler_warnings.custom_element_no_tag);
}
this.tag = this.component_options.tag || compile_options.tag;
} else {
Expand Down Expand Up @@ -478,18 +477,12 @@ export default class Component {

extract_exports(node) {
if (node.type === 'ExportDefaultDeclaration') {
this.error(node, {
code: 'default-export',
message: 'A component cannot have a default export'
});
this.error(node, compiler_errors.default_export);
}

if (node.type === 'ExportNamedDeclaration') {
if (node.source) {
this.error(node, {
code: 'not-implemented',
message: 'A component currently cannot have an export ... from'
});
this.error(node, compiler_errors.not_implemented);
}
if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') {
Expand All @@ -498,10 +491,7 @@ export default class Component {
const variable = this.var_lookup.get(name);
variable.export_name = name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(declarator, {
code: 'unused-export-let',
message: `${this.name.name} has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``
});
this.warn(declarator, compiler_warnings.unused_export_let(this.name.name, name));
}
});
});
Expand All @@ -521,10 +511,7 @@ export default class Component {
variable.export_name = specifier.exported.name;

if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(specifier, {
code: 'unused-export-let',
message: `${this.name.name} has unused export property '${specifier.exported.name}'. If it is for external reference only, please consider using \`export const ${specifier.exported.name}\``
});
this.warn(specifier, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name));
}
}
});
Expand Down Expand Up @@ -555,10 +542,7 @@ export default class Component {
walk(script.content, {
enter(node: Node) {
if (node.type === 'LabeledStatement' && node.label.name === '$') {
component.warn(node as any, {
code: 'module-script-reactive-declaration',
message: '$: has no effect in a module script'
});
component.warn(node as any, compiler_warnings.module_script_reactive_declaration);
}
}
});
Expand All @@ -568,10 +552,7 @@ export default class Component {

scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, {
code: 'illegal-declaration',
message: 'The $ prefix is reserved, and cannot be used for variable and import names'
});
this.error(node as any, compiler_errors.illegal_declaration);
}

const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
Expand All @@ -586,10 +567,7 @@ export default class Component {

globals.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, {
code: 'illegal-subscription',
message: 'Cannot reference store value inside <script context="module">'
});
this.error(node as any, compiler_errors.illegal_subscription);
} else {
this.add_var({
name,
Expand Down Expand Up @@ -647,10 +625,7 @@ export default class Component {

instance_scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, {
code: 'illegal-declaration',
message: 'The $ prefix is reserved, and cannot be used for variable and import names'
});
this.error(node as any, compiler_errors.illegal_declaration);
}

const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
Expand Down Expand Up @@ -684,10 +659,7 @@ export default class Component {
});
} else if (name[0] === '$') {
if (name === '$' || name[1] === '$') {
this.error(node as any, {
code: 'illegal-global',
message: `${name} is an illegal variable name`
});
this.error(node as any, compiler_errors.illegal_global(name));
}

this.add_var({
Expand Down Expand Up @@ -870,10 +842,7 @@ export default class Component {
node.label.name === '$' &&
parent.type !== 'Program'
) {
this.warn(node as any, {
code: 'non-top-level-reactive-declaration',
message: '$: has no effect outside of the top-level'
});
this.warn(node as any, compiler_warnings.non_top_level_reactive_declaration);
}

if (is_reference(node, parent)) {
Expand All @@ -887,10 +856,7 @@ export default class Component {

if (name[1] !== '$' && scope.has(name.slice(1)) && scope.find_owner(name.slice(1)) !== this.instance_scope) {
if (!((/Function/.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) {
this.error(node as any, {
code: 'contextual-store',
message: 'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
});
this.error(node as any, compiler_errors.contextual_store);
}
}
}
Expand Down Expand Up @@ -955,10 +921,7 @@ export default class Component {

if (variable.export_name) {
// TODO is this still true post-#3539?
component.error(declarator as any, {
code: 'destructured-prop',
message: 'Cannot declare props in destructured declaration'
});
component.error(declarator as any, compiler_errors.destructured_prop);
}

if (variable.subscribable) {
Expand Down Expand Up @@ -1248,10 +1211,7 @@ export default class Component {
variable.is_reactive_dependency = true;
if (variable.module) {
should_add_as_dependency = false;
component.warn(node as any, {
code: 'module-script-reactive-declaration',
message: `"${name}" is declared in a module script and will not be reactive`
});
component.warn(node as any, compiler_warnings.module_script_variable_reactive_declaration(name));
}
}
const is_writable_or_mutated =
Expand Down Expand Up @@ -1316,10 +1276,7 @@ export default class Component {
if (cycle && cycle.length) {
const declarationList = lookup.get(cycle[0]);
const declaration = declarationList[0];
this.error(declaration.node, {
code: 'cyclical-reactive-declaration',
message: `Cyclical dependency detected: ${cycle.join(' → ')}`
});
this.error(declaration.node, compiler_errors.cyclical_reactive_declaration(cycle));
}

const add_declaration = declaration => {
Expand All @@ -1342,10 +1299,7 @@ export default class Component {
warn_if_undefined(name: string, node, template_scope: TemplateScope) {
if (name[0] === '$') {
if (name === '$' || name[1] === '$' && !is_reserved_keyword(name)) {
this.error(node, {
code: 'illegal-global',
message: `${name} is an illegal variable name`
});
this.error(node, compiler_errors.illegal_global(name));
}

this.has_reactive_assignments = true; // TODO does this belong here?
Expand All @@ -1359,15 +1313,7 @@ export default class Component {
if (template_scope && template_scope.names.has(name)) return;
if (globals.has(name) && node.type !== 'InlineComponent') return;

let message = `'${name}' is not defined`;
if (!this.ast.instance) {
message += `. Consider adding a <script> block with 'export let ${name}' to declare a prop`;
}

this.warn(node, {
code: 'missing-declaration',
message
});
this.warn(node, compiler_warnings.missing_declaration(name, !!this.ast.instance));
}

push_ignores(ignores) {
Expand Down Expand Up @@ -1395,7 +1341,7 @@ function process_component_options(component: Component, nodes) {

const node = nodes.find(node => node.name === 'svelte:options');

function get_value(attribute, code, message) {
function get_value(attribute, {code, message}) {
const { value } = attribute;
const chunk = value[0];

Expand All @@ -1421,54 +1367,34 @@ function process_component_options(component: Component, nodes) {

switch (name) {
case 'tag': {
const code = 'invalid-tag-attribute';
const message = "'tag' must be a string literal";
const tag = get_value(attribute, code, message);
const tag = get_value(attribute, compiler_errors.invalid_tag_attribute);

if (typeof tag !== 'string' && tag !== null) {
component.error(attribute, { code, message });
component.error(attribute, compiler_errors.invalid_tag_attribute);
}

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.error(attribute, compiler_errors.invalid_tag_property);
}

if (tag && !component.compile_options.customElement) {
component.warn(attribute, {
code: 'missing-custom-element-compile-options',
message: "The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?"
});
component.warn(attribute, compiler_warnings.missing_custom_element_compile_options);
}

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);
const ns = get_value(attribute, compiler_errors.invalid_namespace_attribute);

if (typeof ns !== 'string') {
component.error(attribute, { code, message });
component.error(attribute, compiler_errors.invalid_namespace_attribute);
}

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.error(attribute, compiler_errors.invalid_namespace_property(ns, match));
}

component_options.namespace = ns;
Expand All @@ -1478,29 +1404,21 @@ function process_component_options(component: Component, nodes) {
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);
const value = get_value(attribute, compiler_errors.invalid_attribute_value(name));

if (typeof value !== 'boolean') {
component.error(attribute, { code, message });
component.error(attribute, compiler_errors.invalid_attribute_value(name));
}

component_options[name] = value;
break;
}

default:
component.error(attribute, {
code: 'invalid-options-attribute',
message: '<svelte:options> unknown attribute'
});
component.error(attribute, compiler_errors.invalid_options_attribute_unknown);
}
} else {
component.error(attribute, {
code: 'invalid-options-attribute',
message: "<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes"
});
component.error(attribute, compiler_errors.invalid_options_attribute);
}
});
}
Expand Down
Loading