Friday 26 August 2016

The story of M&m

(or, as I like to think of it, "how I wasted over an hour trying to figure out what was breaking the code I was learning, only to discover a convoluted chain of fail which ends (imo) at some code doing something other than what its name suggests because someone had an opinionated idea that it should)


Brace yourself. The rant is strong with this post. There may be a useful nugget in there, but I can't guarantee anything.

One of the greatest cores of our culture at Chillisoft, is a commitment to learning. Learning new technologies, new techniques, new life skills. Part of that commitment is our "Deliberate Practice" time, a time set aside on Fridays from 11h00 to COB in which we put aside production work and focus on learning new things.

The topics vary -- and sometimes the aim is just to experiment with technologies and figure out what may be useful. The current topic is that of TypeScript and Angular 2 and the current focus point with those two topics is for everyone to persevere in producing their own "TODO" app with TypeScript and Angular 2, hopefully from scratch, but using any available online resources.

One of those resources is the Tour of Heroes tutorial on angular.io. That tutorial goes through a lot of the basics of Angular, but suggests starting with the 5 Minute Quickstart tutorial which uses SystemJS for building. Personally, I have a few gripes about the output from that method, mostly that you're expected to reference scripts within node_modules (so ship your entire node_modules folder with your app? Hand-pick the bits you need? Post-process with another tool?) but also because I didn't see a way to process in other resources, like templates -- which I most definitely want to keep out of the code, for the day when I'm fortunate enough to once again have an html-capable designer on my team.

I could well be missing something fundamental with SystemJS -- feel free to drop comments if you can set me straight!

Having been recently exposed to Webpack and both wanting to learn more about that technology whilst avoiding the things I didn't like about what I'd seen of SystemJS, I was excited to see that the kind Angular tutorial fairies had provided a Webpack variant of the 5 Minute Quickstart tutorial.

Naturally, I dove right in.

I'd like to stop here for a moment and talk about tutorials.

So often, I've come across tutorials and other learning material which follows this kind of instruction pattern:
  1. Type something into an IDE or terminal. No explanation, just type it!
  2. Copy this file somewhere
  3. Run this command
  4. Copy these seven other files somewhere else
  5. Run this other command
  6. Congratulations! You are a 1337 developer!
Sorry to burst your bubble, but you're not. You can follow instructions. Bully for you. This is why I stopped at the end of chapter 3 of my MCSD materials, well over a decade ago: I got to a point where I'd followed all of the instructions and had been declared a proficient C++ developer by the materials -- but I had to disagree. I couldn't even repeat what I had done, let alone solve a brand new problem.

Without understanding the simple principles, a lot seems like magick and we end up copying about revered configuration files from project to project, oblivious of the cruft they bring with them.

When you are going through a tutorial (like the ones linked above) and you're instructed to copy some files into your source folder the very least you can do is type them out yourself. At least then you're forced to properly read them and start asking questions. Better, you might start wondering where these files come from and how you're going to create them next time.
In all of these tutorials, I see no mention of the following commands -- but I see their artifacts as "copy-and-paste-into-your-project-folder" instructions:
  • npm init
  • tsc --init
  • typings init
  • karma init
(When tooling doesn't have an init function, tutorials should point to documentation references like the Webpack configuration documentation. And perhaps walk you through some of the basics.)

The result is that juniors learning these techs have no idea how their fundamentals work -- and are unable to stand on their own two feet. I see a similar philosophy coming out of colleges, where graduates can program in C# but have no idea how a program is started by the OS, what JIT is, how to use a REPL -- simple things that really should be the bedrock of understanding.

As an analogy, I'm not saying that a professional car drivers should understand every part of the combustion engine -- but at least knowing about the roles played by pistons, clutch, gears, fuel, braking systems, etc.

But I digress -- back to the story of m&M... or was that M&m...

So between the Tour of Heroes and the 5 Minute Quick Start (and the Webpack variant). I finally get something up and building. I'm feeling all proud of myself until I try to do my first two-way binding, using the Angular2 FormsModule. I read that I can use a template string or a templateUrl to a file of my choosing -- and I consider the latter a better idea because of separation of concerns (and the whole designer theme above). So I point my templateUrl at a file in the same folder as the component, fire up the webpack dev server, bust out a browser and get an error like so:

Can't bind to 'ngmodel' since it isn't a known property of 'input'

Note that my template has the following code only:


<main>
  <h1>TODO</h1>
<input type="text" [(ngModel)]="newTodoText" (keyup.enter)="addTodo()" />
</main>

Nothing particularly exciting, elegant or informative in there and it takes me a while to realise that I've specified ngModel but the error is complaining about ngmodel.

I suspect that Angular is being case-sensitive about these "attributes" and my camelCasing is being clubbed by something. The Angular devs have stirred the pot a little (apparently) with this decision, but I'm not to burned up about it -- it's a template, to be pre-processed: it doesn't have to be conformant html5

Now, since I'm loading a template by url, I'm using the Webpack html loader -- that's what the Angular tutorial suggests, and it makes sense to me. I get the brainwave to use a string instead of the templateUrl -- just stuff the above code into a template attribute and try again... Success! So my suspicion are confirmed:
  1. Angular does care about case
  2. Somehow my ngModel is getting through to the code as ngmodel when the template is loaded from a file and that's causing the issue.
Ok, try another loader: raw. Success! It works! So my second suspicion is confirmed: it's the html-loader.  But this is supposed to be the "correct" way to load html -- the internet says so, and the internet is never wrong!

I suspect that html-loader is lower-casing attributes for some arbitrary reason -- but no documentation at https://www.npmjs.com/package/html-loader gives me any clues -- except that I see that html-loader is minifying by default, using https://www.npmjs.com/package/html-minifier. Ok, so perhaps minification could be considered to be part of loading, especially for packing. Fair enough.

I discover that html-minifier has lots of options with one of them being caseSensitive. Better than that, this option is disabled by default.

And this is where I start losing the plot.

Remember, folks, this package is a minifier. It has one job: make what comes in smaller and push it back out again. Unless the developers are privy to some fantastic new knowledge about bytes, I'm pretty sure that lower-casing text does not save space. Technically, proper html should have lower-cased attributes -- but that's really an opinion which belongs in another plugin (perhaps a tidy plugin?), not something which has a job of minifying. Just my opionion, mind you.

Just as confusing is that html-loader is using this extra plugin, so the only point of configuration for the minifier is html-loader, which can be configured with a query-string like syntax (blech -- someone else has to grok out all the options that I put in there? horrid!), or via a property on the exported configuration from your webpack.conf.js. Except that that mechanism only works for a select few configuration options. And html-loader doesn't have comprehensive documentation, so I guess I'll have to clone their code and go trawling through it -- a practice which is becoming annoyingly common for me.

(Side-note: I know that I'm not the best documenter either -- PeanutButter could do with online documentation -- I tend to answer questions rather, and that's a bad habit )':  The only defense I have is that at least I don't mislead anyone with partial documentation. You knew you would have to check out the code the moment you stepped through the door.)

Anyway, after trudging through code a bit and finding this github issue of interest, I eventually figure out that the magick to fix my problems can appear in my webpack.config.js, like so:


module.exports = {
...
      {
        test: /\.html$/,
        loader: 'html?caseSensitive'
      },
...
}

That little ?caseSensitive and the resultant m/M debacle took me an hour to figure out >_<

And at the end of the day, I'm still of the opinion that having a minifier change the case of code it outputs is a silly idea. Haters gonna hate, but I really don't see the point. At the very least, it produces "WAT?!" moments like the one above. Worse than that, it promotes the idea of writing sloppy code and getting a tool to fix it (for real html attributes, which should jolly-well be lower-case when the developer writes them!)

What's new in PeanutButter?

Retrieving the post... Please hold. If the post doesn't load properly, you can check it out here: https://github.com/fluffynuts/blog/...