SmooshGate FAQ

What the smoosh happened?!

A proposal for a JavaScript language feature called Array.prototype.flatten turns out to be Web-incompatible. Shipping the feature in Firefox Nightly caused at least one popular website to break. Given that the problematic code is part of the widespread MooTools library, it’s likely that many more websites are affected. (Although MooTools is not commonly used for new websites in 2018, it used to be very popular and is still present on many production websites.)

The proposal author jokingly suggested renaming flatten to smoosh to avoid the compatibility issue. The joke was not clear to everyone, some people started to incorrectly believe that the new name had already been decided, and things escalated quickly.

What does Array.prototype.flatten do?

Array.prototype.flat, originally proposed as Array.prototype.flatten, flattens arrays recursively up to the specified depth, which defaults to 1.

// Flatten one level:
const array = [1, [2, [3]]];
array.flat();
// → [1, 2, [3]]

// Flatten recursively until the array contains no more nested arrays:
array.flat(Infinity);
// → [1, 2, 3]

The same proposal includes Array.prototype.flatMap, which is like Array.prototype.map except it flattens the result into a new array.

[2, 3, 4].flatMap((x) => [x, x * 2]);
// → [2, 4, 3, 6, 4, 8]

What is MooTools doing that causes this problem?

MooTools defines their own non-standard version of Array.prototype.flatten:

Array.prototype.flatten = /* non-standard implementation */;

MooTools’ flatten implementation differs from the proposed standard. However, this is not the problem! When browsers ship Array.prototype.flatten natively, MooTools overrides the native implementation. This ensures that code relying on the MooTools behavior works as intended regardless of whether native flatten is available. So far, so good!

Unfortunately, something else then happens. MooTools copies over all its custom array methods to Elements.prototype (where Elements is a MooTools-specific API):

for (var key in Array.prototype) {
  Elements.prototype[key] = Array.prototype[key];
}

for-in iterates over “enumerable” properties, which doesn’t include native methods like Array.prototype.sort, but it does include regularly-assigned properties like Array.prototype.foo = whatever. But — and here’s the kicker — if you overwrite a non-enumerable property, e.g. Array.prototype.sort = whatever, it remains non-enumerable.

Currently, Array.prototype.flatten = mooToolsFlattenImplementation creates an enumerable flatten property, so it’s later copied to Elements. But if browsers ship a native version of flatten, it becomes non-enumerable, and isn’t copied to Elements. Any code relying on MooTools’ Elements.prototype.flatten is now broken.

Although it seems like changing the native Array.prototype.flatten to be enumerable would fix the problem, it would likely cause even more compatibility issues. Every website relying on for-in to iterate over an array (which is a bad practice, but it happens) would then suddenly get an additional loop iteration for the flatten property.

The bigger underlying problem here is modifying built-in objects. Extending native prototypes is generally accepted as a bad practice nowadays, as it doesn’t compose nicely with other libraries and third-party code. Don’t modify objects you don’t own!

Why don’t we just keep the existing name and break the Web?

In 1996, before CSS became widespread, and long before “HTML5” became a thing, the Space Jam website went live. Today, the website still works the same way it did 22 years ago.

How did that happen? Did someone maintain that website for all these years, updating it every time browser vendors shipped a new feature?

As it turns out, “don’t break the Web” is the number one design principle for HTML, CSS, JavaScript, and any other standard that’s widely used on the Web. If shipping a new browser feature causes existing websites to stop working, that’s bad for everyone:

  • visitors of the affected websites suddenly get a broken user experience;
  • the website owners went from having a perfectly-working website to a non-functional one without them changing anything;
  • browser vendors shipping the new feature lose market share, due to users switching browsers after noticing “it works in browser X”;
  • once the compatibility issue is known, other browser vendors refuse to ship it. The feature specification does not match reality (“nothing but a work of fiction”), which is bad for the standardization process.

Sure, in retrospect MooTools did the wrong thing — but breaking the web doesn’t punish them, it punishes users. These users do not know what a moo tool is. Alternatively, we can find another solution, and users can continue to use the web. The choice is easy to make.

Does that mean bad APIs can never be removed from the Web Platform?

It depends. In rare cases, bad features can be removed from the Web. Even just figuring out whether it’s possible to remove a feature is a very tricky effort, requiring extensive telemetry to quantify how many web pages would have their behavior changed. But when the feature is sufficiently insecure, is harmful to users, or is used very rarely, this can be done.

<applet>, <keygen>, and showModalDialog() are all examples of bad APIs that were successfully removed from the Web Platform.

Why don’t we just fix MooTools?

Patching MooTools so that it no longer extends built-in objects is a good idea. However, it doesn’t solve the problem at hand. Even if MooTools were to release a patched version, all existing websites using it would have to update for the compatibility problem to go away.

Can’t people just update their copy of MooTools?

In a perfect world, MooTools would release a patch, and every single website using MooTools would magically be updated the next day. Problem solved, right?!

Unfortunately, this is unrealistic. Even if someone were to somehow identify the full set of affected websites, manage to find contact information for each and every one of them, successfully reach out to all the website owners, and convince them all to perform the update (which might mean refactoring their entire code base), the entire process would take years, at best.

Keep in mind that many of these websites are old and likely unmaintained. Even if the maintainer is still around, it’s possible they’re not a highly-skilled web developer like yourself. We can’t expect everyone to go and change their 8-year-old website because of a web compatibility issue.

How does the TC39 process work?

TC39 is the committee in charge of evolving the JavaScript language through the ECMAScript standard.

#SmooshGate caused some to believe that “TC39 wants to rename flatten to smoosh”, but it was an in-joke that wasn’t well-communicated externally. Major decisions like renaming a proposal are not taken lightly, are not taken by a single person, and are definitely not taken overnight based on a single GitHub comment.

TC39 operates on a clear staging process for feature proposals. ECMAScript proposals and any major changes to them (including method renaming) are discussed during TC39 meetings, and need to be approved by the entire committee before they become official. In the case of Array.prototype.flatten, the proposal has already gone through several stages of agreement, all the way up to Stage 3, indicating the feature is ready to be implemented in Web browsers. It’s common for additional spec issues to come up during implementation. In this case, the most important feedback came after trying to ship it: the feature, in its current state, breaks the Web. Hard-to-predict issues like these are part of the reason why the TC39 process doesn’t just end once browsers ship a feature.

TC39 operates on consensus, meaning the committee has to agree on any new changes. Even if smoosh had been a serious suggestion, it seems likely that a committee member would object to it in favor of a more common name like compact or chain.

The renaming from flatten to smoosh (even if it hadn’t been a joke) has never been discussed at a TC39 meeting. As such, the official TC39 stance on this topic is currently unknown. No single individual can speak on behalf of all of TC39 until consensus is reached at the next meeting.

TC39 meetings are generally attended by people with highly diverse backgrounds: some have years of programming language design experience, others work on a browser or JavaScript engine, and an increasing number of attendants are there to represent the JavaScript developer community.

How was SmooshGate resolved, eventually?

During the May 2018 TC39 meeting, #SmooshGate was officially resolved by renaming flatten to flat.

Array.prototype.flat and Array.prototype.flatMap shipped in V8 v6.9 and Chrome 69.