Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion tools/build-scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ async function build() {
'@wordpress/validation-tools',
'--silent',
] ).catch( () => {
throw new Error( 'Run `npm install` to update.' );
throw new Error(
'Run `npm install` to update, or set GUTENBERG_CHECK_INSTALLED_DEPS=NEVER to skip this check.'
);
} );
}

Expand Down
4 changes: 3 additions & 1 deletion tools/build-scripts/dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ async function dev() {
'@wordpress/validation-tools',
'--silent',
] ).catch( () => {
throw new Error( 'Run `npm install` to update.' );
throw new Error(
'Run `npm install` to update, or set GUTENBERG_CHECK_INSTALLED_DEPS=NEVER to skip this check.'
);
} );
}

Expand Down
52 changes: 39 additions & 13 deletions tools/validation/check-installed-deps.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* `package-lock.json` against `node_modules/.package-lock.json` (npm's hidden
* lockfile, written on every install to record the actual installed tree).
*
* Works with both the hoisted and isolated layouts.
*
* Exits non-zero with a hint to run `npm install` if the trees diverge.
*/

Expand Down Expand Up @@ -85,6 +87,28 @@ if ( needsCheck ) {
const lockPkgs = lock.packages || {};
const hiddenPkgs = hidden.packages || {};

// The package name is the path segment after the final `node_modules/`.
const NM = 'node_modules/';
const packageName = ( pkgPath ) => {
const i = pkgPath.lastIndexOf( NM );
return i === -1 ? pkgPath : pkgPath.slice( i + NM.length );
};

/*
* Index installed packages by `name@version` → set of `resolved` sources.
*/
const installedByKey = new Map();
for ( const [ pkgPath, info ] of Object.entries( hiddenPkgs ) ) {
if ( info.link || ! info.version ) {
continue;
}
const key = `${ packageName( pkgPath ) }@${ info.version }`;
if ( ! installedByKey.has( key ) ) {
Comment on lines +105 to +106

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The linked layout relocates packages under .store and omits integrity, so we match on resolved source rather than path/integrity.

installedByKey.set( key, new Set() );
}
installedByKey.get( key ).add( info.resolved );
}

const reportedMismatches = [];
const MAX_REPORTED = 5;
let totalMismatches = 0;
Expand All @@ -100,22 +124,24 @@ if ( needsCheck ) {
continue;
}

const installed = hiddenPkgs[ pkgPath ];
// Optional/extraneous deps may legitimately not be installed.
if ( info.optional || info.extraneous ) {
continue;
}

/*
* Match by name@version. For aliases `info.name` is the real name
*/
const leaf = packageName( pkgPath );
const resolvedSet =
installedByKey.get( `${ info.name || leaf }@${ info.version }` ) ||
installedByKey.get( `${ leaf }@${ info.version }` );
Comment on lines +136 to +138

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For aliases, info.name is the real name (the linked .store key), while the path leaf is the alias install name (the hoisted key), so we try both.


let mismatch;
if ( ! installed ) {
/*
* Optional deps may be skipped by npm on the current platform
* (e.g. macOS-only fsevents on Linux). Don't flag them as
* missing. Real drift on an optional dep would still be caught
* below as an integrity mismatch.
*/
if ( info.optional ) {
continue;
}
if ( ! resolvedSet ) {
mismatch = `missing: ${ pkgPath }`;
} else if ( installed.integrity !== info.integrity ) {
mismatch = `integrity mismatch: ${ pkgPath }`;
} else if ( info.resolved && ! resolvedSet.has( info.resolved ) ) {
mismatch = `source mismatch: ${ pkgPath }`;
}

if ( ! mismatch ) {
Expand Down
Loading