Babel: When Open Source Is Not Free Software

12 August 2019 TLDR; Babel is the most relied upon software in the Node.JS ecosystem, yet it leaves out ridiculous bugs that take away essential user freedoms such as ability to write JSDoc, while its owners get paid up to $10k a month for maintenance without fixing these issues for years. TypeScript also comes for free, but also has bugs of the same nature that make developers switch to the OOP ideology that is characterised by self-righteous "Open Source" community who feel free to lash out on everyone who disagrees with them. My simple software offers solutions to restore the freedom of the development process. It's aim is not to become popular and known, but to help ME and others program freely.

Free Software VS Software For Free

When you're studying a particular topic, and express your interest in a certain area of this vast world, not only you actively find more information and opinions, but so it happens that bits of the puzzle actually find you in unexpected ways. One of this bits was a blog post "My Personal Journey From MIT To GPL", that I came across by chance. Despite the randomness of the encounter, this article marked a milestone in my Open Source career: I finally began to grasp the significance of differences between Open Source licensing models. A little bit later, I also discovered other source of knowledge from the same domain, that is, the Free Software Foundation's philosophy regarding the relation of Open Source to user freedoms.

A tower building under construction in London.

bandcamp activation placeholder
BandCamp uses cookies on .bandcamp.com domain. Click To Activate.

In short, the difference between MIT, which I used to license all my packages with just because that's how I'd been doing it from day one by following example of all other packages that did the same, and GPL is that anyone can take a piece of MIT work, use it in proprietary software, and start selling it without any responsibilities to the author other than retaining the copyright notice and license copy, whereas GPL requires them to publish the source code of all derived products that link to GPL-licensed code (linking means using as a dependency). This point didn't really matter to me until I realised, that the permissive MIT license allowed anyone to not just use my work in their projects, but make a product out of what I'd invested hours of coding into, and then compete with me. That's probably not such big risk for when coding is your hobby, but when you accumulate a large code base that form the basis of your business operations, you start to think about certain safeguards since world is cold unless you pay for gas.

That's why I'm now releasing all my software under AGPL too ("A" meaning that it can't be installed on a server for SaaS purposes either, as my Node.JS development company in London specialises in web software). The best point of reference to understanding professional Open Source licensing is the GNU website that outlines the Free Software Foundation philosophy. It confirmed my suspicions that there is a difference between Open Source, and Free software. In one sentence, the word free is used to highlight essential human freedoms and rights, such as:
  • freedom of expression;
  • right for privacy — Article 8 of the European Convention of Human Rights;
  • right for data protection — recital 1 of the GDPR;
  • and others.

Open Source does not really mean that software which anyone can download and which has its source code released to public, is free in humanist terms. Humans are probably the only animals that can understand respect, but there's little respect left in today's Open Source that only uses the fact that the software is provided for free to hide its incompetence. AGPL is an alternative for real professionals who value their own work and pay respect to their users by putting effort into making sure that their software is of a high standard. AGPL is the license of true Open Source held on shoulders of expert software developers, whereas MIT is the license of poser Open Source that only swims in popularity but doesn't know a thing about quality (I'm exercising my freedom of expression).

Every society has its myths. The lack of professionalism within the "community" of developers led to sprouting of a convenient myth that those who make software without being paid, make good, quality and free software. The Open Source Initiative puts it that "the promise of open source is better quality, higher reliability, more flexibility, lower cost, and an end to predatory vendor lock-in". Now I'm going to prove to you that one of the most installed, essential and well-known Node.JS packages, babel achieves exactly opposite objectives while justifying its shortcomings by referring to the "Open Source" label.

section break

Babel R U OK

That's the title of an issue that I opened in Babel's repository after I discovered one of the most devastating bugs in the entire Node.JS ecosystem. I already wasn't happy with having to use a transpiler with 265 transient dependencies (via npm, 250 via yarn) for every package that I created just because I liked the import syntax and believed that it was essential to follow the modern day standard. Although with an SSD and fibre-optics it doesn't take that long to download and link dependencies and rebuild fsevents (still takes about 30s), in summer 2018 I was downshifting from Sky to mobile 3G and became conscious of inbound/outbound data, as well as worked on an older iMac with a standard hard drive. Therefore, every new package I started took a few minutes to initialise due to all linking that had to be done against Babel's bloatware (the linking N... number actually grows exponentially). But what I found about this popular transpiler in August that year, really upset me.

So let's take an example that uses ES6 modules, i.e., the JS standard since mid 2015:
import Stream, { Transform } from 'stream'
import { join } from 'path'

/**
 * Create a new instance of the class
 */
