Skip to content

Commit

Permalink
fix(workspace): resolve glob pattern once to avoid name collision (#6489
Browse files Browse the repository at this point in the history
)
  • Loading branch information
sheremet-va committed Sep 13, 2024
1 parent 16aa76c commit 36b5ace
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 32 deletions.
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

0 comments on commit 36b5ace

Please sign in to comment.