MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Friday, September 11, 2026

As I'm wrapping up today, GPT-5 mini via Copilot gave me code that included:

? window.platform.isWindows
: globalThis.isWindows === true;

Me: if it's never truthy when we want to equate to false then just use truthiness. make sense?

Copilot: Yes โ€” that makes sense if you control all assignments. Prefer reading once and coercing to a boolean, then use plain truthiness everywhere.

Edits to this (following what it just said):

const isWindows = !!(
    window.platform?.isWindows 
    ?? globalThis.isWindows
);

Me: no I also hate !!. If you want hard typed booleans go help someone with typescript. you don't code javascript like that.

Copilot: Do you want a full conversion to TypeScript (rename files, add types, adjust build) or just add TypeScript typings/declarations for window.platform (no build changes)?

Touchรฉ.

Labels: , ,


posted by Jalindrine at 9/11/2026 07:25:00 PM
Monday, December 12, 2022

Back in May (!!), I mentioned why I wanted to use Preact in legacy client stacks, and promised I'd follow-up with some instructions on how. These are those stories.

(I'll probably iterate on these posts a good deal over the coming months before publishing elsewhere. That said, though this page might change, the main gist and topic should stay the same.)


Add p/React to any legacy client codebase

I've mentioned I'm working on a project that has a lot of legacy code. "How legacy?" you ask? Well, let's just say this year I used Response.Write while refactoring an .aspx page to make it better (mostly so we could pull complicated string logic out of the template and into an include file on our way to removing aspx entirely), something I hadn't done in 20 years.

Note that even 20 years ago Response.Write wasn't new! Response.Write was on its way out then!

My task, then: Find a way to write modern code for new features that can live side-by-side with a 20+ year-old build process.

That means no npm, no builds, no transpilation. Any overhead could cause the teams to pull up short.

Luckily, as we've discussed before, I found Preact and HTM and I'm off to the races.

Let's see how to use "React with hooks without transpilation," or, in our case, specifically, "Let's party with React hooks using JavaScript like it's 1999!!! Well, kinda.

WARNING: This doesn't work with Internet Explorer b/c of, at the very least, its dependency on template strings.

Read more ยป

Labels: , , ,


posted by ruffin at 12/12/2022 08:23:00 AM
Sunday, December 11, 2022

In the last post, we discussed how to set up a Preact project by importing Preact and HTM libraries in "1999-style JavaScript" to help us understand how to modernize a legacy client codebase and/or introduce modern templating conventions to developers who have never used them before.

This pattern allows us to get a componentized paradigm up and running without any serious overhead. Nobody will ask, "What is npm?" or "Why isn't webpack working?" Using Preact and HTM allows for a perfect simplest case, introducing nothing beyond what's immediately needed, but being able to deploy immediately, if that's the goal.

But we only got so far, with one simplest-case component in a static system.

Let's make things a little more complicated,

  1. Add a second nested "tier" of components to our setup
  2. Show how to use children within our renders
  3. Explore some best practices for including html (only in our leaves!)
  4. & best practices for including CSS

WARNING: I'm posting this in reverse chronological order so they appear correctly in the blog, which lists the most recent first.

Read more ยป

Labels: , , , ,


posted by ruffin at 12/11/2022 08:07:00 AM
Saturday, December 10, 2022

This is the third part of a series on using Preact and HTM without transpilation.

  1. Part 1: Conceptual introduction & simple, single component, static site.
  2. Part 2: Nested components, injecting children components, html & CSS best practices
  3. Part 3: State and events. <<< You are here. ;^)
  4. Not yet published: Routing
  5. ???

State & Events Up

If we're truly using the functional programming paradigm, I think we can argue that there should be no state in our components. There is state somewhere, but it lives outside of our UI "tree".

There are some exceptions to this that we'll cover in a bit, but for the most part, we want to store state upsteam from our Preact UI code.

Look, this is one of the more controversial takes (if you ignore that our goal is to avoid transpilation!) you're going to get from me here. Most folks are going to use some sort of library for state management, but I'd counter by saying that if you need to manage state in a way much more complicated than what you have here, you've got a code smell.

Try to keep interactions small enough that the paradigm offered here works easily, and if you can't, see if you can't simplify. You'll thank me later. And if you can't, welcome to the world of Redux or MobX or whatever you choose.

Let's show what a simple state management setup might look like.

WARNING: I'm posting this in reverse chronological order so they appear correctly in the blog, which lists the most recent first.

Read more ยป

Labels: , , ,


posted by ruffin at 12/10/2022 07:50:00 AM
Friday, May 20, 2022

Step 1: How I chose React

I'm working for a company that has a lot of legacy code, where "legacy" means "nothing on the client newer than AngularJS". Recently, I essentially volunteered to figure out how to stop building more technical debt by proposing a "modern" JavaScript templating engine (and associated tools) to use going forward for new feature work.

But since there's very little pure greenfield work for us in the near future -- and plenty of new features on the backlog -- my solution needed to be able to operate in a lot of different environments without causing ramp-up headaches for each new team of developers. Finding a solution to that is the real challenge I'm going to describe here.

I should note that, even in a working museum of obsolete code, the idea isn't and shouldn't be to toss the legacy code and start over, no matter how tempting that might feel. As Spolsky said years ago...

The sheer volume of bugs [in Netscape 6 on release], it seems, proves that rewriting code from scratch does not make for a better code base, it makes it worse. Old code doesnโ€™t rust, it gets better, as bugs are fixed.

Lou Montulli ['one of the 5 programming superstars who did the original version of Navigator' -Spolsky] again: โ€œI laughed heartily as I got questions from one of my former employees about FTP code the he was rewriting. It had taken 3 years of tuning to get code that could read the 60 different types of FTP servers, those 5000 lines of code may have looked ugly, but at least they worked.โ€ [emphasis mine -mfn]

