close
Skip to content

Auto-run npm ci when JS dependencies change - #13080

Merged
RayBB merged 1 commit into
masterfrom
auto-npm-ci-in-builds
Jul 1, 2026
Merged

Auto-run npm ci when JS dependencies change#13080
RayBB merged 1 commit into
masterfrom
auto-npm-ci-in-builds

Conversation

@RayBB

@RayBB RayBB commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Problem

When a contributor updates package.json / package-lock.json (adding or updating a JS dependency), the next person who runs make js, make css, make frontend, or the build-assets npm script will get a build error — node_modules is stale and the new package is missing. This is a common footgun in local dev and CI.

Solution

Use Make's native timestamp-based dependency tracking to gate npm ci:

node_modules: package-lock.json package.json
	npm ci --no-audit --no-fund

css: node_modules
js: node_modules
components: node_modules
lit-components: node_modules

How it works

  • Make compares node_modules/'s mtime against package-lock.json and package.json.
  • If either file is newer than node_modules, npm ci runs before the build target.
  • If node_modules is already up to date, the rule is skipped — zero overhead.

npm ci is used over npm install because:

  • It installs exactly what's in package-lock.json (deterministic).
  • It fails hard if package-lock.json and package.json are out of sync.
  • It skips the resolution step, making it faster on clean runs.

How to test

1. Idempotent build (no unnecessary installs)

# Run a build — should NOT trigger npm ci (node_modules is already fresh)
make -n js
# Confirm no "npm ci" line appears, just the webpack build

2. Stale deps trigger auto-install

# Simulate a dependency change by touching the lock file
touch package-lock.json

# Dry run confirms npm ci would run
make -n js
# Expected output starts with: npm ci --no-audit --no-fund

# Real run installs deps then builds
make js

# Subsequent runs skip npm ci again
make -n js
# No "npm ci" line

3. All build targets are covered

make -n css
make -n components
make -n lit-components
# None of these should show "npm ci" when node_modules is fresh

Trade-offs considered

Approach Verdict
post-checkout git hook Requires every dev to configure it; easy to sidestep.
prepare npm script Runs on npm install only, not on git pull with new deps.
Unconditional npm ci per build Adds ~30s+ to every build when node_modules is already fresh.
Make timestamp target (this PR) Zero overhead in the common case, automatic, uses existing build tooling.

Note: Many modern projects sidestep this problem entirely by using pnpm (which has a content-addressable store — pnpm install is sub-second when the cache is warm, so they just run it unconditionally before every build) or Yarn Berry Zero-Installs (zipped packages committed to git — deps are always present after git pull). Switching package managers is out of scope here, but worth knowing those options exist if the team ever evaluates alternatives. Docker dev images (install once at image build time) are another common approach. |

Uses Make's native timestamp tracking: if package.json or
package-lock.json is newer than node_modules/, run npm ci
before the build. Covers js, css, components, and lit-components
targets.

Closes: # (no issue)
@RayBB
RayBB requested a review from lokesh July 1, 2026 04:13
@RayBB

RayBB commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

@lokesh this is based on our discussion in the weekly call today. I'd love to hear your thoughts and if it looks good lets merge it and remove one class of issue from this project forever!

@lokesh

lokesh commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

@RayBB

Couple of interesting gotchas:

  1. The "zero overhead" promise doesn't hold on first run. In their Docker setup, node_modules lives inside a prebuilt image (could be weeks old), but package-lock.json gets its timestamp from when you just cloned the repo (fresh = newer). So Make thinks "deps changed!" even though they didn't, and your very first build triggers a full clean reinstall — minutes of waiting, and it fails if you're offline. Previously that first build just ran webpack.
  2. Editing package.json alone now breaks the build. npm ci refuses to run if package.json and package-lock.json disagree. So if you bump a version in package.json and haven't regenerated the lockfile yet, every build errors out until you fix it — even builds that have nothing to do with that package.

And an alternative solution:

The core idea is good (auto-install when deps change is a real quality-of-life win). The nit is that keying off a folder's timestamp is fragile — a cleaner approach is to key off node_modules/.package-lock.json, a small file npm writes after a successful install specifically so tools can detect "is this install current?"

@RayBB

RayBB commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

@lokesh

  1. Is an annoyance but do you think. It's going to be a major problem in practice? It would only be when people actually run a relevant make command. I think if people are running any css/js make command they probably should have some matching dependencies.
  2. I think that's expected right? They need to be in sync?

We do key in just the package.json and packagelock json.

Ultimately, do you think this would be an improvement or be more of an annoyance? You work on frontend much more than me so I think you know.

Maybe we should also consider PNPM which seems to have some performance improvements according AI?

@lokesh

lokesh commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

re: num 1
My day to day won't be impacted. I probably overstated the cost.

re: num 2 package and package lock disagreement
This is what we want, you're right.

The only question that is outstanding, and not blocking, but worth understanding, is what happens if you are offline and run it. Will it wipe your node modules and then fail to install anything?

@lokesh lokesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Though there might be small annoyances and extra build costs on occasion, I think they are worth the trade-off for keeping local dev dependencies up to date.

@RayBB
RayBB merged commit 474549c into master Jul 1, 2026
9 checks passed
@RayBB
RayBB deleted the auto-npm-ci-in-builds branch July 1, 2026 20:10
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.

2 participants