Skip to content

Commit c7b66cd

Browse files
committed
fix types
1 parent 7ee085e commit c7b66cd

4 files changed

Lines changed: 119 additions & 5 deletions

File tree

packages/cli/build.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ async function createShimForExport(
156156
// Check if it's a type-only export
157157
if (exportValue.endsWith('.d.ts')) {
158158
const dtsPath = join(shimDirForFile, `${baseFileName}.d.ts`);
159-
await writeFile(dtsPath, `export * from '${testImportSpecifier}';\n`);
159+
// Include side-effect import to preserve module augmentations (e.g., toMatchSnapshot on Assertion)
160+
await writeFile(
161+
dtsPath,
162+
`import '${testImportSpecifier}';\nexport * from '${testImportSpecifier}';\n`,
163+
);
160164
return { types: `./dist/test/${shimBaseName}.d.ts` };
161165
}
162166

@@ -184,7 +188,11 @@ async function createShimForExport(
184188

185189
if (value.types && typeof value.types === 'string') {
186190
const dtsPath = join(shimDirForFile, `${baseFileName}.d.ts`);
187-
await writeFile(dtsPath, `export * from '${testImportSpecifier}';\n`);
191+
// Include side-effect import to preserve module augmentations (e.g., toMatchSnapshot on Assertion)
192+
await writeFile(
193+
dtsPath,
194+
`import '${testImportSpecifier}';\nexport * from '${testImportSpecifier}';\n`,
195+
);
188196
(result as Record<string, string>).types = `./dist/test/${shimBaseName}.d.ts`;
189197
}
190198

@@ -220,7 +228,11 @@ async function createConditionalShim(
220228
// Handle top-level types (flat structure like { types, require, default })
221229
if (value.types && typeof value.types === 'string' && !value.import) {
222230
const dtsPath = join(shimDir, `${baseFileName}.d.ts`);
223-
await writeFile(dtsPath, `export * from '${testImportSpecifier}';\n`);
231+
// Include side-effect import to preserve module augmentations (e.g., toMatchSnapshot on Assertion)
232+
await writeFile(
233+
dtsPath,
234+
`import '${testImportSpecifier}';\nexport * from '${testImportSpecifier}';\n`,
235+
);
224236
(result as Record<string, string>).types = `./dist/test/${shimBaseName}.d.ts`;
225237
}
226238

@@ -244,7 +256,11 @@ async function createConditionalShim(
244256

245257
if (importValue.types && typeof importValue.types === 'string') {
246258
const dtsPath = join(shimDir, `${baseFileName}.d.ts`);
247-
await writeFile(dtsPath, `export * from '${testImportSpecifier}';\n`);
259+
// Include side-effect import to preserve module augmentations (e.g., toMatchSnapshot on Assertion)
260+
await writeFile(
261+
dtsPath,
262+
`import '${testImportSpecifier}';\nexport * from '${testImportSpecifier}';\n`,
263+
);
248264
importResult.types = `./dist/test/${shimBaseName}.d.ts`;
249265
}
250266

@@ -276,7 +292,11 @@ async function createConditionalShim(
276292

277293
if (requireValue.types && typeof requireValue.types === 'string') {
278294
const dctsPath = join(shimDir, `${baseFileName}.d.cts`);
279-
await writeFile(dctsPath, `export * from '${testImportSpecifier}';\n`);
295+
// Include side-effect import to preserve module augmentations (e.g., toMatchSnapshot on Assertion)
296+
await writeFile(
297+
dctsPath,
298+
`import '${testImportSpecifier}';\nexport * from '${testImportSpecifier}';\n`,
299+
);
280300
requireResult.types = `./dist/test/${shimBaseName}.d.cts`;
281301
}
282302

packages/test/build.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ await createModuleRunnerStub();
211211
await createNodeEntry();
212212
await copyBrowserClientFiles();
213213
await createBrowserEntryFiles();
214+
await patchModuleAugmentations();
215+
await patchChaiTypeReference();
214216
const pluginExports = await createPluginExports();
215217
await mergePackageJson(pluginExports);
216218
await validateExternalDeps();
@@ -1816,6 +1818,94 @@ export * from '../dist/@vitest/browser/context.d.ts'
18161818
console.log(' Created browser/context.d.ts');
18171819
}
18181820

1821+
/**
1822+
* Patch module augmentations in global.d.*.d.ts files to use relative paths.
1823+
*
1824+
* The original vitest types use module augmentation like:
1825+
* declare module "@vitest/expect" { interface Assertion<T> { toMatchSnapshot: ... } }
1826+
*
1827+
* Since we bundle @vitest/* packages inside dist/@vitest/*, the bare specifier
1828+
* "@vitest/expect" doesn't exist as a package for consumers. This breaks the
1829+
* module augmentation - TypeScript can't find @vitest/expect to augment.
1830+
*
1831+
* The fix: Change module augmentation to use relative paths that TypeScript CAN resolve:
1832+
* declare module "../@vitest/expect/index.js" { ... }
1833+
*
1834+
* This makes TypeScript augment the same module that our index.d.ts imports from,
1835+
* so the augmented properties (toMatchSnapshot, toMatchInlineSnapshot, etc.)
1836+
* appear on the Assertion type that consumers import.
1837+
*/
1838+
async function patchModuleAugmentations() {
1839+
console.log('\nPatching module augmentations in global.d.*.d.ts files...');
1840+
1841+
const chunksDir = join(distDir, 'chunks');
1842+
const globalDtsFiles: string[] = [];
1843+
1844+
// Find all global.d.*.d.ts files
1845+
for await (const file of fsGlob(join(chunksDir, 'global.d.*.d.ts'))) {
1846+
globalDtsFiles.push(file);
1847+
}
1848+
1849+
if (globalDtsFiles.length === 0) {
1850+
console.log(' No global.d.*.d.ts files found');
1851+
return;
1852+
}
1853+
1854+
// Module augmentation mappings: bare specifier -> relative path from chunks/
1855+
const augmentationMappings: Record<string, string> = {
1856+
'@vitest/expect': '../@vitest/expect/index.js',
1857+
'@vitest/runner': '../@vitest/runner/index.js',
1858+
};
1859+
1860+
for (const file of globalDtsFiles) {
1861+
let content = await readFile(file, 'utf-8');
1862+
let modified = false;
1863+
1864+
for (const [bareSpecifier, relativePath] of Object.entries(augmentationMappings)) {
1865+
const oldPattern = `declare module "${bareSpecifier}"`;
1866+
const newPattern = `declare module "${relativePath}"`;
1867+
1868+
if (content.includes(oldPattern)) {
1869+
content = content.replaceAll(oldPattern, newPattern);
1870+
modified = true;
1871+
console.log(` Patched: ${bareSpecifier} -> ${relativePath} in ${basename(file)}`);
1872+
}
1873+
}
1874+
1875+
if (modified) {
1876+
await writeFile(file, content, 'utf-8');
1877+
}
1878+
}
1879+
}
1880+
1881+
/**
1882+
* Add triple-slash reference to @types/chai in @vitest/expect types.
1883+
*
1884+
* The @vitest/expect types use the Chai namespace (e.g., Chai.Assertion) which
1885+
* is defined in @types/chai. Without a reference directive, TypeScript won't
1886+
* automatically find the Chai types, causing the `not` property and other
1887+
* chai-specific features to be missing from the Assertion interface.
1888+
*/
1889+
async function patchChaiTypeReference() {
1890+
console.log('\nAdding @types/chai reference to @vitest/expect types...');
1891+
1892+
const expectIndexDts = join(distDir, '@vitest/expect/index.d.ts');
1893+
1894+
let content = await readFile(expectIndexDts, 'utf-8');
1895+
1896+
// Check if reference already exists
1897+
if (content.includes('/// <reference types="chai"')) {
1898+
console.log(' Reference already exists, skipping');
1899+
return;
1900+
}
1901+
1902+
// Add triple-slash reference at the top
1903+
content = `/// <reference types="chai" />\n${content}`;
1904+
1905+
await writeFile(expectIndexDts, content, 'utf-8');
1906+
console.log(' Added /// <reference types="chai" /> to @vitest/expect/index.d.ts');
1907+
}
1908+
18191909
/**
18201910
* Create /plugins/* exports for all copied @vitest/* packages.
18211911
* This allows pnpm overrides to redirect @vitest/* imports to our copied versions.

packages/test/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
}
271271
},
272272
"dependencies": {
273+
"@types/chai": "^5.2.2",
273274
"@voidzero-dev/vite-plus-core": "workspace:*",
274275
"es-module-lexer": "^1.7.0",
275276
"expect-type": "^1.2.2",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)