In brief, you want to keep as much as your legacy code running as is realistically possible, refactoring it as your resources and priorities allow.

I've split my response into two posts.

  1. The sort of research and thinking that went into picking a solution.
  2. Code examples where I'm exhibiting the solution I've selected Is Not Wrong ยฉ 1842.

What follows is part 1...

Read more ยป

Labels: , , , , ,


posted by ruffin at 5/20/2022 03:20:00 PM
Thursday, August 12, 2021

NOTE: Snowpack is obsolete at this point, which makes this post, um, less useful.


If you've been working with JavaScript for a while, you'll know that things have changed from the day when the code end users saw in production was the same code you hacked with your hands. Though I might prefer the days of Vanilla.js, it's gotten nearly impossible to hold a job without knowing how to transpile code.

If you're still on the Vanilla side of the chart, transpiling JavaScript code is when you take a recent version of JavaScript, or, more properly, ECMAscript, and essentially compile it into an older, more compatible version. Transpiling is a "source-to-source" compilation, from one programming language (or version) to another, so to speak.

Maybe you want to use "farts" or "fat arrow functions" where you can exchange this code...

function (x) {
    return x + 1;
}

... with the shorthand...

(x) => x + 1;

Or maybe you want to use shorthand property definitions, which turns this:

var o = {
  a: a,
  b: b,
  c: c
};

... into...

let o = {a, b, c};

Or maybe you just want to use let and const instead of being limited to var.

Or, even better, maybe you want to use TypeScript, which is a great idea, bringing along the bug-squashing safety of strong-typing without sacrificing the abilty to go full dynamic when you need it.

You get the point. JavaScript keeps adding features (as does TypeScript), but older browsers won't magically support all of them. You need to turn your cutting-edge code into something [insert lowest common denominator based on your browser requirements] can run.

That's transpilation. Even if you're not going to transpile something, you need to be aware of how it works so that you can pick the right tools for your projects.

I'll probably make a video at some point, but I would like to quickly get down the steps I think I'd take when moving from a vanilla, es5-compatible JavaScript codebase.


Notes, warnings, and caveats

Why target your transpilation for es5? es5 is the latest version IE supports, as a benchmark. es6 requires more modern browsers. Luckily requirements to support IE seem to be going the way of the dinosaur.

That said, I haven't (yet) run into a situation where a transpiler balks at targeting es5. That is, why not include IE if transpiling to something newer buys me nothing? Everything I can do in es6 I can, thanks to some insane transpiler shimming, do in es5.

Warning: This is not going to be a howto, I'm afraid, but a command recipe for those who already kind of know what they're doing.

It's also not a really nuanced recipe. For instance, when you run npm init, you probably don't want to use the -y flag and should instead pick a specific license. For some reason, using the -y flag means you'll pick "ISC", "a permissive free software license published by the Internet Software Consortium" instead of "UNLICENSED" for your project, and "UNLICENSED" is what the docs say to use for copyrighted code.

So use at your own risk.

And to be overly clear, this is for browser development, not server-side node dev, natch.


Recipe to move es5 codebase to modern JS (with snowpack, webpack, & babel)

Here's the 10,000' view:

  • Use Snowpack to transpile your TypeScript into modern JavaScript.
  • Use webpack to fold your modern JavaScript into a single file.
  • Use babel to transpile your modern JavaScript into es5 compliant code.
  • (Use some node code, called by your build process, to link to that single es5 page from your starting html.)

