From f35edafad3ab51077f6e849f207f4ce9ae8174d1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 27 Nov 2018 09:42:53 -0500 Subject: [PATCH] more validation tests --- src/compile/Component.ts | 29 +++++++++++++++---- src/compile/nodes/Action.ts | 5 +++- src/compile/nodes/Animation.ts | 2 ++ src/compile/nodes/InlineComponent.ts | 2 ++ src/compile/nodes/Transition.ts | 5 +++- src/utils/annotateWithScopes.ts | 6 +++- .../samples/a11y-not-on-components/input.html | 4 +++ .../a11y-not-on-components/warnings.json | 10 +++---- .../{errors.json => warnings.json} | 4 +-- .../{errors.json => warnings.json} | 4 +-- .../{errors.json => warnings.json} | 4 +-- .../{errors.json => warnings.json} | 4 +-- 12 files changed, 57 insertions(+), 22 deletions(-) rename test/validator/samples/action-invalid/{errors.json => warnings.json} (64%) rename test/validator/samples/animation-missing/{errors.json => warnings.json} (65%) rename test/validator/samples/missing-component/{errors.json => warnings.json} (63%) rename test/validator/samples/transition-missing/{errors.json => warnings.json} (64%) diff --git a/src/compile/Component.ts b/src/compile/Component.ts index f42412c630..bb61ff0506 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -17,6 +17,7 @@ import getCodeFrame from '../utils/getCodeFrame'; import flattenReference from '../utils/flattenReference'; import addToSet from '../utils/addToSet'; import isReference from 'is-reference'; +import TemplateScope from './nodes/shared/TemplateScope'; type Meta = { namespace?: string; @@ -53,7 +54,8 @@ export default class Component { options: CompileOptions; fragment: Fragment; module_scope: Scope; - module_scope_map: WeakMap; + instance_scope: Scope; + instance_scope_map: WeakMap; meta: Meta; @@ -391,8 +393,8 @@ export default class Component { return dependencies; } - let { module_scope, module_scope_map: map } = this; - let scope = module_scope; + let { instance_scope, instance_scope_map: map } = this; + let scope = instance_scope; const component = this; let bail = false; @@ -405,7 +407,7 @@ export default class Component { if (isReference(node, parent)) { const { name } = flattenReference(node); - if (scope.findOwner(name) === module_scope) { + if (scope.findOwner(name) === instance_scope) { dependencies.add(name); } } @@ -502,6 +504,9 @@ export default class Component { this.addSourcemapLocations(script.content); + let { scope } = createScopes(script.content); + this.module_scope = scope; + // TODO unindent this.extract_imports_and_exports(script.content, this.imports, this.module_exports); @@ -515,8 +520,8 @@ export default class Component { this.addSourcemapLocations(script.content); let { scope, map, globals } = createScopes(script.content); - this.module_scope = scope; - this.module_scope_map = map; + this.instance_scope = scope; + this.instance_scope_map = map; scope.declarations.forEach((node, name) => { this.userVars.add(name); @@ -561,6 +566,18 @@ export default class Component { this.javascript = this.extract_javascript(script); } + warn_if_undefined(node, template_scope: TemplateScope) { + const { name } = node; + if (this.module_scope && this.module_scope.declarations.has(name)) return; + if (this.instance_scope && this.instance_scope.declarations.has(name)) return; + if (template_scope.names.has(name)) return; + + this.warn(node, { + code: 'missing-declaration', + message: `'${name}' is not defined` + }); + } + instrument(node, parent, name, is_event_handler) { // TODO only make values reactive if they're used // in the template diff --git a/src/compile/nodes/Action.ts b/src/compile/nodes/Action.ts index 87565a0151..80d8f215c5 100644 --- a/src/compile/nodes/Action.ts +++ b/src/compile/nodes/Action.ts @@ -1,5 +1,6 @@ import Node from './shared/Node'; import Expression from './shared/Expression'; +import Component from '../Component'; export default class Action extends Node { type: 'Action'; @@ -7,9 +8,11 @@ export default class Action extends Node { expression: Expression; usesContext: boolean; - constructor(component, parent, scope, info) { + constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); + component.warn_if_undefined(info, scope); + this.name = info.name; this.expression = info.expression diff --git a/src/compile/nodes/Animation.ts b/src/compile/nodes/Animation.ts index 31e39da69c..5470b34fe7 100644 --- a/src/compile/nodes/Animation.ts +++ b/src/compile/nodes/Animation.ts @@ -9,6 +9,8 @@ export default class Animation extends Node { constructor(component, parent, scope, info) { super(component, parent, scope, info); + component.warn_if_undefined(info, scope); + this.name = info.name; if (parent.animation) { diff --git a/src/compile/nodes/InlineComponent.ts b/src/compile/nodes/InlineComponent.ts index ae019ad295..5a5d155657 100644 --- a/src/compile/nodes/InlineComponent.ts +++ b/src/compile/nodes/InlineComponent.ts @@ -20,6 +20,8 @@ export default class InlineComponent extends Node { constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); + component.warn_if_undefined(info, scope); + component.hasComponents = true; this.name = info.name; diff --git a/src/compile/nodes/Transition.ts b/src/compile/nodes/Transition.ts index e296d2a24b..b77b57f21b 100644 --- a/src/compile/nodes/Transition.ts +++ b/src/compile/nodes/Transition.ts @@ -1,5 +1,6 @@ import Node from './shared/Node'; import Expression from './shared/Expression'; +import Component from '../Component'; export default class Transition extends Node { type: 'Transition'; @@ -7,9 +8,11 @@ export default class Transition extends Node { directive: string; expression: Expression; - constructor(component, parent, scope, info) { + constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); + component.warn_if_undefined(info, scope); + this.name = info.name; this.directive = info.intro && info.outro ? 'transition' : info.intro ? 'in' : 'out'; diff --git a/src/utils/annotateWithScopes.ts b/src/utils/annotateWithScopes.ts index e89b3b50d6..602e9c3cad 100644 --- a/src/utils/annotateWithScopes.ts +++ b/src/utils/annotateWithScopes.ts @@ -10,7 +10,11 @@ export function createScopes(expression: Node) { walk(expression, { enter(node: Node, parent: Node) { - if (/Function/.test(node.type)) { + if (node.type === 'ImportDeclaration') { + node.specifiers.forEach(specifier => { + scope.declarations.set(specifier.local.name, specifier); + }); + } else if (/Function/.test(node.type)) { if (node.type === 'FunctionDeclaration') { scope.declarations.set(node.id.name, node); scope = new Scope(scope, false); diff --git a/test/validator/samples/a11y-not-on-components/input.html b/test/validator/samples/a11y-not-on-components/input.html index eda4d18db8..e0ee327280 100644 --- a/test/validator/samples/a11y-not-on-components/input.html +++ b/test/validator/samples/a11y-not-on-components/input.html @@ -1,3 +1,7 @@ + + diff --git a/test/validator/samples/a11y-not-on-components/warnings.json b/test/validator/samples/a11y-not-on-components/warnings.json index 58f114f554..7e17fda95a 100644 --- a/test/validator/samples/a11y-not-on-components/warnings.json +++ b/test/validator/samples/a11y-not-on-components/warnings.json @@ -4,14 +4,14 @@ "message": "A11y: Avoid using autofocus", "start": { "column": 8, - "line": 2, - "character": 29 + "line": 6, + "character": 86 }, "end": { - "line": 2, + "line": 6, "column": 17, - "character": 38 + "character": 95 }, - "pos": 29 + "pos": 86 } ] diff --git a/test/validator/samples/action-invalid/errors.json b/test/validator/samples/action-invalid/warnings.json similarity index 64% rename from test/validator/samples/action-invalid/errors.json rename to test/validator/samples/action-invalid/warnings.json index b0ad0eb4e7..d5c38acf48 100644 --- a/test/validator/samples/action-invalid/errors.json +++ b/test/validator/samples/action-invalid/warnings.json @@ -1,6 +1,6 @@ [{ - "code": "missing-action", - "message": "Missing action 'whatever'", + "code": "missing-declaration", + "message": "'whatever' is not defined", "pos": 5, "start": { "line": 1, diff --git a/test/validator/samples/animation-missing/errors.json b/test/validator/samples/animation-missing/warnings.json similarity index 65% rename from test/validator/samples/animation-missing/errors.json rename to test/validator/samples/animation-missing/warnings.json index 1ddaaa1e01..a9df077ade 100644 --- a/test/validator/samples/animation-missing/errors.json +++ b/test/validator/samples/animation-missing/warnings.json @@ -1,6 +1,6 @@ [{ - "code": "missing-animation", - "message": "Missing animation 'foo'", + "code": "missing-declaration", + "message": "'foo' is not defined", "start": { "line": 2, "column": 6, diff --git a/test/validator/samples/missing-component/errors.json b/test/validator/samples/missing-component/warnings.json similarity index 63% rename from test/validator/samples/missing-component/errors.json rename to test/validator/samples/missing-component/warnings.json index 01abf1e7ac..6aed92a6ad 100644 --- a/test/validator/samples/missing-component/errors.json +++ b/test/validator/samples/missing-component/warnings.json @@ -1,6 +1,6 @@ [{ - "code": "missing-component", - "message": "Widget component is not defined", + "code": "missing-declaration", + "message": "'Widget' is not defined", "start": { "line": 2, "column": 1, diff --git a/test/validator/samples/transition-missing/errors.json b/test/validator/samples/transition-missing/warnings.json similarity index 64% rename from test/validator/samples/transition-missing/errors.json rename to test/validator/samples/transition-missing/warnings.json index 1261cc4bec..b50d55d579 100644 --- a/test/validator/samples/transition-missing/errors.json +++ b/test/validator/samples/transition-missing/warnings.json @@ -1,6 +1,6 @@ [{ - "code": "missing-transition", - "message": "Missing transition 'foo'", + "code": "missing-declaration", + "message": "'foo' is not defined", "start": { "line": 1, "column": 5,