Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: lib setting from custom config is ignored (backport #1576) #1577

Merged
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
20 changes: 11 additions & 9 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export class Compiler implements Emitter {
const tsconf = this.tsconfig!;

const prog = ts.createIncrementalProgram({
rootNames: this.rootFiles.concat(_pathOfLibraries(this.compilerHost)),
rootNames: this.rootFiles.concat(_pathOfLibraries(tsconf.compilerOptions, this.compilerHost)),
options: tsconf.compilerOptions,
// Make the references absolute for the compiler
projectReferences: tsconf.references?.map((ref) => ({
Expand Down Expand Up @@ -600,17 +600,19 @@ export interface NonBlockingWatchOptions {
readonly compilationComplete: (emitResult: ts.EmitResult) => void;
}

function _pathOfLibraries(host: ts.CompilerHost | ts.WatchCompilerHost<any>): string[] {
if (!BASE_COMPILER_OPTIONS.lib || BASE_COMPILER_OPTIONS.lib.length === 0) {
function _pathOfLibraries(options: ts.CompilerOptions, host: ts.CompilerHost | ts.WatchCompilerHost<any>): string[] {
// Prefer user libraries, falling back to a library based on the target if not supplied by the user.
// This matches tsc behavior.
const libs = options.lib ?? [ts.getDefaultLibFileName(options)] ?? [];
if (libs.length === 0) {
return [];
}
const lib = host.getDefaultLibLocation?.();
if (!lib) {
throw new Error(
`Compiler host doesn't have a default library directory available for ${BASE_COMPILER_OPTIONS.lib.join(', ')}`,
);

const libDir = host.getDefaultLibLocation?.();
if (!libDir) {
throw new Error(`Compiler host doesn't have a default library directory available for ${libs.join(', ')}`);
}
return BASE_COMPILER_OPTIONS.lib.map((name) => path.join(lib, name));
return libs.map((name) => path.join(libDir, name));
}

function parseConfigHostFromCompilerHost(host: ts.CompilerHost): ts.ParseConfigHost {
Expand Down
68 changes: 61 additions & 7 deletions test/compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import { mkdirSync, existsSync, mkdtempSync, rmSync, writeFileSync, readFileSync
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { loadAssemblyFromPath, SPEC_FILE_NAME, SPEC_FILE_NAME_COMPRESSED } from '@jsii/spec';
import * as ts from 'typescript';
import { compile, Lock } from './fixtures';
import { Compiler } from '../src/compiler';
import { TYPES_COMPAT } from '../src/downlevel-dts';
import { ProjectInfo } from '../src/project-info';
import { TypeScriptConfigValidationRuleSet } from '../src/tsconfig';
import { TypeScriptConfigValidator } from '../src/tsconfig/tsconfig-validator';

// This is necessary to be able to jest.spyOn to functions in the 'ts' module. Replace the read-only
// object descriptors with a plain object.
jest.mock('typescript', () => ({ ...jest.requireActual('typescript') }));

beforeEach(() => {
jest.restoreAllMocks();
});

describe(Compiler, () => {
describe('generated tsconfig', () => {
test('default is tsconfig.json', () => {
Expand Down Expand Up @@ -227,9 +236,17 @@ describe(Compiler, () => {
});

describe('user-provided tsconfig', () => {
let sourceDir: string;
const tsconfigPath = 'tsconfig.dev.json';
beforeEach(() => {
sourceDir = mkdtempSync(join(tmpdir(), 'jsii-compiler-user-tsconfig-'));
});

afterEach(() => {
rmSync(sourceDir, { force: true, recursive: true });
});

test('will use user-provided config', () => {
const sourceDir = mkdtempSync(join(tmpdir(), 'jsii-compiler-user-tsconfig-'));
const tsconfigPath = 'tsconfig.dev.json';
writeFileSync(join(sourceDir, tsconfigPath), JSON.stringify(tsconfigForNode18Strict(), null, 2));

writeFileSync(join(sourceDir, 'index.ts'), 'export class MarkerA {}');
Expand All @@ -245,9 +262,7 @@ describe(Compiler, () => {
});

test('use user-provided config uses include and exclude', () => {
const sourceDir = mkdtempSync(join(tmpdir(), 'jsii-compiler-user-tsconfig-'));
mkdirSync(join(sourceDir, 'sub'));
const tsconfigPath = 'tsconfig.dev.json';
writeFileSync(
join(sourceDir, tsconfigPath),
JSON.stringify(
Expand Down Expand Up @@ -282,9 +297,48 @@ describe(Compiler, () => {
expect(result.emitSkipped).toBe(false);
});

test('respect "lib" setting from user-provided config', () => {
const tsconfig = tsconfigForNode18Strict();
tsconfig.compilerOptions.lib = ['Decorators.Legacy']; // Something very nonstandard
writeFileSync(join(sourceDir, tsconfigPath), JSON.stringify(tsconfig, null, 2));

const createIncrementalProgram = jest.spyOn(ts, 'createIncrementalProgram');

const compiler = new Compiler({
projectInfo: _makeProjectInfo(sourceDir, 'index.d.ts'),
typeScriptConfig: tsconfigPath,
});
compiler.emit();

expect(createIncrementalProgram).toHaveBeenCalledWith(
expect.objectContaining({
rootNames: expect.arrayContaining([expect.stringContaining('lib.decorators.legacy.d.ts')]),
}),
);
});

test('missing "lib" setting is based on compilation target', () => {
const tsconfig = tsconfigForNode18Strict();
tsconfig.compilerOptions.target = 'es6';
delete tsconfig.compilerOptions.lib;
writeFileSync(join(sourceDir, tsconfigPath), JSON.stringify(tsconfig, null, 2));

const createIncrementalProgram = jest.spyOn(ts, 'createIncrementalProgram');

const compiler = new Compiler({
projectInfo: _makeProjectInfo(sourceDir, 'index.d.ts'),
typeScriptConfig: tsconfigPath,
});
compiler.emit();

expect(createIncrementalProgram).toHaveBeenCalledWith(
expect.objectContaining({
rootNames: expect.arrayContaining([expect.stringContaining('lib.es6.d.ts')]),
}),
);
});

test('"watch" mode', async () => {
const sourceDir = mkdtempSync(join(tmpdir(), 'jsii-compiler-watch-mode-'));
const tsconfigPath = 'tsconfig.dev.json';
writeFileSync(join(sourceDir, tsconfigPath), JSON.stringify(tsconfigForNode18Strict(), null, 2));

try {
Expand Down Expand Up @@ -465,7 +519,7 @@ function expectedTypeScriptConfig() {
function tsconfigForNode18Strict() {
return {
compilerOptions: {
lib: ['es2022'],
lib: ['es2022'] as string[] | undefined,
module: 'node16',
target: 'es2022',

Expand Down
Loading