Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
c5c50db
Bring webpack configuration to a valid (but not working) state
Feb 18, 2022
f139c67
webpack build working
Feb 21, 2022
a2c91cb
Remove sourcemap obsolete flag
Feb 21, 2022
3d3352d
Redo ForkTsCheckerWebpackPlugin conf
Feb 21, 2022
5fcacfa
Fix stats type mismatch
Feb 21, 2022
8fb8342
start.js
Feb 22, 2022
b20e694
dev server working but trying to typecheck node_modules
Feb 23, 2022
bdfd0ec
webpack start is working
Feb 23, 2022
8986bd9
Change css minimizer to remove deprecation
Feb 23, 2022
9bd0894
Remove diagnostics
Feb 23, 2022
510fb6b
Update snapshots
Feb 24, 2022
92597a4
Support new version of minimize
Feb 24, 2022
8bf2aea
Add explaining comment
Feb 24, 2022
c6df324
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Feb 28, 2022
5a340d0
Update yarn.lock
Feb 28, 2022
3122acf
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 4, 2022
1f7a830
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 7, 2022
8b1f89b
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 8, 2022
f075c72
Update snapshots
Mar 8, 2022
7c98350
Update all snapshots
Mar 8, 2022
5f3c001
Update snapshot hash
Mar 8, 2022
7b45909
Add missing quotes
Mar 8, 2022
f97c6f2
Merge branch 'main' into feature/experiment-webpack-5
cristiano-belloni Mar 11, 2022
bc9cb4c
Remove unwanted media dist files
Mar 11, 2022
87964dd
remove tsbuildinfo
Mar 11, 2022
187f866
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 11, 2022
e1abbef
Make test names decoupled from hashes
Mar 14, 2022
8816b88
Update test snapshots
Mar 14, 2022
e5d772f
Create silver-dolphins-remember.md
cristiano-belloni Mar 16, 2022
51e770e
Enter pre-release mode
Mar 16, 2022
b16fae1
Merge remote-tracking branch 'origin/main' into release/webpack-5
Mar 17, 2022
681ab74
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 17, 2022
07052ac
Remove requireEnsure
Mar 21, 2022
55df9e0
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 22, 2022
76ce1bb
Merge remote-tracking branch 'origin/main' into release/webpack-5
Mar 25, 2022
dc846d8
Regenerate pre.json
Mar 25, 2022
43c42b7
remove workflow rule
Mar 25, 2022
bd5e86b
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 25, 2022
50332fd
Merge remote-tracking branch 'origin/main' into release/webpack-5
Mar 28, 2022
fb7db88
Update pre.json
Mar 28, 2022
5a6dfdc
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
Mar 28, 2022
47e1ec4
Update snapshots
Mar 28, 2022
64f46f7
Update build snapshots
Mar 28, 2022
41ded67
Update browser versions
Mar 28, 2022
b2ca58b
Merge pull request #1421 from jpmorganchase/feature/experiment-webpack-5
cristiano-belloni Mar 29, 2022
8cfe64f
Version Packages (alpha-webpack5)
github-actions[bot] Mar 29, 2022
d8562e6
Merge pull request #1521 from jpmorganchase/changeset-release/release…
cristiano-belloni Mar 29, 2022
4f1af82
Revert "Revert engine range (#1518)"
Apr 5, 2022
d2aacf6
Revert "fix node-12 incompatible javascript (#1517)"
Apr 5, 2022
d2b10e9
Remove node 12 support
Apr 5, 2022
69da976
Add changeset
Apr 5, 2022
c40158b
Add node 18 in CI
Apr 5, 2022
074bff4
Revert "Add node 18 in CI"
Apr 5, 2022
4baca0a
Hardcode node to v14
Apr 6, 2022
9f48679
support node 16 explicitly and run test workflow against target node …
LukeSheard Apr 6, 2022
8115519
fix node versions so that tests run
LukeSheard Apr 6, 2022
8b3f1f5
Create dirty-mugs-double.md
LukeSheard Apr 6, 2022
8901b9f
fix node versions so that tests run and remove 15 since it's unsupported
LukeSheard Apr 6, 2022
0a0bc65
Merge remote-tracking branch 'origin/bugfix/workflow-node-version' in…
Apr 7, 2022
4abf766
Raise version to be compatible with eslint
Apr 7, 2022
4f9cba9
Version Packages (alpha-webpack5)
github-actions[bot] Apr 7, 2022
a31fb3f
Merge pull request #1554 from jpmorganchase/changeset-release/release…
cristiano-belloni Apr 8, 2022
af674c5
Support for esm-view type in react scripts
Apr 26, 2022
d6d2e8c
Add esm-view into modular types
Apr 26, 2022
49c0604
Better names for isView + activate esm-views in start
Apr 26, 2022
69c480c
Update tests
Apr 26, 2022
93e0adb
Add tests and way of adding esm-view
Apr 26, 2022
a60bec5
Better comments/names
Apr 26, 2022
b0ccaf4
Merge remote-tracking branch 'origin/main' into feature/esm-views
May 3, 2022
8c0a688
Add esm-view type
May 4, 2022
375c7e8
Add package modular-template-esm-view
May 4, 2022
309cf20
Fix logger error
May 4, 2022
4e46075
fix esm view tests
May 4, 2022
865b80f
Fix app tests
May 4, 2022
f168606
Fix view tests
May 4, 2022
2130f56
Fix build tests
May 4, 2022
7fa41ae
Remove residual changelogs
May 4, 2022
d34acec
Fix app node env tests
May 4, 2022
c189293
Fix WorkspaceInfo test
May 5, 2022
455cb57
Merge remote-tracking branch 'origin/main' into feature/esm-views
May 5, 2022
c051e92
Create proud-starfishes-stare.md
cristiano-belloni May 5, 2022
2a5b5a3
Write docs for ESM Views
May 9, 2022
83febbd
Merge remote-tracking branch 'origin/main' into feature/esm-view-docs
May 12, 2022
0cca5ff
Merge remote-tracking branch 'origin/main' into feature/esm-view-docs
May 16, 2022
e4fe7fc
Merge api.ts
May 16, 2022
acded77
Update esm views.md
cristiano-belloni May 25, 2022
18d4de1
Remove redundant micromatch link
cristiano-belloni May 25, 2022
f276d41
Reformat docs
May 25, 2022
5f13815
Mention resolutions
May 26, 2022
8577034
Fix grammar / content
May 31, 2022
558990b
Explain trampoline file
May 31, 2022
654ea6f
Additional review fixes
May 31, 2022
c6f5abd
Apply suggestions from code review
cristiano-belloni May 31, 2022
f8e2e8c
Linting
May 31, 2022
68cb77f
content fix
May 31, 2022
8d24944
Apply suggestions from code review
cristiano-belloni May 31, 2022
b171d91
Lint
May 31, 2022
d94e521
Hyphenate file names
May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions docs/building-apps/esm-views.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
parent: Building your Apps
title: ESM Views
---

