Skip to content
Closed
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
13 changes: 9 additions & 4 deletions packages/eslint-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ const eslintSettings = merge(require('@yardinternet/eslint-config'), [
module.exports = eslintSettings;
```

## brave-root vs theme-root
## Import aliases

The `@sage/scripts` import alias auto-detects where ESLint runs:
Every theme gets an `@<theme>/scripts` alias pointing at its own
`resources/scripts`, in both layouts:

- **brave-root** — cwd has `web/app/themes/`. `@sage/scripts` → `web/app/themes/sage/resources/scripts`.
- **theme-root** — cwd is a single theme (has `style.css`, no `web/app/themes/`). `@sage/scripts` and `@<theme>/scripts` → `./resources/scripts`.
- **brave-root** — cwd is the project root; aliases resolve per theme, e.g. `@sage/scripts` → `web/app/themes/sage/resources/scripts`.
- **theme-root** — cwd is a single theme (has `style.css`, no themes directory); `@<theme>/scripts` → `./resources/scripts`.

`@sage/scripts` is always available: in projects without a theme named `sage` it
falls back to the default theme, so shared starter code keeps resolving. See
[project layout detection](../shared-utils/README.md#project-layout-detection).
1 change: 1 addition & 0 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"license": "ISC",
"dependencies": {
"@wordpress/eslint-plugin": "^26.0.0",
"@yardinternet/shared-utils": "workspace:*",
"eslint-import-resolver-alias": "^1.1.2",
"globals": "^17.12.0",
"typescript-eslint": "^8.70.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const sharedRules = {
module.exports = [
...wordpress.configs.recommended,
{
files: [ '**/*.js', '**/*.jsx' ],
files: [ '**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs' ],
languageOptions: {
globals: sharedGlobals,
},
Expand Down
51 changes: 35 additions & 16 deletions packages/eslint-config/src/utils/resolve-import-aliases.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
const fs = require( 'fs' );
const path = require( 'path' );
const { tryResolveThemeContext } = require( '@yardinternet/shared-utils' );

/**
* Resolves the `import/resolver` aliases for the current project layout.
*
* - brave-root (default): the `@sage/scripts` alias, unchanged.
* - theme-root (cwd is a theme — no `web/app/themes`, has `style.css`): point
* both `@<theme>/scripts` and the `@sage/scripts` at the local
* `./resources/scripts` so theme imports resolve.
* - brave-root: `@<theme>/scripts` for every theme in the themes directory.
* - theme-root (cwd is a theme): `@<theme>/scripts` points at the local
* `./resources/scripts`.
*
* `@sage/scripts` is kept in both layouts, aliased to the default theme, so
* starter code importing it keeps resolving in projects that do not name their
* parent theme `sage`.
*/
const resolveImportAliases = () => {
const cwd = process.cwd();
const isBraveRoot = fs.existsSync( path.resolve( cwd, 'web/app/themes' ) );
const context = tryResolveThemeContext();

if ( ! context ) {
// Not a WordPress project layout — assume the cwd is the theme.
const themeName = path.basename( process.cwd() );

if ( isBraveRoot ) {
return [
[ '@sage/scripts', './web/app/themes/sage/resources/scripts' ],
[ `@${ themeName }/scripts`, './resources/scripts' ],
[ '@sage/scripts', './resources/scripts' ],
];
}

const themeName = path.basename( cwd );
const scriptsPath = ( theme ) =>
context.mode === 'theme-root'
? './resources/scripts'
: `./${ theme.relDirPosix }/resources/scripts`;

const aliases = context.themes.map( ( theme ) => [
`@${ theme.name }/scripts`,
scriptsPath( theme ),
] );

if ( ! aliases.some( ( [ alias ] ) => alias === '@sage/scripts' ) ) {
const defaultTheme = context.themes.find(
( theme ) => theme.name === context.defaultTheme
);

if ( defaultTheme ) {
aliases.push( [ '@sage/scripts', scriptsPath( defaultTheme ) ] );
}
}

return [
[ `@${ themeName }/scripts`, './resources/scripts' ],
// Brave-root alias kept so shared/starter code importing `@sage/scripts`
// keeps resolving in a theme-root build.
[ '@sage/scripts', './resources/scripts' ],
];
return aliases;
};

module.exports = resolveImportAliases;
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,8 @@ exports[`eslint config files patterns match snapshot 1`] = `
[
"**/*.js",
"**/*.jsx",
"**/*.cjs",
"**/*.mjs",
],
[
"**/*.ts",
Expand Down
59 changes: 59 additions & 0 deletions packages/eslint-config/tests/import-aliases.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

const path = require( 'path' );
const resolveImportAliases = require( '../src/utils/resolve-import-aliases' );
const { clearThemeContextCache } = require( '@yardinternet/shared-utils' );

const FIXTURES = path.resolve( __dirname, '../../shared-utils/tests/fixtures' );

const originalCwd = process.cwd();

const useProject = ( ...segments ) => {
process.chdir( path.join( FIXTURES, ...segments ) );
clearThemeContextCache();
};

afterEach( () => {
process.chdir( originalCwd );
clearThemeContextCache();
} );

test( 'brave-root aliases every theme and keeps @sage/scripts', () => {
useProject( 'bedrock' );

expect( resolveImportAliases() ).toEqual( [
[ '@sage/scripts', './web/app/themes/sage/resources/scripts' ],
[
'@sage-child/scripts',
'./web/app/themes/sage-child/resources/scripts',
],
] );
} );

test( '@sage/scripts falls back to the default theme when no sage exists', () => {
useProject( 'configured' );

expect( resolveImportAliases() ).toEqual( [
[ '@alpha/scripts', './src/themes/alpha/resources/scripts' ],
[ '@zulu/scripts', './src/themes/zulu/resources/scripts' ],
[ '@sage/scripts', './src/themes/zulu/resources/scripts' ],
] );
} );

test( 'theme-root points every alias at the local resources', () => {
useProject( 'theme-root', 'basis' );

expect( resolveImportAliases() ).toEqual( [
[ '@basis/scripts', './resources/scripts' ],
[ '@sage/scripts', './resources/scripts' ],
] );
} );

test( 'unresolvable layouts keep the historical theme-root guess', () => {
useProject( 'empty' );

expect( resolveImportAliases() ).toEqual( [
[ '@empty/scripts', './resources/scripts' ],
[ '@sage/scripts', './resources/scripts' ],
] );
} );
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

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

Loading