Allow cross-project references to const enums in isolatedModules when referenced project has preserveConstEnums (#57914)

This commit is contained in:
Andrew Branch 2024-03-27 11:45:57 -07:00 committed by GitHub
parent fd388f7d00
commit b24b5c4ec1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 464 additions and 2 deletions

View File

@ -39782,7 +39782,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (getIsolatedModules(compilerOptions)) {
Debug.assert(!!(type.symbol.flags & SymbolFlags.ConstEnum));
const constEnumDeclaration = type.symbol.valueDeclaration as EnumDeclaration;
if (constEnumDeclaration.flags & NodeFlags.Ambient && !isValidTypeOnlyAliasUseSite(node)) {
const redirect = host.getRedirectReferenceForResolutionFromSourceOfProject(getSourceFileOfNode(constEnumDeclaration).resolvedPath);
if (constEnumDeclaration.flags & NodeFlags.Ambient && !isValidTypeOnlyAliasUseSite(node) && (!redirect || !shouldPreserveConstEnums(redirect.commandLine.options))) {
error(node, Diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, isolatedModulesLikeFlagName);
}
}

View File

@ -1925,6 +1925,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
getResolvedProjectReferenceByPath,
forEachResolvedProjectReference,
isSourceOfProjectReferenceRedirect,
getRedirectReferenceForResolutionFromSourceOfProject,
emitBuildInfo,
fileExists,
readFile,

View File

@ -4734,9 +4734,14 @@ export interface Program extends ScriptReferenceHost {
getProjectReferences(): readonly ProjectReference[] | undefined;
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
/** @internal */ getProjectReferenceRedirect(fileName: string): string | undefined;
/** @internal */ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
/**
* @internal
* Get the referenced project if the file is input file from that reference project
*/
getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
/** @internal */ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
/** @internal */ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
/** @internal */ getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path): ResolvedProjectReference | undefined;
/** @internal */ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
/** @internal */ getBuildInfo?(): BuildInfo;
/** @internal */ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
@ -4853,6 +4858,7 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
getSourceFile(fileName: string): SourceFile | undefined;
getProjectReferenceRedirect(fileName: string): string | undefined;
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path): ResolvedProjectReference | undefined;
getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
getResolvedModule(f: SourceFile, moduleName: string, mode: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined;

View File

@ -49,4 +49,31 @@ describe("unittests:: tsc:: projectReferences::", () => {
}),
commandLineArgs: ["--p", "src/project"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "referencing ambient const enum from referenced project with preserveConstEnums",
fs: () =>
loadProjectFromFiles({
"/src/utils/index.ts": "export const enum E { A = 1 }",
"/src/utils/index.d.ts": "export declare const enum E { A = 1 }",
"/src/utils/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
preserveConstEnums: true,
},
}),
"/src/project/index.ts": `import { E } from "../utils"; E.A;`,
"/src/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
isolatedModules: true,
},
references: [
{ path: "../utils" },
],
}),
}),
commandLineArgs: ["--p", "src/project"],
});
});

View File