Here are the specific steps:

  1. Ensure you've got node installed globally
    • The node install will include installing npm.
    • If you know you will need to swap to different versions of node for different projects (as in you know you have some older ones that won't work in the latest version of node), google nvm ("node version manager").
      • You should, however, know if this applies to you.
      • (If this is your first transpilation project, different versions of node don't apply. Yet.)
  2. Navigate to an es5 code base.
  3. In the home directory of the codebase, run npm i -y
    • See caveat about licensing, above.
  4. Now run npm install -D snowpack
    • Snowpack is a development tool only imo.
    • It is not a conventional transpiler so much as a package manager.
    • snowpack's goal is to allow you to develop in a browser that supports whatever version of JavaScript you want to program in (vs. deploy) quickly.
    • It does, however, support compiling TypeScript on the fly. That and the more deliberate management of libraries are the biggest gains snowpack buys for me.
  5. "Port" your existing es5 to TypeScript by changing all JavaScript files' extensions from .js to .ts
    • TypeScript is a superscript of JavaScript, so that's all you have to do.
    • On macOS, you can use this command:
      • find . -iname "*.js" -exec bash -c 'mv "$0" "${0%\.js}.ts"' {} \;
    • I've got a PowerShell script to do this somewhere, but until I find it, try these answers.
  6. Add a snowpack.config.js file to the root directory.
  7. Add "start": "snowpack dev" to your package.json's scripts collection
  8. Install webpack as a "dev dependency" for your project with npm install -D webpack-cli
  9. Add this build command to your package.json's scripts collection:
    • "build": "snowpack build && webpack"
    • Here, snowpack is going to compile your TypeScript into JavaScript.
    • Then webpack is going to take the compiled JavaScript and pack it into a single index.js file.
    • Hang in there. We're getting to the transpilation.
  10. npm install --save-dev babel-loader @babel/core
    • We're using Babel to transpile the code.
    • Up until this point, we needed a modern browser that understood our untranspiled code.
    • Not a big deal as you develop, but potentially a very big deal before releasing.
  11. Add this command to package.json:
    • "launderIndexHtml": "node ./buildscripts/build.js"
  12. Add build.js to your project's home directory using the template, below.
  13. Edit package.json's build command to include this new build command:
    • "build": "snowpack build && webpack && npm run launderIndexHtml",
    • Why didn't we just start with that for our build command? Idk. This recipe is too tutorial-ly, I suppose.

Suggested initial snowpack.config.js

/*global module */
module.exports = {
    mount: {
        public: { url: "/", static: true },
        app: "/",
    },
    buildOptions: {
        out: "./dist",
    },
    devOptions: {
        open: "brave",
    },
};

Why does this open Brave when in devOptions.open? Well, I like to have a browser dedicated to testing and Canary wasn't an option snowpack supported at the time of this writing, strangely enough.


Suggested initial webpack.config.js

/*eslint-disable */
const path = require("path");
module.exports = {
    entry: "./dist/index.js",
    output: {
        filename: "./index.js",
        path: path.resolve(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: ["babel-loader"],
            },
        ],
    },
    resolve: {
        extensions: ["*", ".js"],
    },
};

Custom build.js script

/* eslint-env node */
// We need to do two things after we've compiled our TypeScript with snowpack,
// transpiled into es5 with babel, and bundled & minified with webpack:
// 1. Update index.html to load our minified es5 file as-is, not as a module.
// 2. Get rid of the source we bundled into our webpack output.
var fs = require("fs");
var indexLoc = "./dist/index.html";
fs.readFile(indexLoc, "utf8", function (err, data) {
    if (err) {
        return console.log(err);
    }
    var result = data.replace('script type="module" src="./index.js"', 'script src="./index.js"');
    // var result = data.replace(/script type="module" src="./index.js"/, 'script src="./index.js"');
    fs.writeFile(indexLoc, result, "utf8", function (errWrite) {
        if (errWrite) {
            return console.log(errWrite);
        }
    });
});
function getDirectories(path) {
    return fs.readdirSync(path).filter(function (file) {
        return fs.statSync(path + "/" + file).isDirectory();
    });
}
var foldersToKeep = ["lib", "css", "assets"];
getDirectories("./dist").forEach((x) => {
    if (foldersToKeep.indexOf(x) === -1) {
        fs.rmdirSync("./dist/" + x, { recursive: true });
    }
});

Labels: , , ,


posted by ruffin at 8/12/2021 04:45:00 PM
Tuesday, June 15, 2021

Okay, one thing I hate in JavaScript code is all the code around AJAX requests.

jQuery has a wrapper. And AngularJS has a wrapper. And RxJS has a wrapper. And...

Why don't we just use XMLHttpRequest? Then our services, which are often fairly UI-templating-agnostic already, can live anywhere we want.

There are at least two good reasons... The first is to handle JSONP. I'm ignoring that for now.

The second is to handle async code in a manner that's conventional for each templating engine. $http in AngularJS, for instance, uses $q in place of Promises to ensure changes are communicated correctly to AngularJS. RxJS returns things as Observables (RxJS specific, give or take) rather than Promises (which is a standard), though this is easy to work around.

It's probably worth saying that, well, first that I stole the framework for this code from a SO answer, and second that this is a great example of how to hand-roll a Promise. Just call resolve or reject when you're ready and poof, you've got a Promise.

For the most part, however, you can pass the data event back into the templating system's change detection scheme fairly easily.

So I wanted to write a quick XMLHttpRequest library one could use for the vast majority of CRUD operations. I've only got CR below (give or take. Let's not get into POST vs PUT for the time being), but you get the picture.

I'll try to edit this as I make changes. Comments welcome.

The bottom line, though, is that this is not difficult code. Why we thought we needed wrappers to make AJAX calls I'll never know. The longer you can resist context-specific code, like library-specific wrappers, the longer your original code can live.

(See Exhibit VanillaJS (or the more useful, but strangely mascoted, competing Vanilla JS project).)

Yes, this is, in contrast to my original goals, in TypeScript. It's a pretty easy port. But seriously, you should be using TypeScript.

export default class BaseService {
    getOneUntyped(url: string, id?: string): Promise<any> {
        return new Promise((resolve, reject) => {
            let request = new XMLHttpRequest();

            request.onerror = function () {
                reject(`No response was given.`);
            };

            request.onreadystatechange = function () {
                console.log(request.readyState, request.status);

                // 4 === DONE
                if (request.readyState === 4) {
                    // 200 === OK.
                    if (request.status === 200) {
                        resolve(JSON.parse(request.response));
                    } else {
                        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
                        // Before the request completes, the value of status is 0.
                        // Browsers also report a status of 0 in case of XMLHttpRequest errors.
                        if (request.status !== 0) {
                            reject(`${request.response} ${request.statusText}`);
                        }
                    }
                }
            };

            let urlToUse = url + (id ? id : "");
            request.open("GET", urlToUse, true);
            request.send();
        });
    }

    postOneTyped<T>(url: string, payload: T): Promise<boolean> {
        return new Promise((resolve, reject) => {
            var request = new XMLHttpRequest(); // new HttpRequest instance
            request.open("POST", url);
            request.setRequestHeader("Content-Type", "application/json");
            request.send(JSON.stringify(payload));

            request.onerror = function () {
                reject(`No response was given.`);
            };

            request.onreadystatechange = function () {
                console.log(request.readyState, request.status);

                // 4 === DONE
                if (request.readyState === 4) {
                    // 201 === Created.
                    if (request.status === 201) {
                        resolve(true);
                    } else {
                        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
                        // Before the request completes, the value of status is 0.
                        // Browsers also report a status of 0 in case of XMLHttpRequest errors.
                        if (request.status !== 0) {
                            reject(`${request.response} ${request.statusText}`);
                        }
                    }
                }
            };
        });
    }
}

Labels: , , ,


posted by ruffin at 6/15/2021 10:30:00 AM
Wednesday, May 12, 2021

I noticed today I've been using TypeScript for years and really haven't blogged about it at all. I guess that's not surprising; it essentially does exactly what it says it does: Turns the Wild West of fully dynamic JavaScript development into a strongly-typed, well-controlled arena.

