diff --git a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts index 493f70ba1f775..df0ad7c3fcc5a 100644 --- a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts @@ -9,6 +9,7 @@ import { type ExtractorMessage, type ExtractorResult, } from '@microsoft/api-extractor'; +import * as path from 'node:path'; import { basename, join } from 'node:path'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync, readdirSync } from 'node:fs'; import { execSync } from 'node:child_process'; @@ -18,6 +19,7 @@ import { type TsConfig } from '../../types'; import { type GenerateApiExecutorSchema } from './schema'; import executor from './executor'; import { isCI } from './lib/shared'; +import { getExportSubpathConfigs } from './lib/utils'; const fixturesRootDir = join(__dirname, '__fixtures__'); @@ -349,6 +351,31 @@ describe('GenerateApi Executor – export subpath resolution', () => { } }); + it('resolves declaration base correctly when path.resolve returns Windows-style backslashes', async () => { + const subDirs = ['alpha']; + const { context } = prepareExportFixture({ wildcardSubDirs: subDirs }); + + const resolveSpy = jest.spyOn(path, 'resolve').mockImplementation((...args) => { + const posixPath = path.posix.resolve(...args); + return posixPath.replace(/\//g, '\\'); + }); + + const capturedConfigs: ExtractorConfig[] = []; + jest.spyOn(Extractor, 'invoke').mockImplementation(cfg => { + capturedConfigs.push(cfg); + return { succeeded: true } as ExtractorResult; + }); + + try { + const output = await executor({ ...options, exportSubpaths: true }, context); + + expect(capturedConfigs).toHaveLength(1 + subDirs.length); + expect(output.success).toBe(true); + } finally { + resolveSpy.mockRestore(); + } + }); + it('skips wildcard exports with no types field', async () => { const { paths, context } = prepareFixture('valid', {}); const { projRoot } = paths; diff --git a/tools/workspace-plugin/src/executors/generate-api/lib/utils.ts b/tools/workspace-plugin/src/executors/generate-api/lib/utils.ts index ae526525a0645..3e8d270c41227 100644 --- a/tools/workspace-plugin/src/executors/generate-api/lib/utils.ts +++ b/tools/workspace-plugin/src/executors/generate-api/lib/utils.ts @@ -146,10 +146,11 @@ export function getExportSubpathConfigs(options: NormalizedOptions): IConfigFile // they act as literal path segments that the subsequent "../" chain traverses through. // path.resolve(configDir, "/../../../../../../...") naturally normalizes to the correct path. const configDir = dirname(opts.config); + // Normalize to POSIX separators so the suffix check and slice work consistently across platforms (e.g. Windows). const resolvedPrimaryEntry = resolve( configDir, primaryMainEntryTemplate.replace(//g, unscopedPackageName), - ); + ).replace(/\\/g, '/'); const indexDtsSuffix = '/index.d.ts'; if (!resolvedPrimaryEntry.endsWith(indexDtsSuffix)) {