Skip to content

Commit 0005f02

Browse files
committed
fix(test): merge module augmentation types and re-export BrowserCommands to resolve MISSING_EXPORT warnings
1 parent c517536 commit 0005f02

1 file changed

Lines changed: 64 additions & 17 deletions

File tree

packages/test/build.ts

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,7 +2065,7 @@ export * from '../dist/@vitest/browser/context.d.ts'
20652065
}
20662066

20672067
/**
2068-
* Patch module augmentations in global.d.*.d.ts files to use relative paths.
2068+
* Patch module augmentations in global.d.*.d.ts files.
20692069
*
20702070
* The original vitest types use module augmentation like:
20712071
* declare module "@vitest/expect" { interface Assertion<T> { toMatchSnapshot: ... } }
@@ -2074,12 +2074,11 @@ export * from '../dist/@vitest/browser/context.d.ts'
20742074
* "@vitest/expect" doesn't exist as a package for consumers. This breaks the
20752075
* module augmentation - TypeScript can't find @vitest/expect to augment.
20762076
*
2077-
* The fix: Change module augmentation to use relative paths that TypeScript CAN resolve:
2078-
* declare module "../@vitest/expect/index.js" { ... }
2079-
*
2080-
* This makes TypeScript augment the same module that our index.d.ts imports from,
2081-
* so the augmented properties (toMatchSnapshot, toMatchInlineSnapshot, etc.)
2082-
* appear on the Assertion type that consumers import.
2077+
* The fix has two parts:
2078+
* 1. Change module augmentation to use relative paths that TypeScript CAN resolve:
2079+
* declare module "../@vitest/expect/index.js" { ... }
2080+
* 2. Merge augmented interface/type definitions into the target .d.ts files so that
2081+
* downstream DTS bundlers (rolldown) can resolve them without cross-file augmentation.
20832082
*/
20842083
async function patchModuleAugmentations() {
20852084
console.log('\nPatching module augmentations in global.d.*.d.ts files...');
@@ -2097,31 +2096,79 @@ async function patchModuleAugmentations() {
20972096
return;
20982097
}
20992098

2100-
// Module augmentation mappings: bare specifier -> relative path from chunks/
2101-
const augmentationMappings: Record<string, string> = {
2102-
'@vitest/expect': '../@vitest/expect/index.js',
2103-
'@vitest/runner': '../@vitest/runner/index.js',
2099+
// Module augmentation mappings: bare specifier -> [relative path, target .d.ts file]
2100+
const augmentationMappings: Record<string, { relativePath: string; targetFile: string }> = {
2101+
'@vitest/expect': {
2102+
relativePath: '../@vitest/expect/index.js',
2103+
targetFile: join(distDir, '@vitest/expect/index.d.ts'),
2104+
},
2105+
'@vitest/runner': {
2106+
relativePath: '../@vitest/runner/index.js',
2107+
targetFile: join(distDir, '@vitest/runner/utils.d.ts'),
2108+
},
21042109
};
21052110

21062111
for (const file of globalDtsFiles) {
21072112
let content = await readFile(file, 'utf-8');
21082113
let modified = false;
21092114

2110-
for (const [bareSpecifier, relativePath] of Object.entries(augmentationMappings)) {
2115+
for (const [bareSpecifier, { relativePath, targetFile }] of Object.entries(augmentationMappings)) {
21112116
const oldPattern = `declare module "${bareSpecifier}"`;
2112-
const newPattern = `declare module "${relativePath}"`;
21132117

2114-
if (content.includes(oldPattern)) {
2115-
content = content.replaceAll(oldPattern, newPattern);
2116-
modified = true;
2117-
console.log(` Patched: ${bareSpecifier} -> ${relativePath} in ${basename(file)}`);
2118+
if (!content.includes(oldPattern)) continue;
2119+
2120+
// Extract the augmentation block content using brace matching
2121+
const startIdx = content.indexOf(oldPattern);
2122+
const braceStart = content.indexOf('{', startIdx);
2123+
if (braceStart === -1) continue;
2124+
2125+
let depth = 0;
2126+
let braceEnd = -1;
2127+
for (let i = braceStart; i < content.length; i++) {
2128+
if (content[i] === '{') depth++;
2129+
else if (content[i] === '}') {
2130+
depth--;
2131+
if (depth === 0) {
2132+
braceEnd = i;
2133+
break;
2134+
}
2135+
}
21182136
}
2137+
if (braceEnd === -1) continue;
2138+
2139+
const innerContent = content.slice(braceStart + 1, braceEnd).trim();
2140+
2141+
// Merge augmented types into the target .d.ts file
2142+
if (innerContent && existsSync(targetFile)) {
2143+
let targetContent = await readFile(targetFile, 'utf-8');
2144+
const exportBlock = innerContent.replace(/^(\t*)interface /gm, '$1export interface ');
2145+
targetContent += `\n// Merged from module augmentation: declare module "${bareSpecifier}"\n${exportBlock}\n`;
2146+
await writeFile(targetFile, targetContent, 'utf-8');
2147+
console.log(` Merged augmentation "${bareSpecifier}" into ${basename(targetFile)}`);
2148+
}
2149+
2150+
// Rewrite declare module path to relative
2151+
const newPattern = `declare module "${relativePath}"`;
2152+
content = content.replaceAll(oldPattern, newPattern);
2153+
modified = true;
2154+
console.log(` Patched: ${bareSpecifier} -> ${relativePath} in ${basename(file)}`);
21192155
}
21202156

21212157
if (modified) {
21222158
await writeFile(file, content, 'utf-8');
21232159
}
21242160
}
2161+
2162+
// Re-export BrowserCommands from context.d.ts (imported but not exported)
2163+
const contextDtsPath = join(distDir, '@vitest/browser/context.d.ts');
2164+
if (existsSync(contextDtsPath)) {
2165+
let content = await readFile(contextDtsPath, 'utf-8');
2166+
if (content.includes('BrowserCommands') && !content.match(/export\s+(type\s+)?\{[^}]*BrowserCommands/)) {
2167+
content += '\nexport type { BrowserCommands };\n';
2168+
await writeFile(contextDtsPath, content, 'utf-8');
2169+
console.log(' Added BrowserCommands re-export to context.d.ts');
2170+
}
2171+
}
21252172
}
21262173

21272174
/**

0 commit comments

Comments
 (0)