Skip to content
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

Document package types #2246

Merged
merged 34 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4aafc87
Start documenting package types
Jan 3, 2023
4655c35
Describe App and ESM View types
Jan 3, 2023
048f7e3
Packages
Jan 4, 2023
8d2848c
Views, sources and templates
Jan 4, 2023
56ecf0d
Link add command page
Jan 4, 2023
a724457
Link package types in index
Jan 5, 2023
e82dbc1
Update docs/commands/add.md
cristiano-belloni Jan 5, 2023
286fd60
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
35114b0
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
7bb2648
Merge remote-tracking branch 'origin/feature/v4' into feature/documen…
Jan 5, 2023
e9d18f3
Link esm-views section in add page
Jan 5, 2023
ea4774d
Update docs/commands/add.md
cristiano-belloni Jan 5, 2023
77d3643
Link app section for more info
Jan 5, 2023
0b72782
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
1a8aafe
Clarify manifest -> package.json + fix incorrect description
Jan 5, 2023
89302d2
Merge branch 'feature/document-package-types' of https://github.com/j…
Jan 5, 2023
71e12a4
Add reasons why we don't support things
Jan 5, 2023
9cbd85b
Disambiguate words
Jan 5, 2023
c0a46c7
Phrase 'default export' concept better
Jan 5, 2023
217ae18
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
137d28a
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
96565ad
Update docs/commands/add.md
cristiano-belloni Jan 5, 2023
35fbad8
Reword tricky sentence
Jan 5, 2023
b8a7aa6
Update docs/commands/add.md
cristiano-belloni Jan 5, 2023
e08ef2a
Update docs/concepts/package-types.md
cristiano-belloni Jan 5, 2023
db62b30
Split long sentence
Jan 5, 2023
faeb1f1
Add all package types to configuration docs
Jan 5, 2023
bad4cba
Various typos
Jan 5, 2023
2f7b79f
Split packge types and move template page
Jan 6, 2023
09e501d
Fix links to package types and fix table layout
Jan 6, 2023
b44505b
Slim down table
Jan 6, 2023
af864c7
build/start/entrypoint/template sections
Jan 6, 2023
a25c948
Fix configuration
Jan 9, 2023
31c9018
Remove list of types in documentation
Jan 9, 2023
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
73 changes: 51 additions & 22 deletions docs/commands/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,69 @@ title: modular add

# `modular add <packageName>`