export default class Babel extends Stream {
  /**
   * Transpiles the source code.
   * @param {string} path Path to the source code to transpile.
   */
  constructor(path) {
    super()
    this.path = join('example', path)
    const stream = new Stream()
    stream.pipe(new Transform())
  }
}

/**
 * Returns whether the version is stable.
 * @param {number} version The version to check.
 */
export const stable = (version) => {
  return version <= 7
}

/**
 * Transpile the source code.
 * @param {string} hello The code to transpile.
 */
export const transpile = (hello) => {
  return hello + 'world'
}

/**
 * Software for free that breaks JSDoc.
 * @param {number} downloads How many downloads.
 */
export const shmable = (downloads) => {
  return downloads - 1000000
}

Above is just an example code with a default export followed by 3 named exports which are functions with JSDoc annotations. JSDoc is just as important as the code itself, especially for complex programs that accept configs, so that developers who consume our package (and more importantly, ourselves), can use it easily, receive access to autocompletion hints and feel secure that they work process is correct. JSDoc is crucial for productivity and developer experience, so we usually pay a lot of attention to documenting our code.

The source however needs to be transpiled since imports and exports are not understood. The convention is to use Babel. My setup is minimal required to transpile imports/exports, with @babel's core, cli, and transform plugin (latest versions as of 1 Feb 2020). Just to enable modules, I need to install about 250 dependencies, but that's half the trouble.
{
  "name": "shmable",
  "scripts": {
    "b": "babel src -d build"
  },
  "dependencies": {
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.8.4",
    "@babel/plugin-transform-modules-commonjs": "^7.8.3"
  }
}
And see how it is compiled:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.shmable = exports.transpile = exports.stable = exports.default = void 0;

var _stream = _interopRequireWildcard(require("stream"));

// ˅ ˅ ˅ CLICK TO EXPAND ˅ ˅ ˅
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.shmable = exports.transpile = exports.stable = exports.default = void 0;

var _stream = _interopRequireWildcard(require("stream"));

var _path = require("path");

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

/**
 * Create a new instance of the class
 */
class Babel extends _stream.default {
  /**
   * Transpiles the source code.
   * @param {string} path Path to the source code to transpile.
   */
  constructor(path) {
    super();
    this.path = (0, _path.join)('example', path);
    const stream = new _stream.default();
    stream.pipe(new _stream.Transform());
  }

}
/**
 * Returns whether the version is stable.
 * @param {number} version The version to check.
 */


exports.default = Babel;

const stable = version => {
  return version <= 7;
};
/**
 * Transpile the source code.
 * @param {string} hello The code to transpile.
 */


exports.stable = stable;

const transpile = hello => {
  return hello + 'world';
};
/**
 * Software for free that breaks JSDoc.
 * @param {number} downloads How many downloads.
 */


exports.transpile = transpile;

const shmable = downloads => {
  return downloads - 1000000;
};

exports.shmable = shmable;

