Skip to content

Conversation

@balzss
Copy link
Contributor

@balzss balzss commented Oct 6, 2025

Summary

Implement Phase 1 of pnpm migration with workspace protocol and core tooling updates. Migrate from npm to pnpm v10.18.3 as the package manager. All packages now use the workspace:* protocol for internal dependencies, which pnpm automatically resolves to actual versions on publish.

Changes

Package Manager Configuration

  • Add pnpm-workspace.yaml defining workspace packages
  • Add .npmrc with pnpm configuration (hoisted node linker, strict peer deps)
  • Update lerna.json to use pnpm as npm client
  • Update root package.json with pnpm engines, overrides, and devDependencies
  • Update .gitignore for pnpm artifacts
  • Remove package-lock.json (replaced by pnpm-lock.yaml)

Workspace Dependencies

  • Convert all 94 internal @instructure/* dependencies from exact versions to workspace:*
  • Create scripts/convert-to-workspace-protocol.js for automated conversion

Build & Tooling

  • Update scripts/bootstrap.js to use pnpm and run builds sequentially
  • Update packages/ui-scripts/lib/commands/bump.js to use pnpm install
  • Update packages/ui-scripts/lib/utils/npm.js to use pnpm whoami
  • Fix babel-plugin-transform-imports for pnpm workspace module resolution
  • Fix generate-all-tokens for pnpm workspace package resolution
  • Add @types/react and jsdom to root devDependencies (were phantom dependencies)
  • Fix TypeScript type build and project references
  • Optimize scripts/clean.js for better performance:
    • Parallelize deletions with async/await and Promise.all() (17% faster)
    • Fix broken --nuke_node mode with native shell command (10-100x faster)
    • Improves bootstrap time and fixes timeout issues when cleaning node_modules
  • Fix markdown hot reload in dev server:
    • pnpm's module resolution prevents webpack-dev-server's onListening callback from firing
    • Implement standalone markdown watcher using concurrently to run in parallel with webpack
    • Add packages/docs/buildScripts/watch-markdown.mjs with chokidar file watching
    • Update start:watch script to run markdown watcher alongside webpack bundle --watch
    • Add concurrently dependency for parallel process management with colored output
    • Markdown file changes now trigger hot reload during development

regression-test Workspace Decoupling

  • Change regression-test to use link: protocol instead of workspace overlap
  • Separate regression-test workspace (React 19) from main workspace (React 18)
  • Mirrors how external consumers import from built @instructure/ui bundle

CI/CD

  • Update all GitHub Actions workflows to use pnpm v10

Documentation

  • Update CLAUDE.md with pnpm commands throughout (tech stack, quick start, essential commands, workflows)
  • Update all contributor documentation with pnpm commands:
    • docs/contributor-docs/dev-commands.md (complete command reference)
    • docs/contributor-docs/release.md (release process)
    • docs/contributor-docs/adding-icons.md
    • docs/contributor-docs/codemods.md
    • docs/contributor-docs/building-instui.md
    • docs/contributor-docs/contributing.md
  • Update testing documentation:
    • docs/testing/vitest-unit-testing.md
    • docs/testing/cypress-component-testing.md
  • Update getting started guide:
    • docs/getting-started/usage.md
  • Update package documentation:
    • packages/ui-scripts/README.md (CLI reference)
    • packages/docs/README.md
    • regression-test/README.md
  • Add docs/contributor-docs/pnpm-migration-plan.md as comprehensive migration reference

Breaking Changes

None for package consumers.

For contributors: install pnpm and use pnpm commands instead of npm.

Test plan

npm install -g pnpm@latest
git checkout feat/pnpm-migration
rm -rf node_modules regression-test/node_modules && pnpm install
pnpm run bootstrap       # Verify TypeScript build succeeds
pnpm run dev             # Verify dev server and markdown hot reload work
pnpm run test:vitest     # Verify unit tests pass

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

@github-actions
Copy link

github-actions bot commented Oct 6, 2025

PR Preview Action v1.6.2
Preview removed because the pull request was closed.
2025-11-05 01:08 UTC

@balzss balzss self-assigned this Oct 7, 2025
@balzss balzss force-pushed the feat/pnpm-migration branch from 7c5ddb5 to 339bb0a Compare October 7, 2025 12:23
@balzss balzss requested a review from matyasf October 7, 2025 16:32
@balzss balzss force-pushed the feat/pnpm-migration branch 16 times, most recently from ffdc140 to b850083 Compare October 14, 2025 11:40
@balzss balzss marked this pull request as ready for review October 14, 2025 11:52
@balzss balzss changed the title docs: add comprehensive pnpm + release-it migration plan feat(many): migrate from npm to pnpm Phase 1 Oct 14, 2025
@balzss balzss force-pushed the feat/pnpm-migration branch from b850083 to 57b6c70 Compare October 14, 2025 12:37
@balzss balzss removed the request for review from matyasf October 14, 2025 12:59
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Implements Phase 1 of pnpm migration with workspace protocol and core tooling updates. Migrate from npm to pnpm v10.18.3 as the package manager. All packages now use the workspace:* protocol for internal dependencies, which pnpm automatically resolves to actual versions on publish.

  • Package manager configuration with pnpm-workspace.yaml and .npmrc
  • Convert all 94 internal @instructure/* dependencies from exact versions to workspace:*
  • Update build & tooling scripts to use pnpm commands and fix workspace module resolution

Reviewed Changes

Copilot reviewed 171 out of 177 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Define workspace packages configuration
lerna.json Update to use pnpm as npm client
package.json Add pnpm engines, overrides, devDependencies and update scripts
scripts/clean.js Optimize performance with parallel deletions and native shell commands
scripts/bootstrap.js Update to use pnpm and sequential builds
packages/*/package.json Convert internal dependencies to workspace:* protocol
regression-test/* Decouple workspace using link: protocol and update imports
babel-plugin-transform-imports Fix for pnpm workspace module resolution
docs/* Update documentation to use pnpm commands

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


export default function ButtonPage() {
const myElementRef = useRef<HTMLButtonElement>(null)
const myElementRef = useRef<any>(null)
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The ref type should be more specific than any. Use HTMLButtonElement | null for better type safety.

Suggested change
const myElementRef = useRef<any>(null)
const myElementRef = useRef<HTMLButtonElement | null>(null)

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +52
] as const
const sizes = ['small', 'medium', 'large'] as const
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

Consider defining these arrays outside the component to avoid recreation on each render, improving performance.

Copilot uses AI. Check for mistakes.
))}
</div>
<Button renderIcon={IconAddLine}>Icon Button</Button>
<Button renderIcon={<IconAddLine />}>Icon Button</Button>
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The icon should be passed as a component reference rather than an element to allow the Button component to control rendering. Use renderIcon={IconAddLine} instead.

Suggested change
<Button renderIcon={<IconAddLine />}>Icon Button</Button>
<Button renderIcon={IconAddLine}>Icon Button</Button>

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@matyasf matyasf left a comment

Choose a reason for hiding this comment

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

  • pnpm run dev runs, but the docs app does not load for me (its stuck in loading, like it would be blocked by a firewall
  • there are a couple of npm references left, e.g. "build:watch": "npm run ts:check is in lots of places
  • Please double check ui-scrips scripts, e.g. publish.js, tag.js still uses npm, deprecate, dangerfile.js

if (matches && matches[1]) {
const packageName = matches[1]
const sourceIndex = require.resolve(packageName)
// For pnpm workspaces, resolve from the project root to find workspace packages
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this needed with pnpm?

Comment on lines 108 to 109
if (args.length > 0 && args[0] === '--nuke_node') {
// eslint-disable-next-line no-console
console.info('Deleting node_modules recursively...')
removeNodeModules()
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this still needed? Can there be node_modules folders in packages?

@balzss
Copy link
Contributor Author

balzss commented Oct 15, 2025

  • pnpm run dev runs, but the docs app does not load for me (its stuck in loading, like it would be blocked by a firewall

this turned out to be a harder issue than anticipated, i think something happened with webpack processing the symlinked files that pnpm uses which causes an infinite loop, i'm investigating...

@balzss balzss requested a review from matyasf October 27, 2025 16:41
@matyasf
Copy link
Collaborator

matyasf commented Oct 28, 2025

There are still some npm references, search the codebase for 'npm and "npm. They are in publish.js, deprecarte.js, tag.js

Copy link
Collaborator

@matyasf matyasf left a comment

Choose a reason for hiding this comment

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

See my comments. I've tested most scripts, all seem to work, nice job!

Copy link
Contributor

@joyenjoyer joyenjoyer left a comment

Choose a reason for hiding this comment

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

great job, I tried most of the commands, they work well
please check the ui-scripts folder for npm and change those too

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 179 out of 185 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +92 to +94
execSync(
'find . -name "node_modules" -type d -prune -exec rm -rf {} + 2>/dev/null || true',
{ stdio: 'inherit' }
)
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The shell command uses unescaped user input from process.cwd() context and relies on shell globbing. On Windows or with special characters in paths, this could fail or behave unexpectedly. Consider using Node.js APIs instead of shell commands for cross-platform reliability.

Copilot uses AI. Check for mistakes.
"prepare-build": "npm run build-icons",
"build-icons": "ui-scripts build-icons --svgoConfig svgo.config.js --config icons.config.js",
"prepare-build": "pnpm run build-icons",
"build-icons": "pnpm exec ui-scripts build-icons --skipOptimization --config icons.config.js",
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The --skipOptimization flag is used by default with a TODO comment indicating this is temporary. Consider creating a separate script for optimized builds (like build-icons:optimize) and making the default behavior explicit rather than temporary.

Copilot uses AI. Check for mistakes.
Comment on lines +190 to 192
// If any transform fails, we can't safely transform this import
allTransformsSucceeded = false
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

When a transform fails, allTransformsSucceeded is set to false but the loop continues processing remaining imports. This could lead to partial transforms in the memberTransforms array that are later discarded. Consider breaking early or restructuring to avoid unnecessary work.

Copilot uses AI. Check for mistakes.
package.json Outdated
Comment on lines 47 to 49
"react": "^18",
"react-dom": "^18",
"@types/react": "^18"
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The pnpm overrides use caret ranges (^18) which allow minor and patch updates, while devDependencies specify exact versions (18.3.1). This inconsistency could lead to version mismatches. Consider using exact versions in overrides to match the devDependencies, or document why flexible versions are needed.

Suggested change
"react": "^18",
"react-dom": "^18",
"@types/react": "^18"
"react": "18.3.1",
"react-dom": "18.3.1",
"@types/react": "18.3.26"

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +85
const packageMatch = sourceTokens.match(
/^(@instructure\/)?([\w-]+)(.*)$/
)
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

[nitpick] The regex pattern [\w-]+ matches word characters and hyphens, but doesn't account for scoped package names with slashes or special characters that might exist in subpaths. Consider a more explicit pattern or validate the expected format in the error message.

Copilot uses AI. Check for mistakes.
},
"dependencies": {
"instructure-ui": "file:../packages",
"@instructure/ui": "link:../packages/ui",
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

[nitpick] The regression-test uses link: protocol instead of workspace:* like other packages. While this may be intentional for the separate workspace, consider documenting why a different protocol is used here compared to the main workspace pattern.

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +81
function debouncedProcess(filePath) {
const now = Date.now()
const lastProcessed = processedFiles.get(filePath)

if (lastProcessed && now - lastProcessed < DEBOUNCE_MS) {
return // Skip if file was processed recently
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The debounce implementation doesn't clear old entries from the processedFiles Map, which will grow unbounded during long-running watch sessions. Consider periodically cleaning up entries older than DEBOUNCE_MS or using a proper debounce library.

Copilot uses AI. Check for mistakes.
scripts/clean.js Outdated

const fs = require('fs')
const fs = require('fs').promises
const fsSync = require('fs')
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Unused variable fsSync.

Copilot uses AI. Check for mistakes.
@balzss balzss requested a review from git-nandor November 3, 2025 11:06
@balzss balzss force-pushed the feat/pnpm-migration branch 2 times, most recently from dd7cc43 to 6d8ae68 Compare November 3, 2025 12:11
@balzss balzss force-pushed the feat/pnpm-migration branch 3 times, most recently from 8536b5f to 0c60fe4 Compare November 4, 2025 13:49
Implement pnpm migration with workspace protocol and core tooling updates:

Configuration:
- Add pnpm-workspace.yaml defining workspace packages
- Add .npmrc with pnpm configuration (hoisted node linker, strict peer deps)
- Update lerna.json to use pnpm as npm client
- Update root package.json with pnpm engines, overrides, and devDependencies
- Update .gitignore for pnpm artifacts
- Delete package-lock.json (replaced by pnpm-lock.yaml)

Workspace Dependencies:
- Convert all internal @instructure/* dependencies from exact versions to workspace:*
- Update 94 package.json files across all packages
- Create scripts/convert-to-workspace-protocol.js for automated conversion

Build Tooling:
- Update scripts/bootstrap.js to use pnpm and run builds sequentially
- Update packages/ui-scripts/lib/commands/bump.js to use pnpm install
- Update packages/ui-scripts/lib/utils/npm.js to use pnpm whoami
- Fix babel-plugin-transform-imports for pnpm workspace module resolution
- Fix generate-all-tokens for pnpm workspace package resolution
- Optimize clean script performance
- Restore markdown hot reload in dev server

Regression Test App (npm):
- Keep regression-test using npm to simulate external package consumption
- Change @instructure/ui dependency to file: protocol (from link:)
- Remove pnpm-lock.yaml and pnpm-workspace.yaml
- Generate package-lock.json with npm install
- Update .github/workflows/visual-regression.yml to use npm commands
- Update README.md with npm commands and explanation
- Add .npmrc explaining npm-only approach

Documentation:
- Update all internal npm references to pnpm across the codebase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@balzss balzss force-pushed the feat/pnpm-migration branch from 0c60fe4 to 474e8bd Compare November 4, 2025 16:25
@balzss balzss requested review from joyenjoyer and removed request for joyenjoyer November 4, 2025 17:22
@balzss balzss merged commit f7bb16e into master Nov 5, 2025
10 of 11 checks passed
@balzss balzss deleted the feat/pnpm-migration branch November 5, 2025 01:08
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.

5 participants