Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Perseus to pnpm #2202

Merged
merged 54 commits into from
Feb 20, 2025
Merged

Migrate Perseus to pnpm #2202

merged 54 commits into from
Feb 20, 2025

Conversation

jeremywiebe
Copy link
Collaborator

@jeremywiebe jeremywiebe commented Feb 5, 2025

Summary:

This PR migrates this repo from yarn to pnpm. The version of Yarn we are using is very outdated and is no longer supported. Khan Academy has made an engineering-wide decision to adopt pnpm as its "package manager of choice" and so let's adopt!

Notable changes:

  • Use workspace: syntax for in-repo dependencies
  • Use catalog: syntax for peer/dev dependencies (easier "global" upgrade of these) - and Github just added support for catalog: support Dependabot yesterday!!
  • Build system is simplified because we don't have to worry about build order

Issue: LEMS-2825

Test plan:

pnpm build
pnpm dev
pnpm start # storybook
pnpm cypress:ci

Copy link
Contributor

github-actions bot commented Feb 5, 2025

Size Change: -604 kB (-41.08%) 🎉

Total Size: 866 kB

Filename Size Change
packages/kas/dist/es/index.js 38.9 kB -88 B (-0.23%)
packages/kmath/dist/es/index.js 10.4 kB -76.4 kB (-87.99%) 🏆
packages/math-input/dist/es/index.js 77.6 kB -135 B (-0.17%)
packages/perseus-core/dist/es/index.js 29.6 kB -18.6 kB (-38.56%) 🎉
packages/perseus-editor/dist/es/index.js 275 kB -414 kB (-60.05%) 🏆
packages/perseus-linter/dist/es/index.js 22 kB -114 B (-0.52%)
packages/perseus-score/dist/es/index.js 20.3 kB -94.8 kB (-82.33%) 🏆
packages/perseus/dist/es/index.js 367 kB -270 B (-0.07%)
packages/pure-markdown/dist/es/index.js 3.54 kB -119 B (-3.25%)
packages/simple-markdown/dist/es/index.js 12.5 kB -1 B (-0.01%)
ℹ️ View Unchanged
Filename Size
packages/keypad-context/dist/es/index.js 760 B
packages/math-input/dist/es/strings.js 1.79 kB
packages/perseus/dist/es/strings.js 6.57 kB

compressed-size-action

Copy link
Contributor

@mark-fitzgerald mark-fitzgerald left a comment

Choose a reason for hiding this comment

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

Storybook/Vite/Aphrodite adjustment looks good. Thanks for keeping it along for the configuration changes.

Copy link
Contributor

github-actions bot commented Feb 6, 2025

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (e2ad2ea) and published it to npm. You
can install it using the tag PR2202.

Example:

pnpm add @khanacademy/perseus@PR2202

If you are working in Khan Academy's webapp, you can run:

./dev/tools/bump_perseus_version.sh -t PR2202

@@ -34,74 +32,6 @@ const rootDir = ancesdir(__dirname);
* from the default behavior.
*/

// Kahn's algorithm
// https://en.wikipedia.org/wiki/Topological_sorting#Kahn%27s_algorithm
const topoSort = (yarnWorkspacesOutput) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not needed. WonderBlocks doesn't build in any dependency order, and after thinking about it a bit, we don't need to either. The build doesn't depend on build artifacts of other in-repo packages, so this simplifies our build a bunch!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@kevinb-khan I tagged you mostly for these changes. I'm pretty sure we don't need to build in any dependency order.

Copy link
Member

Choose a reason for hiding this comment

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

Does it still build if you reverse the topological sort? If it does, I think we're probably okay.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep. I did a manual ordering in the reverse of what topoSort() produces and it built fine.

platform === "browser" && format === "esm"
? "runtime"
: "bundled",
babelHelpers: "runtime",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

WonderBlocks uses runtime for both ES and CJS builds, so we'll do the same!