The code looks ugly but since every JS developer trusts Babel so much (why not, the fact that it's got 50m monthly downloads means it's reliable, doesn't it?), there's no need to ever look into the "build" folder. Can you spot any problems with that code?. It's OK if not, I couldn't either, and that was partly because I'm using VS Code as my IDE. You can't spot any problems during testing, or writing examples because when importing functions from source code, VSCode will statically analyse source code and not transpiled code for your developer experience (notice proper JSDoc description for functions on the demo below).

no problem within package

The problems start, when we publish the package with transpiled code, and require it from other packages. I call my new bug example package shmable1.0.0, which you can install yourself.

jsdoc is broken after transpilation

When consuming the package and its transpiled source code from another project, there's no JSDoc documentation whatsoever for named exports... Why?

const transpile = hello => {
  return hello + 'world';
};
/**
 * Software for free that breaks JSDoc.
 * @param {number} downloads How many downloads.
 */


exports.transpile = transpile;

const shmable = downloads => {
  return downloads - 1000000;
};

If you look at the code above, you can see how Babel inserted the exports assignment of one function right between the next exported function and its JSDoc! And it repeated it for all named exports! Only default is fine because it came first and because it's a class so that Babel cannot insert an assignment between the constructor and its JSDoc. We can fix the issue manually:

exports.transpile = transpile;

/**
 * Software for free that breaks JSDoc.
 * @param {number} downloads How many downloads.
 */
const shmable = downloads => {
  return downloads - 1000000;
};

And the problem goes away.

jsdoc is fixed manually

Here you go folks. The most known, popular and industry-standard free software recklessly destroys JSDoc of all Node.JS package makers who are diligently documenting their code to leave those hints behind for themselves and colleagues, hoping to get that autocompletion when it'll be needed the most, but it'll never be there. In other words, I'd been carefully and honestly producing my JSDoc for all methods, just for the tool that I trusted, to take the mick out of all my efforts. It's not an issue with the IDE, it's an issue with the transpiler. Babel R U OK?

section break

Incorrect placement of JSDoc comments #8437

That's the title and number of the second issue because the first one was removed since it was "insulting". In BABEL R U UK I didn't use swear words to call authors names to make it insulting, it was primary "all caps", asking "wat is wrong with you". This one person goes "fill issue template so that we can help you", another one says "Rude issues aren't the way to get help" — mateys, it's Babel who needs urgent help OK not me? Yet because they think their software is so popular, everyone's coming to them to beg for help, isn't. This bug freaked me out because for months I'd been trying to understand why do my hints go away after publishing, but it also made me really angry because:

  • I'm a Senior Software Developer with a BSc/Masters degrees in Computer Science yet people who are not even qualified in CS think that I come to them to ask for help;
  • I'm passionate and obsessional about quality, what is there to say of quality of a program that doesn't include any tests for 1/2 of the whole JS which JSDoc is;
  • I finally understood why JSDoc was not showing in the editor properly for my own packages, and while all that time I thought it was somehow me doing something wrong, my only fault was in using that awful piece of free software called Babel;
  • There's nobody who would tell them off for that offense, there's no Open Source quality assurance commission that would shame an essential piece of Node.JS infrastructure for how they treated me and my work. What there is, is Handshake who gave them a $100k cheque SIMPLY because they are so known.
  • Follows from the above, what about taking some responsibility instead of only bragging about how many people use your software, collecting donations from fellow developers who trust you?

This bug is everything what is wrong with this unprofessional industry called poser Open Source. It finally established my attitude towards so-called "community" where no responsibility is ever taken by anyone and my freedom of writing documentation using JSDoc was taken away from me by software that I was extorted into using by Node.JS's inability to JUST make ES6 modules happen for 4 years. NearForm, where were you with your 5-day design sprints all these years? NearForm is another organisation that prides itself on being so Open Source, yet look at the havoc they both have wreaked upon us.

Let's have a look at the the first comment from a maintainer on that new issue:
demurgos avatar
demurgos commented on 8 Aug 2018 •
Your previous issue was closed because its tone was insulting. This one is too: it's not just about avoiding all-caps. Babel is used by thousands of production-grade projects, many of which use doc comments to generate their documentation. With a bit of humility, you may have recognized that maybe you are missing something instead of immediately calling the maintainers incompetent.

Now, to answer you issue: the output you see is expected behavior (Edit: See below, I agree that it's worse than in Babel 6). Output generated by Babel is no longer source code (by definition, it's generated content): it is not intended to act as source code. Comments are emitted on a best effort basis to help readability when debugging the output file. Babel output should be treated similarly to machine code: you do not generate documentation from machine code but from the source.

The input you pass to your documentation generation tool should be the the source code with ES-next syntax, not the lowered Babel output.

So what I called somebody incompetent. Are you being paid $10k per month by your sponsors, yet you can't add a test for essential JSDoc? It's in the job spec of every developer to write tests for her code. If she's not doing it, she's incompetent as it's her direct responsibility. But OFC you can't criticise anyone on the internet, right? If it was a fair enough small overlooked bug I obviously wouldn't get so emotional, but JSDoc is absolutely crucial. Everyone needs JSDoc. I'm only reacting like that because I care about JS. And look how well people who attend JS committee meetings to "participate in discussion of various proposals", take care of us Are they competent? How about they write some tests first prior to that meetings.

"Babel is used by thousands of production-grade projects"

Which makes the situation even more sad...

I also want to remind you that Babel is supported by a small team of volunteers. If you want to improve things, you can help and send PRs. You are not entitled to free support and insults are not tolerated.

Well what you did to my JSDoc is insulting to me, and I have to tolerate it because you hide behind the fact that you're volunteering, but so is everyone else, yet nobody gets paid as much as you do, and not even via paid support for which I can't blame you, but via donations like that Handshake's $100k (yet Handshake's not even using Babel).

Babel Open Collective landing: £300.000 yearly budget.

Babel Maintainer getting paid $15.000 in August

Babel Maintainer getting paid $8.000 in August