@ -347,6 +347,35 @@ function foo() {
baselineTsserverLogs("projectReferences", "reusing d.ts files from composite and non composite projects", session);
});
it("referencing const enum from referenced project with preserveConstEnums", () => {
const projectLocation = `/user/username/projects/project`;
const utilsIndex: File = {
path: `${projectLocation}/src/utils/index.ts`,
content: "export const enum E { A = 1 }",
};
const utilsDeclaration: File = {
path: `${projectLocation}/src/utils/index.d.ts`,
content: "export declare const enum E { A = 1 }",
};
const utilsConfig: File = {
path: `${projectLocation}/src/utils/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { composite: true, declaration: true, preserveConstEnums: true } }),
};
const projectIndex: File = {
path: `${projectLocation}/src/project/index.ts`,
content: `import { E } from "../utils"; E.A;`,
};
const projectConfig: File = {
path: `${projectLocation}/src/project/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { isolatedModules: true }, references: [{ path: "../utils" }] }),
};
const host = createServerHost([libFile, utilsIndex, utilsDeclaration, utilsConfig, projectIndex, projectConfig]);
const session = new TestSession(host);
openFilesForSession([projectIndex], session);
verifyGetErrRequest({ session, files: [projectIndex] });
baselineTsserverLogs("projectReferences", `referencing const enum from referenced project with preserveConstEnums`, session);
});
describe("when references are monorepo like with symlinks", () => {
interface Packages {
bPackageJson: File;

View File

@ -0,0 +1,61 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Input::
//// [/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };
//// [/src/project/index.ts]
import { E } from "../utils"; E.A;
//// [/src/project/tsconfig.json]
{
"compilerOptions": {
"isolatedModules": true
},
"references": [
{
"path": "../utils"
}
]
}
//// [/src/utils/index.d.ts]
export declare const enum E { A = 1 }
//// [/src/utils/index.ts]
export const enum E { A = 1 }
//// [/src/utils/tsconfig.json]
{
"compilerOptions": {
"composite": true,
"declaration": true,
"preserveConstEnums": true
}
}
Output::
/lib/tsc --p src/project
exitCode:: ExitStatus.Success
//// [/src/project/index.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("../utils");
utils_1.E.A;

View File

@ -0,0 +1,337 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
Before request
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/project/src/utils/index.ts]
export const enum E { A = 1 }
//// [/user/username/projects/project/src/utils/index.d.ts]
export declare const enum E { A = 1 }
//// [/user/username/projects/project/src/utils/tsconfig.json]
{
"compilerOptions": {
"composite": true,
"declaration": true,
"preserveConstEnums": true
}
}
//// [/user/username/projects/project/src/project/index.ts]
import { E } from "../utils"; E.A;
//// [/user/username/projects/project/src/project/tsconfig.json]
{
"compilerOptions": {
"isolatedModules": true
},
"references": [
{
"path": "../utils"
}
]
}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/user/username/projects/project/src/project/index.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /user/username/projects/project/src/project
Info seq [hh:mm:ss:mss] For info: /user/username/projects/project/src/project/index.ts :: Config file name: /user/username/projects/project/src/project/tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /user/username/projects/project/src/project/tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/project/src/project/tsconfig.json 2000 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "projectLoadingStart",
"body": {
"projectName": "/user/username/projects/project/src/project/tsconfig.json",
"reason": "Creating possible configured project for /user/username/projects/project/src/project/index.ts to open"
}
}
Info seq [hh:mm:ss:mss] Config: /user/username/projects/project/src/project/tsconfig.json : {
"rootNames": [
"/user/username/projects/project/src/project/index.ts"
],
"options": {
"isolatedModules": true,
"configFilePath": "/user/username/projects/project/src/project/tsconfig.json"
},
"projectReferences": [
{
"path": "/user/username/projects/project/src/utils",
"originalPath": "../utils"
}
]
}
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/project 1 undefined Config: /user/username/projects/project/src/project/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/project 1 undefined Config: /user/username/projects/project/src/project/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/project/src/project/tsconfig.json
Info seq [hh:mm:ss:mss] Config: /user/username/projects/project/src/utils/tsconfig.json : {
"rootNames": [
"/user/username/projects/project/src/utils/index.ts"
],
"options": {
"composite": true,
"declaration": true,
"preserveConstEnums": true,
"configFilePath": "/user/username/projects/project/src/utils/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils/tsconfig.json 2000 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils 1 undefined Config: /user/username/projects/project/src/utils/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils 1 undefined Config: /user/username/projects/project/src/utils/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src 0 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src 0 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/project/src/utils/index.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/project/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/project/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/src/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/project/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/project/src/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/project/src/project/tsconfig.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/user/username/projects/project/src/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (3)
/a/lib/lib.d.ts Text-1 "/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }"
/user/username/projects/project/src/utils/index.ts Text-1 "export const enum E { A = 1 }"
/user/username/projects/project/src/project/index.ts SVC-1-0 "import { E } from \"../utils\"; E.A;"
../../../../../../a/lib/lib.d.ts
Default library for target 'es5'
../utils/index.ts
Imported via "../utils" from file 'index.ts'
index.ts
Matched by default include pattern '**/*'
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "projectLoadingFinish",
"body": {
"projectName": "/user/username/projects/project/src/project/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "telemetry",
"body": {
"telemetryEventName": "projectInfo",
"payload": {
"projectId": "0ecf3cccdd8e71701ab41fc1852f59ac0c6c0d069aea4eb2fba6867e4bd6f54c",
"fileStats": {
"js": 0,
"jsSize": 0,
"jsx": 0,
"jsxSize": 0,
"ts": 2,
"tsSize": 63,
"tsx": 0,
"tsxSize": 0,
"dts": 1,
"dtsSize": 334,
"deferred": 0,
"deferredSize": 0
},
"compilerOptions": {
"isolatedModules": true
},
"typeAcquisition": {
"enable": false,
"include": false,
"exclude": false
},
"extends": false,
"files": false,
"include": false,
"exclude": false,
"compileOnSave": false,
"configFileName": "tsconfig.json",
"projectType": "configured",
"languageServiceEnabled": true,
"version": "FakeVersion"
}
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "configFileDiag",
"body": {
"triggerFile": "/user/username/projects/project/src/project/index.ts",
"configFile": "/user/username/projects/project/src/project/tsconfig.json",
"diagnostics": []
}
}
Info seq [hh:mm:ss:mss] Project '/user/username/projects/project/src/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (3)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /user/username/projects/project/src/project/index.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /user/username/projects/project/src/project/tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/user/username/projects/node_modules/@types: *new*
{"pollingInterval":500}
/user/username/projects/project/node_modules/@types: *new*
{"pollingInterval":500}
/user/username/projects/project/src/node_modules/@types: *new*
{"pollingInterval":500}
/user/username/projects/project/src/project/node_modules/@types: *new*
{"pollingInterval":500}
FsWatches::
/a/lib/lib.d.ts: *new*
{}
/user/username/projects/project/src: *new*
{}
/user/username/projects/project/src/project/tsconfig.json: *new*
{}
/user/username/projects/project/src/utils/index.ts: *new*
{}
/user/username/projects/project/src/utils/tsconfig.json: *new*
{}
FsWatchesRecursive::
/user/username/projects/project/src/project: *new*
{}
/user/username/projects/project/src/utils: *new*
{}
Projects::
/user/username/projects/project/src/project/tsconfig.json (Configured) *new*
projectStateVersion: 1
projectProgramVersion: 1
ScriptInfos::
/a/lib/lib.d.ts *new*
version: Text-1
containingProjects: 1
/user/username/projects/project/src/project/tsconfig.json
/user/username/projects/project/src/project/index.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/user/username/projects/project/src/project/tsconfig.json *default*
/user/username/projects/project/src/utils/index.ts *new*
version: Text-1
containingProjects: 1
/user/username/projects/project/src/project/tsconfig.json
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "geterr",
"arguments": {
"delay": 0,
"files": [
"/user/username/projects/project/src/project/index.ts"
]
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
Timeout callback:: count: 1
1: checkOne *new*
Before running Timeout callback:: count: 1
1: checkOne
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "syntaxDiag",
"body": {
"file": "/user/username/projects/project/src/project/index.ts",
"diagnostics": []
}
}
After running Timeout callback:: count: 0
Immedidate callback:: count: 1
1: semanticCheck *new*
Before running Immedidate callback:: count: 1
1: semanticCheck
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "semanticDiag",
"body": {
"file": "/user/username/projects/project/src/project/index.ts",
"diagnostics": []
}
}
After running Immedidate callback:: count: 1
Immedidate callback:: count: 1
2: suggestionCheck *new*
Before running Immedidate callback:: count: 1
2: suggestionCheck
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "suggestionDiag",
"body": {
"file": "/user/username/projects/project/src/project/index.ts",
"diagnostics": []
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "requestCompleted",
"body": {
"request_seq": 2
}
}
After running Immedidate callback:: count: 0