nteract / semiotic

A data visualization framework combining React & D3
https://semioticv1.nteract.io/
Other
2.43k stars 132 forks source link

Modernize build infrastructure #561

Closed alexeyraspopov closed 2 years ago

alexeyraspopov commented 2 years ago

Rationale

The way Semiotic is bundled and shipped right now it includes all its dependencies as a part of the bundle which increases the size of the package and affects consumers bundles. It becomes harder to update dependencies versions and potentially introduce duplicated code. UMD format does not allow tree-shaking which also forces users to import from direct files. This PR aims at modernizing the build configs to ensure smaller bundle, better dependencies management, better compatibility with JS tooling and CDNs.

Breaking changes

Some changes directly affected how the package is distributed and consumed by developers. Here's a brief description.

Improved files structure of Semiotic NPM package

dist/
  semiotic.js
  semiotic.d.ts
  semiotic.module.js
  semiotic.module.d.ts
LICENSE
README.md
package.json

The dist folder only includes bundled (via rollup) commonjs version semiotic.js and ES modules version semiotic.module.js. The package.json has been updated to work with both files (see fields main and module). Rollup now bundles semiotic.d.ts file with all necessary TypeScript declarations for users. semiotic.module.d.ts is created automatically for the sake of proper TS compatibility, but is not really different from the original file.

No more direct file imports or default Semiotic import

Given the new files structure, there is no longer a way to import some components directly, e.g. import XYFrame from 'semiotic/lib/XYFrame'. The only way to import things is by using named imports like import { XYFrame } from 'semiotic'. This improves developer experience, ensures proper TS types evaluation and helps build systems to eliminate unused code. sideEffects: false has been added to package.json to ensure it.

Since the docs suggest using syntax like import XYFrame from 'semiotic/lib/XYFrame', this change becomes a breaking change. The docs need to be updated and changelog needs to mention what can possibly break after upgrading the library.

Dependencies are not bundled

Before, several dependencies (e.g. D3 related libs, labella, etc) have been added to the resulting bundle and shipped to NPM as a part of Semiotic source code. This made it harder to updated these dependencies to fix bugs and this definitely adds unnecessary weight to users bundles if they have Semiotic dependencies used for something else (e.g. D3 libs).

Now, Rollup skips bundling Semiotic dependencies so they will be installed as a part of users code base. This makes the resulting bundle significantly smaller, also eliminating the need to minify the code. The code will still be minified on the users side (modern build system has it enabled by default) but during development process users will be able to dig into semiotic code if necessary.

Running npm publish --dry-run here is what stats I see on this branch:

$ npm publish --dry-run
📦  semiotic@2.0.0
=== Tarball Contents ===
552B    LICENSE
1.2kB   README.md
55.1kB  dist/semiotic.d.ts
732.3kB dist/semiotic.js
55.1kB  dist/semiotic.module.d.ts
727.0kB dist/semiotic.module.js
6.0kB   package.json
=== Tarball Details ===
name:          semiotic
version:       2.0.0
filename:      semiotic-2.0.0.tgz
package size:  269.4 kB
unpacked size: 1.6 MB
total files:   7

When minification and gzip are applied to dist/semiotic.js, it becomes just 78.85KB in size.

UMD format is no longer available

UMD format is slowly dying and it is better to switch to ES Modules to fully support modern bundlers and other tooling. In fact, given browsers support of ES Modules, it is still very easy to use Semiotic in browser environment. README was already suggesting using unpkg.com, so I updated that example with the new approach:

<script type="module">
  import { XYFrame } from "https://unpkg.com/semiotic?module"
</script>

Modern JavaScript CDNs like unpkg.com and esm.sh work great with ES Modules, properly resolving their dependencies and making the use of libraries a pure joy.

Details