Skip to content

Tree shaking optimizations and composability #581

Open
@JohnAlbin

Description

@JohnAlbin

Package containing the bug

next-drupal (NPM package)

Describe the bug

A library should maximize composability so that it can be:

  • easily extended (by itself or other libraries)
  • bundled with tree-shaking optimizations

tree shaking

When used in apps, JavaScript libraries are trimmed-down to just the parts that the app uses when the app's bundler (like Webpack or Rollup) performs tree shaking optimizations to eliminate dead (unused) code. According to the article, “How To Make Tree Shakeable Libraries”, tree shaking can be split in two optimizations:

  • usedExports: determine which exports of a module are used and unused
  • sideEffects: skip over entire modules that do not have any used exports and that are side effects free.

The usedExports optimization can determine which exports in a single module are not used by an application. The app bundler will import the entire module and then remove any dead code exports that are not used. However, when bundlers use code splitting to create different bundles for different pages (like Next.js does), the bundlers can only determine which exports are used in the application as a whole not in the specific code split, which means all the exports that are used anywhere in the app are bundled with every code split chunk.

The sideEffects optimization is way more effective than just detecting unused export statements in a module. If a library modifies JavaScript globals or does any state-management when the module is imported, then an app bundler must import the module before doing any other optimizations. Since it is very difficult for an app bundler to determine that on its own, the convention is to add "sideEffects": "false" to a library's package.json file (next-drupal already does this.) When the app bundler sees this package.json flag, the sideEffects optimization can skip a module import entirely if none of the named exports are used. The details of how this works are in the Code splitting section of the above article.

To use both of these optimizations, a library must:

  • provide ES Modules
  • split the code into small, discrete modules (files). In other words, limit the bundling before publishing.
  • set "sideEffects": "false" in its package.json

composability

A highly composable system provides components that can be selected and assembled in various combinations to satisfy specific user requirements. The self-contained components can be deployed independently – note that it may cooperate with other components, but dependent components are replaceable. — from Wikipedia's article on composability

Expected behavior

For next-drupal a highly composable system would be one where a website developer can choose specific queries to use and the rest of the queries are not included in the app's bundled JavaScript. It's also one where 3rd party developers could get different Drupal data by writing their own queries that tie into next-drupal's configuration.

The old API was composable as it was a bunch of independent query functions, but not easy to configure, relying on global environment variables. 3rd party developers could extend the system by creating new query functions that used the same next-drupal configuration.

The DrupalClient API introduced in 1.6 is easy to configure. But it is a class definition that can't be split into discrete modules.

As for tree-shaking, next-drupal has always been bundled into a single module so it can't be optimized with sideEffects tree-shaking. But this is easily fixable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions