Skip to content

[feat] Add errorMode option to compile to allow continuing on errors (and mark them as warnings) #6194

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 12 commits into from
Jul 17, 2021
2 changes: 2 additions & 0 deletions site/content/docs/04-compile-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The following options can be passed to the compiler. None are required:
| `name` | string | `"Component"`
| `format` | `"esm"` or `"cjs"` | `"esm"`
| `generate` | `"dom"` or `"ssr"` or `false` | `"dom"`
| `errorMode` | `"throw"` or `"warn"` | `"throw"`
| `varsReport` | `"strict"` or `"full"` or `false` | `"strict"`
| `dev` | boolean | `false`
| `immutable` | boolean | `false`
Expand All @@ -67,6 +68,7 @@ The following options can be passed to the compiler. None are required:
| `name` | `"Component"` | `string` that sets the name of the resulting JavaScript class (though the compiler will rename it if it would otherwise conflict with other variables in scope). It will normally be inferred from `filename`.
| `format` | `"esm"` | If `"esm"`, creates a JavaScript module (with `import` and `export`). If `"cjs"`, creates a CommonJS module (with `require` and `module.exports`), which is useful in some server-side rendering situations or for testing.
| `generate` | `"dom"` | If `"dom"`, Svelte emits a JavaScript class for mounting to the DOM. If `"ssr"`, Svelte emits an object with a `render` method suitable for server-side rendering. If `false`, no JavaScript or CSS is returned; just metadata.
| `errorMode` | `"throw"` | If `"throw"`, Svelte throws when a compilation error occured. If `"warn"`, Svelte will treat errors as warnings and add them to the warning report.
| `varsReport` | `"strict"` | If `"strict"`, Svelte returns a variables report with only variables that are not globals nor internals. If `"full"`, Svelte returns a variables report with all detected variables. If `false`, no variables report is returned.
| `dev` | `false` | If `true`, causes extra code to be added to components that will perform runtime checks and provide debugging information during development.
| `immutable` | `false` | If `true`, tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed.
Expand Down
58 changes: 31 additions & 27 deletions src/compiler/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,18 @@ export default class Component {
message: string;
}
) {
error(e.message, {
name: 'ValidationError',
code: e.code,
source: this.source,
start: pos.start,
end: pos.end,
filename: this.compile_options.filename
});
if (this.compile_options.errorMode === 'warn') {
this.warn(pos, e);
} else {
error(e.message, {
name: 'ValidationError',
code: e.code,
source: this.source,
start: pos.start,
end: pos.end,
filename: this.compile_options.filename
});
}
}