Looking at it closer, this seems to be a regression introduced during the Babel 7 beta.
A simple workaround would be to use a stable version of Babel (Babel 6).
It's not good that this regression happened, but it's why Babel 7 is still in beta. Thanks for reporting issues on the beta release, but there's no need to get mad if you use unstable versions. This will probably be fixed until the stable release.
babel issue comment complaining about non-beta version
Unstable version they say... Babel 7 went out of beta since actually August 27, 2018.
babel issue comment saying how a person had to change entire built system to typescript because of babel
Just as I was writing this article, another comment appeared highlighting exactly all of my concerns here.
2 Babel maintainers getting paid almost $100.000 on Open Collective
Why are you not fixing the bug?

I believe that essential requirements need to be met first, before working on proposals. There are clear requirements for a transpiler: to change imports to module.exports, while keeping everything else intact. Making software is profession for some, so stop making a joke out of it already. When you want to brag about being the most popular tool, you're more than happy to do that, but now everyone knows what's really happening quality-wise. You're so detached from reality because of your fame that you even started calling your software a compiler which it is not! Only NodeTools provide a way to properly compile Node.JS packages.

section break

Promise<Open Source>.reject()

Going back to the the promise of open source is better quality, higher reliability, more flexibility, lower cost, and an end to predatory vendor lock-in, so far we can draw the following conclusions:

  • Better quality is not a thing since JSDoc is essential to the development process. You can't not have functions annotated properly (@param, an occasional @example) if you publish your package. A transpiler MUST keep JSDoc in the proper place, if it doesn't that means the transpiler is of low-quality since it doesn't even have tests for JSDoc.
  • Higher reliability is a myth because even though the issue was filed, with screenshots and explanations, it was ignored totally for the amount of time equal to one and a half years, even after version 7 went out of beta.
  • Lower cost is actually higher, because of the time developers have to waste to look up the API documentation online since they cannot get access the runtime IDE JSDoc. Also because of the time wasted again and again to install 250 dependencies (if Babel is a compiler, why can't it compile itself into a single dependency?).
  • and finally, perhaps the most unintuitively, an end to predatory vendor lock-in becomes...

The Start Of Predatory Vendor Lock-In

First of all, if you're using Babel you already know that you don't have freedom. You'd want not to have install a transpiler, when since Node 8 you only use it for modules. As I mentioned earlier, that's entirely Node.JS fault, that should've treated import as require and export as module.export long ago instead of splitting the hair around the specification of how modules should actually work. However, I'm really happy this all happened since every obstacle is an opportunity to create something new which is independent, better, and more unique. But what other negative effect does Babel have on the ecosystem and the development of JavaScript by breaking JSDoc?

The answer is that without properly working JSDoc, people are tempted to use TypeScript — the biggest evil that you will encounter today. I know this because that's how I felt when I struggled with my JSDoc, when it wasn't working. I thought, OMG things are just not working, if I just used typings, that would solve all my problems! And I'm sure that's how other developers feel as well. But once you make this decision, there is no road back, you become hostage to the most overrated technology ever, that was made by Microsoft and

a) makes you addicted to typing every singe thing

b) tells you that apparently that's how you scale up, i.e. fear-mongering that without it your large project will fail,

c) basically does what the heading of this section says, that is locks you into itself, because the more you typescript, the less likely you will go back to pure JavaScript, and,

d) turns you into a close-minded zombie ready to defend TypeScript at any price because it's too much psychological pain to admit that you've been trapped into technology you know you don't want.

TypeScript is too unnecessary for a duck-typed programming language. Because people learn Java at universities, they will want to use TypeScript, forgetting that JavaScript is basically a functional programming language. .Map, .reduce, .filter, these are the pillars of the web development, not types. Types are just there to make life convenient, it's a tool but TypeScript makes them an aim in themselves.

Developers can achieve everything they need with JSDoc, but free software like Babel makes it impossible. Microsoft also break JSDoc capabilities and don't want to fix them, openly stating that people should switch to TypeScript, instead of fixing bugs. Here's the proof of VSCode bug.

The nature of the bug is such that TypeScript (not language, the service running in background in VSCode for JSDoc) will not read source code files required at level 2+ from the main file. E.g., main is index.js, requires lib.js - fine; lib.js requires lib2.js - file not read, types not resolved, JSDoc broken.
/* 1. Create a new package with src folder with the following structure: */
// src/index.js - exports a default from lib, the rest doesn't matter.
export { default as bug } from './lib'
// CLICK TO SHOW FULL INSTRUCTIONS
/* 1. Create a new package with src folder with the following structure: */
// src/index.js - exports a default from lib, the rest doesn't matter.
export { default as bug } from './lib'

/**
 * Directly exported method.
 * @param {string} name The name of the method.
 */
