Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
25 changes: 15 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@wordpress/integration-tests": "file:../../test/integration",
"@wordpress/stylelint-tools": "file:../../tools/stylelint",
"deep-freeze": "^0.0.1"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/cover/test/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import userEvent from '@testing-library/user-event';
import {
initializeEditor,
selectBlock,
} from 'test/integration/helpers/integration-test-editor';
} from '@wordpress/integration-tests/helpers/integration-test-editor';

const defaultSettings = {
__experimentalFeatures: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { createContext, useContext, memo, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import { Card, CardBody } from '../../../card';
import { View } from '../../../view';
import { Text } from '../../../text';
import { Card, CardBody } from '../../card';
import { View } from '../../view';
import { Text } from '../../text';
import { ContextSystemProvider } from '../index';

const SomeContext = createContext();
Expand Down
1 change: 1 addition & 0 deletions packages/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@wordpress/block-library": "file:../block-library",
"deep-freeze": "^0.0.1"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
serialize,
} from '@wordpress/blocks';
import { RichTextData } from '@wordpress/rich-text';
import * as paragraphBlock from '@wordpress/block-library/src/paragraph';
import * as groupBlock from '@wordpress/block-library/src/group';
import * as paragraphBlock from '@wordpress/block-library/build-module/paragraph/index.mjs';
import * as groupBlock from '@wordpress/block-library/build-module/group/index.mjs';
Comment on lines -12 to +13

@aduth aduth Jun 30, 2026

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.

These changes break the unit tests, which similarly expect to be able to run without a preceding build. Which leaves us in a tricky spot because the point of the import rules is to ensure valid imports, and the previous code here is not valid: @wordpress/block-library doesn't export /src/ on its package API surface.

Not entirely sure yet what the best option here would be, but a couple ideas to explore:

  • Making block implementations available on the root package exports (affects the public API in a way I'm not sure we're comfortable with)
  • Mirroring the same kind of remapping resolver logic we're implementing here for ESLint but in Jest

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.

  • Mirroring the same kind of remapping resolver logic we're implementing here for ESLint but in Jest

We already have something like this, but only supporting top-level package imports:

// Finds all packages which are transpiled with Babel to force Jest to use their source code.
const transpiledPackageNames = glob(
path.join( ROOT_DIR, 'packages/*/src/index.{js,ts,tsx}' )
).map( ( fileName ) => {
const relative = path.relative( ROOT_DIR, fileName );
return relative.split( path.sep )[ 1 ];
} );

[ `@wordpress\\/(${ transpiledPackageNames.join( '|' ) })$` ]:
'packages/$1/src',

It looks like we already had to hack around this for some package subpath exports, like in @wordpress/theme:

'@wordpress/theme/design-tokens.js':
'<rootDir>/packages/theme/src/prebuilt/js/design-tokens.mjs',

Maybe that's the short-term option here: Add a remapping for these block imports to simulate support, and separately work to mirror the remapping behavior consistently between ESLint and Jest.


/**
* Internal dependencies
Expand Down
1 change: 1 addition & 0 deletions packages/is-shallow-equal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"types": "build-types",
"sideEffects": false,
"devDependencies": {
"@wordpress/lazy-import": "file:../lazy-import",
"benchmark": "^2.1.4"
},
"publishConfig": {
Expand Down
1 change: 1 addition & 0 deletions test/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@y/websocket-server": "^0.1.1",
"esbuild": "^0.27.2",
"filenamify": "^4.2.0",
"wasm-vips": "^0.0.18",
"ws": "^8.18.3",
"y-websocket": "^3.0.0"
},
Expand Down
3 changes: 3 additions & 0 deletions test/integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"url": "git+https://github.com/WordPress/gutenberg.git",
"directory": "test/integration"
},
"exports": {
"./helpers/integration-test-editor": "./helpers/integration-test-editor.js"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
Expand Down
3 changes: 2 additions & 1 deletion test/performance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"@playwright/test": "^1.58.2",
"@types/node": "^20.19.39",
"@wordpress/e2e-test-utils-playwright": "file:../../packages/e2e-test-utils-playwright",
"@wordpress/scripts": "file:../../packages/scripts"
"@wordpress/scripts": "file:../../packages/scripts",
"wasm-vips": "^0.0.18"
},
"publishConfig": {
"access": "public"
Expand Down
4 changes: 1 addition & 3 deletions tools/eslint/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,6 @@ export default dedupePlugins( [
{
files: developmentFiles,
rules: {
'import/default': 'off',
'import/no-unresolved': 'off',
'import/named': 'off',
'@wordpress/data-no-store-string-literals': 'off',
},
},
Expand All @@ -387,6 +384,7 @@ export default dedupePlugins( [
files: [ '**/fixtures/**' ],
rules: {
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'off',
},
},

Expand Down
84 changes: 79 additions & 5 deletions tools/eslint/import-resolver.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,81 @@ const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );

exports.interfaceVersion = 2;

/**
* @typedef ExportEntryObject
*
* @property {string} import ESM import entrypoint.
* @property {string} default Default export entrypoint.
*/

/**
* @typedef {string|ExportEntryObject} ExportEntry
*/

/**
* Given an export entry and a subpath, returns the resolved export path for
* the matching entry. Defaults to the ESM entrypoint if available, falling back
* to the default export or the root entry if it is a string.
*
* @param {ExportEntry} exportEntry Export entry from package manifest.
*
* @return {string|undefined} The resolved export path, or undefined if no
* matching entry is found.
*/
function getResolvedExportPath( exportEntry ) {
return typeof exportEntry === 'string'
? exportEntry
: exportEntry?.import ?? exportEntry?.default;
}

/**
* Given a package entrypoint identifier, returns the resolved export path for
* the matching entry. Supports matching wildcard entries, and defaults to the
* ESM entrypoint if available.
*
* @param {string} subpath Package entrypoint identifier.
* @param {Record<string, ExportEntry>} exportMap Export map from package manifest.
*
* @return {string|void|undefined} The resolved export path, or undefined if no
* matching entry is found.
*/
function getResolvedExport( subpath, exportMap ) {
if ( Object.hasOwn( exportMap, subpath ) ) {
return getResolvedExportPath( exportMap[ subpath ] );
}

for ( const key in exportMap ) {
if ( ! Object.hasOwn( exportMap, key ) ) {
continue;
}

const wildcardIndex = key.indexOf( '*' );
if ( wildcardIndex === -1 ) {
continue;
}

if ( subpath.startsWith( key.substring( 0, wildcardIndex ) ) ) {
const wildcardPath = subpath.substring( wildcardIndex );
const resolvedPath = getResolvedExportPath( exportMap[ key ] );
return resolvedPath?.replace( '*', wildcardPath );
}
}
}

exports.resolve = function ( source, file, config ) {
const resolve = ( sourcePath ) =>
resolverNode.resolve( sourcePath, file, {
...config,
extensions: [ '.tsx', '.ts', '.mjs', '.js', '.json', '.node' ],
extensions: [
'.tsx',
'.ts',
'.mjs',
'.js',
'.jsx',
'.cjs',
'.json',
'.node',
],
} );

if ( source.startsWith( '@wordpress/' ) ) {
Expand All @@ -30,11 +100,15 @@ exports.resolve = function ( source, file, config ) {
try {
const manifestPath = path.join( packagePath, 'package.json' );
const manifest = JSON.parse( readFileSync( manifestPath, 'utf8' ) );
const subpath = path.join( '.', pathParts.join( '/' ) );
const exportPath = manifest.exports?.[ subpath ]?.import;
Comment on lines -33 to -34

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.

I don't know that this was working at all before for anything other than the . entrypoint. With this previous logic, subpath would end up would end up like build-module/paragraph/index.mjs (for the @wordpress/block-library example in the changes above) because path.join only emits a leading dot if the given path segments are empty. But each exports key must have a leading dot:

All target paths in the "exports" map (the values associated with export keys) must be relative URL strings starting with ./.

https://nodejs.org/api/packages.html#targets-must-be-relative-urls

So trying to access manifest.exports?.[ 'build-module/paragraph/index.mjs' ] would never work (ignoring the fact the package defines the matching export with a wildcard).

let subpath = '.';
if ( pathParts.length > 0 ) {
subpath += '/' + pathParts.join( '/' );
}
const exportPath = getResolvedExport( subpath, manifest.exports );

const sourcePath = exportPath
.replace( 'build-module', 'src' )
.replace( /\.mjs$/, '.js' );
.replace( /build(-module)?/, 'src' )
.replace( /\.[cm]?js$/, '.js' );

return resolve( path.join( packagePath, sourcePath ) );
} catch {
Expand Down
Loading