Skip to content
Merged
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
13 changes: 10 additions & 3 deletions scripts/tasks/src/api-extractor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';
Comment thread
Hotell marked this conversation as resolved.

import type { ExtractorMessageCategory, ExtractorResult } from '@microsoft/api-extractor';
import { workspaceRoot } from '@nrwl/devkit';
import chalk from 'chalk';
import * as glob from 'glob';
import { ApiExtractorOptions, TaskFunction, apiExtractorVerifyTask, logger, series, task } from 'just-scripts';
Expand Down Expand Up @@ -56,12 +57,18 @@ export function apiExtractor(): TaskFunction {
let configDebug: Parameters<NonNullable<ApiExtractorOptions['onConfigLoaded']>>[0] | null = null;

const args: ReturnType<typeof getJustArgv> & Partial<ApiExtractorCliRunCommandArgs> = getJustArgv();
const { isUsingTsSolutionConfigs, packageJson, tsConfig, tsConfigPath } = getTsPathAliasesConfig();
const { isUsingTsSolutionConfigs, packageJson, tsConfig } = getTsPathAliasesConfig();

if (configsToExecute.length === 0) {
return noop;
}

/**
* overrides api-extractor default `true` to be `false` on local dev machine
* Triggers if path aliases will be used or yarn workspaces (that needs to be build based on package dependency tree)
*/
const isLocalBuild = args.local ?? !process.env.TF_BUILD;

const tasks = configsToExecute.map(([configPath, configName]) => {
const taskName = `api-extractor:${configName}`;

Expand All @@ -73,7 +80,7 @@ export function apiExtractor(): TaskFunction {
showDiagnostics: args.diagnostics,
typescriptCompilerFolder: args['typescript-compiler-folder'],
configJsonFilePath: args.config ?? configPath,
localBuild: args.local ?? !process.env.TF_BUILD,
localBuild: isLocalBuild,
onConfigLoaded,
messageCallback,
onResult,
Expand Down Expand Up @@ -103,8 +110,8 @@ export function apiExtractor(): TaskFunction {

const compilerConfig = getTsPathAliasesApiExtractorConfig({
tsConfig,
tsConfigPath,
packageJson,
pathAliasesTsConfigPath: isLocalBuild ? path.join(workspaceRoot, 'tsconfig.base.json') : undefined,
definitionsRootPath: 'dist/out-tsc/types',
});

Expand Down
19 changes: 17 additions & 2 deletions scripts/tasks/src/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as path from 'path';

import { workspaceRoot } from '@nrwl/devkit';

import { getTsPathAliasesApiExtractorConfig } from './utils';

type DeepPartial<T> = Partial<{ [P in keyof T]: DeepPartial<T[P]> }>;
Expand All @@ -12,7 +16,6 @@ describe(`utils`, () => {
...options.tsConfig?.compilerOptions,
},
},
tsConfigPath: 'tsconfig.lib.json',
packageJson: {
name: '@proj/one',
version: '0.0.1',
Expand All @@ -21,6 +24,7 @@ describe(`utils`, () => {
peerDependencies: { ...options.packageJson?.peerDependencies },
},
definitionsRootPath: options.definitionsRootPath ?? 'dist/types',
pathAliasesTsConfigPath: options.pathAliasesTsConfigPath ?? undefined,
};

return getTsPathAliasesApiExtractorConfig(defaults as Options);
Expand All @@ -34,8 +38,19 @@ describe(`utils`, () => {
);
});

it(`should not use path aliases to emitted declaration files`, () => {
const actual = setup({
definitionsRootPath: 'dist/for/types',
});

expect(actual.overrideTsconfig.compilerOptions).toEqual(expect.objectContaining({ paths: undefined }));
});

it(`should override path aliases to emitted declaration files instead of source files`, () => {
const actual = setup({ definitionsRootPath: 'dist/for/types' });
const actual = setup({
definitionsRootPath: 'dist/for/types',
pathAliasesTsConfigPath: path.join(workspaceRoot, 'tsconfig.base.json'),
});

const newPaths = (actual.overrideTsconfig.compilerOptions.paths as unknown) as Record<string, string[]>;

Expand Down
36 changes: 24 additions & 12 deletions scripts/tasks/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';

import { stripJsonComments, workspaceRoot } from '@nrwl/devkit';
import { stripJsonComments } from '@nrwl/devkit';
import * as jju from 'jju';
import type { TscTaskOptions } from 'just-scripts';

Expand Down Expand Up @@ -50,41 +50,53 @@ function enableAllowSyntheticDefaultImports(options: { pkgJson: PackageJson }) {
return shouldEnable ? { allowSyntheticDefaultImports: true } : null;
}

const rootTsConfig = JSON.parse(fs.readFileSync(path.join(workspaceRoot, 'tsconfig.base.json'), 'utf-8')) as TsConfig;
function createNormalizedTsPaths(options: { definitionsRootPath: string; pathAliasesTsConfigPath: string }) {
type PathAliases = Record<string, string[]>;
const { definitionsRootPath, pathAliasesTsConfigPath } = options;
const tsConfigRoot = JSON.parse(fs.readFileSync(pathAliasesTsConfigPath, 'utf-8')) as TsConfig;
const paths = (tsConfigRoot.compilerOptions.paths as unknown) as undefined | PathAliases;

function createNormalizedTsPaths(options: { definitionsRootPath: string; rootTsConfig: TsConfig }) {
const paths = (options.rootTsConfig.compilerOptions.paths as unknown) as Record<string, string[]>;
if (!paths) {
throw new Error(`Provided "${pathAliasesTsConfigPath}" has no compilerOptions.path defined`);
}

const normalizedPaths = Object.entries(paths).reduce((acc, [pkgName, pathAliases]) => {
acc[pkgName] = [path.join(options.definitionsRootPath, pathAliases[0].replace('index.ts', 'index.d.ts'))];
acc[pkgName] = [path.join(definitionsRootPath, pathAliases[0].replace('index.ts', 'index.d.ts'))];
return acc;
}, {} as typeof paths);
}, {} as PathAliases);

return normalizedPaths;
}

export function getTsPathAliasesApiExtractorConfig(options: {
tsConfig: TsConfig;
tsConfigPath: string;
packageJson: PackageJson;
definitionsRootPath: string;
pathAliasesTsConfigPath?: string;
}) {
const normalizedPaths = createNormalizedTsPaths({ definitionsRootPath: options.definitionsRootPath, rootTsConfig });
const { packageJson, tsConfig, pathAliasesTsConfigPath, definitionsRootPath } = options;
/**
* Because api-extractor ran into race conditions when executing via lage (https://github.com/microsoft/fluentui/issues/25766),
* we won't use path aliases on CI, rather serving api-extractor rolluped dts files cross package, that will be referenced via yarn workspace sym-links
*/
const normalizedPaths = pathAliasesTsConfigPath
? createNormalizedTsPaths({ definitionsRootPath, pathAliasesTsConfigPath })
: undefined;

/**
* Customized TSConfig that uses `tsconfig.lib.json` as base with some required overrides:
*
* NOTES:
* - `extends` is properly resolved via api-extractor which uses TS api
* - `skipLibCheck` needs to be explicitly set to `false` so errors propagate to api-extractor
* - `paths` is overriden to path mapping that points to generated declaration files. This also enables creation of dts rollup without a need of generating rollups for all dependencies 🫡
* - `paths` if usePathAliases is enabled, we override it to path mapping that points to generated declaration files. This also enables creation of dts rollup without a need of generating rollups for all dependencies 🫡
*
*/
const apiExtractorTsConfig: TsConfig = {
...options.tsConfig,
...tsConfig,
compilerOptions: {
...options.tsConfig.compilerOptions,
...enableAllowSyntheticDefaultImports({ pkgJson: options.packageJson }),
...tsConfig.compilerOptions,
...enableAllowSyntheticDefaultImports({ pkgJson: packageJson }),
/**
* This option has no effect on type declarations '.d.ts' thus can be turned off. For more info see https://www.typescriptlang.org/tsconfig#non-module-files
*
Expand Down