From 9e25bf1abbd827611529db777d43e5ead7d0cec6 Mon Sep 17 00:00:00 2001 From: "Adolfo R. Brandes" Date: Wed, 2 Sep 2026 15:51:35 -0300 Subject: [PATCH 1/4] build: support use as an npm workspace package Resolve babel from PATH, add clean and watch:build, and report 0.0.0-dev as the checked-in version. Co-Authored-By: Claude --- Makefile | 9 ++- nodemon.json | 18 +++++ package-lock.json | 166 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 +- 4 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 nodemon.json diff --git a/Makefile b/Makefile index 741bc7bfbb8..98873f16a4c 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,12 @@ -.PHONY: build -build: +.PHONY: clean +clean: rm -rf ./dist + +.PHONY: build +build: clean tsc --project tsconfig.build.json rm icons/es5/index.d.ts # We don't need this; not sure how to tell tsc not to generate it - ./node_modules/.bin/babel src --config-file ./babel.config.json --out-dir dist --source-maps --ignore **/*.d.ts,**/*.test.jsx,**/*.test.tsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files --extensions ".ts,.tsx,.jsx" + babel src --config-file ./babel.config.json --out-dir dist --source-maps --ignore **/*.d.ts,**/*.test.jsx,**/*.test.tsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files --extensions ".ts,.tsx,.jsx" # --copy-files will bring in everything else that wasn't processed by babel. Remove what we don't want. find ./dist -name "tests" -type d -prune -exec rm -rf "{}" \; # delete tests directories find ./dist -name "*.test.*" -delete # delete other tests files that weren't in tests directories diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 00000000000..5703315f984 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,18 @@ +{ + "watch": [ + "src", + "styles", + "tokens", + "lib", + "bin" + ], + "ext": "ts,tsx,js,jsx,json,scss,css", + "ignore": [ + "node_modules/**", + ".git/**", + "dist/**", + "**/*.test.*", + "**/__snapshots__/**" + ], + "delay": 250 +} diff --git a/package-lock.json b/package-lock.json index e2c26a8991a..5aee71de09c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@openedx/paragon", - "version": "20.18.1", + "version": "0.0.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@openedx/paragon", - "version": "20.18.1", + "version": "0.0.0-dev", "license": "Apache-2.0", "workspaces": [ "example", @@ -106,6 +106,7 @@ "jest-cli": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "markdown-loader-jest": "^0.1.1", + "nodemon": "^3.1.14", "react": "^18", "react-intl": "^10.1.20", "react-test-renderer": "^18", @@ -27054,6 +27055,13 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/image-minimizer-webpack-plugin": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-4.1.4.tgz", @@ -33100,6 +33108,110 @@ "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "license": "MIT" }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -38305,6 +38417,13 @@ "url": "https://github.com/sponsors/lupomontero" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/pubsub-js": { "version": "1.9.5", "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.5.tgz", @@ -43701,6 +43820,32 @@ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -46132,6 +46277,16 @@ "node": ">=6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -46798,6 +46953,13 @@ "react": ">=15.0.0" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/undici": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", diff --git a/package.json b/package.json index 24459760601..2f5fc8e9bb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openedx/paragon", - "version": "20.18.1", + "version": "0.0.0-dev", "description": "Accessible, responsive UI component library based on Bootstrap.", "main": "dist/index.js", "module": "dist/index.js", @@ -30,6 +30,8 @@ }, "scripts": { "build": "make build", + "clean": "make clean", + "watch:build": "nodemon -x \"npm run build\"", "build-docs": "make build-docs", "commit": "commit", "debug-test": "node --inspect-brk node_modules/.bin/jest --runInBand --coverage", @@ -154,6 +156,7 @@ "jest-cli": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "markdown-loader-jest": "^0.1.1", + "nodemon": "^3.1.14", "react": "^18", "react-intl": "^10.1.20", "react-test-renderer": "^18", From 24a6350a4775614e27cc4eecbed28b4cfd20daee Mon Sep 17 00:00:00 2001 From: "Adolfo R. Brandes" Date: Wed, 2 Sep 2026 17:54:50 -0300 Subject: [PATCH 2/4] build: resolve sass '~' imports from hoisted node_modules Co-Authored-By: Claude --- lib/build-scss.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/build-scss.js b/lib/build-scss.js index 4d297d8bed9..fcdd9a13210 100755 --- a/lib/build-scss.js +++ b/lib/build-scss.js @@ -90,7 +90,12 @@ const compileAndWriteStyleSheets = ({ if (!url.startsWith('~')) { return null; } - return new URL(url.substring(1), `${pathToFileURL('node_modules')}/node_modules`); + const specifier = url.substring(1); + // The package name is an optional '@scope/' prefix followed by one more path segment. + const packageName = specifier.match(/^(?:@[^/]+\/)?[^/]+/)[0]; + 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; }, }], // For now we can't resolve these warnings as we need to upgrade our 'bootstrap' dependency to do so: From 294fdb75c59ab54868d7d8dbf48d53ad9e6efe3c Mon Sep 17 00:00:00 2001 From: "Adolfo R. Brandes" Date: Wed, 2 Sep 2026 18:40:58 -0300 Subject: [PATCH 3/4] chore(deps): remove deprecated @types/react-responsive Co-Authored-By: Claude --- package-lock.json | 12 ------------ package.json | 1 - 2 files changed, 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5aee71de09c..998997b518b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,7 +85,6 @@ "@types/jest": "^29.5.10", "@types/react": "^18", "@types/react-dom": "^18", - "@types/react-responsive": "^9.0.0", "@types/react-table": "^7.7.19", "@types/react-test-renderer": "^18.0.0", "@types/uuid": "^9.0.0", @@ -12230,17 +12229,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-responsive": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/react-responsive/-/react-responsive-9.0.0.tgz", - "integrity": "sha512-2B/tmI2OE5Ul57/yo6szbTmI6rbWus6K317lk6baf6Ngvs1RPfvwerns+c0FtmoIDaK7wwVGllZjTTrpYfW2fA==", - "deprecated": "This is a stub types definition. react-responsive provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "react-responsive": "*" - } - }, "node_modules/@types/react-table": { "version": "7.7.20", "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.20.tgz", diff --git a/package.json b/package.json index 2f5fc8e9bb8..8215174591e 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,6 @@ "@types/jest": "^29.5.10", "@types/react": "^18", "@types/react-dom": "^18", - "@types/react-responsive": "^9.0.0", "@types/react-table": "^7.7.19", "@types/react-test-renderer": "^18.0.0", "@types/uuid": "^9.0.0", From e392b0be6401a277973746649c9ed878c7c834a8 Mon Sep 17 00:00:00 2001 From: "Adolfo R. Brandes" Date: Wed, 2 Sep 2026 16:08:51 -0300 Subject: [PATCH 4/4] build: only recompile stylesheets when their sources change 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 --- Makefile | 19 ++++++++++++++++++- package.json | 3 ++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 98873f16a4c..8d79ec9df1b 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,18 @@ clean: rm -rf ./dist +# A full build from scratch. Use this for CI and for publishing. .PHONY: build -build: clean +build: clean build-incremental + +# Rebuilds only the parts whose sources changed. Compiling the stylesheets takes +# minutes while the JavaScript takes seconds, so a watch rebuild should not +# redo the stylesheets on every component edit. +.PHONY: build-incremental +build-incremental: build-js build-scss + +.PHONY: build-js +build-js: tsc --project tsconfig.build.json rm icons/es5/index.d.ts # We don't need this; not sure how to tell tsc not to generate it babel src --config-file ./babel.config.json --out-dir dist --source-maps --ignore **/*.d.ts,**/*.test.jsx,**/*.test.tsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files --extensions ".ts,.tsx,.jsx" @@ -14,6 +24,13 @@ build: clean rm -rf dist/**/__snapshots__ rm -rf dist/__mocks__ rm -rf dist/setupTest.js + +THEME_SOURCES := $(shell find styles/scss styles/css src \( -name '*.scss' -o -name '*.css' \)) + +.PHONY: build-scss +build-scss: dist/theme-urls.json + +dist/theme-urls.json: $(THEME_SOURCES) lib/build-scss.js ./bin/paragon-scripts.js build-scss NPM_TESTS=build i18n_extract lint test diff --git a/package.json b/package.json index 8215174591e..5deae083103 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,9 @@ }, "scripts": { "build": "make build", + "build-incremental": "make build-incremental", "clean": "make clean", - "watch:build": "nodemon -x \"npm run build\"", + "watch:build": "nodemon -x \"npm run build-incremental\"", "build-docs": "make build-docs", "commit": "commit", "debug-test": "node --inspect-brk node_modules/.bin/jest --runInBand --coverage",