rsnext/packages/next-swc
Shu Ding 70d41b2627
Improve barrel optimizer with loader caching and wilcard exports (#54695)
## Initial Pass

The initial pass applies to all user code. If an import (`foo`) matches one of the packages from our list, say:

```js
// user code

import { a } from 'foo'
import { otherThings } from './code'
```

The initial pass transforms it into:

```js
// user code

import { a } from '__barrel_optimize__?names=a!=!foo'
import { otherThings } from './code'
```

## Barrel Optimizations

This `__barrel_optimize__` loader is used to optimize the imports of "barrel" files that have many
re-exports. Currently, both Node.js and Webpack have to enter all of these
submodules even if we only need a few of them.

For example, say a file `foo.js` with the following contents:

```js
  export { a } from './a'
  export { b } from './b'
  export { c } from './c'
  ...
```

If the user imports `a` only, this loader will accept the `names` option to
be `['a']`. Then, it request the `__barrel_transform__` SWC loader to load
`foo.js` (via `this.loadModule` Webpack API) and receive the following info:

```js
  export const __next_private_export_map__ = '[["./a","a","a"],["./b","b","b"],["./c","c","c"],...]'
```

The export map, generated by SWC, is a JSON that represents the exports of
that module, their original file, and their original name (since you can do
`export { a as b }`).

Then, this loader can safely remove all the exports that are not needed and
re-export the ones from `names`:

```js
  export { a } from './a'
```

That's the basic situation and also the happy path.



## Wildcard Exports

For wildcard exports (e.g. `export * from './a'`), it becomes a bit more complicated.
Say `foo.js` with the following contents:

```js
  export * from './a'
  export * from './b'
  export * from './c'
  ...
```

If the user imports `bar` from it, SWC can never know which files are going to be
exporting `bar`. So, we have to keep all the wildcard exports and do the same
process recursively. This loader will return the following output:

```js
  export * from '__barrel_optimize__?names=bar&wildcard!=!./a'
  export * from '__barrel_optimize__?names=bar&wildcard!=!./b'
  export * from '__barrel_optimize__?names=bar&wildcard!=!./c'
  ...
```

The "!=!" tells Webpack to use the same loader to process './a', './b', and './c'.
After the recursive process, the "inner loaders" will either return an empty string
or:

```js
  export * from './target'
```

Where `target` is the file that exports `bar`.



## Non-Barrel Files

If the file is not a barrel, we can't apply any optimizations. That's because
we can't easily remove things from the file. For example, say `foo.js` with:

```js
  const v = 1
  export function b () {
    return v
  }
```

If the user imports `b` only, we can't remove the `const v = 1` even though
the file is side-effect free. In these caes, this loader will simply re-export
`foo.js`:

```js
  export * from './foo'
```

Besides these cases, this loader also carefully handles the module cache so
SWC won't analyze the same file twice, and no instance of the same file will
be accidentally created as different instances.

---

- [x] Disable this loader for Jest
- [x] Add tests for SWC loader caching
- [x] Add tests for external modules
- [x] Add tests for pages
- [x] Add tests for recursive `export *`s

---

Closes #54038, closes #54286.
2023-08-30 02:29:14 +00:00
..
crates Improve barrel optimizer with loader caching and wilcard exports (#54695) 2023-08-30 02:29:14 +00:00
native Extract next-swc Rust code into its own package (#31635) 2021-11-21 12:59:56 +01:00
package.json v13.4.20-canary.12 2023-08-29 10:42:17 +00:00
README.md docs: add readme with development instructions for next/swc (#43834) 2022-12-08 13:38:44 +01:00

@next/swc

This package is responsible for swc compilation customized for next.js

Development

Run tests

cargo test

# Update snapshots and fixtures for tests
UPDATE=1 cargo test

Format code before submitting code

cargo fmt

Build the binary to integrate with next.js

pnpm build-native