-
Notifications
You must be signed in to change notification settings - Fork 73
Write docs for ESM Views #1704
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
Merged
Merged
Write docs for ESM Views #1704
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
f139c67
webpack build working
a2c91cb
Remove sourcemap obsolete flag
3d3352d
Redo ForkTsCheckerWebpackPlugin conf
5fcacfa
Fix stats type mismatch
8fb8342
start.js
b20e694
dev server working but trying to typecheck node_modules
bdfd0ec
webpack start is working
8986bd9
Change css minimizer to remove deprecation
9bd0894
Remove diagnostics
510fb6b
Update snapshots
92597a4
Support new version of minimize
8bf2aea
Add explaining comment
c6df324
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
5a340d0
Update yarn.lock
3122acf
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
1f7a830
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
8b1f89b
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
f075c72
Update snapshots
7c98350
Update all snapshots
5f3c001
Update snapshot hash
7b45909
Add missing quotes
f97c6f2
Merge branch 'main' into feature/experiment-webpack-5
cristiano-belloni bc9cb4c
Remove unwanted media dist files
87964dd
remove tsbuildinfo
187f866
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
e1abbef
Make test names decoupled from hashes
8816b88
Update test snapshots
e5d772f
Create silver-dolphins-remember.md
cristiano-belloni 51e770e
Enter pre-release mode
b16fae1
Merge remote-tracking branch 'origin/main' into release/webpack-5
681ab74
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
07052ac
Remove requireEnsure
55df9e0
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
76ce1bb
Merge remote-tracking branch 'origin/main' into release/webpack-5
dc846d8
Regenerate pre.json
43c42b7
remove workflow rule
bd5e86b
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
50332fd
Merge remote-tracking branch 'origin/main' into release/webpack-5
fb7db88
Update pre.json
5a6dfdc
Merge remote-tracking branch 'origin/main' into feature/experiment-we…
47e1ec4
Update snapshots
64f46f7
Update build snapshots
41ded67
Update browser versions
b2ca58b
Merge pull request #1421 from jpmorganchase/feature/experiment-webpack-5
cristiano-belloni 8cfe64f
Version Packages (alpha-webpack5)
github-actions[bot] d8562e6
Merge pull request #1521 from jpmorganchase/changeset-release/release…
cristiano-belloni 4f1af82
Revert "Revert engine range (#1518)"
d2aacf6
Revert "fix node-12 incompatible javascript (#1517)"
d2b10e9
Remove node 12 support
69da976
Add changeset
c40158b
Add node 18 in CI
074bff4
Revert "Add node 18 in CI"
4baca0a
Hardcode node to v14
9f48679
support node 16 explicitly and run test workflow against target node …
LukeSheard 8115519
fix node versions so that tests run
LukeSheard 8b3f1f5
Create dirty-mugs-double.md
LukeSheard 8901b9f
fix node versions so that tests run and remove 15 since it's unsupported
LukeSheard 0a0bc65
Merge remote-tracking branch 'origin/bugfix/workflow-node-version' in…
4abf766
Raise version to be compatible with eslint
4f9cba9
Version Packages (alpha-webpack5)
github-actions[bot] a31fb3f
Merge pull request #1554 from jpmorganchase/changeset-release/release…
cristiano-belloni af674c5
Support for esm-view type in react scripts
d6d2e8c
Add esm-view into modular types
49c0604
Better names for isView + activate esm-views in start
69c480c
Update tests
93e0adb
Add tests and way of adding esm-view
a60bec5
Better comments/names
b0ccaf4
Merge remote-tracking branch 'origin/main' into feature/esm-views
8c0a688
Add esm-view type
375c7e8
Add package modular-template-esm-view
309cf20
Fix logger error
4e46075
fix esm view tests
865b80f
Fix app tests
f168606
Fix view tests
2130f56
Fix build tests
7fa41ae
Remove residual changelogs
d34acec
Fix app node env tests
c189293
Fix WorkspaceInfo test
455cb57
Merge remote-tracking branch 'origin/main' into feature/esm-views
c051e92
Create proud-starfishes-stare.md
cristiano-belloni 2a5b5a3
Write docs for ESM Views
83febbd
Merge remote-tracking branch 'origin/main' into feature/esm-view-docs
0cca5ff
Merge remote-tracking branch 'origin/main' into feature/esm-view-docs
e4fe7fc
Merge api.ts
acded77
Update esm views.md
cristiano-belloni 18d4de1
Remove redundant micromatch link
cristiano-belloni f276d41
Reformat docs
5f13815
Mention resolutions
8577034
Fix grammar / content
558990b
Explain trampoline file
654ea6f
Additional review fixes
c6f5abd
Apply suggestions from code review
cristiano-belloni f8e2e8c
Linting
68cb77f
content fix
8d24944
Apply suggestions from code review
cristiano-belloni b171d91
Lint
d94e521
Hyphenate file names
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| `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) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.