https://www.npmjs.com/package/@rollup/plugin-babel#babelhelpers

@@ -0,0 +1,61 @@
import {readFileSync} from "node:fs";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Most of this file is from the file that was moved from dev/vite.config.ts

@@ -1,5 +1,6 @@
{
"name": "hubble",
"private": true,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Juuuuust so we don't try to publish this package!

Comment on lines +161 to +167
# MUST be before we install node_modules as that depends on finding
# Cypress binaries in this cache!
- uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Cypress runs a post-install script (which we've allowed in package.json), but it doesn't seem to include downloading the Cypress binaries if we've cached node_modules (next step). By caching this directory, the post-install script is able to find a cached binary and connect it.

On a clean install (or Cypress upgrade) the node_modules cache will be busted and the cypress npm package properly downloads the correct binary and caches it in ~/.cache/Cypress.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's nice, I imagine that would speed things up a bit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

On a random sampling of one other PR, this workflow took 1m 34s.

On this PR, this same workflow took 1m 10s.

So, yes, 24s faster!

@@ -1,4 +1,4 @@
import viteConfig from "../dev/vite.config";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I moved some vite configs around. It always felt weird that we used dev/vite.config.ts as our base. Now we have a shared base config in the root of the repo and both Storybook and dev/ leverage it.

This will pave the way in the future for us to use Vitest for testing (faster!!)

@@ -72,7 +72,7 @@ const config: StorybookConfig = {
},
// Fix from: https://github.com/storybookjs/storybook/issues/25256#issuecomment-1866441206
assetsInclude: ["/sb-preview/runtime.js"],
plugins: [...viteConfig.plugins, lessWrapper],
plugins: [...(viteConfig.plugins ?? []), lessWrapper],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No guarantee that there are plugins defined in the base config.

@@ -1,9 +1,11 @@
import * as React from "react";
import {color} from "@khanacademy/wonder-blocks-tokens";
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
import {Dependencies} from "@khanacademy/perseus";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The root package doesn't depend on one of its monorepo packages, so this needed to change to a relative path now that we're using pnpm.

Copy link
Member

Choose a reason for hiding this comment

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

By "one of its monorepo packages," you mean @khanacademy/perseus? I think the resolutions for these monorepo package imports are defined in vite.config.ts. Maybe there's a problem with how that's being done?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By "one of its monorepo packages," you mean @khanacademy/perseus?

Yes

I think the resolutions for these monorepo package imports are defined in vite.config.ts. Maybe there's a problem with how that's being done?

We did have aliases. But with pnpm, we don't need them anymore as pnpm correctly sets up in-repo dependencies. I removed the aliases from the Vite config and this was the only thing that needed adjustment. I figured it was cleaner to have fewer things (and customizations) in Vite config if pnpm solves the same problem. Changing this to a relative path import worked (we were already importing the DependenciesContext using a relative path in this file).

@@ -8,15 +8,13 @@ module.exports = function createBabelPlugins({platform, format, coverage}) {
if (coverage) {
plugins.push("istanbul");
}
if (platform === "browser" && format === "esm") {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I switched this because we always build for "browser" and I made a change in rollup-config.js to match how WB builds - namely, we always use babelHelpers: "runtime" and so we need this plugin always.

@@ -50,7 +50,7 @@ module.exports = {
},
// Allow transforming files imported from @phosphor-icons/core.
// This is required by the .svg transform above.
transformIgnorePatterns: ["/node_modules/(?!(@phosphor-icons/core)/)"],
transformIgnorePatterns: ["/node_modules/.pnpm/(?!@phosphor-icons.core@)"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Shamelessly stole this also from WB. pnpm organizes the details of node_modules differently!

Comment on lines +23 to +24
"@khanacademy/simple-markdown": "workspace:*",
"@khanacademy/wonder-blocks-banner": "catalog:",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

workspace:* for in-repo dependencies, catalog: for the deps that we'll be provided at runtime (peer deps).

This should make managing peer and dev dependency versions so much easier! Plus, Dependabot (Github) just announced full support for pnpm catalogs a few days ago!

@@ -55,7 +55,6 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/classnames": "^2.3.1",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

pnpm kept telling me that this library ships with its types built-in now... so we don't need this dep.

@@ -123,22 +125,24 @@
"tiny-invariant": "^1.3.1",
"typescript": "5.7.2",
"typescript-coverage-report": "^0.7.0",
"vite": "5.1.0",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved up from dev/

"vite-plugin-istanbul": "^5.0.0",
"winston": "^3.7.2"
},
"scripts": {
"gen:parsers": "yarn --cwd packages/kas gen:parsers",
"prebuild": "yarn gen:parsers",
"preinstall": "npx -y only-allow pnpm",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This catches if someone runs yarn...

"dependencies": {}
"packageManager": "[email protected]+sha512.b8fef5494bd3fe4cbd4edabd0745df2ee5be3e4b0b8b08fa643aa3e4c6702ccc0f00d68fa8a8c9858a735a0032485a44990ed2810526c875e416f001b17df12b",
"pnpm": {
"onlyBuiltDependencies": [
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This flags packages that we explicitly allow to run post-install scripts during pnpm install. A nice safety feature to protect us from supply-chain attacks.

@@ -23,19 +23,18 @@
],
"scripts": {
"prepublishOnly": "../../utils/package-pre-publish-check.sh",
"gen:parsers": "node src/parser-generator.ts",
"test": "bash -c 'yarn --silent --cwd \"../..\" test ${@:0} $($([[ ${@: -1} = -* ]] || [[ ${@: -1} = bash ]]) && echo $PWD)'"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed this. As far as I know, nobody cd's into the various package directories and runs tests. It's just as easy to run from the root, and you can just as easily run pnpm test packages/kas from the root!

@@ -1,2 +1,2 @@
@import "./overrides.less";
@import (inline) "../../../node_modules/mathquill/build/mathquill.css";
@import (inline) "../node_modules/mathquill/build/mathquill.css";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

pnpm installs direct package dependencies into the package's local node_modules folder, so these relative paths now make more sense.

@jeremywiebe
Copy link
Collaborator Author

Size Change: -191 kB (-12.89%) 👏

Total Size: 1.29 MB

Filename Size Change
packages/kas/dist/es/index.js 38.9 kB -88 B (-0.23%)
packages/kmath/dist/es/index.js 10.4 kB -76.4 kB (-87.99%) 🏆
packages/math-input/dist/es/index.js 77.5 kB -141 B (-0.18%)
packages/perseus-core/dist/es/index.js 27.7 kB -18.6 kB (-40.12%) 🎉
packages/perseus-editor/dist/es/index.js 688 kB -158 B (-0.02%)
packages/perseus-linter/dist/es/index.js 22.1 kB -114 B (-0.51%)
packages/perseus-score/dist/es/index.js 20.1 kB -94.8 kB (-82.5%) 🏆
packages/perseus/dist/es/index.js 379 kB -273 B (-0.07%)
packages/pure-markdown/dist/es/index.js 3.54 kB -118 B (-3.23%)
ℹ️ View Unchanged
compressed-size-action

I checked these size changes. We were bundling jQuery which we've marked as a dev/peer dep (meaning that we expect the host app to supply it). We no longer are. There are a few reductions in that we're using babelHelpers: "runtime" to allow us to use the host app's babel (which is what Wonder Blocks also does).

@jeremywiebe jeremywiebe marked this pull request as ready for review February 7, 2025 23:10
@jeremywiebe jeremywiebe merged commit c7f6f63 into main Feb 20, 2025
8 checks passed
@jeremywiebe jeremywiebe deleted the jer/pnpm branch February 20, 2025 04:15
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.

4 participants