I love doing dynamic stuff with JavaScript (a dynamic language provides for a very particular set of mental challenges), but for the most part, aside from massaging payloads to and from servers (so when we jump concerns from, eg, client to API), it's A Very Bad Idea to go full dynamic. And that means I love TypeScript. It takes some of the silly putty of JavaScript and puts it into the hard Lego blocks of C#, if I can reuse an interview answer I gave once upon a time...


The use case

But as I prepare a large and reasonably mature .NET MVC/AngularJS codebase [sic] for TypeScript, I ran back into the age old issue of how to expose my exports. Is there a way to import an entire directory of files easily? No, not really, no there's not.

The usual shorthand for doing this is to import from barrel files. And that's probably the way to go to start, creating multiple child barrels per folder and exposing them in an uber-barrel at the top. We have scores of AngularJS files to move to TypeScript, and currently have a <script> import per file (no, literally a line of <script> per AngularJS source file) in the main cshtml page, all generated by a giant .NET MVC's bundle config. Ewww. So we cut each folder into its own import and import the parent barrel in the index.html equivalent.

(I'd mistakenly thought the bundles were log files, I think as in logrolling, another term I tend to remember in concept but forget the term, though with the growing importance of elections, I'm doing better remembering now.)


What's a barrel?

Here's a decent description of the advantage of barrels from a self-titled TypeScript Book on github.com:

Without a barrel, a consumer would need three import statements:

import { Foo } from '../demo/foo';
import { Bar } from '../demo/bar';
import { Baz } from '../demo/baz';

You can instead add a barrelย demo/index.tsย containing the following:

// demo/index.ts
export * from './foo'; // re-export all of its exports
export * from './bar'; // re-export all of its exports
export * from './baz'; // re-export all of its exports

Now the consumer can import what it needs from the barrel:

import { Foo, Bar, Baz } from '../demo'; // demo/index.ts is implied

Here's another source that give or take explains my use case:

Since we are using many components in app.routing.module.ts file, we must import their reference for .component.ts and .module.ts files. A general import statements would looks like this:

import { DashboardComponent } from './dashboard/dashboard.component';
import { LoginComponent } from './login/login.component';
import { SignUpComponent } from './sign-up/sign-up.component';
import { AccountOverviewComponent } from './dashboard/account/account-overview/account-overview.component';
import { AccountSecurityComponent } from './dashboard/account/account-security/account-security.component';
import { CartOverviewComponent } from './dashboard/cart/cart-overview/cart-overview.component';
import { CartItemDetailsComponent } from './dashboard/cart/cart-item-details/cart-item-details.component';
import { CartComponent } from './dashboard/cart/cart/cart.component';
import { ProductComponent } from './dashboard/products/product/product.component';
import { ProductsDetailsComponent } from './dashboard/products/products-details/products-details.component';
import { ProductsOverviewComponent } from './dashboard/products/products-overview/products-overview.component';
import { DashboardModule } from './dashboard/dashboard.module';
import { AccountModule } from './dashboard/account/account.module';
import { CartModule } from './dashboard/cart/cart.module';
import { LoginModule } from './login/login.module';
import { SignUpModule } from './sign-up/sign-up.module';
import { ProductsModule } from './dashboard/products/products.module';

...

Typescript module resolution picks up index.ts file from folder name if it is there and try to import packages.

In this file, we can export all folder specific components and modules. For example, consider index.ts file inside products folder:

export * from './product/product.component';
export * from './products-details/products-details.component';
export * from './products-overview/products-overview.component';
export * from './products.module';

...

Here are the complete import statement[s] using index.ts file approach

import { AccountOverviewComponent, AccountSecurityComponent, AccountModule } from './dashboard/account';
import { CartComponent, CartOverviewComponent, CartItemDetailsComponent, CartModule } from './dashboard/cart';
import { ProductComponent, ProductsDetailsComponent, ProductsOverviewComponent, ProductsModule } from './dashboard/products';
import { DashboardComponent, DashboardModule } from './dashboard';
import { LoginComponent, LoginModule } from './login'
import { SignUpModule, SignUpComponent } from './sign-up';

Looks neat and simple, [doesn't] it?


The evil side of barrels: Circular dependencies

As I got ready to put our AngularJS files into barrels for the TypeScript port, I recalled that thar be dragons in the barrel file. But I couldn't remember what it was, precisely.

Turns out it's that importing and exporting indiscriminately can run you into circular dependencies.

Here's a decent explanation:

Barrel File Caveats

Although its is not directly related to barrel files i-e circular dependency. Circular dependency occurs when a Module A somehow imports itself.

How circular dependency happens best explaned by technbuzz.com
B imports A, C imports B
Things gets works when one Module somehow import itself technbuzz.com
At the end of the day Module A imports itself

Make sure you donโ€™t get into this kind of trouble especially when using Barrel files.

And because everyone and their brother says circular dependencies, but don't actually explain where it'd happen, here's one real-world example of this issue:

From nestjs.com:

WARNING

A circular dependency might also be caused when using "barrel files"/index.ts files to group imports. Barrel files should be omitted when it comes to module/provider classes. For example, barrel files should not be used when importing files within the same directory as the barrel file, i.e.ย cats/cats.controllerย should not importย catsย to import theย cats/cats.serviceย file. For more details please also seeย this github issue.

The linked GitHub issue:

alexandr2110proย commentedย on Oct 9, 2018:

Minimal reproduction of the problem with instructions

Not sure. We've got nearly 50 modules and all are working fine.

And most of them actually useย AuthModuleย as you might guess.

Only this one, that I've created recently can't load. Any ideas on what am I doing wrong? :)

The begrudging fix:

alexandr2110proย commentedย on Oct 11, 2018:

So in the end, nothing has changed... I didn't remove/change the code. I've just moved 2 functions from separate files to the test file body. That's it!

And of course, the imports in the test file changed:

