Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove compat.js build artifacts #472

Closed
stasm opened this issue Apr 9, 2020 · 3 comments · Fixed by #479
Closed

Remove compat.js build artifacts #472

stasm opened this issue Apr 9, 2020 · 3 comments · Fixed by #479

Comments

@stasm
Copy link
Contributor

stasm commented Apr 9, 2020

Our build process produces the following files for each @fluent package:

  • Individual JS files in esm/ using ES modules transpiled from TS to ES2019,
  • index.js in the package directory, bundled with Rollup from the files in esm/,
  • compat.js in the package directory, bundled with Rollup from the files in esm/ and transpiled using Babel and preset-env.

The configuration for preset-env is defined in babel_config.js. The way I defined it was a bit backwards, however. It wasn't that we really needed to support these browser versions. Instead, we settled on supporting all browsers which supported async functions. In other words, the configuration of preset-env emulates the now-obsolete preset-es2017.

With the migration of @fluent packages to TypeScript, we could also use tsconfig's target field to perform a similar version-based transpilation. We currently transpile to ES2019, but we could it run it again and transpile to ES2017, and get rid of Babel completely.

Furthermore, I've investigated what the differences between ES2017 and ES2019 are, on the assumption that perhaps we could just transpile to one version and completly remove the compat artifacts from our build system. The changes are not significant, I'll post a summary in a comment.

This idea is motivated by three circumstances:

  • In modern web development, compat builds are rarily needed any more. I came up with this solution a long time ago when build systems didn't support transpiling node_modules dependencies. Today, my understanding is that Webpack and other tools take care of that automatically, and if they don't, it's easy to configure them to do so.
  • When importing from @fluent/PACKAGE/compat, you lose the automatic typing discovery in IDEs like VS Code. This is because the typings are in the esm directory and they map to symbols imported from @fluent/PACKAGE. See Missing types for compat in fluent-react #456.
  • In @fluent/react which depends on @fluent/sequence, we need a piece of extra build logic which makes it such that @fluent/react imports from @fluent/sequence, but @fluent/react/compat imports from @fluent/sequence/compat.
@stasm
Copy link
Contributor Author

stasm commented Apr 9, 2020

Here's the summary of the changes between the ES2017, ES2018 and ES2019 targets in all TypeScript-enabled @fluent packages:

@fluent/bundle

All changes relate to spreading objects. One example:

ES2017

this._functions = Object.assign(
    { NUMBER, DATETIME },
    functions
);

ES2018, ES2019

this._functions = {
    NUMBER,
    DATETIME,
    ...functions
};

@fluent/react

Same as above, only object spreads are affected. In fact, only one:

ES2017

return react.createElement(Inner, Object.assign({ getString }, props));

ES2018, ES2019

return react.createElement(Inner, { getString, ...props });

@fluent/sequence

Here we see a bigger difference. Async iteration was only introduced in ES2018, and for ES2017 it's emulated with iterators and async/await. This code requires the __asyncValues from the official tslib package.

ES2017

try {
    for (var bundles_1 = __asyncValues(bundles), bundles_1_1; bundles_1_1 = await bundles_1.next(), !bundles_1_1.done;) {
        const bundle = bundles_1_1.value;
        if (bundle.hasMessage(ids)) {
            return bundle;
        }
    }
} catch (e_1_1) {
    e_1 = {
        error: e_1_1
    };
} finally {
    try {
        if (bundles_1_1 && !bundles_1_1.done && (_a = bundles_1.return)) await _a.call(bundles_1);
    } finally {
        if (e_1) throw e_1.error;
    }
}

ES2018, ES2019

for await (const bundle of bundles) {
    if (bundle.hasMessage(ids)) {
        return bundle;
    }
}

And that's it. Only three packages are affected. I think it would be interestig to consider switch from ES2017 as the target to ES2018 or even TS2019. According to caniuse.com, ES2017 has 93% of market coverage, while ES2018 has 88%. But importantly, most apps using Fluent probably already have their own build and tranpilation setup; the market coverage is really only important for cases when someone includes Fluent through a <script> tag.

Targeting ES2018 or ES2019 would mean that we can natively use for await in our code. Conveniently, async iteartion is supported since Node 10, which is the oldest LTS right now and thus the oldest version we support.

(I'm personally leaning toward ES2018.)

@stasm
Copy link
Contributor Author

stasm commented Apr 9, 2020

I should also add that while the above summary compares the outputs of TypeScript transpilation with different target values, it would be good to also compare the output of today's compat builds transpiled by Babel with TypeScript's ES2017. It's hard to simply diff them, however; more manual work will be needed.

@stasm stasm changed the title Remove compat.js build artifacts Remove compat.js build artifacts Apr 9, 2020
@Pike
Copy link
Contributor

Pike commented Apr 15, 2020

ES2018 sounds good to me, and I agree with the storyline about moving compat processing to older browsers to the pipelines of the consuming projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants