Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,6 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
bundler: ModuleResolutionKind.Bundler,
})),
deprecatedKeys: new Set(["node"]),
affectsSourceFile: true,
affectsModuleResolution: true,
paramType: Diagnostics.STRATEGY,
category: Diagnostics.Modules,
Expand Down
52 changes: 18 additions & 34 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1419,12 +1419,9 @@ export function getImpliedNodeFormatForFileWorker(
host: ModuleResolutionHost,
options: CompilerOptions,
): ResolutionMode | Partial<CreateSourceFileOptions> | undefined {
const moduleResolution = getEmitModuleResolutionKind(options);
const shouldLookupFromPackageJson = ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext
|| pathContainsNodeModules(fileName);
return fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Mjs]) ? ModuleKind.ESNext :
fileExtensionIsOneOf(fileName, [Extension.Dcts, Extension.Cts, Extension.Cjs]) ? ModuleKind.CommonJS :
shouldLookupFromPackageJson && fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx]) ? lookupFromPackageJson() :
fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx]) ? lookupFromPackageJson() :
undefined; // other extensions, like `json` or `tsbuildinfo`, are set as `undefined` here but they should never be fed through the transformer pipeline

function lookupFromPackageJson(): Partial<CreateSourceFileOptions> {
Expand Down Expand Up @@ -3784,22 +3781,20 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

let redirectedPath: Path | undefined;
if (!useSourceOfProjectReferenceRedirect) {
const redirectProject = getProjectReferenceRedirectProject(fileName);
if (redirectProject) {
if (redirectProject.commandLine.options.outFile) {
// Shouldnt create many to 1 mapping file in --out scenario
return undefined;
}
const redirect = getProjectReferenceOutputName(redirectProject, fileName);
fileName = redirect;
// Once we start redirecting to a file, we can potentially come back to it
// via a back-reference from another file in the .d.ts folder. If that happens we'll
// end up trying to add it to the program *again* because we were tracking it via its
// original (un-redirected) name. So we have to map both the original path and the redirected path
// to the source file we're about to find/create
redirectedPath = toPath(redirect);
const redirectProject = getProjectReferenceRedirectProject(fileName);
if (!useSourceOfProjectReferenceRedirect && redirectProject) {
if (redirectProject.commandLine.options.outFile) {
// Shouldnt create many to 1 mapping file in --out scenario
return undefined;
}
const redirect = getProjectReferenceOutputName(redirectProject, fileName);
fileName = redirect;
// Once we start redirecting to a file, we can potentially come back to it
// via a back-reference from another file in the .d.ts folder. If that happens we'll
// end up trying to add it to the program *again* because we were tracking it via its
// original (un-redirected) name. So we have to map both the original path and the redirected path
// to the source file we're about to find/create
redirectedPath = toPath(redirect);
}

// We haven't looked for this file, do so now and cache result
Expand Down Expand Up @@ -5211,24 +5206,13 @@ export function getEmitModuleFormatOfFileWorker(sourceFile: Pick<SourceFile, "fi
/** @internal Prefer `program.getImpliedNodeFormatForEmit` when possible. */
export function getImpliedNodeFormatForEmitWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): ResolutionMode {
const moduleKind = getEmitModuleKind(options);
if (ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
return sourceFile.impliedNodeFormat;
}
if (
sourceFile.impliedNodeFormat === ModuleKind.CommonJS
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "commonjs"
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cjs, Extension.Cts]))
ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cts, Extension.Dcts, Extension.Cjs, Extension.Mts, Extension.Dmts, Extension.Mjs])
|| pathContainsNodeModules(sourceFile.fileName)

