Skip to content

feat!: compile to JS before publishing#164

Merged
arbrandes merged 3 commits intoopenedx:mainfrom
arbrandes:compile-before-publishing
Feb 11, 2026
Merged

feat!: compile to JS before publishing#164
arbrandes merged 3 commits intoopenedx:mainfrom
arbrandes:compile-before-publishing

Conversation

@arbrandes
Copy link
Contributor

@arbrandes arbrandes commented Feb 6, 2026

Description

Ship compiled JavaScript + .d.ts declarations instead of raw TypeScript source. This allows consuming apps to also pre-compile their TypeScript and use tsc-alias to resolve arbitrary paths (such as @src).

At the bundler level, we add tsconfig-paths-webpack-plugin to Webpack configuration so that the Typescript aliases can also be used by Webpack builds without duplicating their definition.

As part of this, unify the tsc builds so that everything ends up in /dist, and then use more modern export maps to decouple the internal file structure from the package's API.

Of note, the default tsconfig (not the one used by tools) now uses moduleResolution: bundler to allow for a more modern entrypoint definition.

BREAKING CHANGE

Consuming apps need to modify their imports and configuration accordingly. See the changes in the migration howto for details.

Downstream PRs

Testing

Check the frontend-template-site PR for instructions.

LLM Usage Notice

Built with assistance from Claude Opus 4.6.

@arbrandes arbrandes marked this pull request as draft February 6, 2026 02:10
@arbrandes arbrandes force-pushed the compile-before-publishing branch 2 times, most recently from ba98344 to 1a10c88 Compare February 6, 2026 02:30
@arbrandes arbrandes changed the title compile before publishing feat!: compile before publishing Feb 6, 2026
@arbrandes arbrandes changed the title feat!: compile before publishing feat!: compile to JS before publishing Feb 6, 2026
@arbrandes arbrandes force-pushed the compile-before-publishing branch 3 times, most recently from 7f44c4e to a008b3b Compare February 6, 2026 14:36
@arbrandes arbrandes force-pushed the compile-before-publishing branch 5 times, most recently from 845ae45 to c95035b Compare February 9, 2026 20:42
Ship compiled JavaScript + .d.ts declarations instead of raw TypeScript
source. This allows consuming apps to also pre-compile their TypeScript
and use tsc-alias to resolve arbitrary paths (such as @src).

At the bundler level, we add tsconfig-paths-webpack-plugin to Webpack
configuration so that the Typescript aliases can also be used by Webpack
builds without duplicating their definition.

As part of this, unify the tsc builds so that everything ends up in
/dist, and then use more modern export maps to decouple the internal
file structure from the package's API.

Of note, the default tsconfig (not the one used by tools) now uses
`moduleResolution: bundler` to allow for more modern entrypoint
definition.

BREAKING CHANGE: consuming apps need to modify their imports and
configuration accordingly.  See the changes in the migration howto for
details.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@arbrandes arbrandes force-pushed the compile-before-publishing branch from c95035b to 63d57cb Compare February 10, 2026 15:08
@arbrandes arbrandes marked this pull request as ready for review February 10, 2026 15:09
@arbrandes arbrandes force-pushed the compile-before-publishing branch from 475e884 to cadf121 Compare February 10, 2026 16:33
Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

Overall this looks great! Couple small comments, one bigger question but mostly nothing major!

Thank you so much for digging into this! Looks like handling @src turned into quite the quest!


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.

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth mentioning we have tooling to aid in this workflow (detailed in the implementation section)?

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. (While converting to rst: this repo is a bit of a mess in that some ADRs are md, others rst. I believe our standard is rst.)

To develop a local dependency (e.g., `@openedx/frontend-base`) against a consuming project:

1. In the dependency: `npm run build` (or use a watcher like `nodemon` with `npm run pack:local`)
2. In the consumer: install from the tarball and run the dev server
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth mentioning the autoinstall tool in frontend-dev-utils here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think it's worth mentioning in the implementation, for sure. I'll add it in.

Comment on lines +147 to +161
Then add a `build:` target to your `Makefile`:

```makefile
build:
tsc --project tsconfig.build.json
tsc-alias -p tsconfig.build.json
find src -type f -name '*.scss' -exec sh -c '\
for f in "$$@"; do \
d="dist/$${f#src/}"; \
mkdir -p "$$(dirname "$$d")"; \
cp "$$f" "$$d"; \
done' sh {} +
```

This 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: reading the makefile target before the description made me jump into "try to understand what it's doing" mode. I'd prefer the description come first (totally optional change here).

Suggested change
Then add a `build:` target to your `Makefile`:
```makefile
build:
tsc --project tsconfig.build.json
tsc-alias -p tsconfig.build.json
find src -type f -name '*.scss' -exec sh -c '\
for f in "$$@"; do \
d="dist/$${f#src/}"; \
mkdir -p "$$(dirname "$$d")"; \
cp "$$f" "$$d"; \
done' sh {} +
```
This 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.
Then add a `build:` target to your `Makefile` that:
* Compiles TypeScript to JavaScript
* Uses `tsc-alias` to rewrite `@src` path aliases to relative paths
* Copies all SCSS files from `src/` into `dist/` preserving directory structure
```makefile
build:
tsc --project tsconfig.build.json
tsc-alias -p tsconfig.build.json
find src -type f -name '*.scss' -exec sh -c '\
for f in "$$@"; do \
d="dist/$${f#src/}"; \
mkdir -p "$$(dirname "$$d")"; \
cp "$$f" "$$d"; \
done' sh {} +

Comment on lines +348 to +351
This config:
- Extends your main `tsconfig.json`
- Outputs compiled JavaScript and type declarations to `dist/`
- Excludes test files and mocks from the published package
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar nit to the makefile target one. Moving this up and saying "The following config" might be nice.

@arbrandes arbrandes force-pushed the compile-before-publishing branch from cadf121 to 85562b8 Compare February 10, 2026 21:45
…ocal dev workflow

Remove the getLocalAliases() functionality and all module.config.js
references from the codebase. This mechanism is replaced by the
tgz-based local development workflow introduced earlier.

Add ADR 0010 documenting the decisions to compile TypeScript before
publishing, remove module.config.js, and adopt tarball-based local
development as the official workflow.

BREAKING CHANGE: module.config.js is no longer supported for local
development of dependencies. Use the tgz-based workflow instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@arbrandes arbrandes force-pushed the compile-before-publishing branch from 85562b8 to 4fe8787 Compare February 10, 2026 21:51
To avoid the common mistake of publishing before building, add `npm run
build` to the `prepack` stage.  This takes care of building before both
`npm publish` and `npm pack`.

Also rename the pack/nodemon npm scripts for consistency.
@arbrandes arbrandes force-pushed the compile-before-publishing branch from 29146e4 to 988758f Compare February 11, 2026 11:51
npm ci
npm run build
npm run pack:local
npm run pack
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewer: I snuck this in, as well as some script renames I found justifiable, into a new commit.

Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

LGTM!

@arbrandes arbrandes merged commit 5b70c59 into openedx:main Feb 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants