Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/calm-lions-debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@module-federation/dts-plugin': patch
---

Preserve inferred source roots in temporary DTS projects and retain reproducible TypeScript diagnostics after failed dependency scans or declaration generation.
2 changes: 1 addition & 1 deletion apps/website-new/docs/en/configure/dts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ Instance of compiled type
- Required: No
- Default value: `true`

Whether to delete the temporary tsconfig configuration file.
Whether to delete the temporary tsconfig configuration file. If type generation fails, a diagnostic copy of the effective config and compiler output is retained under `.mf/diagnostics/dts/` even when this option is enabled.

## consumeTypes

Expand Down
7 changes: 7 additions & 0 deletions apps/website-new/docs/en/guide/troubleshooting/type.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ When compiling TS types for exposed (`exposes`) files, the current project's `ts
1. Remove tsconfig.json `incremental` and `tsBuildInfoFile` from the `cmd` command.
2. Run the `cmd` from the error message in terminal and fix the file or `tsconfig` based on the output.

When the TypeScript dependency scan or declaration generation fails, Module Federation copies the effective temporary configuration and compiler output to:

- `.mf/diagnostics/dts/list-files/` for dependency-scan warnings
- `.mf/diagnostics/dts/generate-types/` for fatal `TYPE-001` failures

Each directory contains `tsconfig.json` and `compiler.log`. The log records the exact compiler command and TypeScript version, so the failure can be reproduced even when [`deleteTsConfig`](/configure/dts.html#deletetsconfig) is enabled.

If you want to ignore TS type checking errors, set [`compilerOptions.noCheck`](https://www.typescriptlang.org/tsconfig/#noCheck) to `true` in `tsconfig.json` (requires TS 5.5+).

If the `cmd` runs without error but you still get a TS compile failure, check the `exposes` field in `ModuleFederationPlugin`:
Expand Down
1 change: 1 addition & 0 deletions packages/dts-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"directory-tree": "3.5.2",
"rimraf": "~6.0.1",
"typescript": "6.0.3",
"typescript-5": "npm:typescript@5.9.3",
"typescript-7": "npm:typescript@7.0.2",
"vue": "^3.5.13",
"vue-tsc": "^2.2.10",
Expand Down
119 changes: 111 additions & 8 deletions packages/dts-plugin/src/core/configurations/remotePlugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'fs';
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from 'fs';
import { createRequire } from 'module';
import os from 'os';
import { dirname, join, resolve } from 'path';
import { afterEach, describe, expect, it } from '@rstest/core';

import { formatCommandForDisplay } from '../lib/typeScriptDiagnostics';
import { retrieveRemoteConfig } from './remotePlugin';

describe('hostPlugin', () => {
Expand Down Expand Up @@ -44,15 +53,19 @@ describe('hostPlugin', () => {
return context;
};

const installTypeScript7 = (context: string) => {
const typeScript7PackageJsonPath = requireFromTest.resolve(
'typescript-7/package.json',
const installTypeScript = (context: string, packageName: string) => {
const typeScriptPackageJsonPath = requireFromTest.resolve(
`${packageName}/package.json`,
);
const typeScript7Root = dirname(typeScript7PackageJsonPath);
const typeScriptRoot = join(context, 'node_modules', 'typescript');
mkdirSync(dirname(typeScriptRoot), { recursive: true });
symlinkSync(typeScript7Root, typeScriptRoot, 'junction');
const installedTypeScriptRoot = dirname(typeScriptPackageJsonPath);
const projectTypeScriptRoot = join(context, 'node_modules', 'typescript');
mkdirSync(dirname(projectTypeScriptRoot), { recursive: true });
symlinkSync(installedTypeScriptRoot, projectTypeScriptRoot, 'junction');
};
const installTypeScript5 = (context: string) =>
installTypeScript(context, 'typescript-5');
const installTypeScript7 = (context: string) =>
installTypeScript(context, 'typescript-7');

afterEach(() => {
for (const tempDir of tempDirs.splice(0)) {
Expand All @@ -74,6 +87,11 @@ describe('hostPlugin', () => {
};

const typeScriptScenarios = [
{
name: 'TypeScript 5',
setup: installTypeScript5,
expectedModuleResolution: 'node10',
},
{
name: 'TypeScript 6',
setup: undefined,
Expand Down Expand Up @@ -368,6 +386,50 @@ describe('hostPlugin', () => {
expect(tsConfig.references).toBeUndefined();
});

it('includes exposed workspace sources when rootDir is inferred', () => {
const context = createTemporaryProject({
'src/index.ts': 'export const local = 1;\n',
'shared/button.ts': "export { dependency } from './dependency';\n",
'shared/dependency.ts': 'export const dependency = 1;\n',
});
writeFileSync(
join(context, 'tsconfig.json'),
JSON.stringify(
{
compilerOptions: {
target: 'es2017',
module: 'esnext',
moduleResolution: 'node10',
strict: true,
},
include: ['src'],
},
null,
2,
),
);

const { tsConfig } = retrieveRemoteConfig({
context,
tsConfigPath: './tsconfig.json',
moduleFederationConfig: withTypeScriptScenario(scenario, context, {
name: 'remotePluginTestHost',
filename: 'remoteEntry.js',
exposes: {
'./button': './shared/button.ts',
},
}),
});

expect(tsConfig.compilerOptions.rootDir).toBe(context);
expect(tsConfig.files).toEqual(
expect.arrayContaining([
resolve(context, 'shared/button.ts'),
resolve(context, 'shared/dependency.ts'),
]),
);
});

it('applies custom output folders', () => {
const context = createProjectWithDependencies();
const { tsConfig, remoteOptions } = retrieveRemoteConfig({
Expand Down Expand Up @@ -521,6 +583,46 @@ describe('hostPlugin', () => {
resolve(context, 'src/components/foo/index.ts'),
);
});

if (scenario.name === 'TypeScript 7') {
it('preserves diagnostics when dependency scanning fails', () => {
const { context } = resolveExpose(
'./src/components/foo.generated.jsx',
{
'src/components/foo.generated.jsx':
'export const Foo = () => null;\n',
},
);
const diagnosticDir = join(
context,
'.mf/diagnostics/dts/list-files',
);
const diagnosticConfigPath = join(diagnosticDir, 'tsconfig.json');
const diagnosticLogPath = join(diagnosticDir, 'compiler.log');

expect(existsSync(diagnosticConfigPath)).toBe(true);
expect(existsSync(diagnosticLogPath)).toBe(true);
const diagnosticConfig = JSON.parse(
readFileSync(diagnosticConfigPath, 'utf8'),
);
expect(diagnosticConfig.compilerOptions.rootDir).toBe(context);
expect(diagnosticConfig.files).toContain(
resolve(context, 'src/components/foo.generated.jsx'),
);
expect(readFileSync(diagnosticLogPath, 'utf8')).toContain(
'TypeScript version: 7.0.2',
);
const diagnosticLog = readFileSync(diagnosticLogPath, 'utf8');
expect(diagnosticLog).toContain('--listFilesOnly');
expect(diagnosticLog).toContain('TS6504');
const diagnosticCommand = diagnosticLog
.split(/\r?\n/)
.find((line) => line.startsWith('Command:'));
expect(diagnosticCommand).toContain(
formatCommandForDisplay('', [diagnosticConfigPath]).trim(),
);
});
}
});
}
});
Expand All @@ -546,6 +648,7 @@ describe('hostPlugin', () => {
const tsConfigPath = join(__dirname, tsConfigFile);
const typeScriptContext = createTemporaryProject({});
const { tsConfig } = retrieveRemoteConfig({
context: typeScriptContext,
moduleFederationConfig: withTypeScriptScenario(
scenario,
typeScriptContext,
Expand Down
Loading
Loading