Skip to content

build: support use as an npm workspace package - #4442

Merged
brian-smith-tcril merged 5 commits into
openedx:release-23.xfrom
arbrandes:arbrandes/workspace-package-support
Oct 16, 2026
Merged

brian-smith-tcril merged 5 commits into
openedx:release-23.xfrom
arbrandes:arbrandes/workspace-package-support

Conversation

@arbrandes

@arbrandes arbrandes commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Description

Paragon cannot currently be developed as a packages/ workspace of a site built on frontend-base, which is how frontend-base and the MFEs themselves are developed. Three small things get in the way, all fixed here. Closes #4440.

The Makefile called ./node_modules/.bin/babel directly, which does not exist under workspace hoisting. It now resolves babel from PATH, the way the tsc line above it already does.

A clean target plus clean and watch:build scripts give npm run clean:packages and npm run dev:packages something to call, instead of silently no-opping for Paragon.

The checked-in version becomes 0.0.0-dev, like every other Open edX frontend repo, leaving the real version to the release process. 20.18.1 on release-23.x is stale regardless, since semantic-release never commits the bump back.

Compiling the stylesheets takes about three minutes here, against four seconds for everything else, so the build is now split into build-js and build-scss, with the latter keyed on its scss and theme inputs. build still cleans and does everything; watch:build uses build-incremental, which skips the stylesheets unless they changed.

LLM usage notice

Built with assistance from Claude.

@netlify

netlify Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for paragon-openedx-v23 ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 4c99e8f
🔍 Latest deploy log https://app.netlify.com/projects/paragon-openedx-v23/deploys/6aad673e530b2d0008388c03
😎 Deploy Preview https://deploy-preview-4442--paragon-openedx-v23.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.21%. Comparing base (99eda14) to head (4c99e8f).
⚠️ Report is 4 commits behind head on release-23.x.

Additional details and impacted files
@@               Coverage Diff                @@
##           release-23.x    #4442      +/-   ##
================================================
- Coverage         94.25%   94.21%   -0.05%     
================================================
  Files               242      242              
  Lines              4301     4305       +4     
  Branches            992      994       +2     
================================================
+ Hits               4054     4056       +2     
- Misses              243      245       +2     
  Partials              4        4              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Resolve babel from PATH, add clean and watch:build, and report 0.0.0-dev
as the checked-in version.

Co-Authored-By: Claude <noreply@anthropic.com>
@arbrandes
arbrandes force-pushed the arbrandes/workspace-package-support branch 2 times, most recently from 2aeb09c to 2a7da1f Compare September 2, 2026 19:37
Co-Authored-By: Claude <noreply@anthropic.com>
@arbrandes
arbrandes force-pushed the arbrandes/workspace-package-support branch 2 times, most recently from 159889a to 7496a5e Compare September 2, 2026 21:42
arbrandes and others added 2 commits September 2, 2026 19:01
Co-Authored-By: Claude <noreply@anthropic.com>
Split the build into build-js and build-scss, the latter keyed on its
scss and theme inputs, and point watch:build at the incremental target.
Compiling the stylesheets takes minutes; the JavaScript takes seconds.

Co-Authored-By: Claude <noreply@anthropic.com>

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Quick claude pass on this one (with my responses to claude's findings)

  • The version change to 0.0.0-dev is user-visible. lib/version.js prints the package.json version for paragon version, so a workspace checkout will report 0.0.0-dev. The test in lib/__tests__/version.test.js reads the same file, so it still passes. Probably acceptable given the release process stamps the real version, but worth a conscious yes.

This part makes sense to me. We've standardized on 0.0.0-dev for semantically released frontend stuff so this gets my 👍.

  • The THEME_SOURCES prerequisite list omits tokens/. nodemon.json watches tokens, but the Make rule only rebuilds dist/theme-urls.json when styles/ or src/ scss/css changes. build-scss does not read tokens directly, so this is consistent today. It only bites if someone expects a token edit to trigger a stylesheet rebuild in watch mode.

Functionally I'm cool with the prereqs as they are. The only nit I'd have here would be to maybe rename it from THEME_SOURCES to STYLE_SOURCES or STYLESHEET_SOURCES to better reflect what's actually coming from the find.

  • build: clean build-incremental relies on serial prerequisite order. Correct without -j, but make -j could run clean concurrently with the build steps. Low risk since nobody runs Paragon's build with -j, but a build-incremental: | clean-style ordering or a recursive $(MAKE) would make it explicit.

I agree this is low risk, if addressing it makes the Makefile more confusing it doesn't feel worth it to me at the moment, but if it can be addressed simply why not?

  • The new ~ resolver in lib/build-scss.js throws on a bare ~ import. specifier.match(...)[0] dereferences a possibly-null match. No stylesheet in the repo does that, so it is a nit at most.

I asked "any suggestions for addressing the ~ resolver thing?" and got the following response. Considering the "no stylesheet in the repo does that" thing I'm happy to ignore this one.

Claude response to my follow-up question

The cleanest fix folds the tilde check and the package-name capture into one regex, so the guard that already exists does double duty and there is no separate null path to forget:

findFileUrl(url) {
  // '~<package>/...' where the package is an optional '@scope/' plus one segment.
  const match = url.match(/^~((?:@[^/]+\/)?[^/]+)/);
  if (!match) {
    return null;
  }
  const [, packageName] = match;
  const specifier = url.substring(1);
  const searchPaths = require.resolve.paths(packageName) || [];
  const dir = searchPaths.find((nodeModules) => fs.existsSync(path.join(nodeModules, packageName)));
  return dir ? pathToFileURL(path.join(dir, specifier)) : null;
},

Why this shape rather than just wrapping the [0] in a null check:

  • One guard instead of two. The original startsWith('~') line becomes redundant, so the function gets shorter, not longer. A bare ~, or ~@scope with no package segment, no longer matches and falls through to return null.
  • Returning null is the right failure mode. Sass's importer contract treats null as "not mine", so it moves on to load paths and then reports its standard "Can't find stylesheet to import" error with the file and line. That is a better message than a TypeError from inside the importer with a stack trace into build-scss.js.
  • Keep the require.resolve.paths plus existsSync approach. The tempting alternative is path.dirname(require.resolve(${packageName}/package.json)), but packages with an exports map that does not list package.json will throw on that, and Paragon's stylesheets already reach into bootstrap's scss/ directory in ways an exports map could block. Walking the candidate node_modules directories sidesteps that entirely.

One thing worth confirming since it is the point of the PR: require.resolve.paths anchors on the location of lib/build-scss.js, so it walks up from Paragon's own directory. In a workspace that reaches the hoisted root node_modules, which is what you want. It would only misbehave if a consumer ran the script with a stylesheet whose tilde import named a package that is installed next to the stylesheet but not visible from Paragon, and nothing in the repo does that today.

Rename THEME_SOURCES to STYLESHEET_SOURCES, and make `build` invoke
clean and build-incremental recursively so the ordering holds under -j.

Co-Authored-By: Claude <noreply@anthropic.com>
@arbrandes

Copy link
Copy Markdown
Contributor Author

Addressed, thanks!

@brian-smith-tcril
brian-smith-tcril merged commit 2a2e80c into openedx:release-23.x Oct 16, 2026
10 checks passed
@openedx-semantic-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 23.23.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support developing Paragon as a workspace package

3 participants