modular builds packages of `"type": "esm-view"` as
[ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules),
rewriting all of a subset of their imports to make use of a configurable ESM CDN
(e.g. [Skypack](https://www.skypack.dev) or [esm.sh](https://esm.sh/)). This
allows users to implement the
[microfrontend pattern](../concepts/microfrontends.md), by creating an artifact
that can be
[`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports)ed
at runtime by a host application, or loaded stand-alone thanks to the automatic
generation of the `index.html` and trampoline file.

## Why ESM Views need an external CDN

ESM Views are designed to exclude external dependencies from the output bundle
and `import` them at runtime. This simplifies the build process by removing the
need for explicit dependency de-duplication as the browser will do so
automatically though its cache so long as all dependencies are served from the
same origin, i.e. a single CDN.

This is particularly useful for a host application that lazily loads several
independently developed and hosted applications onto a browser tab at runtime;
If each of those applications naively bundled all their dependencies this would
result in inefficiencies as a copy of each dependency that is used by more than
one application would be included in each bundle. For stateful dependencies like
React that don't allow multiple instances of themselves in the same page
context, this would cause crashes. Importing these external dependencies from a
CDN, instead, means that every shared dependency is loaded from the server
exactly once at runtime and re-used in every point of the code where it's
imported. This improves efficiency and, since every dependency is loaded and
evaluated only once, it plays well with stateful libraries.

## How to build

ESM views are built with the [`modular build`](../commands/build.md) command.
The default behaviour when building an ESM view is:

1. All the non-local `import`s in the package's `src` directory are extracted
and matched with their version in the package's `package.json` or the
`package.json` in the repository root and with their exact version in the
repo's lockfile.
2. The main entrypoint (as defined in the ESM view's `package.json`'s `main`
field) and its local imports are bundled in a single file.
3. All the `import` statements to non-local dependencies encountered in the
process are rewritten to `import` from an external ESM CDN (by default
https://www.skypack.dev/), using the versions extracted in step 1. By
default, versions extracted from `package.json` will be used, but users can
customize the rewrite template to use versions from the lockfile instead.
4. All the local CSS is bundled in a single file.
5. The `dist` directory is generated, containing:
- The js file
- The css file
- A [package manifest](#package-manifest) (`package.json`) file containing:
- The location of the js bundle (`"module"` field)
- The location of the css bundle (`"style"` field)
- An object with the whole set of dependencies and their version ranges
(`"dependencies"` field)
- An array of bundled dependencies (`bundledDependencies` field)
- A synthetically generated `index.html` file, linking the trampoline file
and the css bundle
- A synthetically generated trampoline file, dynamically `import`ing the js
bundle and `React.render`ing its default export to a `#root` div.

The ESM view build result can either be
[dynamically imported](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports)
from a host application,
([including the css bundle](https://web.dev/css-module-scripts/)) or served
statically as a standalone application (for example, using `modular serve` or
from a web server).

## Customise the ESM CDN

You can specify a CDN template to rewrite dependencies using the environment
variable `EXTERNAL_CDN_TEMPLATE`.

For example:

- The default template for the [Skypack](https://www.skypack.dev/) public CDN is
`EXTERNAL_CDN_TEMPLATE="https://cdn.skypack.dev/[name]@[resolution]"`
- A valid template to work with the esm.sh public CDN can be specified with
`EXTERNAL_CDN_TEMPLATE="https://esm.sh/[name]@[version]"`

These are the substrings that are replaced in the template:

- `[name]` is replaced with the name of the imported dependency
- `[version]` is replaced with the version of the imported dependency as
extracted from the package's or the root's (hoisted) `package.json`.
- `[resolution]` is replaced with the version of the imported dependency as
extracted from the yarn lockfile (`yarn.lock`).

## Customise bundling / rewriting strategy

By default, all external dependencies are rewritten to a CDN URL and none is
bundled. This logic can be controlled using two environment variables:

1. `EXTERNAL_ALLOW_LIST` is a comma-separated string that specifies which
dependencies are allowed to be rewritten to the CDN; if not specified, its
default value is `**` ( -> all dependencies are rewritten)
2. `EXTERNAL_BLOCK_LIST` is a comma-separated string that specifies which
dependencies are **not** allowed to be rewritten to the CDN; if not specified
its default value is empty ( -> no dependency excluded, i.e. all dependencies
are rewritten)

The allow / block lists are parsed and processed according to this logic:

- If a dependency is only in the allow list, it will be rewritten
- If a dependency is only in the block list, it will be bundled
- If a dependency is in both lists, it will be bundled (`EXTERNAL_BLOCK_LIST`
wins)
- If a dependency is in none of the lists, it will be bundled (but remember that
all dependencies are in allow list by default)

The dependencies will be reflected in the output package manifest
(`package.json`) according to these rules:

- All dependencies and their versions are listed in the `dependencies` field, as
an object
- The dependencies that are bundled are listed in the `bundledDependencies`
field, as an array

It is possible to specify wildcards in the block and allow list.
[Micromatch](https://github.com/micromatch/micromatch) syntax is supported.

## Package manifest

Every build of an ESM view will generate a package manifest (`package.json`),
which will contain a selection of the original `package.json` fields, plus a set
of added / modified fields:

- [`style`](https://jaketrent.com/post/package-json-style-attribute): the
location of the js bundle (example:
`"style": "static/css/main.c6ac0a5c.css"`), useful for an host to dynamically
load the styles and add them to the page `<head>` or the
[adopted stylesheet](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets).
- [`module`](https://github.com/dherman/defense-of-dot-js/blob/f31319be735b21739756b87d551f6711bd7aa283/proposal.md):
the location of the js bundle (example:
`"module": "static/js/main.5077b483.js"`), useful for an host to dynamically
load the ESM view and render it in a React tree.
- [`dependencies`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#dependencies):
an object containing all the dependencies imported in the package source
(including the hoisted ones) and their correspondent version ranges.
- [`bundledDependencies`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bundleddependencies):
an array of bundled dependencies. Since `dependencies` contains all the
dependencies (bundled and not bundled), it is always possible to know which
dependencies were rewritten and which were bundled.

## External CSS imports

CSS imports pointing to an external package (for example:
[`import 'regular-table/dist/css/material.css'`](https://www.npmjs.com/package/regular-table)
) will be rewritten to a CDN URL (for example, using Skypack,
`https://cdn.skypack.dev/regular-table@[version]/dist/css/material.css`). The
only difference is that they will be rewritten in the bundle as code that
applies the CSS into the page, either by simply adding it to the `head` or,
depending on the build `target`, using
[CSS Module scripts](https://web.dev/css-module-scripts/) and adding the script
to the
[adopted stylesheet](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets).

This feature is experimental and feedback is appreciated.
File renamed without changes.
22 changes: 13 additions & 9 deletions docs/commands/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ Adds a new package by creating a new workspace at `packages/<packagePath>`
(i.e. `modular add my-app` would create a package in `packages/my-app` and
`modular add libs/lib-a` would create a package in `packages/libs/lib-a`)

Packages can currently be one of 3 types:
Packages can currently be one of the following types:

- A standalone application. This corresponds to a single `create-react-app`
project in a workspace. Inside this workspace, you can import packages from
other workspaces freely, and features like jsx and typechecking work out of
the box.
- A standalone `app`. This corresponds to a single `create-react-app` project in
a workspace. Inside this workspace, you can import packages from other
workspaces freely, and features like jsx and typechecking work out of the box.

- A View, which is a package that exports a React component by default. Views
are primary, top-level components in `modular`. Read more about Views in
[this explainer](../concepts/views.md).
- An `esm-view`, which is a package that typically exports a React component by
default. ESM Views are built as ES modules that can be `import`ed at runtime
by a host to implement a [micro frontend](../concepts/microfrontends.md)
architecture or started as a normal standalone application. See also
[the view building reference](../building-apps/esm-views.md)

- A typical javascript package. You can use this to create any other kind of
- A `view`, which is a package that exports a React component by default. Read
more about Views in [this explainer](../concepts/views.md).

- A typical JavaScript `package`. You can use this to create any other kind of
utility, tool, or whatever your needs require you to do. As an example, you
could build a node.js server inside one of these.

Expand Down
76 changes: 76 additions & 0 deletions docs/concepts/microfrontends.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
parent: Concepts
---

## ESM micro frontends in Modular

Micro frontends are a pattern in which discrete UIs (frontends) are composed of
independent fragments that can be built and deployed separately by different
teams and loaded on-demand at runtime. Modular gives developers the opportunity
to implement micro frontends through [ESM Views](../building-apps/esm-views.md),
which are applications built as ES Modules. ESM Views can be served standalone
or dynamically imported by a host application at runtime and rendered in the
host application's own React tree, without the need of using Iframes. ESM Views
also allow automatic dependency de-duplication in the browser, thanks to how
Modular offloads third-party dependencies to a configurable ESM CDN.

## How we build micro frontends

An ESM View can be added to a Modular monorepo via the `modular add` command,
which will set the package's Modular type to `"esm-view"` in its `package.json`.

```json
"modular": {
"type": "esm-view"
}
```

This will instruct the `modular build` command to output an
[ES Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).

The ESM View's local dependencies will be bundled in the dist result, while the
ESM View's external dependencies will be rewritten to import from an ESM CDN by
default, like (for example) [Skypack](https://www.skypack.dev/) or
[esm.sh](https://esm.sh/). This allows the ESM View's external dependencies to
be loaded on-demand at runtime and to be automatically de-duplicated (i.e. the
same dependency will be not re-fetched and re-evaluated, but simply re-used if
any view tries to use it the second time). This is especially useful in a React
micro frontend scenario, where multiple ESM views share the same copy of React,
without incurring errors associated with
[multiple copies of React in the same page](https://reactjs.org/warnings/invalid-hook-call-warning.html).

Since dynamically importing ES Modules leverages a
[widely supported standard](https://caniuse.com/es6-module-dynamic-import),
where the browser does the heavy lifting of fetching, de-duplicating and making
dependencies available, ES Modules are Modular's preferred building blocks to
implement a micro frontend architecture.

## How to load micro frontends

Building an ESM View generates a single JavaScript entrypoint and a single CSS
entrypoint that can be imported at runtime by any application (or other ESM
View) using dynamic import (or any viable technique in case of styles). ESM
Views will generate a package manifest (`package.json` file) that contains:

- Information regarding the location of the built files, whose names are
uniquely hashed to facilitate caching
- Lists of bundled and rewritten dependencies along with their dependencies, in
order to decouple importing of ESM Views from the actual build result
structure. For more information, visit the
[ESM Views reference page](../building-apps/esm-views.md)

## Standalone support

As ESM views typically export a React component by default, a synthetic
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: what happens if an ESM View doesn't export a React component by default? Will the index.html file be generated?

Copy link
Copy Markdown
Contributor Author

@cristiano-belloni cristiano-belloni May 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will and, in that case, the index file will break if served. The ESM View, if imported, will obviously work. We discussed this already and said that in the future we might want a configuration flag for the synthetic index / trampoline.

`index.html` capable of loading the view as a standalone web page is provided in
the `dist/{{view-name}}` directory. This means that ESM views can additionally
be served (for example with the `modular serve` command or any HTTP server) as
normal applications, while retaining the ability of loading their dependencies
via an ESM CDN.

## Customisation

ESM views can be customised by editing the template used to rewrite the external
imports and by setting which dependency imports are rewritten to point to a CDN
and which are bundled. For more information on how to customise the build of ESM
views, visit the [ESM Views reference page](../building-apps/esm-views.md)