fix(js): avoid nested paths in workspaces because they can lead to future issues (#29553)

For the new TS setup, we currently use nested glob patterns: `apps/**`,
`libs/**`, and `packages/**`.

Nested paths can result into too many projects being matched. For
example, if `libs/mylib/src/__fixtures__/package.json` is there for
testing, it will be matched as a project and likely result in an error.

Other tools like turborepo also caution against this:
https://turbo.build/repo/docs/crafting-your-repository/structuring-a-repository#declaring-directories-for-packages

If users want to, they could change to nested `**` paths, but we should
not use it by default.

Note: For CNW, we only use `apps/*` by default since that is where the
project lives. When users do `nx g lib packages/foo` then `packages/*`
will be added automatically.

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
Use nested `**` paths.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
Don't use nested `**` paths.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Jack Hsu 2025-01-08 18:08:39 -05:00 committed by GitHub
parent e99cf07ffd
commit fb318005f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 37 additions and 23 deletions

View File

@ -33,13 +33,13 @@ describe('Node Applications', () => {
'pnpm-workspace.yaml',
`
packages:
- 'apps/**'
- 'packages/**'
- 'apps/*'
- 'packages/*'
`
);
} else {
updateJson('package.json', (json) => {
json.workspaces = ['apps/**', 'packages/**'];
json.workspaces = ['apps/*', 'packages/*'];
return json;
});
}

View File

@ -23,13 +23,13 @@ describe('Vue Plugin', () => {
'pnpm-workspace.yaml',
`
packages:
- 'apps/**'
- 'packages/**'
- 'apps/*'
- 'packages/*'
`
);
} else {
updateJson('package.json', (json) => {
json.workspaces = ['apps/**', 'packages/**'];
json.workspaces = ['apps/*', 'packages/*'];
return json;
});
}

View File

@ -115,8 +115,8 @@ function getWorkspaceGlobsFromPreset(preset: string): string[] {
case Preset.RemixMonorepo:
case Preset.VueMonorepo:
case Preset.WebComponents:
return ['apps/**', 'libs/**', 'packages/**'];
return ['apps/*'];
default:
return ['libs/**', 'packages/**'];
return ['packages/*'];
}
}

View File

@ -198,10 +198,11 @@ export function addProjectToTsSolutionWorkspace(
tree: Tree,
projectDir: string
) {
// If dir is "libs/foo" then use "libs/**" so we don't need so many entries in the workspace file.
// If dir is "libs/foo" then use "libs/*" so we don't need so many entries in the workspace file.
// If dir is nested like "libs/shared/foo" then we add "libs/shared/*".
// If the dir is just "foo" then we have to add it as is.
const baseDir = dirname(projectDir);
const pattern = baseDir === '.' ? projectDir : `${baseDir}/**`;
const pattern = baseDir === '.' ? projectDir : `${baseDir}/*`;
if (tree.exists('pnpm-workspace.yaml')) {
const { load, dump } = require('@zkochan/js-yaml');
const workspaceFile = tree.read('pnpm-workspace.yaml', 'utf-8');

View File

@ -1279,7 +1279,7 @@ describe('app', () => {
beforeEach(() => {
appTree = createTreeWithEmptyWorkspace();
updateJson(appTree, 'package.json', (json) => {
json.workspaces = ['packages/**', 'apps/**'];
json.workspaces = ['packages/*', 'apps/*'];
return json;
});
writeJson(appTree, 'tsconfig.base.json', {
@ -1482,10 +1482,10 @@ describe('app', () => {
const packageJson = readJson(appTree, 'package.json');
expect(packageJson.workspaces).toEqual([
'packages/**',
'apps/**',
'packages/*',
'apps/*',
'myapp',
'libs/**',
'libs/*',
]);
});
@ -1519,11 +1519,24 @@ describe('app', () => {
unitTestRunner: 'none',
e2eTestRunner: 'none',
});
await applicationGenerator(appTree, {
directory: 'packages/shared/util',
addPlugin: true,
linter: Linter.EsLint,
style: 'none',
bundler: 'vite',
unitTestRunner: 'none',
e2eTestRunner: 'none',
});
const pnpmContent = appTree.read('pnpm-workspace.yaml', 'utf-8');
const pnpmWorkspaceFile = load(pnpmContent);
expect(pnpmWorkspaceFile.packages).toEqual(['myapp', 'apps/**']);
expect(pnpmWorkspaceFile.packages).toEqual([
'myapp',
'apps/*',
'packages/shared/*',
]);
});
});

View File

@ -230,7 +230,7 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
"scripts": {},
"version": "0.0.0",
"workspaces": [
"packages/**",
"packages/*",
],
}
`);
@ -294,7 +294,7 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
defaultBase: 'main',
packageManager: 'npm',
isCustomPreset: false,
workspaceGlobs: ['apps/**', 'packages/**'],
workspaceGlobs: ['apps/*', 'packages/*'],
});
const packageJson = readJson(tree, '/proj/package.json');
@ -310,8 +310,8 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
"scripts": {},
"version": "0.0.0",
"workspaces": [
"apps/**",
"packages/**",
"apps/*",
"packages/*",
],
}
`);
@ -326,14 +326,14 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
defaultBase: 'main',
packageManager: 'pnpm',
isCustomPreset: false,
workspaceGlobs: ['apps/**', 'packages/**'],
workspaceGlobs: ['apps/*', 'packages/*'],
});
const packageJson = tree.read('/proj/pnpm-workspace.yaml', 'utf-8');
expect(packageJson).toMatchInlineSnapshot(`
"packages:
- "apps/**"
- "packages/**"
- "apps/*"
- "packages/*"
"
`);
});

View File

@ -428,7 +428,7 @@ function setUpWorkspacesInPackageJson(tree: Tree, options: NormalizedSchema) {
options.preset === Preset.Express) &&
options.workspaces)
) {
const workspaces = options.workspaceGlobs ?? ['packages/**'];
const workspaces = options.workspaceGlobs ?? ['packages/*'];
if (options.packageManager === 'pnpm') {
tree.write(
join(options.directory, 'pnpm-workspace.yaml'),