Skip to content

Architectural audit: newspack-scripts dependency declarations #234

@thomasguillot

Description

@thomasguillot

Context

PR #233 attempted to bring @wordpress/* packages up to latest within eslint@8 compatibility. The goal was simple: keep @wordpress/components and @wordpress/icons at latest, plus the obvious safe minor bumps.

The PR turned into a multi-day investigation of cascading transitive-dependency issues:

  • @wordpress/block-editor 15.6 → 15.18 brought @wordpress/commandscmdk@radix-ui/* requiring React 19, which conflicted with @wordpress/element's strict React 18 dependency, causing React duplication that broke 78 tests in newspack-plugin (hooks returned null because components and react-dom loaded different React copies).
  • @wordpress/dataviews (transitively pulled) ended up nested in some scenarios instead of hoisted, breaking consumer SCSS imports of @wordpress/dataviews/build-style/style.css.
  • @wordpress/ui@0.12.0 (transitively pulled by @wordpress/block-library@9.45.0) requires engines.node: ">=20.10.0", but the codebase declares >=18.12.0 — silent runtime mismatch.
  • Pinning packages tightly didn't propagate: ranges like ^15.6.0 allowed npm to resolve 15.18.x. Even narrowing to ~15.6.0 for direct deps was bypassed by transitive resolution chains (block-librarycore-datablock-editor@^15.18).
  • npm overrides: only apply at the root install level — they do NOT propagate to packages that depend on us. So we cannot shape consumer trees from within newspack-scripts.

PR #233 was closed because the surgical revert that "worked" was only a partial fix dependent on consumers' existing lockfile state.

The structural observation

newspack-scripts declares ~25 @wordpress/* packages as direct dependencies, but only ~5 of them are imported by newspack-scripts's own code (@wordpress/scripts, @wordpress/eslint-plugin, @wordpress/browserslist-config, @wordpress/prettier-config, @wordpress/stylelint-config).

The rest (@wordpress/components, icons, element, data, block-editor, etc.) are listed "for testing environment" per the comment in config/eslintrc.js — referencing webpack's DependencyExtractionPlugin behavior where consumer builds extract these at runtime but tests need them locally.

The result: newspack-scripts is an ambient distributor of the WordPress editor ecosystem. Every transitive shift in WP-core land becomes a newspack-scripts release event, and every consumer downstream pays the blast radius. We hit this in PR #233 with React/cmdk/Radix/ui and it's a recurring pattern.

Goal

We want all @wordpress/* packages at latest, sustainably — meaning future major-version updates from WP-core don't trigger week-long PR investigations. PR #233 demonstrates the current architecture can't deliver that.

Audit topics for discussion

Each is a discussion prompt, not a settled answer. Looking for input from the team.

  1. Inventory: declared vs imported. For each @wordpress/* we declare, is it (a) imported by our code, (b) needed for consumer build/test environment via DependencyExtractionPlugin, or (c) neither? The "neither" bucket is pure surface area we maintain for free. Open question: when did we last verify the (b) bucket against current webpack/jest behavior?

  2. dependencies vs peerDependencies. Should runtime libraries like @wordpress/element, @wordpress/data, @wordpress/components be peerDependencies? Shifts version-choice responsibility to consumers and reduces blast radius from our releases.

  3. Where does the testing-environment constraint actually come from? The eslintrc.js comment is from years ago. Verify the constraint is still real, then determine the minimum set of packages required to satisfy it, vs the maximum we currently ship.

  4. What's the consumer-visible API of newspack-scripts? CLI subcommands (build, test, lint:js, lint:scss, format:js, commit, etc.) plus exported configs (getWebpackConfig, eslintrc, babel.config, postcss.config, prettier.config, stylelint.config, tsconfig). That's a small surface compared to what's listed in deps.

  5. WP version coupling. Should newspack-scripts target a specific Gutenberg/WP-core release window and document it? Right now we track latest with no policy about which WP combination is "supported together."

  6. Lockfile strategy. Should we ship a lockfile and recommend npm ci to consumers? Today's setup (broad caret ranges, no shipped lockfile) means consumer trees are unpredictable.

  7. Compare to @wordpress/scripts. That package is a build-tool wrapper and doesn't bundle the editor. Why does newspack-scripts? Did the difference accumulate by accident, or was there a deliberate decision?

  8. Migration paths. Likely outcomes:

    • Slim down: delete most @wordpress/* declarations; keep newspack-scripts as a thin tooling wrapper.
    • Formalize: split into two packages — newspack-scripts (CLI + configs) and newspack-deps-bundle (WP package set with explicit version coupling).
    • peerDeps shift: keep declarations but as peers; document expected consumer setup.

Why now

Two unresolved Copilot threads on PR #233 (thread on block-editor pin, thread on engines.node) are concrete examples of issues we cannot fix incrementally — they're consequences of the structural shape of newspack-scripts as a package, not bugs in any specific declaration.

Rather than continuing to whack moles on PR #233, closing it and starting from this issue.

Asks

  • Reactions / 🚀 if the broad direction (audit → architectural change) sounds right
  • Comments on any of the 8 audit topics, especially Resolving prettier dependency #1 (inventory) and @wordpress/* packages handling #3 (testing-env constraint) which are factual questions someone may already have answered
  • Any other architectural concerns about newspack-scripts that this PR didn't surface but you've been waiting to raise

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions