|
| 1 | +# Compile TypeScript before publishing and adopt tgz-based local development |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +We compile `@openedx/frontend-base` and `@openedx/frontend-app-*` packages to JavaScript (with `.d.ts` declarations) before publishing to npm, and adopt a tarball-based (`npm pack`) workflow as the official mechanism for local development of said packages. As part of this, we remove support for `module.config.js`-based local aliases. |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +### The need for TypeScript compilation |
| 10 | + |
| 11 | +Previously, `@openedx/frontend-base` shipped raw TypeScript source and relied on consumers' bundlers (webpack via `ts-loader`) to transpile it at build time. This worked, but prevented the use of TypeScript path aliases (e.g., `@src/*`) in consuming applications. Path aliases are resolved by `tsc` during compilation, and when a dependency ships raw TypeScript, its own aliases leak into the consumer's build, where they cannot be resolved. By compiling to JavaScript before publishing and using `tsc-alias` to resolve path aliases at that stage, consumers are free to define and use their own aliases independently. |
| 12 | + |
| 13 | +### The problem with module.config.js |
| 14 | + |
| 15 | +`module.config.js` was the previous mechanism for local development of frontend dependencies. It worked by reading a configuration file at webpack build time and creating `resolve.alias` entries to redirect imports to local directories. While functional in simple cases, it had several problems: |
| 16 | + |
| 17 | +1. **Webpack-only**: It only affected webpack's module resolution, so features that depend on Node.js resolution (such as TypeScript path aliases or `tsc`-based compilation) were not supported. |
| 18 | +2. **Brittle dependency resolution**: It attempted to second-guess npm's dependency resolution by manually resolving peer dependencies to the consumer's `node_modules`. This not only side-stepped the deduplication that happens in production, but often broke things entirely with modern `exports` maps. |
| 19 | +3. **Divergence from production**: The aliased resolution paths differed fundamentally from how dependencies resolve in a published package, violating the principle that development should mirror production as closely as possible. |
| 20 | + |
| 21 | +### Alternatives considered |
| 22 | + |
| 23 | +- **`npm link`**: Does not work with the project's TypeScript configuration (`moduleResolution: "bundler"` is incompatible with symlinked packages). |
| 24 | +- **`yarn`, `pnpm`, `bun`**: Switching package managers would entail too many changes across the Open edX ecosystem for the benefit gained. |
| 25 | +- **`yalc`**: A promising tool that wraps `npm pack` with push/watch semantics. It does what we need, but has not been actively maintained (last merge in 2023) and is missing features we would want (e.g., `--peer` flag). It remains a potential future option if it becomes actively maintained again or if we fork it. |
| 26 | + |
| 27 | +### Design principles |
| 28 | + |
| 29 | +The following principles guided this decision: |
| 30 | + |
| 31 | +- **Closeness to production**: The development workflow should mirror production as closely as possible to minimize environment-specific bugs. |
| 32 | +- **Resource optimization**: The workflow should not add unnecessary hoops. Developer time, system resources, and cognitive load should be minimized. |
| 33 | +- **Consistency**: The same development workflow should work uniformly across all packages: `frontend-base` itself, consuming applications like `frontend-app-authn`, third-party packages, and site configuration repositories. |
| 34 | + |
| 35 | +## Decision |
| 36 | + |
| 37 | +1. **Compile TypeScript before publishing**: `@openedx/frontend-base` and all frontend-app-* repositories ship compiled JavaScript with `.d.ts` declaration files, using export maps to define its public API. This enables consuming applications to also pre-compile their TypeScript and use path aliases. |
| 38 | + |
| 39 | +2. **Remove `module.config.js` support**: The `getLocalAliases()` function and all references to `module.config.js` are removed from the webpack configurations and the codebase. |
| 40 | + |
| 41 | +3. **Adopt tarball-based local development**: The official mechanism for local development of Open edX frontend dependencies is the `npm pack` / `.tgz`-based workflow. The developer builds the dependency, packs it into a tarball, and the consuming project installs from that tarball. Tooling to automate this (watching for changes, re-packing, and re-installing) is provided in the `frontend-dev-utils` repository and can be adapted by consuming projects. |
| 42 | + |
| 43 | +## Implementation |
| 44 | + |
| 45 | +### TypeScript compilation |
| 46 | + |
| 47 | +The package is compiled with `tsc` using `tsconfig.build.json`, producing JavaScript output and declaration files under `/dist`. Path aliases are resolved post-compilation with `tsc-alias`. The npm `exports` map in `package.json` maps public entry points to their compiled locations. |
| 48 | + |
| 49 | +At the bundler level, we add `tsconfig-paths-webpack-plugin` to the default webpack configurations so that TypeScript path aliases are also respected by webpack builds (including during `npm run dev`) without duplicating their definitions. |
| 50 | + |
| 51 | +As part of this change, we unify the TypeScript build outputs under `/dist` and use npm export maps to decouple the internal file structure from the package's public API. |
| 52 | + |
| 53 | +### Local development workflow |
| 54 | + |
| 55 | +To develop a local dependency (e.g., `@openedx/frontend-base`) against a consuming project: |
| 56 | + |
| 57 | +1. In the dependency: `npm run build` (or use a watcher like `nodemon` with `npm run pack:local`) |
| 58 | +2. In the consumer: install from the tarball and run the dev server |
| 59 | + |
| 60 | +This approach is consistent across all package types and faithfully reproduces production resolution semantics, since `npm pack` produces the same artifact that `npm publish` would. |
| 61 | + |
| 62 | +The `test-site` project contains reference tooling that automates detecting new tarballs and reinstalling them during development. |
0 commit comments