From d3e11bb731e604a6c4c084a8d5f8c208b39de5b6 Mon Sep 17 00:00:00 2001 From: Maxime LUCE Date: Tue, 13 Apr 2021 18:27:31 +0200 Subject: [PATCH 1/9] feat: add errorMode option to compile - allow compiler to pass error as warnings - enforce stops after errors during compilation - should review Element.ts:302 - add a test case for errorMode - adding documentation --- site/content/docs/04-compile-time.md | 2 + src/compiler/compile/Component.ts | 60 ++++++++++--------- src/compiler/compile/css/Selector.ts | 4 +- src/compiler/compile/index.ts | 1 + src/compiler/compile/nodes/Animation.ts | 2 + src/compiler/compile/nodes/Binding.ts | 5 ++ src/compiler/compile/nodes/EachBlock.ts | 1 + src/compiler/compile/nodes/Element.ts | 60 ++++++++++--------- src/compiler/compile/nodes/Head.ts | 1 + src/compiler/compile/nodes/InlineComponent.ts | 8 +-- src/compiler/compile/nodes/Let.ts | 2 +- src/compiler/compile/nodes/Slot.ts | 6 +- src/compiler/compile/nodes/SlotTemplate.ts | 6 +- src/compiler/compile/nodes/Title.ts | 3 +- src/compiler/compile/nodes/Transition.ts | 1 + src/compiler/compile/nodes/Window.ts | 6 +- .../compile/nodes/shared/Expression.ts | 2 +- src/compiler/interfaces.ts | 1 + .../samples/error-mode-warn/input.svelte | 6 ++ .../samples/error-mode-warn/options.json | 3 + .../samples/error-mode-warn/warnings.json | 47 +++++++++++++++ 21 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 test/validator/samples/error-mode-warn/input.svelte create mode 100644 test/validator/samples/error-mode-warn/options.json create mode 100644 test/validator/samples/error-mode-warn/warnings.json diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/04-compile-time.md index 361c9cdbc4ec..8d4db3595868 100644 --- a/site/content/docs/04-compile-time.md +++ b/site/content/docs/04-compile-time.md @@ -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"` | `"dom"` +| `errorMode` | `"throw"` or `"warn"` | `"throw"` | `dev` | boolean | `false` | `immutable` | boolean | `false` | `hydratable` | boolean | `false` @@ -66,6 +67,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. | `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. | `hydratable` | `false` | If `true` when generating DOM code, enables the `hydrate: true` runtime option, which allows a component to upgrade existing DOM rather than creating new DOM from scratch. When generating SSR code, this adds markers to `` elements so that hydration knows which to replace. diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 155b64778baa..0d1cddacaf90 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -412,14 +412,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( @@ -460,7 +464,7 @@ export default class Component { extract_exports(node) { if (node.type === 'ExportDefaultDeclaration') { - this.error(node, { + return this.error(node, { code: 'default-export', message: 'A component cannot have a default export' }); @@ -468,7 +472,7 @@ export default class Component { if (node.type === 'ExportNamedDeclaration') { if (node.source) { - this.error(node, { + return this.error(node, { code: 'not-implemented', message: 'A component currently cannot have an export ... from' }); @@ -550,7 +554,7 @@ export default class Component { scope.declarations.forEach((node, name) => { if (name[0] === '$') { - this.error(node as any, { + return this.error(node as any, { code: 'illegal-declaration', message: 'The $ prefix is reserved, and cannot be used for variable and import names' }); @@ -568,7 +572,7 @@ export default class Component { globals.forEach((node, name) => { if (name[0] === '$') { - this.error(node as any, { + return this.error(node as any, { code: 'illegal-subscription', message: 'Cannot reference store value inside + + + diff --git a/test/validator/samples/error-mode-warn/options.json b/test/validator/samples/error-mode-warn/options.json new file mode 100644 index 000000000000..8d48d2f34eee --- /dev/null +++ b/test/validator/samples/error-mode-warn/options.json @@ -0,0 +1,3 @@ +{ + "errorMode": "warn" +} \ No newline at end of file diff --git a/test/validator/samples/error-mode-warn/warnings.json b/test/validator/samples/error-mode-warn/warnings.json new file mode 100644 index 000000000000..cce6324df5dd --- /dev/null +++ b/test/validator/samples/error-mode-warn/warnings.json @@ -0,0 +1,47 @@ +[ + { + "code": "invalid-binding", + "message": "Cannot bind to a variable which is not writable", + "pos": 61, + "start": { + "line": 5, + "column": 19, + "character": 61 + }, + "end": { + "line": 5, + "column": 24, + "character": 66 + } + }, + { + "code": "missing-declaration", + "message": "'undeclared' is not defined", + "pos": 88, + "start": { + "character": 88, + "column": 19, + "line": 6 + }, + "end": { + "character": 98, + "column": 29, + "line": 6 + } + }, + { + "code": "binding-undeclared", + "message": "undeclared is not declared", + "pos": 88, + "end": { + "character": 98, + "column": 29, + "line": 6 + }, + "start": { + "character": 88, + "column": 19, + "line": 6 + } + } +] \ No newline at end of file From 506a1d0395098d63e544ca5c2fd5c43d3a5ea07a Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:17:22 +0200 Subject: [PATCH 2/9] unscrew --- src/compiler/compile/nodes/Animation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/nodes/Animation.ts b/src/compiler/compile/nodes/Animation.ts index 0a0674ac5988..ac9dddd275ad 100644 --- a/src/compiler/compile/nodes/Animation.ts +++ b/src/compiler/compile/nodes/Animation.ts @@ -21,13 +21,15 @@ export default class Animation extends Node { component.add_reference(info.name.split('.')[0]); if (parent.animation) { - return component.error(this, compiler_errors.duplicate_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? - return component.error(this, compiler_errors.invalid_animation_immediate); + component.error(this, compiler_errors.invalid_animation_immediate); + return; } (block as EachBlock).has_animation = true; From ff5b4d5f45feaee79a7b6d411ededc326acfb008 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:18:09 +0200 Subject: [PATCH 3/9] unscrew --- src/compiler/compile/nodes/Binding.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index f64afd702bae..83849c892bb6 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -49,10 +49,12 @@ export default class Binding extends Node { // make sure we track this as a mutable ref if (scope.is_let(name)) { - return component.error(this, compiler_errors.invalid_binding_let); + component.error(this, compiler_errors.invalid_binding_let); + return; } else if (scope.names.has(name)) { if (scope.is_await(name)) { - return component.error(this, compiler_errors.invalid_binding_await); + component.error(this, compiler_errors.invalid_binding_await); + return; } scope.dependencies_for_name.get(name).forEach(name => { @@ -65,13 +67,15 @@ export default class Binding extends Node { const variable = component.var_lookup.get(name); if (!variable || variable.global) { - return component.error(this.expression.node as any, compiler_errors.binding_undeclared(name)); + 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) { - return component.error(this.expression.node as any, compiler_errors.invalid_binding_writibale); + component.error(this.expression.node as any, compiler_errors.invalid_binding_writibale); + return; } } From b2a7b50053599f35ce6dbb4ae4ec33a902060645 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:18:34 +0200 Subject: [PATCH 4/9] unscrew --- src/compiler/compile/nodes/EachBlock.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts index 4f1f3a572fd7..c03128c388a6 100644 --- a/src/compiler/compile/nodes/EachBlock.ts +++ b/src/compiler/compile/nodes/EachBlock.ts @@ -62,7 +62,8 @@ export default class EachBlock extends AbstractBlock { if (this.has_animation) { if (this.children.length !== 1) { const child = this.children.find(child => !!(child as Element).animation); - return component.error((child as Element).animation, compiler_errors.invalid_animation_sole); + component.error((child as Element).animation, compiler_errors.invalid_animation_sole); + return; } } From e9f43759b6792d060b8ee9b476d1ebccea3a91a3 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:19:08 +0200 Subject: [PATCH 5/9] unscrew --- src/compiler/compile/nodes/Binding.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 83849c892bb6..1efc1a3038c4 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -36,7 +36,8 @@ export default class Binding extends Node { super(component, parent, scope, info); if (info.expression.type !== 'Identifier' && info.expression.type !== 'MemberExpression') { - return component.error(info, compiler_errors.invalid_directive_value); + component.error(info, compiler_errors.invalid_directive_value); + return; } this.name = info.name; From 9536e568bfe3b091636201c0c5b58a6d4b587e1b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 7 Jul 2021 17:19:55 +0200 Subject: [PATCH 6/9] unscrew --- src/compiler/compile/nodes/Element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 81dcaf0d31c4..dbde8f1b2df8 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -141,7 +141,8 @@ export default class Element extends Node { if (info.children.length > 0) { const value_attribute = info.attributes.find(node => node.name === 'value'); if (value_attribute) { - return component.error(value_attribute, compiler_errors.textarea_duplicate_value); + component.error(value_attribute, compiler_errors.textarea_duplicate_value); + return; } // this is an egregious hack, but it's the easiest way to get