Adds a new package by creating a new workspace at `packages/<packageName>`,
omitting the scope if the package is
Adds a new package by creating a new package at the workspace located at
sgb-io marked this conversation as resolved.
Show resolved Hide resolved
benpryke marked this conversation as resolved.
Show resolved Hide resolved
`packages/<packageName>`, omitting the scope if the package is
[scoped](https://docs.npmjs.com/cli/v8/using-npm/scope). If `--path <somePath>`
is specified, create the workspace at `<somePath>/<packageName>`.
is specified, the command creates the workspace at `<somePath>/<packageName>`.

(i.e. `modular add my-app` would create a package in `packages/my-app`,
`modular add @scoped/my-scoped-app` would create a package in
`packages/my-scoped-app` and `modular add lib-a --path libs` would create a
package in `libs/lib-a`)

Packages can currently be one of the following types:
The `modular add` command prompts the user to choose the Modular `type` of the
package it's about to create. The next section briefly describes the various
types that can be created by the `modular add` command. For an in-depth
discussion of the available package types and their characteristics, please see
[this page](../concepts/package-types.md).

- A standalone `app`. This corresponds to a static Single Page Application (SPA)
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.
### Standalone (bundled) package types
benpryke marked this conversation as resolved.
Show resolved Hide resolved

- 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](../esm-views/index.md)
These package types are built with [Webpack v5](https://webpack.js.org/) or, if
specified in the [configuration](../configuration.md),
[esbuild](https://esbuild.github.io/). Modules imported in the source of these
package types are bundled in the final result (in case of `esm-view`s, only
local modules get bundled, and external dependencies are rewritten to use an
external ESM CDN. [This section](../esm-views/index.md) explains the process in
more depth).

- A `view`, which is a `package` that exports a React component by default. Read
more about Views in [this explainer](../concepts/views.md).
- `app`. This package type corresponds to a static Single Page Application (SPA)
project in a workspace. It's possible to specify a custom `index.html` file
sgb-io marked this conversation as resolved.
Show resolved Hide resolved
and public assets in the `public` directory. See
[this page](../concepts/package-types.md/#app) for more information about
apps.

- A generic JavaScript `package`. You can use this to create a library with an
entry point that gets transpiled to Common JS and ES Module format when built.
Packages can be [built](../commands/build.md) but not
[start](../commands/start.md)ed by Modular.
- `esm-view`. This package type is an app that gets built as an ES module that
can be imported at runtime. `esm-view`s are typically used to implement a
[micro-frontend](../concepts/microfrontends.md) architecture. `esm-views`,
when [built](./build.md) or [started](./start.md) will also generate a
`index.html` file that tries to load the ES Module and render its default
export as a React component onto the DOM (standalone mode). See also
[the esm-view reference](../esm-views/index.md) for an in-depth introduction.

### Library package types

These package types are either built with
[Rollup.js](https://rollupjs.org/guide/en/) as CommonJS and ES Modules or, in
case of `source` modules, they are not built at all. Library package types get
typically published to NPM (`package` and `view` types) or get imported by other
packages in the monorepo (`source` type). For this reason, files are transpiled
separately on build and external dependencies are never "pulled in" (i.e. not
included in a bundle).

- `package`. This is a generic package with a single entry point. It's normally
used to create a publishable library that gets transpiled to CommonJS and ES
Module format when built. Packages can be [built](../commands/build.md) but
not [start](../commands/start.md)ed by Modular.

- `view`. This is a `package` that exports a default React component. Views are
built exactly like `package`s, but, since Modular knows that the default
export can be rendered, `view`s can be [`modular start`](../start.md)ed to
preview them locally.

- A `source`, which is a shared package that is imported by other packages from
source (i.e. directly importing its source), and it's never built standalone
or published. This kind of package is never [built](../commands/build.md) or
- `source`. A shared package that is imported by other package types in the
monorepo, directly specifying one or more of its source files. This kind of
package can be never [built](../commands/build.md) or
[start](../commands/start.md)ed by Modular.

## Options:
Expand Down
229 changes: 229 additions & 0 deletions docs/concepts/package-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
---
title: Types of packages
parent: Concepts
---

Copy link
Contributor

Choose a reason for hiding this comment

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

Needs an intro. Something like:

Modular can build several different kinds of packages. We refer to each package type as a Modular type, and each has different features. The table below summarises the compatibility of each Modular type with Modular commands and details their unique features.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

| Type | `modular start` | `modular build` | `modular test` | `modular lint` | Custom index / assets | Bundled | Entrypoint |
| ---------- | --------------- | --------------- | -------------- | -------------- | -------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `app` | Yes | Yes | Yes | Yes | Yes | Yes | The compiled bundle will use `src/index.tsx` as an entrypoint and include it in the generated `index.html` |
| `esm-view` | Yes | Yes | Yes | Yes | No | Yes + rewriting external dependencies to CDN | The compiled bundle will use `src/index.tsx` as an entrypoint and [link it](../esm-views/output-package-manifest.md) in the output `package.json`; the entrypoint needs to export a default (non-named) React component for `modular start`/ [standalone `index.html`](../esm-views/how-to-build.md) to work |
| `package` | No | Yes | Yes | Yes | N/A | No | Whatever is specified in `main` field of `package.json` is used as the entrypoint |
| `view` | Yes | Yes | Yes | Yes | N/A | No | Whatever is specified in `main` field of `package.json` is used as the entrypoint; needs to default export React component for `modular start` to work |
| `source` | No | No | Yes | Yes | N/A | N/A - never built | N/A - don't need the concept of entrypoint since this package is never built |
| `template` | No | No | Yes | Yes | Depends on the target type | N/A - never built | N/A - don't need the concept of entrypoint since this is a special type of package used by `modular add` which is never built directly |

## App

Modular `app`s are TypeScript and React-based Single Page Applications (SPAs)
built with [Webpack v5](https://webpack.js.org/) (by default) or
[esbuild](https://esbuild.github.io/) (by turning on `useModularEsbuild` in
[the configuration](../configuration.md)).

Apps need at least a `public/index.html` file and an entrypoint located in
`src/index.tsx`, that typically `render`s a React component to the DOM, to work.

Apps support pretty much all the functionalities from
sgb-io marked this conversation as resolved.
Show resolved Hide resolved
[Create React App v5](https://create-react-app.dev/docs/custom-templates), with
some important differences:

sgb-io marked this conversation as resolved.
Show resolved Hide resolved
- [ejecting](https://create-react-app.dev/docs/available-scripts/#npm-run-eject)
is not supported. As an opinionated tool, Modular tries to offer an uniform
way of building applications, although feedback on our build configuration is
welcome!
- [template support](../concepts/templates.md) is integrated in the
[`modular add`](../commands/add.md) command. Modular templates are not
guaranteed to be compatible with CRA templates.
- Source files are loaded with the more performant
[`esbuild-loader`](https://github.com/privatenumber/esbuild-loader) in the
Webpack configuration. For this reason, Babel plugins are not supported.
- [Local proxies](https://create-react-app.dev/docs/proxying-api-requests-in-development/)
are supported, but [esbuild mode](../configuration.md) doesn't support the
`package.json` `proxy` field. The more flexible
[manual proxy configration](https://create-react-app.dev/docs/proxying-api-requests-in-development/#configuring-the-proxy-manually)
is supported in both Webpack and esbuild mode.

Apps can be built using [`modular build`](../commands/build.md): the resulting
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's keep it example driven with clear commands that are separated from the text so they're easier for a developer to find while scanning the docs. We should also repeat the message that build is for deployed assets and start is for local development, as not everybody is familiar with these concepts.

To build your app for deployment, run:

modular build my-app-name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

output is an optimized site that can be served statically. All code (files in
`src` plus external dependencies required in the code) are bundled in a single
blob of code that can be split in different files.

Apps can be started using [`modular start`](../commands/start.md): a developer
Copy link
Contributor

Choose a reason for hiding this comment

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

To run your app locally on a development server, run start:

modular start my-app-name

Copy link
Contributor Author

@cristiano-belloni cristiano-belloni Jan 6, 2023

Choose a reason for hiding this comment

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

Done.

server runs on port 3000, serving the app with an additional runtime layer that
provides developer experience functionalities like hot reloading and on-screen
error overlay.

Apps are generated by `modular add` using the
[`modular-template-app`](https://github.com/jpmorganchase/modular/tree/main/packages/modular-template-app)
[template](../concepts/templates.md)

## ESM View

Modular `esm-view`s are similar to `app`s with some essential differences: they
Copy link
Contributor

Choose a reason for hiding this comment

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

Be precise: use numbered bullet-points and say "two essential differences".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, see below.

are compiled as [ES
Modules(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)]
and all their external `import`s are rewritten to point to an
[ESM CDN](../esm-views/esm-cdn.md). Like apps, they are be built with the same
Copy link
Contributor

Choose a reason for hiding this comment

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

This phrasing makes it sound like is it the build configs between Webpack and esbuild that are the same; use something like this:

ESM Views are built with the same Webpack or esbuild configuration and support the same functionalities as Modular apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Webpack or esbuild configuration and support the same functionalities. Unlike
apps, they don't allow the user to specify a custom `index.html`, they don't
Copy link
Contributor

Choose a reason for hiding this comment

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

These differences could be a list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Collated all differences in a ordered list.

include a `public` folder in the build and they expect their entrypoint
(`src/index.tsx`) to not `render` to the DOM, but to export something, typically
a React Component.

Esm Views are Modular's type of choice to implement the
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be more dogmatic: The esm-view type is used for creating micro-frontends.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

[micro-frontend](../concepts/microfrontends.md) pattern. They typically expect
Copy link
Contributor

Choose a reason for hiding this comment

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

The typical vs served case is unclear to the uninitiated. The way this is posed makes these docs more philosophical; to make them active, use code-examples that show how to import an esm-view at run-time and how to serve it standalone (etc!).

That may be too much for this document. Could we create a top-level "Package Types" category at docs/package-types where the index.md file contains the table at the top of this file and there are specific pages for each type? The existing ESM Views section may need to be rethought if we do that. Can it be nested? I think this structure will make it clearer what Modular is/does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed: will create a section for package types but not nest esm-views

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

their entrypoint exports to be
[dynamically `import`ed at runtime](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import)
by some other micro-frontend, but can be also
[served standalone](../esm-views/how-to-build.md) exactly as an `app`, provided
that they default export a React component that can be rendered to the DOM.

Since the micro-frontend pattern allows different teams to build and serve their
Copy link
Contributor

Choose a reason for hiding this comment

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

This paragraph is very wordy and I fear it will get lost. Is there a better way to provide this information? E.g. in a "How to use a CDN for external dependencies" guide or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fear we don't have such a resource. Do you wanna try to simplify this? I would like to convey the message that we have esm views because they can be built in isolation and composed at run-time.

applications independently and compose them at runtime (parallelizing otherwise
expensive build operations), third-party dependencies need to be de-duplicated
across micro-frontends; for this reason, external dependencies are not bundled,
but they are rewritten to a [configurable ESM CDN](../esm-views/esm-cdn.md) that
serves them as ES modules on the fly.

Esm Views can be built using [`modular build`](../commands/build.md): the
Copy link
Contributor

Choose a reason for hiding this comment

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

Following my comment about separate pages for each package type, I think each page should have headings for each of the [appropriate?] Modular commands (always in the same order), that explain the detail here and include a code snippet for e.g. modular build my-view, modular add --type esm-view etc. Don't worry about the duplication between pages; this is on purpose: some users will only view one page, and those who view several will quickly learn about the common interface that Modular exposes.

Optionally, you could link the answers in the table above to each specific section on the page to provide additional detail, if desired, instead of using longer sentences in the table.

Copy link
Contributor Author

@cristiano-belloni cristiano-belloni Jan 6, 2023

Choose a reason for hiding this comment

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

The problem with add is that we have --unstable-type, derived from CRA, that we don't really want to show on the docs. The preferred mode of adding a page is the interactive prompt.

Apart from that, I like the idea of having structured information: I can do that for build, start and entry-points.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh yes, I forgot about our unstable params. I don't know the history there but it's unintuitive!

resulting output is an optimized site that can be either served statically
(using a synthetically-generated `index.html` file and a `_trampoline.js`
loader), or dynamically imported by another ESM view. The JS and CSS
entrypoints, in the second case, are linked in the
[generated manifest file](../esm-views/output-package-manifest.md), that the
loading code can fetch and examine before loading the `esm-view`. When building
an ESM View, all "local" code (files in `src`) is bundled in a single blob that
can be split into different files. All external dependencies are rewritten to an
external CDN by default and don't get bundled, although this behavior is
[configurable](../configuration.md).

ESM Views are generated by `modular add` using the
[`modular-template-esm-view`](https://github.com/jpmorganchase/modular/tree/main/packages/modular-template-esm-view)
[template](../concepts/templates.md)

## Package

Modular `package`s are generic, publishable libraries with a single entrypoint
file. They are built with [Rollup.js](https://rollupjs.org/guide/en/) and they
are not bundled in a single blob; files required directly or indirectly from the
entry point are separately transpiled as CommonJS Modules and ES Modules
(although it's not a 1:1 correspondence: the compilation process typically
creates additional files to normalize exports). External dependencies are not
included in the compilation output, but they are copied in the output
`package.json` to be eventually consumed by a bundler. Modular calculates the
package entrypoint by looking at the `main` field in the package's
`package.json`; by default, `modular add`ing a new package sets it as
`"./src/index.ts"`, but it's possible to manually modify it. Packages can be
[`test`](../commands/test.md)ed and [`built`](../commands/build.md) by Modular
but, since they don't necessarily export UI functionality, they can't be
[`start`](../commands/start.md)ed. If you need a piece of publishable UI (React)
code that can be previewed locally, you should use `view`s.

Packages are generated by `modular add` using the
[`modular-template-package`](https://github.com/jpmorganchase/modular/tree/main/packages/modular-template-package)
[template](../concepts/templates.md)

### Build output

This is what the build output of a Modular `package` looks like:

```
<modular-root>/dist/<your-package-name>
├── dist-cjs
| ├── index.js
| ├── index.js.map
| ├── index2.js
| └── index2.js.map
├── dist-es
| ├── index.js
| ├── index.js.map
| ├── index2.js
| └── index2.js.map
├── dist-types
| └── index.d.ts
└── package.json
```

Modular transpiles the `package` starting from its entrypoint twice: once with a
target format of [CommonJS](https://nodejs.org/api/modules.html) in the
`dist-cjs` directory and once with a target format of
[ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules),
in the `dist-es` directory. The output `package.json` links both compiled
entrypoints respectively in the
[`main`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#main) and
[`module`](https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md)
field.

Modular also extracts types from the source and outputs them in the `dist-types`
directory, linking them in the
[`typings`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html)
manifest field. Here's an example of an output `package.json` generated when
building a Modular `package`:

```json
{
"name": "<your-package-name>",
"private": false,
"modular": {
"type": "package"
},
"main": "dist-cjs/index.js",
"version": "1.0.0",
"module": "dist-es/index.js",
"typings": "dist-types/index.d.ts",
"dependencies": {},
"files": ["dist-cjs", "dist-es", "dist-types", "README.md"]
}
```

### When to use the "package" type

Modular `package`s are meant to provide re-usable functionality that can be
published to third-party registries (like
[the npm registry](https://www.npmjs.com/)) out-of-the-box; to underline this,
Modular will, for example, refuse to work with packages that have the
[`private`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#private)
field set. You should use packages _only when there is a need to consume their
build output_; if you just need to share some common source code inside your
monorepo but you don't need to publish it externally, you should use the
`source` type instead.

## View

Modular `view`s are Modular `package`s that, by convention, export a default
React component. They are [created](../commands/add.md) with a default
entrypoint is `src/index.tsx` in their `package.json` and they are built and
tested exactly like `package` types. The only difference is that, since `view`s
should always export a React component, they can be
[`start`ed](../commands/start.md) to spawn a local developer server and render
their default export to the DOM.

Views are generated by `modular add` using the
[`modular-template-view`](https://github.com/jpmorganchase/modular/tree/main/packages/modular-template-view)
[template](../concepts/templates.md)

## Source

Modular `source` types are packages that contain only source code in their `src`
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't packages also only contain source code in their src directory? I think the defining feature of the source type is: Modular source packages contain common source code that can be imported from other packages in the monorepo but cannot be built themselves.

Copy link
Contributor Author

@cristiano-belloni cristiano-belloni Jan 6, 2023

Choose a reason for hiding this comment

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

The subject of the adversative seems to refer to the "that" in the relative clause and sounds a bit confusing. What do you think of:

Modular source packages contain common source code that can be imported from
other packages in the monorepo. Source packages are not meant to be built
themselves; they are meant to be imported directly from other packages in the
monorepo.

Copy link
Contributor

Choose a reason for hiding this comment

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

I like it!

directory; they are meant to be imported directly from other packages in the
monorepository and can be [`test`ed](../commands/test.md), but not
[`built`](../commands/build.md) or [`start`ed](../commands/start.md). `source`s
are useful to factor out some common code that's used in a monorepository and
doesn't need to be built separately or published to a registry. `source`s can
also be used to temporarily work around
[circular dependency errors](../concepts/circular-dependencies.md), although
their use in this sense is limited and it's always prefereable to factor out the
common parts of a circular dependency in an additional dependency to break the
cycle.

Sources are generated by `modular add` using the
[`modular-template-source`](https://github.com/jpmorganchase/modular/tree/main/packages/modular-template-source)
[template](../concepts/templates.md)

## Template

The Modular `template` type is a special type that is never directly created by
[`modular add`](../commands/add.md), but it's used by it as a blueprint to
create other packages of various types. An in-depth explanation of templates can
be found [here](../concepts/templates.md). Templates are created from scratch;
benpryke marked this conversation as resolved.
Show resolved Hide resolved
to find out how to set up one, please follow
[this guide](../how-to/create-template.md)
Loading