export default function (name) {
  console.log(name)
}
// src/lib.js - exports a default from other file.
export { default } from './lib2'
// src/lib2.js - documented method
/**
 * The bug in TypeScript.
 * @param {Config} [config] Options for the program.
 * @param {boolean} [config.shouldRun=true] A boolean option. Default `true`.
 * @param {string} config.text A text to return.
 */
export default async function typescriptBug(config = {}) {
  const {
    shouldRun = true,
    text,
  } = config
  if (!shouldRun) return
  LOG('@artdeco/typescript-bug called with %s', text)
  return text
}
typescript bug does not appear in source code
In source code, everything works fine. You don't find out about the bug until you publish your transpiled package and consume it in another place. Together with Babel's bug, it brings total devastation to your JSDoc documentation.

A use case? Say you have an index.js file which exports a method from lib.js. The method from lib.js is a function which returns an instance of a certain class implemented in a separate file, User.js. I.e., index.js -> lib.js -> User.js. Once you published package, the return of the method from lib will be unknown. All your documentation will once again be broken. Refer to the GIF below:

typescript bug happens in transpiled code: cannot jump to imported file at level 2
In published code, can't jump to local level 2+.

You can easily verify this with the following source code, after doing yarn npm iadd artdecoweb/typescript-bug (this will install the package from GitHub):

// hold CMD and click on the package name to jump to source
import ok, { bug } from '@artdeco/typescript-bug'

ok()
bug()