This comment was marked as duplicate.

) {
return ModuleKind.CommonJS;
}
if (
sourceFile.impliedNodeFormat === ModuleKind.ESNext
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "module"
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Mjs, Extension.Mts]))
) {
return ModuleKind.ESNext;
return sourceFile.impliedNodeFormat;
}
return undefined;
}
/** @internal Prefer `program.getDefaultResolutionModeForFile` when possible. */
export function getDefaultResolutionModeForFileWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): ResolutionMode {
Expand Down
19 changes: 6 additions & 13 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4363,21 +4363,14 @@ export interface SourceFile extends Declaration, LocalsContainer {
languageVersion: ScriptTarget;

/**
* When `module` is `Node16` or `NodeNext`, this field controls whether the
* source file in question is an ESNext-output-format file, or a CommonJS-output-format
* module. This is derived by the module resolver as it looks up the file, since
* it is derived from either the file extension of the module, or the containing
* `package.json` context, and affects both checking and emit.
* This field controls whether the source file in question is an ESNext-output-format file,
* or a CommonJS-output-format module. This is derived by the module resolver as it looks
* up the file, since it is derived from either the file extension of the module, or the
* containing `package.json` context, and may affect both checking and emit, depending on
* `module` and `moduleResolution` compiler options.
*
* It is _public_ so that (pre)transformers can set this field,
* since it switches the builtin `node` module transform. Generally speaking, if unset,
* the field is treated as though it is `ModuleKind.CommonJS`.
*
* Note that this field is only set by the module resolution process when
* `moduleResolution` is `Node16` or `NodeNext`, which is implied by the `module` setting
* of `Node16` or `NodeNext`, respectively, but may be overriden (eg, by a `moduleResolution`
* of `node`). If so, this field will be unset and source files will be considered to be
* CommonJS-output-format by the node module transformer and type checker, regardless of extension or context.
* since it switches the builtin `node` module transform.
*/
impliedNodeFormat?: ResolutionMode;
/** @internal */ packageJsonLocations?: readonly string[];
Expand Down
6 changes: 3 additions & 3 deletions src/testRunner/unittests/tscWatch/incremental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,19 @@ describe("unittests:: tscWatch:: incremental:: emit file --incremental", () => {
version: system.createHash(libFile.content),
signature: system.createHash(libFile.content),
affectsGlobalScope: true,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});
assert.deepEqual(builderProgram.state.fileInfos.get(file1.path as ts.Path), {
version: system.createHash(file1.content),
signature: system.createHash(file1.content),
affectsGlobalScope: undefined,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});
assert.deepEqual(builderProgram.state.fileInfos.get(file2.path as ts.Path), {
version: system.createHash(fileModified.content),
signature: system.createHash(fileModified.content),
affectsGlobalScope: undefined,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});

assert.deepEqual(builderProgram.state.compilerOptions, {
Expand Down
125 changes: 125 additions & 0 deletions src/testRunner/unittests/tsserver/symLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,129 @@ new C();`,
});
});
});

it("when monorepo module gets loaded from other symlinked monorepo module and from a regular node module", () => {
const projectRootPath = "/users/username/cal.com";

const libPkg = `${projectRootPath}/packages/lib`;
const getAllUserBookingsFile: File = {
path: `${libPkg}/bookings/getAllUserBookings.ts`,
content: `export const getAllUserBookings = async () => {};`,
};
const libPkgJson: File = {
path: `${libPkg}/package.json`,
content: jsonToReadableText({
name: "@calcom/lib",
version: "0.0.0", // version field for this package is important for the test
}),
};
const libTsconfig: File = {
path: `${libPkg}/tsconfig.json`,
content: jsonToReadableText({
compilerOptions: {
moduleResolution: "node",
},
}),
};
const typesPkg = `${projectRootPath}/packages/types`;
const paymentServiceFile: File = {
path: `${typesPkg}/PaymentService.d.ts`,
content: ``,
};
const videoApiAdapterFile: File = {
path: `${typesPkg}/VideoApiAdapter.d.ts`,
content: `import {} from "@calcom/lib/bookings/getAllUserBookings";`,
};
const typesPkgJson: File = {
path: `${typesPkg}/package.json`,
content: jsonToReadableText({ name: "@calcom/types" }),
};
const typesTsconfig: File = {
path: `${typesPkg}/tsconfig.json`,
content: jsonToReadableText({
compilerOptions: {
moduleResolution: "node",
},
}),
};
const uiPkg = `${projectRootPath}/packages/ui`;
const uiIndexFile: File = {
path: `${uiPkg}/index.tsx`,
content: `import type {} from "@calcom/platform-libraries";`,
};
const uiPkgJson: File = {
path: `${uiPkg}/package.json`,
content: jsonToReadableText({ name: "@calcom/ui" }),
};
const uiTsconfig: File = {
path: `${uiPkg}/tsconfig.json`,
content: jsonToReadableText({
compilerOptions: {
moduleResolution: "node",
},
include: [
"../types/*.d.ts", // this includes files from another project
"**/*.tsx",
],
}),
};

const libSymLink: SymLink = {
path: `${projectRootPath}/node_modules/@calcom/lib`,
symLink: "../../packages/lib",
};

const typesSymLink: SymLink = {
path: `${projectRootPath}/node_modules/@calcom/types`,
symLink: "../../packages/types",
};

const uiSymLink: SymLink = {
path: `${projectRootPath}/node_modules/@calcom/ui`,
symLink: "../../packages/ui",
};

const platformLibrariesNodeModule = `${projectRootPath}/node_modules/@calcom/platform-libraries`;

// this non-symlinked node module reaches into a sibling symlinked node module
const platformLibrariesIndexDts: File = {
path: `${platformLibrariesNodeModule}/dist/index.d.ts`,
content: `export { getAllUserBookings } from '../../lib/bookings/getAllUserBookings';`,
};

const platformLibrariesPkgJson: File = {
path: `${platformLibrariesNodeModule}/package.json`,
content: jsonToReadableText({ name: "@calcom/platform-libraries", types: "./dist/index.d.ts" }),
};

const files = [
// monorepo files
getAllUserBookingsFile,
libPkgJson,
libTsconfig,
paymentServiceFile,
videoApiAdapterFile,
typesPkgJson,
typesTsconfig,
uiIndexFile,
uiPkgJson,
uiTsconfig,

// regular monorepo node_modules symlinks
libSymLink,
typesSymLink,
uiSymLink,

// regular node_modules
platformLibrariesIndexDts,
platformLibrariesPkgJson,
];
const host = TestServerHost.createServerHost(files);
const session = new TestSession(host);
openFilesForSession([uiIndexFile], session);
openFilesForSession([videoApiAdapterFile], session);
closeFilesForSession([uiIndexFile], session);
openFilesForSession([paymentServiceFile], session);
baselineTsserverLogs("symLinks", "when monorepo module gets loaded from other symlinked monorepo module and from a regular node module", session);
});
});
Loading
Loading