Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(workspace): resolve glob pattern once to avoid name collision #6489

Merged
merged 1 commit into from
Sep 13, 2024
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
7 changes: 2 additions & 5 deletions docs/guide/workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default [
```
:::

Vitest will consider every folder in `packages` as a separate project even if it doesn't have a config file inside.
Vitest will consider every folder in `packages` as a separate project even if it doesn't have a config file inside. Since Vitest 2.1, if this glob pattern matches any file it will be considered a Vitest config even if it doesn't have a `vitest` in its name.

::: warning
Vitest will not consider the root config as a workspace project (so it will not run tests specified in `include`) unless it is specified in this config.
Expand All @@ -44,10 +44,6 @@ export default [

This pattern will only include projects with `vitest.config` file that includes `e2e` and `unit` before the extension.

::: warning
If you are referencing filenames with glob pattern, make sure your config file starts with `vite.config` or `vitest.config`. Otherwise Vitest will skip it.
:::

You can also define projects with inline config. Workspace file supports using both syntaxes at the same time.

:::code-group
Expand All @@ -56,6 +52,7 @@ import { defineWorkspace } from 'vitest/config'

// defineWorkspace provides a nice type hinting DX
export default defineWorkspace([
// matches every folder and file inside the `packages` folder
'packages/*',
{
// add "extends" to merge two configs together
Expand Down
61 changes: 35 additions & 26 deletions packages/vitest/src/node/workspace/resolveWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ export async function resolveWorkspace(
const cwd = process.cwd()

const projects: WorkspaceProject[] = []
const fileProjects = [...configFiles, ...nonConfigDirectories]

try {
// we have to resolve them one by one because CWD should depend on the project
for (const filepath of [...configFiles, ...nonConfigDirectories]) {
for (const filepath of fileProjects) {
// if file leads to the root config, then we can just reuse it because we already initialized it
if (vitest.server.config.configFile === filepath) {
const project = await vitest._createCoreProject()
Expand Down Expand Up @@ -111,12 +112,20 @@ export async function resolveWorkspace(
const name = project.getName()
if (names.has(name)) {
const duplicate = resolvedProjects.find(p => p.getName() === name && p !== project)!
const filesError = fileProjects.length
? [
'\n\nYour config matched these files:\n',
fileProjects.map(p => ` - ${relative(vitest.config.root, p)}`).join('\n'),
'\n\n',
].join('')
: [' ']
throw new Error([
`Project name "${name}"`,
project.server.config.configFile ? ` from "${relative(vitest.config.root, project.server.config.configFile)}"` : '',
' is not unique.',
duplicate?.server.config.configFile ? ` The project is already defined by "${relative(vitest.config.root, duplicate.server.config.configFile)}".` : '',
' All projects in a workspace should have unique names. Make sure your configuration is correct.',
filesError,
'All projects in a workspace should have unique names. Make sure your configuration is correct.',
].join(''))
}
names.add(name)
Expand Down Expand Up @@ -196,36 +205,36 @@ async function resolveWorkspaceProjectConfigs(
else {
projectsOptions.push(await definition)
}
}

if (workspaceGlobMatches.length) {
const globOptions: GlobOptions = {
absolute: true,
dot: true,
onlyFiles: false,
cwd: vitest.config.root,
expandDirectories: false,
ignore: ['**/node_modules/**', '**/*.timestamp-*'],
}
if (workspaceGlobMatches.length) {
const globOptions: GlobOptions = {
absolute: true,
dot: true,
onlyFiles: false,
cwd: vitest.config.root,
expandDirectories: false,
ignore: ['**/node_modules/**', '**/*.timestamp-*'],
}

const workspacesFs = await glob(workspaceGlobMatches, globOptions)
const workspacesFs = await glob(workspaceGlobMatches, globOptions)

await Promise.all(workspacesFs.map(async (filepath) => {
// directories are allowed with a glob like `packages/*`
// in this case every directory is treated as a project
if (filepath.endsWith('/')) {
const configFile = await resolveDirectoryConfig(filepath)
if (configFile) {
workspaceConfigFiles.push(configFile)
}
else {
nonConfigProjectDirectories.push(filepath)
}
await Promise.all(workspacesFs.map(async (filepath) => {
// directories are allowed with a glob like `packages/*`
// in this case every directory is treated as a project
if (filepath.endsWith('/')) {
const configFile = await resolveDirectoryConfig(filepath)
if (configFile) {
workspaceConfigFiles.push(configFile)
}
else {
workspaceConfigFiles.push(filepath)
nonConfigProjectDirectories.push(filepath)
}
}))
}
}
else {
workspaceConfigFiles.push(filepath)
}
}))
}

const projectConfigFiles = Array.from(new Set(workspaceConfigFiles))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "b"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { test } from 'vitest';

test('test - b')
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "a"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { test } from 'vitest';

test('test - a')
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default [
'projects/*',
'apps/*'
]
18 changes: 17 additions & 1 deletion test/config/test/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,29 @@ it('runs the workspace if there are several vitest config files', async () => {
expect(stdout).toContain('2 + 2 = 4')
})

it('correctly resolves workspace projects with a several folder globs', async () => {
const { stderr, stdout } = await runVitest({
root: 'fixtures/workspace/several-folders',
workspace: './fixtures/workspace/several-folders/vitest.workspace.ts',
})
expect(stderr).toBe('')
expect(stdout).toContain('test - a')
expect(stdout).toContain('test - b')
})

it('fails if project names are identical with a nice error message', async () => {
const { stderr } = await runVitest({
root: 'fixtures/workspace/invalid-duplicate-configs',
workspace: './fixtures/workspace/invalid-duplicate-configs/vitest.workspace.ts',
}, [], 'test', {}, { fails: true })
expect(stderr).toContain(
'Project name "test" from "vitest2.config.js" is not unique. The project is already defined by "vitest1.config.js". All projects in a workspace should have unique names. Make sure your configuration is correct.',
`Project name "test" from "vitest2.config.js" is not unique. The project is already defined by "vitest1.config.js".

Your config matched these files:
- vitest1.config.js
- vitest2.config.js

All projects in a workspace should have unique names. Make sure your configuration is correct.`,
)
})

Expand Down
Loading