You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: support npm workspaces for local development
Decouple clean from build in the Makefile so that watch mode can
rebuild without wiping dist/. Add nodemon.json and watch:build,
watch:docs, watch:pack scripts to standardize file watching.
Part of #184
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/how_tos/migrate-frontend-app.md
+78-3Lines changed: 78 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -134,7 +134,7 @@ With the exception of any custom scripts, replace the `scripts` section of your
134
134
"i18n_extract": "openedx formatjs extract",
135
135
"lint": "openedx lint .",
136
136
"lint:fix": "openedx lint --fix .",
137
-
"prepack": "npm run build",
137
+
"prepack": "npm run clean && npm run build",
138
138
"snapshot": "openedx test --updateSnapshot",
139
139
"test": "openedx test --coverage --passWithNoTests"
140
140
},
@@ -156,13 +156,15 @@ Also:
156
156
> **Why change `fedx-scripts` to `openedx`?**
157
157
> A few reasons. One, the Open edX project shouldn't be using the name of an internal community of practice at edX for its frontend tooling. Two, some dependencies of your MFE invariably still use frontend-build for their own build needs. This means that they already installed `fedx-scripts` into your `node_modules/.bin` folder. Only one version can be in there, so we need a new name. Seemed like a great time for a naming refresh. |
158
158
159
-
Last but not least, add `clean:` and `build:` targets to your `Makefile`. The build target compiles TypeScript to JavaScript, uses `tsc-alias` to rewrite `@src` path aliases to relative paths, and copies all SCSS files from `src/` into `dist/` preserving directory structure:
159
+
Last but not least, add `clean:` and `build:` targets to your `Makefile`. The build target compiles TypeScript to JavaScript, uses `tsc-alias` to rewrite `@src` path aliases to relative paths, and copies all SCSS files from `src/` into `dist/` preserving directory structure.
160
+
161
+
Note that `build` intentionally does *not* depend on `clean`. This allows incremental rebuilds during development (especially in workspace mode, where a watcher triggers `build` on every change). The `prepack` script in `package.json` runs `clean && build` explicitly, so published packages always start fresh.
160
162
161
163
```makefile
162
164
clean:
163
165
rm -rf dist
164
166
165
-
build: clean
167
+
build:
166
168
tsc --project tsconfig.build.json
167
169
tsc-alias -p tsconfig.build.json
168
170
find src -type f -name '*.scss' -exec sh -c '\
@@ -248,6 +250,7 @@ node_modules
248
250
npm-debug.log
249
251
coverage
250
252
dist/
253
+
packages/
251
254
/*.tgz
252
255
253
256
### i18n ###
@@ -952,3 +955,75 @@ Refactor slots
952
955
First, rename `src/plugin-slots`, if it exists, to `src/slots`. Modify imports and documentation across the codebase accordingly.
953
956
954
957
Next, the frontend-base equivalent to `<PluginSlot />` is `<Slot />`, and has a different API. This includes a change in the slot ID, according to the [new slot naming ADR](../decisions/0009-slot-naming-and-lifecycle.rst) in this repository. Rename them accordingly. You can refer to the `src/shell/dev` in this repository for examples.
958
+
959
+
960
+
Set up npm workspaces for local development
961
+
===========================================
962
+
963
+
Frontend apps support `npm workspaces <https://docs.npmjs.com/cli/using-npm/workspaces>`_ so that developers can work on the app and its dependencies (such as ``frontend-base``) simultaneously, with changes reflected automatically.
964
+
965
+
Add the workspaces field to package.json
966
+
-----------------------------------------
967
+
968
+
```diff
969
+
+ "workspaces": [
970
+
+ "packages/*"
971
+
+ ],
972
+
```
973
+
974
+
This tells npm to look in ``packages/`` for local overrides of published packages. The ``packages/`` directory is gitignored (see the `.gitignore` step above), since it contains development-only bind-mounted checkouts.
975
+
976
+
Add a nodemon.json file
977
+
------------------------
978
+
979
+
Create a ``nodemon.json`` at the repository root. This configures the ``watch:build`` script to rebuild automatically when source files change:
980
+
981
+
```json
982
+
{
983
+
"watch": [
984
+
"src"
985
+
],
986
+
"ext": "js,jsx,ts,tsx,scss"
987
+
}
988
+
```
989
+
990
+
Add workspace-aware scripts
991
+
----------------------------
992
+
993
+
Install ``concurrently`` and ``nodemon`` as dev dependencies:
994
+
995
+
```sh
996
+
npm install --save-dev concurrently nodemon
997
+
```
998
+
999
+
Then add the following scripts to ``package.json``:
1000
+
1001
+
```json
1002
+
"build:packages": "npm run build --workspaces --if-present",
1003
+
"dev:packages": "npm run build:packages && concurrently 'npm run watch:build --workspaces --if-present' 'npm run dev'",
1004
+
"watch:build": "nodemon --exec 'npm run build'",
1005
+
```
1006
+
1007
+
-``watch:build`` uses ``nodemon`` to watch for source changes (as configured in ``nodemon.json``) and re-runs ``npm run build`` on each change.
1008
+
-``build:packages`` runs ``build`` in every workspace package that has the script (i.e., ``frontend-base``), then in the app itself. This is a one-shot build with no watching.
1009
+
-``dev:packages`` does an initial ``build:packages``, then concurrently watches all workspace packages for changes and starts the dev server.
1010
+
1011
+
Using workspaces
1012
+
-----------------
1013
+
1014
+
To develop against a local ``frontend-base``:
1015
+
1016
+
```sh
1017
+
mkdir -p packages/frontend-base
1018
+
sudo mount --bind /path/to/frontend-base packages/frontend-base
1019
+
npm install
1020
+
npm run dev:packages
1021
+
```
1022
+
1023
+
Bind mounts are used instead of symlinks because Node.js resolves symlinks to real paths, which breaks hoisted dependency resolution. Docker volume mounts work equally well (and are what ``tutor dev`` uses).
0 commit comments