/* <...> */
import {
  createAccessToken,
  createAccount,
  createUser,
  // findOneInvitation,  <-- was here when I had an exception
  // findOneAccount,     <-- was here when I had an exception
  E2ETestingContainer,
  E2ETestingModulesFactory,
  findOneRole,
  findOneUser,
  FixtureLoader,
  FixtureLoaderConfig,
  JEST_E2E_TIMEOUT,
  MongooseFixturesService,
} from '../../testing';
/* <...> */

@kamilmysliwiec

I think we can close the issue if you want.
But if somebody can tell me what the hell is going on here, I'd greatly appreciate that!

The explanation:

kamilmysliwiecย commentedย on Oct 16, 2018:ย ย 

I would suggest omitting barrel files when it comes to module's providers/module class OR using them very carefully (for example, even if you create a barrel, you shouldn't use it from within the same module itself [only from outside] and when circular dependency may potentially appear).


Wait, huh?

I'm not going to swear I get it. I think there might be some interaction with dependency injection.

See this blog post on class declarations not hoisting in JavaScript and how order matters. I think a similar issue is illustrated (as in literally illustrated with a flowchart) here, but doesn't mention barrels.

See also this reddit post [sic] that lays out a circular dependency setup for related objects reasonably well.

Another real world use case. And here's some jive on circular references in general, elementary stuff, but perhaps useful.

From github.com:

Desired functionality.

An option to ignore these kind of circular deps
A => barrel => A

I think that's it in a nutshell. Something in the indiscriminately broad barrel is importing/exporting the same thing that started the chain. I'm suspicious it's actually A => barrel => Lots of difficult to follow dependencies => A, but you get the idea. If you import a barrel that could contain something used in the same library, you can easily get a circular dependency. Instead, you should roll the barrel in one directly only, never using it within your own self-contained module, only exposing its contents to use in another app that imports it.

And that makes sense with the original example of having helper functions that blow up. If your object imports helper functions and models that also use the helper functions you could be entering a world of pain. Though I'm still not 100% convinced. More like 50% sure I smell something.

And here's the bottom line from an anonymous poster:


Wait a minute. Didn't the Angular.io style guide used to encourage barrels?

Yes, yes it did. But though it originally encouraged them, the Angular style guide now doesn't mention barrels. This change used to be described on the Angular changelog page, but isn't there any more.

As of today, anyhow, you can find an archive of the warning here, if you're curious:

"Style Guide" withย NgModulesย (2016-09-27)

StyleGuideย explains recommended conventions for Angular modules (NgModule). Barrels now are far less useful and have been removed from the style guide; they remain valuable but are not a matter of Angular style.

Looks like you can find the history of this style guide here now, and here's what the style guide used to say about using barrels:

Create and Import Barrels

Style 04-10

Consider creating a file that imports, aggregates, and re-exports items. We call this technique a barrel.

Consider naming this barrel file index.ts.

Why? A barrel aggregates many imports into a single import.

Why? A barrel reduces the number of imports a file may need.

Why? A barrel provides a consistent pattern to import everything exported in the barrel from a folder.

Why? This is consistent with a pattern from Node, which imports the index.js|ts file from a folder.

Why? A barrel shortens import statements.


And that's the belated history of barrel file use for importing JavaScript/TypeScript files.


Btw, is anyone else going crazy with the new font choices at StackOverflow? Why did you sell, Joel? I hope you're enjoying your hats of money. ;^D

Labels: , ,


posted by ruffin at 5/12/2021 11:03:00 PM
Wednesday, March 17, 2021

How do you Prettier on save in Visual Studio 2019? You use Mads Kristensen's extension, JavaScript Prettier. It lets you set it to run on save, which you absolutely must do. It's an 100% godsend.

"But Mads' extension only runs Prettier 1.12.1 if another Prettier isn't installed locally in my project!" I hear you say. "That doesn't put in the extra space between function and () in anonymous declarations! I can't use it!" you complain.

Well, thanks to your resident hacker (maybe not my best separation of concerns, but it works), that's not a problem any more. Open Tools >>> settings, choose Prettier, type in the version you want to use. Poof. The embedded version is the version you entered and you're Prettier-ing.

Why did I bother updating a Prettier extension for Visual Studio? With my current team, I'm the only dev using, um, modern client-side tooling (mostly VS Code). Most folks are using Visual Studio 2019. That's fine. I love VS2019 for C# work. I can see how familiarity sometimes breeds not contempt but content. -Edness. Or something. But we need to use Prettier as a team. The tool's that good. So update it is!

You're welcome. ;^) 

Btw, I really owe Rackis a whiskey of his choice for this one. Being OCD about whitespace myself, I complained at first (okay, I complained like a cat getting a bath) when he told me years ago that I had to use a tool that automatically "corrected" whitespace for me, reformatting the entire file without apology, before I could check in my code. What the heck?! I know how to format code to make it easiest to read and understand. How dare a tool mess with my art?!!?

Then, very quickly, I dropped the pretense and became addicted. 

In a team using Prettier, I never got a file with craptastic whitespace from another coder. 

And, more important selfishly, I found I never wasted another moment making sure the whitespace for my code was perfect.

Get lazy, type some code, hit save, WHAM, competent whitespace is inserted for you. It's instant. It's mindless. It's magical.

Anything that makes coding easier and standardizes something subjective in a way that's Not Wrong is a very good thing. You should use it. Now. You can thank me later for the recommendation... and the extension, if you're still a Visual Studio 2019 addict.

Labels: , , ,


posted by ruffin at 3/17/2021 10:43:00 PM
Friday, February 26, 2021

Here's my reply, edited a little, to a recent code review where I got taken to the woodshed for using the underscore prefix for private variables in es5 JavaScript code used in an AngularJS project.

Stick around until the end. Spoiler: A, um, widely recognized AngularJS style guide author agrees with me.

The john papa style guide allows them.

"disallowDanglingUnderscores": null,

