diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 141d5d7b5e..e6898ccac9 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -33,7 +33,7 @@ jobs: - name: "Install Node" uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: front_end/.nvmrc cache: "npm" cache-dependency-path: front_end/package-lock.json - name: "Install node_modules and build the frontend" diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 839cbbade2..2ba7fde30b 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -103,7 +103,7 @@ jobs: - name: "Install Node" uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: front_end/.nvmrc cache: "npm" cache-dependency-path: front_end/package-lock.json - name: Cache node_modules diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 98c12fa3c7..13325bf55b 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: front_end/.nvmrc cache: npm cache-dependency-path: front_end/package-lock.json - run: npm ci diff --git a/Dockerfile b/Dockerfile index 2b4da49525..539f0808a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,7 +86,7 @@ COPY --from=backend_deps /app/venv /app/venv COPY --from=frontend_deps /app/front_end/node_modules /app/front_end/node_modules ENV NODE_ENV=production -RUN cd front_end && npm run build && npm install pm2 -g +RUN cd front_end && NODE_OPTIONS=--max-old-space-size=4096 npm run build && npm install pm2 -g RUN cd front_end && npx sentry-cli sourcemaps inject .next RUN source venv/bin/activate && ./manage.py collectstatic --noinput diff --git a/README.md b/README.md index 6d13d7f273..12b26232c8 100644 --- a/README.md +++ b/README.md @@ -127,22 +127,24 @@ poetry install ``` ## Nvm/Node & Frontend -You'll need node to build the frontend. We use nvm for managing node versions. +You'll need node to build the frontend. We use nvm for managing node versions. Install nvm with: ```bash -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash ``` -Then, install node 20.18.0: +For more detailed installation instructions, see the [nvm installation guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating). +Then, install and use the node version specified in `.nvmrc`: ```bash -nvm install 20.18.0 -nvm use 20.18.0 +cd front_end +nvm install +nvm use ``` -To install the frontend dependencies, run: +This will automatically use the version specified in `front_end/.nvmrc` (currently 24.12.0). +To install the frontend dependencies, run in the `front_end` directory: ```bash -cd front_end npm install ``` -Note: you have to switch to the front_end directory to run the npm commands as they are all nested there. +Note: you always have to switch to the `front_end` directory to run the npm commands as they are all nested there. ## Running the server The first time you're booting up the server, make sure postgres is running (`sudo service postgresql start`), then you'll need to run the migrations and collect static files. Start by navigating to the root directory. @@ -163,7 +165,7 @@ poetry run python manage.py runserver Running the front end is pretty easy. Note that you'll have to navigate to the `front_end` directory first. ```bash cd front_end -nvm use 20.18.0 # If node 20.18.0 is not already being used +nvm use # Uses the version specified in .nvmrc npm run dev ``` diff --git a/front_end/.nvmrc b/front_end/.nvmrc index 2a393af592..248216ad5b 100644 --- a/front_end/.nvmrc +++ b/front_end/.nvmrc @@ -1 +1 @@ -20.18.0 +24.12.0 diff --git a/front_end/.prettierrc.json b/front_end/.prettierrc.json index e77954d2eb..3301315790 100644 --- a/front_end/.prettierrc.json +++ b/front_end/.prettierrc.json @@ -10,5 +10,7 @@ "tabWidth": 2, "trailingComma": "es5", "useTabs": false, - "plugins": ["prettier-plugin-tailwindcss"] + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindPreserveWhitespace": true, + "tailwindPreserveDuplicates": true } diff --git a/front_end/package-lock.json b/front_end/package-lock.json index 75b7c135e8..f9c7194617 100644 --- a/front_end/package-lock.json +++ b/front_end/package-lock.json @@ -87,7 +87,7 @@ "storybook": "^9.1.17", "strip-markdown": "^6.0.0", "stripe": "^17.7.0", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "ts-invariant": "^0.10.3", "ts-node": "^10.9.2", "victory": "^37.0.2", @@ -105,11 +105,11 @@ "husky": "^9.0.11", "lint-staged": "^15.2.4", "npm-run-all": "^4.1.5", - "postcss": "^8", + "postcss": "^8.5.6", "prettier": "3.3.1", - "prettier-plugin-tailwindcss": "0.5.14", + "prettier-plugin-tailwindcss": "0.7.2", "server-only": "^0.0.1", - "tailwindcss": "^3.4.1", + "tailwindcss": "^3.4.19", "typescript": "^5.8.2" } }, @@ -11272,6 +11272,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -16276,9 +16277,10 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", "peer": true, "bin": { "jiti": "bin/jiti.js" @@ -16667,9 +16669,10 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", "engines": { "node": ">=14" }, @@ -19839,27 +19842,35 @@ } }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -19982,35 +19993,43 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz", - "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz", + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.21.3" + "node": ">=20.19" }, "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", - "@zackad/prettier-plugin-twig-melody": "*", + "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", - "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", - "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "peerDependenciesMeta": { "@ianvs/prettier-plugin-sort-imports": { "optional": true }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, "@prettier/plugin-pug": { "optional": true }, @@ -20020,7 +20039,7 @@ "@trivago/prettier-plugin-sort-imports": { "optional": true }, - "@zackad/prettier-plugin-twig-melody": { + "@zackad/prettier-plugin-twig": { "optional": true }, "prettier-plugin-astro": { @@ -20029,15 +20048,15 @@ "prettier-plugin-css-order": { "optional": true }, - "prettier-plugin-import-sort": { - "optional": true - }, "prettier-plugin-jsdoc": { "optional": true }, "prettier-plugin-marko": { "optional": true }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, "prettier-plugin-organize-attributes": { "optional": true }, @@ -20047,9 +20066,6 @@ "prettier-plugin-sort-imports": { "optional": true }, - "prettier-plugin-style-order": { - "optional": true - }, "prettier-plugin-svelte": { "optional": true } @@ -22417,42 +22433,44 @@ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, "node_modules/tailwind-merge": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.5.tgz", - "integrity": "sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" } }, "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -22462,14 +22480,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "engines": { - "node": ">=10" - } - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", diff --git a/front_end/package.json b/front_end/package.json index 1be980ad59..46a6a0aaa0 100644 --- a/front_end/package.json +++ b/front_end/package.json @@ -97,7 +97,7 @@ "storybook": "^9.1.17", "strip-markdown": "^6.0.0", "stripe": "^17.7.0", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "ts-invariant": "^0.10.3", "ts-node": "^10.9.2", "victory": "^37.0.2", @@ -115,11 +115,11 @@ "husky": "^9.0.11", "lint-staged": "^15.2.4", "npm-run-all": "^4.1.5", - "postcss": "^8", + "postcss": "^8.5.6", "prettier": "3.3.1", - "prettier-plugin-tailwindcss": "0.5.14", + "prettier-plugin-tailwindcss": "0.7.2", "server-only": "^0.0.1", - "tailwindcss": "^3.4.1", + "tailwindcss": "^3.4.19", "typescript": "^5.8.2" }, "overrides": { diff --git a/front_end/src/components/markdown_editor/editor.css b/front_end/src/components/markdown_editor/editor.css index 930364a626..86ac911006 100644 --- a/front_end/src/components/markdown_editor/editor.css +++ b/front_end/src/components/markdown_editor/editor.css @@ -59,7 +59,8 @@ } .mdxeditor-toolbar { - @apply sticky top-12 flex-wrap @container; + @apply sticky top-12 flex-wrap; + container-type: inline-size; & [class*="toolbarTitleMode"] { display: none; /* We render our custom source mode title instead */ }