warn(
Expand Down Expand Up @@ -477,12 +481,12 @@ export default class Component {

extract_exports(node) {
if (node.type === 'ExportDefaultDeclaration') {
this.error(node, compiler_errors.default_export);
return this.error(node, compiler_errors.default_export);
}

if (node.type === 'ExportNamedDeclaration') {
if (node.source) {
this.error(node, compiler_errors.not_implemented);
return this.error(node, compiler_errors.not_implemented);
}
if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') {
Expand Down Expand Up @@ -552,7 +556,7 @@ export default class Component {

scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, compiler_errors.illegal_declaration);
return this.error(node as any, compiler_errors.illegal_declaration);
}

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

globals.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, compiler_errors.illegal_subscription);
return this.error(node as any, compiler_errors.illegal_subscription);
} else {
this.add_var({
name,
Expand Down Expand Up @@ -625,7 +629,7 @@ export default class Component {

instance_scope.declarations.forEach((node, name) => {
if (name[0] === '$') {
this.error(node as any, compiler_errors.illegal_declaration);
return this.error(node as any, compiler_errors.illegal_declaration);
}

const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
Expand Down Expand Up @@ -659,7 +663,7 @@ export default class Component {
});
} else if (name[0] === '$') {
if (name === '$' || name[1] === '$') {
this.error(node as any, compiler_errors.illegal_global(name));
return this.error(node as any, compiler_errors.illegal_global(name));
}

this.add_var({
Expand Down Expand Up @@ -856,7 +860,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, compiler_errors.contextual_store);
return this.error(node as any, compiler_errors.contextual_store);
}
}
}
Expand Down Expand Up @@ -921,7 +925,7 @@ export default class Component {

if (variable.export_name) {
// TODO is this still true post-#3539?
component.error(declarator as any, compiler_errors.destructured_prop);
return component.error(declarator as any, compiler_errors.destructured_prop);
}

if (variable.subscribable) {
Expand Down Expand Up @@ -1276,7 +1280,7 @@ export default class Component {
if (cycle && cycle.length) {
const declarationList = lookup.get(cycle[0]);
const declaration = declarationList[0];
this.error(declaration.node, compiler_errors.cyclical_reactive_declaration(cycle));
return this.error(declaration.node, compiler_errors.cyclical_reactive_declaration(cycle));
}

const add_declaration = declaration => {
Expand All @@ -1299,7 +1303,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, compiler_errors.illegal_global(name));
return this.error(node, compiler_errors.illegal_global(name));
}

this.has_reactive_assignments = true; // TODO does this belong here?
Expand Down Expand Up @@ -1348,13 +1352,13 @@ function process_component_options(component: Component, nodes) {
if (!chunk) return true;

if (value.length > 1) {
component.error(attribute, { code, message });
return component.error(attribute, { code, message });
}

if (chunk.type === 'Text') return chunk.data;

if (chunk.expression.type !== 'Literal') {
component.error(attribute, { code, message });
return component.error(attribute, { code, message });
}

return chunk.expression.value;
Expand All @@ -1370,11 +1374,11 @@ function process_component_options(component: Component, nodes) {
const tag = get_value(attribute, compiler_errors.invalid_tag_attribute);

if (typeof tag !== 'string' && tag !== null) {
component.error(attribute, compiler_errors.invalid_tag_attribute);
return 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, compiler_errors.invalid_tag_property);
return component.error(attribute, compiler_errors.invalid_tag_property);
}

if (tag && !component.compile_options.customElement) {
Expand All @@ -1389,12 +1393,12 @@ function process_component_options(component: Component, nodes) {
const ns = get_value(attribute, compiler_errors.invalid_namespace_attribute);

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

if (valid_namespaces.indexOf(ns) === -1) {
const match = fuzzymatch(ns, valid_namespaces);
component.error(attribute, compiler_errors.invalid_namespace_property(ns, match));
return component.error(attribute, compiler_errors.invalid_namespace_property(ns, match));
}

component_options.namespace = ns;
Expand All @@ -1407,18 +1411,18 @@ function process_component_options(component: Component, nodes) {
const value = get_value(attribute, compiler_errors.invalid_attribute_value(name));

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

component_options[name] = value;
break;
}

default:
component.error(attribute, compiler_errors.invalid_options_attribute_unknown);
return component.error(attribute, compiler_errors.invalid_options_attribute_unknown);
}
} else {
component.error(attribute, compiler_errors.invalid_options_attribute);
return component.error(attribute, compiler_errors.invalid_options_attribute);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/css/Selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export default class Selector {

for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) {
component.error(this.blocks[i].selectors[0], compiler_errors.css_invalid_global);
return component.error(this.blocks[i].selectors[0], compiler_errors.css_invalid_global);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/compile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const valid_options = [
'filename',
'sourcemap',
'generate',
'errorMode',
'varsReport',
'outputFilename',
'cssOutputFilename',
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/Animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export default class Animation extends Node {

if (parent.animation) {
component.error(this, compiler_errors.duplicate_animation);
return;
}

const block = parent.parent;
if (!block || block.type !== 'EachBlock' || !block.key) {
// TODO can we relax the 'immediate child' rule?
component.error(this, compiler_errors.invalid_animation_immediate);
return;
}

(block as EachBlock).has_animation = true;
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/compile/nodes/Binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default class Binding extends Node {

if (info.expression.type !== 'Identifier' && info.expression.type !== 'MemberExpression') {
component.error(info, compiler_errors.invalid_directive_value);
return;
}

this.name = info.name;
Expand All @@ -50,9 +51,11 @@ export default class Binding extends Node {
// make sure we track this as a mutable ref
if (scope.is_let(name)) {
component.error(this, compiler_errors.invalid_binding_let);
return;
} else if (scope.names.has(name)) {
if (scope.is_await(name)) {
component.error(this, compiler_errors.invalid_binding_await);
return;
}

scope.dependencies_for_name.get(name).forEach(name => {
Expand All @@ -66,12 +69,14 @@ export default class Binding extends Node {

if (!variable || variable.global) {
component.error(this.expression.node as any, compiler_errors.binding_undeclared(name));
return;
}

variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;

if (info.expression.type === 'Identifier' && !variable.writable) {
component.error(this.expression.node as any, compiler_errors.invalid_binding_writibale);
return;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/compile/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default class EachBlock extends AbstractBlock {
if (this.children.length !== 1) {
const child = this.children.find(child => !!(child as Element).animation);
component.error((child as Element).animation, compiler_errors.invalid_animation_sole);
return;
}
}

Expand Down
Loading