An underscore prefixed to a variable means the items are intended purely for internal use. They should not be directly exposed.

Here's a good sum from a StackOverflow answer on the underscore convention:

[An underscore prefix] means private fields or private methods. Methods that are only for internal use.

They should not be invoked outside of the class.

Private fields contain data for internal use.

They should not be read or written into (directly) from outside of the class.

Note: It is very important to note that just adding an underscore to a variable does not make it private, it is only a naming convention.

That's an important distinction for me. If I have side-effects for specific calls that I don't want to expose to a consumer, I want to ensure those things are "hidden" (not exposed outside of the file's scope). This helps me both declare and double-check that.

I don't have to do it, but it's not random and it's not a made up practice.

Your TypeScript reference is Google's (as in not Microsoft's). Nothing inherently wrong with that, but, um, why?

Though I disagree with some of the stuff already (there's nothing wrong with interfaces starting with I. Fight me ;^D), I can see why you wouldn't use _ in TypeScript -- TypeScript has a concept of and enforces that concept of private fields.

https://www.typescriptlang.org/docs/handbook/classes.html#ecmascript-private-fields

For example, if I want _myProp to be private, I just say so: private: myProp. That's a little different than my usage here, but you can twist it into the same thing.

Make sense? My point here isn't that I can't remove the _ if it's driving the team crazy. The point is that there is a clear reason to use it, and it is a popular, useful convention.


Fun update: John Papa from 2016 says exactly the same thing as me:

johnpapa commented on Mar 28, 2016 โ€ข

I don't like underscores personally ... but since there is no real private nor public in javascript, it is very helpful to have a way to differentiate them. So I use them

UPDATE: i was referring to ES5 ... with Typescript I don;t use underscore

I'm biased, but well said.

Though, again, this is why I hate working in dated codebases. The code doesn't rust, but it kinda feels like your career is stuck in an episode of Doctor Who.

Labels: , ,


posted by ruffin at 2/26/2021 10:03:00 AM
Monday, January 25, 2021

It's kinda hard to see in the giant MDN Assertions Browser Compatibility Table, so here's why, if you still support IE or Safari at all, you don't get to use lookbehinds yet.

I'm not working with a transpiler right now [sic], but wonder if they're smart enough to parse regexes as part of the transpilation process. My guess would be no, but they should at least throw an error if you've got something your target doesn't support if they don't. Not going to check now, but I should. (note to self)


desktopDesktop

Chrome Edge Firefox Internet Explorer Opera Safari
lookbehind assertions ((?<= ) and (?<! ))

Labels: , , ,


posted by ruffin at 1/25/2021 10:08:00 AM
Thursday, January 09, 2020

I don't recall when I signed up for it, but I routinely get a newsletter from Baldur Bjarnason about software development with a focus on web development.

His new year's email includes some points on JavaScript frameworks that I'm suspicious I like because they so closely match mine.

But what they so clearly indicate is that web development has become a field whose outward appearance and, all too often, local practice, has been completely co-opted by the needs of the largest of enterprise corporations.

(Words in bullets are his. All emphasis is mine.)

  • Once you step outside of the social media bubble, however, vanilla JS and more โ€˜modestโ€™ approaches like Svelte or Stimulus/TurboLinks seem to have reached critical mass in terms of sustainability.Irrespective of those newer trends, jQuery and PHP-driven, un-hydrated, old-fashioned server-side rendering still utterly dominate the web that people use.
  • Web dev driven by npm packages, frameworks, and bundling is to the field of web design what Java and C# in 2010s was to web servers. If you work in enterprise software [npm, framework, and bundling-through-transpilation driven patterns are] all you can see. Web developers working on CMS themes (or on Rails-based projects) using jQuery and plain old JSโ€”maybe with a couple of libraries imported directly via a script tagโ€”are the unseen dark matter of the web dev community.
  • What should worry you is that npm- and framework-driven web development feels just as painful as enterprise software dev because it is enterprise development.

He continues by comparing TypeScript -- and I think he means more specifically "enterprise TypeScript development", because there's lots to enjoy about TypeScript the language, nothing inherently evil about it -- to enterprise Java of 20 years ago.

  • TypeScript smells like Java.
  • The complexity of npm packages harkens back to painful Java packaging monstrosities.
  • JavaScript build systems are about as much fun as Java build systems, even though they are doing very different things.
  • Deployment, as implemented in the Kubernetes and Docker ecosystems, is exactly as hard to understand and use as its Java predecessors because those are their predecessors.

Then he has a few points that serve as a sort of manifesto for the non-enterprise developer.

  • Some of use work exclusively with SMBs (Small-/Medium-sized Businesses) and shouldnโ€™t need to run the enterprise anti-productivity gauntlet. Our needs in terms of frameworks, bundling, and packages are very different from those working in enterprises with hundreds or thousands of employees.
  • It is not rational to expect [developers for small and medium-sized businesses] to be using enterprise-oriented tools and environments or to demand that we be happy about being saddled with your need for complexity.

I'd also ask people to take a close look at how they define what's an SMB project and what's an enterprise project. That is, defining projects by the size of the corporation that produces them isn't always -- heck, isn't usually -- the best metric.

Where something becomes "enterprise" is when you have hundreds of people working on the same project, in the same codebase. What npm/library-based bundling buys you is the ability to firewall smaller projects from each other. If someone writes a horribly inefficient page that no rendering library could solve without magic...


... your development process already includes a baked-in firewall to ensure that code doesn't adversely impact the rest of your system. You can stitch together completely independent projects easily, iff you really need to do that stitching.

But how big is your day-to-day work, really? How many of the problems "solved" by mass package import could have been solved reasonably well for your uses with a (think what's often derisively called a "NIH syndrome-induced") roll-my-own, homebrew solution?

Put another way, how many developers are really part of your specific product? 

If it's not hundreds, ask yourself how much time you'd save on developer ramp-up, maintenance, and new development if you too used "jQuery and plain old JSโ€”maybe with a couple of libraries imported directly via a script tag". Even in a team of "just" 20-30, the resource savings from going just 25% faster (a conservative cost of doing framework-style development in my experience) are unbelievable.

I'm not sure I know when going "full framework" is the best idea. At a conference, some friends of mine (coworkers) managed to corner a guy who was then working at Google on AngularJS (before Angular 2+). We talked to him a bit about AngularJS' pain points and how they solved problems of large DOMs, as our Knockout.js-based system was getting crushed in our more dynamic, feature-rich UIs.

His basic comment was that you can't fix these issues easily, and there was no silver bullet. Inefficient or complex UIs are trouble no matter where you build them.

The problem here was browser performance, which by definition isn't an SMB or enterprise problem. It's a client-side rendering one. Frameworks don't fix these issues. "Full framework" dev doesn't provide solutions to day-to-day problems, it provides the passive coordination that allows the amalgamation of code from hundreds of developers working at once.

Is that really the state your team finds itself in? Why would you want it to be?

And then here's a final comment from Bjarnason about how the thinkspace of development has been dominated by the enterprises -- he does a good job in the balance of his new year's points discussing how we see so much enterprise-specific information because those enterprises have a vested interest in making that come to be.

  • The divide between what you read in developer social media and what you see on web dev websites, blogs, and actual practice has never in my recollection been this wide. Iโ€™ve never before seen web dev social media and forum discourse so dominated by the US west coast enterprise tech company bubble, and Iโ€™ve been doing this for a couple of decades now. The pre-2000 dot-com bubble comes close although that one came attached to an actual financial bubble and happened before social media had evolved into its current form.

Anyhow, it's a good, thought provoking post, and worth a full read.

Labels: , , , , ,


posted by ruffin at 1/09/2020 10:06:00 AM
Wednesday, November 06, 2019

Note to self (from SO):

document.addEventListener("DOMContentLoaded", function() {
  // code...
});

Labels: , ,


posted by ruffin at 11/06/2019 09:37:00 AM
Saturday, July 07, 2018

I was watching an excellent video describing the iterations of the Angular compiler, and rabbit holed a little with hidden classes.

The most enjoyable resource I found on this (and javascript optimization in general) was from mrale.ph:

[Falling back to the runtime to bullheadedly access properties from objects out of any context from our code] is an absolutely valid way to implement property lookup, however it has one significant problem: if we pit our property lookup implementation against those used in modern JS VMs we will discover that it is far too slow.

Our interpreter isย amnesiac: every time it does a property lookup it has to execute a generic property lookup algorithm, it does not learn anything from the previous attempts and has to pay full price again and again. Thatโ€™s why performance oriented VMs implement property lookup in a different way.

What if each property access in our program was capable of learning from objects that it saw before and apply this knowledge to similar objects? Potentially that would allow us to save a lot of time by avoiding costly generic lookup algorithm and instead use a quicker one that only applies to objects of certain shape.

โ€ฆ

This optimization technique is known as Inline Caching and I have written about it before.

[emph and bracketed paraphrase mine]

It's worth a full read. And once you've got how hidden classes, polymorphism, and megamorphism works, you could probably fall into exactly the same compiler optimization steps Angular's Tobias Bosch does in his video, above.


Here's a quick bit on poly/mega/morphism from the same source, as I once again save you from googling, one resource at a time.

If we continue callingย fย with objects of different shapes its degree of polymorphism will continue to grow until it reaches a predefined threshold - maximum possible capacity for the inline cache (e.g.ย 4ย for property loads in V8) - at that point [the] cache will transition to aย megamorphicย state.
...
In V8 megamorphic ICs can still continue to cache things but instead of doing it locally they will put what they want to cache into a global hashtable. This hashtable has a fixed size and entries are simply overwritten on collisions.

It's duck typing, all the way down, until you have too many ducks, at which point we default to a home-rolled bird almanac.

Labels: , , ,


posted by ruffin at 7/07/2018 09:58:00 AM
Tuesday, June 12, 2018

MDN has an excellent sample to show the differences between for... in, for... in with hasOwnProperty, and for... of.

Good thing we had more prepositions, I guess.

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // logs 0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7
}

Labels:


posted by ruffin at 6/12/2018 08:54:00 PM
Friday, June 08, 2018

I'm apparently late to the JavaScript Fatigue is a Thing game.

Too Manyย Tools.

At work this past quarter, weย painstakinglyย started three new projects at work. I say โ€œpainstakinglyโ€ becauseย everyย project required decisions to be made around tooling depending on the scope & needs.

Ultimately, the problem is thatย by choosing React (and inherently JSX), youโ€™ve unwittingly opted into a confusing nest of build tools, boilerplate, linters, & time-sinks to deal with before you ever get toย createย anything.

Labels: ,


posted by ruffin at 6/08/2018 09:49:00 AM
Wednesday, June 06, 2018

I've been trying to sell a client on minimizing libraries in React for a while, and have been suggesting different vanilla state management architectures in place of Redux or MobX.

Interesting, then, to come across this article today:

You Donโ€™t Need Redux, MobX, RxJS, Cerebral:

I know what youโ€™re thinking:ย Redux alternatives are a dime a dozen.ย But this isnโ€™t yet another library. In fact,ย itโ€™s not a library at all.

Itโ€™s just a simple pattern:ย Meiosis.

Why aย Pattern?

Using a pattern instead of a library means that you have more freedom. You are not dependent on a libraryโ€™s features, bugfixes, and release dates. You are not worried about backward compatibility, deprecation, upgrade migration paths, or project abandonment. You are never waiting for a missing feature.

Yes, please. I need to read up on what they're proposing. It sounds like it might skip some of the advantages of having a singleton state object, like Redux and some MobX implementation do, but this is good momentum.

Labels: , ,


posted by ruffin at 6/06/2018 10:03:00 AM
Monday, May 28, 2018

I'm not sure why, but Promises in JavaScript took a while for me to grok. I mean, the premise is simple: You have a function, and once it's done, whenever that is, the appropriate chained methods will be executed, like .then if it's successful and .catch if it isn't, with the appropriate parameters given. That's easy, and you can start using Promise workflows quickly.

But how do you make a Promise? Strangely, many tutorials approach this as if all Promises were sui generis (sui generis apparently means "of its own kind" -- as in something that can't be reduced to something simpler. Or, as the Wikipedia might put it, "[E]xamples are sui generis [when] they simply exist in society and are widely accepted without thoughts of where they come from or how they were created").

Let's correct that approach. MDN's most basic example of creating a Promise is pretty straightforward:

const myFirstPromise = new Promise((resolve, reject) => {
  // do something asynchronous which eventually calls either:
  //
  //   resolve(someValue); // fulfilled
  // or
  //   reject("failure reason"); // rejected
});

Do something, then resolve or reject it. That's simple. We're used to doing just that in callback-land excepting the Promise wrapper.

Take this example code from a pretty good, very basic tutorial on Promises from Wesley Handy here.

function getData() {
    return new Promise((resolve, reject)=>{
        $.ajax({
            url: `http://www.omdbapi.com/?t=The+Matrix`,
            method: 'GET'
        }).done((response)=>{
                //this means my api call suceeded, so I will call resolve on the response
                resolve(response);
        }).fail((error)=>{
                //this means the api call failed, so I will call reject on the error
                reject(error);
        });
    });
}

The behind the curtain magic happens in the formulation of resolve and reject. You set those up with this:

getData()
    .then(data => console.log(data))
    .catch(error => console.log(error));

If you're not careful, your Promise tutorial might accept that that's magic, and not bother asking Mitch Pileggi how the trick was done. What you're doing is wrapping all of your .thens and catches into two [possibly composite] functions. This is that "syntactic sugar" everyone loves to talk about.

Unraveling a Promise

The quick getData call, above, looks like this outside of the Promise-sugar.

function doIt(resolve, reject) {
    $.ajax({
        url: `http://www.omdbapi.com/?t=The+Matrix`,
        method: 'GET'
    }).done((response)=>{
        //this means my api call succeeded, so I will call resolve on the response
        resolve(response);
    }).fail((error)=>{
        //this means the api call failed, so I will call reject on the error
        reject(error);
    });
}


var ajaxSuccess = function (data) {
    console.log(data);
}

var ajaxFail = function (error) {
    console.log(error);
}

doIt(ajaxSuccess, ajaxFail);

That's it. That's what that call looks like outside of a Promise. There's no real Promise-centric benefit for this simple case, imo.


Unraveling a Promise with a finally

More interesting would be if that example also had a finally, like this:

getData()
    .then(data => console.log(data))
    .catch(error => console.log(error))
    .finally(() => console.log("Finally gets no arguments"));

That looks like this, with the same doIt function as earlier (ie, doIt doesn't change):

function doIt(resolve, reject) {
    $.ajax({
        // Same as above...
}

// Here's the new function we want to use after `doIt` completes, no matter what.
var ajaxFinally = function () {
    console.log("Finally gets no arguments");
}

// Now back to our success/fail functions with *one* change for each...
var ajaxSuccess = function (data) {
    console.log(data);
    ajaxFinally();    // and now we add it to BOTH the success and fail functions.
}

var ajaxFail = function (error) {
    console.log(error);
    ajaxFinally();    // <<< OMGWTFBBQ!!1! ajaxFinally is here, too.
}

doIt(ajaxSuccess, ajaxFail);

Here, we do get a little bit of a readability improvement, and certainly some DRYness.


Promise wrapping

That is, the Promise wrapper...

  1. Packages the Promise in a ripcord-ready state, but doesn't actually deploy the action, and...
  2. Magically combines all the chained functions wrapped in then, catch, and finally into resolve and reject (more precisely, "into the two parameters any Promise expects"), above.

Again, the Promise constructor just does some sugar-magic to wrap up all the chained functions that follow it once its async action completes, and route logic into one (resolve) or the other (reject) once the wrapped action is complete. Fwiw, all the chainable functions are described here, at MDN.

I think part of my block on Promises was that callbacks are so danged easy to understand. And if you have a true pyramid of callback doom, I've taken that as a code smell rather than an insurmountable issue inherent to javascript. What is this Promise doing that makes it simpler to follow than callbacks? In a sense, nothing. It's hiding how callbacks are chained together. Some folks find functions-as-objects difficult to grok, and I think the structure Promises gives helps folks that don't like function-freewheeling.

But, as thecodebarbarian.com explorer here, pyramids usually are signs of bad architecture, not a place where you're limited by code.

This case [of "callback hell" used as an example earlier] is a classic example where the single function is responsible for doing way too much, otherwise known as theย God object anti-pattern. As written, this function does a lot of tangentially related tasks:

  • registers a job in a work queue
  • cleans up the job queue
  • reads and writes from S3 with hard-coded options
  • executes two shell commands
  • etc. etc.

The function does way too much and has way too many points of failure. Furthermore, it skips error checks for many of these. Promises and async have mechanisms to help you check for errors in a more concise way, but odds are, if you're the type of person who ignores errors in callbacks, you'll also ignore them even if you're usingย promise.catch()ย orย async.waterfall(). Callback hell is the least of this function's problems. [emph mine, natch -mfn]


TL;DR

I'm belaboring the point, but bottom line is very simple:

Promises simply use chaining functions to organize your callbacks into two composite functions. One composite function gets called on success, one on failure.

here endeth the lesson

Labels: , ,


posted by ruffin at 5/28/2018 09:37:00 PM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.