OK what's TypeScript's opinion on that (see the issue I opened only a month after Babel's one)?

mjbvz github avatar
mjbvz commented on 6 Sep 2018
TypeScript powers both VS Code's javascript and typescript intellisense. It is what analyzes jsdocs.

I recommend that you try writing d.ts typings for zoroaster. Even if this specific issue is fixed, proper typings will provide a much better experience

I don't really care what powers VS Code, I NEED JSDoc to work. I have the freedom of expression of my documentation. And sure, much better experience. Just stop lying, all the experience that I want can be (and has been) easily achieved with JSDoc. You just want to convert as many people into TypeScript . Still, people would argue that because TypeScript is Open Source, it's good.

Much otherwise, to be honest, a lot of Open Source software take the freedom away by providing low-quality solutions and introducing bugs that are then used against you so that you become a consumer of technology you don't even need. Using TypeScript instead of JSDoc for Node.JS programming is like treating a scratch with surgery when you just need to apply a plaster.

Instead of just being a hater of everything and everyone, I went on and implemented a tool called Typal that could be used to embed JSDoc documentation into source code automatically from a separate XML file. It's part of the NodeTools stack and is needed for other requirements, such as making externs compilation possible as shown on this website. I then published information about it on Medium. The first response was quite positive.

medium comment protecting typescript

I didn't change the article ever since, so you're welcome to find all my amateurish spelling mistakes. I hope Medium does not sue me for not matching the "Supposed level of professionalism that Medium is supposed to represent". O-M-G. And the truth is, if I wrote how much I love TypeScript, added a simple tutorial of how I just switched all of my code to it, and then praised Microsoft for liberating us from JSDoc, I would have got 10000 claps. Although what gives me hope is that there were people who gave me a few claps also so I know developers are keen on being liberated from this tyrannical OOP ideology and its followers, feeling so free and secure to shut anyone who thinks differently up, and that's exactly what my software achieves — gives actual freedom.

section break

Just To Be Objective

After all, I do use VSCode and it's kind of really good. I have to admit that TypeScript succeeded and pushed the JSDoc state-of-art forward in two directions:

  • @type {import('package').Type} statements. That's great and essential as it gets rid of the need to actually import ES6 modules to annotate types, which is sometimes not even possible (think circular dependencies). There's also the same bug as above but regarding importing typedefs at level 2+, therefore all types need to be imported in the the main entry file index.js, and only from there in other files.
  • @type {TypeA & TypeB} unions. This is also crucial and important because otherwise there's no way to annotate "extends" functionality.

These annotations can be written in pure JS, without having to write TypeScript since they are just comments. Things that are not possible without TypeScript which should be possible:

  • Annotating types of even listeners, e.g., on(event: "close", listener: () => void): this;.
  • Overloading of methods.

This praise still doesn't mean that keeping bugs that prevent proper working of JSDoc in the IDE, not implementing essential JSDoc features such as overloading, and then openly telling me to switch to TypeScript justifies the "FOSS"-iness of VSCode. In the age of capitalism, why would a corporation invest time and labour resources into developing a product? Or you think that apart from monetary gain, there is no secondary rewards such as wiring developers brains on subconscious level for a particular frequency expressed in an attitude towards a company?

In case of a programming language, this wiring goes so deep because you literally express yourself using the language, and what's the first thing that comes to mind when you think TypeScript, your daily tool that earns you your daily bread? That's right, Microsoft. For some people that's fine, but not for all of us. Maybe I'm hypocritical when I don't associate VS Code with Microsoft but oh well you caught me here I just don't like TypeScript. Finally, I must admit that I fell into TypeScript trap in my first industry year, and thought "this is how programming should be done" because of a lot of Java coding, but that affair was quickly put to an end by my manager. So thank you GNU for putting it so clearly, that free software protects human freedoms of users, rather than comes for free.

section break

Code À La Mode

Code À La Mode is one of company names that I came up when thinking of names for my software development company. What I wanted to convey from the very beginning, is that modern code is beautiful, eloquent and practical. ES6 modules, async syntax and destructuring are the best features of JS, because they are so straight to the point as to what their purpose is, and just handsome to look at.

So after the final tipping point with Babel I took some time to write my own transpiler, since that idea had long been in my head prior to that. But building ASTs, traversing them and generating code is too much effort. Programming must be practical, and like the language, solve problems at hand rather than build sand castles around standards and stuff. The second approach to transpilation is to actually use regular expressions, because imports are pretty much requires:
import ArtDeco from      '@artdeco/stdlib'
const  ArtDeco = require('@artdeco/stdlib')

import { artdeco } from      '@artdeco/stdlib'
const  { artdeco } = require('@artdeco/stdlib')

import ArtDeco, { artdeco } from '@artdeco/stdlib'
const ArtDeco =         require ('@artdeco/stdlib'); const { artdeco } = ArtDeco

and exports are almost the same as assignments:

export default class ArtDeco {}
module.exports =     ArtDeco {}

export const artdeco = () = {}
     exports.artdeco = () = {}

What's the point of all this intellectual exercise of making up random requirement like what the ES6 modules standard is for Node.JS when all you have to do is to read the source code from a separate file? It probably is important in a browser, but who's ever going to use something like "async namespaces" in Node? Sure, regular expressions are probably not as robust as ASTs, but they also don't require installing 4k dependencies, and can be run in literally milliseconds! I've transpiled 100s of packages with ÀLaMode now, and the only problem I encountered is due to the fact that I implemented it in such a way as to cut out comments, strings and regular expressions first, then paste them back to prevent false-positives, but when you have something like const a = `https://test.com` you might have a problem because the transpiler removes the comment first, and the opening backtick does not get balanced with its counter part, so that if there is another template literal later on in the file, what's between them will not be transpiled. OK that might be frustrating that it's not 100% accurate, but it's 100% practical, and I know of a way to overcome that, I just couldn't be bothered when all I can do is to write 'https:/' + '/test.com', or even better url.format({ protocol: 'http', hostname: 'test.com' }) (since I had to use backticks, that means that the URL string needs to be formatted).

alamode transpilation example: clean code
Example from Github: all one has to do to enable modules, is to rename import into require, and export into the module.exports assignment. All module.exports are moved to the end of the file because otherwise you can't export a default declared after a named export.
Babel output? Here:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.b = exports.c = exports.default = void 0;

var _stream = _interopRequireWildcard(require("stream"));

var _path = require("path");

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

class S extends _stream.Transform {
// SHOW WHOLE FILE
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.b = exports.c = exports.default = void 0;

var _stream = _interopRequireWildcard(require("stream"));

var _path = require("path");

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

class S extends _stream.Transform {
  /**
   * Creates a new instance.
   * @param {string} path
   * @param {Stream} [parent]
   */
  constructor(path, parent) {
    super();
    this.source = (0, _path.join)('example', path);
    if (parent instanceof _stream.default) this.pipe(parent);
  }

}
/**
 * A function that returns `c`.
 * @param {string} input
 */


exports.default = S;

const c = (input = '') => {
  return 'c' + input ? `-${input}` : '';
};
/**
 * A function that returns `b`.
 * @param {number} times
 */


exports.c = c;

const b = (times = 0) => {
  return 'b' + times ? `-${times}` : '';
};

exports.b = b;

Which one looks better? Again, the freedom to just transpile code with minimal changes to enable modules which Node.JS cannot do, is taken away by Babel that thinks it is entitled to append ;, prepend use strict;, insert random interops, rearrange code and break JSDoc. So instead of just letting us use beautiful import syntax for the simplest task of dependency injection, Node.JS and Babel provoke a situation where we must use free software when its authors, who travel the world lobbying for standards, tell us you how we all must think. To clarify the comment below, the author is saying that I can configure the use strict in config, but if I use the modules transform, it is enabled automatically (and the way to disable it is not documented). In other words, even if I KNOW my code will be transpiled, I can't use imports in a non-strict file only because it's not a standard. All this standards-worshipping is so annoying.

babel maintainer commenting not to use es6 modules without strict mode

section break

Hacks Vs Standards

I would suggest people start feeling less constrained by standards' apparition in order to go beyond what's normal and find out something really new. To be specific, the problem with not generating ASTs to build a transpiler, is that the professional software development for a mature project goes the following:

  • go into your tests, and add a test case, i.e., the scenario that you want to either fix, or make possible;
  • then continue to implementation and add a rough sketch of how you think it can work. Do as much coding from head as you can without running the program;
  • run the focused test, see it fail with an error;
  • Set the debugging point at where it fails, and start the debugger;
  • when the debugger pauses, check the contents of variables, properties, etc and see how the state of the program is different from what you expected when sketching it;
  • update the code, save it and your test should automatically rerun;
  • iterate; then run all tests to ensure nothing else broke (regression testing).

You DON'T debug with console.log, manually calling node program.js after each change. console.log is only for really limited cases like when forking a process (even then VS Code apparently can auto-attach to a fork on the same debugging port but I never got that working). I know for some people that's obvious, but many developers don't know how to use debugger which is essential.

But the problem you will run into when using a regex-based transpiler, is that you cannot generate source maps, because a source map is a collection of mappings of entries in source code to entries in the transpiled code, entries being variable names, properties, operators and everything else. Without this information, you can't have a source map, and without source map, when you debug in VS Code, your debugger won't open the source file, it will open the transpiled file in the read-only mode. You can't work like that and be efficient at the development process outlined above.
// copyright bengourley
// https://gist.github.com/bengourley/c3c62e41c9b579ecc1d51e9d9eb8b9d2
"mappings": {
  "0": [
   ^
   └── the line number of the output file

    "231 => source.js 5:64 foo"
      ^        ^       ^    ^
      │        │       │    └── the symbol name from the source file
      │        │       │
      │        │       └── the line:column position in the source file
      │        │
      │        └── the name of the source file
      │
      └── the column number of the output file

  ]
}

Therefore, to generate a source map, you need to have an AST. Or do you? The answer is not necessary. I watched the news the other day, and there was an International Olympiad in Informatics happening recently. Olympic programming is a special kind of programming, you need to be like really smart, know all these algorithms, their Θ-s (running time as function of size of data) and how to solve algorithmic problems, so that's for special people with high IQ. I don't think I have high IQ, but solving problems in web-computing comes natural during a creative process, so practical coding is definitely more about being artistic and making something sleek by applying simple solutions that achieve results. You don't need to be smart, you just need to think outside the box and dismiss standards. The true task is to find a simple solution because there always is one.

So if we need a mapping, but don't have AST how do we go about solving this? The answer is that we need to keep the line and column numbers intact when updating the imports into requires, so that then each line can be split by whitespace, and pretty much mapped onto itself! That's the only way to do it.

If when developing the transpiler, I was restricted by such thoughts as "but regexes won't have 100% coverage" or "I will have to make sure the interoperability follows the specification" and all the rest of the nonsense, I would not be able to achieve my only aim, that is, use import instead of require because it looks so handsome. And having my own ES6 modules transpiler, allowed me to build the JSX transpiler also — obviously that's not possible if you just use somebody else's software. The joy of being an indie developer!

section break

A Sad Spammer Is All I Am

In October, I was casually browsing the web and found out about the recent release of React, where the link was posted to an issue on GitHub that relates to ES5 transpilation. In there I saw:

sindresorhus avatar
sindresorhus commented on 20 Feb 2017
While most of my modules also work in the browser, I make them mainly for Node.js. I love how dependencies in Node.js just work (1) without having to resort to a bundler, compile-step, and a huge config file. ... The web on the other hand is a mess. ... It would also be hard for individual packages to know what Babel (2) config to use.

2 things to take away:

  • Although import is a language feature since 2015, many people, including top Node.JS Open Source developers, use require because it just works. This is also true for other packages such as Koa, Express, etc.
  • Babel IS de-facto what powers the web and you know that, you can't live without Babel.

For me, keeping using require is not a solution when I can see how pretty imports are. It's like still using callbacks/promises when you can use async. Why should we all be limited in expressing ourselves using the richness of our language only because of outdated infrastructure that we have?

So I commented on the post, "Epic I don't want to transpile anything as I also write for Node :P @sindresorhus you can use import and export in your packages without Babel by installing my regex-based transpiler alamode: https://github.com/a-la/alamode! It's super-fast, has a require hook and works without building any AST! You can even debug with no problems." Then I explained the bug in Babel.

my comment marked as spam on github

That really ticked me off. If I was a revered Node.JS star, and somebody commented on one of my issues on GitHub, showing how Babel is destroying JSDoc, I think I would have been like "Oh OK thank you, I'll probably keep using require but thanks for heads up!". It was then that I remembered I was doing all this work for MYSELF, not for anyone. The work I've done is always for my company first, but I also want to help people move in the right direction (TypeJavaScript direction). This is why all code is Open Source, but under Affero.

The terrible suffering of the higher person, and the herd morality that encourages the alleviation of suffering, means they often come to ruin ( §269). They need to forget what they know about life and themselves, their contempt and revulsion; and so they become seduced by flattery and lose their nobility to become someone ‘great’, revered by the herd.
eagle photo

Professional Open Source is a noble enterprise as it's run by creative specialists who know how to do their job well. Art Deco™ is a professional Node.JS company so to meet our own standard of the highest grade, we created NodeTools. Any healthy business needs to generate revenue, and to be competitive, one needs to be productive. Unlike mainstream Open Source that prides itself on popularity while not taking any responsibility for the lack of quality, our software is professional software. We don't do it for your stars. We do it for our business ops and to help those who choose to get into web computing to learn the art of Software Engineering.

section break

Conclusion

To quickly sum up, it's time to debunk myths that

  • Open Source means software that comes for free instead of software that encourages freedom.
  • That all popular packages are good packages
  • and that JavaScript is deficient because it misses types.

Truly free software is the one that cares about you as a developer by honestly attempting to show as little of its presence as possible and letting you do your job rather than dictating how you must work. 250 dependencies, complete negligence to documentation techniques and culture in which enormous bugs don't get fixed for years while maintainers travel around the world "moving JS proposals forward", might indeed make Babel free, but it only means something in the poser Open Source world.

Moreover, it is the fault of the official Node.JS maintainer, NearForm, who for 5 years could not add imports that we were under tyranny of Babel and TypeScript. Standards don't code programs, people code programs, and we shouldn't create artificial constraints on ourselves that hamper the development process. All we want from an import in Node, is to take a function from a separate file. If risk is involved, then proper response mechanisms should be put in place instead of rejecting innovation. To quote Seneca: "It is not that we have a short time to live, but that we waste a lot of it" and we must use technology as our instruments to achieving our real goals, instead of worshipping the sacredness of our tools.

Another "free" software, TypeScript is completely proprietary because you cannot go anywhere without tsserver binary. More and more code that you write in TypeScript, takes you further and further away from pure JavaScript. Microsoft does not prioritise fixing bugs that prevent proper functioning of JSDoc, such as that types from level 2+ files are not imported which results in poor experience for developers, their confusion and subsequent switch to TypeScript typings. Moreover, instead of admitting the presence of such bugs in VS Code IDE, Microsoft calls for developers to switch to TypeScript to avoid issues. This is not freedom — it's nothing less than vendor lock-in and abuse of trust that developers put in.

I hope that on the other hand, my transpiler, ÀLaMode can match the criteria of free software because:
  1. It has 0 dependencies, so that it will not waste developer time for linking of itself and future packages.
  2. ÀLaMode: 8 yarn.lock linesBabel: 1650 yarn.lock Lines
    Installing ÀLaMode in 1 sec Linking Babel's Dependencies in 20 sec
  3. It does not change anything in source code apart from import and export statements, which looks exactly like a human would write it if they were writing it using require.
  4. It skips adding any interops that force their own interoperability standard such as __esModule notation.
  5. It solves the problem of easily importing modules, without making a cult out of transpilation and ES6 modules specification.
  6. It preserves JSDoc, allows to debug programs using a source-map generation hack, and avoids constructing ASTs which is computationally much more expensive than using regular expressions.
  7. Regarding freedom, using RegExes means that anyone can fork my project and make simple changes (e.g., implement unnamed imports such as import 'example') with minimum effort/knowledge, i.e., there's no need to dive deep into compiler theories with its visitor patterns etc.

There are some limitations to ÀLaMode, such as it currently does not transpile import 'example' or dynamic imports, or that it might not be able to handle // inside of template literals well, but those are minor bugs which are documented unlike the Babel bug that completely devastates the NPM infrastructure. To compensate for them, ÀLaMode also allows to write JSX and can replace the locations of imports from import t from 'package' to import t from '../stdlib/package' to creation of 0-dependency packages via stdlibs. As a company, we'll never hide behind the fact that we published our software for free, and will always respect our users' freedoms to build software how they want it.

section break

Comments

Loading comments...