diff --git a/.changeset/blue-turtles-shake.md b/.changeset/blue-turtles-shake.md new file mode 100644 index 000000000..30e8a22b2 --- /dev/null +++ b/.changeset/blue-turtles-shake.md @@ -0,0 +1,5 @@ +--- +'modular-scripts': major +--- + +Removed commands: convert, init, port, rename diff --git a/.changeset/cyan-flowers-rest.md b/.changeset/cyan-flowers-rest.md new file mode 100644 index 000000000..a5051e1e0 --- /dev/null +++ b/.changeset/cyan-flowers-rest.md @@ -0,0 +1,6 @@ +--- +'modular-scripts': major +--- + +Changed default CDN from Skypack to esm.sh as skypack is no longer actively +maintained. Add support for configuring modular through a configuration file. diff --git a/.changeset/fair-kangaroos-retire.md b/.changeset/fair-kangaroos-retire.md new file mode 100644 index 000000000..c643157a1 --- /dev/null +++ b/.changeset/fair-kangaroos-retire.md @@ -0,0 +1,5 @@ +--- +'modular-scripts': patch +--- + +Test pattern now includes jsx diff --git a/.changeset/fluffy-dragons-behave.md b/.changeset/fluffy-dragons-behave.md new file mode 100644 index 000000000..b280814e3 --- /dev/null +++ b/.changeset/fluffy-dragons-behave.md @@ -0,0 +1,14 @@ +--- +'create-modular-react-app': major +'eslint-config-modular-app': major +'modular-scripts': major +'modular-template-app': patch +'modular-template-esm-view': patch +'modular-template-view': patch +--- + +Added Node 18 engine support +Upgraded Jest from 26 to 29 as 26 wasn't compatible with Node 18 +Upgraded to rollup-plugin-esbuild 5, dropping support for Node 14.17 and below +Supported Node versions now: ^14.18.0 || >=16.10.0 || >=18.0.0 +Changed Jest flag --watchAll default to false (was previously true if running locally and not in CI) \ No newline at end of file diff --git a/.changeset/green-shrimps-build.md b/.changeset/green-shrimps-build.md new file mode 100644 index 000000000..8cd945c16 --- /dev/null +++ b/.changeset/green-shrimps-build.md @@ -0,0 +1,5 @@ +--- +"modular-scripts": major +--- + +Fully selective `modular test` command interface, compatible with `modular build`. diff --git a/.changeset/itchy-dancers-brush.md b/.changeset/itchy-dancers-brush.md new file mode 100644 index 000000000..46bd510d6 --- /dev/null +++ b/.changeset/itchy-dancers-brush.md @@ -0,0 +1,6 @@ +--- +'eslint-config-modular-app': major +'modular-scripts': major +--- + +Updated eslint to ^8.0.0 and minimum supported TypeScript version to 4.5.3 diff --git a/.changeset/light-countries-report.md b/.changeset/light-countries-report.md new file mode 100644 index 000000000..06b07fde2 --- /dev/null +++ b/.changeset/light-countries-report.md @@ -0,0 +1,5 @@ +--- +'create-modular-react-app': major +--- + +Bumped default TypeScript version to ^4.8.3 diff --git a/.changeset/odd-bees-speak.md b/.changeset/odd-bees-speak.md new file mode 100644 index 000000000..dcd3ed008 --- /dev/null +++ b/.changeset/odd-bees-speak.md @@ -0,0 +1,12 @@ +--- +"create-modular-react-app": minor +"modular-scripts": minor +"modular-template-app": minor +"modular-template-esm-view": minor +"modular-template-package": minor +"modular-template-source": minor +"modular-template-view": minor +--- + +Generate README inside newly created packages +Improve root and default workspaces container README diff --git a/.changeset/purple-numbers-camp.md b/.changeset/purple-numbers-camp.md new file mode 100644 index 000000000..e2606a402 --- /dev/null +++ b/.changeset/purple-numbers-camp.md @@ -0,0 +1,5 @@ +--- +'modular-scripts': minor +--- + +App type modular packages are no longer required to be private diff --git a/.changeset/wet-gifts-count.md b/.changeset/wet-gifts-count.md new file mode 100644 index 000000000..56d87dbfb --- /dev/null +++ b/.changeset/wet-gifts-count.md @@ -0,0 +1,5 @@ +--- +'modular-scripts': major +--- + +Sunset modular-site package diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55913143c..2de272ccc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3.3.0 with: - node-version: '14.17.0' + node-version: '14.18.0' registry-url: https://registry.npmjs.org/ cache: 'yarn' diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index ed77bced9..e6fc56d2c 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -29,7 +29,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3.3.0 with: - node-version: '14.17.0' + node-version: '14.18.0' cache: 'yarn' - name: 'Install Dependencies' diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index d3009990a..b19190574 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [windows-latest] - node-version: ['14.17.0', '16.x'] + node-version: ['14.18.0', '16.x', '18.x'] steps: - uses: actions/checkout@v3 @@ -34,4 +34,6 @@ jobs: - name: 'Build internal prerequisites' run: yarn workspace @modular-scripts/workspace-resolver build - name: Run Windows tests - run: yarn test esmView.test.ts workspace-resolver addPackage.test.ts + run: + yarn test --regex esmView.test.ts workspace-resolver + addPackage.test.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a74043966..79498a266 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - node-version: ['14.17.0', '16.x'] + node-version: ['14.18.0', '16.x', '18.x'] steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index d65dfb740..f96f6a30d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,9 @@ yarn-error.log* .tsbuildinfo *.tsbuildinfo +# Local Jekyll preview of docs +docs/_site +docs/.bundle +docs/vendor +docs/Gemfile* + diff --git a/__fixtures__/templates/modular-template-app/README.md b/__fixtures__/templates/modular-template-app/README.md new file mode 100644 index 000000000..80d5cadeb --- /dev/null +++ b/__fixtures__/templates/modular-template-app/README.md @@ -0,0 +1 @@ +# This is a README placeholder for this template fixture diff --git a/__fixtures__/templates/modular-template-filter/README.md b/__fixtures__/templates/modular-template-filter/README.md new file mode 100644 index 000000000..80d5cadeb --- /dev/null +++ b/__fixtures__/templates/modular-template-filter/README.md @@ -0,0 +1 @@ +# This is a README placeholder for this template fixture diff --git a/__fixtures__/templates/modular-template-no-filter/README.md b/__fixtures__/templates/modular-template-no-filter/README.md new file mode 100644 index 000000000..80d5cadeb --- /dev/null +++ b/__fixtures__/templates/modular-template-no-filter/README.md @@ -0,0 +1 @@ +# This is a README placeholder for this template fixture diff --git a/__fixtures__/test-config/.modular.js b/__fixtures__/test-config/.modular.js new file mode 100644 index 000000000..6ae251fac --- /dev/null +++ b/__fixtures__/test-config/.modular.js @@ -0,0 +1,3 @@ +module.exports = { + useModularEsbuild: true, +}; diff --git a/docs/building-apps/index.md b/docs/building-apps/index.md deleted file mode 100644 index 1170418ff..000000000 --- a/docs/building-apps/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -has_children: true -title: Building your Apps -nav_order: 500 ---- - -# Building your Apps diff --git a/docs/commands/add.md b/docs/commands/add.md index 9ea6ed55c..14dd662eb 100644 --- a/docs/commands/add.md +++ b/docs/commands/add.md @@ -3,48 +3,79 @@ parent: Commands title: modular add --- -# `modular add ` +# `modular add [options] [packageName]` -Adds a new package by creating a new workspace at `packages/`, -omitting the scope if the package is +Adds a new package by creating a new package at the workspace located at +`packages/`, omitting the scope if the package is [scoped](https://docs.npmjs.com/cli/v8/using-npm/scope). If `--path ` -is specified, create the workspace at `/`. +is specified, the command creates the workspace at `/`. (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](../package-types/index.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 -- 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`](../package-types/app.md). 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 and public assets in the `public` directory. See + [this page](../package-types/#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`](../package-types/esm-view.md). 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. -- 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 - [start](../commands/start.md)ed by Modular. +### 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`](../package-types/package.md). 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`](../package-types/view.md). 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. + +- [`source`](../package-types/source.md). 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: -`--path`: Optionally set the directory in which the workspace is created. If the -provided path is outside (i.e., not a descendant) of the paths specified in +`--path `: Optionally set the directory in which the workspace is +created. If the provided path is outside (i.e., not a descendant) of the paths +specified in [the `workspaces` field](https://classic.yarnpkg.com/lang/en/docs/workspaces/#toc-how-to-use-it) of the root `package.json`, the command will fail @@ -55,4 +86,4 @@ of the root `package.json`, the command will fail `--template `: Use the package `templateName` from the repository or the registry as a template for the new package. Find more information about -Modular templates [in this page](../concepts/templates.md) +Modular templates [in this page](../package-types/template.md) diff --git a/docs/commands/build.md b/docs/commands/build.md index 174b59897..51c6f9d3d 100644 --- a/docs/commands/build.md +++ b/docs/commands/build.md @@ -3,7 +3,7 @@ parent: Commands title: modular build --- -# `modular build [packages...]` +# `modular build [options] [packages...]` Search workspaces based on their `name` field in the `package.json` and build them according to their respective `modular.type`, in order of dependency (e.g. @@ -13,6 +13,13 @@ The output directory for built artifacts is `dist/`, which has a flat structure of modular package names. Each built app/view/package is added to the `dist/` as its own folder. +When `packages` is empty and no selective options have been specified (for +example when running `yarn modular build`), all packages in the monorepo will be +built. When `packages` contains one or more non-existing package name, the +non-existing packages will be ignored without an error. If any package or +selective option have been defined but the final set of regular expressions is +empty, Modular will write a message to `stdout` and exit with code `0`. + For views and packages, package names are transformed to `Param case` (e.g. this-is-param-case) in `dist/` @@ -25,7 +32,7 @@ this-is-param-case) in `dist/` `--preserve-modules`: Preserve module structure in generated modules. -`--changed`: Build only packages whose workspaces contain files that have +`--changed`: Build only the packages whose workspaces contain files that have changed. Files that have changed are calculated comparing the current state of the repository with the branch specified by `compareBranch` or, if `compareBranch` is not set, with the default git branch. diff --git a/docs/commands/check.md b/docs/commands/check.md index 742f0fa6c..982a8c380 100644 --- a/docs/commands/check.md +++ b/docs/commands/check.md @@ -3,11 +3,13 @@ parent: Commands title: modular check --- -# `modular check` +# `modular check [options]` Checks the modular root repo has yarn workspaces and modular packages are set up properly and checks your package tree for issues with your dependencies. ## Options: -`--fix`: Run autofix over applications. +`--fix`: Run autofix over applications + +`--verbose`: Run yarn commands with the --verbose flag set diff --git a/docs/commands/convert.md b/docs/commands/convert.md deleted file mode 100644 index 6116f7f70..000000000 --- a/docs/commands/convert.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -parent: Commands -title: modular convert ---- - -# `modular convert` - -Converts the react app in the current directory into a modular project with a -modular app workspace. - -This action is `atomic` so if an error occurs while converting, it will stash -any changes made and bring the repo back to the previous state prior to the -attempt. - -- Sets up the current directory as a modular project with a `packages/` - workspaces - -- Moves the current react app source content (`src/` and `public/`) into a - modular app within `packages/` workspace - -- Relocates setupTests file from `src/` to `modular/` - -- Updates the `react-app-env.d.ts` file within the modular app to reference - modular-scripts for types - -- Updates `tsconfig.json` to include the modular packages workspace - -- Removes `react-scripts` as a dependency and installs - eslint-config-modular-app. You can point to it by adding 'modular-app' to the - extends array in your eslint config. diff --git a/docs/commands/init.md b/docs/commands/init.md deleted file mode 100644 index 0999bca2a..000000000 --- a/docs/commands/init.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -parent: Commands -title: modular init ---- - -# `modular init` - -Initializes a modular root type package.json in the current directory with -packages folder set up to add modular packages to. - -## Options: - -`-y`: Equivalent to setting it for `npm init`. Generates an empty npm project -without all of the interactive processes. - -`--prefer-offline`: Uses offline yarn cache when possible - -`--verbose`: Run yarn commands with --verbose set and sets -`MODULAR_LOGGER_DEBUG` to true diff --git a/docs/commands/lint.md b/docs/commands/lint.md index ae41c7be2..e061ba7a2 100644 --- a/docs/commands/lint.md +++ b/docs/commands/lint.md @@ -3,7 +3,7 @@ parent: Commands title: modular lint --- -# `modular lint` +# `modular lint [options] [regexes...]` `modular lint` will check the diff between the current branch and your remote origin default branch (i.e. `master` or `main`) and only lint the `.ts`, `.tsx`, diff --git a/docs/commands/port.md b/docs/commands/port.md deleted file mode 100644 index dba1eddda..000000000 --- a/docs/commands/port.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -parent: Commands -title: modular port ---- - -# `modular port ` - -Takes a relative path from the modular root directory to the targeted -create-react-app project and ports it over to the current modular project as a -modular app. - -``` -$ modular port ../another-react-app -``` - -This action is `atomic` so if an error occurs while porting, it will stash any -changes made and bring the repo back to the previous state prior to the attempt. - -- Creates a new folder in packages workspace, named using your targeted app's - package.json name - -- Moves the `src` and `public` folders into the new workspace - -- If present, updates the `react-app-env.d.ts` file within the new workspace to - reference modular-scripts for types of static assets (e.g. svgs) - -- Creates a tsconfig.json within the new workspace to extend the root - `tsconfig.json` - -- If you do not have a `modular/setupTests` file and the targeted app has a - `src/setupTests` file, it will move it into the `modular` folder to load - before executing `modular test` - -- Resolves dependencies between the two repos. - -## Dependency Resolution - -`modular port` does not set up `nohoist` in `package.json` for mismatched -versions. - -If the targeted app has a `dependency` that is versioned differently than the -modular root dependency, the package@version in modular root will take -precedence. - -If the targeted app has a `devDependency` that is marked as a `dependency` in -modular root, it will not be ported over into the modular app as a -`devDependency` but instead be kept as a dependency in modular root. During this -resolution, if modular root has the package in its dependencies, the version in -modular root will take precedence. - -Given the case that the app you are porting over has a dependency that is a -local package in modular worktree, if the target app's dep has a different -version than the local version, that package would not be symlinked to the local -package at all if brought over directly. It would get its own copy in its -node_modules. -(https://github.com/yarnpkg/yarn/issues/6898#issuecomment-478188695) - -Example: TargetApp's dependency: foo@^1.0.5 - -Modular package foo's local version: 2.0.1 - -TargetApp will have copy of foo@1.0.5 in its workspace node_modules. - -It will be marked as a `mismatchedWorkspaceDependencies` in yarn workspaces. We -do not allow `mismatchedWorkspaceDependencies` in the modular workspace. - -If the targeted app has a `dependency` or `devDependency` of a package that is a -local workspace in your modular repo, we will remove that dependency from the -target app and have it use the local symlinked version instead. diff --git a/docs/commands/rename.md b/docs/commands/rename.md deleted file mode 100644 index 9329b25aa..000000000 --- a/docs/commands/rename.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -parent: Commands -title: modular rename ---- - -# `modular rename ` - -Renames oldPackageName to newPackageName by: - -1. Re-writing the "name" field in the existing package.json to newPackageName -1. Parsing all the sources in all the depending packages and rewriting the - imports to oldPackageName to import newPackageName - -This action is `atomic`: if an error occurs while converting, it will stash any -changes made and bring the repo back to the previous state prior to the attempt. - -Please note that the directory containing oldPackageName is _not_ renamed, -because the link between directory name and package name is not unambiguous: for -example, nested or scoped packages created with modular get a nested path that -is not easily unambiguosly invertible. The user will need to rename the -directory if needed. - -## Options: - -`--verbose`: Shows debug information diff --git a/docs/commands/serve.md b/docs/commands/serve.md new file mode 100644 index 000000000..e625fa776 --- /dev/null +++ b/docs/commands/serve.md @@ -0,0 +1,17 @@ +--- +parent: Commands +title: modular serve +--- + +# `modular serve [options] ` + +Start a local HTTP server to serve an already-[built](./build.md) +[application](../package-types/app.md) or +[ESM View](../package-types/esm-view.md). This is different from +[start](./start.md) in that it statically serves an already-built `app` or +`esm-view` directly from `dist/` without injecting any run-time script. +Use it to preview the result of a build with optimized bundles and minification. + +## Options + +`--port`: Select the port to serve on (default: '3000') diff --git a/docs/commands/start.md b/docs/commands/start.md index 83e85cd6e..dd91f234c 100644 --- a/docs/commands/start.md +++ b/docs/commands/start.md @@ -3,7 +3,7 @@ parent: Commands title: modular start --- -# `modular start ` +# `modular start [options] ` Runs [`react-scripts start`](https://create-react-app.dev/docs/getting-started#npm-start-or-yarn-start) @@ -15,3 +15,7 @@ what modular views are initialized as). Modular will import this view as a module within a template app, which we stage in a `node_modules/.modular` folder. You can develop your view as you normally would an app and it will automatically re-compile as you make changes in the view package. + +## Options: + +`--verbose`: Run yarn commands with the --verbose flag set diff --git a/docs/commands/test.md b/docs/commands/test.md index b18abdfa6..b780ecafb 100644 --- a/docs/commands/test.md +++ b/docs/commands/test.md @@ -3,7 +3,7 @@ parent: Commands title: modular test --- -# `modular test` +# `modular test [options] [packages...]` `test` is an opinionated wrapper around [`jest`](https://jestjs.io/) which runs tests against the entire `modular` project. It comes with out-of-the-box @@ -21,51 +21,51 @@ This contains the setup for tests corresponding to This contains the setup for tests corresponding to [`jest.config.js#setupFilesAfterEnv`](https://jestjs.io/docs/en/configuration#setupfilesafterenv-array). -## Command line options +## Command line options and arguments -### Modular-specific options - -#### ancestors - -Default: `false` - -Can be used only in combination with `changed` or the command will fail. If set, -it will additionally execute tests for all the workspaces that (directly or -indirectly) depend on the workspaces selected by `changed`. +### Arguments -#### changed +`[packages ...]`: List of packages to test. Can be combined with multiple +selective options (`--ancestors`, `--descendants`, `--changed` and `--regex`). +Modular will generate a list of regular expressions that satisfies all options +passed which are then passed to Jest. The tests will run in non-predictable +order. When `packages` is empty and no selective options have been specified +(for example when running `yarn modular test`), all tests in the monorepo will +be executed. When `packages` contains one or more non-existing package name, the +non-existing packages will be ignored without an error. If any package or +selective option have been defined but the final set of regular expressions is +empty, Modular will write a message to `stdout` and exit with code `0`. -Default: `false` +### Modular-specific options -Execute tests only for the workspaces that contain files that have changed. -Files that have changed are calculated comparing the current state of the -repository with the branch specified by `compareBranch` or, if `compareBranch` -is not set, with the default git branch. +`--ancestors`: Take the packages specified by the user via arguments or options +and add their ancestors (i.e. the packages that have a direct or indirect +dependency on them) to the test list. -#### debug +`--descendants`: Take the packages specified by the user via arguments or +options and add their descendants (i.e. the packages they directly or indirectly +depend on) to the test list. -Default: `false` +`--changed`: Take the packages specified by the user via arguments or options +and add all the packages whose workspaces contain files that have changed, +calculated comparing the current state of the git repository with the branch +specified by `compareBranch` or, if `compareBranch` is not set, with the default +branch. -Add the `--inspect-brk` option to the Node.js process executing Jest to allow a -debugger to be attached to the running process. For more information, +`--debug`: Add the `--inspect-brk` option to the Node.js process executing Jest +to allow a debugger to be attached to the running process. For more information, [see the Node.js debugging guide](https://nodejs.org/en/docs/guides/debugging-getting-started/). -#### compareBranch - -Default: `undefined` - -Specify the comparison branch used to determine which files have changed when -using the `changed` option. If this option is used without `changed`, the -command will fail. - -#### package +`--compareBranch `: Specify the comparison branch used to determine +which files have changed when using the `changed` option. If this option is used +without `changed`, the command will fail. -Default: `undefined` +`--regex `: Select all the test files matching the specified regular +expressions. Can be combined with all the other selective options. -Run all the tests for the workspace with the specified package name. Can be -repeated to select more than one workspace. Can be combined with the -`--ancestors` option to test the specified workspace(s) plus all the workspaces -that, directly or indirectly, depend on them. Conflicts with `--changed`. +`--verbose`: Activate debug logging. Useful to see which packages have been +selected and which regular expression and arguments have been passed to the +underlying Jest process. ### Jest CLI Options diff --git a/docs/commands/typecheck.md b/docs/commands/typecheck.md index 83c792576..c579c7171 100644 --- a/docs/commands/typecheck.md +++ b/docs/commands/typecheck.md @@ -3,7 +3,7 @@ parent: Commands title: modular typecheck --- -# `modular typecheck` +# `modular typecheck [options]` `modular typecheck` will programmatically report the semantic, syntactic, and declaration type errors found in your code, based on your tsconfig.json. @@ -12,3 +12,7 @@ In a CI environment, it will print condensed errors if they are present. In non-CI environments, it will print the full details of the error, line, and small snapshot of the offending line in question. + +## Options: + +`--verbose`: Enables verbose logging within modular diff --git a/docs/compatibility.md b/docs/compatibility.md new file mode 100644 index 000000000..b055d4897 --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,34 @@ +--- +nav_order: 800 +--- + +# Compatibility + +## Package managers + +Modular is based on +[Yarn Workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/). It uses +the `yarnpkg` command under the hood. At the moment, Modular is developed and +tested with [Yarn Classic (v1)](https://classic.yarnpkg.com). Other versions of +Yarn have different levels of support: + +- Yarn v2 is not supported by Modular, due to + [PnP](https://yarnpkg.com/features/pnp). +- Yarn v3 is supported partially; we are actively trying to support more and + more features of Yarn v3, but at the moment some features, such as nested + worktrees, are not supported. If you find any example of v3 features which + cause Modular to fail, please + [let us know](https://github.com/jpmorganchase/modular/issues). +- Future versions of Yarn are not supported at the moment. + +## Node versions + +Modular is tested on the latest three +[Long Term Support versions of Node.js (v14, v16 and v18)](https://github.com/nodejs/release#release-schedule). +Node.js v14 is supported from version `14.18.0` onwards, while Node 16 is +supported from version `16.10.0` onwards. + +## Platforms + +Modular is tested on `ubuntu-latest` and `windows-latest` +[Github Hosted Runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). diff --git a/docs/concepts/circular-dependencies.md b/docs/concepts/circular-dependencies.md index f85a9e19a..764354b73 100644 --- a/docs/concepts/circular-dependencies.md +++ b/docs/concepts/circular-dependencies.md @@ -1,6 +1,7 @@ --- title: Circular Dependencies parent: Concepts +nav_order: 200 --- # Circular dependencies and Modular @@ -21,7 +22,7 @@ refactoring fragile: if a part of A depends on a part of B and an unrelated part of B depends on an unrelated part of A, refactoring code in A can make the require order in B fail and vice versa. -## `--dangerouslyIgnoreCircularDependencies` escape hatch +## Escape hatch: `--dangerouslyIgnoreCircularDependencies` If your circular dependencies involve packages that never get built (namely, `modular.type: source` packages), Modular can still calculate the correct build @@ -36,12 +37,12 @@ production and always split you dependencies to avoid cycles. ## Examples -### Cycle disappearing when `source` types are removed from the dependency graph (cycle between `package` and `source`) +### Cycle disappearing when `source` types are removed from the dependency graph (assuming that `package` b is depending on `source` c and `source` c is depending on `package` b and `package` d): -#### Without `--dangerouslyIgnoreCircularDependencies` the build fails +Without `--dangerouslyIgnoreCircularDependencies` the build fails: ```bash > modular-dev build b --descendants @@ -49,7 +50,7 @@ depending on `package` b and `package` d): [modular] Cycle detected, b -> c -> b ``` -#### With `--dangerouslyIgnoreCircularDependencies` the build warns but succeeds +With `--dangerouslyIgnoreCircularDependencies` the build warns but succeeds: ```bash > modular-dev build b --descendants --dangerouslyIgnoreCircularDependencies @@ -63,12 +64,12 @@ depending on `package` b and `package` d): [modular] $ d: built d in /Users/N761472/dev/rig/ghost-building/dist/d ``` -### Cycle not disappearing when `source` types are removed from the dependency graph (cycle between `package`s) +### Cycle not disappearing when `source` types are removed from the dependency graph (assuming that `package` b is depending on `package` c and `package` c is depending on `package` b and `package` d): -#### Even with `--dangerouslyIgnoreCircularDependencies` the build fails: +Even with `--dangerouslyIgnoreCircularDependencies` the build fails: ```bash > modular-dev build b --descendants --dangerouslyIgnoreCircularDependencies diff --git a/docs/concepts/linting.md b/docs/concepts/linting.md index 8e06dec11..2773757b7 100644 --- a/docs/concepts/linting.md +++ b/docs/concepts/linting.md @@ -1,5 +1,6 @@ --- parent: Concepts +nav_order: 300 --- # Linting diff --git a/docs/concepts/microfrontends.md b/docs/concepts/microfrontends.md index 250829943..c09dde85e 100644 --- a/docs/concepts/microfrontends.md +++ b/docs/concepts/microfrontends.md @@ -1,7 +1,11 @@ --- +title: Micro-frontends +nav_order: 1 parent: Concepts --- +# Micro-frontends + ## ESM micro frontends in Modular Micro frontends are a pattern in which discrete UIs (frontends) are composed of diff --git a/docs/concepts/philosophy.md b/docs/concepts/philosophy.md deleted file mode 100644 index d93e08e6d..000000000 --- a/docs/concepts/philosophy.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Philosophy -nav_order: 1 -parent: Concepts ---- - -`modular` believes the burden to learn a "Framework" with proprietary APIs in a -rapidly evolving landscape is an inhibitor to **scaled web engineering**. - -There is already a very strong set of **Language Constructs, Frameworks and -Tooling** that the front end community is rallying around like `TypeScript`, -`ES6 Modules`, `React`, `Parcel`, `Webpack`, `GitHub Actions`, `Jest`, -`Workspaces` etc. - -Scaled Engineering requires a few more Frameworks, Libraries and Tools that are -not yet first class citizens in the world of Front End Engineering like -**Universal Data Fetching**, **Feature Flags**, **Analytics Capture**, -**Security**, **Deployment** etc. - -`modular` attempts to bring the best Language Constructs, Libraries, Frameworks -and Tooling together to establish a set of patterns and definitions to enable -**Monorepo** based engineering. - -## Convention over configuration - -The goal of `modular` is to decrease the number of decisions the programmer has -to make and eliminate the complexity of having to configure all and each of the -areas of application development. The immediate result is that you can create -many more things in less time. - -`modular` is designed to make you focus on the things that are critical to -delivering features in your application by making a set of **pragmatic** -configuration choices - this includes things like testing configuration, file -location and repository structure. - -The problem with many of the tools in the front end landscape is they encourage -every team, repository, and person to make a configuration choice. This works -great for open source software because tools can appeal to a broad range of -developers, but for businesses this means that teams often need dedicated people -to manage this configuration and apply decisions to teams. - -This is the reasont that modular doesn't expose any internal configuration to -end-users, apart from repository agnostic choices, or options required for -_integration_. diff --git a/docs/concepts/supported-cra.md b/docs/concepts/supported-cra.md new file mode 100644 index 000000000..9d4b845d5 --- /dev/null +++ b/docs/concepts/supported-cra.md @@ -0,0 +1,55 @@ +--- +title: Supported CRA features +parent: Concepts +nav_order: 10 +--- + +# Supported CRA Features + +Modular [apps](../package-types/app.md) and +[ESM Views](../package-types/esm-view.md) are compatible with a subset of +features from [Create React App](https://create-react-app.dev): + +## Templates + +[Template support](../package-types/template.md) is integrated in the +[`modular add`](../commands/add.md) command. Although Modular templates work in +a similar way to Create React App templates, they implement templating in a +different context (different types of packages that live in the same repository) +and they are not compatible with CRA templates. + +## Proxies + +[Local development proxies](https://create-react-app.dev/docs/proxying-api-requests-in-development/) +are supported, but [esbuild mode](../configuration.md#usemodularesbuild) 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. + +## Importing CSS / CSS Modules + +Importing CSS stylesheets in Modular apps and ESM Views works in the same way as +[in Create React App](https://create-react-app.dev/docs/adding-a-stylesheet). + +[CSS Modules](https://github.com/css-modules/css-modules) +[are also supported](https://create-react-app.dev/docs/adding-a-css-modules-stylesheet), +but only in Webpack mode - imported classes are always `undefined` in +[esbuild mode](../configuration.md/#usemodularesbuild). + +All stylesheets are +[autoprefixed](https://create-react-app.dev/docs/post-processing-css) by +default. + +## Importing assets + +Importing assets in source files is supported exactly as +[in Create React App](https://create-react-app.dev/docs/adding-a-stylesheet). +[Assets in the public folder](https://create-react-app.dev/docs/using-the-public-folder) +are supported in Modular applications, but not in Modular ESM Views. + +## Environment variables + +Injection of `NODE_ENV` and environment variables starting with `REACT_APP` at +build-time, support for `.env` files, and injection of environment variables in +the `index.html` file (only for Modular Apps) work exactly as +[in Create React App](https://create-react-app.dev/docs/adding-custom-environment-variables). diff --git a/docs/concepts/versioning.md b/docs/concepts/versioning.md index 99bcdc674..199d5fa36 100644 --- a/docs/concepts/versioning.md +++ b/docs/concepts/versioning.md @@ -1,22 +1,16 @@ --- title: Versioning Packages parent: Concepts +nav_order: 400 --- # Version Control in a Modular Repository -Modular's primary focus is on the build & testing of your packages and -applications, it's not concerned with how you run CI or version the artefacts -built by it. +Modular's primary objective is to provide frictionless +[build](../commands/build.md) and [test](../commands/test.md) functionality for +your micro-frontend monorepo. -There's a multitude of packages and tools out there which you can use to version -the packages within your repository - some of the biggest of these are - -- [`changesets`](https://github.com/atlassian/changesets) - a tool written by - Atlassian which applies changeset files to generate the new versions of - packages. This is what modular uses internally for managing package versions - when publishing. - -- [`lerna`](https://github.com/lerna/lerna/) - another open source tool designed - for monorepository management. Lerna is more abstract than changesets when it - comes to determining version increments but is still easy to use. +How you version the built artifacts and run your CI pipelines is up to you, but +we recommend [`changesets`](https://github.com/atlassian/changesets), a tool +written by Atlassian which uses changeset files to generate new versions of +packages. diff --git a/docs/concepts/views.md b/docs/concepts/views.md deleted file mode 100644 index 8acb1a791..000000000 --- a/docs/concepts/views.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -parent: Concepts ---- - -## Views - -User Interfaces are made of components. When you sketch a wireframe on a -whiteboard or graphics tool of choice, the rectangles that you draw define the -visual and semantic boundaries of these components. They usually correspond to -some equivalent in your UI framework (e.g - In -[React](https://reactjs.org/docs/components-and-props.html), these are defined -as classes that extend from `React.Component`, or regular functions that return -JSX. In [Flutter](https://flutter.dev/docs/development/ui/widgets), these are -classes that extend from different types of Views. In -[Jetpack Compose](https://developer.android.com/jetpack#foundation-components), -these are called Composable Functions). These are a neat unit of encapsulating -state, behaviour and presentation. They also _compose_ with each other to -provide higher abstraction of UI, eventually forming the application itself. - -Every application also has a concept of 'primary' components. These are -'top-level' components that are considered special, and most important when -describing the high level architecture of an application. - -For example, a site that operates as a blog will probably have these primary -components - ``, ``, ``, -``. These would associate with routes like `/`, -`/posts?offset=20&length=10`, `/post/:id/:slug`, `/contact`. Further, `` -may just be an alias for `` with a default query. It all depends on -whoever's implementing the site, of course. Each of these components would be -composed of a number of secondary components (and could also be sharing these -components among themselves). - -An e-commerce site would have different primary component, ``, -``, `
`, ``, ``, -and ``. Like the above, it could also have routing patterns -for each of these components, and could be composed by a number of secondary -components. - -For applications that behave like dashboards, we still have the concept of -primary components; there will be a host/container component (let's say we call -it ``), but pages will be composed of a number of primary components -(which could be charts, graphs, tables, lists; whatever developers and product -managers feel is a window into a data slice relevant to a user). Routes are then -used to show different user-generated dashboards composed of these primary -components (the layouts for which are probably stored on a database somewhere), -or for drilling down into a particular component to expose and interact with -more data. As before, these primary components are composed of a number of -secondary components, possibly sharing low level components amongst themselves. - -In `modular`, these primary components are what we call 'views'. - -As these sites scale through time, we notice some patterns emerge in the -development of views. They'll usually start as single files, usually all under -one main folder. As each page gains more functionality, they'll become folders, -with supporting components/models/functions split into files in that folder. -They'll also start getting more developers attached to each primary component; -full teams and roadmaps that become part of a broader plan for the -site/application. Each of these views will start managing their own specific -dependencies, and they'll decoupled from the main application in one of a number -of ways (i.e. microfrontends, or as separate workspaces, or separate packages, -or repositories that publish assets to a cdn, and so on.) Managing the growth of -these components and associated development practices and architecture then -becomes key to being able to iterate features safely, quickly, and reliably. - -`modular` suggests 3 strategies for managing the growth of these components. - -**Strategy 0**: Try to keep the codebase as small as possible, for as long as -possible. - -The best kind of scale is no scale at all. As such, it would introduce -unnecessary complexity to a codebase if it takes on scaling strategies when only -1 (or few) developers are working on it, only for a little time, and for very -low stakes. In such a situation it would be a mistake to build a completely -decomposed architecture of React components with a design system, split into -multiple parts and files, when instead it could have been built with a plain -html file and an accompanying css file. If this is your situation, you do not -need `modular`, and you should invest your time and effort into something that -has better returns. If you do need a javascript framework, try to keep it in one -file. Split into multiple files only when it's painful. Only when you've -exhausted these basic scaling options, should you move on to the next option... - -**Strategy 1**: Move development of the view to a -[workspace](https://classic.yarnpkg.com/en/docs/workspaces/). - -If keeping each primary component in a sub-folder is causing growing pains in -the codebase, move it to a workspace. Workspaces are a great option, because -they provide some benefits of decoupling from the main application, without -losing the benefits of colocating within the same repository. Of note: - -- Teams will be able to declare and manage their own dependencies instead of it - all being crammed into a central package.json - -- These components could (theoretically) be used in other applications, since - they could be published from this workspace as a package (we try to avoid - this. I should probably remove this point altogether.) - -- A sense of ownership for the team; they can define their own - [CODEOWNERS](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners) - and code review flow, and most of their PRs and changes won't affect the - working of the rest of the application. - -- However, since they're all under one typescript project, they still won't - break expectations (or if they do, the static type system will catch it and - make you fix it to proceed with a commit/deploy) - -- Similarly, tests will run for the entire repository, and assuming there's some - form of integration/e2e coverage, it'll be hard to break expectations despite - working in this isolated manner. - -- It is relatively easy to make upgrades in this system; a core dependency can - be updated, types and tests checked across the system, and changes committed - in an atomic fashion. Something that would take weeks (if not months) in a - distributed repo system will take a day or so (if not hours) in this - centralised system. - -In a repository generated by `modular`, you can add a view by running -`modular add `, and choosing the option to create a View. This creates a -new workspace with the view name, that can be imported from the application, or -any of the other workspaces, but still will be included in the main build, and -participates in the type/test infrastructure. Since they're still regular React -components, you can wrap them with -[React.lazy](https://reactjs.org/docs/code-splitting.html#reactlazy) and use -them as regular components, but they won't be included in the main bundle, and -will load dynamically on demand. This is dope. - -Now, with the dashboard usecase, there's usually never explicit code that -renders a specific view; 'layouts' are loaded from a database/service. To make -this system work well with `modular`, `modular` has a special module -[`modular-views.macro`](../packages/modular-views.macro/README.md); this module -exports an object that maps every view (wrapped with `React.lazy`) to a string -identifier (i.e. - the name of the view) - -(question: do we want to use some other identifier? this seems sufficient for -now, but something to keep an eye on.) - -This gives a scalable local registry of all the views/primary component, and -it's never necessary to manually update this map since it's defined based on the -state of the filesystem. This then becomes our primary system of 'dynamically' -loading and rendering views onto a rendering surface; we leverage and build on -regular javascript/React semantics instead of inventing something bespoke. This -is the power of colocating code in the same repository and scaling -infrastructure and tooling around it. - -NB: It could be that someone has done a -[sparse checkout](https://gist.github.com/threepointone/d62b4d92a1e92df5f2f4d2d91a0582cd) -of the repository and not included all the views in their local instance; no -problem, the map excludes those views from the map, and when rendered you can -use a generic placeholder component. - -There are some cons to this system, and it's important to note that we're -trading one set of problems for these. However, these problems are a better set -of problems to have, have a lot of historical research and precedence and thus -aren't unique problems, and can be solved incrementally. - -- Builds might become fairly big: In most cases, as long as you're not building - a systems with many (read: 100s or 1000s of views), the build for such a - system shouldn't be a problem. However, if you are building at that big a - scale, you can expect the requirements for build infrastructure to increase - proportionally. In that scenario, there are 3 'solutions': - - - Invest in having beefier hardware for doing builds (instead of a farm of - weaker machines). This will give you some breathing space and time to fix - root problems. - - - Invest in better tooling infrastructure; publicly available tools like - webpack/parcel weren't built to handle that kind of scale. Consider adopting - guidelines from - [webpack's performance recommendations](https://webpack.js.org/guides/build-performance/). - We are also aware that these tools are considering scale as a first class - feature for newer versions, introducing features like module federation, - persistent caching, incremental builds, and so on. - - - At this scale, it's not sufficient to simply follow public guidance, it'll - probably be necessary to hire and invest in teams whose sole purpose is to - solve these problems, much like big corps like facebook, google, etc do. - `modular`is also working on providing these optimisations by default. - -- Developer Workflow: Your teams may not be used to working in a single - repository, and as such, may not have the tooling or guidance to do so. Some - things that may make this better: - - - setting up - [CODEOWNERS](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners) - so every developer is not spammed with notifications about every PR, and - only relevant people are targeted as reviewers. - - - Using a - [feature flag service](https://gist.github.com/threepointone/2c2fae0622681284410ec9edcc6acf9e) - to simplify uat/deploy workflows - - - using - [sparse checkouts](https://gist.github.com/threepointone/d62b4d92a1e92df5f2f4d2d91a0582cd) - to only work on the part of the codebase that's relevant to a developer - - - using a configuration service / key management service for holding and - managing private/public keys used by the system - -- Fighting [Conway's law](https://en.wikipedia.org/wiki/Conway%27s_law) (There's - a whole spiel here about fighting company org structures that we'll write - about some day) - -We DO NOT recommend pulling your view into a another repository. There are -serious costs associated with this (TODO, critical: enumerate all the problems -with pulling into a separate repo / the multi repository architecture). However, -you may be dealing with a legacy/preexisting system where views/primary -components are defined and built in separate repositories, or some bespoke -component registry/loading system. We present a couple of strategies to -interface with those views: - -**Strategy 2**: Wrap with a React component that establishes an interface -between your application and the component's expectations. - -The idea here is to write a component that takes props that define how to load -and interface with a fully decoupled component. For example, you may have a -registry of components that are defined by a urls that are to be used as an -iframe src, and have a postmessage based api to communicate across the iframe -boundary. The wrapper component would then look something like this - - -```jsx -function IframeWrap({ src }) { - const iframeChannel = useContext(IframeWrapContext); - - const ref = useRef(null); - - useEffect(() => { - // setup communication to send/receive data on iframeChannel - - return () => { - // cleanup listeners - }; - }); - - return