Skip to content

Proposal: Lint Vue templates and check bindings against Haxe codebase

Glenn Ko edited this page Apr 28, 2017 · 67 revisions

So how to validate that all stringed bindings on your Vue string-based template is strictly-typed linked to the relevant fields in your HaxeVx component class? (whether it may be Vue component properties or CSS module class bindings?).

A compile-time checking macro to compile Vue's HTML string templates' and analyse attributes for v-for, v-on, :attr, {{expr}} and various other scopes, and generate scoped/nested dummy anonymous functions of Expr from them for typechecking purposes only during dev builds within the HaxeVX's component's Template() call. This is an alternative approach for writing template markup that involves full compile-time type-checking (a great to run basic checks for typos and prop type mismatches, etc.), instead of relying on JSX (which is now on the HaxeVx roadmap only and not implemented yet).

This would allow both developers/designers to design directly from plain external .html files with the Vue templating language itself, but still get strict-typed compiling support with it.

If a binding is spelled wrongly, the compiler outputs out an error immediately of it, with the Position of each generated dummy test Expr being tied to respective range of characters in the .html file instead. With it, you can jump straight to the problematic error (depending on your IDE), with the exact location of the error marked on the html file itself.

https://vuejs.org/v2/guide/syntax.html


SPECS:

What to parse and generate:

  • v-for="variableName in Expr" v-for="(variableNames max 3 declarations) in Expr". and generate respective inner scopes for anything else within that node's descendants with those variable names.
  • v-on:XXX="Expr" and @XXX="Expr". Include var _event:js.html.Event variable for VueJS's $event.
  • v-html="String Expr". Return String.
  • v-if="Expr" v-show="Expr". Return Dynamic (to be type-coerced to Boolean in typical JS fashion) or Return Bool in extra-strict mode.
  • <input v-model="Expr"> <input :value="Expr">. Return String for general case. But take into account specific cases in Vue where particular data types are expected (eg. For input fields, v-model.number must return Float) https://vuejs.org/v2/guide/forms.html
  • :attr="Expr". For general case, return Dynamic. But, if attr matches a Component Prop(P) field, return type signature (if any) of Prop field instead.
  • v-XXX="Expr". Return Dynamic.
  • {{Expr}} Return Dynamic. ~~~Return String with return Std.string(Expr). (not much point i think)~~~
  • ${String intepolated Expr}. Return String literal containing this string interpolated expression.

Also, how to check if the string template is well-formed HTML to begin with? Replace string interpolated tags/content ${} with empty/dummy/div strings prior to validation against some form of generic html template syntax checker. Prefably, it should produce a traversible structure as well so you can similar iterate through the nodes and scan for the relevant attributes or text content bindings {{}} within the dom tree, and form the relvantly scoped anonymous function(){} expressions based on those strings.

Possible could use: https://lib.haxe.org/p/HtmlParser/

Important notes regarding Expr parsing:

  • All direct Expr in string template with CIdent found, must be wrapped with a "this." reference, if no "this." reference is already found. This is to maintained consistency with resolving VueJS stringed identity values with the HaxeVx component instance itself, minimizing false positives. However, the caveat with this approach is that you can no longer use common JS library methods like Math. within string templates itself (unlike vanilla VueJS). For HaxeVx, you are forced to encapsulate such operations containing such JS library references (eg. Math) and such....as actual component-specific methods.

Filtering support:

  • filter: support. Needs to be done on HaxeVx roadmap first. (for now, just truncate it off for Expr)
  • For every "Expr" within attribute literals, check for whole-word filter | bar, and type-check accordingly to see if such filters are available in actual Component class.

What to replace for cross compatability:

  • Vue API-related $stuff to their _vxStuff equilevents if possible, otherwise replace with $stuff with $$stuff for output and type check this with var _stuff replacement. Also check validity of stuff within attribute context by checking against Vue API if such a binding exists. If such a binding doesn't exist, may assume it to be just a plain custom plugin-based variable on your part, but this is based on your compiler settings if "leniency" or more "dynamism" is required.

What to syntax color (Your mileage may vary..IDE specific for "nicer" look)

  • ${}. The look of it within HTML tags as well.

Other file extensions:

Extended type-checking syntax for template:

  • Explicit Prop Prefix type checking. Not part of Vue by default, but attributes may be prefixed with a certain special symbol to explicitly indicate a Props attribute and not a plain HTML attribute, and this will run further typecheckings against Component class to see if such a prop field exist. The final output string template will have the prefixes removed.

Integration with Vue template compiler?

  • It's nowadays a fairly common practice in Vue to precompile string templates into actual Render() functions (if not using virtual dom/JSX) for single-page apps. For static string-only templates, may integrate HaxeVx's proposed type-checking process of string templates with Vue template compiler. https://www.npmjs.com/package/vue-template-compiler So, after HaxeVx type-checks that the given string template code compiles fine, the macro can optionally run some gulp/npm related process to execute the template compiler with the given string template markup, and create a new JS file with the actual JS render() function code. Then, simply assign by macro this.render= __js__(render function code from JS file) accordingly for the component's _Init() method in Haxe, and you've got a pre-rendered string template in Haxe with type-checking done by both Haxe and string-template compiling by VueJS template compiler.
  • Caveat: You cannot use the typical Haxe-based dynamic string concatenation ${STYLE.cssModuleClass} if compiling with Vue template compiler. So, for CSS modules, you are forced to used Vue's prescribed method of defining CSS module-based classes as :class binding attributes with local computed style property. For example: <p :class="[STYLE.red, STYLE.bold]">.

and a STYLE computed: inline getter must be generated as well by HaxeVx to support this,

  • For strictly-typed component name references, string literals of those component names must be used instead of string-concatenated ${} CIdent references, or the Haxe macro would need to find a consistent way to find and replace those tokens with the actual matching component string literal names, for it to be Vue template-compiler compatible. For HaxeVx's interpretation of the .vue file format spec (https://github.com/Glidias/haxevx/wiki/Proposal:-Integrated-html-vue-type-file), this can be easily inferred, but not if your html templates contain other extra ${} bindings outside of this spec, tied to .hx file which can only be resolved at inline-compiling-time/runtime. (Infering the inline value of a CIdent expression by compile macro isn't trivial to do in Haxe.)