Refactor tsserver, tsc and fourslash-server tests so that paths are always watchable (#59844)

This commit is contained in:
Sheetal Nandi
2024-09-04 14:22:10 -07:00
committed by GitHub
parent 29d92edd1c
commit 7976d9cef5
2663 changed files with 293468 additions and 263848 deletions
+17 -11
View File
@@ -303,28 +303,34 @@ export function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics:
return sortAndDeduplicate<T>(diagnostics, compareDiagnostics, diagnosticsEqualityComparer);
}
/** @internal */
export const targetToLibMap = new Map<ScriptTarget, string>([
[ScriptTarget.ESNext, "lib.esnext.full.d.ts"],
[ScriptTarget.ES2023, "lib.es2023.full.d.ts"],
[ScriptTarget.ES2022, "lib.es2022.full.d.ts"],
[ScriptTarget.ES2021, "lib.es2021.full.d.ts"],
[ScriptTarget.ES2020, "lib.es2020.full.d.ts"],
[ScriptTarget.ES2019, "lib.es2019.full.d.ts"],
[ScriptTarget.ES2018, "lib.es2018.full.d.ts"],
[ScriptTarget.ES2017, "lib.es2017.full.d.ts"],
[ScriptTarget.ES2016, "lib.es2016.full.d.ts"],
[ScriptTarget.ES2015, "lib.es6.d.ts"], // We don't use lib.es2015.full.d.ts due to breaking change.
]);
export function getDefaultLibFileName(options: CompilerOptions): string {
switch (getEmitScriptTarget(options)) {
const target = getEmitScriptTarget(options);
switch (target) {
case ScriptTarget.ESNext:
return "lib.esnext.full.d.ts";
case ScriptTarget.ES2023:
return "lib.es2023.full.d.ts";
case ScriptTarget.ES2022:
return "lib.es2022.full.d.ts";
case ScriptTarget.ES2021:
return "lib.es2021.full.d.ts";
case ScriptTarget.ES2020:
return "lib.es2020.full.d.ts";
case ScriptTarget.ES2019:
return "lib.es2019.full.d.ts";
case ScriptTarget.ES2018:
return "lib.es2018.full.d.ts";
case ScriptTarget.ES2017:
return "lib.es2017.full.d.ts";
case ScriptTarget.ES2016:
return "lib.es2016.full.d.ts";
case ScriptTarget.ES2015:
return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change.
return targetToLibMap.get(target)!;
default:
return "lib.d.ts";
}
-215
View File
@@ -422,218 +422,3 @@ export class CompilerHost implements ts.CompilerHost {
return parsed;
}
}
export type ExpectedDiagnosticMessage = [ts.DiagnosticMessage, ...(string | number)[]];
export interface ExpectedDiagnosticMessageChain {
message: ExpectedDiagnosticMessage;
next?: ExpectedDiagnosticMessageChain[];
}
export interface ExpectedDiagnosticLocation {
file: string;
start: number;
length: number;
}
export interface ExpectedDiagnosticRelatedInformation extends ExpectedDiagnosticMessageChain {
location?: ExpectedDiagnosticLocation;
}
export enum DiagnosticKind {
Error = "Error",
Status = "Status",
}
export interface ExpectedErrorDiagnostic extends ExpectedDiagnosticRelatedInformation {
relatedInformation?: ExpectedDiagnosticRelatedInformation[];
}
export type ExpectedDiagnostic = ExpectedDiagnosticMessage | ExpectedErrorDiagnostic;
interface SolutionBuilderDiagnostic {
kind: DiagnosticKind;
diagnostic: ts.Diagnostic;
}
function indentedText(indent: number, text: string) {
if (!indent) return text;
let indentText = "";
for (let i = 0; i < indent; i++) {
indentText += " ";
}
return `
${indentText}${text}`;
}
function expectedDiagnosticMessageToText([message, ...args]: ExpectedDiagnosticMessage) {
let text = ts.getLocaleSpecificMessage(message);
if (args.length) {
text = ts.formatStringFromArgs(text, args);
}
return text;
}
function expectedDiagnosticMessageChainToText({ message, next }: ExpectedDiagnosticMessageChain, indent = 0) {
let text = indentedText(indent, expectedDiagnosticMessageToText(message));
if (next) {
indent++;
next.forEach(kid => text += expectedDiagnosticMessageChainToText(kid, indent));
}
return text;
}
function expectedDiagnosticRelatedInformationToText({ location, ...diagnosticMessage }: ExpectedDiagnosticRelatedInformation) {
const text = expectedDiagnosticMessageChainToText(diagnosticMessage);
if (location) {
const { file, start, length } = location;
return `${file}(${start}:${length}):: ${text}`;
}
return text;
}
function expectedErrorDiagnosticToText({ relatedInformation, ...diagnosticRelatedInformation }: ExpectedErrorDiagnostic) {
let text = `${DiagnosticKind.Error}!: ${expectedDiagnosticRelatedInformationToText(diagnosticRelatedInformation)}`;
if (relatedInformation) {
for (const kid of relatedInformation) {
text += `
related:: ${expectedDiagnosticRelatedInformationToText(kid)}`;
}
}
return text;
}
function expectedDiagnosticToText(errorOrStatus: ExpectedDiagnostic) {
return ts.isArray(errorOrStatus) ?
`${DiagnosticKind.Status}!: ${expectedDiagnosticMessageToText(errorOrStatus)}` :
expectedErrorDiagnosticToText(errorOrStatus);
}
function diagnosticMessageChainToText({ messageText, next }: ts.DiagnosticMessageChain, indent = 0) {
let text = indentedText(indent, messageText);
if (next) {
indent++;
next.forEach(kid => text += diagnosticMessageChainToText(kid, indent));
}
return text;
}
function diagnosticRelatedInformationToText({ file, start, length, messageText }: ts.DiagnosticRelatedInformation) {
const text = typeof messageText === "string" ?
messageText :
diagnosticMessageChainToText(messageText);
return file ?
`${file.fileName}(${start}:${length}):: ${text}` :
text;
}
function diagnosticToText({ kind, diagnostic: { relatedInformation, ...diagnosticRelatedInformation } }: SolutionBuilderDiagnostic) {
let text = `${kind}!: ${diagnosticRelatedInformationToText(diagnosticRelatedInformation)}`;
if (relatedInformation) {
for (const kid of relatedInformation) {
text += `
related:: ${diagnosticRelatedInformationToText(kid)}`;
}
}
return text;
}
export const version = "FakeTSVersion";
export function patchHostForBuildInfoReadWrite<T extends ts.System>(sys: T) {
const originalReadFile = sys.readFile;
sys.readFile = (path, encoding) => {
const value = originalReadFile.call(sys, path, encoding);
if (!value || !ts.isBuildInfoFile(path)) return value;
const buildInfo = ts.getBuildInfo(path, value);
if (!buildInfo) return value;
ts.Debug.assert(buildInfo.version === version);
buildInfo.version = ts.version;
return ts.getBuildInfoText(buildInfo);
};
return patchHostForBuildInfoWrite(sys, version);
}
export function patchHostForBuildInfoWrite<T extends ts.System>(sys: T, version: string) {
const originalWrite = sys.write;
sys.write = msg => originalWrite.call(sys, msg.replace(ts.version, version));
const originalWriteFile = sys.writeFile;
sys.writeFile = (fileName: string, content: string, writeByteOrderMark: boolean) => {
if (ts.isBuildInfoFile(fileName)) {
const buildInfo = ts.getBuildInfo(fileName, content);
if (buildInfo) {
buildInfo.version = version;
return originalWriteFile.call(sys, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
}
}
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
};
return sys;
}
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost<ts.BuilderProgram> {
createProgram: ts.CreateProgram<ts.BuilderProgram>;
private constructor(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>, jsDocParsingMode?: ts.JSDocParsingMode) {
super(sys, options, setParentNodes, jsDocParsingMode);
this.createProgram = createProgram || ts.createEmitAndSemanticDiagnosticsBuilderProgram as unknown as ts.CreateProgram<ts.BuilderProgram>;
}
static create(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>, jsDocParsingMode?: ts.JSDocParsingMode) {
const host = new SolutionBuilderHost(sys, options, setParentNodes, createProgram, jsDocParsingMode);
patchHostForBuildInfoReadWrite(host.sys);
return host;
}
createHash(data: string) {
return `${ts.generateDjb2Hash(data)}-${data}`;
}
diagnostics: SolutionBuilderDiagnostic[] = [];
reportDiagnostic(diagnostic: ts.Diagnostic) {
this.diagnostics.push({ kind: DiagnosticKind.Error, diagnostic });
}
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
this.diagnostics.push({ kind: DiagnosticKind.Status, diagnostic });
}
clearDiagnostics() {
this.diagnostics.length = 0;
}
assertDiagnosticMessages(...expectedDiagnostics: ExpectedDiagnostic[]) {
const actual = this.diagnostics.slice().map(diagnosticToText);
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
assert.deepEqual(
actual,
expected,
`Diagnostic arrays did not match:
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`,
);
}
assertErrors(...expectedDiagnostics: ExpectedErrorDiagnostic[]) {
const actual = this.diagnostics.filter(d => d.kind === DiagnosticKind.Error).map(diagnosticToText);
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
assert.deepEqual(
actual,
expected,
`Diagnostics arrays did not match:
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}
Actual All:: ${JSON.stringify(this.diagnostics.slice().map(diagnosticToText), /*replacer*/ undefined, " ")}`,
);
}
printDiagnostics(header = "== Diagnostics ==") {
const out = ts.createDiagnosticReporter(ts.sys);
ts.sys.write(header + "\r\n");
for (const { diagnostic } of this.diagnostics) {
out(diagnostic);
}
}
now() {
return this.sys.now();
}
}
+32 -4
View File
@@ -9,6 +9,11 @@ import * as vpath from "./_namespaces/vpath.js";
import { LoggerWithInMemoryLogs } from "./tsserverLogger.js";
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
import {
harnessSessionLibLocation,
harnessTypingInstallerCacheLocation,
} from "./harnessLanguageService.js";
import { ensureWatchablePath } from "./watchUtils.js";
export const enum FourSlashTestType {
Native,
@@ -318,7 +323,17 @@ export class TestState {
let startResolveFileRef: FourSlashFile | undefined;
let configFileName: string | undefined;
if (testData.symlinks && this.testType === FourSlashTestType.Server) {
for (const symlink of ts.getOwnKeys(testData.symlinks)) {
ensureWatchablePath(ts.getDirectoryPath(symlink), `Directory of input link: ${symlink}`);
const target = (testData.symlinks[symlink] as vfs.Symlink).symlink;
ensureWatchablePath(ts.getDirectoryPath(target), `Directory of target link: ${target} for symlink ${symlink}`);
}
}
for (const file of testData.files) {
if (this.testType === FourSlashTestType.Server) {
ensureWatchablePath(ts.getDirectoryPath(file.fileName), `Directory of input file: ${file.fileName}`);
}
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles.set(file.fileName, file.content);
if (isConfig(file)) {
@@ -348,6 +363,11 @@ export class TestState {
}
}
const libName = (name: string) =>
this.testType !== FourSlashTestType.Server ?
name :
`${harnessSessionLibLocation}/${name}`;
let configParseResult: ts.ParsedCommandLine | undefined;
if (configFileName) {
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
@@ -401,13 +421,21 @@ export class TestState {
// Check if no-default-lib flag is false and if so add default library
if (!resolvedResult.isLibFile) {
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.getDefaultLibrarySourceFile()!.text, /*isRootFile*/ false);
this.languageServiceAdapterHost.addScript(
libName(Harness.Compiler.defaultLibFileName),
Harness.Compiler.getDefaultLibrarySourceFile()!.text,
/*isRootFile*/ false,
);
compilationOptions.lib?.forEach(fileName => {
const libFile = Harness.Compiler.getDefaultLibrarySourceFile(fileName);
ts.Debug.assertIsDefined(libFile, `Could not find lib file '${fileName}'`);
if (libFile) {
this.languageServiceAdapterHost.addScript(fileName, libFile.text, /*isRootFile*/ false);
this.languageServiceAdapterHost.addScript(
libName(fileName),
libFile.text,
/*isRootFile*/ false,
);
}
});
}
@@ -419,7 +447,7 @@ export class TestState {
// all files if config file not specified, otherwise root files from the config and typings cache files are root files
const isRootFile = !configParseResult ||
ts.contains(configParseResult.fileNames, fileName) ||
(ts.isDeclarationFileName(fileName) && ts.containsPath("/Library/Caches/typescript", fileName));
(ts.isDeclarationFileName(fileName) && ts.containsPath(harnessTypingInstallerCacheLocation, fileName));
this.languageServiceAdapterHost.addScript(fileName, file, isRootFile);
}
});
@@ -431,7 +459,7 @@ export class TestState {
seen.add(fileName);
const libFile = Harness.Compiler.getDefaultLibrarySourceFile(fileName);
ts.Debug.assertIsDefined(libFile, `Could not find lib file '${fileName}'`);
this.languageServiceAdapterHost.addScript(fileName, libFile.text, /*isRootFile*/ false);
this.languageServiceAdapterHost.addScript(libName(fileName), libFile.text, /*isRootFile*/ false);
if (!ts.some(libFile.libReferenceDirectives)) return;
for (const directive of libFile.libReferenceDirectives) {
addSourceFile(`lib.${directive.fileName}.d.ts`);
+20 -7
View File
@@ -249,6 +249,8 @@ export abstract class LanguageServiceAdapterHost {
}
}
/** TypeScript Typings Installer global cache location for the tests */
export const harnessTypingInstallerCacheLocation = "/home/src/Library/Caches/typescript";
/// Native adapter
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost, LanguageServiceAdapterHost {
isKnownTypesPackageName(name: string): boolean {
@@ -256,7 +258,7 @@ class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts
}
getGlobalTypingsCacheLocation() {
return "/Library/Caches/typescript";
return harnessTypingInstallerCacheLocation;
}
installPackage = ts.notImplemented;
@@ -348,6 +350,11 @@ export class NativeLanguageServiceAdapter implements LanguageServiceAdapter {
}
}
/**
* This is set to vscode so that in tsserver tests its what is expected
* and is unrelated and is error to not specify for tsc tests
*/
export const harnessSessionCurrentDirectory = "/home/src/Vscode/Projects/bin";
// Server adapter
class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
private client!: ts.server.SessionClient;
@@ -356,6 +363,10 @@ class SessionClientHost extends NativeLanguageServiceHost implements ts.server.S
super(cancellationToken, settings);
}
override getCurrentDirectory(): string {
return harnessSessionCurrentDirectory;
}
onMessage = ts.noop;
writeMessage = ts.noop;
@@ -382,6 +393,9 @@ interface ServerHostFileWatcher {
interface ServerHostDirectoryWatcher {
cb: ts.DirectoryWatcherCallback;
}
/** Default typescript and lib installs location for tests */
export const harnessSessionLibLocation = "/home/src/tslibs/TS/Lib";
class SessionServerHost implements ts.server.ServerHost {
args: string[] = [];
newLine: string;
@@ -404,10 +418,6 @@ class SessionServerHost implements ts.server.ServerHost {
}
readFile(fileName: string): string | undefined {
if (fileName.includes(Compiler.defaultLibFileName)) {
fileName = Compiler.defaultLibFileName;
}
// System FS would follow symlinks, even though snapshots are stored by original file name
const snapshot = this.host.getScriptSnapshot(fileName) || this.host.getScriptSnapshot(this.realpath(fileName));
return snapshot && ts.getSnapshotText(snapshot);
@@ -433,7 +443,7 @@ class SessionServerHost implements ts.server.ServerHost {
}
getExecutingFilePath(): string {
return "";
return harnessSessionLibLocation + "/tsc.js";
}
exit = ts.noop;
@@ -631,7 +641,10 @@ export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: { ...ts.server.nullTypingsInstaller, globalTypingsCacheLocation: "/Library/Caches/typescript" },
typingsInstaller: {
...ts.server.nullTypingsInstaller,
globalTypingsCacheLocation: harnessTypingInstallerCacheLocation,
},
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
logger: this.logger,
-1
View File
@@ -57,7 +57,6 @@ function handleLoggerGroup(logger: Logger, host: ts.server.ServerHost, sanitizeL
logger.hasLevel = ts.returnTrue;
logger.loggingEnabled = ts.returnTrue;
logger.host = host;
if (host) logger.logs!.push(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
let inGroup = false;
let firstInGroup = false;
+10
View File
@@ -1,5 +1,6 @@
import {
arrayFrom,
canWatchDirectoryOrFile,
compareStringsCaseSensitive,
contains,
createMultiMap,
@@ -7,11 +8,20 @@ import {
FileWatcher,
FileWatcherCallback,
GetCanonicalFileName,
getPathComponents,
MultiMap,
Path,
PollingInterval,
System,
} from "./_namespaces/ts.js";
export function ensureWatchablePath(path: string, locationType: string) {
Debug.assert(
canWatchDirectoryOrFile(getPathComponents(path as Path)),
`Not a watchable location: ${locationType} like "/home/src/workspaces/project" or refer canWatchDirectoryOrFile for more allowed locations`,
);
}
export interface TestFileWatcher {
cb: FileWatcherCallback;
pollingInterval: PollingInterval;
+4 -1
View File
@@ -1336,6 +1336,10 @@ export class ProjectService {
: undefined;
this.throttledOperations = new ThrottledOperations(this.host, this.logger);
this.logger.info(`currentDirectory:: ${this.host.getCurrentDirectory()} useCaseSensitiveFileNames:: ${this.host.useCaseSensitiveFileNames}`);
this.logger.info(`libs Location:: ${getDirectoryPath(this.host.getExecutingFilePath())}`);
this.logger.info(`globalTypingsCacheLocation:: ${this.typingsInstaller.globalTypingsCacheLocation}`);
if (this.typesMapLocation) {
this.loadTypesMap();
}
@@ -2684,7 +2688,6 @@ export class ProjectService {
/** @internal */
createConfiguredProject(configFileName: NormalizedPath, reason: string) {
tracing?.instant(tracing.Phase.Session, "createConfiguredProject", { configFilePath: configFileName });
this.logger.info(`Creating configuration project ${configFileName}`);
const canonicalConfigFilePath = asNormalizedPath(this.toCanonicalFileName(configFileName));
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
// We could be in this scenario if project is the configured project tracked by external project
+2 -1
View File
@@ -573,6 +573,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
directoryStructureHost: DirectoryStructureHost,
currentDirectory: string,
) {
projectService.logger.info(`Creating ${ProjectKind[projectKind]}Project: ${projectName}, currentDirectory: ${currentDirectory}`);
this.projectName = projectName;
this.directoryStructureHost = directoryStructureHost;
this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory);
@@ -1622,7 +1623,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
protected removeExistingTypings(include: string[]): string[] {
if (!include.length) return include;
const existing = getAutomaticTypeDirectiveNames(this.getCompilerOptions(), this.directoryStructureHost);
const existing = getAutomaticTypeDirectiveNames(this.getCompilerOptions(), this);
return filter(include, i => !existing.includes(i));
}
+1 -1
View File
@@ -136,6 +136,7 @@ export * from "./unittests/tscWatch/extends.js";
export * from "./unittests/tscWatch/forceConsistentCasingInFileNames.js";
export * from "./unittests/tscWatch/incremental.js";
export * from "./unittests/tscWatch/libraryResolution.js";
export * from "./unittests/tscWatch/listFilesOnly.js";
export * from "./unittests/tscWatch/moduleResolution.js";
export * from "./unittests/tscWatch/nodeNextWatch.js";
export * from "./unittests/tscWatch/noEmit.js";
@@ -200,7 +201,6 @@ export * from "./unittests/tsserver/pasteEdits.js";
export * from "./unittests/tsserver/plugins.js";
export * from "./unittests/tsserver/pluginsAsync.js";
export * from "./unittests/tsserver/projectErrors.js";
export * from "./unittests/tsserver/projectImportHelpers.js";
export * from "./unittests/tsserver/projectReferenceCompileOnSave.js";
export * from "./unittests/tsserver/projectReferenceErrors.js";
export * from "./unittests/tsserver/projectReferences.js";
@@ -1,9 +1,9 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { FsContents } from "./contents.js";
import { libFile } from "./virtualFileSystemWithWatch.js";
import { TscWatchCompileChange } from "./tscWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsConentsForAlternateResultAtTypesPackageJson(packageName: string, addTypesCondition: boolean) {
function getFsConentsForAlternateResultAtTypesPackageJson(packageName: string, addTypesCondition: boolean) {
return jsonToReadableText({
name: `@types/${packageName}`,
version: "1.0.0",
@@ -17,7 +17,7 @@ export function getFsConentsForAlternateResultAtTypesPackageJson(packageName: st
});
}
export function getFsContentsForAlternateResultPackageJson(packageName: string, addTypes: boolean, addTypesCondition: boolean) {
function getFsContentsForAlternateResultPackageJson(packageName: string, addTypes: boolean, addTypesCondition: boolean) {
return jsonToReadableText({
name: packageName,
version: "1.0.0",
@@ -33,7 +33,7 @@ export function getFsContentsForAlternateResultPackageJson(packageName: string,
});
}
export function getFsContentsForAlternateResultDts(packageName: string) {
function getFsContentsForAlternateResultDts(packageName: string) {
return `export declare const ${packageName}: number;`;
}
@@ -45,8 +45,8 @@ function mjs(packageName: string) {
return `export const ${packageName} = 1;`;
}
export function getFsContentsForAlternateResult(): FsContents {
return {
function getSysForAlternateResult(forTsserver: boolean) {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/projects/project/node_modules/@types/bar/package.json": getFsConentsForAlternateResultAtTypesPackageJson("bar", /*addTypesCondition*/ false),
"/home/src/projects/project/node_modules/@types/bar/index.d.ts": getFsContentsForAlternateResultDts("bar"),
"/home/src/projects/project/node_modules/bar/package.json": getFsContentsForAlternateResultPackageJson("bar", /*addTypes*/ false, /*addTypesCondition*/ false),
@@ -81,6 +81,117 @@ export function getFsContentsForAlternateResult(): FsContents {
},
files: ["index.mts"],
}),
[libFile.path]: libFile.content,
};
}, { currentDirectory: "/home/src/projects/project" });
}
export function verifyAlternateResultScenario(
forTsserver: boolean,
action: (
scenario: string,
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
) => void,
) {
action(
"alternateResult",
() => getSysForAlternateResult(forTsserver),
() => [
{
caption: "delete the alternateResult in @types",
edit: sys => sys.deleteFile("/home/src/projects/project/node_modules/@types/bar/index.d.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "delete the ndoe10Result in package/types",
edit: sys => sys.deleteFile("/home/src/projects/project/node_modules/foo/index.d.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "add the alternateResult in @types",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/@types/bar/index.d.ts", getFsContentsForAlternateResultDts("bar")),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "add the alternateResult in package/types",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/foo/index.d.ts", getFsContentsForAlternateResultDts("foo")),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "update package.json from @types so error is fixed",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/@types/bar/package.json", getFsConentsForAlternateResultAtTypesPackageJson("bar", /*addTypesCondition*/ true)),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "update package.json so error is fixed",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/foo/package.json", getFsContentsForAlternateResultPackageJson("foo", /*addTypes*/ true, /*addTypesCondition*/ true)),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "update package.json from @types so error is introduced",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/@types/bar2/package.json", getFsConentsForAlternateResultAtTypesPackageJson("bar2", /*addTypesCondition*/ false)),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "update package.json so error is introduced",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/foo2/package.json", getFsContentsForAlternateResultPackageJson("foo2", /*addTypes*/ true, /*addTypesCondition*/ false)),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "delete the alternateResult in @types",
edit: sys => sys.deleteFile("/home/src/projects/project/node_modules/@types/bar2/index.d.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "delete the ndoe10Result in package/types",
edit: sys => sys.deleteFile("/home/src/projects/project/node_modules/foo2/index.d.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "add the alternateResult in @types",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/@types/bar2/index.d.ts", getFsContentsForAlternateResultDts("bar2")),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "add the ndoe10Result in package/types",
edit: sys => sys.writeFile("/home/src/projects/project/node_modules/foo2/index.d.ts", getFsContentsForAlternateResultDts("foo2")),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
],
);
}
+134 -18
View File
@@ -1,15 +1,13 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as Harness from "../../_namespaces/Harness.js";
import { SourceMapRecorder } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { TscCompileSystem } from "./tsc.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function sanitizeSysOutput(output: string) {
return output
.replace(/Elapsed::\s\d+(?:\.\d+)?ms/g, "Elapsed:: *ms")
.replace(/\d\d:\d\d:\d\d\s(?:A|P)M/g, "HH:MM:SS AM");
}
import {
changeToHostTrackingWrittenFiles,
SerializeOutputOrder,
StateLogger,
TestServerHost,
TestServerHostTrackingWrittenFiles,
} from "./virtualFileSystemWithWatch.js";
export type CommandLineProgram = [ts.Program, ts.BuilderProgram?];
export interface CommandLineCallbacks {
@@ -21,7 +19,7 @@ function isAnyProgram(program: ts.Program | ts.BuilderProgram | ts.ParsedCommand
return !!(program as ts.Program | ts.BuilderProgram).getCompilerOptions;
}
export function commandLineCallbacks(
sys: TscCompileSystem | TestServerHost,
sys: TestServerHost,
originalReadCall?: ts.System["readFile"],
): CommandLineCallbacks {
let programs: CommandLineProgram[] | undefined;
@@ -48,7 +46,12 @@ export function commandLineCallbacks(
};
}
export function baselinePrograms(baseline: string[], programs: readonly CommandLineProgram[], oldPrograms: readonly (CommandLineProgram | undefined)[], baselineDependencies: boolean | undefined) {
function baselinePrograms(
baseline: string[],
programs: readonly CommandLineProgram[],
oldPrograms: readonly (CommandLineProgram | undefined)[],
baselineDependencies: boolean | undefined,
) {
for (let i = 0; i < programs.length; i++) {
baselineProgram(baseline, programs[i], oldPrograms[i], baselineDependencies);
}
@@ -127,10 +130,10 @@ function baselineProgram(baseline: string[], [program, builderProgram]: CommandL
baseline.push("");
}
export function generateSourceMapBaselineFiles(sys: ts.System & { writtenFiles: ts.ReadonlyCollection<ts.Path>; }) {
function generateSourceMapBaselineFiles(sys: ts.System & { writtenFiles: ts.ReadonlyCollection<ts.Path>; }) {
const mapFileNames = ts.mapDefinedIterator(sys.writtenFiles.keys(), f => f.endsWith(".map") ? f : undefined);
for (const mapFile of mapFileNames) {
const text = Harness.SourceMapRecorder.getSourceMapRecordWithSystem(sys, mapFile);
const text = SourceMapRecorder.getSourceMapRecordWithSystem(sys, mapFile);
sys.writeFile(`${mapFile}.baseline.txt`, text);
}
}
@@ -228,7 +231,7 @@ export interface ReadableBuildInfo extends ts.BuildInfo {
function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildInfo: ts.BuildInfo) {
let fileIdsList: string[][] | undefined;
let result;
const version = buildInfo.version === ts.version ? fakes.version : buildInfo.version;
const version = buildInfo.version === ts.version ? fakeTsVersion : buildInfo.version;
if (!ts.isIncrementalBuildInfo(buildInfo)) {
result = {
...buildInfo,
@@ -396,7 +399,7 @@ export function toPathWithSystem(sys: ts.System, fileName: string): ts.Path {
export function baselineBuildInfo(
options: ts.CompilerOptions,
sys: TscCompileSystem | TestServerHost,
sys: TestServerHost,
originalReadCall?: ts.System["readFile"],
) {
const buildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(options);
@@ -408,6 +411,119 @@ export function baselineBuildInfo(
generateBuildInfoBaseline(sys, buildInfoPath, buildInfo);
}
export function tscBaselineName(scenario: string, subScenario: string, commandLineArgs: readonly string[], isWatch?: boolean, suffix?: string) {
return `${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
export function isWatch(commandLineArgs: readonly string[]) {
return ts.forEach(commandLineArgs, arg => {
if (arg.charCodeAt(0) !== ts.CharacterCodes.minus) return false;
const option = arg.slice(arg.charCodeAt(1) === ts.CharacterCodes.minus ? 2 : 1).toLowerCase();
return option === "watch" || option === "w";
});
}
export type TscWatchSystem = TestServerHostTrackingWrittenFiles;
function changeToTestServerHostWithTimeoutLogging(host: TestServerHostTrackingWrittenFiles, baseline: string[]): TscWatchSystem {
const logger: StateLogger = {
log: s => baseline.push(s),
logs: baseline,
};
host.switchToBaseliningInvoke(logger, SerializeOutputOrder.BeforeDiff);
return host;
}
export interface BaselineBase {
baseline: string[];
sys: TscWatchSystem;
}
export interface Baseline extends BaselineBase, CommandLineCallbacks {
}
export const fakeTsVersion = "FakeTSVersion";
export function patchHostForBuildInfoReadWrite<T extends ts.System>(sys: T) {
const originalReadFile = sys.readFile;
sys.readFile = (path, encoding) => {
const value = originalReadFile.call(sys, path, encoding);
if (!value || !ts.isBuildInfoFile(path)) return value;
const buildInfo = ts.getBuildInfo(path, value);
if (!buildInfo) return value;
ts.Debug.assert(buildInfo.version === fakeTsVersion);
buildInfo.version = ts.version;
return ts.getBuildInfoText(buildInfo);
};
return patchHostForBuildInfoWrite(sys, fakeTsVersion);
}
export function patchHostForBuildInfoWrite<T extends ts.System>(sys: T, version: string) {
const originalWrite = sys.write;
sys.write = msg => originalWrite.call(sys, msg.replace(ts.version, version));
const originalWriteFile = sys.writeFile;
sys.writeFile = (fileName: string, content: string, writeByteOrderMark: boolean) => {
if (ts.isBuildInfoFile(fileName)) {
const buildInfo = ts.getBuildInfo(fileName, content);
if (buildInfo) {
buildInfo.version = version;
return originalWriteFile.call(sys, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
}
}
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
};
return sys;
}
export function createBaseline(
system: TestServerHost,
modifySystem?: (sys: TestServerHost, originalRead: TestServerHost["readFile"]) => void,
): Baseline {
const originalRead = system.readFile;
const initialSys = patchHostForBuildInfoReadWrite(system);
modifySystem?.(initialSys, originalRead);
const baseline: string[] = [];
const sys = changeToTestServerHostWithTimeoutLogging(changeToHostTrackingWrittenFiles(initialSys), baseline);
baseline.push(`currentDirectory:: ${sys.getCurrentDirectory()} useCaseSensitiveFileNames:: ${sys.useCaseSensitiveFileNames}`);
baseline.push("Input::");
sys.serializeState(baseline, SerializeOutputOrder.None);
const { cb, getPrograms } = commandLineCallbacks(sys);
return { sys, baseline, cb, getPrograms };
}
export function applyEdit(
sys: BaselineBase["sys"],
baseline: BaselineBase["baseline"],
edit: (sys: TscWatchSystem) => void,
caption?: string,
) {
baseline.push(`Change::${caption ? " " + caption : ""}`, "");
edit(sys);
baseline.push("Input::");
sys.serializeState(baseline, SerializeOutputOrder.AfterDiff);
}
export function baselineAfterTscCompile(
sys: BaselineBase["sys"],
baseline: BaselineBase["baseline"],
getPrograms: CommandLineCallbacks["getPrograms"],
oldPrograms: readonly (CommandLineProgram | undefined)[],
baselineSourceMap: boolean | undefined,
shouldBaselinePrograms: boolean | undefined,
baselineDependencies: boolean | undefined,
) {
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
const programs = getPrograms();
sys.writtenFiles.forEach((value, key) => {
// When buildinfo is same for two projects,
// it gives error and doesnt write buildinfo but because buildInfo is written for one project,
// readable baseline will be written two times for those two projects with same contents and is ok
ts.Debug.assert(value === 1 || ts.endsWith(key, "baseline.txt"), `Expected to write file ${key} only once`);
});
sys.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
if (shouldBaselinePrograms) {
baselinePrograms(baseline, programs, oldPrograms, baselineDependencies);
}
baseline.push(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}`, "");
return programs;
}
export function tscBaselineName(scenario: string, subScenario: string, commandLineArgs: readonly string[], suffix?: string) {
return `${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
}
+18 -13
View File
@@ -1,14 +1,27 @@
import {
harnessSessionLibLocation,
harnessTypingInstallerCacheLocation,
} from "../../../harness/harnessLanguageService.js";
import * as ts from "../../_namespaces/ts.js";
import { libFile } from "./virtualFileSystemWithWatch.js";
/** Default typescript and lib installs location for tests */
export const tscTypeScriptTestLocation = getPathForTypeScriptTestLocation("tsc.js");
export function getPathForTypeScriptTestLocation(fileName: string) {
return ts.combinePaths(harnessSessionLibLocation, fileName);
}
export function getTypeScriptLibTestLocation(libName: string) {
return getPathForTypeScriptTestLocation(ts.libMap.get(libName) ?? `lib.${libName}.d.ts`);
}
export function getPathForTypeScriptTypingInstallerCacheTest(fileName: string) {
return `${harnessTypingInstallerCacheLocation}/${fileName}`;
}
export function compilerOptionsToConfigJson(options: ts.CompilerOptions) {
return ts.optionMapToObject(ts.serializeCompilerOptions(options));
}
export const libContent = `${libFile.content}
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };`;
export const symbolLibContent = `
interface SymbolConstructor {
readonly species: symbol;
@@ -20,14 +33,6 @@ interface Symbol {
}
`;
export interface FsContents {
[path: string]: string;
}
export function libPath(forLib: string) {
return `${ts.getDirectoryPath(libFile.path)}/lib.${forLib}.d.ts`;
}
export function getProjectConfigWithNodeNext(withNodeNext: boolean | undefined) {
return withNodeNext ? { module: "nodenext", target: "es5" } : undefined;
}
@@ -1,13 +1,11 @@
import { CompilerOptions } from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { FileSystem } from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { libContent } from "./contents.js";
import { loadProjectFromFiles } from "./vfs.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsForDeclarationEmitWithErrors(options: CompilerOptions, incremental: true | undefined) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
function getSysForDeclarationEmitWithErrors(options: CompilerOptions, incremental: true | undefined) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "NodeNext",
moduleResolution: "NodeNext",
@@ -17,32 +15,31 @@ export function getFsForDeclarationEmitWithErrors(options: CompilerOptions, incr
skipDefaultLibCheck: true,
},
}),
"/src/project/index.ts": dedent`
"/home/src/workspaces/project/index.ts": dedent`
import ky from 'ky';
export const api = ky.extend({});
`,
"/src/project/package.json": jsonToReadableText({
"/home/src/workspaces/project/package.json": jsonToReadableText({
type: "module",
}),
"/src/project/node_modules/ky/distribution/index.d.ts": dedent`
"/home/src/workspaces/project/node_modules/ky/distribution/index.d.ts": dedent`
type KyInstance = {
extend(options: Record<string,unknown>): KyInstance;
}
declare const ky: KyInstance;
export default ky;
`,
"/src/project/node_modules/ky/package.json": jsonToReadableText({
"/home/src/workspaces/project/node_modules/ky/package.json": jsonToReadableText({
name: "ky",
type: "module",
main: "./distribution/index.js",
}),
"/lib/lib.esnext.full.d.ts": libContent,
});
}, { currentDirectory: "/home/src/workspaces/project" });
}
export function getFsForDeclarationEmitWithErrorsWithOutFile(options: CompilerOptions, incremental: true | undefined) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
function getSysForDeclarationEmitWithErrorsWithOutFile(options: CompilerOptions, incremental: true | undefined) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "amd",
...options,
@@ -53,25 +50,24 @@ export function getFsForDeclarationEmitWithErrorsWithOutFile(options: CompilerOp
},
include: ["src"],
}),
"/src/project/src/index.ts": dedent`
"/home/src/workspaces/project/src/index.ts": dedent`
import ky from 'ky';
export const api = ky.extend({});
`,
"/src/project/ky.d.ts": dedent`
"/home/src/workspaces/project/ky.d.ts": dedent`
type KyInstance = {
extend(options: Record<string,unknown>): KyInstance;
}
declare const ky: KyInstance;
export default ky;
`,
"/lib/lib.esnext.full.d.ts": libContent,
});
}, { currentDirectory: "/home/src/workspaces/project" });
}
export function forEachDeclarationEmitWithErrorsScenario(
action: (
scenarioName: (scenario: string) => string,
fs: () => FileSystem,
sys: () => TestServerHost,
) => void,
withComposite: boolean,
) {
@@ -80,8 +76,8 @@ export function forEachDeclarationEmitWithErrorsScenario(
action(
scenario => `${outFile ? "outFile" : "multiFile"}/${scenario}${incremental ? " with incremental" : ""}`,
() =>
(outFile ? getFsForDeclarationEmitWithErrorsWithOutFile :
getFsForDeclarationEmitWithErrors)(
(outFile ? getSysForDeclarationEmitWithErrorsWithOutFile :
getSysForDeclarationEmitWithErrors)(
withComposite && incremental ?
{ composite: true } :
{ declaration: true },
@@ -1,14 +1,6 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
libContent,
} from "./contents.js";
import { loadProjectFromFiles } from "./vfs.js";
import {
createWatchedSystem,
libFile,
} from "./virtualFileSystemWithWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsContentsForDemoProjectReferencesCoreConfig(additional?: object) {
return jsonToReadableText({
@@ -20,8 +12,8 @@ export function getFsContentsForDemoProjectReferencesCoreConfig(additional?: obj
...additional,
});
}
export function getFsContentsForDemoProjectReferences(): FsContents {
return {
export function getSysForDemoProjectReferences() {
return TestServerHost.createWatchedSystem({
"/user/username/projects/demo/animals/animal.ts": dedent`
export type Size = "small" | "medium" | "large";
export default interface Animal {
@@ -124,25 +116,5 @@ export function getFsContentsForDemoProjectReferences(): FsContents {
},
],
}),
[libFile.path]: libContent,
};
}
export function getFsForDemoProjectReferences() {
return loadProjectFromFiles(
getFsContentsForDemoProjectReferences(),
{
cwd: "/user/username/projects/demo",
executingFilePath: libFile.path,
},
);
}
export function getSysForDemoProjectReferences() {
return createWatchedSystem(
getFsContentsForDemoProjectReferences(),
{
currentDirectory: "/user/username/projects/demo",
},
);
}, { currentDirectory: "/user/username/projects/demo" });
}
+26 -14
View File
@@ -1,15 +1,10 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { FsContents } from "./contents.js";
import {
createServerHost,
createWatchedSystem,
libFile,
TestServerHost,
} from "./virtualFileSystemWithWatch.js";
import { TscWatchCompileChange } from "./tscWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getSymlinkedExtendsSys(forTsserver?: true): TestServerHost {
return (!forTsserver ? createWatchedSystem : createServerHost)({
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/users/user/projects/myconfigs/node_modules/@something/tsconfig-node/tsconfig.json": jsonToReadableText({
extends: "@something/tsconfig-base/tsconfig.json",
compilerOptions: {
@@ -29,12 +24,11 @@ export function getSymlinkedExtendsSys(forTsserver?: true): TestServerHost {
"/users/user/projects/myproject/node_modules/@something/tsconfig-node": {
symLink: "/users/user/projects/myconfigs/node_modules/@something/tsconfig-node",
},
[libFile.path]: libFile.content,
}, { currentDirectory: "/users/user/projects/myproject" });
}
export function getConfigDirExtendsSys(): FsContents {
return {
export function getConfigDirExtendsSys(forTsserver?: boolean): TestServerHost {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/projects/configs/first/tsconfig.json": jsonToReadableText({
extends: "../second/tsconfig.json",
include: ["${configDir}/src"], // eslint-disable-line no-template-curly-in-string
@@ -82,11 +76,10 @@ export function getConfigDirExtendsSys(): FsContents {
"/home/src/projects/myproject/root2/other/sometype2/index.d.ts": dedent`
export const k = 10;
`,
[libFile.path]: libFile.content,
};
}, { currentDirectory: "/home/src/projects/myproject" });
}
export function modifyFirstExtendedConfigOfConfigDirExtendsSys(sys: TestServerHost) {
function modifyFirstExtendedConfigOfConfigDirExtendsSys(sys: TestServerHost) {
sys.modifyFile(
"/home/src/projects/configs/first/tsconfig.json",
jsonToReadableText({
@@ -99,3 +92,22 @@ export function modifyFirstExtendedConfigOfConfigDirExtendsSys(sys: TestServerHo
}),
);
}
export function forConfigDirExtendsSysScenario(
forTsserver: boolean,
action: (
scenario: string,
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
) => void,
) {
action(
"configDir template",
() => getConfigDirExtendsSys(forTsserver),
() => [{
caption: "edit extended config file",
edit: modifyFirstExtendedConfigOfConfigDirExtendsSys,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}],
);
}
@@ -1,13 +1,9 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
libContent,
} from "./contents.js";
import { libFile } from "./virtualFileSystemWithWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsContentsForMultipleErrorsForceConsistentCasingInFileNames(): FsContents {
return {
export function getSysForMultipleErrorsForceConsistentCasingInFileNames(forTsserver?: boolean) {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/projects/project/src/struct.d.ts": dedent`
import * as xs1 from "fp-ts/lib/Struct";
import * as xs2 from "fp-ts/lib/struct";
@@ -28,6 +24,5 @@ export function getFsContentsForMultipleErrorsForceConsistentCasingInFileNames()
`,
"/home/src/projects/project/tsconfig.json": jsonToReadableText({}),
"/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts": `export function foo(): void`,
[libFile.path]: libContent,
};
}, { currentDirectory: "/home/src/projects/project" });
}
@@ -1,89 +1,217 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { TscWatchSystem } from "./baseline.js";
import { getTypeScriptLibTestLocation } from "./contents.js";
import { TscWatchCompileChange } from "./tscWatch.js";
import {
FsContents,
libContent,
} from "./contents.js";
import { loadProjectFromFiles } from "./vfs.js";
import {
createServerHost,
createWatchedSystem,
libFile,
TestServerHost,
} from "./virtualFileSystemWithWatch.js";
function getFsContentsForLibResolution(libRedirection?: boolean): FsContents {
return {
"/home/src/projects/project1/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project1/file.ts": `export const file = 10;`,
"/home/src/projects/project1/core.d.ts": `export const core = 10;`,
"/home/src/projects/project1/index.ts": `export const x = "type1";`,
"/home/src/projects/project1/file2.ts": dedent`
function getSysForLibResolution(libRedirection?: boolean, forTsserver?: boolean) {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/workspace/projects/project1/utils.d.ts": `export const y = 10;`,
"/home/src/workspace/projects/project1/file.ts": `export const file = 10;`,
"/home/src/workspace/projects/project1/core.d.ts": `export const core = 10;`,
"/home/src/workspace/projects/project1/index.ts": `export const x = "type1";`,
"/home/src/workspace/projects/project1/file2.ts": dedent`
/// <reference lib="webworker"/>
/// <reference lib="scripthost"/>
/// <reference lib="es5"/>
`,
"/home/src/projects/project1/tsconfig.json": jsonToReadableText({
"/home/src/workspace/projects/project1/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, typeRoots: ["./typeroot1"], lib: ["es5", "dom"], traceResolution: true },
}),
"/home/src/projects/project1/typeroot1/sometype/index.d.ts": `export type TheNum = "type1";`,
"/home/src/projects/project2/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project2/index.ts": `export const y = 10`,
"/home/src/projects/project2/tsconfig.json": jsonToReadableText({
"/home/src/workspace/projects/project1/typeroot1/sometype/index.d.ts": `export type TheNum = "type1";`,
"/home/src/workspace/projects/project2/utils.d.ts": `export const y = 10;`,
"/home/src/workspace/projects/project2/index.ts": `export const y = 10`,
"/home/src/workspace/projects/project2/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, lib: ["es5", "dom"], traceResolution: true },
}),
"/home/src/projects/project3/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project3/index.ts": `export const z = 10`,
"/home/src/projects/project3/tsconfig.json": jsonToReadableText({
"/home/src/workspace/projects/project3/utils.d.ts": `export const y = 10;`,
"/home/src/workspace/projects/project3/index.ts": `export const z = 10`,
"/home/src/workspace/projects/project3/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, lib: ["es5", "dom"], traceResolution: true },
}),
"/home/src/projects/project4/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project4/index.ts": `export const z = 10`,
"/home/src/projects/project4/tsconfig.json": jsonToReadableText({
"/home/src/workspace/projects/project4/utils.d.ts": `export const y = 10;`,
"/home/src/workspace/projects/project4/index.ts": `export const z = 10`,
"/home/src/workspace/projects/project4/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, lib: ["esnext", "dom", "webworker"], traceResolution: true },
}),
"/home/src/lib/lib.es5.d.ts": libContent,
"/home/src/lib/lib.esnext.d.ts": libContent,
"/home/src/lib/lib.dom.d.ts": "interface DOMInterface { }",
"/home/src/lib/lib.webworker.d.ts": "interface WebWorkerInterface { }",
"/home/src/lib/lib.scripthost.d.ts": "interface ScriptHostInterface { }",
"/home/src/projects/node_modules/@typescript/unlreated/index.d.ts": "export const unrelated = 10;",
[getTypeScriptLibTestLocation("dom")]: "interface DOMInterface { }",
[getTypeScriptLibTestLocation("webworker")]: "interface WebWorkerInterface { }",
[getTypeScriptLibTestLocation("scripthost")]: "interface ScriptHostInterface { }",
"/home/src/workspace/projects/node_modules/@typescript/unlreated/index.d.ts": "export const unrelated = 10;",
...libRedirection ? {
"/home/src/projects/node_modules/@typescript/lib-es5/index.d.ts": libContent,
"/home/src/projects/node_modules/@typescript/lib-esnext/index.d.ts": libContent,
"/home/src/projects/node_modules/@typescript/lib-dom/index.d.ts": "interface DOMInterface { }",
"/home/src/projects/node_modules/@typescript/lib-webworker/index.d.ts": "interface WebworkerInterface { }",
"/home/src/projects/node_modules/@typescript/lib-scripthost/index.d.ts": "interface ScriptHostInterface { }",
"/home/src/workspace/projects/node_modules/@typescript/lib-es5/index.d.ts": libFile.content,
"/home/src/workspace/projects/node_modules/@typescript/lib-esnext/index.d.ts": libFile.content,
"/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts": "interface DOMInterface { }",
"/home/src/workspace/projects/node_modules/@typescript/lib-webworker/index.d.ts": "interface WebWorkerInterface { }",
"/home/src/workspace/projects/node_modules/@typescript/lib-scripthost/index.d.ts": "interface ScriptHostInterface { }",
} : undefined,
};
}, { currentDirectory: "/home/src/workspace/projects" });
}
export function getFsForLibResolution(libRedirection: true | undefined) {
return loadProjectFromFiles(
getFsContentsForLibResolution(libRedirection),
{
cwd: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
},
);
function getLibResolutionEditOptions(
withoutConfig: true | undefined,
changeLib: (sys: TscWatchSystem) => void,
): readonly TscWatchCompileChange[] {
return withoutConfig ?
emptyArray :
[
{
caption: "change program options to update module resolution",
edit: sys =>
sys.writeFile(
"/home/src/workspace/projects/project1/tsconfig.json",
jsonToReadableText({
compilerOptions: {
composite: true,
typeRoots: ["./typeroot1", "./typeroot2"],
lib: ["es5", "dom"],
traceResolution: true,
},
}),
),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "change program options to update module resolution and also update lib file",
edit: sys => {
sys.writeFile(
"/home/src/workspace/projects/project1/tsconfig.json",
jsonToReadableText({
compilerOptions: {
composite: true,
typeRoots: ["./typeroot1"],
lib: ["es5", "dom"],
traceResolution: true,
},
}),
);
changeLib(sys);
},
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
];
}
export function getSysForLibResolution(libRedirection?: true) {
return createWatchedSystem(
getFsContentsForLibResolution(libRedirection),
function getLibResolutionWithoutRedirection(withoutConfig: true | undefined): readonly TscWatchCompileChange[] {
return [
{
currentDirectory: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
caption: "write redirect file dom",
edit: sys => sys.ensureFileOrFolder({ path: "/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts", content: "interface DOMInterface { }" }),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
);
{
caption: "edit file",
edit: sys => sys.appendFile("/home/src/workspace/projects/project1/file.ts", "export const xyz = 10;"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "delete core",
edit: sys => sys.deleteFile("/home/src/workspace/projects/project1/core.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "delete redirect file dom",
edit: sys => sys.deleteFile("/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
...getLibResolutionEditOptions(
withoutConfig,
sys =>
sys.ensureFileOrFolder({
path: "/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts",
content: "interface DOMInterface { }",
}),
),
{
caption: "write redirect file webworker",
edit: sys => sys.ensureFileOrFolder({ path: "/home/src/workspace/projects/node_modules/@typescript/lib-webworker/index.d.ts", content: "interface WebWorkerInterface { }" }),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "delete redirect file webworker",
edit: sys => sys.deleteFile("/home/src/workspace/projects/node_modules/@typescript/lib-webworker/index.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
];
}
export function getServerHostForLibResolution(libRedirection?: true) {
return createServerHost(
getFsContentsForLibResolution(libRedirection),
function getLibResolutionWithRedirection(withoutConfig: true | undefined): readonly TscWatchCompileChange[] {
return [
{
currentDirectory: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
caption: "delete redirect file dom",
edit: sys => sys.deleteFile("/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
);
{
caption: "edit file",
edit: sys => sys.appendFile("/home/src/workspace/projects/project1/file.ts", "export const xyz = 10;"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "delete core",
edit: sys => sys.deleteFile("/home/src/workspace/projects/project1/core.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "write redirect file dom",
edit: sys => sys.ensureFileOrFolder({ path: "/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts", content: "interface DOMInterface { }" }),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
...getLibResolutionEditOptions(
withoutConfig,
sys => sys.deleteFile("/home/src/workspace/projects/node_modules/@typescript/lib-dom/index.d.ts"),
),
{
caption: "delete redirect file webworker",
edit: sys => sys.deleteFile("/home/src/workspace/projects/node_modules/@typescript/lib-webworker/index.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "write redirect file webworker",
edit: sys => sys.ensureFileOrFolder({ path: "/home/src/workspace/projects/node_modules/@typescript/lib-webworker/index.d.ts", content: "interface WebWorkerInterface { }" }),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
];
}
export function forEachLibResolutionScenario(
forTsserver: boolean,
withoutConfig: true | undefined,
action: (scenario: string, sys: () => TestServerHost, edits: () => readonly TscWatchCompileChange[]) => void,
) {
[undefined, true].forEach(libRedirection => {
action(
`${withoutConfig ? "without" : "with"} config${libRedirection ? " with redirection" : ""}`,
() => getSysForLibResolution(libRedirection, forTsserver),
() =>
libRedirection ?
getLibResolutionWithRedirection(withoutConfig) :
getLibResolutionWithoutRedirection(withoutConfig),
);
});
}
export function getCommandLineArgsForLibResolution(withoutConfig: true | undefined) {
@@ -92,45 +220,24 @@ export function getCommandLineArgsForLibResolution(withoutConfig: true | undefin
["-p", "project1", "--explainFiles"];
}
function getFsContentsForLibResolutionUnknown(): FsContents {
return {
"/home/src/projects/project1/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project1/file.ts": `export const file = 10;`,
"/home/src/projects/project1/core.d.ts": `export const core = 10;`,
"/home/src/projects/project1/index.ts": `export const x = "type1";`,
"/home/src/projects/project1/file2.ts": dedent`
export function getSysForLibResolutionUnknown() {
return TestServerHost.createWatchedSystem({
"/home/src/workspace/projects/project1/utils.d.ts": `export const y = 10;`,
"/home/src/workspace/projects/project1/file.ts": `export const file = 10;`,
"/home/src/workspace/projects/project1/core.d.ts": `export const core = 10;`,
"/home/src/workspace/projects/project1/index.ts": `export const x = "type1";`,
"/home/src/workspace/projects/project1/file2.ts": dedent`
/// <reference lib="webworker2"/>
/// <reference lib="unknownlib"/>
/// <reference lib="scripthost"/>
`,
"/home/src/projects/project1/tsconfig.json": jsonToReadableText({
"/home/src/workspace/projects/project1/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
traceResolution: true,
},
}),
"/home/src/lib/lib.d.ts": libContent,
"/home/src/lib/lib.webworker.d.ts": "interface WebWorkerInterface { }",
"/home/src/lib/lib.scripthost.d.ts": "interface ScriptHostInterface { }",
};
}
export function getFsForLibResolutionUnknown() {
return loadProjectFromFiles(
getFsContentsForLibResolutionUnknown(),
{
cwd: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
},
);
}
export function getSysForLibResolutionUnknown() {
return createWatchedSystem(
getFsContentsForLibResolutionUnknown(),
{
currentDirectory: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
},
);
[getTypeScriptLibTestLocation("webworker")]: "interface WebWorkerInterface { }",
[getTypeScriptLibTestLocation("scripthost")]: "interface ScriptHostInterface { }",
}, { currentDirectory: "/home/src/workspace/projects" });
}
@@ -4,16 +4,10 @@ import {
} from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { libContent } from "./contents.js";
import { TscWatchSystem } from "./baseline.js";
import { solutionBuildWithBaseline } from "./solutionBuilder.js";
import { TscWatchCompileChange } from "./tscWatch.js";
import {
TscWatchCompileChange,
TscWatchSystem,
} from "./tscWatch.js";
import {
createServerHost,
createWatchedSystem,
libFile,
osFlavorToString,
TestServerHost,
TestServerHostOsFlavor,
@@ -38,7 +32,7 @@ function getMonorepoSymlinkedSiblingPackagesSys(forTsserver: boolean, built: boo
"dist/**/*",
],
});
const sys = (!forTsserver ? createWatchedSystem : createServerHost)({
const sys = TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/projects/project/packages/package1/package.json": getPackageJson("package1"),
"/home/src/projects/project/packages/package1/tsconfig.json": configText,
"/home/src/projects/project/packages/package1/src/index.ts": dedent`
@@ -53,7 +47,6 @@ function getMonorepoSymlinkedSiblingPackagesSys(forTsserver: boolean, built: boo
type MyBarType = BarType;
`,
"/home/src/projects/project/node_modules/package1": { symLink: "/home/src/projects/project/packages/package1" },
"/a/lib/lib.es2016.full.d.ts": libContent,
}, { currentDirectory: "/home/src/projects/project", osFlavor });
if (built) buildMonorepoSymlinkedSiblingPackage1(sys);
return sys;
@@ -68,7 +61,7 @@ function getPackageJson(packageName: string) {
}
function buildMonorepoSymlinkedSiblingPackage1(host: TestServerHost) {
solutionBuildWithBaseline(host, ["packages/package1"]);
solutionBuildWithBaseline(host, ["/home/src/projects/project/packages/package1"]);
}
function cleanMonorepoSymlinkedSiblingPackage1(host: TestServerHost) {
@@ -82,6 +75,7 @@ function forEachMonorepoSymlinkedSiblingPackagesSys(
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
project: string,
currentDirectory: string,
) => void,
) {
for (const built of [false, true]) {
@@ -98,6 +92,7 @@ function forEachMonorepoSymlinkedSiblingPackagesSys(
cleanMonorepoSymlinkedSiblingPackage1,
),
"packages/package2",
"/home/src/projects/project",
);
}
}
@@ -149,7 +144,7 @@ function getMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(
built: boolean,
osFlavor: TestServerHostOsFlavor,
): TestServerHost {
const sys = (!forTsserver ? createWatchedSystem : createServerHost)({
const sys = TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/projects/c/3/c-impl/c/src/c.ts": `export const c: string = 'test';`,
"/home/src/projects/c/3/c-impl/c/src/index.ts": `export * from './c';`,
"/home/src/projects/c/3/c-impl/c/tsconfig.json": jsonToReadableText({
@@ -192,14 +187,13 @@ function getMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(
include: ["src/**/*.ts"],
}),
"/home/src/projects/b/2/b-impl/b/node_modules/a": { symLink: "/home/src/projects/a/1/a-impl/a" },
[libFile.path]: libContent,
}, { currentDirectory: "/home/src/projects/b/2/b-impl/b", osFlavor });
if (built) buildDependenciesOfMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(sys);
return sys;
}
function buildDependenciesOfMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(host: TestServerHost) {
solutionBuildWithBaseline(host, ["../../../../c/3/c-impl/c", "../../../../a/1/a-impl/a"]);
solutionBuildWithBaseline(host, ["/home/src/projects/c/3/c-impl/c", "/home/src/projects/a/1/a-impl/a"]);
}
function cleanDependenciesOfMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(host: TestServerHost) {
@@ -214,6 +208,7 @@ function forEachMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
indexFile: string,
currentDirectory: string,
) => void,
) {
for (const built of [false, true]) {
@@ -266,6 +261,7 @@ function forEachMonorepoSymlinkedSiblingPackagesSysWithUnRelatedFolders(
],
),
".",
"/home/src/projects/b/2/b-impl/b",
);
}
}
@@ -278,6 +274,7 @@ export function forEachMonorepoSymlinkScenario(
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
indexFile: string,
currentDirectory: string,
) => void,
) {
describe("monorepoSymlinkedSiblingPackages:: monorepo style sibling packages symlinked", () => {
+13 -12
View File
@@ -1,15 +1,16 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import {
noChangeRun,
TestTscEdit,
verifyTsc,
} from "./tsc.js";
import { loadProjectFromFiles } from "./vfs.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function forEachTscScenarioWithNoCheck(buildType: "-b" | "-p") {
const commandLineArgs = buildType === "-b" ?
["-b", "/src/tsconfig.json", "-v"] :
["-p", "/src/tsconfig.json"];
["-b", "-v"] :
emptyArray;
function forEachNoCheckScenarioWorker(
subScenario: string,
@@ -22,11 +23,11 @@ export function forEachTscScenarioWithNoCheck(buildType: "-b" | "-p") {
};
const noCheckFixError: TestTscEdit = {
caption: "Fix `a` error with noCheck",
edit: fs => fs.writeFileSync("/src/a.ts", `export const a = "hello";`),
edit: sys => sys.writeFile("/home/src/workspaces/project/a.ts", `export const a = "hello";`),
};
const noCheckError: TestTscEdit = {
caption: "Introduce error with noCheck",
edit: fs => fs.writeFileSync("/src/a.ts", aText),
edit: sys => sys.writeFile("/home/src/workspaces/project/a.ts", aText),
};
const noChangeRunWithCheckPendingDiscrepancy: TestTscEdit = {
...noChangeRun,
@@ -41,18 +42,18 @@ export function forEachTscScenarioWithNoCheck(buildType: "-b" | "-p") {
verifyTsc({
scenario: "noCheck",
subScenario: `${options.outFile ? "outFile" : "multiFile"}/${subScenario}${incremental ? " with incremental" : ""}`,
fs: () =>
loadProjectFromFiles({
"/src/a.ts": aText,
"/src/b.ts": `export const b = 10;`,
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a.ts": aText,
"/home/src/workspaces/project/b.ts": `export const b = 10;`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
declaration: true,
incremental,
...options,
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: [...commandLineArgs, "--noCheck"],
edits: [
noChangeRun, // Should be no op
@@ -70,7 +71,7 @@ export function forEachTscScenarioWithNoCheck(buildType: "-b" | "-p") {
checkNoChangeRun, // Should check errors and update buildInfo
{
caption: "Add file with error",
edit: fs => fs.writeFileSync("/src/c.ts", `export const c: number = "hello";`),
edit: sys => sys.writeFile("/home/src/workspaces/project/c.ts", `export const c: number = "hello";`),
commandLineArgs,
},
noCheckError,
+76 -82
View File
@@ -6,25 +6,14 @@ import {
} from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
compilerOptionsToConfigJson,
FsContents,
libContent,
} from "./contents.js";
import { compilerOptionsToConfigJson } from "./contents.js";
import {
noChangeRun,
TestTscEdit,
verifyTsc,
} from "./tsc.js";
import { verifyTscWatch } from "./tscWatch.js";
import {
loadProjectFromFiles,
replaceText,
} from "./vfs.js";
import {
createWatchedSystem,
libFile,
} from "./virtualFileSystemWithWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: CompilerOptions) {
const discrepancyExplanation = () => [
@@ -34,7 +23,7 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
const noChangeRunWithNoEmit: TestTscEdit = {
...noChangeRun,
caption: "No Change run with noEmit",
commandLineArgs: [...commandType, "src/project", "--noEmit"],
commandLineArgs: [...commandType, ".", "--noEmit"],
discrepancyExplanation: compilerOptions.composite ?
discrepancyExplanation :
undefined,
@@ -42,7 +31,7 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
const noChangeRunWithEmit: TestTscEdit = {
...noChangeRun,
caption: "No Change run with emit",
commandLineArgs: [...commandType, "src/project"],
commandLineArgs: [...commandType, "."],
};
let optionsString = "";
for (const key in compilerOptions) {
@@ -57,22 +46,22 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
verifyTsc({
scenario: "noEmit",
subScenario: scenarioName("changes"),
commandLineArgs: [...commandType, "src/project"],
fs,
commandLineArgs: [...commandType, "."],
sys,
edits: [
noChangeRunWithNoEmit,
noChangeRunWithNoEmit,
{
caption: "Introduce error but still noEmit",
commandLineArgs: [...commandType, "src/project", "--noEmit"],
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop", "prop1"),
commandLineArgs: [...commandType, ".", "--noEmit"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop", "prop1"),
discrepancyExplanation: compilerOptions.composite ?
discrepancyExplanation :
undefined,
},
{
caption: "Fix error and emit",
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop1", "prop"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop1", "prop"),
},
noChangeRunWithEmit,
noChangeRunWithNoEmit,
@@ -80,7 +69,7 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
noChangeRunWithEmit,
{
caption: "Introduce error and emit",
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop", "prop1"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop", "prop1"),
},
noChangeRunWithEmit,
noChangeRunWithNoEmit,
@@ -88,8 +77,8 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
noChangeRunWithEmit,
{
caption: "Fix error and no emit",
commandLineArgs: [...commandType, "src/project", "--noEmit"],
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop1", "prop"),
commandLineArgs: [...commandType, ".", "--noEmit"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop1", "prop"),
discrepancyExplanation: compilerOptions.composite ?
discrepancyExplanation :
undefined,
@@ -104,18 +93,18 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
verifyTsc({
scenario: "noEmit",
subScenario: scenarioName("changes with initial noEmit"),
commandLineArgs: [...commandType, "src/project", "--noEmit"],
fs,
commandLineArgs: [...commandType, ".", "--noEmit"],
sys,
edits: [
noChangeRunWithEmit,
{
caption: "Introduce error with emit",
commandLineArgs: [...commandType, "src/project"],
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop", "prop1"),
commandLineArgs: [...commandType, "."],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop", "prop1"),
},
{
caption: "Fix error and no emit",
edit: fs => replaceText(fs, "/src/project/src/class.ts", "prop1", "prop"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/class.ts", "prop1", "prop"),
discrepancyExplanation: compilerOptions.composite ?
discrepancyExplanation :
undefined,
@@ -124,33 +113,33 @@ function forEachNoEmitChangesWorker(commandType: string[], compilerOptions: Comp
],
});
function fs() {
return loadProjectFromFiles({
"/src/project/src/class.ts": dedent`
function sys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/class.ts": dedent`
export class classC {
prop = 1;
}`,
"/src/project/src/indirectClass.ts": dedent`
"/home/src/workspaces/project/src/indirectClass.ts": dedent`
import { classC } from './class';
export class indirectClass {
classC = new classC();
}`,
"/src/project/src/directUse.ts": dedent`
"/home/src/workspaces/project/src/directUse.ts": dedent`
import { indirectClass } from './indirectClass';
new indirectClass().classC.prop;`,
"/src/project/src/indirectUse.ts": dedent`
"/home/src/workspaces/project/src/indirectUse.ts": dedent`
import { indirectClass } from './indirectClass';
new indirectClass().classC.prop;`,
"/src/project/src/noChangeFile.ts": dedent`
"/home/src/workspaces/project/src/noChangeFile.ts": dedent`
export function writeLog(s: string) {
}`,
"/src/project/src/noChangeFileWithEmitSpecificError.ts": dedent`
"/home/src/workspaces/project/src/noChangeFileWithEmitSpecificError.ts": dedent`
function someFunc(arguments: boolean, ...rest: any[]) {
}`,
"/src/project/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(compilerOptions),
}),
});
}, { currentDirectory: "/home/src/workspaces/project" });
}
}
@@ -176,12 +165,12 @@ function editsForDtsChanges(
{
caption: "With declaration enabled noEmit - Should report errors",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit", "--declaration"],
commandLineArgs: [...commandType, ".", "--noEmit", "--declaration"],
},
{
caption: "With declaration and declarationMap noEmit - Should report errors",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit", "--declaration", "--declarationMap"],
commandLineArgs: [...commandType, ".", "--noEmit", "--declaration", "--declarationMap"],
},
incremental ? {
...noChangeRun,
@@ -193,21 +182,21 @@ function editsForDtsChanges(
{
caption: "Dts Emit with error",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project", "--declaration"],
commandLineArgs: [...commandType, ".", "--declaration"],
},
{
caption: "Fix the error",
edit: fs => fs.writeFileSync("/home/src/projects/project/a.ts", aContent.replace("private", "public")),
edit: sys => sys.writeFile("/home/src/projects/project/a.ts", aContent.replace("private", "public")),
},
{
caption: "With declaration enabled noEmit",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit", "--declaration"],
commandLineArgs: [...commandType, ".", "--noEmit", "--declaration"],
},
{
caption: "With declaration and declarationMap noEmit",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit", "--declaration", "--declarationMap"],
commandLineArgs: [...commandType, ".", "--noEmit", "--declaration", "--declarationMap"],
// Multi file still needs to report error so will emit build info (for pending dtsMap)
discrepancyExplanation: incremental && !multiFile ? () => [
"Clean build will have declaration and declarationMap",
@@ -228,16 +217,16 @@ export function forEachNoEmitDtsChanges(commandType: string[]) {
verifyTsc({
scenario: "noEmit",
subScenario: `${options.outFile ? "outFile" : "multiFile"}/dts errors with declaration enable changes${incremental ? " with incremental" : ""}${asModules ? " as modules" : ""}`,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit"],
fs: () =>
loadProjectFromFiles({
commandLineArgs: [...commandType, ".", "--noEmit"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/projects/project/a.ts": aContent,
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: { ...options, incremental },
}),
}),
modifyFs: asModules ?
fs => fs.writeFileSync("/home/src/projects/project/b.ts", `export const b = 10;`) :
}, { currentDirectory: "/home/src/projects/project" }),
modifySystem: asModules ?
sys => sys.writeFile("/home/src/projects/project/b.ts", `export const b = 10;`) :
undefined,
edits: editsForDtsChanges(commandType, aContent, incremental, /*multiFile*/ false),
baselinePrograms: true,
@@ -253,9 +242,9 @@ export function forEachNoEmitDtsChanges(commandType: string[]) {
verifyTsc({
scenario: "noEmit",
subScenario: `${options.outFile ? "outFile" : "multiFile"}/dts errors with declaration enable changes with multiple files`,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit"],
fs: () =>
loadProjectFromFiles({
commandLineArgs: [...commandType, ".", "--noEmit"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/projects/project/a.ts": aContent,
"/home/src/projects/project/b.ts": `export const b = 10;`,
"/home/src/projects/project/c.ts": aContent.replace("a", "c"),
@@ -263,13 +252,13 @@ export function forEachNoEmitDtsChanges(commandType: string[]) {
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: { ...options, incremental: true },
}),
}),
}, { currentDirectory: "/home/src/projects/project" }),
edits: [
...editsForDtsChanges(commandType, aContent, /*incremental*/ true, /*multiFile*/ true),
{
caption: "Fix the another ",
edit: fs => fs.writeFileSync("/home/src/projects/project/c.ts", aContent.replace("a", "c").replace("private", "public")),
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit", "--declaration", "--declarationMap"],
edit: sys => sys.writeFile("/home/src/projects/project/c.ts", aContent.replace("a", "c").replace("private", "public")),
commandLineArgs: [...commandType, ".", "--noEmit", "--declaration", "--declarationMap"],
},
],
baselinePrograms: true,
@@ -284,7 +273,7 @@ function forEachNoEmitAndErrorsWorker(
additionalOptions: CompilerOptions | undefined,
action: (
subScenario: string,
fsContents: FsContents,
sys: () => TestServerHost,
aTsContent: string,
fixedATsContent: string,
compilerOptions: CompilerOptions,
@@ -300,16 +289,18 @@ function forEachNoEmitAndErrorsWorker(
incremental,
...additionalOptions,
};
const fsContents: FsContents = {
"/home/src/projects/project/a.ts": aContent,
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(compilerOptions),
}),
};
if (asModules) fsContents["/home/src/projects/project/b.ts"] = `export const b = 10;`;
action(
`${options.outFile ? "outFile" : "multiFile"}/${subScenario}${incremental ? " with incremental" : ""}${asModules ? " as modules" : ""}`,
fsContents,
() => {
const sys = TestServerHost.createWatchedSystem({
"/home/src/projects/project/a.ts": aContent,
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(compilerOptions),
}),
}, { currentDirectory: "/home/src/projects/project" });
if (asModules) sys.writeFile("/home/src/projects/project/b.ts", `export const b = 10;`);
return sys;
},
aContent,
`${asModules ? "export " : ""}const a = "hello";`,
compilerOptions,
@@ -322,7 +313,7 @@ function forEachNoEmitAndErrorsWorker(
function forEachNoEmitAndErrors(
action: (
subScenario: string,
fsContents: FsContents,
sys: () => TestServerHost,
aTsContent: string,
fixedATsContent: string,
compilerOptions: CompilerOptions,
@@ -351,28 +342,28 @@ function forEachNoEmitAndErrors(
}
export function forEachNoEmitTsc(commandType: string[]) {
forEachNoEmitAndErrors((subScenario, fsContents, aTsContent, fixedATsContent, compilerOptions) =>
forEachNoEmitAndErrors((subScenario, sys, aTsContent, fixedATsContent, compilerOptions) =>
verifyTsc({
scenario: "noEmit",
subScenario,
commandLineArgs: [...commandType, "/home/src/projects/project", "--noEmit"],
fs: () => loadProjectFromFiles(fsContents),
commandLineArgs: [...commandType, ".", "--noEmit"],
sys,
edits: [
noChangeRun,
{
caption: "Fix error",
edit: fs => fs.writeFileSync("/home/src/projects/project/a.ts", fixedATsContent),
edit: sys => sys.writeFile("/home/src/projects/project/a.ts", fixedATsContent),
},
noChangeRun,
{
caption: "Emit after fixing error",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project"],
commandLineArgs: [...commandType, "."],
},
noChangeRun,
{
caption: "Introduce error",
edit: fs => fs.writeFileSync("/home/src/projects/project/a.ts", aTsContent),
edit: sys => sys.writeFile("/home/src/projects/project/a.ts", aTsContent),
discrepancyExplanation: compilerOptions.incremental && subScenario.indexOf("multiFile/syntax") !== -1 ? () => [
"DtsSignature of ts files: Incremental build have dts signature for ts files from emit so its not d.ts or same as file version",
] : undefined,
@@ -380,7 +371,7 @@ export function forEachNoEmitTsc(commandType: string[]) {
{
caption: "Emit when error",
edit: noop,
commandLineArgs: [...commandType, "/home/src/projects/project"],
commandLineArgs: [...commandType, "."],
},
compilerOptions.incremental && subScenario.indexOf("multiFile/syntax") !== -1 ? {
...noChangeRun,
@@ -395,20 +386,23 @@ export function forEachNoEmitTsc(commandType: string[]) {
}
export function forEachNoEmitTscWatch(commandType: string[]) {
forEachNoEmitAndErrors((subScenario, fsContents, aTsContent, fixedATsContent, compilerOptions) =>
forEachNoEmitAndErrors((subScenario, sys, aTsContent, fixedATsContent, compilerOptions) =>
verifyTscWatch({
scenario: "noEmit",
subScenario,
commandLineArgs: [...commandType, "/home/src/projects/project", "-w"],
commandLineArgs: [...commandType, "-w"],
sys: () => {
fsContents["/home/src/projects/project/tsconfig.json"] = jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson({
...compilerOptions,
noEmit: true,
const result = sys();
result.writeFile(
"/home/src/projects/project/tsconfig.json",
jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson({
...compilerOptions,
noEmit: true,
}),
}),
});
fsContents[libFile.path] = libContent;
return createWatchedSystem(fsContents);
);
return result;
},
edits: [
{
@@ -1,9 +1,5 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
libContent,
} from "./contents.js";
import {
noChangeRun,
verifyTsc,
@@ -12,19 +8,15 @@ import {
TscWatchCompileChange,
verifyTscWatch,
} from "./tscWatch.js";
import { loadProjectFromFiles } from "./vfs.js";
import {
createWatchedSystem,
libFile,
} from "./virtualFileSystemWithWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
function getFsContentsForNoEmitOnError(
function getSysForNoEmitOnError(
mainErrorContent: string,
outFile: boolean,
declaration: true | undefined,
incremental: true | undefined,
): FsContents {
return {
) {
return TestServerHost.createWatchedSystem({
"/user/username/projects/noEmitOnError/tsconfig.json": jsonToReadableText({
compilerOptions: {
...outFile ? { outFile: "../dev-build.js", module: "amd" } : { outDir: "./dev-build" },
@@ -43,15 +35,14 @@ function getFsContentsForNoEmitOnError(
console.log("hi");
export { }
`,
[libFile.path]: libContent,
};
}, { currentDirectory: "/user/username/projects/noEmitOnError" });
}
function forEachNoEmitOnErrorScenario(
subScenario: string,
action: (
subScenario: string,
fsContents: (mainErrorContent: string) => FsContents,
fsContents: (mainErrorContent: string) => TestServerHost,
) => void,
) {
for (const outFile of [false, true]) {
@@ -60,7 +51,7 @@ function forEachNoEmitOnErrorScenario(
action(
`${outFile ? "outFile" : "multiFile"}/${subScenario}${declaration ? " with declaration" : ""}${incremental ? " with incremental" : ""}`,
mainErrorContent =>
getFsContentsForNoEmitOnError(
getSysForNoEmitOnError(
mainErrorContent,
outFile,
declaration,
@@ -115,25 +106,18 @@ export function forEachNoEmitOnErrorScenarioTsc(commandLineArgs: string[]) {
getNoEmitOnErrorErrorsType().forEach(([subScenario, mainErrorContent, fixedErrorContent]) =>
forEachNoEmitOnErrorScenario(
subScenario,
(subScenario, fsContents) => {
(subScenario, sys) => {
describe(subScenario, () => {
verifyTsc({
scenario: "noEmitOnError",
subScenario,
fs: () =>
loadProjectFromFiles(
fsContents(mainErrorContent),
{
cwd: "/user/username/projects/noEmitOnError",
executingFilePath: libFile.path,
},
),
sys: () => sys(mainErrorContent),
commandLineArgs,
edits: [
noChangeRun,
{
caption: "Fix error",
edit: fs => fs.writeFileSync("src/main.ts", fixedErrorContent, "utf-8"),
edit: sys => sys.writeFile("src/main.ts", fixedErrorContent),
},
noChangeRun,
],
@@ -149,16 +133,12 @@ export function forEachNoEmitOnErrorScenarioTscWatch(commandLineArgs: string[])
const errorTypes = getNoEmitOnErrorErrorsType();
forEachNoEmitOnErrorScenario(
"noEmitOnError",
(subScenario, fsContents) =>
(subScenario, sys) =>
verifyTscWatch({
scenario: "noEmitOnError",
subScenario,
commandLineArgs: [...commandLineArgs, "--w"],
sys: () =>
createWatchedSystem(
fsContents(errorTypes[0][1]),
{ currentDirectory: "/user/username/projects/noEmitOnError" },
),
sys: () => sys(errorTypes[0][1]),
edits: getEdits(errorTypes),
}),
);
@@ -1,14 +1,14 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
libContent,
} from "./contents.js";
import { libFile } from "./virtualFileSystemWithWatch.js";
noopChange,
TscWatchCompileChange,
} from "./tscWatch.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
function getFsContentsForRootsFromReferencedProject(serverFirst: boolean): FsContents {
return {
"/home/src/workspaces/tsconfig.json": jsonToReadableText({
function getSysForRootsFromReferencedProject(forTsserver: boolean, serverFirst: boolean) {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
@@ -17,29 +17,29 @@ function getFsContentsForRootsFromReferencedProject(serverFirst: boolean): FsCon
{ path: "projects/shared" },
],
}),
"/home/src/workspaces/projects/shared/src/myClass.ts": `export class MyClass { }`,
"/home/src/workspaces/projects/shared/src/logging.ts": dedent`
"/home/src/workspaces/solution/projects/shared/src/myClass.ts": `export class MyClass { }`,
"/home/src/workspaces/solution/projects/shared/src/logging.ts": dedent`
export function log(str: string) {
console.log(str);
}
`,
"/home/src/workspaces/projects/shared/src/random.ts": dedent`
"/home/src/workspaces/solution/projects/shared/src/random.ts": dedent`
export function randomFn(str: string) {
console.log(str);
}
`,
"/home/src/workspaces/projects/shared/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/projects/shared/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.json",
compilerOptions: {
outDir: "./dist",
},
include: ["src/**/*.ts"],
}),
"/home/src/workspaces/projects/server/src/server.ts": dedent`
"/home/src/workspaces/solution/projects/server/src/server.ts": dedent`
import { MyClass } from ':shared/myClass.js';
console.log('Hello, world!');
`,
"/home/src/workspaces/projects/server/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/projects/server/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.json",
compilerOptions: {
baseUrl: "./src",
@@ -56,11 +56,42 @@ function getFsContentsForRootsFromReferencedProject(serverFirst: boolean): FsCon
{ path: "../shared" },
],
}),
[libFile.path]: libContent,
};
}, { currentDirectory: "/home/src/workspaces/solution" });
}
export function forEachScenarioForRootsFromReferencedProject(action: (subScenario: string, getFsContents: () => FsContents) => void) {
action("when root file is from referenced project", () => getFsContentsForRootsFromReferencedProject(/*serverFirst*/ true));
action("when root file is from referenced project and shared is first", () => getFsContentsForRootsFromReferencedProject(/*serverFirst*/ false));
export function forEachScenarioForRootsFromReferencedProject(
forTsserver: boolean,
action: (
subScenario: string,
sys: () => TestServerHost,
edits: () => readonly TscWatchCompileChange[],
) => void,
) {
[true, false].forEach(serverFirst =>
action(
`when root file is from referenced project${!serverFirst ? " and shared is first" : ""}`,
() => getSysForRootsFromReferencedProject(forTsserver, serverFirst),
() => [
noopChange,
{
caption: "edit logging file",
edit: sys => sys.appendFile("/home/src/workspaces/solution/projects/shared/src/logging.ts", "export const x = 10;"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks(); // build shared
sys.runQueuedTimeoutCallbacks(); // build server
},
},
noopChange,
{
caption: "delete random file",
edit: sys => sys.deleteFile("/home/src/workspaces/solution/projects/shared/src/random.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks(); // build shared
sys.runQueuedTimeoutCallbacks(); // build server
},
},
noopChange,
],
)
);
}
@@ -1,15 +1,8 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
getProjectConfigWithNodeNext,
} from "./contents.js";
import { loadProjectFromFiles } from "./vfs.js";
import {
createServerHost,
createWatchedSystem,
libFile,
} from "./virtualFileSystemWithWatch.js";
import { getProjectConfigWithNodeNext } from "./contents.js";
import { solutionBuildWithBaseline } from "./solutionBuilder.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsContentsForSampleProjectReferencesLogicConfig(withNodeNext?: boolean) {
return jsonToReadableText({
@@ -26,9 +19,13 @@ export function getFsContentsForSampleProjectReferencesLogicConfig(withNodeNext?
],
});
}
export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean): FsContents {
return {
[libFile.path]: libFile.content,
export function getSysForSampleProjectReferences(
withNodeNext?: boolean,
skipReferenceCoreFromTest?: boolean,
forTsserver?: boolean,
) {
return TestServerHost.getCreateWatchedSystem(forTsserver)({
"/user/username/projects/sample1/core/tsconfig.json": jsonToReadableText({
compilerOptions: {
...getProjectConfigWithNodeNext(withNodeNext),
@@ -82,33 +79,12 @@ export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean,
import * as mod from '../core/anotherModule';
export const m = mod;
`,
};
}, { currentDirectory: "/user/username/projects/sample1" });
}
export function getFsForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean) {
return loadProjectFromFiles(
getFsContentsForSampleProjectReferences(withNodeNext, skipReferenceCoreFromTest),
{
cwd: "/user/username/projects/sample1",
executingFilePath: libFile.path,
},
);
}
export function getSysForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean) {
return createWatchedSystem(
getFsContentsForSampleProjectReferences(withNodeNext, skipReferenceCoreFromTest),
{
currentDirectory: "/user/username/projects/sample1",
},
);
}
export function getServerHostForSampleProjectReferences() {
return createServerHost(
getFsContentsForSampleProjectReferences(),
{
currentDirectory: "/user/username/projects/sample1",
},
export function getSysForSampleProjectReferencesBuilt(withNodeNext?: boolean) {
return solutionBuildWithBaseline(
getSysForSampleProjectReferences(withNodeNext),
["tests"],
);
}
@@ -1,31 +1,48 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { commandLineCallbacks } from "./baseline.js";
import {
makeSystemReadyForBaseline,
TscCompileSystem,
CommandLineCallbacks,
commandLineCallbacks,
patchHostForBuildInfoReadWrite,
patchHostForBuildInfoWrite,
TscWatchSystem,
} from "./baseline.js";
import {
verifyTsc,
VerifyTscWithEditsInput,
} from "./tsc.js";
import {
changeToHostTrackingWrittenFiles,
TestServerHost,
} from "./virtualFileSystemWithWatch.js";
export type SolutionBuilderHostWithGetPrograms =
& ts.SolutionBuilderHost<ts.EmitAndSemanticDiagnosticsBuilderProgram>
& { getPrograms: CommandLineCallbacks["getPrograms"]; };
export function createSolutionBuilderHostForBaseline(
sys: TscCompileSystem | TestServerHost,
versionToWrite?: string,
originalRead?: (TscCompileSystem | TestServerHost)["readFile"],
) {
if (sys instanceof fakes.System) makeSystemReadyForBaseline(sys, versionToWrite);
const { cb } = commandLineCallbacks(sys, originalRead);
const host = ts.createSolutionBuilderHost(sys, /*createProgram*/ undefined, ts.createDiagnosticReporter(sys, /*pretty*/ true), ts.createBuilderStatusReporter(sys, /*pretty*/ true));
sys: TscWatchSystem,
originalRead?: TestServerHost["readFile"],
): ts.SolutionBuilderHost<ts.EmitAndSemanticDiagnosticsBuilderProgram> & { getPrograms: CommandLineCallbacks["getPrograms"]; } {
const { cb, getPrograms } = commandLineCallbacks(sys, originalRead);
const host = ts.createSolutionBuilderHost(
sys,
/*createProgram*/ undefined,
ts.createDiagnosticReporter(sys, /*pretty*/ true),
ts.createBuilderStatusReporter(sys, /*pretty*/ true),
) as SolutionBuilderHostWithGetPrograms;
host.afterProgramEmitAndDiagnostics = cb;
host.getPrograms = getPrograms;
return host;
}
export function createSolutionBuilder(system: TestServerHost, rootNames: readonly string[], originalRead?: TestServerHost["readFile"]) {
const host = createSolutionBuilderHostForBaseline(system, /*versionToWrite*/ undefined, originalRead);
return ts.createSolutionBuilder(host, rootNames, {});
export function createSolutionBuilder(
system: TscWatchSystem,
rootNames: readonly string[],
buildOptions?: ts.BuildOptions,
originalRead?: TestServerHost["readFile"],
) {
const host = createSolutionBuilderHostForBaseline(system, originalRead);
return ts.createSolutionBuilder(host, rootNames, buildOptions ?? {});
}
export function ensureErrorFreeBuild(host: TestServerHost, rootNames: readonly string[]) {
@@ -34,16 +51,25 @@ export function ensureErrorFreeBuild(host: TestServerHost, rootNames: readonly s
assert.equal(host.getOutput().length, 0, jsonToReadableText(host.getOutput()));
}
export function solutionBuildWithBaseline(sys: TestServerHost, solutionRoots: readonly string[], originalRead?: TestServerHost["readFile"]) {
export function solutionBuildWithBaseline(
sys: TestServerHost,
solutionRoots: readonly string[],
buildOptions?: ts.BuildOptions,
versionToWrite?: string,
originalRead?: TestServerHost["readFile"],
) {
if (sys.writtenFiles === undefined) {
const originalReadFile = sys.readFile;
const originalWrite = sys.write;
const originalWriteFile = sys.writeFile;
const solutionBuilder = createSolutionBuilder(
changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(sys),
versionToWrite ?
patchHostForBuildInfoWrite(sys, versionToWrite) :
patchHostForBuildInfoReadWrite(sys),
),
solutionRoots,
buildOptions,
originalRead,
);
solutionBuilder.build();
@@ -51,11 +77,41 @@ export function solutionBuildWithBaseline(sys: TestServerHost, solutionRoots: re
sys.write = originalWrite;
sys.writeFile = originalWriteFile;
sys.writtenFiles = undefined;
sys.clearOutput();
return sys;
}
else {
const solutionBuilder = createSolutionBuilder(sys, solutionRoots);
const solutionBuilder = createSolutionBuilder(sys as TscWatchSystem, solutionRoots, buildOptions, originalRead);
solutionBuilder.build();
return sys;
}
}
export function verifySolutionBuilderWithDifferentTsVersion(input: VerifyTscWithEditsInput, rootNames: readonly string[]) {
describe(input.subScenario, () => {
let originalReadFile: TscWatchSystem["readFile"];
let originalWriteFile: TscWatchSystem["writeFile"];
verifyTsc({
...input,
sys: () => {
const sys = input.sys();
originalReadFile = sys.readFile;
originalWriteFile = sys.writeFile;
return sys;
},
compile: sys => {
sys.readFile = originalReadFile;
sys.writeFile = originalWriteFile;
originalReadFile = undefined!;
originalWriteFile = undefined!;
sys.writtenFiles = undefined!;
// Buildinfo will have version which does not match with current ts version
changeToHostTrackingWrittenFiles(patchHostForBuildInfoWrite(sys, "FakeTSCurrentVersion"));
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, rootNames, { verbose: true });
sys.exit(builder.build());
return buildHost.getPrograms;
},
});
});
}
@@ -1,11 +1,7 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
FsContents,
getProjectConfigWithNodeNext,
libContent,
} from "./contents.js";
import { libFile } from "./virtualFileSystemWithWatch.js";
import { getProjectConfigWithNodeNext } from "./contents.js";
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export function getFsContentsForTransitiveReferencesRefsAdts() {
return dedent`
@@ -39,8 +35,8 @@ export function getFsContentsForTransitiveReferencesAConfig(withNodeNext: boolea
});
}
export function getFsContentsForTransitiveReferences(withNodeNext?: boolean): FsContents {
return {
export function getSysForTransitiveReferences(withNodeNext?: boolean) {
return TestServerHost.createWatchedSystem({
"/user/username/projects/transitiveReferences/refs/a.d.ts": getFsContentsForTransitiveReferencesRefsAdts(),
"/user/username/projects/transitiveReferences/a.ts": dedent`
export class A {}
@@ -68,6 +64,5 @@ export function getFsContentsForTransitiveReferences(withNodeNext?: boolean): Fs
},
references: [{ path: "tsconfig.b.json" }],
}),
[libFile.path]: libContent,
};
}, { currentDirectory: "/user/username/projects/transitiveReferences" });
}
+132 -275
View File
@@ -1,33 +1,32 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as Harness from "../../_namespaces/Harness.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import {
baselinePrograms,
applyEdit,
baselineAfterTscCompile as baseline_baselineAfterTscCompile,
BaselineBase,
CommandLineCallbacks,
commandLineCallbacks,
CommandLineProgram,
generateSourceMapBaselineFiles,
createBaseline,
isReadableIncrementalBuildInfo,
isReadableIncrementalBundleEmitBuildInfo,
isReadableIncrementalMultiFileEmitBuildInfo,
isWatch,
ReadableBuildInfo,
ReadableIncrementalBuildInfo,
ReadableIncrementalBuildInfoFileInfo,
ReadableIncrementalBundleEmitBuildInfo,
ReadableIncrementalMultiFileEmitBuildInfo,
sanitizeSysOutput,
toPathWithSystem,
tscBaselineName,
TscWatchSystem,
} from "./baseline.js";
export type TscCompileSystem = fakes.System & {
writtenFiles: Set<ts.Path>;
baseLine(): { file: string; text: string; };
dtsSignaures?: Map<ts.Path, Map<string, string>>;
storeSignatureInfo?: boolean;
};
import {
isFsFile,
TestServerHost,
TestServerHostSnapshot,
} from "./virtualFileSystemWithWatch.js";
export const noChangeRun: TestTscEdit = {
caption: "no-change-run",
@@ -35,106 +34,40 @@ export const noChangeRun: TestTscEdit = {
};
export const noChangeOnlyRuns = [noChangeRun];
export interface TestTscCompile extends TestTscCompileLikeBase {
export interface TestTscCompile {
commandLineArgs: readonly string[];
sys: () => TestServerHost;
modifySystem?: (fs: TestServerHost) => void;
computeDtsSignatures?: boolean;
getWrittenFiles?: boolean;
baselineSourceMap?: boolean;
baselineReadFileCalls?: boolean;
baselinePrograms?: boolean;
baselineDependencies?: boolean;
}
export interface TestTscCompileLikeBase extends VerifyTscCompileLike {
diffWithInitial?: boolean;
modifyFs?: (fs: vfs.FileSystem) => void;
computeDtsSignatures?: boolean;
environmentVariables?: Record<string, string>;
}
export interface TestTscCompileLike extends TestTscCompileLikeBase {
compile: (sys: TscCompileSystem) => void;
additionalBaseline?: (sys: TscCompileSystem) => void;
}
/**
* Initialize FS, run compile function and save baseline
*/
export function testTscCompileLike(input: TestTscCompileLike) {
const initialFs = input.fs();
const inputFs = initialFs.shadow();
const {
scenario,
subScenario,
diffWithInitial,
commandLineArgs,
modifyFs,
environmentVariables,
compile: worker,
additionalBaseline,
} = input;
if (modifyFs) modifyFs(inputFs);
inputFs.makeReadonly();
const fs = inputFs.shadow();
// Create system
const sys = new fakes.System(fs, { executingFilePath: `${fs.meta.get("defaultLibLocation")}/tsc`, env: environmentVariables }) as TscCompileSystem;
sys.storeSignatureInfo = true;
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
sys.exit = exitCode => sys.exitCode = exitCode;
worker(sys);
sys.write(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}\n`);
additionalBaseline?.(sys);
fs.makeReadonly();
sys.baseLine = () => {
const baseFsPatch = diffWithInitial ?
inputFs.diff(initialFs, { includeChangedFileWithSameContent: true }) :
inputFs.diff(/*base*/ undefined, { baseIsNotShadowRoot: true });
const patch = fs.diff(inputFs, { includeChangedFileWithSameContent: true });
return {
file: tscBaselineName(scenario, subScenario, commandLineArgs),
text: `Input::
${baseFsPatch ? vfs.formatPatch(baseFsPatch) : ""}
Output::
${sys.output.map(sanitizeSysOutput).join("")}
${patch ? vfs.formatPatch(patch) : ""}`,
};
};
return sys;
}
export function makeSystemReadyForBaseline(sys: TscCompileSystem, versionToWrite?: string) {
if (versionToWrite) {
fakes.patchHostForBuildInfoWrite(sys, versionToWrite);
}
else {
fakes.patchHostForBuildInfoReadWrite(sys);
}
const writtenFiles = sys.writtenFiles = new Set();
const originalWriteFile = sys.writeFile;
sys.writeFile = (fileName, content, writeByteOrderMark) => {
const path = toPathWithSystem(sys, fileName);
// When buildinfo is same for two projects,
// it gives error and doesnt write buildinfo but because buildInfo is written for one project,
// readable baseline will be written two times for those two projects with same contents and is ok
ts.Debug.assert(!writtenFiles.has(path) || ts.endsWith(path, "baseline.txt"));
writtenFiles.add(path);
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
};
compile?: (sys: TscWatchSystem) => CommandLineCallbacks["getPrograms"];
}
/**
* Initialize Fs, execute command line and save baseline
*/
export function testTscCompile(input: TestTscCompile) {
function initialTestTscCompile(input: TestTscCompile) {
const { sys, baseline } = createBaseline(input.sys(), input.modifySystem);
sys.exit = exitCode => sys.exitCode = exitCode;
ts.Debug.assert(!isWatch(input.commandLineArgs), "use verifyTscWatch");
return testTscCompileWith(sys, baseline, input);
}
/**
* execute command line and save baseline
*/
function testTscCompileWith(
sys: BaselineBase["sys"],
baseline: BaselineBase["baseline"],
input: TestTscCompileBaselineInput & Pick<TestTscCompile, "compile">,
) {
let actualReadFileMap: ts.MapLike<number> | undefined;
let getPrograms: CommandLineCallbacks["getPrograms"] | undefined;
return testTscCompileLike({
...input,
compile: commandLineCompile,
additionalBaseline,
});
function commandLineCompile(sys: TscCompileSystem) {
makeSystemReadyForBaseline(sys);
if (!input.compile) {
actualReadFileMap = {};
const originalReadFile = sys.readFile;
if (input.baselineReadFileCalls) {
@@ -146,7 +79,6 @@ export function testTscCompile(input: TestTscCompile) {
return originalReadFile.call(sys, path);
};
}
const result = commandLineCallbacks(sys, originalReadFile);
ts.executeCommandLine(
sys,
@@ -156,33 +88,57 @@ export function testTscCompile(input: TestTscCompile) {
sys.readFile = originalReadFile;
getPrograms = result.getPrograms;
}
function additionalBaseline(sys: TscCompileSystem) {
const { baselineSourceMap, baselineReadFileCalls, baselinePrograms: shouldBaselinePrograms, baselineDependencies } = input;
const programs = getPrograms!();
if (input.computeDtsSignatures) storeDtsSignatures(sys, programs);
if (shouldBaselinePrograms) {
const baseline: string[] = [];
baselinePrograms(baseline, programs, ts.emptyArray, baselineDependencies);
sys.write(baseline.join("\n"));
}
if (baselineReadFileCalls) {
sys.write(`readFiles:: ${jsonToReadableText(actualReadFileMap)} `);
}
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
actualReadFileMap = undefined;
getPrograms = undefined;
else {
getPrograms = input.compile(sys);
}
const { dtsSignatures, writtenFiles } = baselineAfterTscCompile(sys, baseline, input, getPrograms, actualReadFileMap);
return { sys, baseline, dtsSignatures, writtenFiles };
}
type TestTscCompileBaselineInput = Pick<
TestTscCompile,
| "commandLineArgs"
| "computeDtsSignatures"
| "getWrittenFiles"
| "baselineSourceMap"
| "baselineReadFileCalls"
| "baselinePrograms"
| "baselineDependencies"
>;
function baselineAfterTscCompile(
sys: BaselineBase["sys"],
baseline: BaselineBase["baseline"],
input: TestTscCompileBaselineInput,
getPrograms: () => readonly CommandLineProgram[],
actualReadFileMap: ts.MapLike<number> | undefined,
) {
baseline.push(`${sys.getExecutingFilePath()} ${input.commandLineArgs.join(" ")}`);
const writtenFiles = input.getWrittenFiles ? new Set(sys.writtenFiles.keys()) : undefined;
const programs = baseline_baselineAfterTscCompile(
sys,
baseline,
getPrograms,
ts.emptyArray,
input.baselineSourceMap,
input.baselinePrograms,
input.baselineDependencies,
);
const dtsSignatures = input.computeDtsSignatures ? storeDtsSignatures(sys, programs) : undefined;
if (input.baselineReadFileCalls) {
baseline.push(`readFiles:: ${jsonToReadableText(actualReadFileMap)} `);
}
actualReadFileMap = undefined;
return { dtsSignatures, writtenFiles };
}
function storeDtsSignatures(sys: TscCompileSystem, programs: readonly CommandLineProgram[]) {
function storeDtsSignatures(sys: TscWatchSystem, programs: readonly CommandLineProgram[]) {
let dtsSignatures: Map<ts.Path, Map<string, string>> | undefined;
for (const [program, builderProgram] of programs) {
if (!builderProgram) continue;
const buildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(program.getCompilerOptions());
if (!buildInfoPath) continue;
sys.dtsSignaures ??= new Map();
dtsSignatures ??= new Map();
const dtsSignatureData = new Map<string, string>();
sys.dtsSignaures.set(`${toPathWithSystem(sys, buildInfoPath)}.readable.baseline.txt` as ts.Path, dtsSignatureData);
dtsSignatures.set(`${toPathWithSystem(sys, buildInfoPath)}.readable.baseline.txt` as ts.Path, dtsSignatureData);
builderProgram.state.hasCalledUpdateShapeSignature?.forEach(resolvedPath => {
const file = program.getSourceFileByPath(resolvedPath);
if (!file || file.isDeclarationFile) return;
@@ -203,84 +159,36 @@ function storeDtsSignatures(sys: TscCompileSystem, programs: readonly CommandLin
return ts.ensurePathIsNonModuleName(ts.getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
}
}
return dtsSignatures;
}
export function verifyTscBaseline(sys: () => { baseLine: TscCompileSystem["baseLine"]; }) {
it(`Generates files matching the baseline`, () => {
const { file, text } = sys().baseLine();
Harness.Baseline.runBaseline(file, text);
});
}
export interface VerifyTscCompileLike {
scenario: string;
subScenario: string;
commandLineArgs: readonly string[];
fs: () => vfs.FileSystem;
}
/**
* Verify by baselining after initializing FS and custom compile
*/
export function verifyTscCompileLike<T extends VerifyTscCompileLike>(verifier: (input: T) => { baseLine: TscCompileSystem["baseLine"]; }, input: T) {
describe(`tsc ${input.commandLineArgs.join(" ")} ${input.scenario}:: ${input.subScenario}`, () => {
describe(input.scenario, () => {
describe(input.subScenario, () => {
verifyTscBaseline(() =>
verifier({
...input,
fs: () => input.fs().makeReadonly(),
})
);
});
});
});
}
interface VerifyTscEditDiscrepanciesInput {
index: number;
edits: readonly TestTscEdit[];
scenario: TestTscCompile["scenario"];
baselines: string[] | undefined;
commandLineArgs: TestTscCompile["commandLineArgs"];
modifyFs: TestTscCompile["modifyFs"];
baseFs: vfs.FileSystem;
newSys: TscCompileSystem;
environmentVariables: TestTscCompile["environmentVariables"];
}
function verifyTscEditDiscrepancies({
index,
edits,
scenario,
commandLineArgs,
environmentVariables,
baselines,
modifyFs,
baseFs,
newSys,
}: VerifyTscEditDiscrepanciesInput): string[] | undefined {
const { caption, discrepancyExplanation } = edits[index];
const sys = testTscCompile({
scenario,
subScenario: caption,
fs: () => baseFs.makeReadonly(),
commandLineArgs: edits[index].commandLineArgs || commandLineArgs,
modifyFs: fs => {
if (modifyFs) modifyFs(fs);
function verifyTscEditDiscrepancies(
input: VerifyTscWithEditsInput,
index: number,
snaps: readonly TestServerHostSnapshot[],
baselines: string[] | undefined,
): string[] | undefined {
const result = initialTestTscCompile({
...input,
modifySystem: fs => {
input.modifySystem?.(fs);
for (let i = 0; i <= index; i++) {
edits[i].edit(fs);
input.edits![i].edit(fs as TscWatchSystem);
}
},
environmentVariables,
commandLineArgs: input.edits![index].commandLineArgs ?? input.commandLineArgs,
computeDtsSignatures: true,
getWrittenFiles: true,
});
let headerAdded = false;
for (const outputFile of sys.writtenFiles.keys()) {
const cleanBuildText = sys.readFile(outputFile);
const incrementalBuildText = newSys.readFile(outputFile);
for (const outputFile of result.writtenFiles!.keys()) {
const cleanBuildText = result.sys.readFile(outputFile);
const incrementalFsEntry = snaps[index].get(outputFile);
const incrementalBuildText = isFsFile(incrementalFsEntry) ? incrementalFsEntry.content : undefined;
if (ts.isBuildInfoFile(outputFile)) {
// Check only presence and absence and not text as we will do that for readable baseline
if (!sys.fileExists(`${outputFile}.readable.baseline.txt`)) addBaseline(`Readable baseline not present in clean build:: File:: ${outputFile}`);
if (!newSys.fileExists(`${outputFile}.readable.baseline.txt`)) addBaseline(`Readable baseline not present in incremental build:: File:: ${outputFile}`);
if (!result.sys.fileExists(`${outputFile}.readable.baseline.txt`)) addBaseline(`Readable baseline not present in clean build:: File:: ${outputFile}`);
if (!isFsFile(snaps[index].get(`${outputFile}.readable.baseline.txt` as ts.Path))) addBaseline(`Readable baseline not present in incremental build:: File:: ${outputFile}`);
verifyPresenceAbsence(incrementalBuildText, cleanBuildText, `Incremental and clean tsbuildinfo file presence differs:: File:: ${outputFile}`);
}
else if (!ts.fileExtensionIs(outputFile, ".tsbuildinfo.readable.baseline.txt")) {
@@ -290,14 +198,14 @@ function verifyTscEditDiscrepancies({
// Verify build info without affectedFilesPendingEmit
const { buildInfo: incrementalBuildInfo, readableBuildInfo: incrementalReadableBuildInfo } = getBuildInfoForIncrementalCorrectnessCheck(incrementalBuildText);
const { buildInfo: cleanBuildInfo, readableBuildInfo: cleanReadableBuildInfo } = getBuildInfoForIncrementalCorrectnessCheck(cleanBuildText);
const dtsSignaures = sys.dtsSignaures?.get(outputFile);
const dtsSignatures = result.dtsSignatures?.get(outputFile);
verifyTextEqual(incrementalBuildInfo, cleanBuildInfo, `TsBuild info text without affectedFilesPendingEmit:: ${outputFile}::`);
// Verify file info sigantures
verifyMapLike(
(incrementalReadableBuildInfo as ReadableIncrementalMultiFileEmitBuildInfo)?.fileInfos,
(cleanReadableBuildInfo as ReadableIncrementalMultiFileEmitBuildInfo)?.fileInfos,
(key, incrementalFileInfo, cleanFileInfo) => {
const dtsForKey = dtsSignaures?.get(key);
const dtsForKey = dtsSignatures?.get(key);
if (
!incrementalFileInfo ||
!cleanFileInfo ||
@@ -436,7 +344,7 @@ function verifyTscEditDiscrepancies({
});
}
}
if (!headerAdded && discrepancyExplanation) addBaseline("*** Supplied discrepancy explanation but didnt find any difference");
if (!headerAdded && input.edits![index].discrepancyExplanation) addBaseline("*** Supplied discrepancy explanation but didnt find any difference");
return baselines;
function verifyTextEqual(incrementalText: string | undefined, cleanText: string | undefined, message: string) {
@@ -487,7 +395,7 @@ function verifyTscEditDiscrepancies({
function addBaseline(...text: string[]) {
if (!baselines || !headerAdded) {
(baselines ||= []).push(`${index}:: ${caption}`, ...(discrepancyExplanation?.() || ["*** Needs explanation"]));
(baselines ||= []).push(`${index}:: ${input.edits![index].caption}`, ...(input.edits![index].discrepancyExplanation?.() || ["*** Needs explanation"]));
headerAdded = true;
}
baselines.push(...text);
@@ -537,107 +445,56 @@ function getBuildInfoForIncrementalCorrectnessCheck(text: string | undefined): {
}
export interface TestTscEdit {
edit: (fs: vfs.FileSystem) => void;
edit: (sys: TscWatchSystem) => void;
caption: string;
commandLineArgs?: readonly string[];
/** An array of lines to be printed in order when a discrepancy is detected */
discrepancyExplanation?: () => readonly string[];
}
export interface VerifyTscWithEditsInput extends TestTscCompile {
export interface VerifyTscWithEditsInput extends Omit<TestTscCompile, "computeDtsSignatures" | "getWrittenFiles"> {
scenario: string;
subScenario: string;
edits?: readonly TestTscEdit[];
}
/**
* Verify non watch tsc invokcation after each edit
*/
export function verifyTsc({
subScenario,
fs,
scenario,
commandLineArgs,
environmentVariables,
baselineSourceMap,
modifyFs,
baselineReadFileCalls,
baselinePrograms,
edits,
}: VerifyTscWithEditsInput) {
describe(`tsc ${commandLineArgs.join(" ")} ${scenario}:: ${subScenario}`, () => {
let sys: TscCompileSystem;
let baseFs: vfs.FileSystem;
let editsSys: TscCompileSystem[] | undefined;
before(() => {
baseFs = fs().makeReadonly();
sys = testTscCompile({
scenario,
subScenario,
fs: () => baseFs,
commandLineArgs,
modifyFs,
baselineSourceMap,
baselineReadFileCalls,
baselinePrograms,
environmentVariables,
});
edits?.forEach((
{ edit, caption, commandLineArgs: editCommandLineArgs },
index,
) => {
(editsSys || (editsSys = [])).push(testTscCompile({
scenario,
subScenario: caption,
diffWithInitial: true,
fs: () => index === 0 ? sys.vfs : editsSys![index - 1].vfs,
commandLineArgs: editCommandLineArgs || commandLineArgs,
modifyFs: edit,
baselineSourceMap,
baselineReadFileCalls,
baselinePrograms,
environmentVariables,
}));
});
});
export function verifyTsc(input: VerifyTscWithEditsInput) {
describe(`tsc ${input.commandLineArgs.join(" ")} ${input.scenario}:: ${input.subScenario}`, () => {
let snaps: TestServerHostSnapshot[] | undefined;
after(() => {
baseFs = undefined!;
sys = undefined!;
editsSys = undefined!;
snaps = undefined;
});
verifyTscBaseline(() => ({
baseLine: () => {
const { file, text } = sys.baseLine();
const texts: string[] = [text];
editsSys?.forEach((sys, index) => {
const incrementalScenario = edits![index];
texts.push("");
texts.push(`Change:: ${incrementalScenario.caption}`);
texts.push(sys.baseLine().text);
it("baseline for the tsc compiles", () => {
const { sys, baseline } = initialTestTscCompile(input);
input.edits?.forEach(edit => {
applyEdit(sys, baseline, edit.edit, edit.caption);
testTscCompileWith(sys, baseline, {
...input,
commandLineArgs: edit.commandLineArgs ?? input.commandLineArgs,
});
return {
file,
text: `currentDirectory:: ${sys.getCurrentDirectory()} useCaseSensitiveFileNames: ${sys.useCaseSensitiveFileNames}\r\n` +
texts.join("\r\n"),
};
},
}));
if (edits?.length) {
(snaps ??= []).push(sys.getSnap());
});
Baseline.runBaseline(
tscBaselineName(input.scenario, input.subScenario, input.commandLineArgs),
baseline.join("\r\n"),
);
});
if (input.edits?.length) {
it("tsc invocation after edit and clean build correctness", () => {
let baselines: string[] | undefined;
for (let index = 0; index < edits.length; index++) {
baselines = verifyTscEditDiscrepancies({
for (let index = 0; index < input.edits!.length; index++) {
baselines = verifyTscEditDiscrepancies(
input,
index,
edits,
scenario,
snaps!,
baselines,
baseFs,
newSys: editsSys![index],
commandLineArgs,
modifyFs,
environmentVariables,
});
);
}
Harness.Baseline.runBaseline(
tscBaselineName(scenario, subScenario, commandLineArgs, /*isWatch*/ undefined, "-discrepancies"),
Baseline.runBaseline(
tscBaselineName(input.scenario, input.subScenario, input.commandLineArgs, "-discrepancies"),
baselines ? baselines.join("\r\n") : null, // eslint-disable-line no-restricted-syntax
);
});
+19 -80
View File
@@ -2,34 +2,20 @@ import {
verifyProgramStructure,
verifyResolutionCache,
} from "../../../harness/incrementalUtils.js";
import { patchHostForBuildInfoReadWrite } from "../../_namespaces/fakes.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import {
baselinePrograms,
CommandLineCallbacks,
applyEdit,
baselineAfterTscCompile,
BaselineBase,
commandLineCallbacks,
CommandLineProgram,
generateSourceMapBaselineFiles,
createBaseline,
isWatch,
tscBaselineName,
TscWatchSystem,
} from "./baseline.js";
import {
changeToHostTrackingWrittenFiles,
File,
SerializeOutputOrder,
StateLogger,
TestServerHost,
TestServerHostTrackingWrittenFiles,
} from "./virtualFileSystemWithWatch.js";
export const commonFile1: File = {
path: "/a/b/commonFile1.ts",
content: "let x = 1",
};
export const commonFile2: File = {
path: "/a/b/commonFile2.ts",
content: "let y = 1",
};
import { TestServerHost } from "./virtualFileSystemWithWatch.js";
export type WatchOrSolution<T extends ts.BuilderProgram> = void | ts.SolutionBuilder<T> | ts.WatchOfConfigFile<T> | ts.WatchOfFilesAndCompilerOptions<T>;
export interface TscWatchCompileChange<T extends ts.BuilderProgram = ts.EmitAndSemanticDiagnosticsBuilderProgram> {
@@ -62,7 +48,7 @@ export const noopChange: TscWatchCompileChange = {
};
function tscWatchCompile(input: TscWatchCompile) {
it("tsc-watch:: Generates files matching the baseline", () => {
it("tscWatch:: Generates files matching the baseline", () => {
const { sys, baseline } = createBaseline(input.sys());
const {
scenario,
@@ -72,8 +58,7 @@ function tscWatchCompile(input: TscWatchCompile) {
baselineSourceMap,
baselineDependencies,
} = input;
if (!isWatch(commandLineArgs)) sys.exit = exitCode => sys.exitCode = exitCode;
ts.Debug.assert(isWatch(commandLineArgs), "use verifyTsc");
const { cb, getPrograms } = commandLineCallbacks(sys);
const watchOrSolution = ts.executeCommandLine(
sys,
@@ -95,38 +80,6 @@ function tscWatchCompile(input: TscWatchCompile) {
});
}
export type TscWatchSystem = TestServerHostTrackingWrittenFiles;
function changeToTestServerHostWithTimeoutLogging(host: TestServerHostTrackingWrittenFiles, baseline: string[]): TscWatchSystem {
const logger: StateLogger = {
log: s => baseline.push(s),
logs: baseline,
};
host.switchToBaseliningInvoke(logger, SerializeOutputOrder.BeforeDiff);
return host;
}
export interface BaselineBase {
baseline: string[];
sys: TscWatchSystem;
}
export interface Baseline extends BaselineBase, CommandLineCallbacks {
}
export function createBaseline(system: TestServerHost, modifySystem?: (sys: TestServerHost, originalRead: TestServerHost["readFile"]) => void): Baseline {
const originalRead = system.readFile;
const initialSys = patchHostForBuildInfoReadWrite(system);
modifySystem?.(initialSys, originalRead);
const baseline: string[] = [];
const sys = changeToTestServerHostWithTimeoutLogging(changeToHostTrackingWrittenFiles(initialSys), baseline);
baseline.push(`currentDirectory:: ${sys.getCurrentDirectory()} useCaseSensitiveFileNames: ${sys.useCaseSensitiveFileNames}`);
baseline.push("Input::");
sys.serializeState(baseline, SerializeOutputOrder.None);
const { cb, getPrograms } = commandLineCallbacks(sys);
return { sys, baseline, cb, getPrograms };
}
export function createSolutionBuilderWithWatchHostForBaseline(sys: TestServerHost, cb: ts.ExecuteCommandLineCallbacks) {
const host = ts.createSolutionBuilderWithWatchHost(sys, /*createProgram*/ undefined, ts.createDiagnosticReporter(sys, /*pretty*/ true), ts.createBuilderStatusReporter(sys, /*pretty*/ true), ts.createWatchStatusReporter(sys, /*pretty*/ true));
host.afterProgramEmitAndDiagnostics = cb;
@@ -175,13 +128,6 @@ function updateWatchHostForBaseline<T extends ts.BuilderProgram>(host: ts.WatchC
return host;
}
export function applyEdit(sys: BaselineBase["sys"], baseline: BaselineBase["baseline"], edit: TscWatchCompileChange["edit"], caption?: TscWatchCompileChange["caption"]) {
baseline.push(`Change::${caption ? " " + caption : ""}`, "");
edit(sys);
baseline.push("Input::");
sys.serializeState(baseline, SerializeOutputOrder.AfterDiff);
}
export interface RunWatchBaseline<T extends ts.BuilderProgram> extends BaselineBase, TscWatchCompileBase<T> {
sys: TscWatchSystem;
getPrograms: () => readonly CommandLineProgram[];
@@ -228,15 +174,7 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
});
}
}
Baseline.runBaseline(tscBaselineName(scenario, subScenario, commandLineArgs, isWatch(commandLineArgs)), baseline.join("\r\n"));
}
export function isWatch(commandLineArgs: readonly string[]) {
return ts.forEach(commandLineArgs, arg => {
if (arg.charCodeAt(0) !== ts.CharacterCodes.minus) return false;
const option = arg.slice(arg.charCodeAt(1) === ts.CharacterCodes.minus ? 2 : 1).toLowerCase();
return option === "watch" || option === "w";
});
Baseline.runBaseline(tscBaselineName(scenario, subScenario, commandLineArgs), baseline.join("\r\n"));
}
export interface WatchBaseline extends BaselineBase, TscWatchCheckOptions {
@@ -257,14 +195,15 @@ export function watchBaseline({
resolutionCache,
useSourceOfProjectReferenceRedirect,
}: WatchBaseline) {
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
const programs = getPrograms();
sys.writtenFiles.forEach((value, key) => {
assert.equal(value, 1, `Expected to write file ${key} only once`);
});
sys.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
baselinePrograms(baseline, programs, oldPrograms, baselineDependencies);
baseline.push(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}`, "");
const programs = baselineAfterTscCompile(
sys,
baseline,
getPrograms,
oldPrograms,
baselineSourceMap,
/*shouldBaselinePrograms*/ true,
baselineDependencies,
);
// Verify program structure and resolution cache when incremental edit with tsc --watch (without build mode)
if (resolutionCache && programs.length) {
ts.Debug.assert(programs.length === 1);
+10 -10
View File
@@ -6,9 +6,9 @@ import {
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger.js";
import { FileRangesRequestArgs } from "../../../server/protocol.js";
import { patchHostForBuildInfoReadWrite } from "../../_namespaces/fakes.js";
import * as Harness from "../../_namespaces/Harness.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import { patchHostForBuildInfoReadWrite } from "./baseline.js";
import { ensureErrorFreeBuild } from "./solutionBuilder.js";
import { TscWatchCompileChange } from "./tscWatch.js";
import {
@@ -18,17 +18,15 @@ import {
} from "./typingsInstaller.js";
import {
changeToHostTrackingWrittenFiles,
createServerHost,
File,
FileOrFolderOrSymLink,
libFile,
SerializeOutputOrder,
TestServerHost,
TestServerHostTrackingWrittenFiles,
} from "./virtualFileSystemWithWatch.js";
export function baselineTsserverLogs(scenario: string, subScenario: string, sessionOrService: { logger: LoggerWithInMemoryLogs; }) {
Harness.Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.js`, sessionOrService.logger.logs.join("\r\n"));
Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.js`, sessionOrService.logger.logs.join("\r\n"));
}
export function toExternalFile(fileName: string): ts.server.protocol.ExternalFile {
@@ -200,7 +198,9 @@ export interface TestSessionOptions extends ts.server.SessionOptions, TestTyping
useCancellationToken?: boolean | number;
regionDiagLineCountThreshold?: number;
}
export type TestSessionPartialOptionsAndHost = Partial<Omit<TestSessionOptions, "typingsInstaller" | "cancellationToken">> & Pick<TestSessionOptions, "host">;
export type TestSessionPartialOptionsAndHost =
& Partial<Omit<TestSessionOptions, "typingsInstaller" | "cancellationToken">>
& Pick<TestSessionOptions, "host">;
export type TestSessionConstructorOptions = TestServerHost | TestSessionPartialOptionsAndHost;
export type TestSessionRequest<T extends ts.server.protocol.Request> = Pick<T, "command" | "arguments">;
@@ -551,7 +551,7 @@ function filePath(file: string | File) {
function verifyErrorsUsingGeterr({ scenario, subScenario, allFiles, openFiles, getErrRequest }: VerifyGetErrScenario) {
it("verifies the errors in open file", () => {
const host = createServerHost([...allFiles(), libFile]);
const host = TestServerHost.createServerHost(allFiles());
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
@@ -562,7 +562,7 @@ function verifyErrorsUsingGeterr({ scenario, subScenario, allFiles, openFiles, g
function verifyErrorsUsingGeterrForProject({ scenario, subScenario, allFiles, openFiles, getErrForProjectRequest }: VerifyGetErrScenario) {
it("verifies the errors in projects", () => {
const host = createServerHost([...allFiles(), libFile]);
const host = TestServerHost.createServerHost(allFiles());
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
@@ -579,7 +579,7 @@ function verifyErrorsUsingGeterrForProject({ scenario, subScenario, allFiles, op
function verifyErrorsUsingSyncMethods({ scenario, subScenario, allFiles, openFiles, syncDiagnostics }: VerifyGetErrScenario) {
it("verifies the errors using sync commands", () => {
const host = createServerHost([...allFiles(), libFile]);
const host = TestServerHost.createServerHost(allFiles());
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
for (const { file, project } of syncDiagnostics()) {
@@ -626,7 +626,7 @@ export function verifyGetErrScenario(scenario: VerifyGetErrScenario) {
}
export function createHostWithSolutionBuild(files: readonly FileOrFolderOrSymLink[], rootNames: readonly string[]) {
const host = createServerHost(files);
const host = TestServerHost.createServerHost(files);
// ts build should succeed
ensureErrorFreeBuild(host, rootNames);
return host;
@@ -5,34 +5,35 @@ import {
import * as ts from "../../_namespaces/ts.js";
import { stringifyIndented } from "../../_namespaces/ts.server.js";
import { jsonToReadableText } from "../helpers.js";
import { getPathForTypeScriptTestLocation } from "./contents.js";
import { TestSession } from "./tsserver.js";
import {
File,
TestServerHost,
} from "./virtualFileSystemWithWatch.js";
export const customTypesMap = {
path: "/typesMap.json" as ts.Path,
content: `{
"typesMap": {
"jquery": {
"match": "jquery(-(\\\\.?\\\\d+)+)?(\\\\.intellisense)?(\\\\.min)?\\\\.js$",
"types": ["jquery"]
},
"quack": {
"match": "/duckquack-(\\\\d+)\\\\.min\\\\.js",
"types": ["duck-types"]
}
export const customTypesMap: File = {
path: getPathForTypeScriptTestLocation("typesMap.json"),
content: jsonToReadableText({
typesMap: {
jquery: {
match: "jquery(-(\\.?\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$",
types: ["jquery"],
},
"simpleMap": {
"Bacon": "baconjs",
"bliss": "blissfuljs",
"commander": "commander",
"cordova": "cordova",
"react": "react",
"lodash": "lodash"
}
}`,
quack: {
match: "/duckquack-(\\d+)\\.min\\.js",
types: ["duck-types"],
},
},
simpleMap: {
Bacon: "baconjs",
bliss: "blissfuljs",
commander: "commander",
cordova: "cordova",
react: "react",
lodash: "lodash",
},
}),
};
export function loggerToTypingsInstallerLog(logger: LoggerWithInMemoryLogs): ts.server.typingsInstaller.Log {
@@ -60,7 +61,7 @@ function loadTypesRegistryFile(typesRegistryFilePath: string, host: TestServerHo
}
}
const typesRegistryPackageName = "types-registry";
function getTypesRegistryFileLocation(globalTypingsCacheLocation: string): string {
export function getTypesRegistryFileLocation(globalTypingsCacheLocation: string): string {
return ts.combinePaths(ts.normalizeSlashes(globalTypingsCacheLocation), `node_modules/${typesRegistryPackageName}/index.json`);
}
@@ -86,8 +87,8 @@ export class TestTypingsInstallerWorker extends ts.server.typingsInstaller.Typin
super(
testTypingInstaller.session.host,
testTypingInstaller.globalTypingsCacheLocation,
"/safeList.json" as ts.Path,
customTypesMap.path,
getPathForTypeScriptTestLocation("typingSafeList.json") as ts.Path,
customTypesMap.path as ts.Path,
testTypingInstaller.throttleLimit,
log,
);
@@ -96,16 +97,7 @@ export class TestTypingsInstallerWorker extends ts.server.typingsInstaller.Typin
this.log.writeLine(`Updating ${typesRegistryPackageName} npm package...`);
this.log.writeLine(`npm install --ignore-scripts ${typesRegistryPackageName}@${this.latestDistTag}`);
testTypingInstaller.session.host.ensureFileOrFolder({
path: getTypesRegistryFileLocation(testTypingInstaller.globalTypingsCacheLocation),
content: jsonToReadableText(createTypesRegistryFileContent(
testTypingInstaller.typesRegistry ?
ts.isString(testTypingInstaller.typesRegistry) ?
[testTypingInstaller.typesRegistry] :
testTypingInstaller.typesRegistry :
ts.emptyArray,
)),
});
testTypingInstaller.session.host.ensureTypingRegistryFile();
this.log.writeLine(`Updated ${typesRegistryPackageName} npm package`);
this.typesRegistry = loadTypesRegistryFile(
@@ -170,10 +162,8 @@ export class TestTypingsInstallerWorker extends ts.server.typingsInstaller.Typin
export interface TestTypingsInstallerOptions {
host: TestServerHost;
logger?: LoggerWithInMemoryLogs;
globalTypingsCacheLocation?: string;
throttleLimit?: number;
installAction?: InstallAction;
typesRegistry?: string | readonly string[];
throttledRequests?: number;
}
@@ -183,25 +173,22 @@ export class TestTypingsInstallerAdapter extends ts.server.TypingsInstallerAdapt
// Options
readonly throttleLimit: number;
readonly installAction: InstallAction;
readonly typesRegistry: string | readonly string[] | undefined;
readonly throttledRequests: number | undefined;
constructor(options: TestTypingsInstallerOptions) {
const globalTypingsCacheLocation = options.globalTypingsCacheLocation || options.host.getHostSpecificPath("/a/data");
super(
/*telemetryEnabled*/ false,
options.throttledRequests === undefined ?
{ ...options.logger!, hasLevel: ts.returnFalse } :
options.logger!,
options.host,
globalTypingsCacheLocation,
options.host.globalTypingsCacheLocation,
(...args) => this.session.event(...args),
// Some large number so requests arent throttled
options.throttledRequests === undefined ? 10 : options.throttledRequests,
);
this.throttleLimit = options.throttleLimit || 5;
this.installAction = options.installAction !== undefined ? options.installAction : true;
this.typesRegistry = options.typesRegistry;
this.throttledRequests = options.throttledRequests;
}
@@ -221,7 +208,8 @@ export class TestTypingsInstallerAdapter extends ts.server.TypingsInstallerAdapt
}
}
}
function createTypesRegistryFileContent(list: readonly string[]): TypesRegistryFile {
export function createTypesRegistryFileContent(list: readonly string[]): TypesRegistryFile {
const versionMap = {
"latest": "1.3.0",
"ts2.0": "1.0.0",
-147
View File
@@ -1,147 +0,0 @@
import { getDirectoryPath } from "../../_namespaces/ts.js";
import * as vfs from "../../_namespaces/vfs.js";
import { libContent } from "./contents.js";
export interface FsOptions {
libContentToAppend?: string;
cwd?: string;
executingFilePath?: string;
}
export type FsOptionsOrLibContentsToAppend = FsOptions | string;
function valueOfFsOptions(options: FsOptionsOrLibContentsToAppend | undefined, key: keyof FsOptions) {
return typeof options === "string" ?
key === "libContentToAppend" ? options : undefined :
options?.[key];
}
/**
* All the files must be in /src
*/
export function loadProjectFromFiles(
files: vfs.FileSet,
options?: FsOptionsOrLibContentsToAppend,
): vfs.FileSystem {
const executingFilePath = valueOfFsOptions(options, "executingFilePath");
const defaultLibLocation = executingFilePath ? getDirectoryPath(executingFilePath) : "/lib";
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files,
cwd: valueOfFsOptions(options, "cwd") || "/",
meta: { defaultLibLocation },
});
const libContentToAppend = valueOfFsOptions(options, "libContentToAppend");
fs.mkdirpSync(defaultLibLocation);
fs.writeFileSync(`${defaultLibLocation}/lib.d.ts`, libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
fs.makeReadonly();
return fs;
}
export function replaceText(fs: vfs.FileSystem, path: string, oldText: string, newText: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const old = fs.readFileSync(path, "utf-8");
if (!old.includes(oldText)) {
throw new Error(`Text "${oldText}" does not exist in file ${path}`);
}
const newContent = old.replace(oldText, newText);
fs.writeFileSync(path, newContent, "utf-8");
}
export function prependText(fs: vfs.FileSystem, path: string, additionalContent: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const old = fs.readFileSync(path, "utf-8");
fs.writeFileSync(path, `${additionalContent}${old}`, "utf-8");
}
export function appendText(fs: vfs.FileSystem, path: string, additionalContent: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const old = fs.readFileSync(path, "utf-8");
fs.writeFileSync(path, `${old}${additionalContent}`);
}
export function enableStrict(fs: vfs.FileSystem, path: string) {
replaceText(fs, path, `"strict": false`, `"strict": true`);
}
export function addTestPrologue(fs: vfs.FileSystem, path: string, prologue: string) {
prependText(
fs,
path,
`${prologue}
`,
);
}
export function addShebang(fs: vfs.FileSystem, project: string, file: string) {
prependText(
fs,
`src/${project}/${file}.ts`,
`#!someshebang ${project} ${file}
`,
);
}
export function restContent(project: string, file: string) {
return `function for${project}${file}Rest() {
const { b, ...rest } = { a: 10, b: 30, yy: 30 };
}`;
}
function nonrestContent(project: string, file: string) {
return `function for${project}${file}Rest() { }`;
}
export function addRest(fs: vfs.FileSystem, project: string, file: string) {
appendText(fs, `src/${project}/${file}.ts`, restContent(project, file));
}
export function removeRest(fs: vfs.FileSystem, project: string, file: string) {
replaceText(fs, `src/${project}/${file}.ts`, restContent(project, file), nonrestContent(project, file));
}
export function addStubFoo(fs: vfs.FileSystem, project: string, file: string) {
appendText(fs, `src/${project}/${file}.ts`, nonrestContent(project, file));
}
export function changeStubToRest(fs: vfs.FileSystem, project: string, file: string) {
replaceText(fs, `src/${project}/${file}.ts`, nonrestContent(project, file), restContent(project, file));
}
export function addSpread(fs: vfs.FileSystem, project: string, file: string) {
const path = `src/${project}/${file}.ts`;
const content = fs.readFileSync(path, "utf8");
fs.writeFileSync(
path,
`${content}
function ${project}${file}Spread(...b: number[]) { }
const ${project}${file}_ar = [20, 30];
${project}${file}Spread(10, ...${project}${file}_ar);`,
);
replaceText(
fs,
`src/${project}/tsconfig.json`,
`"strict": false,`,
`"strict": false,
"downlevelIteration": true,`,
);
}
export function getTripleSlashRef(project: string) {
return `/src/${project}/tripleRef.d.ts`;
}
export function addTripleSlashRef(fs: vfs.FileSystem, project: string, file: string) {
fs.writeFileSync(getTripleSlashRef(project), `declare class ${project}${file} { }`);
prependText(
fs,
`src/${project}/${file}.ts`,
`///<reference path="./tripleRef.d.ts"/>
const ${file}Const = new ${project}${file}();
`,
);
}
@@ -1,5 +1,11 @@
import {
harnessSessionCurrentDirectory,
harnessTypingInstallerCacheLocation,
} from "../../../harness/harnessLanguageService.js";
import { replaceAll } from "../../../harness/tsserverLogger.js";
import {
createWatchUtils,
ensureWatchablePath,
Watches,
WatchUtils,
} from "../../../harness/watchUtils.js";
@@ -18,6 +24,7 @@ import {
createSystemWatchFunctions,
Debug,
directorySeparator,
emptyArray,
FileSystemEntryKind,
FileWatcherCallback,
FileWatcherEventKind,
@@ -36,6 +43,7 @@ import {
insertSorted,
isArray,
isString,
libMap,
mapDefined,
matchFiles,
ModuleImportResult,
@@ -47,14 +55,23 @@ import {
server,
SortedArray,
sys,
targetToLibMap,
toPath,
} from "../../_namespaces/ts.js";
import { typingsInstaller } from "../../_namespaces/ts.server.js";
import { timeIncrements } from "../../_namespaces/vfs.js";
import { sanitizeSysOutput } from "./baseline.js";
import { jsonToReadableText } from "../helpers.js";
import {
getPathForTypeScriptTestLocation,
tscTypeScriptTestLocation,
} from "./contents.js";
import {
createTypesRegistryFileContent,
getTypesRegistryFileLocation,
} from "./typingsInstaller.js";
export const libFile: File = {
path: "/a/lib/lib.d.ts",
path: getPathForTypeScriptTestLocation("lib.d.ts"),
content: `/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
@@ -65,13 +82,11 @@ interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }`,
interface Array<T> { length: number; [n: number]: T; }
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };`,
};
function getExecutingFilePathFromLibFile(): string {
return combinePaths(getDirectoryPath(libFile.path), "tsc.js");
}
export const enum TestServerHostOsFlavor {
Windows,
MacOs,
@@ -87,45 +102,47 @@ export interface TestServerHostCreationParameters {
environmentVariables?: Map<string, string>;
runWithFallbackPolling?: boolean;
osFlavor?: TestServerHostOsFlavor;
typingsInstallerGlobalCacheLocation?: string;
typingsInstallerTypesRegistry?: string | readonly string[];
}
export function createWatchedSystem(fileOrFolderList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
return new TestServerHost(fileOrFolderList, params);
export interface WatchableExempt {
/** Add this field to true only if its the interesting scenario about what to watch */
watchableExempt?: boolean;
}
export function createServerHost(fileOrFolderList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
const host = new TestServerHost(fileOrFolderList, params);
// Just like sys, patch the host to use writeFile
patchWriteFileEnsuringDirectory(host);
return host;
}
export interface File {
export interface File extends WatchableExempt {
path: string;
content: string;
fileSize?: number;
}
export interface Folder {
export interface Folder extends WatchableExempt {
path: string;
}
export interface SymLink {
export interface SymLink extends WatchableExempt {
/** Location of the symlink. */
path: string;
/** Relative path to the real file. */
symLink: string;
}
export interface LibFile extends WatchableExempt {
path: string;
libFile: true;
}
export type FileOrFolderOrSymLink = File | Folder | SymLink;
export type FileOrFolderOrSymLinkOrLibFile = FileOrFolderOrSymLink | LibFile;
export interface FileOrFolderOrSymLinkMap {
[path: string]: string | Omit<File, "path"> | Omit<SymLink, "path"> | undefined;
}
export function isFile(fileOrFolderOrSymLink: FileOrFolderOrSymLink): fileOrFolderOrSymLink is File {
return isString((fileOrFolderOrSymLink as File).content);
export function isFile(fileOrFolderOrSymLink: FileOrFolderOrSymLinkOrLibFile): fileOrFolderOrSymLink is File | LibFile {
return isString((fileOrFolderOrSymLink as File).content) || (fileOrFolderOrSymLink as LibFile).libFile;
}
export function isSymLink(fileOrFolderOrSymLink: FileOrFolderOrSymLink): fileOrFolderOrSymLink is SymLink {
export function isSymLink(fileOrFolderOrSymLink: FileOrFolderOrSymLinkOrLibFile): fileOrFolderOrSymLink is SymLink {
return isString((fileOrFolderOrSymLink as SymLink).symLink);
}
@@ -140,6 +157,11 @@ interface FsFile extends FSEntryBase {
fileSize?: number;
}
interface FsLibFile extends FSEntryBase {
content?: string;
libFile: true;
}
interface FsFolder extends FSEntryBase {
entries: SortedArray<FSEntry>;
}
@@ -148,16 +170,29 @@ interface FsSymLink extends FSEntryBase {
symLink: string;
}
export type FSEntry = FsFile | FsFolder | FsSymLink;
export type FSEntry = FsFile | FsFolder | FsSymLink | FsLibFile;
export type FSFileOrLibFile = FsFile | FsLibFile;
function isFsFolder(s: FSEntry | undefined): s is FsFolder {
return !!s && isArray((s as FsFolder).entries);
}
function isFsFile(s: FSEntry | undefined): s is FsFile {
function isFsFileOrFsLibFile(s: FSEntry | undefined) {
return isFsFile(s) || isFsLibFile(s);
}
function isFsLibFile(s: FSEntry | undefined): s is FsLibFile {
return !!s && (s as FsLibFile).libFile;
}
export function isFsFile(s: FSEntry | undefined): s is FsFile {
return !!s && isString((s as FsFile).content);
}
function getFsFileOrLibFileContent(entry: FSFileOrLibFile) {
return entry.content ?? (entry.content = libFile.content);
}
function isFsSymLink(s: FSEntry | undefined): s is FsSymLink {
return !!s && isString((s as FsSymLink).symLink);
}
@@ -337,6 +372,28 @@ export enum SerializeOutputOrder {
BeforeDiff,
AfterDiff,
}
export type TestServerHostSnapshot = Map<Path, FSEntry>;
export function cloneFsMap(fs: Map<Path, FSEntry>): TestServerHostSnapshot {
const snap = new Map<Path, FSEntry>();
const clonedMap = new Map<FSEntry, FSEntry>();
const getCloned = (entry: FSEntry) => {
const existing = clonedMap.get(entry);
if (existing) return existing;
const cloned = clone(entry);
clonedMap.set(entry, cloned);
if (isFsFolder(cloned)) {
cloned.entries = cloned.entries.map(clone) as SortedArray<FSEntry>;
}
return cloned;
};
fs.forEach((value, key) => {
snap.set(key, getCloned(value));
});
return snap;
}
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, ModuleResolutionHost {
args: string[] = [];
@@ -369,7 +426,9 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
service?: server.ProjectService;
osFlavor: TestServerHostOsFlavor;
preferNonRecursiveWatch: boolean;
constructor(
globalTypingsCacheLocation: string;
private readonly typesRegistry: string | readonly string[] | undefined;
private constructor(
fileOrFolderorSymLinkList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[],
{
useCaseSensitiveFileNames,
@@ -380,6 +439,8 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
environmentVariables,
runWithFallbackPolling,
osFlavor,
typingsInstallerGlobalCacheLocation,
typingsInstallerTypesRegistry,
}: TestServerHostCreationParameters = {},
) {
this.useCaseSensitiveFileNames = !!useCaseSensitiveFileNames;
@@ -387,12 +448,11 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.osFlavor = osFlavor || TestServerHostOsFlavor.Windows;
this.windowsStyleRoot = windowsStyleRoot;
this.environmentVariables = environmentVariables;
currentDirectory = currentDirectory || "/";
this.currentDirectory = this.getHostSpecificPath(currentDirectory ?? harnessSessionCurrentDirectory);
this.getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
this.watchUtils = createWatchUtils("PolledWatches", "FsWatches", s => this.getCanonicalFileName(s), this);
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
this.executingFilePath = this.getHostSpecificPath(executingFilePath || getExecutingFilePathFromLibFile());
this.currentDirectory = this.getHostSpecificPath(currentDirectory);
this.toPath = s => toPath(s, this.currentDirectory, this.getCanonicalFileName);
this.executingFilePath = this.getHostSpecificPath(executingFilePath ?? tscTypeScriptTestLocation);
this.runWithFallbackPolling = !!runWithFallbackPolling;
const tscWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHFILE");
const tscWatchDirectory = this.environmentVariables && this.environmentVariables.get("TSC_WATCHDIRECTORY");
@@ -424,6 +484,78 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.watchFile = watchFile;
this.watchDirectory = watchDirectory;
this.reloadFS(fileOrFolderorSymLinkList);
// Ensure current directory exists
this.ensureFileOrFolder({ path: this.currentDirectory });
// Ensure libs
this.ensureFileOrFolder({
path: combinePaths(getDirectoryPath(this.executingFilePath), "lib.d.ts"),
content: libFile.content,
});
targetToLibMap.forEach(libFile => this.ensureLib(libFile));
libMap.forEach(libFile => this.ensureLib(libFile));
// Ensure global cache location has required data
this.globalTypingsCacheLocation = this.getHostSpecificPath(
typingsInstallerGlobalCacheLocation ??
harnessTypingInstallerCacheLocation,
);
this.typesRegistry = typingsInstallerTypesRegistry;
if (this.directoryExists(this.globalTypingsCacheLocation)) {
// Package.json
const npmConfigPath = combinePaths(this.globalTypingsCacheLocation, "package.json");
if (!this.fileExists(npmConfigPath)) {
this.writeFile(npmConfigPath, '{ "private": true }');
}
// Typings registry
this.ensureTypingRegistryFile();
}
}
private ensureLib(path: string) {
this.ensureFileOrFolder({
path: combinePaths(getDirectoryPath(this.executingFilePath), path),
libFile: true,
});
}
ensureTypingRegistryFile() {
this.ensureFileOrFolder({
path: getTypesRegistryFileLocation(this.globalTypingsCacheLocation),
content: jsonToReadableText(createTypesRegistryFileContent(
this.typesRegistry ?
isString(this.typesRegistry) ?
[this.typesRegistry] :
this.typesRegistry :
emptyArray,
)),
});
}
static createWatchedSystem(
fileOrFolderList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[],
params:
& Omit<TestServerHostCreationParameters, "typingsInstallerGlobalCacheLocation" | "typingsInstallerTypesRegistry">
& { currentDirectory: string; },
): TestServerHost {
ensureWatchablePath(params.currentDirectory, `currentDirectory: ${params.currentDirectory}`);
return new TestServerHost(fileOrFolderList, params);
}
static createServerHost(
fileOrFolderList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[],
params?: Omit<TestServerHostCreationParameters, "currentDirectory">,
): TestServerHost {
if ((params as TestServerHostCreationParameters)?.currentDirectory) (params as TestServerHostCreationParameters).currentDirectory = undefined;
const host = new TestServerHost(fileOrFolderList, params);
// Just like sys, patch the host to use writeFile
patchWriteFileEnsuringDirectory(host);
return host;
}
static getCreateWatchedSystem(forTsserver: boolean | undefined) {
return (!forTsserver ? TestServerHost.createWatchedSystem : TestServerHost.createServerHost);
}
private nextInode = 0;
@@ -449,8 +581,13 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
getHostSpecificPath(s: string) {
if (this.windowsStyleRoot && s.startsWith(directorySeparator)) {
return this.windowsStyleRoot + s.substring(1);
if (this.windowsStyleRoot) {
let result = s;
if (s.startsWith(directorySeparator)) {
result = this.windowsStyleRoot + s.substring(1);
}
if (!result.startsWith(directorySeparator)) result = replaceAll(result, directorySeparator, "\\");
return result;
}
return s;
}
@@ -479,11 +616,16 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.pendingInstalls.switchToBaseliningInvoke(logger, serializeOutputOrder);
}
private ensureInitialFileOrFolder(f: FileOrFolderOrSymLink) {
if (!f.watchableExempt) ensureWatchablePath(getDirectoryPath(f.path), `Directory path of FileOrFolderOrSymLink: ${f.path}`);
this.ensureFileOrFolder(f);
}
private reloadFS(fileOrFolderOrSymLinkList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[]) {
Debug.assert(this.fs.size === 0);
if (isArray(fileOrFolderOrSymLinkList)) {
fileOrFolderOrSymLinkList.forEach(f =>
this.ensureFileOrFolder(
this.ensureInitialFileOrFolder(
!this.windowsStyleRoot ?
f :
{ ...f, path: this.getHostSpecificPath(f.path) },
@@ -496,10 +638,10 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
const path = this.getHostSpecificPath(key);
const value = fileOrFolderOrSymLinkList[key];
if (isString(value)) {
this.ensureFileOrFolder({ path, content: value });
this.ensureInitialFileOrFolder({ path, content: value });
}
else {
this.ensureFileOrFolder({ path, ...value });
this.ensureInitialFileOrFolder({ path, ...value });
}
}
}
@@ -511,11 +653,11 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
modifyFile(filePath: string, content: string, options?: Partial<WatchInvokeOptions>) {
const path = this.toFullPath(filePath);
const currentEntry = this.fs.get(path);
if (!currentEntry || !isFsFile(currentEntry)) {
if (!currentEntry || !isFsFileOrFsLibFile(currentEntry)) {
throw new Error(`file not present: ${filePath}`);
}
if (options && options.invokeFileDeleteCreateAsPartInsteadOfChange) {
if (isFsLibFile(currentEntry) || options?.invokeFileDeleteCreateAsPartInsteadOfChange) {
this.removeFileOrFolder(currentEntry, /*isRenaming*/ false, options);
this.ensureFileOrFolder({ path: filePath, content }, /*ignoreWatchInvokedWithTriggerAsFileCreate*/ undefined, /*ignoreParentWatch*/ undefined, options);
}
@@ -547,11 +689,11 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
// Add updated folder with new folder name
const newFullPath = getNormalizedAbsolutePath(newFileName, this.currentDirectory);
const newFile = this.toFsFile({ path: newFullPath, content: file.content });
const newFile = this.toFsFileOrLibFile({ path: newFullPath, content: file.content });
const newPath = newFile.path;
const basePath = getDirectoryPath(path);
const basePath = getDirectoryPath(newPath);
Debug.assert(basePath !== path);
Debug.assert(basePath === getDirectoryPath(newPath));
this.ensureFileOrFolder({ path: getDirectoryPath(newFullPath) });
const baseFolder = this.fs.get(basePath) as FsFolder;
this.addFileOrFolderInFolder(baseFolder, newFile);
}
@@ -598,9 +740,9 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
}
ensureFileOrFolder(fileOrDirectoryOrSymLink: FileOrFolderOrSymLink, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean, ignoreParentWatch?: boolean, options?: Partial<WatchInvokeOptions>) {
ensureFileOrFolder(fileOrDirectoryOrSymLink: FileOrFolderOrSymLinkOrLibFile, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean, ignoreParentWatch?: boolean, options?: Partial<WatchInvokeOptions>) {
if (isFile(fileOrDirectoryOrSymLink)) {
const file = this.toFsFile(fileOrDirectoryOrSymLink);
const file = this.toFsFileOrLibFile(fileOrDirectoryOrSymLink);
// file may already exist when updating existing type declaration file
if (!this.fs.get(file.path)) {
const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath), ignoreParentWatch, options);
@@ -642,7 +784,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
return folder;
}
private addFileOrFolderInFolder(folder: FsFolder, fileOrDirectory: FsFile | FsFolder | FsSymLink, ignoreWatch?: boolean, options?: Partial<WatchInvokeOptions>) {
private addFileOrFolderInFolder(folder: FsFolder, fileOrDirectory: FSEntry, ignoreWatch?: boolean, options?: Partial<WatchInvokeOptions>) {
if (!this.fs.has(fileOrDirectory.path)) {
insertSorted(folder.entries, fileOrDirectory, (a, b) => compareStringsCaseSensitive(getBaseFileName(a.path), getBaseFileName(b.path)));
}
@@ -666,7 +808,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.inodeWatching = inodeWatching;
}
private removeFileOrFolder(fileOrDirectory: FsFile | FsFolder | FsSymLink, isRenaming?: boolean, options?: Partial<WatchInvokeOptions>) {
private removeFileOrFolder(fileOrDirectory: FSEntry, isRenaming?: boolean, options?: Partial<WatchInvokeOptions>) {
const basePath = getDirectoryPath(fileOrDirectory.path);
const baseFolder = this.fs.get(basePath) as FsFolder;
if (basePath !== fileOrDirectory.path) {
@@ -683,9 +825,15 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.inodes?.delete(fileOrDirectory.path);
}
rimrafSync(fileOrFolderPath: string) {
const fileOrFolder = this.getRealFileOrFolder(fileOrFolderPath);
if (isFsFileOrFsLibFile(fileOrFolder)) this.removeFileOrFolder(fileOrFolder);
else if (isFsFolder(fileOrFolder)) this.deleteFolder(fileOrFolder.fullPath, /*recursive*/ true);
}
deleteFile(filePath: string) {
const file = this.getRealFileOrFolder(filePath);
Debug.assert(isFsFile(file));
Debug.assert(isFsFileOrFsLibFile(file));
this.removeFileOrFolder(file);
}
@@ -801,10 +949,11 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
};
}
private toFsFile(file: File): FsFile {
const fsFile = this.toFsEntry(file.path) as FsFile;
fsFile.content = file.content;
fsFile.fileSize = file.fileSize;
private toFsFileOrLibFile(file: File | LibFile): FsFile | FsLibFile {
const fsFile = this.toFsEntry(file.path) as FsFile & FsLibFile;
fsFile.content = (file as File).content;
fsFile.fileSize = (file as File).fileSize;
fsFile.libFile = (file as LibFile).libFile;
return fsFile;
}
@@ -846,8 +995,8 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
return !!this.getRealFile(fsEntry.path, fsEntry);
}
private getRealFile(path: Path, fsEntry?: FSEntry): FsFile | undefined {
return this.getRealFsEntry(isFsFile, path, fsEntry);
private getRealFile(path: Path, fsEntry?: FSEntry): FSFileOrLibFile | undefined {
return this.getRealFsEntry(isFsFileOrFsLibFile, path, fsEntry);
}
private isFsFolder(fsEntry: FSEntry) {
@@ -885,14 +1034,14 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
readFile(s: string): string | undefined {
const fsEntry = this.getRealFile(this.toFullPath(s));
return fsEntry ? fsEntry.content : undefined;
return fsEntry ? getFsFileOrLibFileContent(fsEntry) : undefined;
}
getFileSize(s: string) {
const path = this.toFullPath(s);
const entry = this.fs.get(path)!;
if (isFsFile(entry)) {
return entry.fileSize ? entry.fileSize : entry.content.length;
if (isFsFileOrFsLibFile(entry)) {
return (entry as FsFile).fileSize ? (entry as FsFile).fileSize! : getFsFileOrLibFileContent(entry).length;
}
return undefined!; // TODO: GH#18217
}
@@ -996,7 +1145,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
writeFile(path: string, content: string): void {
const file = this.toFsFile({ path, content });
const file = this.toFsFileOrLibFile({ path, content });
// base folder has to be present
const base = getDirectoryPath(file.path);
@@ -1057,15 +1206,12 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.clearOutput();
}
getSnap() {
return cloneFsMap(this.fs);
}
private snap() {
this.serializedDiff = new Map<Path, FSEntry>();
this.fs.forEach((value, key) => {
const cloneValue = clone(value);
if (isFsFolder(cloneValue)) {
cloneValue.entries = cloneValue.entries.map(clone) as SortedArray<FSEntry>;
}
this.serializedDiff.set(key, cloneValue);
});
this.serializedDiff = cloneFsMap(this.fs);
}
serializeState(baseline: string[], serializeOutput: SerializeOutputOrder) {
@@ -1080,7 +1226,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
writtenFiles?: Map<Path, number>;
private serializedDiff = new Map<Path, FSEntry>();
private serializedDiff: TestServerHostSnapshot = new Map<Path, FSEntry>();
diff(baseline: string[]) {
this.fs.forEach((newFsEntry, path) => {
diffFsEntry(baseline, this.serializedDiff.get(path), newFsEntry, this.inodes?.get(path), this.writtenFiles);
@@ -1131,19 +1277,24 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
}
function diffFsFile(baseline: string[], fsEntry: FsFile, newInode: number | undefined) {
baseline.push(`//// [${fsEntry.fullPath}]${inodeString(newInode)}\r\n${fsEntry.content}`, "");
function diffFsFile(baseline: string[], fsEntry: FSFileOrLibFile, newInode: number | undefined) {
if (!isFsLibFile(fsEntry)) {
baseline.push(`//// [${fsEntry.fullPath}]${inodeString(newInode)}\r\n${fsEntry.content}`, "");
}
else if (fsEntry.content) {
baseline.push(`//// [${fsEntry.fullPath}] *Lib*${inodeString(newInode)}`, "");
}
}
function diffFsSymLink(baseline: string[], fsEntry: FsSymLink, newInode: number | undefined) {
baseline.push(`//// [${fsEntry.fullPath}] symlink(${fsEntry.symLink})${inodeString(newInode)}`);
baseline.push(`//// [${fsEntry.fullPath}] symlink(${fsEntry.symLink})${inodeString(newInode)}`, "");
}
function inodeString(inode: number | undefined) {
return inode !== undefined ? ` Inode:: ${inode}` : "";
}
function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsEntry: FSEntry | undefined, newInode: number | undefined, writtenFiles: Map<string, any> | undefined): void {
const file = newFsEntry && newFsEntry.fullPath;
if (isFsFile(oldFsEntry)) {
if (isFsFile(newFsEntry)) {
if (isFsFileOrFsLibFile(oldFsEntry)) {
if (isFsFileOrFsLibFile(newFsEntry)) {
if (oldFsEntry.content !== newFsEntry.content) {
diffFsFile(baseline, newFsEntry, newInode);
}
@@ -1185,12 +1336,12 @@ function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsE
}
else {
baseline.push(`//// [${oldFsEntry.fullPath}] deleted symlink`);
if (isFsFile(newFsEntry)) {
if (isFsFileOrFsLibFile(newFsEntry)) {
diffFsFile(baseline, newFsEntry, newInode);
}
}
}
else if (isFsFile(newFsEntry)) {
else if (isFsFileOrFsLibFile(newFsEntry)) {
diffFsFile(baseline, newFsEntry, newInode);
}
else if (isFsSymLink(newFsEntry)) {
@@ -1206,6 +1357,12 @@ function baselineOutputs(baseline: string[], output: readonly string[], start: n
if (baselinedOutput) baseline.push(baselinedOutput.join(""));
}
function sanitizeSysOutput(output: string) {
return output
.replace(/Elapsed::\s\d+(?:\.\d+)?ms/g, "Elapsed:: *ms")
.replace(/\d\d:\d\d:\d\d\s(?:A|P)M/g, "HH:MM:SS AM");
}
export type TestServerHostTrackingWrittenFiles = TestServerHost & { writtenFiles: Map<Path, number>; };
export function changeToHostTrackingWrittenFiles(inputHost: TestServerHost) {
+9 -12
View File
@@ -4,8 +4,7 @@ import * as Harness from "../_namespaces/Harness.js";
import * as ts from "../_namespaces/ts.js";
import * as vfs from "../_namespaces/vfs.js";
import { jsonToReadableText } from "./helpers.js";
import { libContent } from "./helpers/contents.js";
import { loadProjectFromFiles } from "./helpers/vfs.js";
import { TestServerHost } from "./helpers/virtualFileSystemWithWatch.js";
describe("unittests:: Public APIs", () => {
function verifyApi(fileName: string) {
@@ -264,10 +263,10 @@ class C {
describe("unittests:: Public APIs:: createProgram", () => {
function verifyAPI(useJsonParsingApi: boolean) {
const fs = loadProjectFromFiles({
"/src/projects/project/packages/a/index.js": `export const a = 'a';`,
"/src/projects/project/packages/a/test/index.js": `import 'a';`,
"/src/projects/project/packages/a/tsconfig.json": jsonToReadableText({
const sys = TestServerHost.createWatchedSystem({
"/home/src/projects/project/packages/a/index.js": `export const a = 'a';`,
"/home/src/projects/project/packages/a/test/index.js": `import 'a';`,
"/home/src/projects/project/packages/a/tsconfig.json": jsonToReadableText({
compilerOptions: {
checkJs: true,
composite: true,
@@ -277,7 +276,7 @@ describe("unittests:: Public APIs:: createProgram", () => {
outDir: "types",
},
}),
"/src/projects/project/packages/a/package.json": jsonToReadableText({
"/home/src/projects/project/packages/a/package.json": jsonToReadableText({
name: "a",
version: "0.0.0",
type: "module",
@@ -288,11 +287,9 @@ describe("unittests:: Public APIs:: createProgram", () => {
},
},
}),
"/lib/lib.esnext.full.d.ts": libContent,
}, { cwd: "/src/projects/project", executingFilePath: "/lib/tsc.js" });
const sys = new fakes.System(fs, { executingFilePath: "/lib/tsc.js" });
}, { currentDirectory: "/home/src/projects/project" });
const commandLine = ts.getParsedCommandLineOfConfigFile(
"/src/projects/project/packages/a/tsconfig.json",
"/home/src/projects/project/packages/a/tsconfig.json",
/*optionsToExtend*/ undefined,
{
fileExists: sys.fileExists.bind(sys),
@@ -306,7 +303,7 @@ describe("unittests:: Public APIs:: createProgram", () => {
realpath: sys.realpath.bind(sys),
},
)!;
const config = !useJsonParsingApi ? JSON.parse(fs.readFileSync("/src/projects/project/packages/a/tsconfig.json", "utf-8")) : undefined;
const config = !useJsonParsingApi ? JSON.parse(sys.readFile("/home/src/projects/project/packages/a/tsconfig.json")!) : undefined;
// This is really createCompilerHost but we want to use our own sys so simple usage
const host = ts.createCompilerHostWorker(
useJsonParsingApi ? commandLine.options : config.compilerOptions,
@@ -14,9 +14,8 @@ import {
updateProgramText,
} from "./helpers.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "./helpers/virtualFileSystemWithWatch.js";
describe("unittests:: reuseProgramStructure:: General", () => {
@@ -115,17 +114,17 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("successful if change affects a single module of a package", () => {
const files = [
{ name: "/a.ts", text: SourceText.New("", "import {b} from 'b'", "var a = b;") },
{ name: "/node_modules/b/index.d.ts", text: SourceText.New("", "export * from './internal';", "") },
{ name: "/node_modules/b/internal.d.ts", text: SourceText.New("", "", "export const b = 1;") },
{ name: "/node_modules/b/package.json", text: SourceText.New("", "", jsonToReadableText({ name: "b", version: "1.2.3" })) },
{ name: "/home/src/workspaces/project/a.ts", text: SourceText.New("", "import {b} from 'b'", "var a = b;") },
{ name: "/home/src/workspaces/project/node_modules/b/index.d.ts", text: SourceText.New("", "export * from './internal';", "") },
{ name: "/home/src/workspaces/project/node_modules/b/internal.d.ts", text: SourceText.New("", "", "export const b = 1;") },
{ name: "/home/src/workspaces/project/node_modules/b/package.json", text: SourceText.New("", "", jsonToReadableText({ name: "b", version: "1.2.3" })) },
];
const options: ts.CompilerOptions = { target, moduleResolution: ts.ModuleResolutionKind.Node10 };
const program1 = newProgram(files, ["/a.ts"], options);
const program1 = newProgram(files, ["/home/src/workspaces/project/a.ts"], options);
const baselines: string[] = [];
baselineProgram(baselines, program1);
const program2 = updateProgram(program1, ["/a.ts"], options, files => {
const program2 = updateProgram(program1, ["/home/src/workspaces/project/a.ts"], options, files => {
files[2].text = files[2].text.updateProgram("export const b = 2;");
});
baselineProgram(baselines, program2);
@@ -200,19 +199,19 @@ describe("unittests:: reuseProgramStructure:: General", () => {
});
it("succeeds if rootdir changes", () => {
const program1 = newProgram(getFiles(), ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/a/b" });
const program1 = newProgram(getFiles(), ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/home/src/workspaces/project/a/b" });
const baselines: string[] = [];
baselineProgram(baselines, program1);
const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/a/c" }, ts.noop);
const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/home/src/workspaces/project/a/c" }, ts.noop);
baselineProgram(baselines, program2);
runBaseline("rootdir changes", baselines);
});
it("fails if config path changes", () => {
const program1 = newProgram(getFiles(), ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
const program1 = newProgram(getFiles(), ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/home/src/workspaces/project/a/b/tsconfig.json" });
const baselines: string[] = [];
baselineProgram(baselines, program1);
const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, ts.noop);
const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/home/src/workspaces/project/a/c/tsconfig.json" }, ts.noop);
baselineProgram(baselines, program2);
runBaseline("config path changes", baselines);
});
@@ -273,14 +272,14 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("set the resolvedImports after re-using an ambient external module declaration", () => {
const files = [
{ name: "/a.ts", text: SourceText.New("", "", 'import * as a from "a";') },
{ name: "/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') },
{ name: "/home/src/workspaces/project/a.ts", text: SourceText.New("", "", 'import * as a from "a";') },
{ name: "/home/src/workspaces/project/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') },
];
const options: ts.CompilerOptions = { target, typeRoots: ["/types"] };
const program1 = newProgram(files, ["/a.ts"], options);
const options: ts.CompilerOptions = { target, typeRoots: ["/home/src/workspaces/project/types"] };
const program1 = newProgram(files, ["/home/src/workspaces/project/a.ts"], options);
const baselines: string[] = [];
baselineProgram(baselines, program1);
const program2 = updateProgram(program1, ["/a.ts"], options, files => {
const program2 = updateProgram(program1, ["/home/src/workspaces/project/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram('import * as aa from "a";');
});
baselineProgram(baselines, program2);
@@ -290,14 +289,14 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("works with updated SourceFiles", () => {
// adapted repro from https://github.com/Microsoft/TypeScript/issues/26166
const files = [
{ name: "/a.ts", text: SourceText.New("", "", 'import * as a from "a";a;') },
{ name: "/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') },
{ name: "/home/src/workspaces/project/a.ts", text: SourceText.New("", "", 'import * as a from "a";a;') },
{ name: "/home/src/workspaces/project/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') },
];
const host = createTestCompilerHost(files, target);
const options: ts.CompilerOptions = { target, typeRoots: ["/types"] };
const program1 = ts.createProgram(["/a.ts"], options, host) as ProgramToBaseline;
const options: ts.CompilerOptions = { target, typeRoots: ["/home/src/workspaces/project/types"] };
const program1 = ts.createProgram(["/home/src/workspaces/project/a.ts"], options, host) as ProgramToBaseline;
program1.version = 1;
let sourceFile = program1.getSourceFile("/a.ts")!;
let sourceFile = program1.getSourceFile("/home/src/workspaces/project/a.ts")!;
const baselines: string[] = [];
baselineProgram(baselines, program1, host);
sourceFile = ts.updateSourceFile(sourceFile, "'use strict';" + sourceFile.text, { newLength: "'use strict';".length, span: { start: 0, length: 0 } });
@@ -308,7 +307,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
return fileName === sourceFile.fileName ? sourceFile : program1.getSourceFile(fileName);
},
};
const program2 = ts.createProgram(["/a.ts"], options, updateHost, program1) as ProgramToBaseline;
const program2 = ts.createProgram(["/home/src/workspaces/project/a.ts"], options, updateHost, program1) as ProgramToBaseline;
program2.version = 2;
baselineProgram(baselines, program2, updateHost);
baselines.push(`parent pointers are not altered: ${sourceFile.statements[2].getSourceFile() === sourceFile}`);
@@ -317,28 +316,28 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("resolved type directives cache follows type directives", () => {
const files = [
{ name: "/a.ts", text: SourceText.New("/// <reference types='typedefs'/>", "", "var x = $") },
{ name: "/types/typedefs/index.d.ts", text: SourceText.New("", "", "declare var $: number") },
{ name: "/home/src/workspaces/project/a.ts", text: SourceText.New("/// <reference types='typedefs'/>", "", "var x = $") },
{ name: "/home/src/workspaces/project/types/typedefs/index.d.ts", text: SourceText.New("", "", "declare var $: number") },
];
const options: ts.CompilerOptions = { target, typeRoots: ["/types"] };
const options: ts.CompilerOptions = { target, typeRoots: ["/home/src/workspaces/project/types"] };
const program1 = newProgram(files, ["/a.ts"], options);
const program1 = newProgram(files, ["/home/src/workspaces/project/a.ts"], options);
const baselines: string[] = [];
baselineProgram(baselines, program1);
const program2 = updateProgram(program1, ["/a.ts"], options, files => {
const program2 = updateProgram(program1, ["/home/src/workspaces/project/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
baselineProgram(baselines, program2);
// type reference directives has changed - program is not reused
const program3 = updateProgram(program2, ["/a.ts"], options, files => {
const program3 = updateProgram(program2, ["/home/src/workspaces/project/a.ts"], options, files => {
files[0].text = files[0].text.updateReferences("");
});
baselineProgram(baselines, program3);
const program4 = updateProgram(program3, ["/a.ts"], options, files => {
const program4 = updateProgram(program3, ["/home/src/workspaces/project/a.ts"], options, files => {
const newReferences = `/// <reference types="typedefs"/>
/// <reference types="typedefs2"/>
`;
@@ -369,8 +368,8 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("should not reuse ambient module declarations from non-modified files", () => {
const files = [
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
{ name: "/a/b/node.d.ts", text: SourceText.New("", "", "declare module 'fs' {}") },
{ name: "/home/src/workspaces/project/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
{ name: "/home/src/workspaces/project/a/b/node.d.ts", text: SourceText.New("", "", "declare module 'fs' {}") },
];
const options = { target: ts.ScriptTarget.ES2015, traceResolution: true };
const program = newProgram(files, files.map(f => f.name), options);
@@ -460,17 +459,17 @@ describe("unittests:: reuseProgramStructure:: General", () => {
});
describe("redirects", () => {
const axIndex = "/node_modules/a/node_modules/x/index.d.ts";
const axPackage = "/node_modules/a/node_modules/x/package.json";
const bxIndex = "/node_modules/b/node_modules/x/index.d.ts";
const bxPackage = "/node_modules/b/node_modules/x/package.json";
const root = "/a.ts";
const axIndex = "/home/src/workspaces/project/node_modules/a/node_modules/x/index.d.ts";
const axPackage = "/home/src/workspaces/project/node_modules/a/node_modules/x/package.json";
const bxIndex = "/home/src/workspaces/project/node_modules/b/node_modules/x/index.d.ts";
const bxPackage = "/home/src/workspaces/project/node_modules/b/node_modules/x/package.json";
const root = "/home/src/workspaces/project/a.ts";
const compilerOptions = { target, moduleResolution: ts.ModuleResolutionKind.Node10 };
function createRedirectProgram(useGetSourceFileByPath: boolean, options?: { bText: string; bVersion: string; }): ProgramWithSourceTexts {
const files: NamedSourceText[] = [
{
name: "/node_modules/a/index.d.ts",
name: "/home/src/workspaces/project/node_modules/a/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export function a(x: X): void;"),
},
{
@@ -482,7 +481,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
text: SourceText.New("", "", jsonToReadableText({ name: "x", version: "1.2.3" })),
},
{
name: "/node_modules/b/index.d.ts",
name: "/home/src/workspaces/project/node_modules/b/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export const b: X;"),
},
{
@@ -574,7 +573,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("forceConsistentCasingInFileNames:: handles file preprocessing dignostics", () => {
const files = [
{
name: "/src/project/src/struct.d.ts",
name: "/home/src/workspaces/project/src/project/src/struct.d.ts",
text: SourceText.New(
"",
"",
@@ -587,7 +586,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/src/anotherFile.ts",
name: "/home/src/workspaces/project/src/project/src/anotherFile.ts",
text: SourceText.New(
"",
"",
@@ -600,7 +599,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/src/oneMore.ts",
name: "/home/src/workspaces/project/src/project/src/oneMore.ts",
text: SourceText.New(
"",
"",
@@ -613,7 +612,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/node_modules/fp-ts/lib/struct.d.ts",
name: "/home/src/workspaces/project/src/project/node_modules/fp-ts/lib/struct.d.ts",
text: SourceText.New("", "", `export function foo(): void`),
},
];
@@ -659,7 +658,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
it("forceConsistentCasingInFileNames:: handles file preprocessing dignostics when diagnostics are not queried", () => {
const files = [
{
name: "/src/project/src/struct.d.ts",
name: "/home/src/workspaces/project/src/project/src/struct.d.ts",
text: SourceText.New(
"",
"",
@@ -672,7 +671,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/src/anotherFile.ts",
name: "/home/src/workspaces/project/src/project/src/anotherFile.ts",
text: SourceText.New(
"",
"",
@@ -685,7 +684,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/src/oneMore.ts",
name: "/home/src/workspaces/project/src/project/src/oneMore.ts",
text: SourceText.New(
"",
"",
@@ -698,7 +697,7 @@ describe("unittests:: reuseProgramStructure:: General", () => {
),
},
{
name: "/src/project/node_modules/fp-ts/lib/struct.d.ts",
name: "/home/src/workspaces/project/src/project/node_modules/fp-ts/lib/struct.d.ts",
text: SourceText.New("", "", `export function foo(): void`),
},
];
@@ -836,43 +835,46 @@ describe("unittests:: reuseProgramStructure:: isProgramUptoDate::", () => {
}
function verifyProgram(files: File[], rootFiles: string[], options: ts.CompilerOptions, configFile: string) {
const system = createWatchedSystem(files);
const system = TestServerHost.createWatchedSystem(
files,
{ currentDirectory: ts.getDirectoryPath(configFile) },
);
verifyProgramWithoutConfigFile(system, rootFiles, options);
verifyProgramWithConfigFile(system, configFile);
}
it("has empty options", () => {
const file1: File = {
path: "/a/b/file1.ts",
path: "/home/src/workspaces/project/a/b/file1.ts",
content: "let x = 1",
};
const file2: File = {
path: "/a/b/file2.ts",
path: "/home/src/workspaces/project/a/b/file2.ts",
content: "let y = 1",
};
const configFile: File = {
path: "/a/b/tsconfig.json",
path: "/home/src/workspaces/project/a/b/tsconfig.json",
content: "{}",
};
verifyProgram([file1, file2, libFile, configFile], [file1.path, file2.path], {}, configFile.path);
verifyProgram([file1, file2, configFile], [file1.path, file2.path], {}, configFile.path);
});
it("has lib specified in the options", () => {
const compilerOptions: ts.CompilerOptions = { lib: ["es5", "es2015.promise"] };
const app: File = {
path: "/src/app.ts",
path: "/home/src/workspaces/project/src/app.ts",
content: "var x: Promise<string>;",
};
const configFile: File = {
path: "/src/tsconfig.json",
path: "/home/src/workspaces/project/src/tsconfig.json",
content: jsonToReadableText({ compilerOptions }),
};
const es5Lib: File = {
path: "/compiler/lib.es5.d.ts",
path: "/home/src/workspaces/project/compiler/lib.es5.d.ts",
content: "declare const eval: any",
};
const es2015Promise: File = {
path: "/compiler/lib.es2015.promise.d.ts",
path: "/home/src/workspaces/project/compiler/lib.es2015.promise.d.ts",
content: "declare class Promise<T> {}",
};
@@ -891,30 +893,30 @@ describe("unittests:: reuseProgramStructure:: isProgramUptoDate::", () => {
},
};
const app: File = {
path: "/src/packages/framework/app.ts",
path: "/home/src/workspaces/project/src/packages/framework/app.ts",
content: 'import classc from "module1/lib/file1";\
import classD from "module3/file3";\
let x = new classc();\
let y = new classD();',
};
const module1: File = {
path: "/src/packages/mail/data/module1/lib/file1.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/file1.ts",
content: 'import classc from "module2/file2";export default classc;',
};
const module2: File = {
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/module2/file2.ts",
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
};
const module3: File = {
path: "/src/packages/styles/module3/file3.ts",
path: "/home/src/workspaces/project/src/packages/styles/module3/file3.ts",
content: "class classD { method() { return 10; } }\nexport default classD;",
};
const configFile: File = {
path: "/src/tsconfig.json",
path: "/home/src/workspaces/project/src/tsconfig.json",
content: jsonToReadableText({ compilerOptions }),
};
verifyProgram([app, module1, module2, module3, libFile, configFile], [app.path], compilerOptions, configFile.path);
verifyProgram([app, module1, module2, module3, configFile], [app.path], compilerOptions, configFile.path);
});
it("has include paths specified in tsconfig file", () => {
@@ -929,45 +931,54 @@ describe("unittests:: reuseProgramStructure:: isProgramUptoDate::", () => {
},
};
const app: File = {
path: "/src/packages/framework/app.ts",
path: "/home/src/workspaces/project/home/src/packages/framework/app.ts",
content: 'import classc from "module1/lib/file1";\
import classD from "module3/file3";\
let x = new classc();\
let y = new classD();',
};
const module1: File = {
path: "/src/packages/mail/data/module1/lib/file1.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/file1.ts",
content: 'import classc from "module2/file2";export default classc;',
};
const module2: File = {
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/module2/file2.ts",
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
};
const module3: File = {
path: "/src/packages/styles/module3/file3.ts",
path: "/home/src/workspaces/project/src/packages/styles/module3/file3.ts",
content: "class classD { method() { return 10; } }\nexport default classD;",
};
const configFile: File = {
path: "/src/tsconfig.json",
path: "/home/src/workspaces/project/src/tsconfig.json",
content: jsonToReadableText({ compilerOptions, include: ["packages/**/*.ts"] }),
};
verifyProgramWithConfigFile(createWatchedSystem([app, module1, module2, module3, libFile, configFile]), configFile.path);
verifyProgramWithConfigFile(
TestServerHost.createWatchedSystem(
[app, module1, module2, module3, configFile],
{ currentDirectory: ts.getDirectoryPath(configFile.path) },
),
configFile.path,
);
});
it("has the same root file names", () => {
const module1: File = {
path: "/src/packages/mail/data/module1/lib/file1.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/file1.ts",
content: 'import classc from "module2/file2";export default classc;',
};
const module2: File = {
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/module2/file2.ts",
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
};
const module3: File = {
path: "/src/packages/styles/module3/file3.ts",
path: "/home/src/workspaces/project/src/packages/styles/module3/file3.ts",
content: "class classD { method() { return 10; } }\nexport default classD;",
};
const rootFiles = [module1.path, module2.path, module3.path];
const system = createWatchedSystem([module1, module2, module3]);
const system = TestServerHost.createWatchedSystem(
[module1, module2, module3],
{ currentDirectory: "/home/src/workspaces/project/src/packages" },
);
const options = {};
const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({
rootFiles,
@@ -989,20 +1000,23 @@ describe("unittests:: reuseProgramStructure:: isProgramUptoDate::", () => {
}
it("has more root file names", () => {
const module1: File = {
path: "/src/packages/mail/data/module1/lib/file1.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/file1.ts",
content: 'import classc from "module2/file2";export default classc;',
};
const module2: File = {
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/module2/file2.ts",
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
};
const module3: File = {
path: "/src/packages/styles/module3/file3.ts",
path: "/home/src/workspaces/project/src/packages/styles/module3/file3.ts",
content: "class classD { method() { return 10; } }\nexport default classD;",
};
const rootFiles = [module1.path, module2.path];
const newRootFiles = [module1.path, module2.path, module3.path];
const system = createWatchedSystem([module1, module2, module3]);
const system = TestServerHost.createWatchedSystem(
[module1, module2, module3],
{ currentDirectory: "/home/src/workspaces/project/src/packages" },
);
const options = {};
const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({
rootFiles,
@@ -1014,20 +1028,23 @@ describe("unittests:: reuseProgramStructure:: isProgramUptoDate::", () => {
});
it("has one root file replaced by another", () => {
const module1: File = {
path: "/src/packages/mail/data/module1/lib/file1.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/file1.ts",
content: 'import classc from "module2/file2";export default classc;',
};
const module2: File = {
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
path: "/home/src/workspaces/project/src/packages/mail/data/module1/lib/module2/file2.ts",
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
};
const module3: File = {
path: "/src/packages/styles/module3/file3.ts",
path: "/home/src/workspaces/project/src/packages/styles/module3/file3.ts",
content: "class classD { method() { return 10; } }\nexport default classD;",
};
const rootFiles = [module1.path, module2.path];
const newRootFiles = [module2.path, module3.path];
const system = createWatchedSystem([module1, module2, module3]);
const system = TestServerHost.createWatchedSystem(
[module1, module2, module3],
{ currentDirectory: "/home/src/workspaces/project/src/packages" },
);
const options = {};
const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({
rootFiles,
@@ -1,8 +1,9 @@
import * as Harness from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import {
createServerHost,
File,
libFile as vfsWatch_LibFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
import {
extractTest,
@@ -12,7 +13,7 @@ import {
} from "./extract/helpers.js";
const libFile: File = {
path: "/a/lib/lib.d.ts",
path: vfsWatch_LibFile.path,
content: `/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
@@ -275,7 +276,7 @@ interface Array<T> {}`,
};
const moduleFile: File = {
path: "/module.ts",
path: "/home/src/workspaces/project/module.ts",
content: `export function fn(res: any): any {
return res;
}`,
@@ -330,7 +331,7 @@ function testConvertToAsyncFunction(it: Mocha.PendingTestFunction, caption: stri
extensions.forEach(extension => it(`${caption} [${extension}]`, () => runBaseline(extension)));
function runBaseline(extension: ts.Extension) {
const path = "/a" + extension;
const path = "/home/src/workspaces/project/a" + extension;
const languageService = makeLanguageService({ path, content: t.source }, includeLib, includeModule);
const program = languageService.getProgram()!;
@@ -412,7 +413,7 @@ function testConvertToAsyncFunction(it: Mocha.PendingTestFunction, caption: stri
if (includeModule) {
files.push(moduleFile);
}
const host = createServerHost(files);
const host = TestServerHost.createServerHost(files);
const projectService = new TestProjectService(host);
projectService.openClientFile(file.path);
return ts.first(projectService.inferredProjects).getLanguageService();
@@ -444,7 +445,7 @@ const _testConvertToAsyncFunctionWithModule = createTestWrapper((it, caption: st
testConvertToAsyncFunction(it, caption, text, "convertToAsyncFunction", ConvertToAsyncTestFlags.IncludeLib | ConvertToAsyncTestFlags.IncludeModule | ConvertToAsyncTestFlags.ExpectSuccess);
});
describe("unittests:: services:: convertToAsyncFunction", () => {
describe("unittests:: services:: convertToAsyncFunction::", () => {
_testConvertToAsyncFunction(
"convertToAsyncFunction_basic",
`
@@ -223,7 +223,6 @@ describe("unittests:: services:: extract:: extractFunctions", () => {
[#|t2.toString();|]
}
}`,
/*includeLib*/ true,
);
// Confirm that the contextual type of an extracted expression counts as a use.
testExtractFunction(
@@ -231,7 +230,6 @@ describe("unittests:: services:: extract:: extractFunctions", () => {
`function F<T>() {
const array: T[] = [#|[]|];
}`,
/*includeLib*/ true,
);
// Class type parameter
testExtractFunction(
@@ -257,7 +255,6 @@ describe("unittests:: services:: extract:: extractFunctions", () => {
`function F<T, U extends T[], V extends U[]>(v: V) {
[#|v.toString()|];
}`,
/*includeLib*/ true,
);
testExtractFunction(
@@ -717,6 +714,6 @@ function F() {
);
});
function testExtractFunction(caption: string, text: string, includeLib?: boolean) {
testExtractSymbol(caption, text, "extractFunction", ts.Diagnostics.Extract_function, includeLib);
function testExtractFunction(caption: string, text: string) {
testExtractSymbol(caption, text, "extractFunction", ts.Diagnostics.Extract_function);
}
@@ -3,11 +3,7 @@ import { createHasErrorMessageLogger } from "../../../../harness/tsserverLogger.
import * as Harness from "../../../_namespaces/Harness.js";
import * as ts from "../../../_namespaces/ts.js";
import { customTypesMap } from "../../helpers/typingsInstaller.js";
import {
createServerHost,
libFile,
TestServerHost,
} from "../../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../../helpers/virtualFileSystemWithWatch.js";
export interface TestProjectServiceOptions extends ts.server.ProjectServiceOptions {
host: TestServerHost;
@@ -112,7 +108,7 @@ export const notImplementedHost: ts.LanguageServiceHost = {
fileExists: ts.notImplemented,
};
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: ts.DiagnosticMessage, includeLib?: boolean) {
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: ts.DiagnosticMessage) {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection")!;
if (!selectionRange) {
@@ -122,8 +118,8 @@ export function testExtractSymbol(caption: string, text: string, baselineFolder:
[ts.Extension.Ts, ts.Extension.Js].forEach(extension => it(`${caption} [${extension}]`, () => runBaseline(extension)));
function runBaseline(extension: ts.Extension) {
const path = "/a" + extension;
const { program } = makeProgram({ path, content: t.source }, includeLib);
const path = "/home/src/workspaces/project/a" + extension;
const { program } = makeProgram({ path, content: t.source });
if (hasSyntacticDiagnostics(program)) {
// Don't bother generating JS baselines for inputs that aren't valid JS.
@@ -158,14 +154,14 @@ export function testExtractSymbol(caption: string, text: string, baselineFolder:
const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation);
data.push(newTextWithRename);
const { program: diagProgram } = makeProgram({ path, content: newText }, includeLib);
const { program: diagProgram } = makeProgram({ path, content: newText });
assert.isFalse(hasSyntacticDiagnostics(diagProgram));
}
Harness.Baseline.runBaseline(`${baselineFolder}/${caption}${extension}`, data.join(newLineCharacter));
}
function makeProgram(f: { path: string; content: string; }, includeLib?: boolean) {
const host = createServerHost(includeLib ? [f, libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
function makeProgram(f: { path: string; content: string; }) {
const host = TestServerHost.createServerHost([f]); // libFile is expensive to parse repeatedly - only test when required
const projectService = new TestProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
@@ -187,10 +183,10 @@ export function testExtractSymbolFailed(caption: string, text: string, descripti
throw new Error(`Test ${caption} does not specify selection range`);
}
const f = {
path: "/a.ts",
path: "/home/src/workspaces/project/a.ts",
content: t.source,
};
const host = createServerHost([f, libFile]);
const host = TestServerHost.createServerHost([f]);
const projectService = new TestProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
@@ -3,12 +3,12 @@ import { expect } from "chai";
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import {
createServerHost,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: services:: languageService", () => {
describe("unittests:: services:: languageService::", () => {
const files: { [index: string]: string; } = {
"foo.ts": `import Vue from "./vue";
import Component from "./vue-class-component";
@@ -183,7 +183,7 @@ export function Component(x: Config): any;`,
path: `/user/username/projects/myproject/projects/project2/class2.ts`,
content: `class class2 {}`,
};
const system = createServerHost([config1, class1, class1Dts, config2, class2, libFile]);
const system = TestServerHost.createServerHost([config1, class1, class1Dts, config2, class2]);
const result = ts.getParsedCommandLineOfConfigFile(`/user/username/projects/myproject/projects/project2/tsconfig.json`, /*optionsToExtend*/ undefined, {
useCaseSensitiveFileNames: true,
fileExists: path => system.fileExists(path),
@@ -1,15 +1,15 @@
import * as Harness from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import {
createServerHost,
File,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
import {
newLineCharacter,
TestProjectService,
} from "./extract/helpers.js";
describe("unittests:: services:: organizeImports", () => {
describe("unittests:: services:: organizeImports::", () => {
describe("Sort imports", () => {
it("Sort - non-relative vs non-relative", () => {
assertSortsBefore(
@@ -338,7 +338,7 @@ describe("unittests:: services:: organizeImports", () => {
describe("Baselines", () => {
const libFile = {
path: "/lib.ts",
path: "/home/src/workspaces/project/lib.ts",
content: `
export function F1();
export default function F2();
@@ -346,7 +346,7 @@ export default function F2();
};
const reactLibFile = {
path: "/react.ts",
path: "/home/src/workspaces/project/react.ts",
content: `
export const React = {
createElement: (_type, _props, _children) => {},
@@ -359,7 +359,7 @@ export const Other = 1;
// Don't bother to actually emit a baseline for this.
it("NoImports", () => {
const testFile = {
path: "/a.ts",
path: "/home/src/workspaces/project/a.ts",
content: "function F() { }",
};
const languageService = makeLanguageService(testFile);
@@ -369,7 +369,7 @@ export const Other = 1;
it("doesn't crash on shorthand ambient module", () => {
const testFile = {
path: "/a.ts",
path: "/home/src/workspaces/project/a.ts",
content: "declare module '*';",
};
const languageService = makeLanguageService(testFile);
@@ -379,7 +379,7 @@ export const Other = 1;
it("doesn't return any changes when the text would be identical", () => {
const testFile = {
path: "/a.ts",
path: "/home/src/workspaces/project/a.ts",
content: `import { f } from 'foo';\nf();`,
};
const languageService = makeLanguageService(testFile);
@@ -388,35 +388,35 @@ export const Other = 1;
});
testDetectionBaseline("detection1", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { abc, Abc } from 'b';
import { I, M, R } from 'a';
const x = abc + Abc + I + M + R;`,
});
testDetectionBaseline("detection2", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { abc, Abc } from 'a';
import { I, M, R } from 'b';
const x = abc + Abc + I + M + R;`,
});
testDetectionBaseline("detection3", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { I, M, R } from 'a';
import { Abc, abc } from 'b';
const x = abc + Abc + I + M + R;`,
});
testDetectionBaseline("detection4", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { I, M, R } from 'a';
import { abc, Abc } from 'b';
const x = abc + Abc + I + M + R;`,
});
testDetectionBaseline("detection5", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import {
Type9,
Type2,
@@ -441,37 +441,37 @@ console.log(Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, func1
});
testDetectionBaseline("detection6", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { A, B, a, b } from 'foo';
console.log(A, B, a, b);`,
});
testDetectionBaseline("detection7", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { A, a, B, b } from 'foo';
console.log(A, B, a, b);`,
});
testDetectionBaseline("detection8", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { A, a, b, B } from 'foo';
console.log(A, B, a, b);`,
});
testDetectionBaseline("detection9", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { a, b, A, B } from 'foo';
console.log(A, B, a, b);`,
});
testDetectionBaseline("detection10", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `import { a, A, b, B } from 'foo';
console.log(A, B, a, b);`,
});
testOrganizeImports("parseErrors", /*skipDestructiveCodeActions*/ false, {
path: "/test.js",
path: "/home/src/workspaces/project/test.js",
content: `declare module 'mod1' {
declare export type P = {|
|};
@@ -493,7 +493,7 @@ declare module 'mod2' {
});
testOrganizeImports("Renamed_used", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1 as EffOne, F2 as EffTwo } from "lib";
EffOne();
@@ -501,7 +501,7 @@ EffOne();
}, libFile);
testOrganizeImports("Simple", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
import * as NS from "lib";
@@ -515,7 +515,7 @@ F2();
}, libFile);
testOrganizeImports("Unused_Some", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
import * as NS from "lib";
@@ -527,7 +527,7 @@ D();
describe("skipDestructiveCodeActions=true", () => {
testOrganizeImports("Syntax_Error_Body_skipDestructiveCodeActions", /*skipDestructiveCodeActions*/ true, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
import * as NS from "lib";
@@ -540,7 +540,7 @@ D;
});
testOrganizeImports("Syntax_Error_Imports_skipDestructiveCodeActions", /*skipDestructiveCodeActions*/ true, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 class class class; } from "lib";
import * as NS from "lib";
@@ -553,7 +553,7 @@ D;
describe("skipDestructiveCodeActions=false", () => {
testOrganizeImports("Syntax_Error_Body", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
import * as NS from "lib";
@@ -565,7 +565,7 @@ D;
}, libFile);
testOrganizeImports("Syntax_Error_Imports", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 class class class; } from "lib";
import * as NS from "lib";
@@ -579,7 +579,7 @@ D;
it("doesn't return any changes when the text would be identical", () => {
const testFile = {
path: "/a.ts",
path: "/home/src/workspaces/project/a.ts",
content: `import { f } from 'foo';\nf();`,
};
const languageService = makeLanguageService(testFile);
@@ -588,7 +588,7 @@ D;
});
testOrganizeImports("Unused_All", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
import * as NS from "lib";
@@ -598,7 +598,7 @@ import D from "lib";
it("Unused_Empty", () => {
const testFile = {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { } from "lib";
`,
@@ -609,7 +609,7 @@ import { } from "lib";
});
testOrganizeImports("Unused_false_positive_module_augmentation", /*skipDestructiveCodeActions*/ false, {
path: "/test.d.ts",
path: "/home/src/workspaces/project/test.d.ts",
content: `
import foo from 'foo';
import { Caseless } from 'caseless';
@@ -623,7 +623,7 @@ declare module 'caseless' {
});
testOrganizeImports("Unused_preserve_imports_for_module_augmentation_in_non_declaration_file", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import foo from 'foo';
import { Caseless } from 'caseless';
@@ -638,7 +638,7 @@ declare module 'caseless' {
it("Unused_false_positive_shorthand_assignment", () => {
const testFile = {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { x } from "a";
const o = { x };
@@ -651,7 +651,7 @@ const o = { x };
it("Unused_false_positive_export_shorthand", () => {
const testFile = {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { x } from "a";
export { x };
@@ -663,7 +663,7 @@ export { x };
});
testOrganizeImports("MoveToTop", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
F1();
@@ -677,7 +677,7 @@ D();
/* eslint-disable no-template-curly-in-string */
testOrganizeImports("MoveToTop_Invalid", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
F1();
@@ -693,7 +693,7 @@ D();
/* eslint-enable no-template-curly-in-string */
testOrganizeImports("TypeOnly", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { X } from "lib";
import type Y from "lib";
@@ -707,7 +707,7 @@ export { A, B, X, Y, Z };`,
"CoalesceMultipleModules",
/*skipDestructiveCodeActions*/ false,
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { d } from "lib1";
import { b } from "lib1";
@@ -716,12 +716,12 @@ import { a } from "lib2";
a + b + c + d;
`,
},
{ path: "/lib1.ts", content: "export const b = 1, d = 2;" },
{ path: "/lib2.ts", content: "export const a = 3, c = 4;" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "export const b = 1, d = 2;" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "export const a = 3, c = 4;" },
);
testOrganizeImports("CoalesceTrivia", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/import /*B*/ { /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
/*J*/import /*K*/ { /*L*/ F1 /*M*/ } /*N*/ from /*O*/ "lib" /*P*/;/*Q*/ //R
@@ -735,25 +735,25 @@ F2();
"SortTrivia",
/*skipDestructiveCodeActions*/ false,
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/import /*B*/ "lib2" /*C*/;/*D*/ //E
/*F*/import /*G*/ "lib1" /*H*/;/*I*/ //J
`,
},
{ path: "/lib1.ts", content: "" },
{ path: "/lib2.ts", content: "" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "" },
);
testOrganizeImports("UnusedTrivia1", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/import /*B*/ { /*C*/ F1 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
`,
}, libFile);
testOrganizeImports("UnusedTrivia2", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/import /*B*/ { /*C*/ F1 /*D*/, /*E*/ F2 /*F*/ } /*G*/ from /*H*/ "lib" /*I*/;/*J*/ //K
@@ -762,7 +762,7 @@ F1();
}, libFile);
testOrganizeImports("UnusedHeaderComment", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
// Header
import { F1 } from "lib";
@@ -773,19 +773,19 @@ import { F1 } from "lib";
"SortHeaderComment",
/*skipDestructiveCodeActions*/ false,
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
// Header
import "lib2";
import "lib1";
`,
},
{ path: "/lib1.ts", content: "" },
{ path: "/lib2.ts", content: "" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "" },
);
testOrganizeImports("AmbientModule", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
declare module "mod" {
import { F1 } from "lib";
@@ -798,7 +798,7 @@ declare module "mod" {
}, libFile);
testOrganizeImports("TopLevelAndAmbientModule", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import D from "lib";
@@ -818,7 +818,7 @@ D();
}, libFile);
testOrganizeImports("JsxFactoryUsedJsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.jsx",
path: "/home/src/workspaces/project/test.jsx",
content: `
import { React, Other } from "react";
@@ -827,7 +827,7 @@ import { React, Other } from "react";
}, reactLibFile);
testOrganizeImports("JsxFactoryUsedJs", /*skipDestructiveCodeActions*/ false, {
path: "/test.js",
path: "/home/src/workspaces/project/test.js",
content: `
import { React, Other } from "react";
@@ -836,7 +836,7 @@ import { React, Other } from "react";
}, reactLibFile);
testOrganizeImports("JsxFactoryUsedTsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.tsx",
path: "/home/src/workspaces/project/test.tsx",
content: `
import { React, Other } from "react";
@@ -846,7 +846,7 @@ import { React, Other } from "react";
// TS files are not JSX contexts, so the parser does not treat `<div/>` as a JSX element.
testOrganizeImports("JsxFactoryUsedTs", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { React, Other } from "react";
@@ -855,7 +855,7 @@ import { React, Other } from "react";
}, reactLibFile);
testOrganizeImports("JsxFactoryUnusedJsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.jsx",
path: "/home/src/workspaces/project/test.jsx",
content: `
import { React, Other } from "react";
`,
@@ -864,28 +864,28 @@ import { React, Other } from "react";
// Note: Since the file extension does not end with "x", the jsx compiler option
// will not be enabled. The import should be retained regardless.
testOrganizeImports("JsxFactoryUnusedJs", /*skipDestructiveCodeActions*/ false, {
path: "/test.js",
path: "/home/src/workspaces/project/test.js",
content: `
import { React, Other } from "react";
`,
}, reactLibFile);
testOrganizeImports("JsxFactoryUnusedTsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.tsx",
path: "/home/src/workspaces/project/test.tsx",
content: `
import { React, Other } from "react";
`,
}, reactLibFile);
testOrganizeImports("JsxFactoryUnusedTs", /*skipDestructiveCodeActions*/ false, {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { React, Other } from "react";
`,
}, reactLibFile);
testOrganizeImports("JsxPragmaTsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.tsx",
path: "/home/src/workspaces/project/test.tsx",
content: `/** @jsx jsx */
import { Global, jsx } from '@emotion/core';
@@ -894,7 +894,7 @@ import * as React from 'react';
export const App: React.FunctionComponent = _ => <Global><h1>Hello!</h1></Global>
`,
}, {
path: "/@emotion/core/index.d.ts",
path: "/home/src/workspaces/project/@emotion/core/index.d.ts",
content: `import { createElement } from 'react'
export const jsx: typeof createElement;
export function Global(props: any): ReactElement<any>;`,
@@ -909,7 +909,7 @@ export namespace React {
});
testOrganizeImports("JsxFragmentPragmaTsx", /*skipDestructiveCodeActions*/ false, {
path: "/test.tsx",
path: "/home/src/workspaces/project/test.tsx",
content: `/** @jsx h */
/** @jsxFrag frag */
import { h, frag } from "@foo/core";
@@ -917,7 +917,7 @@ import { h, frag } from "@foo/core";
const elem = <><div>Foo</div></>;
`,
}, {
path: "/@foo/core/index.d.ts",
path: "/home/src/workspaces/project/@foo/core/index.d.ts",
content: `export function h(): void;
export function frag(): void;
`,
@@ -925,7 +925,7 @@ export function frag(): void;
describe("Exports", () => {
testOrganizeExports("MoveToTop", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
export { F1, F2 } from "lib";
1;
@@ -936,7 +936,7 @@ export * from "lib";
/* eslint-disable no-template-curly-in-string */
testOrganizeExports("MoveToTop_Invalid", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
export { F1, F2 } from "lib";
1;
@@ -951,7 +951,7 @@ export { D } from "lib";
/* eslint-enable no-template-curly-in-string */
testOrganizeExports("MoveToTop_WithImportsFirst", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
import { F1, F2 } from "lib";
1;
@@ -966,7 +966,7 @@ F1(); F2(); NS.F1();
}, libFile);
testOrganizeExports("MoveToTop_WithExportsFirst", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
export { F1, F2 } from "lib";
1;
@@ -983,7 +983,7 @@ F1(); F2(); NS.F1();
testOrganizeExports(
"CoalesceMultipleModules",
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
export { d } from "lib1";
export { b } from "lib1";
@@ -991,12 +991,12 @@ export { c } from "lib2";
export { a } from "lib2";
`,
},
{ path: "/lib1.ts", content: "export const b = 1, d = 2;" },
{ path: "/lib2.ts", content: "export const a = 3, c = 4;" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "export const b = 1, d = 2;" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "export const a = 3, c = 4;" },
);
testOrganizeExports("CoalesceTrivia", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/export /*B*/ { /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
/*J*/export /*K*/ { /*L*/ F1 /*M*/ } /*N*/ from /*O*/ "lib" /*P*/;/*Q*/ //R
@@ -1006,32 +1006,32 @@ export { a } from "lib2";
testOrganizeExports(
"SortTrivia",
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
/*A*/export /*B*/ * /*C*/ from /*D*/ "lib2" /*E*/;/*F*/ //G
/*H*/export /*I*/ * /*J*/ from /*K*/ "lib1" /*L*/;/*M*/ //N
`,
},
{ path: "/lib1.ts", content: "" },
{ path: "/lib2.ts", content: "" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "" },
);
testOrganizeExports(
"SortHeaderComment",
{
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
// Header
export * from "lib2";
export * from "lib1";
`,
},
{ path: "/lib1.ts", content: "" },
{ path: "/lib2.ts", content: "" },
{ path: "/home/src/workspaces/project/lib1.ts", content: "" },
{ path: "/home/src/workspaces/project/lib2.ts", content: "" },
);
testOrganizeExports("AmbientModule", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
declare module "mod" {
export { F1 } from "lib";
@@ -1042,7 +1042,7 @@ declare module "mod" {
}, libFile);
testOrganizeExports("TopLevelAndAmbientModule", {
path: "/test.ts",
path: "/home/src/workspaces/project/test.ts",
content: `
export { D } from "lib";
@@ -1120,7 +1120,7 @@ export * from "lib";
}
function makeLanguageService(...files: File[]) {
const host = createServerHost(files);
const host = TestServerHost.createServerHost(files);
const projectService = new TestProjectService({ host, useSingleInferredProject: true });
projectService.setCompilerOptionsForInferredProjects({ jsx: files.some(f => f.path.endsWith("x")) ? ts.JsxEmit.React : ts.JsxEmit.None });
files.forEach(f => projectService.openClientFile(f.path));
+33 -26
View File
@@ -10,9 +10,9 @@ import {
Deferred,
} from "../../_namespaces/Utils.js";
import {
createWatchedSystem,
FileOrFolderOrSymLinkMap,
osFlavorToString,
TestServerHost,
TestServerHostOsFlavor,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: sys:: symlinkWatching::", () => {
@@ -27,7 +27,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
function verifyWatchFile(
scenario: string,
sys: ts.System,
getSys: () => ts.System,
file: string,
link: string,
watchOptions: Pick<ts.WatchOptions, "watchFile">,
@@ -35,6 +35,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
) {
if (skipSysTests) return;
it(scenario, async () => {
const sys = getSys();
const fileResult = watchFile(file);
const linkResult = watchFile(link);
@@ -211,7 +212,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
linkFileDelete: readonly ExpectedEventAndFileName[];
}
function verifyWatchDirectoryUsingFsEvents<System extends ts.System>(
sys: System,
getSys: () => System,
fsWatch: FsWatch<System>,
dir: string,
link: string,
@@ -219,6 +220,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
) {
if (skipSysTests) return;
it(`watchDirectory using fsEvents ${osFlavorToString(osFlavor)}`, async () => {
const sys = getSys();
const tableOfEvents: FsEventsForWatchDirectory = osFlavor === TestServerHostOsFlavor.MacOs ?
{
fileCreate: [
@@ -328,7 +330,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
],
);
function operation(opType: keyof FsEventsForWatchDirectory) {
function operation(sys: System, opType: keyof FsEventsForWatchDirectory) {
switch (opType) {
case "init":
sys.writeFile(`${dir}/init.ts`, "export const x = 100;");
@@ -376,7 +378,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
parallelLinkFileDelete: readonly ExpectedEventAndFileName[] | undefined;
}
function verifyRecursiveWatchDirectoryUsingFsEvents<System extends ts.System>(
sys: System,
getSys: () => System,
fsWatch: FsWatch<System>,
dir: string,
link: string,
@@ -495,6 +497,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
};
it(`recursive watchDirectory using fsEvents ${osFlavorToString(osFlavor)}`, async () => {
const sys = getSys();
await testWatchDirectoryOperations(
sys,
fsWatch,
@@ -518,6 +521,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
it(`recursive watchDirectory using fsEvents when linked in same folder ${osFlavorToString(osFlavor)}`, async () => {
const sys = getSys();
await testWatchDirectoryOperations(
sys,
fsWatch,
@@ -537,6 +541,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
it(`recursive watchDirectory using fsEvents when links not in directory ${osFlavorToString(osFlavor)}`, async () => {
const sys = getSys();
await testWatchDirectoryOperations(
sys,
fsWatch,
@@ -560,6 +565,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
function watchDirectoryOperation(
sys: System,
opType: keyof RecursiveFsEventsForWatchDirectory,
dir: string,
link: string,
@@ -614,12 +620,12 @@ describe("unittests:: sys:: symlinkWatching::", () => {
}
type EventRecord = Record<string, readonly ExpectedEventAndFileName[] | undefined>;
type Operation<Events extends EventRecord> = (opType: keyof Events, dir: string, link: string) => void;
type Operation<System extends ts.System, Events extends EventRecord> = (sys: System, opType: keyof Events, dir: string, link: string) => void;
async function testWatchDirectoryOperations<System extends ts.System, Events extends EventRecord>(
sys: System,
fsWatch: FsWatch<System>,
tableOfEvents: Events,
operation: Operation<Events>,
operation: Operation<System, Events>,
directoryName: string,
linkName: string,
recursive: boolean,
@@ -629,24 +635,25 @@ describe("unittests:: sys:: symlinkWatching::", () => {
const linkResult = watchDirectory(sys, fsWatch, linkName, recursive);
for (const opType of opTypes) {
await watchDirectoryOperation(tableOfEvents, opType, operation, directoryName, linkName, dirResult, linkResult);
await watchDirectoryOperation(sys, tableOfEvents, opType, operation, directoryName, linkName, dirResult, linkResult);
}
dirResult.watcher.close();
linkResult.watcher.close();
}
async function watchDirectoryOperation<Events extends EventRecord>(
async function watchDirectoryOperation<System extends ts.System, Events extends EventRecord>(
sys: System,
tableOfEvents: Events,
opType: keyof Events & string,
operation: Operation<Events>,
operation: Operation<System, Events>,
directoryName: string,
linkName: string,
dirResult: WatchDirectoryResult,
linkResult: WatchDirectoryResult,
) {
initializeWatchDirectoryResult(dirResult, linkResult);
operation(opType, directoryName, linkName);
operation(sys, opType, directoryName, linkName);
await verfiyWatchDirectoryResult(
opType,
dirResult,
@@ -688,7 +695,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
verifyWatchFile(
"watchFile using polling",
ts.sys,
() => ts.sys,
`${root}/polling/file.ts`,
`${root}/linkedpolling/file.ts`,
{ watchFile: ts.WatchFileKind.PriorityPollingInterval },
@@ -701,7 +708,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
verifyWatchFile(
"watchFile using fsEvents",
ts.sys,
() => ts.sys,
`${root}/fsevents/file.ts`,
`${root}/linkedfsevents/file.ts`,
{ watchFile: ts.WatchFileKind.UseFsEvents },
@@ -714,7 +721,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
verifyWatchFile(
"watchDirectory using polling",
ts.sys,
() => ts.sys,
`${root}/dirpolling`,
`${root}/linkeddirpolling`,
{ watchFile: ts.WatchFileKind.PriorityPollingInterval },
@@ -727,7 +734,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
withSwallowException(() => fs.symlinkSync(`${root}/dirfsevents`, `${root}/linkeddirfsevents`, "junction"));
});
verifyWatchDirectoryUsingFsEvents(
ts.sys,
() => ts.sys,
(dir, _recursive, cb) => fs.watch(dir, { persistent: true }, cb),
`${root}/dirfsevents`,
`${root}/linkeddirfsevents`,
@@ -743,7 +750,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
setupRecursiveFsEvents("recursivefseventsparallel");
});
verifyRecursiveWatchDirectoryUsingFsEvents(
ts.sys,
() => ts.sys,
(dir, recursive, cb) => fs.watch(dir, { persistent: true, recursive }, cb),
`${root}/recursivefsevents`,
`${root}/linkedrecursivefsevents`,
@@ -762,23 +769,23 @@ describe("unittests:: sys:: symlinkWatching::", () => {
});
describe("with virtualFileSystem::", () => {
const root = ts.normalizePath("/tests/baselines/symlinks");
const root = ts.normalizePath("/home/tests/baselines/symlinks");
function getSys(osFlavor?: TestServerHostOsFlavor) {
return createWatchedSystem({
return TestServerHost.createWatchedSystem({
[`${root}/folder/file.ts`]: "export const x = 10;",
[`${root}/linked`]: { symLink: `${root}/folder` },
}, { osFlavor });
}, { osFlavor, currentDirectory: root });
}
verifyWatchFile(
"watchFile using polling",
getSys(),
getSys,
`${root}/folder/file.ts`,
`${root}/linked/file.ts`,
{ watchFile: ts.WatchFileKind.PriorityPollingInterval },
);
verifyWatchFile(
"watchFile using fsEvents",
getSys(),
getSys,
`${root}/folder/file.ts`,
`${root}/linked/file.ts`,
{ watchFile: ts.WatchFileKind.UseFsEvents },
@@ -786,7 +793,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
verifyWatchFile(
"watchDirectory using polling",
getSys(),
getSys,
`${root}/folder`,
`${root}/linked`,
{ watchFile: ts.WatchFileKind.PriorityPollingInterval },
@@ -795,7 +802,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
function verifyWatchDirectoryUsingFsEventsTestServerHost(osFlavor: TestServerHostOsFlavor) {
verifyWatchDirectoryUsingFsEvents(
getSys(osFlavor),
() => getSys(osFlavor),
(dir, recursive, cb, sys) => sys.fsWatchWorker(dir, recursive, cb),
`${root}/folder`,
`${root}/linked`,
@@ -807,11 +814,11 @@ describe("unittests:: sys:: symlinkWatching::", () => {
verifyWatchDirectoryUsingFsEventsTestServerHost(TestServerHostOsFlavor.Linux);
function getRecursiveSys(osFlavor: TestServerHostOsFlavor) {
return createWatchedSystem({
return TestServerHost.createWatchedSystem({
...getRecursiveFs("recursivefsevents"),
...getRecursiveFs("recursivefseventssub"),
...getRecursiveFs("recursivefseventsparallel"),
}, { osFlavor });
}, { osFlavor, currentDirectory: root });
function getRecursiveFs(recursiveName: string): FileOrFolderOrSymLinkMap {
return {
@@ -826,7 +833,7 @@ describe("unittests:: sys:: symlinkWatching::", () => {
function verifyRecursiveWatchDirectoryUsingFsEventsTestServerHost(osFlavor: TestServerHostOsFlavor.Windows | TestServerHostOsFlavor.MacOs) {
verifyRecursiveWatchDirectoryUsingFsEvents(
getRecursiveSys(osFlavor),
() => getRecursiveSys(osFlavor),
(dir, recursive, cb, sys) => sys.fsWatchWorker(dir, recursive, cb),
`${root}/recursivefsevents`,
`${root}/linkedrecursivefsevents`,
@@ -1,22 +1,17 @@
import { dedent } from "../../_namespaces/Utils.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: outFile:: on amd modules with --out", () => {
function outFileFs(prepend?: boolean) {
return loadProjectFromFiles({
"/src/app/file3.ts": dedent`
describe("unittests:: tsbuild:: outFile:: amdModulesWithOut:: on amd modules with --out", () => {
function outFileSys(prepend?: boolean) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/soltion/app/file3.ts": dedent`
export const z = 30;
import { x } from "file1";
`,
"/src/app/file4.ts": `const myVar = 30;`,
"/src/app/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/soltion/app/file4.ts": `const myVar = 30;`,
"/home/src/workspaces/soltion/app/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "amd",
@@ -31,11 +26,11 @@ describe("unittests:: tsbuild:: outFile:: on amd modules with --out", () => {
{ path: "../lib", prepend },
],
}),
"/src/lib/file0.ts": `const myGlob = 20;`,
"/src/lib/file1.ts": `export const x = 10;`,
"/src/lib/file2.ts": `export const y = 20;`,
"/src/lib/global.ts": `const globalConst = 10;`,
"/src/lib/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/soltion/lib/file0.ts": `const myGlob = 20;`,
"/home/src/workspaces/soltion/lib/file1.ts": `export const x = 10;`,
"/home/src/workspaces/soltion/lib/file2.ts": `export const y = 20;`,
"/home/src/workspaces/soltion/lib/global.ts": `const globalConst = 10;`,
"/home/src/workspaces/soltion/lib/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "amd",
@@ -47,47 +42,45 @@ describe("unittests:: tsbuild:: outFile:: on amd modules with --out", () => {
},
exclude: ["module.d.ts"],
}),
});
}, { currentDirectory: "/home/src/workspaces/soltion" });
}
verifyTsc({
scenario: "amdModulesWithOut",
subScenario: "modules and globals mixed in amd",
fs: outFileFs,
commandLineArgs: ["--b", "/src/app", "--verbose"],
sys: outFileSys,
commandLineArgs: ["--b", "app", "--verbose"],
baselineSourceMap: true,
edits: [{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/lib/file1.ts", "console.log(x);"),
edit: sys => sys.appendFile("/home/src/workspaces/soltion/lib/file1.ts", "console.log(x);"),
}],
});
verifyTsc({
scenario: "amdModulesWithOut",
subScenario: "prepend reports deprecation error",
fs: () => outFileFs(/*prepend*/ true),
commandLineArgs: ["--b", "/src/app", "--verbose"],
sys: () => outFileSys(/*prepend*/ true),
commandLineArgs: ["--b", "app", "--verbose"],
baselineSourceMap: true,
edits: [{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/lib/file1.ts", "console.log(x);"),
edit: sys => sys.appendFile("/home/src/workspaces/soltion/lib/file1.ts", "console.log(x);"),
}],
});
describe("when the module resolution finds original source file", () => {
function modifyFs(fs: vfs.FileSystem) {
// Make lib to output to parent dir
replaceText(fs, "/src/lib/tsconfig.json", `"outFile": "module.js"`, `"outFile": "../module.js", "rootDir": "../"`);
// Change reference to file1 module to resolve to lib/file1
replaceText(fs, "/src/app/file3.ts", "file1", "lib/file1");
}
verifyTsc({
scenario: "amdModulesWithOut",
subScenario: "when the module resolution finds original source file",
fs: outFileFs,
commandLineArgs: ["-b", "/src/app", "--verbose"],
modifyFs,
sys: outFileSys,
commandLineArgs: ["-b", "app", "--verbose"],
modifySystem: sys => {
// Make lib to output to parent dir
sys.replaceFileText("/home/src/workspaces/soltion/lib/tsconfig.json", `"outFile": "module.js"`, `"outFile": "../module.js", "rootDir": "../"`);
// Change reference to file1 module to resolve to lib/file1
sys.replaceFileText("/home/src/workspaces/soltion/app/file3.ts", "file1", "lib/file1");
},
baselineSourceMap: true,
});
});
+15 -15
View File
@@ -4,41 +4,41 @@ import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild - clean::", () => {
verifyTsc({
scenario: "clean",
subScenario: `file name and output name clashing`,
commandLineArgs: ["--b", "/src/tsconfig.json", "-clean"],
fs: () =>
loadProjectFromFiles({
"/src/index.js": "",
"/src/bar.ts": "",
"/src/tsconfig.json": jsonToReadableText({
commandLineArgs: ["--b", "-clean"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/index.js": "",
"/home/src/workspaces/solution/bar.ts": "",
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
compilerOptions: { allowJs: true },
}),
}),
}, { currentDirectory: "/home/src/workspaces/solution" }),
});
verifyTsc({
scenario: "clean",
subScenario: "tsx with dts emit",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.tsx": "export const x = 10;",
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/project/src/main.tsx": "export const x = 10;",
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
compilerOptions: { declaration: true },
include: ["src/**/*.tsx", "src/**/*.ts"],
}),
}),
commandLineArgs: ["--b", "src/project", "-v", "--explainFiles"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "project", "-v", "--explainFiles"],
edits: [
noChangeRun,
{
caption: "clean build",
edit: noop,
commandLineArgs: ["-b", "/src/project", "--clean"],
commandLineArgs: ["-b", "project", "--clean"],
},
],
});
+63 -67
View File
@@ -6,11 +6,7 @@ import {
TestTscEdit,
verifyTsc,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: commandLine::", () => {
function scenarioName(text: string, options: ts.CompilerOptions) {
@@ -21,7 +17,7 @@ describe("unittests:: tsbuild:: commandLine::", () => {
return {
caption,
edit: ts.noop,
commandLineArgs: ["--b", "/src/project", "--verbose", ...options],
commandLineArgs: ["--b", "--verbose", ...options],
};
}
function noChangeWithSubscenario(caption: string): TestTscEdit {
@@ -48,24 +44,24 @@ describe("unittests:: tsbuild:: commandLine::", () => {
function localChange(): TestTscEdit {
return {
caption: "local change",
edit: fs => replaceText(fs, "/src/project/a.ts", "Local = 1", "Local = 10"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "Local = 1", "Local = 10"),
};
}
function fs(options: ts.CompilerOptions) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/src/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project/d.ts": `import { b } from "./b";export const d = b;`,
});
function sys(options: ts.CompilerOptions) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/home/src/workspaces/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/home/src/workspaces/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/home/src/workspaces/project/c.ts": `import { a } from "./a";export const c = a;`,
"/home/src/workspaces/project/d.ts": `import { b } from "./b";export const d = b;`,
}, { currentDirectory: "/home/src/workspaces/project" });
}
function verify(options: ts.CompilerOptions) {
verifyTsc({
scenario: "commandLine",
subScenario: scenarioName("different options", options),
fs: () => fs({ composite: true, ...options }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
sys: () => sys({ composite: true, ...options }),
commandLineArgs: ["--b", "--verbose"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
@@ -85,8 +81,8 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verifyTsc({
scenario: "commandLine",
subScenario: scenarioName("different options with incremental", options),
fs: () => fs({ incremental: true, ...options }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
sys: () => sys({ incremental: true, ...options }),
commandLineArgs: ["--b", "--verbose"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
withOptionChange("should re-emit only js so they dont contain sourcemap"),
@@ -109,44 +105,44 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verify({ outFile: "../outFile.js", module: ts.ModuleKind.AMD });
});
describe("emitDeclarationOnly::", () => {
function fs(options: ts.CompilerOptions) {
return loadProjectFromFiles({
"/src/project1/src/tsconfig.json": jsonToReadableText({
function sys(options: ts.CompilerOptions) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/project1/src/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(options),
}),
"/src/project1/src/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project1/src/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project1/src/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project1/src/d.ts": `import { b } from "./b";export const d = b;`,
"/src/project2/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project1/src/a.ts": `export const a = 10;const aLocal = 10;`,
"/home/src/workspaces/solution/project1/src/b.ts": `export const b = 10;const bLocal = 10;`,
"/home/src/workspaces/solution/project1/src/c.ts": `import { a } from "./a";export const c = a;`,
"/home/src/workspaces/solution/project1/src/d.ts": `import { b } from "./b";export const d = b;`,
"/home/src/workspaces/solution/project2/src/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(options),
references: [{ path: "../../project1/src" }],
}),
"/src/project2/src/e.ts": `export const e = 10;`,
"/src/project2/src/f.ts": `import { a } from "${options.outFile ? "a" : "../../project1/src/a"}"; export const f = a;`,
"/src/project2/src/g.ts": `import { b } from "${options.outFile ? "b" : "../../project1/src/b"}"; export const g = b;`,
});
"/home/src/workspaces/solution/project2/src/e.ts": `export const e = 10;`,
"/home/src/workspaces/solution/project2/src/f.ts": `import { a } from "${options.outFile ? "a" : "../../project1/src/a"}"; export const f = a;`,
"/home/src/workspaces/solution/project2/src/g.ts": `import { b } from "${options.outFile ? "b" : "../../project1/src/b"}"; export const g = b;`,
}, { currentDirectory: "/home/src/workspaces/solution" });
}
function verifyWithIncremental(options: ts.CompilerOptions) {
verifyTsc({
scenario: "commandLine",
subScenario: subScenario("emitDeclarationOnly on commandline"),
fs: () => fs(options),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly"],
sys: () => sys(options),
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly"],
edits: [
noChangeRun,
{
caption: "local change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "const aa = 10;"),
},
{
caption: "non local change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "export const aaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "export const aaa = 10;"),
},
{
caption: "emit js files",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
{
...noChangeRun,
@@ -157,12 +153,12 @@ describe("unittests:: tsbuild:: commandLine::", () => {
},
{
caption: "js emit with change without emitDeclarationOnly",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
{
caption: "local change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const aaaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const aaaa = 10;"),
discrepancyExplanation: () => [
`Clean build tsbuildinfo for project2 will have compilerOptions with composite and emitDeclarationOnly`,
`Incremental build will detect that it doesnt need to rebuild project2 so tsbuildinfo for it is from before which has option composite only`,
@@ -170,12 +166,12 @@ describe("unittests:: tsbuild:: commandLine::", () => {
},
{
caption: "non local change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "export const aaaaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "export const aaaaa = 10;"),
},
{
caption: "js emit with change without emitDeclarationOnly",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
],
baselinePrograms: true,
@@ -183,18 +179,18 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verifyTsc({
scenario: "commandLine",
subScenario: subScenario("emitDeclarationOnly false on commandline"),
fs: () => fs({ ...options, emitDeclarationOnly: true }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
sys: () => sys({ ...options, emitDeclarationOnly: true }),
commandLineArgs: ["--b", "project2/src", "--verbose"],
edits: [
noChangeRun,
{
caption: "change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "const aa = 10;"),
},
{
caption: "emit js files",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
...noChangeRun,
@@ -206,12 +202,12 @@ describe("unittests:: tsbuild:: commandLine::", () => {
{
caption: "no change run with js emit",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
caption: "js emit with change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
],
baselinePrograms: true,
@@ -229,41 +225,41 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verifyTsc({
scenario: "commandLine",
subScenario: scenarioName("emitDeclarationOnly on commandline with declaration", options),
fs: () => fs({ declaration: true, ...options }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly"],
sys: () => sys({ declaration: true, ...options }),
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly"],
edits: [
noChangeRun,
{
caption: "local change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "const aa = 10;"),
},
{
caption: "non local change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "export const aaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "export const aaa = 10;"),
},
{
caption: "emit js files",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
noChangeRun,
{
caption: "js emit with change without emitDeclarationOnly",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
{
caption: "local change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const aaaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const aaaa = 10;"),
},
{
caption: "non local change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "export const aaaaa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "export const aaaaa = 10;"),
},
{
caption: "js emit with change without emitDeclarationOnly",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose"],
},
],
baselinePrograms: true,
@@ -272,29 +268,29 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verifyTsc({
scenario: "commandLine",
subScenario: scenarioName("emitDeclarationOnly false on commandline with declaration", options),
fs: () => fs({ declaration: true, emitDeclarationOnly: true, ...options }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
sys: () => sys({ declaration: true, emitDeclarationOnly: true, ...options }),
commandLineArgs: ["--b", "project2/src", "--verbose"],
edits: [
noChangeRun,
{
caption: "change",
edit: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/a.ts", "const aa = 10;"),
},
{
caption: "emit js files",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
noChangeRun,
{
caption: "no change run with js emit",
edit: ts.noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
caption: "js emit with change",
edit: fs => appendText(fs, "/src/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
],
baselinePrograms: true,
@@ -4,31 +4,27 @@ import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: configFileErrors:: when tsconfig extends the missing file", () => {
verifyTsc({
scenario: "configFileErrors",
subScenario: "when tsconfig extends the missing file",
fs: () =>
loadProjectFromFiles({
"/src/tsconfig.first.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.first.json": jsonToReadableText({
extends: "./foobar.json",
compilerOptions: {
composite: true,
},
}),
"/src/tsconfig.second.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.second.json": jsonToReadableText({
extends: "./foobar.json",
compilerOptions: {
composite: true,
},
}),
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
@@ -37,8 +33,8 @@ describe("unittests:: tsbuild:: configFileErrors:: when tsconfig extends the mis
{ path: "./tsconfig.second.json" },
],
}),
}),
commandLineArgs: ["--b", "/src/tsconfig.json"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--b"],
});
});
@@ -47,11 +43,11 @@ describe("unittests:: tsbuild:: configFileErrors:: reports syntax errors in conf
verifyTsc({
scenario: "configFileErrors",
subScenario: `${outFile ? "outFile" : "multiFile"}/reports syntax errors in config file`,
fs: () =>
loadProjectFromFiles({
"/src/a.ts": "export function foo() { }",
"/src/b.ts": "export function bar() { }",
"/src/tsconfig.json": dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a.ts": "export function foo() { }",
"/home/src/workspaces/project/b.ts": "export function bar() { }",
"/home/src/workspaces/project/tsconfig.json": dedent`
{
"compilerOptions": {
"composite": true,${outFile ? jsonToReadableText(outFile).replace(/[{}]/g, "") : ""}
@@ -61,14 +57,13 @@ describe("unittests:: tsbuild:: configFileErrors:: reports syntax errors in conf
"b.ts"
]
}`,
}),
commandLineArgs: ["--b", "/src/tsconfig.json"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--b"],
edits: [
{
edit: fs =>
replaceText(
fs,
"/src/tsconfig.json",
edit: sys =>
sys.replaceFileText(
"/home/src/workspaces/project/tsconfig.json",
",",
`,
"declaration": true,`,
@@ -80,14 +75,14 @@ describe("unittests:: tsbuild:: configFileErrors:: reports syntax errors in conf
],
},
{
edit: fs => appendText(fs, "/src/a.ts", "export function fooBar() { }"),
edit: sys => sys.appendFile("/home/src/workspaces/project/a.ts", "export function fooBar() { }"),
caption: "reports syntax errors after change to ts file",
},
noChangeRun,
{
edit: fs =>
fs.writeFileSync(
"/src/tsconfig.json",
edit: sys =>
sys.writeFile(
"/home/src/workspaces/project/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, declaration: true, ...outFile },
files: ["a.ts", "b.ts"],
@@ -1,22 +1,22 @@
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: configFileExtends:: when tsconfig extends another config", () => {
function getConfigExtendsWithIncludeFs() {
return loadProjectFromFiles({
"/src/tsconfig.json": jsonToReadableText({
function getConfigExtendsWithIncludeSys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
references: [
{ path: "./shared/tsconfig.json" },
{ path: "./webpack/tsconfig.json" },
],
files: [],
}),
"/src/shared/tsconfig-base.json": jsonToReadableText({
"/home/src/workspaces/solution/shared/tsconfig-base.json": jsonToReadableText({
include: ["./typings-base/"],
}),
"/src/shared/typings-base/globals.d.ts": `type Unrestricted = any;`,
"/src/shared/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/shared/typings-base/globals.d.ts": `type Unrestricted = any;`,
"/home/src/workspaces/solution/shared/tsconfig.json": jsonToReadableText({
extends: "./tsconfig-base.json",
compilerOptions: {
composite: true,
@@ -25,8 +25,8 @@ describe("unittests:: tsbuild:: configFileExtends:: when tsconfig extends anothe
},
files: ["./index.ts"],
}),
"/src/shared/index.ts": `export const a: Unrestricted = 1;`,
"/src/webpack/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/shared/index.ts": `export const a: Unrestricted = 1;`,
"/home/src/workspaces/solution/webpack/tsconfig.json": jsonToReadableText({
extends: "../shared/tsconfig-base.json",
compilerOptions: {
composite: true,
@@ -36,19 +36,19 @@ describe("unittests:: tsbuild:: configFileExtends:: when tsconfig extends anothe
files: ["./index.ts"],
references: [{ path: "../shared/tsconfig.json" }],
}),
"/src/webpack/index.ts": `export const b: Unrestricted = 1;`,
});
"/home/src/workspaces/solution/webpack/index.ts": `export const b: Unrestricted = 1;`,
}, { currentDirectory: "/home/src/workspaces/solution" });
}
verifyTsc({
scenario: "configFileExtends",
subScenario: "when building solution with projects extends config with include",
fs: getConfigExtendsWithIncludeFs,
commandLineArgs: ["--b", "/src/tsconfig.json", "--v", "--listFiles"],
sys: getConfigExtendsWithIncludeSys,
commandLineArgs: ["--b", "--v", "--listFiles"],
});
verifyTsc({
scenario: "configFileExtends",
subScenario: "when building project uses reference and both extend config with include",
fs: getConfigExtendsWithIncludeFs,
commandLineArgs: ["--b", "/src/webpack/tsconfig.json", "--v", "--listFiles"],
sys: getConfigExtendsWithIncludeSys,
commandLineArgs: ["--b", "webpack/tsconfig.json", "--v", "--listFiles"],
});
});
@@ -3,32 +3,29 @@ import {
noChangeOnlyRuns,
verifyTsc,
} from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: when containerOnly project is referenced", () => {
verifyTsc({
scenario: "containerOnlyReferenced",
subScenario: "verify that subsequent builds after initial build doesnt build anything",
fs: () =>
loadProjectFromFiles({
"/src/src/folder/index.ts": `export const x = 10;`,
"/src/src/folder/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/src/folder/index.ts": `export const x = 10;`,
"/home/src/workspaces/solution/src/folder/tsconfig.json": jsonToReadableText({
files: ["index.ts"],
compilerOptions: {
composite: true,
},
}),
"/src/src/folder2/index.ts": `export const x = 10;`,
"/src/src/folder2/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/folder2/index.ts": `export const x = 10;`,
"/home/src/workspaces/solution/src/folder2/tsconfig.json": jsonToReadableText({
files: ["index.ts"],
compilerOptions: {
composite: true,
},
}),
"/src/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/tsconfig.json": jsonToReadableText({
files: [],
compilerOptions: {
composite: true,
@@ -38,8 +35,8 @@ describe("unittests:: tsbuild:: when containerOnly project is referenced", () =>
{ path: "./folder2" },
],
}),
"/src/tests/index.ts": `export const x = 10;`,
"/src/tests/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/tests/index.ts": `export const x = 10;`,
"/home/src/workspaces/solution/tests/tsconfig.json": jsonToReadableText({
files: ["index.ts"],
compilerOptions: {
composite: true,
@@ -48,7 +45,7 @@ describe("unittests:: tsbuild:: when containerOnly project is referenced", () =>
{ path: "../src" },
],
}),
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
files: [],
compilerOptions: {
composite: true,
@@ -58,40 +55,40 @@ describe("unittests:: tsbuild:: when containerOnly project is referenced", () =>
{ path: "./tests" },
],
}),
}),
commandLineArgs: ["--b", "/src", "--verbose"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "--verbose"],
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "containerOnlyReferenced",
subScenario: "when solution is referenced indirectly",
fs: () =>
loadProjectFromFiles({
"/src/project1/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/project1/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [],
}),
"/src/project2/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project2/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [],
}),
"/src/project2/src/b.ts": "export const b = 10;",
"/src/project3/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project2/src/b.ts": "export const b = 10;",
"/home/src/workspaces/solution/project3/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [{ path: "../project1" }, { path: "../project2" }],
}),
"/src/project3/src/c.ts": "export const c = 10;",
"/src/project4/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project3/src/c.ts": "export const c = 10;",
"/home/src/workspaces/solution/project4/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [{ path: "../project3" }],
}),
"/src/project4/src/d.ts": "export const d = 10;",
}),
commandLineArgs: ["--b", "/src/project4", "--verbose", "--explainFiles"],
"/home/src/workspaces/solution/project4/src/d.ts": "export const d = 10;",
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "project4", "--verbose", "--explainFiles"],
edits: [{
caption: "modify project3 file",
edit: fs => replaceText(fs, "/src/project3/src/c.ts", "c = ", "cc = "),
edit: sys => sys.replaceFileText("/home/src/workspaces/solution/project3/src/c.ts", "c = ", "cc = "),
}],
});
});
@@ -1,48 +1,50 @@
import { dedent } from "../../_namespaces/Utils.js";
import { FileSet } from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { forEachDeclarationEmitWithErrorsScenario } from "../helpers/declarationEmit.js";
import {
noChangeOnlyRuns,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import {
FileOrFolderOrSymLinkMap,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: declarationEmit", () => {
function getFiles(): FileSet {
return {
"/src/solution/tsconfig.base.json": jsonToReadableText({
describe("unittests:: tsbuild:: declarationEmit::", () => {
function sys(additionalFiles?: FileOrFolderOrSymLinkMap) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/tsconfig.base.json": jsonToReadableText({
compilerOptions: {
rootDir: "./",
outDir: "lib",
},
}),
"/src/solution/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [{ path: "./src" }],
include: [],
}),
"/src/solution/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
references: [{ path: "./subProject" }, { path: "./subProject2" }],
include: [],
}),
"/src/solution/src/subProject/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/subProject/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: { composite: true },
references: [{ path: "../common" }],
include: ["./index.ts"],
}),
"/src/solution/src/subProject/index.ts": dedent`
"/home/src/workspaces/solution/src/subProject/index.ts": dedent`
import { Nominal } from '../common/nominal';
export type MyNominal = Nominal<string, 'MyNominal'>;`,
"/src/solution/src/subProject2/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/subProject2/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: { composite: true },
references: [{ path: "../subProject" }],
include: ["./index.ts"],
}),
"/src/solution/src/subProject2/index.ts": dedent`
"/home/src/workspaces/solution/src/subProject2/index.ts": dedent`
import { MyNominal } from '../subProject/index';
const variable = {
key: 'value' as MyNominal,
@@ -50,89 +52,89 @@ const variable = {
export function getVar(): keyof typeof variable {
return 'key';
}`,
"/src/solution/src/common/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/common/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: { composite: true },
include: ["./nominal.ts"],
}),
"/src/solution/src/common/nominal.ts": dedent`
"/home/src/workspaces/solution/src/common/nominal.ts": dedent`
/// <reference path="./types.d.ts" preserve="true" />
export declare type Nominal<T, Name extends string> = MyNominal<T, Name>;`,
"/src/solution/src/common/types.d.ts": dedent`
"/home/src/workspaces/solution/src/common/types.d.ts": dedent`
declare type MyNominal<T, Name extends string> = T & {
specialKey: Name;
};`,
};
...additionalFiles,
}, { currentDirectory: "/home/src/workspaces/solution" });
}
verifyTsc({
scenario: "declarationEmit",
subScenario: "when declaration file is referenced through triple slash",
fs: () => loadProjectFromFiles(getFiles()),
commandLineArgs: ["--b", "/src/solution/tsconfig.json", "--verbose"],
sys,
commandLineArgs: ["--b", "--verbose"],
});
verifyTsc({
scenario: "declarationEmit",
subScenario: "when declaration file is referenced through triple slash but uses no references",
fs: () =>
loadProjectFromFiles({
...getFiles(),
"/src/solution/tsconfig.json": jsonToReadableText({
sys: () =>
sys({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
extends: "./tsconfig.base.json",
compilerOptions: { composite: true },
include: ["./src/**/*.ts"],
}),
}),
commandLineArgs: ["--b", "/src/solution/tsconfig.json", "--verbose"],
commandLineArgs: ["--b", "--verbose"],
});
verifyTsc({
scenario: "declarationEmit",
subScenario: "when declaration file used inferred type from referenced project",
fs: () =>
loadProjectFromFiles({
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
baseUrl: ".",
paths: { "@fluentui/*": ["packages/*/src"] },
},
}),
"/src/packages/pkg1/src/index.ts": dedent`
"/home/src/workspaces/project/packages/pkg1/src/index.ts": dedent`
export interface IThing {
a: string;
}
export interface IThings {
thing1: IThing;
}`,
"/src/packages/pkg1/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/packages/pkg1/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig",
compilerOptions: { outDir: "lib" },
include: ["src"],
}),
"/src/packages/pkg2/src/index.ts": dedent`
"/home/src/workspaces/project/packages/pkg2/src/index.ts": dedent`
import { IThings } from '@fluentui/pkg1';
export function fn4() {
const a: IThings = { thing1: { a: 'b' } };
return a.thing1;
}`,
"/src/packages/pkg2/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/packages/pkg2/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig",
compilerOptions: { outDir: "lib" },
include: ["src"],
references: [{ path: "../pkg1" }],
}),
}),
commandLineArgs: ["--b", "/src/packages/pkg2/tsconfig.json", "--verbose"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--b", "packages/pkg2/tsconfig.json", "--verbose"],
});
forEachDeclarationEmitWithErrorsScenario(
(scenario, fs) => {
(scenario, sys) => {
verifyTsc({
scenario: "declarationEmit",
subScenario: scenario("reports dts generation errors"),
commandLineArgs: ["-b", `/src/project`, "--explainFiles", "--listEmittedFiles", "--v"],
fs,
commandLineArgs: ["-b", "--explainFiles", "--listEmittedFiles", "--v"],
sys,
edits: noChangeOnlyRuns,
});
},
+9 -21
View File
@@ -1,35 +1,24 @@
import * as vfs from "../../_namespaces/vfs.js";
import {
getFsContentsForDemoProjectReferencesCoreConfig,
getFsForDemoProjectReferences,
getSysForDemoProjectReferences,
} from "../helpers/demoProjectReferences.js";
import { verifyTsc } from "../helpers/tsc.js";
import { prependText } from "../helpers/vfs.js";
describe("unittests:: tsbuild:: on demo project", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = getFsForDemoProjectReferences();
});
after(() => {
projFs = undefined!; // Release the contents
});
describe("unittests:: tsbuild:: on demo:: project", () => {
verifyTsc({
scenario: "demo",
subScenario: "in master branch with everything setup correctly and reports no error",
fs: () => projFs,
sys: getSysForDemoProjectReferences,
commandLineArgs: ["--b", "--verbose"],
});
verifyTsc({
scenario: "demo",
subScenario: "in circular branch reports the error about it by stopping build",
fs: () => projFs,
sys: getSysForDemoProjectReferences,
commandLineArgs: ["--b", "--verbose"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"core/tsconfig.json",
getFsContentsForDemoProjectReferencesCoreConfig({
references: [{
@@ -41,11 +30,10 @@ describe("unittests:: tsbuild:: on demo project", () => {
verifyTsc({
scenario: "demo",
subScenario: "in bad-ref branch reports the error about files not in rootDir at the import location",
fs: () => projFs,
sys: getSysForDemoProjectReferences,
commandLineArgs: ["--b", "--verbose"],
modifyFs: fs =>
prependText(
fs,
modifySystem: sys =>
sys.prependFile(
"core/utilities.ts",
`import * as A from '../animals';
`,
@@ -1,43 +1,38 @@
import { dedent } from "../../_namespaces/Utils.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = loadProjectFromFiles({
"/src/src/a.ts": dedent`
describe("unittests:: tsbuild:: on project with emitDeclarationOnly:: set to true", () => {
function getEmitDeclarationOnlySys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/a.ts": dedent`
import { B } from "./b";
export interface A {
b: B;
}
`,
"/src/src/b.ts": dedent`
"/home/src/workspaces/project/src/b.ts": dedent`
import { C } from "./c";
export interface B {
b: C;
}
`,
"/src/src/c.ts": dedent`
"/home/src/workspaces/project/src/c.ts": dedent`
import { A } from "./a";
export interface C {
a: A;
}
`,
"/src/src/index.ts": dedent`
"/home/src/workspaces/project/src/index.ts": dedent`
export { A } from "./a";
export { B } from "./b";
export { C } from "./c";
`,
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
incremental: true,
target: "es5",
@@ -54,24 +49,21 @@ describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true"
emitDeclarationOnly: true,
},
}),
});
});
after(() => {
projFs = undefined!;
});
}, { currentDirectory: "/home/src/workspaces/project" });
}
function verifyEmitDeclarationOnly(disableMap?: true) {
verifyTsc({
subScenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
fs: () => projFs,
sys: getEmitDeclarationOnlySys,
scenario: "emitDeclarationOnly",
commandLineArgs: ["--b", "/src", "--verbose"],
modifyFs: disableMap ?
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
commandLineArgs: ["--b", "--verbose"],
modifySystem: disableMap ?
(sys => sys.replaceFileText("/home/src/workspaces/project/tsconfig.json", `"declarationMap": true,`, "")) :
undefined,
edits: [{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/a.ts", "b: B;", "b: B; foo: any;"),
}],
});
}
@@ -80,20 +72,19 @@ describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true"
verifyTsc({
subScenario: `only dts output in non circular imports project with emitDeclarationOnly`,
fs: () => projFs,
sys: getEmitDeclarationOnlySys,
scenario: "emitDeclarationOnly",
commandLineArgs: ["--b", "/src", "--verbose"],
modifyFs: fs => {
fs.rimrafSync("/src/src/index.ts");
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
commandLineArgs: ["--b", "--verbose"],
modifySystem: sys => {
sys.rimrafSync("/home/src/workspaces/project/src/index.ts");
sys.replaceFileText("/home/src/workspaces/project/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
},
edits: [
{
caption: "incremental-declaration-doesnt-change",
edit: fs =>
replaceText(
fs,
"/src/src/a.ts",
edit: sys =>
sys.replaceFileText(
"/home/src/workspaces/project/src/a.ts",
"export interface A {",
`class C { }
export interface A {`,
@@ -101,7 +92,7 @@ export interface A {`,
},
{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/a.ts", "b: B;", "b: B; foo: any;"),
},
],
});
+14 -14
View File
@@ -1,14 +1,14 @@
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild - empty files option in tsconfig", () => {
describe("unittests:: tsbuild:: emptyFiles:: option in tsconfig", () => {
verifyTsc({
scenario: "emptyFiles",
subScenario: "has empty files diagnostic when files is empty and no references are provided",
fs: () =>
loadProjectFromFiles({
"/src/no-references/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/no-references/tsconfig.json": jsonToReadableText({
references: [],
files: [],
compilerOptions: {
@@ -18,17 +18,17 @@ describe("unittests:: tsbuild - empty files option in tsconfig", () => {
skipDefaultLibCheck: true,
},
}),
}),
commandLineArgs: ["--b", "/src/no-references"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "no-references"],
});
verifyTsc({
scenario: "emptyFiles",
subScenario: "does not have empty files diagnostic when files is empty and references are provided",
fs: () =>
loadProjectFromFiles({
"/src/core/index.ts": "export function multiply(a: number, b: number) { return a * b; }",
"/src/core/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/core/index.ts": "export function multiply(a: number, b: number) { return a * b; }",
"/home/src/workspaces/solution/core/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
@@ -36,7 +36,7 @@ describe("unittests:: tsbuild - empty files option in tsconfig", () => {
skipDefaultLibCheck: true,
},
}),
"/src/with-references/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/with-references/tsconfig.json": jsonToReadableText({
references: [
{ path: "../core" },
],
@@ -48,7 +48,7 @@ describe("unittests:: tsbuild - empty files option in tsconfig", () => {
skipDefaultLibCheck: true,
},
}),
}),
commandLineArgs: ["--b", "/src/with-references"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "with-references"],
});
});
@@ -1,12 +1,13 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
// https://github.com/microsoft/TypeScript/issues/33849
describe("unittests:: tsbuild:: exitCodeOnBogusFile:: test exit code", () => {
verifyTsc({
scenario: "exitCodeOnBogusFile",
subScenario: `test exit code`,
fs: () => loadProjectFromFiles({}),
sys: () => TestServerHost.createWatchedSystem(emptyArray, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-b", "bogus.json"],
});
});
+3 -5
View File
@@ -3,11 +3,9 @@ import {
getSymlinkedExtendsSys,
} from "../helpers/extends.js";
import { verifyTsc } from "../helpers/tsc.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
describe("unittests:: tsbuild:: extends::", () => {
verifyTscWatch({
verifyTsc({
scenario: "extends",
subScenario: "resolves the symlink path",
sys: getSymlinkedExtendsSys,
@@ -17,7 +15,7 @@ describe("unittests:: tsbuild:: extends::", () => {
verifyTsc({
scenario: "extends",
subScenario: "configDir template",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-b", "/home/src/projects/myproject", "--explainFiles", "--v"],
sys: getConfigDirExtendsSys,
commandLineArgs: ["-b", "--explainFiles", "--v"],
});
});
+26 -26
View File
@@ -3,50 +3,50 @@ import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { compilerOptionsToConfigJson } from "../helpers/contents.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: fileDelete::", () => {
function fs(childOptions: ts.CompilerOptions, mainOptions?: ts.CompilerOptions) {
return loadProjectFromFiles({
"/src/child/child.ts": dedent`
function sys(childOptions: ts.CompilerOptions, mainOptions?: ts.CompilerOptions) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/child/child.ts": dedent`
import { child2 } from "../child/child2";
export function child() {
child2();
}
`,
"/src/child/child2.ts": dedent`
"/home/src/workspaces/solution/child/child2.ts": dedent`
export function child2() {
}
`,
"/src/child/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/child/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(childOptions),
}),
...(mainOptions ? {
"/src/main/main.ts": dedent`
"/home/src/workspaces/solution/main/main.ts": dedent`
import { child } from "${childOptions.outFile ? "child" : "../child/child"}";
export function main() {
child();
}
`,
"/src/main/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/main/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson(mainOptions),
references: [{ path: "../child" }],
}),
} : {}),
});
}, { currentDirectory: "/home/src/workspaces/solution" });
}
verifyTsc({
scenario: "fileDelete",
subScenario: `multiFile/detects deleted file`,
commandLineArgs: ["--b", "/src/main/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
fs: () => fs({ composite: true }, { composite: true }),
commandLineArgs: ["--b", "main/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
sys: () => sys({ composite: true }, { composite: true }),
edits: [{
caption: "delete child2 file",
edit: fs => {
fs.rimrafSync("/src/child/child2.ts");
fs.rimrafSync("/src/child/child2.js");
fs.rimrafSync("/src/child/child2.d.ts");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/solution/child/child2.ts");
sys.rimrafSync("/home/src/workspaces/solution/child/child2.js");
sys.rimrafSync("/home/src/workspaces/solution/child/child2.d.ts");
},
}],
});
@@ -54,24 +54,24 @@ describe("unittests:: tsbuild:: fileDelete::", () => {
verifyTsc({
scenario: "fileDelete",
subScenario: `outFile/detects deleted file`,
commandLineArgs: ["--b", "/src/main/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
fs: () => fs({ composite: true, outFile: "../childResult.js", module: ts.ModuleKind.AMD }, { composite: true, outFile: "../mainResult.js", module: ts.ModuleKind.AMD }),
commandLineArgs: ["--b", "main/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
sys: () => sys({ composite: true, outFile: "../childResult.js", module: ts.ModuleKind.AMD }, { composite: true, outFile: "../mainResult.js", module: ts.ModuleKind.AMD }),
edits: [{
caption: "delete child2 file",
edit: fs => fs.rimrafSync("/src/child/child2.ts"),
edit: sys => sys.rimrafSync("/home/src/workspaces/solution/child/child2.ts"),
}],
});
verifyTsc({
scenario: "fileDelete",
subScenario: `multiFile/deleted file without composite`,
commandLineArgs: ["--b", "/src/child/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
fs: () => fs({}),
commandLineArgs: ["--b", "child/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
sys: () => sys({}),
edits: [{
caption: "delete child2 file",
edit: fs => {
fs.rimrafSync("/src/child/child2.ts");
fs.rimrafSync("/src/child/child2.js");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/solution/child/child2.ts");
sys.rimrafSync("/home/src/workspaces/solution/child/child2.js");
},
}],
});
@@ -79,11 +79,11 @@ describe("unittests:: tsbuild:: fileDelete::", () => {
verifyTsc({
scenario: "fileDelete",
subScenario: `outFile/deleted file without composite`,
commandLineArgs: ["--b", "/src/child/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
fs: () => fs({ outFile: "../childResult.js", module: ts.ModuleKind.AMD }),
commandLineArgs: ["--b", "child/tsconfig.json", "-v", "--traceResolution", "--explainFiles"],
sys: () => sys({ outFile: "../childResult.js", module: ts.ModuleKind.AMD }),
edits: [{
caption: "delete child2 file",
edit: fs => fs.rimrafSync("/src/child/child2.ts"),
edit: sys => sys.rimrafSync("/home/src/workspaces/solution/child/child2.ts"),
}],
});
});
@@ -1,10 +1,9 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as ts from "../../_namespaces/ts.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild - graph-ordering", () => {
let host: fakes.SolutionBuilderHost | undefined;
describe("unittests:: tsbuild:: graphOrdering::", () => {
let host: ts.SolutionBuilderHost<ts.EmitAndSemanticDiagnosticsBuilderProgram> | undefined;
const deps: [string, string][] = [
["A", "B"],
["B", "C"],
@@ -20,9 +19,12 @@ describe("unittests:: tsbuild - graph-ordering", () => {
];
before(() => {
const fs = new vfs.FileSystem(/*ignoreCase*/ false);
host = fakes.SolutionBuilderHost.create(fs);
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"], deps);
const sys = TestServerHost.createWatchedSystem(ts.emptyArray, {
useCaseSensitiveFileNames: true,
currentDirectory: "/home/src/workspaces/project",
});
host = ts.createSolutionBuilderHost(sys);
writeProjects(sys, ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"], deps);
});
after(() => {
@@ -68,25 +70,25 @@ describe("unittests:: tsbuild - graph-ordering", () => {
}
function getProjectFileName(proj: string) {
return `/project/${proj}/tsconfig.json` as ts.ResolvedConfigFileName;
return `/home/src/workspaces/project/${proj}/tsconfig.json` as ts.ResolvedConfigFileName;
}
function writeProjects(fileSystem: vfs.FileSystem, projectNames: string[], deps: [string, string][]): string[] {
function writeProjects(sys: TestServerHost, projectNames: string[], deps: [string, string][]): string[] {
const projFileNames: string[] = [];
for (const dep of deps) {
if (!projectNames.includes(dep[0])) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`);
if (!projectNames.includes(dep[1])) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`);
}
for (const proj of projectNames) {
fileSystem.mkdirpSync(`/project/${proj}`);
fileSystem.writeFileSync(`/project/${proj}/${proj}.ts`, "export {}");
sys.ensureFileOrFolder({ path: `/home/src/workspaces/project/${proj}` });
sys.writeFile(`/home/src/workspaces/project/${proj}/${proj}.ts`, "export {}");
const configFileName = getProjectFileName(proj);
const configContent = jsonToReadableText({
compilerOptions: { composite: true },
files: [`./${proj}.ts`],
references: deps.filter(d => d[0] === proj).map(d => ({ path: `../${d[1]}` })),
});
fileSystem.writeFileSync(configFileName, configContent);
sys.writeFile(configFileName, configContent);
projFileNames.push(configFileName);
}
return projFileNames;
@@ -1,18 +1,12 @@
import { dedent } from "../../_namespaces/Utils.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = loadProjectFromFiles({
"/src/bar.ts": dedent`
function getInferredTypeFromTransitiveModuleSys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/bar.ts": dedent`
interface RawAction {
(...args: any[]): Promise<any> | void;
}
@@ -23,7 +17,7 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
export default foo()(function foobar(param: string): void {
});
`,
"/src/bundling.ts": dedent`
"/home/src/workspaces/project/bundling.ts": dedent`
export class LazyModule<TModule> {
constructor(private importCallback: () => Promise<TModule>) {}
}
@@ -36,7 +30,7 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
}
}
`,
"/src/global.d.ts": dedent`
"/home/src/workspaces/project/global.d.ts": dedent`
interface PromiseConstructor {
new <T>(): Promise<T>;
}
@@ -44,17 +38,17 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
interface Promise<T> {
}
`,
"/src/index.ts": dedent`
"/home/src/workspaces/project/index.ts": dedent`
import { LazyAction, LazyModule } from './bundling';
const lazyModule = new LazyModule(() =>
import('./lazyIndex')
);
export const lazyBar = new LazyAction(lazyModule, m => m.bar);
`,
"/src/lazyIndex.ts": dedent`
"/home/src/workspaces/project/lazyIndex.ts": dedent`
export { default as bar } from './bar';
`,
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
declaration: true,
@@ -62,17 +56,14 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
incremental: true,
},
}),
});
});
after(() => {
projFs = undefined!;
});
}, { currentDirectory: "/home/src/workspaces/project" });
}
verifyTsc({
scenario: "inferredTypeFromTransitiveModule",
subScenario: "inferred type from transitive module",
fs: () => projFs,
commandLineArgs: ["--b", "/src", "--verbose"],
sys: getInferredTypeFromTransitiveModuleSys,
commandLineArgs: ["--b", "--verbose"],
edits: [
{
caption: "incremental-declaration-changes",
@@ -87,10 +78,10 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
verifyTsc({
subScenario: "inferred type from transitive module with isolatedModules",
fs: () => projFs,
sys: getInferredTypeFromTransitiveModuleSys,
scenario: "inferredTypeFromTransitiveModule",
commandLineArgs: ["--b", "/src", "--verbose"],
modifyFs: changeToIsolatedModules,
commandLineArgs: ["--b", "--verbose"],
modifySystem: changeToIsolatedModules,
edits: [
{
caption: "incremental-declaration-changes",
@@ -106,13 +97,12 @@ describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
verifyTsc({
scenario: "inferredTypeFromTransitiveModule",
subScenario: "reports errors in files affected by change in signature with isolatedModules",
fs: () => projFs,
commandLineArgs: ["--b", "/src", "--verbose"],
modifyFs: fs => {
changeToIsolatedModules(fs);
appendText(
fs,
"/src/lazyIndex.ts",
sys: getInferredTypeFromTransitiveModuleSys,
commandLineArgs: ["--b", "--verbose"],
modifySystem: sys => {
changeToIsolatedModules(sys);
sys.appendFile(
"/home/src/workspaces/project/lazyIndex.ts",
`
import { default as bar } from './bar';
bar("hello");`,
@@ -133,20 +123,20 @@ bar("hello");`,
},
{
caption: "Fix Error",
edit: fs => replaceText(fs, "/src/lazyIndex.ts", `bar("hello")`, "bar()"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/lazyIndex.ts", `bar("hello")`, "bar()"),
},
],
});
});
function changeToIsolatedModules(fs: vfs.FileSystem) {
replaceText(fs, "/src/tsconfig.json", `"incremental": true`, `"incremental": true, "isolatedModules": true`);
function changeToIsolatedModules(sys: TestServerHost) {
sys.replaceFileText("/home/src/workspaces/project/tsconfig.json", `"incremental": true`, `"incremental": true, "isolatedModules": true`);
}
function changeBarParam(fs: vfs.FileSystem) {
replaceText(fs, "/src/bar.ts", "param: string", "");
function changeBarParam(sys: TestServerHost) {
sys.replaceFileText("/home/src/workspaces/project/bar.ts", "param: string", "");
}
function changeBarParamBack(fs: vfs.FileSystem) {
replaceText(fs, "/src/bar.ts", "foobar()", "foobar(param: string)");
function changeBarParamBack(sys: TestServerHost) {
sys.replaceFileText("/home/src/workspaces/project/bar.ts", "foobar()", "foobar(param: string)");
}
@@ -1,48 +1,50 @@
import * as Utils from "../../_namespaces/Utils.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { symbolLibContent } from "../helpers/contents.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import {
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: javascriptProjectEmit::", () => {
verifyTsc({
scenario: "javascriptProjectEmit",
subScenario: `loads js-based projects and emits them correctly`,
fs: () =>
loadProjectFromFiles({
"/src/common/nominal.js": Utils.dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/common/nominal.js": dedent`
/**
* @template T, Name
* @typedef {T & {[Symbol.species]: Name}} Nominal
*/
module.exports = {};
`,
"/src/common/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"include": ["nominal.js"]
}`,
"/src/sub-project/index.js": Utils.dedent`
"/home/src/workspaces/solution/common/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
composite: true,
},
include: ["nominal.js"],
}),
"/home/src/workspaces/solution/sub-project/index.js": dedent`
import { Nominal } from '../common/nominal';
/**
* @typedef {Nominal<string, 'MyNominal'>} MyNominal
*/
`,
"/src/sub-project/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../common" }
],
"include": ["./index.js"]
}`,
"/src/sub-project-2/index.js": Utils.dedent`
"/home/src/workspaces/solution/sub-project/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../common" },
],
include: ["./index.js"],
}),
"/home/src/workspaces/solution/sub-project-2/index.js": dedent`
import { MyNominal } from '../sub-project/index';
const variable = {
@@ -56,82 +58,77 @@ describe("unittests:: tsbuild:: javascriptProjectEmit::", () => {
return 'key';
}
`,
"/src/sub-project-2/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../sub-project" }
],
"include": ["./index.js"]
}`,
"/src/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "./sub-project" },
{ "path": "./sub-project-2" }
],
"include": []
}`,
"/src/tsconfig.base.json": Utils.dedent`
{
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./",
"outDir": "../lib",
"allowJs": true,
"checkJs": true,
"declaration": true
}
}`,
}, symbolLibContent),
commandLineArgs: ["-b", "/src"],
"/home/src/workspaces/solution/sub-project-2/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../sub-project" },
],
include: ["./index.js"],
}),
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [
{ path: "./sub-project" },
{ path: "./sub-project-2" },
],
include: [],
}),
"/home/src/workspaces/solution/tsconfig.base.json": jsonToReadableText({
compilerOptions: {
skipLibCheck: true,
rootDir: "./",
outDir: "../lib",
allowJs: true,
checkJs: true,
declaration: true,
},
}),
[libFile.path]: `${libFile.content}${symbolLibContent}`,
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["-b"],
});
verifyTsc({
scenario: "javascriptProjectEmit",
subScenario: `loads js-based projects with non-moved json files and emits them correctly`,
fs: () =>
loadProjectFromFiles({
"/src/common/obj.json": Utils.dedent`
{
"val": 42
}`,
"/src/common/index.ts": Utils.dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/common/obj.json": jsonToReadableText({
val: 42,
}),
"/home/src/workspaces/solution/common/index.ts": dedent`
import x = require("./obj.json");
export = x;
`,
"/src/common/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": null,
"composite": true
},
"include": ["index.ts", "obj.json"]
}`,
"/src/sub-project/index.js": Utils.dedent`
`,
"/home/src/workspaces/solution/common/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
outDir: null, // eslint-disable-line no-restricted-syntax
composite: true,
},
include: ["index.ts", "obj.json"],
}),
"/home/src/workspaces/solution/sub-project/index.js": dedent`
import mod from '../common';
export const m = mod;
`,
"/src/sub-project/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../common" }
],
"include": ["./index.js"]
}`,
"/src/sub-project-2/index.js": Utils.dedent`
"/home/src/workspaces/solution/sub-project/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../common" },
],
include: ["./index.js"],
}),
"/home/src/workspaces/solution/sub-project-2/index.js": dedent`
import { m } from '../sub-project/index';
const variable = {
@@ -142,42 +139,40 @@ describe("unittests:: tsbuild:: javascriptProjectEmit::", () => {
return variable;
}
`,
"/src/sub-project-2/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../sub-project" }
],
"include": ["./index.js"]
}`,
"/src/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "./sub-project" },
{ "path": "./sub-project-2" }
],
"include": []
}`,
"/src/tsconfig.base.json": Utils.dedent`
{
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./",
"outDir": "../out",
"allowJs": true,
"checkJs": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"declaration": true
}
}`,
}, symbolLibContent),
commandLineArgs: ["-b", "/src"],
"/home/src/workspaces/solution/sub-project-2/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../sub-project" },
],
include: ["./index.js"],
}),
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [
{ path: "./sub-project" },
{ path: "./sub-project-2" },
],
include: [],
}),
"/home/src/workspaces/solution/tsconfig.base.json": jsonToReadableText({
compilerOptions: {
skipLibCheck: true,
rootDir: "./",
outDir: "../out",
allowJs: true,
checkJs: true,
resolveJsonModule: true,
esModuleInterop: true,
declaration: true,
},
}),
[libFile.path]: `${libFile.content}${symbolLibContent}`,
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["-b"],
});
});
@@ -1,54 +1,50 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: lateBoundSymbol:: interface is merged and contains late bound member", () => {
verifyTsc({
subScenario: "interface is merged and contains late bound member",
fs: () =>
loadProjectFromFiles({
"/src/src/globals.d.ts": dedent`
interface SymbolConstructor {
(description?: string | number): symbol;
}
declare var Symbol: SymbolConstructor;
`,
"/src/src/hkt.ts": `export interface HKT<T> { }`,
"/src/src/main.ts": dedent`
import { HKT } from "./hkt";
const sym = Symbol();
declare module "./hkt" {
interface HKT<T> {
[sym]: { a: T }
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/globals.d.ts": dedent`
interface SymbolConstructor {
(description?: string | number): symbol;
}
}
const x = 10;
type A = HKT<number>[typeof sym];
`,
"/src/tsconfig.json": jsonToReadableText({
declare var Symbol: SymbolConstructor;
`,
"/home/src/workspaces/project/src/hkt.ts": `export interface HKT<T> { }`,
"/home/src/workspaces/project/src/main.ts": dedent`
import { HKT } from "./hkt";
const sym = Symbol();
declare module "./hkt" {
interface HKT<T> {
[sym]: { a: T }
}
}
const x = 10;
type A = HKT<number>[typeof sym];
`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
rootDir: "src",
incremental: true,
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
scenario: "lateBoundSymbol",
commandLineArgs: ["--b", "/src/tsconfig.json", "--verbose"],
commandLineArgs: ["--b", "--verbose"],
edits: [
{
caption: "incremental-declaration-doesnt-change",
edit: fs => replaceText(fs, "/src/src/main.ts", "const x = 10;", ""),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/src/main.ts", "const x = 10;", ""),
},
{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/src/main.ts", "const x = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/project/src/main.ts", "const x = 10;"),
},
],
});
@@ -1,16 +1,17 @@
import { getFsForLibResolution } from "../helpers/libraryResolution.js";
import { forEachLibResolutionScenario } from "../helpers/libraryResolution.js";
import { verifyTsc } from "../helpers/tsc.js";
describe("unittests:: tsbuild:: libraryResolution:: library file resolution", () => {
function verify(libRedirection?: true) {
verifyTsc({
scenario: "libraryResolution",
subScenario: `with config${libRedirection ? " with redirection" : ""}`,
fs: () => getFsForLibResolution(libRedirection),
commandLineArgs: ["-b", "project1", "project2", "project3", "project4", "--verbose", "--explainFiles"],
baselinePrograms: true,
});
}
verify();
verify(/*libRedirection*/ true);
forEachLibResolutionScenario(
/*forTsserver*/ false,
/*withoutConfig*/ undefined,
(subScenario, sys) =>
verifyTsc({
scenario: "libraryResolution",
subScenario,
sys,
commandLineArgs: ["-b", "project1", "project2", "project3", "project4", "--verbose", "--explainFiles"],
baselinePrograms: true,
}),
);
});
@@ -1,24 +1,18 @@
import * as ts from "../../_namespaces/ts.js";
import * as Utils from "../../_namespaces/Utils.js";
import { Symlink } from "../../_namespaces/vfs.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
noChangeOnlyRuns,
verifyTsc,
} from "../helpers/tsc.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: moduleResolution:: handles the modules and options from referenced project correctly", () => {
function sys(optionsToExtend?: ts.CompilerOptions) {
return createWatchedSystem([
return TestServerHost.createWatchedSystem([
{
path: `/user/username/projects/myproject/packages/pkg1/index.ts`,
content: Utils.dedent`
content: dedent`
import type { TheNum } from 'pkg2'
export const theNum: TheNum = 42;`,
},
@@ -60,18 +54,17 @@ describe("unittests:: tsbuild:: moduleResolution:: handles the modules and optio
path: `/user/username/projects/myproject/node_modules/pkg2`,
symLink: `/user/username/projects/myproject/packages/pkg2`,
},
libFile,
], { currentDirectory: "/user/username/projects/myproject" });
}
verifyTscWatch({
verifyTsc({
scenario: "moduleResolution",
subScenario: `resolves specifier in output declaration file from referenced project correctly`,
sys,
commandLineArgs: ["-b", "packages/pkg1", "--verbose", "--traceResolution"],
});
verifyTscWatch({
verifyTsc({
scenario: "moduleResolution",
subScenario: `resolves specifier in output declaration file from referenced project correctly with preserveSymlinks`,
sys: () => sys({ preserveSymlinks: true }),
@@ -81,22 +74,22 @@ describe("unittests:: tsbuild:: moduleResolution:: handles the modules and optio
verifyTsc({
scenario: "moduleResolution",
subScenario: `type reference resolution uses correct options for different resolution options referenced project`,
fs: () =>
loadProjectFromFiles({
"/src/packages/pkg1_index.ts": `export const theNum: TheNum = "type1";`,
"/src/packages/pkg1.tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/packages/pkg1_index.ts": `export const theNum: TheNum = "type1";`,
"/home/src/workspaces/project/packages/pkg1.tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, typeRoots: ["./typeroot1"] },
files: ["./pkg1_index.ts"],
}),
"/src/packages/typeroot1/sometype/index.d.ts": Utils.dedent`declare type TheNum = "type1";`,
"/src/packages/pkg2_index.ts": `export const theNum: TheNum2 = "type2";`,
"/src/packages/pkg2.tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/packages/typeroot1/sometype/index.d.ts": dedent`declare type TheNum = "type1";`,
"/home/src/workspaces/project/packages/pkg2_index.ts": `export const theNum: TheNum2 = "type2";`,
"/home/src/workspaces/project/packages/pkg2.tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, typeRoots: ["./typeroot2"] },
files: ["./pkg2_index.ts"],
}),
"/src/packages/typeroot2/sometype/index.d.ts": Utils.dedent`declare type TheNum2 = "type2";`,
}),
commandLineArgs: ["-b", "/src/packages/pkg1.tsconfig.json", "/src/packages/pkg2.tsconfig.json", "--verbose", "--traceResolution"],
"/home/src/workspaces/project/packages/typeroot2/sometype/index.d.ts": dedent`declare type TheNum2 = "type2";`,
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-b", "packages/pkg1.tsconfig.json", "packages/pkg2.tsconfig.json", "--verbose", "--traceResolution"],
});
});
@@ -104,41 +97,40 @@ describe("unittests:: tsbuild:: moduleResolution:: impliedNodeFormat differs bet
verifyTsc({
scenario: "moduleResolution",
subScenario: "impliedNodeFormat differs between projects for shared file",
fs: () =>
loadProjectFromFiles({
"/src/projects/a/src/index.ts": "",
"/src/projects/a/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a/src/index.ts": "",
"/home/src/workspaces/project/a/tsconfig.json": jsonToReadableText({
compilerOptions: { strict: true },
}),
"/src/projects/b/src/index.ts": Utils.dedent`
"/home/src/workspaces/project/b/src/index.ts": dedent`
import pg from "pg";
pg.foo();
`,
"/src/projects/b/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/b/tsconfig.json": jsonToReadableText({
compilerOptions: { strict: true, module: "node16" },
}),
"/src/projects/b/package.json": jsonToReadableText({
"/home/src/workspaces/project/b/package.json": jsonToReadableText({
name: "b",
type: "module",
}),
"/src/projects/node_modules/@types/pg/index.d.ts": "export function foo(): void;",
"/src/projects/node_modules/@types/pg/package.json": jsonToReadableText({
"/home/src/workspaces/project/node_modules/@types/pg/index.d.ts": "export function foo(): void;",
"/home/src/workspaces/project/node_modules/@types/pg/package.json": jsonToReadableText({
name: "@types/pg",
types: "index.d.ts",
}),
}),
modifyFs: fs => fs.writeFileSync("/lib/lib.es2022.full.d.ts", libFile.content),
commandLineArgs: ["-b", "/src/projects/a", "/src/projects/b", "--verbose", "--traceResolution", "--explainFiles"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-b", "a", "b", "--verbose", "--traceResolution", "--explainFiles"],
edits: noChangeOnlyRuns,
});
});
describe("unittests:: tsbuild:: moduleResolution:: resolution sharing", () => {
function fs() {
return loadProjectFromFiles({
"/src/projects/project/packages/a/index.js": `export const a = 'a';`,
"/src/projects/project/packages/a/test/index.js": `import 'a';`,
"/src/projects/project/packages/a/tsconfig.json": jsonToReadableText({
function sys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/packages/a/index.js": `export const a = 'a';`,
"/home/src/workspaces/project/packages/a/test/index.js": `import 'a';`,
"/home/src/workspaces/project/packages/a/tsconfig.json": jsonToReadableText({
compilerOptions: {
checkJs: true,
composite: true,
@@ -148,7 +140,7 @@ describe("unittests:: tsbuild:: moduleResolution:: resolution sharing", () => {
outDir: "types",
},
}),
"/src/projects/project/packages/a/package.json": jsonToReadableText({
"/home/src/workspaces/project/packages/a/package.json": jsonToReadableText({
name: "a",
version: "0.0.0",
type: "module",
@@ -159,8 +151,8 @@ describe("unittests:: tsbuild:: moduleResolution:: resolution sharing", () => {
},
},
}),
"/src/projects/project/packages/b/index.js": `export { a } from 'a';`,
"/src/projects/project/packages/b/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/packages/b/index.js": `export { a } from 'a';`,
"/home/src/workspaces/project/packages/b/tsconfig.json": jsonToReadableText({
references: [{ path: "../a" }],
compilerOptions: {
checkJs: true,
@@ -169,27 +161,26 @@ describe("unittests:: tsbuild:: moduleResolution:: resolution sharing", () => {
noImplicitAny: true,
},
}),
"/src/projects/project/packages/b/package.json": jsonToReadableText({
"/home/src/workspaces/project/packages/b/package.json": jsonToReadableText({
name: "b",
version: "0.0.0",
type: "module",
}),
"/src/projects/project/node_modules/a": new Symlink("/src/projects/project/packages/a"),
"/lib/lib.esnext.full.d.ts": libFile.content,
}, { cwd: "/src/projects/project" });
"/home/src/workspaces/project/node_modules/a": { symLink: "/home/src/workspaces/project/packages/a" },
}, { currentDirectory: "/home/src/workspaces/project" });
}
verifyTsc({
scenario: "moduleResolution",
subScenario: "shared resolution should not report error",
fs,
sys,
commandLineArgs: ["-b", "packages/b", "--verbose", "--traceResolution", "--explainFiles"],
});
verifyTsc({
scenario: "moduleResolution",
subScenario: "when resolution is not shared",
fs,
sys,
commandLineArgs: ["-b", "packages/a", "--verbose", "--traceResolution", "--explainFiles"],
edits: [{
caption: "build b",
@@ -1,46 +1,47 @@
import * as Utils from "../../_namespaces/Utils.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { symbolLibContent } from "../helpers/contents.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { libFile } from "../helpers/virtualFileSystemWithWatch.js";
import {
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
// https://github.com/microsoft/TypeScript/issues/31696
describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
verifyTsc({
scenario: "moduleSpecifiers",
subScenario: `synthesized module specifiers resolve correctly`,
fs: () =>
loadProjectFromFiles({
"/src/solution/common/nominal.ts": Utils.dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/packages/solution/common/nominal.ts": dedent`
export declare type Nominal<T, Name extends string> = T & {
[Symbol.species]: Name;
};
`,
"/src/solution/common/tsconfig.json": Utils.dedent`
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"include": ["nominal.ts"]
}`,
"/src/solution/sub-project/index.ts": Utils.dedent`
"/home/src/workspaces/packages/solution/common/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: {
composite: true,
},
include: ["nominal.ts"],
}),
"/home/src/workspaces/packages/solution/sub-project/index.ts": dedent`
import { Nominal } from '../common/nominal';
export type MyNominal = Nominal<string, 'MyNominal'>;
`,
"/src/solution/sub-project/tsconfig.json": Utils.dedent`
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../common" }
],
"include": ["./index.ts"]
}`,
"/src/solution/sub-project-2/index.ts": Utils.dedent`
"/home/src/workspaces/packages/solution/sub-project/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../common" },
],
include: ["./index.ts"],
}),
"/home/src/workspaces/packages/solution/sub-project-2/index.ts": dedent`
import { MyNominal } from '../sub-project/index';
const variable = {
@@ -51,47 +52,45 @@ describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers
return 'key';
}
`,
"/src/solution/sub-project-2/tsconfig.json": Utils.dedent`
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../sub-project" }
],
"include": ["./index.ts"]
}`,
"/src/solution/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "./sub-project" },
{ "path": "./sub-project-2" }
],
"include": []
}`,
"/src/tsconfig.base.json": Utils.dedent`
{
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./",
"outDir": "lib",
}
}`,
"/src/tsconfig.json": Utils.dedent`{
"compilerOptions": {
"composite": true
"/home/src/workspaces/packages/solution/sub-project-2/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
compilerOptions: {
composite: true,
},
"references": [
{ "path": "./solution" }
references: [
{ path: "../sub-project" },
],
"include": []
}`,
}, symbolLibContent),
commandLineArgs: ["-b", "/src", "--verbose"],
include: ["./index.ts"],
}),
"/home/src/workspaces/packages/solution/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [
{ path: "./sub-project" },
{ path: "./sub-project-2" },
],
include: [],
}),
"/home/src/workspaces/packages/tsconfig.base.json": jsonToReadableText({
compilerOptions: {
skipLibCheck: true,
rootDir: "./",
outDir: "lib",
},
}),
"/home/src/workspaces/packages/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [
{ path: "./solution" },
],
include: [],
}),
[libFile.path]: `${libFile.content}${symbolLibContent}`,
}, { currentDirectory: "/home/src/workspaces/packages" }),
commandLineArgs: ["-b", "--verbose"],
});
});
@@ -100,26 +99,26 @@ describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers
verifyTsc({
scenario: "moduleSpecifiers",
subScenario: `synthesized module specifiers across projects resolve correctly`,
fs: () =>
loadProjectFromFiles({
"/src/src-types/index.ts": Utils.dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/packages/src-types/index.ts": dedent`
export * from './dogconfig.js';`,
"/src/src-types/dogconfig.ts": Utils.dedent`
"/home/src/workspaces/packages/src-types/dogconfig.ts": dedent`
export interface DogConfig {
name: string;
}`,
"/src/src-dogs/index.ts": Utils.dedent`
"/home/src/workspaces/packages/src-dogs/index.ts": dedent`
export * from 'src-types';
export * from './lassie/lassiedog.js';
`,
"/src/src-dogs/dogconfig.ts": Utils.dedent`
"/home/src/workspaces/packages/src-dogs/dogconfig.ts": dedent`
import { DogConfig } from 'src-types';
export const DOG_CONFIG: DogConfig = {
name: 'Default dog',
};
`,
"/src/src-dogs/dog.ts": Utils.dedent`
"/home/src/workspaces/packages/src-dogs/dog.ts": dedent`
import { DogConfig } from 'src-types';
import { DOG_CONFIG } from './dogconfig.js';
@@ -130,7 +129,7 @@ describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers
}
}
`,
"/src/src-dogs/lassie/lassiedog.ts": Utils.dedent`
"/home/src/workspaces/packages/src-dogs/lassie/lassiedog.ts": dedent`
import { Dog } from '../dog.js';
import { LASSIE_CONFIG } from './lassieconfig.js';
@@ -138,57 +137,49 @@ describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers
protected static getDogConfig = () => LASSIE_CONFIG;
}
`,
"/src/src-dogs/lassie/lassieconfig.ts": Utils.dedent`
"/home/src/workspaces/packages/src-dogs/lassie/lassieconfig.ts": dedent`
import { DogConfig } from 'src-types';
export const LASSIE_CONFIG: DogConfig = { name: 'Lassie' };
`,
"/src/tsconfig-base.json": Utils.dedent`
{
"compilerOptions": {
"declaration": true,
"module": "node16"
}
}`,
"/src/src-types/package.json": Utils.dedent`
{
"type": "module",
"exports": "./index.js"
}`,
"/src/src-dogs/package.json": Utils.dedent`
{
"type": "module",
"exports": "./index.js"
}`,
"/src/src-types/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig-base.json",
"compilerOptions": {
"composite": true
},
"include": [
"**/*"
]
}`,
"/src/src-dogs/tsconfig.json": Utils.dedent`
{
"extends": "../tsconfig-base.json",
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../src-types" }
],
"include": [
"**/*"
]
}`,
}, ""),
modifyFs: fs => {
fs.writeFileSync("/lib/lib.es2022.full.d.ts", libFile.content);
fs.symlinkSync("/src", "/src/src-types/node_modules");
fs.symlinkSync("/src", "/src/src-dogs/node_modules");
},
commandLineArgs: ["-b", "src/src-types", "src/src-dogs", "--verbose"],
"/home/src/workspaces/packages/tsconfig-base.json": jsonToReadableText({
compilerOptions: {
declaration: true,
module: "node16",
},
}),
"/home/src/workspaces/packages/src-types/package.json": jsonToReadableText({
type: "module",
exports: "./index.js",
}),
"/home/src/workspaces/packages/src-dogs/package.json": jsonToReadableText({
type: "module",
exports: "./index.js",
}),
"/home/src/workspaces/packages/src-types/tsconfig.json": jsonToReadableText({
extends: "../tsconfig-base.json",
compilerOptions: {
composite: true,
},
include: [
"**/*",
],
}),
"/home/src/workspaces/packages/src-dogs/tsconfig.json": jsonToReadableText({
extends: "../tsconfig-base.json",
compilerOptions: {
composite: true,
},
references: [
{ path: "../src-types" },
],
include: [
"**/*",
],
}),
"/home/src/workspaces/packages/src-types/node_modules": { symLink: "/home/src/workspaces/packages" },
"/home/src/workspaces/packages/src-dogs/node_modules": { symLink: "/home/src/workspaces/packages" },
}, { currentDirectory: "/home/src/workspaces/packages" }),
commandLineArgs: ["-b", "src-types", "src-dogs", "--verbose"],
});
});
+81 -106
View File
@@ -1,33 +1,21 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as ts from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { createSolutionBuilderHostForBaseline } from "../helpers/solutionBuilder.js";
import {
createSolutionBuilderHostForBaseline,
solutionBuildWithBaseline,
verifySolutionBuilderWithDifferentTsVersion,
} from "../helpers/solutionBuilder.js";
import {
noChangeOnlyRuns,
testTscCompileLike,
TscCompileSystem,
verifyTsc,
verifyTscCompileLike,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: outFile::", () => {
let outFileFs: vfs.FileSystem;
let outFileWithBuildFs: vfs.FileSystem;
after(() => {
outFileFs = undefined!;
outFileWithBuildFs = undefined!;
});
function getOutFileFs() {
return outFileFs ??= loadProjectFromFiles({
"/src/first/first_PART1.ts": dedent`
function getOutFileSys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/first/first_PART1.ts": dedent`
interface TheFirst {
none: any;
}
@@ -40,15 +28,15 @@ describe("unittests:: tsbuild:: outFile::", () => {
console.log(s);
`,
"/src/first/first_part2.ts": dedent`
"/home/src/workspaces/solution/first/first_part2.ts": dedent`
console.log(f());
`,
"/src/first/first_part3.ts": dedent`
"/home/src/workspaces/solution/first/first_part3.ts": dedent`
function f() {
return "JS does hoists";
}
`,
"/src/first/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/first/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
composite: true,
@@ -66,7 +54,7 @@ describe("unittests:: tsbuild:: outFile::", () => {
],
references: [],
}),
"/src/second/second_part1.ts": dedent`
"/home/src/workspaces/solution/second/second_part1.ts": dedent`
namespace N {
// Comment text
}
@@ -79,14 +67,14 @@ describe("unittests:: tsbuild:: outFile::", () => {
f();
}
`,
"/src/second/second_part2.ts": dedent`
"/home/src/workspaces/solution/second/second_part2.ts": dedent`
class C {
doSomething() {
console.log("something got done");
}
}
`,
"/src/second/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/second/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
composite: true,
@@ -100,11 +88,11 @@ describe("unittests:: tsbuild:: outFile::", () => {
},
references: [],
}),
"/src/third/third_part1.ts": dedent`
"/home/src/workspaces/solution/third/third_part1.ts": dedent`
var c = new C();
c.doSomething();
`,
"/src/third/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/third/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
composite: true,
@@ -124,36 +112,31 @@ describe("unittests:: tsbuild:: outFile::", () => {
{ path: "../second" },
],
}),
});
}, { currentDirectory: "/home/src/workspaces/solution" });
}
function getOutFileFsAfterBuild() {
if (outFileWithBuildFs) return outFileWithBuildFs;
const fs = getOutFileFs().shadow();
const sys = new fakes.System(fs, { executingFilePath: "/lib/tsc" });
const host = createSolutionBuilderHostForBaseline(sys as TscCompileSystem);
const builder = ts.createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: true });
builder.build();
fs.makeReadonly();
return outFileWithBuildFs = fs;
function getOutFileSysAfterBuild() {
const sys = getOutFileSys();
solutionBuildWithBaseline(sys, ["/home/src/workspaces/solution/third"], { dry: false, force: false, verbose: true });
return sys;
}
// Verify initial + incremental edits
verifyTsc({
subScenario: "baseline sectioned sourcemaps",
fs: getOutFileFs,
sys: getOutFileSys,
scenario: "outFile",
commandLineArgs: ["--b", "/src/third", "--verbose", "--explainFiles"],
commandLineArgs: ["--b", "third", "--verbose", "--explainFiles"],
baselineSourceMap: true,
baselineReadFileCalls: true,
edits: [
{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "/src/first/first_PART1.ts", "Hello", "Hola"),
edit: sys => sys.replaceFileText("/home/src/workspaces/solution/first/first_PART1.ts", "Hello", "Hola"),
},
{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/first/first_PART1.ts", "console.log(s);"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/first/first_PART1.ts", "console.log(s);"),
},
],
});
@@ -161,38 +144,37 @@ describe("unittests:: tsbuild:: outFile::", () => {
// Verify baseline with build info + dts unChanged
verifyTsc({
subScenario: "when final project is not composite but uses project references",
fs: getOutFileFs,
sys: getOutFileSys,
scenario: "outFile",
commandLineArgs: ["--b", "/src/third", "--verbose"],
commandLineArgs: ["--b", "third", "--verbose"],
baselineSourceMap: true,
modifyFs: fs => replaceText(fs, "/src/third/tsconfig.json", `"composite": true,`, ""),
modifySystem: sys => sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"composite": true,`, ""),
edits: [{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/first/first_PART1.ts", "console.log(s);"),
edit: sys => sys.appendFile("/home/src/workspaces/solution/first/first_PART1.ts", "console.log(s);"),
}],
});
// Verify baseline with build info
verifyTsc({
subScenario: "when final project is not composite but incremental",
fs: getOutFileFs,
sys: getOutFileSys,
scenario: "outFile",
commandLineArgs: ["--b", "/src/third", "--verbose"],
commandLineArgs: ["--b", "third", "--verbose"],
baselineSourceMap: true,
modifyFs: fs => replaceText(fs, "/src/third/tsconfig.json", `"composite": true,`, `"incremental": true,`),
modifySystem: sys => sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"composite": true,`, `"incremental": true,`),
});
// Verify baseline with build info
verifyTsc({
subScenario: "when final project specifies tsBuildInfoFile",
fs: getOutFileFs,
sys: getOutFileSys,
scenario: "outFile",
commandLineArgs: ["--b", "/src/third", "--verbose"],
commandLineArgs: ["--b", "third", "--verbose"],
baselineSourceMap: true,
modifyFs: fs =>
replaceText(
fs,
"/src/third/tsconfig.json",
modifySystem: sys =>
sys.replaceFileText(
"/home/src/workspaces/solution/third/tsconfig.json",
`"composite": true,`,
`"composite": true,
"tsBuildInfoFile": "./thirdjs/output/third.tsbuildinfo",`,
@@ -202,53 +184,47 @@ describe("unittests:: tsbuild:: outFile::", () => {
verifyTsc({
scenario: "outFile",
subScenario: "clean projects",
fs: getOutFileFsAfterBuild,
commandLineArgs: ["--b", "/src/third", "--clean"],
sys: getOutFileSysAfterBuild,
commandLineArgs: ["--b", "third", "--clean"],
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "outFile",
subScenario: "verify buildInfo absence results in new build",
fs: getOutFileFsAfterBuild,
commandLineArgs: ["--b", "/src/third", "--verbose"],
modifyFs: fs => fs.unlinkSync("/src/first/bin/first-output.tsbuildinfo"),
sys: getOutFileSysAfterBuild,
commandLineArgs: ["--b", "third", "--verbose"],
modifySystem: sys => sys.deleteFile("/home/src/workspaces/solution/first/bin/first-output.tsbuildinfo"),
});
verifyTsc({
scenario: "outFile",
subScenario: "tsbuildinfo is not generated when incremental is set to false",
fs: getOutFileFs,
commandLineArgs: ["--b", "/src/third", "--verbose"],
modifyFs: fs => replaceText(fs, "/src/third/tsconfig.json", `"composite": true,`, ""),
sys: getOutFileSys,
commandLineArgs: ["--b", "third", "--verbose"],
modifySystem: sys => sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"composite": true,`, ""),
});
verifyTscCompileLike(testTscCompileLike, {
verifySolutionBuilderWithDifferentTsVersion({
scenario: "outFile",
subScenario: "rebuilds completely when version in tsbuildinfo doesnt match ts version",
fs: getOutFileFsAfterBuild,
commandLineArgs: ["--b", "/src/third", "--verbose"],
compile: sys => {
// Buildinfo will have version which does not match with current ts version
const buildHost = createSolutionBuilderHostForBaseline(sys, "FakeTSCurrentVersion");
const builder = ts.createSolutionBuilder(buildHost, ["/src/third"], { verbose: true });
sys.exit(builder.build());
},
});
sys: getOutFileSysAfterBuild,
commandLineArgs: ["--b", "third", "--verbose"],
}, ["/home/src/workspaces/solution/third"]);
verifyTsc({
scenario: "outFile",
subScenario: "rebuilds completely when command line incremental flag changes between non dts changes",
fs: getOutFileFs,
sys: getOutFileSys,
// Make non composite third project
modifyFs: fs => replaceText(fs, "/src/third/tsconfig.json", `"composite": true,`, ""),
modifySystem: sys => sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"composite": true,`, ""),
// Build with command line incremental
commandLineArgs: ["--b", "/src/third", "--i", "--verbose"],
commandLineArgs: ["--b", "third", "--i", "--verbose"],
edits: [
{
caption: "Make non incremental build with change in file that doesnt affect dts",
edit: fs => appendText(fs, "/src/first/first_PART1.ts", "console.log(s);"),
commandLineArgs: ["--b", "/src/third", "--verbose"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/first/first_PART1.ts", "console.log(s);"),
commandLineArgs: ["--b", "third", "--verbose"],
discrepancyExplanation: () => [
"Clean build is non incremental so it will have non incremental tsbuildInfo for third project",
"The incremental build does not build third so will only update timestamps for third tsbuildInfo and hence its from incremental build before",
@@ -256,8 +232,8 @@ describe("unittests:: tsbuild:: outFile::", () => {
},
{
caption: "Make incremental build with change in file that doesnt affect dts",
edit: fs => appendText(fs, "/src/first/first_PART1.ts", "console.log(s);"),
commandLineArgs: ["--b", "/src/third", "--verbose", "--incremental"],
edit: sys => sys.appendFile("/home/src/workspaces/solution/first/first_PART1.ts", "console.log(s);"),
commandLineArgs: ["--b", "third", "--verbose", "--incremental"],
},
],
});
@@ -265,58 +241,57 @@ describe("unittests:: tsbuild:: outFile::", () => {
verifyTsc({
scenario: "outFile",
subScenario: "when input file text does not change but its modified time changes",
fs: getOutFileFs,
commandLineArgs: ["--b", "/src/third", "--verbose"],
sys: getOutFileSys,
commandLineArgs: ["--b", "third", "--verbose"],
edits: [
{
caption: "upstream project changes without changing file text",
edit: fs => {
const time = new Date(fs.time());
fs.utimesSync("/src/first/first_PART1.ts", time, time);
},
edit: sys => sys.setModifiedTime("/home/src/workspaces/solution/first/first_PART1.ts", sys.now()),
},
],
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "outFile",
subScenario: "builds till project specified",
fs: getOutFileFs,
commandLineArgs: ["--build", "/src/second/tsconfig.json"],
sys: getOutFileSys,
commandLineArgs: ["--build", "second/tsconfig.json"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["/src/third/tsconfig.json"], {});
sys.exit(builder.build("/src/second/tsconfig.json"));
const builder = ts.createSolutionBuilder(buildHost, ["/home/src/workspaces/solution/third/tsconfig.json"], {});
sys.exit(builder.build("/home/src/workspaces/solution/second/tsconfig.json"));
return buildHost.getPrograms;
},
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "outFile",
subScenario: "cleans till project specified",
fs: getOutFileFsAfterBuild,
commandLineArgs: ["--build", "--clean", "/src/second/tsconfig.json"],
sys: getOutFileSysAfterBuild,
commandLineArgs: ["--build", "--clean", "second/tsconfig.json"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["/src/third/tsconfig.json"], { verbose: true });
sys.exit(builder.clean("/src/second/tsconfig.json"));
const builder = ts.createSolutionBuilder(buildHost, ["/home/src/workspaces/solution/third/tsconfig.json"], { verbose: true });
sys.exit(builder.clean("/home/src/workspaces/solution/second/tsconfig.json"));
return buildHost.getPrograms;
},
});
verifyTsc({
scenario: "outFile",
subScenario: "non module projects without prepend",
fs: getOutFileFs,
commandLineArgs: ["--b", "/src/third", "--verbose"],
modifyFs: fs => {
sys: getOutFileSys,
commandLineArgs: ["--b", "third", "--verbose"],
modifySystem: sys => {
// Non Modules
replaceText(fs, "/src/first/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
replaceText(fs, "/src/second/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
replaceText(fs, "/src/third/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
sys.replaceFileText("/home/src/workspaces/solution/first/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
sys.replaceFileText("/home/src/workspaces/solution/second/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"composite": true,`, `"composite": true, "module": "none",`);
// Own file emit
replaceText(fs, "/src/first/tsconfig.json", `"outFile": "./bin/first-output.js",`, "");
replaceText(fs, "/src/second/tsconfig.json", `"outFile": "../2/second-output.js",`, "");
replaceText(fs, "/src/third/tsconfig.json", `"outFile": "./thirdjs/output/third-output.js",`, "");
sys.replaceFileText("/home/src/workspaces/solution/first/tsconfig.json", `"outFile": "./bin/first-output.js",`, "");
sys.replaceFileText("/home/src/workspaces/solution/second/tsconfig.json", `"outFile": "../2/second-output.js",`, "");
sys.replaceFileText("/home/src/workspaces/solution/third/tsconfig.json", `"outFile": "./thirdjs/output/third-output.js",`, "");
},
});
});
+47 -43
View File
@@ -1,40 +1,44 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import {
noChangeRun,
TestTscEdit,
TscCompileSystem,
verifyTsc,
VerifyTscWithEditsInput,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild - output file paths", () => {
describe("unittests:: tsbuild:: outputPaths::", () => {
const noChangeProject: TestTscEdit = {
edit: ts.noop,
caption: "Normal build without change, that does not block emit on error to show files that get emitted",
commandLineArgs: ["-p", "/src/tsconfig.json"],
commandLineArgs: ["-p", "/home/src/workspaces/project/tsconfig.json"],
};
const edits: TestTscEdit[] = [
noChangeRun,
noChangeProject,
];
function verify(input: Pick<VerifyTscWithEditsInput, "subScenario" | "fs" | "edits">, expectedOuptutNames: readonly string[]) {
function verify(input: Pick<VerifyTscWithEditsInput, "subScenario" | "sys" | "edits">, expectedOuptutNames: readonly string[]) {
verifyTsc({
scenario: "outputPaths",
commandLineArgs: ["--b", "/src/tsconfig.json", "-v"],
commandLineArgs: ["--b", "-v"],
...input,
});
it("verify getOutputFileNames", () => {
const sys = new fakes.System(input.fs().makeReadonly(), { executingFilePath: "/lib/tsc" }) as TscCompileSystem;
const sys = input.sys();
assert.deepEqual(
ts.getOutputFileNames(
ts.parseConfigFileWithSystem("/src/tsconfig.json", {}, /*extendedConfigCache*/ undefined, {}, sys, ts.noop)!,
"/src/src/index.ts",
ts.parseConfigFileWithSystem(
"/home/src/workspaces/project/tsconfig.json",
{},
/*extendedConfigCache*/ undefined,
{},
sys,
ts.noop,
)!,
"/home/src/workspaces/project/src/index.ts",
/*ignoreCase*/ false,
),
expectedOuptutNames,
@@ -44,78 +48,78 @@ describe("unittests:: tsbuild - output file paths", () => {
verify({
subScenario: "when rootDir is not specified",
fs: () =>
loadProjectFromFiles({
"/src/src/index.ts": "export const x = 10;",
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/index.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "dist",
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits,
}, ["/src/dist/index.js"]);
}, ["/home/src/workspaces/project/dist/index.js"]);
verify({
subScenario: "when rootDir is not specified and is composite",
fs: () =>
loadProjectFromFiles({
"/src/src/index.ts": "export const x = 10;",
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/index.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "dist",
composite: true,
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits,
}, ["/src/dist/src/index.js", "/src/dist/src/index.d.ts"]);
}, ["/home/src/workspaces/project/dist/src/index.js", "/home/src/workspaces/project/dist/src/index.d.ts"]);
verify({
subScenario: "when rootDir is specified",
fs: () =>
loadProjectFromFiles({
"/src/src/index.ts": "export const x = 10;",
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/index.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "dist",
rootDir: "src",
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits,
}, ["/src/dist/index.js"]);
}, ["/home/src/workspaces/project/dist/index.js"]);
verify({
subScenario: "when rootDir is specified but not all files belong to rootDir",
fs: () =>
loadProjectFromFiles({
"/src/src/index.ts": "export const x = 10;",
"/src/types/type.ts": "export type t = string;",
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/index.ts": "export const x = 10;",
"/home/src/workspaces/project/types/type.ts": "export type t = string;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "dist",
rootDir: "src",
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits,
}, ["/src/dist/index.js"]);
}, ["/home/src/workspaces/project/dist/index.js"]);
verify({
subScenario: "when rootDir is specified but not all files belong to rootDir and is composite",
fs: () =>
loadProjectFromFiles({
"/src/src/index.ts": "export const x = 10;",
"/src/types/type.ts": "export type t = string;",
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/index.ts": "export const x = 10;",
"/home/src/workspaces/project/types/type.ts": "export type t = string;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "dist",
rootDir: "src",
composite: true,
},
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits,
}, ["/src/dist/index.js", "/src/dist/index.d.ts"]);
}, ["/home/src/workspaces/project/dist/index.js", "/home/src/workspaces/project/dist/index.d.ts"]);
});
+86 -121
View File
@@ -1,135 +1,100 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as ts from "../../_namespaces/ts.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import {
baselinePrograms,
commandLineCallbacks,
toPathWithSystem,
} from "../helpers/baseline.js";
import {
TscCompileSystem,
verifyTscBaseline,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { commandLineCallbacks } from "../helpers/baseline.js";
import { verifyTsc } from "../helpers/tsc.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: Public API with custom transformers when passed to build", () => {
let sys: TscCompileSystem;
before(() => {
const inputFs = loadProjectFromFiles({
"/src/tsconfig.json": jsonToReadableText({
references: [
{ path: "./shared/tsconfig.json" },
{ path: "./webpack/tsconfig.json" },
],
files: [],
}),
"/src/shared/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
}),
"/src/shared/index.ts": `export function f1() { }
describe("unittests:: tsbuild:: PublicAPI:: with custom transformers when passed to build", () => {
verifyTsc({
scenario: "publicAPI",
subScenario: "build with custom transformers",
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/tsconfig.json": jsonToReadableText({
references: [
{ path: "./shared/tsconfig.json" },
{ path: "./webpack/tsconfig.json" },
],
files: [],
}),
"/home/src/workspaces/solution/shared/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
}),
"/home/src/workspaces/solution/shared/index.ts": `export function f1() { }
export class c { }
export enum e { }
// leading
export function f2() { } // trailing`,
"/src/webpack/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [{ path: "../shared/tsconfig.json" }],
}),
"/src/webpack/index.ts": `export function f2() { }
"/home/src/workspaces/solution/webpack/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
},
references: [{ path: "../shared/tsconfig.json" }],
}),
"/home/src/workspaces/solution/webpack/index.ts": `export function f2() { }
export class c2 { }
export enum e2 { }
// leading
export function f22() { } // trailing`,
}).makeReadonly();
const fs = inputFs.shadow();
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b"],
compile: sys => {
const { cb, getPrograms } = commandLineCallbacks(sys, /*originalReadCall*/ undefined);
const buildHost = ts.createSolutionBuilderHost(
sys,
/*createProgram*/ undefined,
ts.createDiagnosticReporter(sys, /*pretty*/ true),
ts.createBuilderStatusReporter(sys, /*pretty*/ true),
(errorCount, filesInError) => sys.write(ts.getErrorSummaryText(errorCount, filesInError, sys.newLine, sys)),
);
buildHost.afterProgramEmitAndDiagnostics = cb;
const builder = ts.createSolutionBuilder(
buildHost,
["/home/src/workspaces/solution/tsconfig.json"],
{ verbose: true },
);
const exitStatus = builder.build(
/*project*/ undefined,
/*cancellationToken*/ undefined,
/*writeFile*/ undefined,
project => {
const before: ts.TransformerFactory<ts.SourceFile> = context => {
return file => ts.visitEachChild(file, visit, context);
function visit(node: ts.Node): ts.VisitResult<ts.Node> {
switch (node.kind) {
case ts.SyntaxKind.FunctionDeclaration:
return visitFunction(node as ts.FunctionDeclaration);
default:
return ts.visitEachChild(node, visit, context);
}
}
function visitFunction(node: ts.FunctionDeclaration) {
ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `@before${project}`, /*hasTrailingNewLine*/ true);
return node;
}
};
// Create system
sys = new fakes.System(fs, { executingFilePath: "/lib/tsc" }) as TscCompileSystem;
sys.storeSignatureInfo = true;
fakes.patchHostForBuildInfoReadWrite(sys);
const commandLineArgs = ["--b", "/src/tsconfig.json"];
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
sys.exit = exitCode => sys.exitCode = exitCode;
const writtenFiles = sys.writtenFiles = new Set();
const originalWriteFile = sys.writeFile;
sys.writeFile = (fileName, content, writeByteOrderMark) => {
const path = toPathWithSystem(sys, fileName);
assert.isFalse(writtenFiles.has(path));
writtenFiles.add(path);
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
};
const { cb, getPrograms } = commandLineCallbacks(sys, /*originalReadCall*/ undefined);
const buildHost = ts.createSolutionBuilderHost(
sys,
/*createProgram*/ undefined,
ts.createDiagnosticReporter(sys, /*pretty*/ true),
ts.createBuilderStatusReporter(sys, /*pretty*/ true),
(errorCount, filesInError) => sys.write(ts.getErrorSummaryText(errorCount, filesInError, sys.newLine, sys)),
);
buildHost.afterProgramEmitAndDiagnostics = cb;
const builder = ts.createSolutionBuilder(buildHost, [commandLineArgs[1]], { verbose: true });
const exitStatus = builder.build(/*project*/ undefined, /*cancellationToken*/ undefined, /*writeFile*/ undefined, getCustomTransformers);
sys.exit(exitStatus);
sys.write(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}\n`);
const baseline: string[] = [];
baselinePrograms(baseline, getPrograms(), ts.emptyArray, /*baselineDependencies*/ false);
sys.write(baseline.join("\n"));
fs.makeReadonly();
sys.baseLine = () => {
const baseFsPatch = inputFs.diff(/*base*/ undefined, { baseIsNotShadowRoot: true });
const patch = fs.diff(inputFs, { includeChangedFileWithSameContent: true });
return {
file: `tsbuild/publicAPI/build-with-custom-transformers.js`,
text: `Input::
${baseFsPatch ? vfs.formatPatch(baseFsPatch) : ""}
Output::
${sys.output.join("")}
${patch ? vfs.formatPatch(patch) : ""}`,
};
};
function getCustomTransformers(project: string): ts.CustomTransformers {
const before: ts.TransformerFactory<ts.SourceFile> = context => {
return file => ts.visitEachChild(file, visit, context);
function visit(node: ts.Node): ts.VisitResult<ts.Node> {
switch (node.kind) {
case ts.SyntaxKind.FunctionDeclaration:
return visitFunction(node as ts.FunctionDeclaration);
default:
return ts.visitEachChild(node, visit, context);
}
}
function visitFunction(node: ts.FunctionDeclaration) {
ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `@before${project}`, /*hasTrailingNewLine*/ true);
return node;
}
};
const after: ts.TransformerFactory<ts.SourceFile> = context => {
return file => ts.visitEachChild(file, visit, context);
function visit(node: ts.Node): ts.VisitResult<ts.Node> {
switch (node.kind) {
case ts.SyntaxKind.VariableStatement:
return visitVariableStatement(node as ts.VariableStatement);
default:
return ts.visitEachChild(node, visit, context);
}
}
function visitVariableStatement(node: ts.VariableStatement) {
ts.addSyntheticLeadingComment(node, ts.SyntaxKind.SingleLineCommentTrivia, `@after${project}`);
return node;
}
};
return { before: [before], after: [after] };
}
const after: ts.TransformerFactory<ts.SourceFile> = context => {
return file => ts.visitEachChild(file, visit, context);
function visit(node: ts.Node): ts.VisitResult<ts.Node> {
switch (node.kind) {
case ts.SyntaxKind.VariableStatement:
return visitVariableStatement(node as ts.VariableStatement);
default:
return ts.visitEachChild(node, visit, context);
}
}
function visitVariableStatement(node: ts.VariableStatement) {
ts.addSyntheticLeadingComment(node, ts.SyntaxKind.SingleLineCommentTrivia, `@after${project}`);
return node;
}
};
return { before: [before], after: [after] };
},
);
sys.exit(exitStatus);
return getPrograms;
},
baselinePrograms: true,
});
after(() => {
sys = undefined!;
});
verifyTscBaseline(() => sys);
});
@@ -1,40 +1,35 @@
import { noop } from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import {
noChangeOnlyRuns,
verifyTsc,
} from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: with rootDir of project reference in parentDirectory", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = loadProjectFromFiles({
"/src/src/main/a.ts": dedent`
describe("unittests:: tsbuild:: projectReferenceWithRootDirInParent:: with rootDir of project reference in parentDirectory", () => {
function getProjectReferenceWithRootDirInParentSys() {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/src/main/a.ts": dedent`
import { b } from './b';
const a = b;
`,
"/src/src/main/b.ts": dedent`
"/home/src/workspaces/solution/src/main/b.ts": dedent`
export const b = 0;
`,
"/src/src/main/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/main/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
references: [
{ path: "../other" },
],
}),
"/src/src/other/other.ts": dedent`
"/home/src/workspaces/solution/src/other/other.ts": dedent`
export const Other = 0;
`,
"/src/src/other/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/src/other/tsconfig.json": jsonToReadableText({
extends: "../../tsconfig.base.json",
}),
"/src/tsconfig.base.json": jsonToReadableText({
"/home/src/workspaces/solution/tsconfig.base.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
@@ -46,43 +41,39 @@ describe("unittests:: tsbuild:: with rootDir of project reference in parentDirec
"node_modules",
],
}),
});
});
after(() => {
projFs = undefined!; // Release the contents
});
}, { currentDirectory: "/home/src/workspaces/solution" });
}
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "builds correctly",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/main", "/src/src/other"],
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/main", "/home/src/workspaces/solution/src/other"],
});
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "reports error for same tsbuildinfo file because no rootDir in the base",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/main", "--verbose"],
modifyFs: fs => replaceText(fs, "/src/tsconfig.base.json", `"rootDir": "./src/",`, ""),
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/main", "--verbose"],
modifySystem: sys => sys.replaceFileText("/home/src/workspaces/solution/tsconfig.base.json", `"rootDir": "./src/",`, ""),
});
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "reports error for same tsbuildinfo file",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/main", "--verbose"],
modifyFs: fs => {
fs.writeFileSync(
"/src/src/main/tsconfig.json",
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/main", "--verbose"],
modifySystem: sys => {
sys.writeFile(
"/home/src/workspaces/solution/src/main/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
references: [{ path: "../other" }],
}),
);
fs.writeFileSync(
"/src/src/other/tsconfig.json",
sys.writeFile(
"/home/src/workspaces/solution/src/other/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
}),
@@ -94,18 +85,18 @@ describe("unittests:: tsbuild:: with rootDir of project reference in parentDirec
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "reports error for same tsbuildinfo file without incremental",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/main", "--verbose"],
modifyFs: fs => {
fs.writeFileSync(
"/src/src/main/tsconfig.json",
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/main", "--verbose"],
modifySystem: sys => {
sys.writeFile(
"/home/src/workspaces/solution/src/main/tsconfig.json",
jsonToReadableText({
compilerOptions: { outDir: "../../dist/" },
references: [{ path: "../other" }],
}),
);
fs.writeFileSync(
"/src/src/other/tsconfig.json",
sys.writeFile(
"/home/src/workspaces/solution/src/other/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
}),
@@ -116,18 +107,18 @@ describe("unittests:: tsbuild:: with rootDir of project reference in parentDirec
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "reports error for same tsbuildinfo file without incremental with tsc",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/other", "--verbose"],
modifyFs: fs => {
fs.writeFileSync(
"/src/src/main/tsconfig.json",
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/other", "--verbose"],
modifySystem: sys => {
sys.writeFile(
"/home/src/workspaces/solution/src/main/tsconfig.json",
jsonToReadableText({
compilerOptions: { outDir: "../../dist/" },
references: [{ path: "../other" }],
}),
);
fs.writeFileSync(
"/src/src/other/tsconfig.json",
sys.writeFile(
"/home/src/workspaces/solution/src/other/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
}),
@@ -137,7 +128,7 @@ describe("unittests:: tsbuild:: with rootDir of project reference in parentDirec
{
caption: "Running tsc on main",
edit: noop,
commandLineArgs: ["-p", "/src/src/main"],
commandLineArgs: ["-p", "src/main"],
},
],
});
@@ -145,20 +136,20 @@ describe("unittests:: tsbuild:: with rootDir of project reference in parentDirec
verifyTsc({
scenario: "projectReferenceWithRootDirInParent",
subScenario: "reports no error when tsbuildinfo differ",
fs: () => projFs,
commandLineArgs: ["--b", "/src/src/main/tsconfig.main.json", "--verbose"],
modifyFs: fs => {
fs.renameSync("/src/src/main/tsconfig.json", "/src/src/main/tsconfig.main.json");
fs.renameSync("/src/src/other/tsconfig.json", "/src/src/other/tsconfig.other.json");
fs.writeFileSync(
"/src/src/main/tsconfig.main.json",
sys: getProjectReferenceWithRootDirInParentSys,
commandLineArgs: ["--b", "src/main/tsconfig.main.json", "--verbose"],
modifySystem: sys => {
sys.renameFile("/home/src/workspaces/solution/src/main/tsconfig.json", "/home/src/workspaces/solution/src/main/tsconfig.main.json");
sys.renameFile("/home/src/workspaces/solution/src/other/tsconfig.json", "/home/src/workspaces/solution/src/other/tsconfig.other.json");
sys.writeFile(
"/home/src/workspaces/solution/src/main/tsconfig.main.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
references: [{ path: "../other/tsconfig.other.json" }],
}),
);
fs.writeFileSync(
"/src/src/other/tsconfig.other.json",
sys.writeFile(
"/home/src/workspaces/solution/src/other/tsconfig.other.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "../../dist/" },
}),
@@ -6,22 +6,19 @@ import {
verifyTsc,
VerifyTscWithEditsInput,
} from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: with resolveJsonModule option on project resolveJsonModuleAndComposite", () => {
describe("unittests:: tsbuild:: with resolveJsonModule:: option on project resolveJsonModuleAndComposite", () => {
function getProjFs(tsconfigFiles: object, additionalCompilerOptions?: CompilerOptions) {
return loadProjectFromFiles({
"/src/src/hello.json": jsonToReadableText({
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/project/src/hello.json": jsonToReadableText({
hello: "world",
}),
"/src/src/index.ts": dedent`
"/home/src/workspaces/solution/project/src/index.ts": dedent`
import hello from "./hello.json"
export default hello.hello
`,
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
moduleResolution: "node",
@@ -35,25 +32,25 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project resolve
},
...tsconfigFiles,
}),
});
}, { currentDirectory: "/home/src/workspaces/solution" });
}
function verfiyJson(
input: Pick<VerifyTscWithEditsInput, "subScenario" | "modifyFs" | "edits"> | string,
input: Pick<VerifyTscWithEditsInput, "subScenario" | "modifySystem" | "edits"> | string,
tsconfigFiles: object,
additionalCompilerOptions?: CompilerOptions,
) {
if (typeof input === "string") input = { subScenario: input };
verifyTsc({
scenario: "resolveJsonModule",
fs: () => getProjFs(tsconfigFiles, additionalCompilerOptions),
commandLineArgs: ["--b", "/src/tsconfig.json", "--v", "--explainFiles", "--listEmittedFiles"],
sys: () => getProjFs(tsconfigFiles, additionalCompilerOptions),
commandLineArgs: ["--b", "project", "--v", "--explainFiles", "--listEmittedFiles"],
...input,
});
verifyTsc({
scenario: "resolveJsonModule",
fs: () => getProjFs(tsconfigFiles, { composite: undefined, ...additionalCompilerOptions }),
commandLineArgs: ["--b", "/src/tsconfig.json", "--v", "--explainFiles", "--listEmittedFiles"],
sys: () => getProjFs(tsconfigFiles, { composite: undefined, ...additionalCompilerOptions }),
commandLineArgs: ["--b", "project", "--v", "--explainFiles", "--listEmittedFiles"],
...input,
subScenario: `${input.subScenario} non-composite`,
});
@@ -73,9 +70,9 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project resolve
verfiyJson({
subScenario: "include only with json not in rootDir",
modifyFs: fs => {
fs.renameSync("/src/src/hello.json", "/src/hello.json");
replaceText(fs, "/src/src/index.ts", "./hello.json", "../hello.json");
modifySystem: sys => {
sys.renameFile("/home/src/workspaces/solution/project/src/hello.json", "/home/src/workspaces/solution/project/hello.json");
sys.replaceFileText("/home/src/workspaces/solution/project/src/index.ts", "./hello.json", "../hello.json");
},
}, {
include: [
@@ -85,9 +82,9 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project resolve
verfiyJson({
subScenario: "include only with json without rootDir but outside configDirectory",
modifyFs: fs => {
fs.renameSync("/src/src/hello.json", "/hello.json");
replaceText(fs, "/src/src/index.ts", "./hello.json", "../../hello.json");
modifySystem: sys => {
sys.renameFile("/home/src/workspaces/solution/project/src/hello.json", "/home/src/workspaces/solution/hello.json");
sys.replaceFileText("/home/src/workspaces/solution/project/src/index.ts", "./hello.json", "../../hello.json");
},
}, {
include: [
@@ -104,9 +101,9 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project resolve
verfiyJson({
subScenario: "include of json along with other include and file name matches ts file",
modifyFs: fs => {
fs.renameSync("/src/src/hello.json", "/src/src/index.json");
replaceText(fs, "/src/src/index.ts", "hello.json", "index.json");
modifySystem: sys => {
sys.renameFile("/home/src/workspaces/solution/project/src/hello.json", "/home/src/workspaces/solution/project/src/index.json");
sys.replaceFileText("/home/src/workspaces/solution/project/src/index.ts", "hello.json", "index.json");
},
}, {
include: [
@@ -152,25 +149,25 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project resolve
}, { outDir: undefined });
});
describe("unittests:: tsbuild:: with resolveJsonModule option on project importJsonFromProjectReference", () => {
describe("unittests:: tsbuild:: with resolveJsonModule:: option on project importJsonFromProjectReference", () => {
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "importing json module from project reference",
fs: () =>
loadProjectFromFiles({
"/src/strings/foo.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/project/strings/foo.json": jsonToReadableText({
foo: "bar baz",
}),
"/src/strings/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/strings/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.json",
include: ["foo.json"],
references: [],
}),
"/src/main/index.ts": dedent`
import { foo } from '../strings/foo.json';
console.log(foo);
`,
"/src/main/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/main/index.ts": dedent`
import { foo } from '../strings/foo.json';
console.log(foo);
`,
"/home/src/workspaces/solution/project/main/tsconfig.json": jsonToReadableText({
extends: "../tsconfig.json",
include: [
"./**/*.ts",
@@ -179,7 +176,7 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project importJ
path: "../strings/tsconfig.json",
}],
}),
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
@@ -195,8 +192,8 @@ describe("unittests:: tsbuild:: with resolveJsonModule option on project importJ
],
files: [],
}),
}),
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose", "--explainFiles"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--b", "project", "--verbose", "--explainFiles"],
edits: noChangeOnlyRuns,
});
});
+72 -87
View File
@@ -1,35 +1,29 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { forEachScenarioForRootsFromReferencedProject } from "../helpers/projectRoots.js";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
} from "../helpers/vfs.js";
import { verifyTsc } from "../helpers/tsc.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: roots::", () => {
verifyTsc({
scenario: "roots",
subScenario: `when two root files are consecutive`,
commandLineArgs: ["--b", "/src/tsconfig.json", "-v"],
fs: () =>
loadProjectFromFiles({
"/src/file1.ts": `export const x = "hello";`,
"/src/file2.ts": `export const y = "world";`,
"/src/tsconfig.json": jsonToReadableText({
commandLineArgs: ["--b", "-v"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/file1.ts": `export const x = "hello";`,
"/home/src/workspaces/project/file2.ts": `export const y = "world";`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
include: ["*.ts"],
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "delete file1",
edit: fs => {
fs.rimrafSync("/src/file1.ts");
fs.rimrafSync("/src/file1.js");
fs.rimrafSync("/src/file1.d.ts");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/project/file1.ts");
sys.rimrafSync("/home/src/workspaces/project/file1.js");
sys.rimrafSync("/home/src/workspaces/project/file1.d.ts");
},
}],
});
@@ -37,24 +31,24 @@ describe("unittests:: tsbuild:: roots::", () => {
verifyTsc({
scenario: "roots",
subScenario: `when multiple root files are consecutive`,
commandLineArgs: ["--b", "/src/tsconfig.json", "-v"],
fs: () =>
loadProjectFromFiles({
"/src/file1.ts": `export const x = "hello";`,
"/src/file2.ts": `export const y = "world";`,
"/src/file3.ts": `export const y = "world";`,
"/src/file4.ts": `export const y = "world";`,
"/src/tsconfig.json": jsonToReadableText({
commandLineArgs: ["--b", "-v"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/file1.ts": `export const x = "hello";`,
"/home/src/workspaces/project/file2.ts": `export const y = "world";`,
"/home/src/workspaces/project/file3.ts": `export const y = "world";`,
"/home/src/workspaces/project/file4.ts": `export const y = "world";`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
include: ["*.ts"],
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "delete file1",
edit: fs => {
fs.rimrafSync("/src/file1.ts");
fs.rimrafSync("/src/file1.js");
fs.rimrafSync("/src/file1.d.ts");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/project/file1.ts");
sys.rimrafSync("/home/src/workspaces/project/file1.js");
sys.rimrafSync("/home/src/workspaces/project/file1.d.ts");
},
}],
});
@@ -62,26 +56,26 @@ describe("unittests:: tsbuild:: roots::", () => {
verifyTsc({
scenario: "roots",
subScenario: `when files are not consecutive`,
commandLineArgs: ["--b", "/src/tsconfig.json", "-v"],
fs: () =>
loadProjectFromFiles({
"/src/file1.ts": `export const x = "hello";`,
"/src/random.d.ts": `export const random = "world";`,
"/src/file2.ts": dedent`
import { random } from "./random";
export const y = "world";
`,
"/src/tsconfig.json": jsonToReadableText({
commandLineArgs: ["--b", "-v"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/file1.ts": `export const x = "hello";`,
"/home/src/workspaces/project/random.d.ts": `export const random = "world";`,
"/home/src/workspaces/project/file2.ts": dedent`
import { random } from "./random";
export const y = "world";
`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
include: ["file*.ts"],
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "delete file1",
edit: fs => {
fs.rimrafSync("/src/file1.ts");
fs.rimrafSync("/src/file1.js");
fs.rimrafSync("/src/file1.d.ts");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/project/file1.ts");
sys.rimrafSync("/home/src/workspaces/project/file1.js");
sys.rimrafSync("/home/src/workspaces/project/file1.d.ts");
},
}],
});
@@ -89,64 +83,55 @@ describe("unittests:: tsbuild:: roots::", () => {
verifyTsc({
scenario: "roots",
subScenario: `when consecutive and non consecutive are mixed`,
commandLineArgs: ["--b", "/src/tsconfig.json", "-v"],
fs: () =>
loadProjectFromFiles({
"/src/file1.ts": `export const x = "hello";`,
"/src/file2.ts": `export const y = "world";`,
"/src/random.d.ts": `export const random = "hello";`,
"/src/nonconsecutive.ts": dedent`
commandLineArgs: ["--b", "-v"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/file1.ts": `export const x = "hello";`,
"/home/src/workspaces/project/file2.ts": `export const y = "world";`,
"/home/src/workspaces/project/random.d.ts": `export const random = "hello";`,
"/home/src/workspaces/project/nonconsecutive.ts": dedent`
import { random } from "./random";
export const nonConsecutive = "hello";
`,
"/src/random1.d.ts": `export const random = "hello";`,
"/src/asArray1.ts": dedent`
"/home/src/workspaces/project/random1.d.ts": `export const random = "hello";`,
"/home/src/workspaces/project/asArray1.ts": dedent`
import { random } from "./random1";
export const x = "hello";
`,
"/src/asArray2.ts": `export const x = "hello";`,
"/src/asArray3.ts": `export const x = "hello";`,
"/src/random2.d.ts": `export const random = "hello";`,
"/src/anotherNonConsecutive.ts": dedent`
"/home/src/workspaces/project/asArray2.ts": `export const x = "hello";`,
"/home/src/workspaces/project/asArray3.ts": `export const x = "hello";`,
"/home/src/workspaces/project/random2.d.ts": `export const random = "hello";`,
"/home/src/workspaces/project/anotherNonConsecutive.ts": dedent`
import { random } from "./random2";
export const nonConsecutive = "hello";
`,
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
include: ["file*.ts", "nonconsecutive*.ts", "asArray*.ts", "anotherNonConsecutive.ts"],
}),
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "delete file1",
edit: fs => {
fs.rimrafSync("/src/file1.ts");
fs.rimrafSync("/src/file1.js");
fs.rimrafSync("/src/file1.d.ts");
edit: sys => {
sys.rimrafSync("/home/src/workspaces/project/file1.ts");
sys.rimrafSync("/home/src/workspaces/project/file1.js");
sys.rimrafSync("/home/src/workspaces/project/file1.d.ts");
},
}],
});
describe("when root file is from referenced project", () => {
forEachScenarioForRootsFromReferencedProject((subScenario, getFsContents) => {
verifyTsc({
scenario: "roots",
subScenario,
commandLineArgs: ["--b", "projects/server", "-v", "--traceResolution", "--explainFiles"],
fs: () => loadProjectFromFiles(getFsContents(), { cwd: "/home/src/workspaces" }),
edits: [
noChangeRun,
{
caption: "edit logging file",
edit: fs => appendText(fs, "/home/src/workspaces/projects/shared/src/logging.ts", "export const x = 10;"),
},
noChangeRun,
{
caption: "delete random file",
edit: fs => fs.unlinkSync("/home/src/workspaces/projects/shared/src/random.ts"),
},
noChangeRun,
],
});
});
forEachScenarioForRootsFromReferencedProject(
/*forTsserver*/ false,
(subScenario, sys, edits) => {
verifyTsc({
scenario: "roots",
subScenario,
commandLineArgs: ["--b", "projects/server", "-v", "--traceResolution", "--explainFiles"],
sys,
edits: edits(),
});
},
);
});
});
+129 -174
View File
@@ -1,69 +1,38 @@
import * as fakes from "../../_namespaces/fakes.js";
import * as Harness from "../../_namespaces/Harness.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { patchHostForBuildInfoReadWrite } from "../helpers/baseline.js";
import { getTypeScriptLibTestLocation } from "../helpers/contents.js";
import {
libContent,
libPath,
} from "../helpers/contents.js";
import {
getFsForSampleProjectReferences,
getSysForSampleProjectReferences,
getSysForSampleProjectReferencesBuilt,
} from "../helpers/sampleProjectReferences.js";
import { createSolutionBuilderHostForBaseline } from "../helpers/solutionBuilder.js";
import {
createSolutionBuilderHostForBaseline,
verifySolutionBuilderWithDifferentTsVersion,
} from "../helpers/solutionBuilder.js";
import {
noChangeOnlyRuns,
noChangeRun,
testTscCompileLike,
TestTscEdit,
TscCompileSystem,
verifyTsc,
verifyTscCompileLike,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
prependText,
replaceText,
} from "../helpers/vfs.js";
import {
changeToHostTrackingWrittenFiles,
libFile,
SerializeOutputOrder,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: on 'sample1' project", () => {
let projFs: vfs.FileSystem;
let projFsWithBuild: vfs.FileSystem;
before(() => {
projFs = getFsForSampleProjectReferences();
});
after(() => {
projFs = undefined!; // Release the contents
projFsWithBuild = undefined!;
});
function getSampleFsAfterBuild() {
if (projFsWithBuild) return projFsWithBuild;
const fs = projFs.shadow();
const sys = new fakes.System(fs, { executingFilePath: libFile.path });
const host = createSolutionBuilderHostForBaseline(sys as TscCompileSystem);
const builder = ts.createSolutionBuilder(host, ["tests"], {});
builder.build();
fs.makeReadonly();
return projFsWithBuild = fs;
}
describe("sanity check of clean build of 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "builds correctly when outDir is specified",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"logic/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, declaration: true, sourceMap: true, outDir: "outDir" },
@@ -75,10 +44,10 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "builds correctly when declarationDir is specified",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"logic/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, declaration: true, sourceMap: true, declarationDir: "out/decls" },
@@ -90,9 +59,9 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "builds correctly when project is not composite or doesnt have any references",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "core", "--verbose"],
modifyFs: fs => replaceText(fs, "core/tsconfig.json", `"composite": true,`, ""),
modifySystem: sys => sys.replaceFileText("core/tsconfig.json", `"composite": true,`, ""),
});
});
@@ -100,7 +69,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "does not write any files in a dry build",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--dry"],
});
});
@@ -109,32 +78,34 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "removes all files it built",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "tests", "--clean"],
edits: noChangeOnlyRuns,
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "sample1",
subScenario: "cleans till project specified",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "logic", "--clean"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["third/tsconfig.json"], {});
const builder = ts.createSolutionBuilder(buildHost, ["tests"], {});
sys.exit(builder.clean("logic"));
return buildHost.getPrograms;
},
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "sample1",
subScenario: "cleaning project in not build order doesnt throw error",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "logic2", "--clean"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["third/tsconfig.json"], {});
const builder = ts.createSolutionBuilder(buildHost, ["tests"], {});
sys.exit(builder.clean("logic2"));
return buildHost.getPrograms;
},
});
});
@@ -143,7 +114,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "always builds under with force option",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--force"],
edits: noChangeOnlyRuns,
});
@@ -153,25 +124,22 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "can detect when and what to rebuild",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "tests", "--verbose"],
edits: [
// Update a file in the leaf node (tests), only it should rebuild the last one
{
caption: "Only builds the leaf node project",
edit: fs => fs.writeFileSync("tests/index.ts", "const m = 10;"),
edit: sys => sys.writeFile("tests/index.ts", "const m = 10;"),
},
// Update a file in the parent (without affecting types), should get fast downstream builds
{
caption: "Detects type-only changes in upstream projects",
edit: fs => replaceText(fs, "core/index.ts", "HELLO WORLD", "WELCOME PLANET"),
edit: sys => sys.replaceFileText("core/index.ts", "HELLO WORLD", "WELCOME PLANET"),
},
{
caption: "rebuilds when tsconfig changes",
edit: fs => {
replaceText(fs, "tests/tsconfig.json", `"composite": true`, `"composite": true, "target": "es2020"`);
fs.writeFileSync(libPath("es2020.full"), libContent);
},
edit: sys => sys.replaceFileText("tests/tsconfig.json", `"composite": true`, `"composite": true, "target": "es2020"`),
},
],
});
@@ -179,32 +147,27 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "when input file text does not change but its modified time changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--verbose"],
edits: [
{
caption: "upstream project changes without changing file text",
edit: fs => {
const time = new Date(fs.time());
fs.utimesSync("core/index.ts", time, time);
},
},
],
edits: [{
caption: "upstream project changes without changing file text",
edit: sys => sys.setModifiedTime("core/index.ts", sys.now()),
}],
});
verifyTsc({
scenario: "sample1",
subScenario: "when declarationMap changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--verbose"],
edits: [
{
caption: "Disable declarationMap",
edit: fs => replaceText(fs, "core/tsconfig.json", `"declarationMap": true,`, `"declarationMap": false,`),
edit: sys => sys.replaceFileText("core/tsconfig.json", `"declarationMap": true,`, `"declarationMap": false,`),
},
{
caption: "Enable declarationMap",
edit: fs => replaceText(fs, "core/tsconfig.json", `"declarationMap": false,`, `"declarationMap": true,`),
edit: sys => sys.replaceFileText("core/tsconfig.json", `"declarationMap": false,`, `"declarationMap": true,`),
},
],
});
@@ -212,109 +175,105 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "indicates that it would skip builds during a dry build",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "tests", "--dry"],
});
verifyTsc({
scenario: "sample1",
subScenario: "rebuilds from start if force option is set",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "tests", "--verbose", "--force"],
});
verifyTsc({
scenario: "sample1",
subScenario: "tsbuildinfo has error",
fs: () =>
loadProjectFromFiles({
"/src/project/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": "{}",
"/src/project/tsconfig.tsbuildinfo": "Some random string",
}),
commandLineArgs: ["--b", "src/project", "-i", "-v"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": "{}",
"/home/src/workspaces/project/tsconfig.tsbuildinfo": "Some random string",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--b", "-i", "-v"],
edits: [{
caption: "tsbuildinfo written has error",
edit: fs => prependText(fs, "/src/project/tsconfig.tsbuildinfo", "Some random string"),
edit: sys => sys.prependFile("/home/src/workspaces/project/tsconfig.tsbuildinfo", "Some random string"),
}],
});
verifyTscCompileLike(testTscCompileLike, {
verifySolutionBuilderWithDifferentTsVersion({
scenario: "sample1",
subScenario: "rebuilds completely when version in tsbuildinfo doesnt match ts version",
fs: getSampleFsAfterBuild,
sys: getSysForSampleProjectReferencesBuilt,
commandLineArgs: ["--b", "tests", "--verbose"],
compile: sys => {
// Buildinfo will have version which does not match with current ts version
const buildHost = createSolutionBuilderHostForBaseline(sys, "FakeTSCurrentVersion");
const builder = ts.createSolutionBuilder(buildHost, ["tests"], { verbose: true });
sys.exit(builder.build());
},
});
}, ["tests"]);
verifyTscCompileLike(testTscCompileLike, {
verifySolutionBuilderWithDifferentTsVersion({
scenario: "sample1",
subScenario: "does not rebuild if there is no program and bundle in the ts build info event if version doesnt match ts version",
fs: () => {
const fs = projFs.shadow();
const host = fakes.SolutionBuilderHost.create(fs, /*options*/ undefined, /*setParentNodes*/ undefined, ts.createAbstractBuilder);
sys: () => {
const sys = getSysForSampleProjectReferences();
patchHostForBuildInfoReadWrite(sys);
const host = ts.createSolutionBuilderHost(
sys,
ts.createAbstractBuilder,
ts.createDiagnosticReporter(sys, /*pretty*/ true),
ts.createBuilderStatusReporter(sys, /*pretty*/ true),
);
const builder = ts.createSolutionBuilder(host, ["tests"], { verbose: true });
builder.build();
fs.makeReadonly();
return fs;
sys.clearOutput();
return sys;
},
commandLineArgs: ["--b", "tests", "--verbose"],
compile: sys => {
// Buildinfo will have version which does not match with current ts version
const buildHost = createSolutionBuilderHostForBaseline(sys, "FakeTSCurrentVersion");
const builder = ts.createSolutionBuilder(buildHost, ["tests"], { verbose: true });
sys.exit(builder.build());
},
});
}, ["tests"]);
verifyTsc({
scenario: "sample1",
subScenario: "rebuilds when extended config file changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--verbose"],
modifyFs: fs => {
fs.writeFileSync("tests/tsconfig.base.json", jsonToReadableText({ compilerOptions: { target: "es5" } }));
replaceText(fs, "tests/tsconfig.json", `"references": [`, `"extends": "./tsconfig.base.json", "references": [`);
modifySystem: sys => {
sys.writeFile("tests/tsconfig.base.json", jsonToReadableText({ compilerOptions: { target: "es5" } }));
sys.replaceFileText("tests/tsconfig.json", `"references": [`, `"extends": "./tsconfig.base.json", "references": [`);
},
edits: [{
caption: "incremental-declaration-changes",
edit: fs => fs.writeFileSync("tests/tsconfig.base.json", jsonToReadableText({ compilerOptions: {} })),
edit: sys => sys.writeFile("tests/tsconfig.base.json", jsonToReadableText({ compilerOptions: {} })),
}],
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "sample1",
subScenario: "builds till project specified",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--build", "logic/tsconfig.json"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["tests"], {});
sys.exit(builder.build("logic/tsconfig.json"));
return buildHost.getPrograms;
},
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "sample1",
subScenario: "building project in not build order doesnt throw error",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--build", "logic2/tsconfig.json"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["tests"], {});
sys.exit(builder.build("logic2/tsconfig.json"));
return buildHost.getPrograms;
},
});
it("building using getNextInvalidatedProject", () => {
const baseline: string[] = [];
const system = changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(
patchHostForBuildInfoReadWrite(
getSysForSampleProjectReferences(),
),
);
@@ -327,7 +286,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyBuildNextResult(); // logic
verifyBuildNextResult(); // tests
verifyBuildNextResult(); // All Done
Harness.Baseline.runBaseline(`tsbuild/sample1/building-using-getNextInvalidatedProject.js`, baseline.join("\r\n"));
Baseline.runBaseline(`tsbuild/sample1/building-using-getNextInvalidatedProject.js`, baseline.join("\r\n"));
function verifyBuildNextResult() {
const project = builder.getNextInvalidatedProject();
@@ -337,15 +296,16 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
}
});
verifyTscCompileLike(testTscCompileLike, {
verifyTsc({
scenario: "sample1",
subScenario: "building using buildReferencedProject",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--build", "logic2/tsconfig.json"],
compile: sys => {
const buildHost = createSolutionBuilderHostForBaseline(sys);
const builder = ts.createSolutionBuilder(buildHost, ["tests"], { verbose: true });
sys.exit(builder.buildReferences("tests"));
return buildHost.getPrograms;
},
});
});
@@ -354,9 +314,9 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: "builds downstream projects even if upstream projects have errors",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--verbose"],
modifyFs: fs => replaceText(fs, "logic/index.ts", "c.multiply(10, 15)", `c.muitply()`),
modifySystem: sys => sys.replaceFileText("logic/index.ts", "c.multiply(10, 15)", `c.muitply()`),
edits: noChangeOnlyRuns,
});
@@ -364,14 +324,14 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
verifyTsc({
scenario: "sample1",
subScenario: `skips builds downstream projects if upstream projects have errors with stopBuildOnErrors${skipReferenceCoreFromTest ? " when test does not reference core" : ""}`,
fs: () => getFsForSampleProjectReferences(/*withNodeNext*/ undefined, skipReferenceCoreFromTest),
sys: () => getSysForSampleProjectReferences(/*withNodeNext*/ undefined, skipReferenceCoreFromTest),
commandLineArgs: ["--b", "tests", "--verbose", "--stopBuildOnErrors"],
modifyFs: fs => appendText(fs, "core/index.ts", `multiply();`),
modifySystem: sys => sys.appendFile("core/index.ts", `multiply();`),
edits: [
noChangeRun,
{
caption: "fix error",
edit: fs => replaceText(fs, "core/index.ts", "multiply();", ""),
edit: sys => sys.replaceFileText("core/index.ts", "multiply();", ""),
},
],
})
@@ -382,7 +342,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
it("invalidates projects correctly", () => {
const baseline: string[] = [];
const system = changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(
patchHostForBuildInfoReadWrite(
getSysForSampleProjectReferences(),
),
);
@@ -403,7 +363,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
// Rebuild this project
system.appendFile("logic/index.ts", `export class cNew {}`);
verifyInvalidation("Dts change to Logic");
Harness.Baseline.runBaseline(`tsbuild/sample1/invalidates-projects-correctly.js`, baseline.join("\r\n"));
Baseline.runBaseline(`tsbuild/sample1/invalidates-projects-correctly.js`, baseline.join("\r\n"));
function verifyInvalidation(heading: string) {
// Rebuild this project
@@ -426,9 +386,8 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
const coreChanges: TestTscEdit[] = [
{
caption: "incremental-declaration-changes",
edit: fs =>
appendText(
fs,
edit: sys =>
sys.appendFile(
"core/index.ts",
`
export class someClass { }`,
@@ -436,9 +395,8 @@ export class someClass { }`,
},
{
caption: "incremental-declaration-doesnt-change",
edit: fs =>
appendText(
fs,
edit: sys =>
sys.appendFile(
"core/index.ts",
`
class someClass2 { }`,
@@ -451,21 +409,21 @@ class someClass2 { }`,
verifyTsc({
scenario: "sample1",
subScenario: "listFiles",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--listFiles"],
edits: coreChanges,
});
verifyTsc({
scenario: "sample1",
subScenario: "listEmittedFiles",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--listEmittedFiles"],
edits: coreChanges,
});
verifyTsc({
scenario: "sample1",
subScenario: "explainFiles",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--explainFiles", "--v"],
edits: coreChanges,
});
@@ -474,7 +432,7 @@ class someClass2 { }`,
describe("emit output", () => {
verifyTsc({
subScenario: "sample",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
scenario: "sample1",
commandLineArgs: ["--b", "tests", "--verbose"],
baselineSourceMap: true,
@@ -483,9 +441,8 @@ class someClass2 { }`,
...coreChanges,
{
caption: "when logic config changes declaration dir",
edit: fs =>
replaceText(
fs,
edit: sys =>
sys.replaceFileText(
"logic/tsconfig.json",
`"declaration": true,`,
`"declaration": true,
@@ -499,10 +456,9 @@ class someClass2 { }`,
verifyTsc({
scenario: "sample1",
subScenario: "when logic specifies tsBuildInfoFile",
fs: () => projFs,
modifyFs: fs =>
replaceText(
fs,
sys: getSysForSampleProjectReferences,
modifySystem: sys =>
sys.replaceFileText(
"logic/tsconfig.json",
`"composite": true,`,
`"composite": true,
@@ -515,11 +471,11 @@ class someClass2 { }`,
verifyTsc({
subScenario: "when declaration option changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
scenario: "sample1",
commandLineArgs: ["--b", "core", "--verbose"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: {
@@ -530,28 +486,27 @@ class someClass2 { }`,
),
edits: [{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "core/tsconfig.json", `"incremental": true,`, `"incremental": true, "declaration": true,`),
edit: sys => sys.replaceFileText("core/tsconfig.json", `"incremental": true,`, `"incremental": true, "declaration": true,`),
}],
});
verifyTsc({
subScenario: "when target option changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
scenario: "sample1",
commandLineArgs: ["--b", "core", "--verbose"],
modifyFs: fs => {
fs.writeFileSync(
libPath("esnext.full"),
modifySystem: sys => {
sys.writeFile(
getTypeScriptLibTestLocation("esnext.full"),
`/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />`,
);
fs.writeFileSync(libPath("esnext"), libContent);
fs.writeFileSync(
sys.writeFile(
libFile.path,
`/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />`,
);
fs.writeFileSync(
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: {
@@ -565,17 +520,17 @@ class someClass2 { }`,
},
edits: [{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "core/tsconfig.json", "esnext", "es5"),
edit: sys => sys.replaceFileText("core/tsconfig.json", "esnext", "es5"),
}],
});
verifyTsc({
subScenario: "when module option changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
scenario: "sample1",
commandLineArgs: ["--b", "core", "--verbose"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: {
@@ -586,17 +541,17 @@ class someClass2 { }`,
),
edits: [{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "core/tsconfig.json", `"module": "commonjs"`, `"module": "amd"`),
edit: sys => sys.replaceFileText("core/tsconfig.json", `"module": "commonjs"`, `"module": "amd"`),
}],
});
verifyTsc({
subScenario: "when esModuleInterop option changes",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
scenario: "sample1",
commandLineArgs: ["--b", "tests", "--verbose"],
modifyFs: fs =>
fs.writeFileSync(
modifySystem: sys =>
sys.writeFile(
"tests/tsconfig.json",
jsonToReadableText({
references: [
@@ -615,41 +570,41 @@ class someClass2 { }`,
),
edits: [{
caption: "incremental-declaration-changes",
edit: fs => replaceText(fs, "tests/tsconfig.json", `"esModuleInterop": false`, `"esModuleInterop": true`),
edit: sys => sys.replaceFileText("tests/tsconfig.json", `"esModuleInterop": false`, `"esModuleInterop": true`),
}],
});
verifyTsc({
scenario: "sample1",
subScenario: "reports error if input file is missing",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--v"],
modifyFs: fs => {
fs.writeFileSync(
modifySystem: sys => {
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true },
files: ["anotherModule.ts", "index.ts", "some_decl.d.ts"],
}),
);
fs.unlinkSync("core/anotherModule.ts");
sys.deleteFile("core/anotherModule.ts");
},
});
verifyTsc({
scenario: "sample1",
subScenario: "reports error if input file is missing with force",
fs: () => projFs,
sys: getSysForSampleProjectReferences,
commandLineArgs: ["--b", "tests", "--v", "--f"],
modifyFs: fs => {
fs.writeFileSync(
modifySystem: sys => {
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true },
files: ["anotherModule.ts", "index.ts", "some_decl.d.ts"],
}),
);
fs.unlinkSync("core/anotherModule.ts");
sys.deleteFile("core/anotherModule.ts");
},
});
});
@@ -1,32 +1,16 @@
import * as vfs from "../../_namespaces/vfs.js";
import { jsonToReadableText } from "../helpers.js";
import { getFsContentsForTransitiveReferences } from "../helpers/transitiveReferences.js";
import { getSysForTransitiveReferences } from "../helpers/transitiveReferences.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { libFile } from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuild:: when project reference is referenced transitively", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = loadProjectFromFiles(
getFsContentsForTransitiveReferences(),
{
cwd: "/user/username/projects/transitiveReferences",
executingFilePath: libFile.path,
},
);
});
after(() => {
projFs = undefined!; // Release the contents
});
function modifyFsBTsToNonRelativeImport(fs: vfs.FileSystem, moduleResolution: "node" | "classic") {
fs.writeFileSync(
describe("unittests:: tsbuild:: transitiveReferences:: when project reference is referenced transitively", () => {
function modifyFsBTsToNonRelativeImport(sys: TestServerHost, moduleResolution: "node" | "classic") {
sys.writeFile(
"b.ts",
`import {A} from 'a';
export const b = new A();`,
);
fs.writeFileSync(
sys.writeFile(
"tsconfig.b.json",
jsonToReadableText({
compilerOptions: {
@@ -42,23 +26,23 @@ export const b = new A();`,
verifyTsc({
scenario: "transitiveReferences",
subScenario: "builds correctly",
fs: () => projFs,
sys: getSysForTransitiveReferences,
commandLineArgs: ["--b", "tsconfig.c.json", "--listFiles"],
});
verifyTsc({
scenario: "transitiveReferences",
subScenario: "builds correctly when the referenced project uses different module resolution",
fs: () => projFs,
sys: getSysForTransitiveReferences,
commandLineArgs: ["--b", "tsconfig.c.json", "--listFiles"],
modifyFs: fs => modifyFsBTsToNonRelativeImport(fs, "classic"),
modifySystem: sys => modifyFsBTsToNonRelativeImport(sys, "classic"),
});
verifyTsc({
scenario: "transitiveReferences",
subScenario: "reports error about module not found with node resolution with external module name",
fs: () => projFs,
sys: getSysForTransitiveReferences,
commandLineArgs: ["--b", "tsconfig.c.json", "--listFiles"],
modifyFs: fs => modifyFsBTsToNonRelativeImport(fs, "node"),
modifySystem: sys => modifyFsBTsToNonRelativeImport(sys, "node"),
});
});
@@ -1,10 +1,7 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: configFileErrors:: reports syntax errors in config file", () => {
function verify(outFile?: object) {
@@ -12,7 +9,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: configFileErrors:: reports synt
scenario: "configFileErrors",
subScenario: `${outFile ? "outFile" : "multiFile"}/reports syntax errors in config file`,
sys: () =>
createWatchedSystem(
TestServerHost.createWatchedSystem(
[
{ path: `/user/username/projects/myproject/a.ts`, content: "export function foo() { }" },
{ path: `/user/username/projects/myproject/b.ts`, content: "export function bar() { }" },
@@ -29,7 +26,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: configFileErrors:: reports synt
]
}`,
},
libFile,
],
{ currentDirectory: "/user/username/projects/myproject" },
),
@@ -5,7 +5,7 @@ import {
} from "../helpers/demoProjectReferences.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: with demo project", () => {
describe("unittests:: tsbuildWatch:: watchMode:: with demo:: project", () => {
verifyTscWatch({
scenario: "demo",
subScenario: "updates with circular reference",
@@ -1,20 +1,16 @@
import {
getConfigDirExtendsSys,
modifyFirstExtendedConfigOfConfigDirExtendsSys,
} from "../helpers/extends.js";
import { forConfigDirExtendsSysScenario } from "../helpers/extends.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: extends::", () => {
verifyTscWatch({
scenario: "extends",
subScenario: "configDir template",
sys: () => createWatchedSystem(getConfigDirExtendsSys(), { currentDirectory: "/home/src/projects/myproject" }),
commandLineArgs: ["-b", "-w", "--extendedDiagnostics", "--explainFiles", "-v"],
edits: [{
caption: "edit extended config file",
edit: modifyFirstExtendedConfigOfConfigDirExtendsSys,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}],
});
forConfigDirExtendsSysScenario(
/*forTsserver*/ false,
(subScenario, sys, edits) =>
verifyTscWatch({
scenario: "extends",
subScenario,
sys,
commandLineArgs: ["-b", "-w", "--extendedDiagnostics", "--explainFiles", "-v"],
edits: edits(),
}),
);
});
@@ -1,15 +1,16 @@
import { getSysForLibResolution } from "../helpers/libraryResolution.js";
import { forEachLibResolutionScenario } from "../helpers/libraryResolution.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: libraryResolution:: library file resolution", () => {
function verify(libRedirection?: true) {
verifyTscWatch({
scenario: "libraryResolution",
subScenario: `with config${libRedirection ? " with redirection" : ""}`,
sys: () => getSysForLibResolution(libRedirection),
commandLineArgs: ["-b", "-w", "project1", "project2", "project3", "project4", "--verbose", "--explainFiles", "--extendedDiagnostics"],
});
}
verify();
verify(/*libRedirection*/ true);
forEachLibResolutionScenario(
/*forTsserver*/ false,
/*withoutConfig*/ undefined,
(subScenario, sys) =>
verifyTscWatch({
scenario: "libraryResolution",
subScenario,
sys,
commandLineArgs: ["-b", "-w", "project1", "project2", "project3", "project4", "--verbose", "--explainFiles", "--extendedDiagnostics"],
}),
);
});
@@ -1,17 +1,14 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution::", () => {
verifyTscWatch({
scenario: "moduleResolutionCache",
subScenario: "handles the cache correctly when two projects use different module resolution settings",
sys: () =>
createWatchedSystem(
TestServerHost.createWatchedSystem(
[
{ path: `/user/username/projects/myproject/project1/index.ts`, content: `import { foo } from "file";` },
{ path: `/user/username/projects/myproject/project1/node_modules/file/index.d.ts`, content: "export const foo = 10;" },
@@ -43,7 +40,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
],
}),
},
libFile,
],
{ currentDirectory: "/user/username/projects/myproject" },
),
@@ -61,7 +57,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
scenario: "moduleResolution",
subScenario: `resolves specifier in output declaration file from referenced project correctly with cts and mts extensions`,
sys: () =>
createWatchedSystem([
TestServerHost.createWatchedSystem([
{
path: `/user/username/projects/myproject/packages/pkg1/package.json`,
content: jsonToReadableText({
@@ -118,7 +114,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
path: `/user/username/projects/myproject/node_modules/pkg2`,
symLink: `/user/username/projects/myproject/packages/pkg2`,
},
{ ...libFile, path: `/a/lib/lib.es2022.full.d.ts` },
], { currentDirectory: "/user/username/projects/myproject" }),
commandLineArgs: ["-b", "packages/pkg1", "-w", "--verbose", "--traceResolution"],
edits: [
@@ -155,7 +150,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
scenario: "moduleResolution",
subScenario: `build mode watches for changes to package-json main fields`,
sys: () =>
createWatchedSystem([
TestServerHost.createWatchedSystem([
{
path: `/user/username/projects/myproject/packages/pkg1/package.json`,
content: jsonToReadableText({
@@ -213,7 +208,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: moduleResolution", () => {
path: `/user/username/projects/myproject/node_modules/pkg2`,
symLink: `/user/username/projects/myproject/packages/pkg2`,
},
libFile,
], { currentDirectory: "/user/username/projects/myproject" }),
commandLineArgs: ["-b", "packages/pkg1", "--verbose", "-w", "--traceResolution"],
edits: [
@@ -1,11 +1,7 @@
import { jsonToReadableText } from "../helpers.js";
import { libContent } from "../helpers/contents.js";
import { forEachNoEmitTscWatch } from "../helpers/noEmit.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: with noEmit::", () => {
function verify(outFile?: object) {
@@ -14,8 +10,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: with noEmit::", () => {
subScenario: `${outFile ? "outFile" : "multiFile"}/does not go in loop when watching when no files are emitted`,
commandLineArgs: ["-b", "-w", "-verbose"],
sys: () =>
createWatchedSystem({
[libFile.path]: libContent,
TestServerHost.createWatchedSystem({
"/user/username/projects/myproject/a.js": "",
"/user/username/projects/myproject/b.ts": "",
"/user/username/projects/myproject/tsconfig.json": jsonToReadableText({
@@ -1,15 +1,11 @@
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { FsContents } from "../helpers/contents.js";
import { createBaseline } from "../helpers/baseline.js";
import {
getFsContentsForSampleProjectReferences,
getFsContentsForSampleProjectReferencesLogicConfig,
getSysForSampleProjectReferences,
} from "../helpers/sampleProjectReferences.js";
import {
commonFile1,
commonFile2,
createBaseline,
createSolutionBuilderWithWatchHostForBaseline,
noopChange,
runWatchBaseline,
@@ -17,12 +13,11 @@ import {
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
describe("unittests:: tsbuildWatch:: watchMode:: programUpdates::", () => {
verifyTscWatch({
scenario: "programUpdates",
subScenario: "creates solution in watch mode",
@@ -47,7 +42,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
});
describe("validates the changes and watched files", () => {
function verifyProjectChanges(subScenario: string, allFilesGetter: () => FsContents) {
function verifyProjectChanges(subScenario: string, sys: () => TestServerHost) {
const buildLogicAndTests: TscWatchCompileChange = {
caption: "Build logic and tests",
edit: ts.noop,
@@ -57,31 +52,27 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
verifyTscWatch({
scenario: "programUpdates",
subScenario: `${subScenario}/change builds changes and reports found errors message`,
commandLineArgs: ["-b", "-w", "sample1/tests"],
sys: () =>
createWatchedSystem(
allFilesGetter(),
{ currentDirectory: "/user/username/projects" },
),
commandLineArgs: ["-b", "-w", "tests"],
sys,
edits: [
{
caption: "Make change to core",
edit: sys => sys.appendFile("sample1/core/index.ts", `\nexport class someClass { }`),
edit: sys => sys.appendFile("core/index.ts", `\nexport class someClass { }`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
},
buildLogicAndTests,
// Another change requeues and builds it
{
caption: "Revert core file",
edit: sys => sys.replaceFileText("sample1/core/index.ts", `\nexport class someClass { }`, ""),
edit: sys => sys.replaceFileText("core/index.ts", `\nexport class someClass { }`, ""),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
},
buildLogicAndTests,
{
caption: "Make two changes",
edit: sys => {
sys.appendFile("sample1/core/index.ts", `\nexport class someClass { }`);
sys.appendFile("sample1/core/index.ts", `\nexport class someClass2 { }`);
sys.appendFile("core/index.ts", `\nexport class someClass { }`);
sys.appendFile("core/index.ts", `\nexport class someClass2 { }`);
},
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
},
@@ -92,15 +83,11 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
verifyTscWatch({
scenario: "programUpdates",
subScenario: `${subScenario}/non local change does not start build of referencing projects`,
commandLineArgs: ["-b", "-w", "sample1/tests"],
sys: () =>
createWatchedSystem(
allFilesGetter(),
{ currentDirectory: "/user/username/projects" },
),
commandLineArgs: ["-b", "-w", "tests"],
sys,
edits: [{
caption: "Make local change to core",
edit: sys => sys.appendFile("sample1/core/index.ts", `\nfunction foo() { }`),
edit: sys => sys.appendFile("core/index.ts", `\nfunction foo() { }`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
}],
});
@@ -108,22 +95,18 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
verifyTscWatch({
scenario: "programUpdates",
subScenario: `${subScenario}/builds when new file is added, and its subsequent updates`,
commandLineArgs: ["-b", "-w", "sample1/tests"],
sys: () =>
createWatchedSystem(
allFilesGetter(),
{ currentDirectory: "/user/username/projects" },
),
commandLineArgs: ["-b", "-w", "tests"],
sys,
edits: [
{
caption: "Change to new File and build core",
edit: sys => sys.writeFile("sample1/core/newfile.ts", `export const newFileConst = 30;`),
edit: sys => sys.writeFile("core/newfile.ts", `export const newFileConst = 30;`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
},
buildLogicAndTests,
{
caption: "Change to new File and build core",
edit: sys => sys.appendFile("sample1/core/newfile.ts", `\nexport class someClass2 { }`),
edit: sys => sys.appendFile("core/newfile.ts", `\nexport class someClass2 { }`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Builds core
},
buildLogicAndTests,
@@ -134,20 +117,24 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
describe("with simple project reference graph", () => {
verifyProjectChanges(
"with simple project reference graph",
getFsContentsForSampleProjectReferences,
getSysForSampleProjectReferences,
);
});
describe("with circular project reference", () => {
verifyProjectChanges(
"with circular project reference",
() => ({
...getFsContentsForSampleProjectReferences(),
"/user/username/projects/sample1/core/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, declaration: true },
references: [{ path: "../tests", circular: true }],
}),
}),
() => {
const sys = getSysForSampleProjectReferences();
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, declaration: true },
references: [{ path: "../tests", circular: true }],
}),
);
return sys;
},
);
});
});
@@ -181,18 +168,17 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
subScenario: "with outFile and non local change",
commandLineArgs: ["-b", "-w", "sample1/logic"],
sys: () =>
createWatchedSystem({
[libFile.path]: libFile.content,
"/user/username/projects/sample1/core/tsconfig.json": jsonToReadableText({
TestServerHost.createWatchedSystem({
"/user/username/workspaces/solution/sample1/core/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, declaration: true, outFile: "index.js" },
}),
"/user/username/projects/sample1/core/index.ts": `function foo() { return 10; }`,
"/user/username/projects/sample1/logic/tsconfig.json": jsonToReadableText({
"/user/username/workspaces/solution/sample1/core/index.ts": `function foo() { return 10; }`,
"/user/username/workspaces/solution/sample1/logic/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true, declaration: true, outFile: "index.js" },
references: [{ path: "../core" }],
}),
"/user/username/projects/sample1/logic/index.ts": `function bar() { return foo() + 1 };`,
}, { currentDirectory: "/user/username/projects" }),
"/user/username/workspaces/solution/sample1/logic/index.ts": `function bar() { return foo() + 1 };`,
}, { currentDirectory: "/user/username/workspaces/solution" }),
edits: [
{
caption: "Make non local change and build core",
@@ -256,8 +242,8 @@ createSomeObject().message;`,
content: jsonToReadableText({ references: [{ path: "../Library" }] }),
};
const files = [libFile, libraryTs, libraryTsconfig, appTs, appTsconfig];
return createWatchedSystem(files, { currentDirectory: `${"/user/username/projects"}/sample1` });
const files = [libraryTs, libraryTsconfig, appTs, appTsconfig];
return TestServerHost.createWatchedSystem(files, { currentDirectory: `${"/user/username/projects"}/sample1` });
},
edits: [
{
@@ -381,8 +367,8 @@ createSomeObject().message;`,
subScenario: "reportErrors/declarationEmitErrors/when fixing error files all files are emitted",
commandLineArgs: ["-b", "-w", subProject],
sys: () =>
createWatchedSystem(
[libFile, fileWithError, fileWithoutError, tsconfig],
TestServerHost.createWatchedSystem(
[fileWithError, fileWithoutError, tsconfig],
{ currentDirectory: `${"/user/username/projects"}/${solution}` },
),
edits: [
@@ -395,8 +381,8 @@ createSomeObject().message;`,
subScenario: "reportErrors/declarationEmitErrors/when file with no error changes",
commandLineArgs: ["-b", "-w", subProject],
sys: () =>
createWatchedSystem(
[libFile, fileWithError, fileWithoutError, tsconfig],
TestServerHost.createWatchedSystem(
[fileWithError, fileWithoutError, tsconfig],
{ currentDirectory: `${"/user/username/projects"}/${solution}` },
),
edits: [
@@ -416,8 +402,8 @@ createSomeObject().message;`,
subScenario: "reportErrors/declarationEmitErrors/introduceError/when fixing errors only changed file is emitted",
commandLineArgs: ["-b", "-w", subProject],
sys: () =>
createWatchedSystem(
[libFile, fileWithFixedError, fileWithoutError, tsconfig],
TestServerHost.createWatchedSystem(
[fileWithFixedError, fileWithoutError, tsconfig],
{ currentDirectory: `${"/user/username/projects"}/${solution}` },
),
edits: [
@@ -431,8 +417,8 @@ createSomeObject().message;`,
subScenario: "reportErrors/declarationEmitErrors/introduceError/when file with no error changes",
commandLineArgs: ["-b", "-w", subProject],
sys: () =>
createWatchedSystem(
[libFile, fileWithFixedError, fileWithoutError, tsconfig],
TestServerHost.createWatchedSystem(
[fileWithFixedError, fileWithoutError, tsconfig],
{ currentDirectory: `${"/user/username/projects"}/${solution}` },
),
edits: [
@@ -483,7 +469,7 @@ createSomeObject().message;`,
},
}),
};
return createWatchedSystem([index, configFile, libFile], { currentDirectory: "/user/username/projects/myproject" });
return TestServerHost.createWatchedSystem([index, configFile], { currentDirectory: "/user/username/projects/myproject" });
},
edits: [
{
@@ -521,19 +507,22 @@ createSomeObject().message;`,
verifyTscWatch({
scenario: "programUpdates",
subScenario: "should not trigger recompilation because of program emit with outDir specified",
commandLineArgs: ["-b", "-w", "sample1/core", "-verbose"],
sys: () =>
createWatchedSystem({
...getFsContentsForSampleProjectReferences(),
"/user/username/projects/sample1/core/tsconfig.json": jsonToReadableText({
commandLineArgs: ["-b", "-w", "core", "-verbose"],
sys: () => {
const sys = getSysForSampleProjectReferences();
sys.writeFile(
"core/tsconfig.json",
jsonToReadableText({
compilerOptions: { composite: true, outDir: "outDir" },
}),
}, { currentDirectory: "/user/username/projects" }),
);
return sys;
},
edits: [
noopChange,
{
caption: "Add new file",
edit: sys => sys.writeFile("sample1/core/file3.ts", `export const y = 10;`),
edit: sys => sys.writeFile("core/file3.ts", `export const y = 10;`),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
noopChange,
@@ -545,12 +534,20 @@ createSomeObject().message;`,
subScenario: "works with extended source files",
commandLineArgs: ["-b", "-w", "-v", "project1.tsconfig.json", "project2.tsconfig.json", "project3.tsconfig.json"],
sys: () => {
const commonFile1: File = {
path: "/user/username/projects/project/commonFile1.ts",
content: "let x = 1",
};
const commonFile2: File = {
path: "/user/username/projects/project/commonFile2.ts",
content: "let y = 1",
};
const alphaExtendedConfigFile: File = {
path: "/a/b/alpha.tsconfig.json",
path: "/user/username/projects/project/alpha.tsconfig.json",
content: "{}",
};
const project1Config: File = {
path: "/a/b/project1.tsconfig.json",
path: "/user/username/projects/project/project1.tsconfig.json",
content: jsonToReadableText({
extends: "./alpha.tsconfig.json",
compilerOptions: {
@@ -560,17 +557,17 @@ createSomeObject().message;`,
}),
};
const bravoExtendedConfigFile: File = {
path: "/a/b/bravo.tsconfig.json",
path: "/user/username/projects/project/bravo.tsconfig.json",
content: jsonToReadableText({
extends: "./alpha.tsconfig.json",
}),
};
const otherFile: File = {
path: "/a/b/other.ts",
path: "/user/username/projects/project/other.ts",
content: "let z = 0;",
};
const project2Config: File = {
path: "/a/b/project2.tsconfig.json",
path: "/user/username/projects/project/project2.tsconfig.json",
content: jsonToReadableText({
extends: "./bravo.tsconfig.json",
compilerOptions: {
@@ -580,11 +577,11 @@ createSomeObject().message;`,
}),
};
const otherFile2: File = {
path: "/a/b/other2.ts",
path: "/user/username/projects/project/other2.ts",
content: "let k = 0;",
};
const extendsConfigFile1: File = {
path: "/a/b/extendsConfig1.tsconfig.json",
path: "/user/username/projects/project/extendsConfig1.tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
composite: true,
@@ -592,7 +589,7 @@ createSomeObject().message;`,
}),
};
const extendsConfigFile2: File = {
path: "/a/b/extendsConfig2.tsconfig.json",
path: "/user/username/projects/project/extendsConfig2.tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
strictNullChecks: false,
@@ -600,7 +597,7 @@ createSomeObject().message;`,
}),
};
const extendsConfigFile3: File = {
path: "/a/b/extendsConfig3.tsconfig.json",
path: "/user/username/projects/project/extendsConfig3.tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
noImplicitAny: true,
@@ -608,17 +605,20 @@ createSomeObject().message;`,
}),
};
const project3Config: File = {
path: "/a/b/project3.tsconfig.json",
path: "/user/username/projects/project/project3.tsconfig.json",
content: jsonToReadableText({
extends: ["./extendsConfig1.tsconfig.json", "./extendsConfig2.tsconfig.json", "./extendsConfig3.tsconfig.json"],
extends: [
"./extendsConfig1.tsconfig.json",
"./extendsConfig2.tsconfig.json",
"./extendsConfig3.tsconfig.json",
],
compilerOptions: {
composite: false,
},
files: [otherFile2.path],
}),
};
return createWatchedSystem([
libFile,
return TestServerHost.createWatchedSystem([
alphaExtendedConfigFile,
project1Config,
commonFile1,
@@ -631,14 +631,14 @@ createSomeObject().message;`,
extendsConfigFile2,
extendsConfigFile3,
project3Config,
], { currentDirectory: "/a/b" });
], { currentDirectory: "/user/username/projects/project" });
},
edits: [
{
caption: "Modify alpha config",
edit: sys =>
sys.writeFile(
"/a/b/alpha.tsconfig.json",
"/user/username/projects/project/alpha.tsconfig.json",
jsonToReadableText({
compilerOptions: { strict: true },
}),
@@ -654,7 +654,7 @@ createSomeObject().message;`,
caption: "change bravo config",
edit: sys =>
sys.writeFile(
"/a/b/bravo.tsconfig.json",
"/user/username/projects/project/bravo.tsconfig.json",
jsonToReadableText({
extends: "./alpha.tsconfig.json",
compilerOptions: { strict: false },
@@ -666,7 +666,7 @@ createSomeObject().message;`,
caption: "project 2 extends alpha",
edit: sys =>
sys.writeFile(
"/a/b/project2.tsconfig.json",
"/user/username/projects/project/project2.tsconfig.json",
jsonToReadableText({
extends: "./alpha.tsconfig.json",
}),
@@ -675,7 +675,7 @@ createSomeObject().message;`,
},
{
caption: "update aplha config",
edit: sys => sys.writeFile("/a/b/alpha.tsconfig.json", "{}"),
edit: sys => sys.writeFile("/user/username/projects/project/alpha.tsconfig.json", "{}"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // build project1
},
{
@@ -687,7 +687,7 @@ createSomeObject().message;`,
caption: "Modify extendsConfigFile2",
edit: sys =>
sys.writeFile(
"/a/b/extendsConfig2.tsconfig.json",
"/user/username/projects/project/extendsConfig2.tsconfig.json",
jsonToReadableText({
compilerOptions: { strictNullChecks: true },
}),
@@ -698,11 +698,11 @@ createSomeObject().message;`,
caption: "Modify project 3",
edit: sys =>
sys.writeFile(
"/a/b/project3.tsconfig.json",
"/user/username/projects/project/project3.tsconfig.json",
jsonToReadableText({
extends: ["./extendsConfig1.tsconfig.json", "./extendsConfig2.tsconfig.json"],
compilerOptions: { composite: false },
files: ["/a/b/other2.ts"],
files: ["/user/username/projects/project/other2.ts"],
}),
),
timeouts: sys => sys.runQueuedTimeoutCallbacks(), // Build project3
@@ -720,8 +720,16 @@ createSomeObject().message;`,
subScenario: "works correctly when project with extended config is removed",
commandLineArgs: ["-b", "-w", "-v"],
sys: () => {
const commonFile1: File = {
path: "/user/username/projects/project/commonFile1.ts",
content: "let x = 1",
};
const commonFile2: File = {
path: "/user/username/projects/project/commonFile2.ts",
content: "let y = 1",
};
const alphaExtendedConfigFile: File = {
path: "/a/b/alpha.tsconfig.json",
path: "/user/username/projects/project/alpha.tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
strict: true,
@@ -729,7 +737,7 @@ createSomeObject().message;`,
}),
};
const project1Config: File = {
path: "/a/b/project1.tsconfig.json",
path: "/user/username/projects/project/project1.tsconfig.json",
content: jsonToReadableText({
extends: "./alpha.tsconfig.json",
compilerOptions: {
@@ -739,7 +747,7 @@ createSomeObject().message;`,
}),
};
const bravoExtendedConfigFile: File = {
path: "/a/b/bravo.tsconfig.json",
path: "/user/username/projects/project/bravo.tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
strict: true,
@@ -747,11 +755,11 @@ createSomeObject().message;`,
}),
};
const otherFile: File = {
path: "/a/b/other.ts",
path: "/user/username/projects/project/other.ts",
content: "let z = 0;",
};
const project2Config: File = {
path: "/a/b/project2.tsconfig.json",
path: "/user/username/projects/project/project2.tsconfig.json",
content: jsonToReadableText({
extends: "./bravo.tsconfig.json",
compilerOptions: {
@@ -761,7 +769,7 @@ createSomeObject().message;`,
}),
};
const configFile: File = {
path: "/a/b/tsconfig.json",
path: "/user/username/projects/project/tsconfig.json",
content: jsonToReadableText({
references: [
{
@@ -774,8 +782,7 @@ createSomeObject().message;`,
files: [],
}),
};
return createWatchedSystem([
libFile,
return TestServerHost.createWatchedSystem([
configFile,
alphaExtendedConfigFile,
project1Config,
@@ -784,14 +791,14 @@ createSomeObject().message;`,
bravoExtendedConfigFile,
project2Config,
otherFile,
], { currentDirectory: "/a/b" });
], { currentDirectory: "/user/username/projects/project" });
},
edits: [
{
caption: "Remove project2 from base config",
edit: sys =>
sys.modifyFile(
"/a/b/tsconfig.json",
"/user/username/projects/project/tsconfig.json",
jsonToReadableText({
references: [
{
@@ -810,12 +817,11 @@ createSomeObject().message;`,
scenario: "programUpdates",
subScenario: "tsbuildinfo has error",
sys: () =>
createWatchedSystem({
"/src/project/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": "{}",
"/src/project/tsconfig.tsbuildinfo": "Some random string",
[libFile.path]: libFile.content,
}),
commandLineArgs: ["--b", "src/project", "-i", "-w"],
TestServerHost.createWatchedSystem({
"/user/username/projects/project/main.ts": "export const x = 10;",
"/user/username/projects/project/tsconfig.json": "{}",
"/user/username/projects/project/tsconfig.tsbuildinfo": "Some random string",
}, { currentDirectory: "/user/username/projects/project" }),
commandLineArgs: ["--b", "-i", "-w"],
});
});
@@ -6,12 +6,11 @@ import {
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding", () => {
describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding::", () => {
function pkgs<T>(cb: (index: number) => T, count: number, startIndex?: number): T[] {
const result: T[] = [];
for (let index = startIndex || 0; count > 0; index++, count--) {
@@ -60,8 +59,8 @@ describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding", () => {
subScenario: `when there are 3 projects in a solution`,
commandLineArgs: ["-b", "-w", "-v"],
sys: () =>
createWatchedSystem(
[libFile, ...ts.flatMap(pkgs(pkgFiles, 3), ts.identity), solution(3)],
TestServerHost.createWatchedSystem(
[...ts.flatMap(pkgs(pkgFiles, 3), ts.identity), solution(3)],
{ currentDirectory: "/user/username/projects/myproject" },
),
edits: [
@@ -85,8 +84,8 @@ describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding", () => {
subScenario: `when there are 5 projects in a solution`,
commandLineArgs: ["-b", "-w", "-v"],
sys: () =>
createWatchedSystem(
[libFile, ...ts.flatMap(pkgs(pkgFiles, 5), ts.identity), solution(5)],
TestServerHost.createWatchedSystem(
[...ts.flatMap(pkgs(pkgFiles, 5), ts.identity), solution(5)],
{ currentDirectory: "/user/username/projects/myproject" },
),
edits: [
@@ -110,8 +109,8 @@ describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding", () => {
subScenario: `when there are 8 projects in a solution`,
commandLineArgs: ["-b", "-w", "-v"],
sys: () =>
createWatchedSystem(
[libFile, ...ts.flatMap(pkgs(pkgFiles, 8), ts.identity), solution(8)],
TestServerHost.createWatchedSystem(
[...ts.flatMap(pkgs(pkgFiles, 8), ts.identity), solution(8)],
{ currentDirectory: "/user/username/projects/myproject" },
),
edits: [
@@ -149,8 +148,8 @@ describe("unittests:: tsbuildWatch:: watchMode:: projectsBuilding", () => {
subScenario: `when there are 23 projects in a solution`,
commandLineArgs: ["-b", "-w", "-v"],
sys: () =>
createWatchedSystem(
[libFile, ...ts.flatMap(pkgs(pkgFiles, 23), ts.identity), solution(23)],
TestServerHost.createWatchedSystem(
[...ts.flatMap(pkgs(pkgFiles, 23), ts.identity), solution(23)],
{ currentDirectory: "/user/username/projects/myproject" },
),
edits: [
@@ -1,17 +1,16 @@
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { createBaseline } from "../helpers/baseline.js";
import {
createBaseline,
createSolutionBuilderWithWatchHostForBaseline,
runWatchBaseline,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
it("unittests:: tsbuildWatch:: watchMode:: Public API with custom transformers", () => {
it("unittests:: tsbuildWatch:: watchMode:: publicAPI:: Public API with custom transformers", () => {
const solution: File = {
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({
@@ -52,7 +51,10 @@ export enum e2 { }
export function f22() { } // trailing`,
};
const commandLineArgs = ["--b", "--w"];
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, solution, sharedConfig, sharedIndex, webpackConfig, webpackIndex], { currentDirectory: "/user/username/projects/myproject" }));
const { sys, baseline, cb, getPrograms } = createBaseline(TestServerHost.createWatchedSystem(
[solution, sharedConfig, sharedIndex, webpackConfig, webpackIndex],
{ currentDirectory: "/user/username/projects/myproject" },
));
const buildHost = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
buildHost.getCustomTransformers = getCustomTransformers;
const builder = ts.createSolutionBuilderWithWatch(buildHost, [solution.path], { verbose: true });
@@ -1,19 +1,15 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { libContent } from "../helpers/contents.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: with reexport when referenced project reexports definitions from another file", () => {
describe("unittests:: tsbuildWatch:: watchMode:: reexport:: with reexport when referenced project reexports definitions from another file", () => {
verifyTscWatch({
scenario: "reexport",
subScenario: "Reports errors correctly",
commandLineArgs: ["-b", "-w", "-verbose", "src"],
sys: () =>
createWatchedSystem({
TestServerHost.createWatchedSystem({
"/user/username/projects/reexport/src/tsconfig.json": jsonToReadableText({
files: [],
include: [],
@@ -49,7 +45,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: with reexport when referenced p
// bar: number;
}
`,
[libFile.path]: libContent,
}, { currentDirectory: `/user/username/projects/reexport` }),
edits: [
{
+12 -34
View File
@@ -1,40 +1,18 @@
import { forEachScenarioForRootsFromReferencedProject } from "../helpers/projectRoots.js";
import {
noopChange,
verifyTscWatch,
} from "../helpers/tscWatch.js";
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
describe("unittests:: tsbuildWatch:: watchMode:: roots::", () => {
describe("when root file is from referenced project", () => {
forEachScenarioForRootsFromReferencedProject((subScenario, getFsContents) => {
verifyTscWatch({
scenario: "roots",
subScenario,
commandLineArgs: ["--b", "projects/server", "-w", "-v", "--traceResolution", "--explainFiles"],
sys: () => createWatchedSystem(getFsContents(), { currentDirectory: "/home/src/workspaces" }),
edits: [
noopChange,
{
caption: "edit logging file",
edit: sys => sys.appendFile("/home/src/workspaces/projects/shared/src/logging.ts", "export const x = 10;"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks(); // build shared
sys.runQueuedTimeoutCallbacks(); // build server
},
},
noopChange,
{
caption: "delete random file",
edit: sys => sys.deleteFile("/home/src/workspaces/projects/shared/src/random.ts"),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks(); // build shared
sys.runQueuedTimeoutCallbacks(); // build server
},
},
noopChange,
],
});
});
forEachScenarioForRootsFromReferencedProject(
/*forTsserver*/ false,
(subScenario, sys, edits) =>
verifyTscWatch({
scenario: "roots",
subScenario,
commandLineArgs: ["--b", "projects/server", "-w", "-v", "--traceResolution", "--explainFiles"],
sys,
edits: edits(),
}),
);
});
});
@@ -1,14 +1,12 @@
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { createBaseline } from "../helpers/baseline.js";
import {
createBaseline,
createSolutionBuilderWithWatchHostForBaseline,
runWatchBaseline,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
@@ -23,7 +21,7 @@ describe("unittests:: tsbuildWatch:: watchEnvironment:: tsbuild:: watchMode:: wi
};
const allPkgFiles = pkgs(pkgFiles);
const system = createWatchedSystem([libFile, typing, ...flatArray(allPkgFiles)], { currentDirectory: project });
const system = TestServerHost.createWatchedSystem([typing, ...flatArray(allPkgFiles)], { currentDirectory: project });
writePkgReferences(system);
const { sys, baseline, cb, getPrograms } = createBaseline(system);
const host = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
@@ -1,20 +1,17 @@
import * as Harness from "../../_namespaces/Harness.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import * as Utils from "../../_namespaces/Utils.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
applyEdit,
baselineBuildInfo,
CommandLineProgram,
} from "../helpers/baseline.js";
import {
applyEdit,
createBaseline,
watchBaseline,
} from "../helpers/tscWatch.js";
} from "../helpers/baseline.js";
import { watchBaseline } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: builder cancellationToken::", () => {
@@ -24,7 +21,7 @@ describe("unittests:: tsc:: builder cancellationToken::", () => {
it(scenario, () => {
const aFile: File = {
path: `/user/username/projects/myproject/a.ts`,
content: Utils.dedent`
content: dedent`
import {B} from './b';
declare var console: any;
let b = new B();
@@ -32,7 +29,7 @@ describe("unittests:: tsc:: builder cancellationToken::", () => {
};
const bFile: File = {
path: `/user/username/projects/myproject/b.ts`,
content: Utils.dedent`
content: dedent`
import {C} from './c';
export class B {
c = new C();
@@ -40,7 +37,7 @@ describe("unittests:: tsc:: builder cancellationToken::", () => {
};
const cFile: File = {
path: `/user/username/projects/myproject/c.ts`,
content: Utils.dedent`
content: dedent`
export var C = class CReal {
d = 1;
};`,
@@ -53,8 +50,8 @@ describe("unittests:: tsc:: builder cancellationToken::", () => {
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { incremental: true, declaration: true } }),
};
const { sys, baseline } = createBaseline(createWatchedSystem(
[aFile, bFile, cFile, dFile, config, libFile],
const { sys, baseline } = createBaseline(TestServerHost.createWatchedSystem(
[aFile, bFile, cFile, dFile, config],
{ currentDirectory: "/user/username/projects/myproject" },
));
sys.exit = exitCode => sys.exitCode = exitCode;
@@ -121,7 +118,7 @@ describe("unittests:: tsc:: builder cancellationToken::", () => {
noChange("Clean build");
baselineCleanBuild();
Harness.Baseline.runBaseline(`tsc/cancellationToken/${scenario.split(" ").join("-")}.js`, baseline.join("\r\n"));
Baseline.runBaseline(`tsc/cancellationToken/${scenario.split(" ").join("-")}.js`, baseline.join("\r\n"));
function noChange(caption: string) {
applyEdit(sys, baseline, ts.noop, caption);
+88 -94
View File
@@ -1,116 +1,110 @@
import * as Utils from "../../_namespaces/Utils.js";
import { emptyArray } from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: composite::", () => {
verifyTsc({
scenario: "composite",
subScenario: "when setting composite false on command line",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"composite": true,
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
composite: true,
},
include: [
"src/**/*.ts",
],
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--composite", "false"],
});
verifyTsc({
scenario: "composite",
subScenario: "when setting composite null on command line",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"composite": true,
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "null", "--p", "src/project"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
composite: true,
},
include: [
"src/**/*.ts",
],
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--composite", "null"],
});
verifyTsc({
scenario: "composite",
subScenario: "when setting composite false on command line but has tsbuild info in config",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"composite": true,
"tsBuildInfoFile": "tsconfig.json.tsbuildinfo"
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
composite: true,
tsBuildInfoFile: "tsconfig.json.tsbuildinfo",
},
include: [
"src/**/*.ts",
],
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--composite", "false"],
});
verifyTsc({
scenario: "composite",
subScenario: "when setting composite false and tsbuildinfo as null on command line but has tsbuild info in config",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"composite": true,
"tsBuildInfoFile": "tsconfig.json.tsbuildinfo"
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project", "--tsBuildInfoFile", "null"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
composite: true,
tsBuildInfoFile: "tsconfig.json.tsbuildinfo",
},
include: [
"src/**/*.ts",
],
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--composite", "false", "--tsBuildInfoFile", "null"],
});
verifyTsc({
scenario: "composite",
subScenario: "converting to modules",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "const x = 10;",
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "none",
composite: true,
},
}),
}),
commandLineArgs: ["-p", "/src/project"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: [],
edits: [
{
caption: "convert to modules",
edit: fs => replaceText(fs, "/src/project/tsconfig.json", "none", "es2015"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/tsconfig.json", "none", "es2015"),
},
],
});
@@ -118,10 +112,10 @@ describe("unittests:: tsc:: composite::", () => {
verifyTsc({
scenario: "composite",
subScenario: "synthetic jsx import of ESM module from CJS module no crash no jsx element",
fs: () =>
loadProjectFromFiles({
"/src/main.ts": "export default 42;",
"/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/projects/project/src/main.ts": "export default 42;",
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
module: "Node16",
@@ -129,26 +123,26 @@ describe("unittests:: tsc:: composite::", () => {
jsxImportSource: "solid-js",
},
}),
"/node_modules/solid-js/package.json": jsonToReadableText({
"/home/src/projects/project/node_modules/solid-js/package.json": jsonToReadableText({
name: "solid-js",
type: "module",
}),
"/node_modules/solid-js/jsx-runtime.d.ts": Utils.dedent`
"/home/src/projects/project/node_modules/solid-js/jsx-runtime.d.ts": dedent`
export namespace JSX {
type IntrinsicElements = { div: {}; };
}
`,
}),
commandLineArgs: [],
}, { currentDirectory: "/home/src/projects/project" }),
commandLineArgs: emptyArray,
});
verifyTsc({
scenario: "composite",
subScenario: "synthetic jsx import of ESM module from CJS module error on jsx element",
fs: () =>
loadProjectFromFiles({
"/src/main.tsx": "export default <div/>;",
"/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/projects/project/src/main.tsx": "export default <div/>;",
"/home/src/projects/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
module: "Node16",
@@ -156,16 +150,16 @@ describe("unittests:: tsc:: composite::", () => {
jsxImportSource: "solid-js",
},
}),
"/node_modules/solid-js/package.json": jsonToReadableText({
"/home/src/projects/project/node_modules/solid-js/package.json": jsonToReadableText({
name: "solid-js",
type: "module",
}),
"/node_modules/solid-js/jsx-runtime.d.ts": Utils.dedent`
"/home/src/projects/project/node_modules/solid-js/jsx-runtime.d.ts": dedent`
export namespace JSX {
type IntrinsicElements = { div: {}; };
}
`,
}),
commandLineArgs: [],
}, { currentDirectory: "/home/src/projects/project" }),
commandLineArgs: emptyArray,
});
});
+24 -30
View File
@@ -1,16 +1,14 @@
import * as Utils from "../../_namespaces/Utils.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { forEachDeclarationEmitWithErrorsScenario } from "../helpers/declarationEmit.js";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import {
createWatchedSystem,
FileOrFolderOrSymLink,
isSymLink,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: declarationEmit::", () => {
@@ -30,21 +28,21 @@ describe("unittests:: tsc:: declarationEmit::", () => {
function verifyDeclarationEmit({ subScenario, files, rootProject, changeCaseFileTestPath }: VerifyDeclarationEmitInput) {
describe(subScenario, () => {
verifyTscWatch({
verifyTsc({
scenario: "declarationEmit",
subScenario,
sys: () => createWatchedSystem(files, { currentDirectory: "/user/username/projects/myproject" }),
sys: () => TestServerHost.createWatchedSystem(files, { currentDirectory: "/user/username/projects/myproject" }),
commandLineArgs: ["-p", rootProject, "--explainFiles"],
});
});
const caseChangeScenario = `${subScenario} moduleCaseChange`;
describe(caseChangeScenario, () => {
verifyTscWatch({
verifyTsc({
scenario: "declarationEmit",
subScenario: caseChangeScenario,
sys: () =>
createWatchedSystem(
TestServerHost.createWatchedSystem(
files.map(f => changeCaseFile(f, changeCaseFileTestPath, str => str.replace("myproject", "myProject"))),
{ currentDirectory: "/user/username/projects/myproject" },
),
@@ -67,14 +65,14 @@ describe("unittests:: tsc:: declarationEmit::", () => {
return `import pluginTwo from "plugin-two"; // include this to add reference to symlink`;
}
function pluginOneAction() {
return Utils.dedent`
return dedent`
import { actionCreatorFactory } from "typescript-fsa"; // Include version of shared lib
const action = actionCreatorFactory("somekey");
const featureOne = action<{ route: string }>("feature-one");
export const actions = { featureOne };`;
}
function pluginTwoDts() {
return Utils.dedent`
return dedent`
declare const _default: {
features: {
featureOne: {
@@ -104,7 +102,7 @@ describe("unittests:: tsc:: declarationEmit::", () => {
});
}
function fsaIndex() {
return Utils.dedent`
return dedent`
export interface Action<Payload> {
type: string;
payload: Payload;
@@ -133,7 +131,6 @@ describe("unittests:: tsc:: declarationEmit::", () => {
{ path: `/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/package.json`, content: fsaPackageJson() },
{ path: `/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() },
{ path: `/user/username/projects/myproject/plugin-one/node_modules/plugin-two`, symLink: `/user/username/projects/myproject/plugin-two` },
libFile,
],
changeCaseFileTestPath: str => str.includes("/plugin-two"),
});
@@ -163,7 +160,6 @@ ${pluginOneAction()}`,
{ path: `/user/username/projects/myproject/plugin-one/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() },
{ path: `/temp/yarn/data/link/plugin-two`, symLink: `/user/username/projects/myproject/plugin-two` },
{ path: `/user/username/projects/myproject/plugin-one/node_modules/plugin-two`, symLink: `/temp/yarn/data/link/plugin-two` },
libFile,
],
changeCaseFileTestPath: str => str.includes("/plugin-two"),
});
@@ -175,12 +171,12 @@ ${pluginOneAction()}`,
files: [
{
path: `/user/username/projects/myproject/pkg1/dist/index.d.ts`,
content: Utils.dedent`
content: dedent`
export * from './types';`,
},
{
path: `/user/username/projects/myproject/pkg1/dist/types.d.ts`,
content: Utils.dedent`
content: dedent`
export declare type A = {
id: string;
};
@@ -206,12 +202,12 @@ ${pluginOneAction()}`,
},
{
path: `/user/username/projects/myproject/pkg2/dist/index.d.ts`,
content: Utils.dedent`
content: dedent`
export * from './types';`,
},
{
path: `/user/username/projects/myproject/pkg2/dist/types.d.ts`,
content: Utils.dedent`
content: dedent`
export {MetadataAccessor} from '@raymondfeng/pkg1';`,
},
{
@@ -225,12 +221,12 @@ ${pluginOneAction()}`,
},
{
path: `/user/username/projects/myproject/pkg3/src/index.ts`,
content: Utils.dedent`
content: dedent`
export * from './keys';`,
},
{
path: `/user/username/projects/myproject/pkg3/src/keys.ts`,
content: Utils.dedent`
content: dedent`
import {MetadataAccessor} from "@raymondfeng/pkg2";
export const ADMIN = MetadataAccessor.create<boolean>('1');`,
},
@@ -256,17 +252,16 @@ ${pluginOneAction()}`,
path: `/user/username/projects/myproject/pkg3/node_modules/@raymondfeng/pkg2`,
symLink: `/user/username/projects/myproject/pkg2`,
},
libFile,
],
changeCaseFileTestPath: str => str.includes("/pkg1"),
});
});
verifyTscWatch({
verifyTsc({
scenario: "declarationEmit",
subScenario: "when using Windows paths and uppercase letters",
sys: () =>
createWatchedSystem([
TestServerHost.createWatchedSystem([
{
path: `D:\\Work\\pkg1\\package.json`,
content: jsonToReadableText({
@@ -315,7 +310,7 @@ ${pluginOneAction()}`,
},
{
path: `D:\\Work\\pkg1\\src\\main.ts`,
content: Utils.dedent`
content: dedent`
import { PartialType } from './utils';
class Common {}
@@ -327,7 +322,7 @@ ${pluginOneAction()}`,
},
{
path: `D:\\Work\\pkg1\\src\\utils\\index.ts`,
content: Utils.dedent`
content: dedent`
import { MyType, MyReturnType } from './type-helpers';
export function PartialType<T>(classRef: MyType<T>) {
@@ -341,7 +336,7 @@ ${pluginOneAction()}`,
},
{
path: `D:\\Work\\pkg1\\src\\utils\\type-helpers.ts`,
content: Utils.dedent`
content: dedent`
export type MyReturnType = {
new (...args: any[]): any;
};
@@ -351,22 +346,21 @@ ${pluginOneAction()}`,
}
`,
},
libFile,
], { currentDirectory: "D:\\Work\\pkg1", windowsStyleRoot: "D:/" }),
commandLineArgs: ["-p", "D:\\Work\\pkg1", "--explainFiles"],
});
forEachDeclarationEmitWithErrorsScenario((scenarioName, fs) => {
forEachDeclarationEmitWithErrorsScenario((scenarioName, sys) => {
verifyTsc({
scenario: "declarationEmit",
subScenario: scenarioName("reports dts generation errors"),
commandLineArgs: ["-p", `/src/project`, "--explainFiles", "--listEmittedFiles"],
fs,
commandLineArgs: ["--explainFiles", "--listEmittedFiles"],
sys,
edits: [
noChangeRun,
{
...noChangeRun,
commandLineArgs: ["-b", `/src/project`, "--explainFiles", "--listEmittedFiles", "-v"],
commandLineArgs: ["-b", "--explainFiles", "--listEmittedFiles", "-v"],
},
],
});
+7 -9
View File
@@ -3,11 +3,9 @@ import {
getSymlinkedExtendsSys,
} from "../helpers/extends.js";
import { verifyTsc } from "../helpers/tsc.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
describe("unittests:: tsc:: extends::", () => {
verifyTscWatch({
verifyTsc({
scenario: "extends",
subScenario: "resolves the symlink path",
sys: getSymlinkedExtendsSys,
@@ -17,21 +15,21 @@ describe("unittests:: tsc:: extends::", () => {
verifyTsc({
scenario: "extends",
subScenario: "configDir template",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--explainFiles"],
sys: getConfigDirExtendsSys,
commandLineArgs: ["--explainFiles"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template showConfig",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--showConfig"],
sys: getConfigDirExtendsSys,
commandLineArgs: ["--showConfig"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template with commandline",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--explainFiles", "--outDir", "${configDir}/outDir"], // eslint-disable-line no-template-curly-in-string
sys: getConfigDirExtendsSys,
commandLineArgs: ["--explainFiles", "--outDir", "${configDir}/outDir"], // eslint-disable-line no-template-curly-in-string
});
});
@@ -1,44 +1,44 @@
import { dedent } from "../../_namespaces/Utils.js";
import { getFsContentsForMultipleErrorsForceConsistentCasingInFileNames } from "../helpers/forceConsistentCasingInFileNames.js";
import { getSysForMultipleErrorsForceConsistentCasingInFileNames } from "../helpers/forceConsistentCasingInFileNames.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: forceConsistentCasingInFileNames::", () => {
verifyTsc({
scenario: "forceConsistentCasingInFileNames",
subScenario: "with relative and non relative file resolutions",
commandLineArgs: ["/src/project/src/struct.d.ts", "--forceConsistentCasingInFileNames", "--explainFiles"],
fs: () =>
loadProjectFromFiles({
"/src/project/src/struct.d.ts": dedent`
commandLineArgs: ["/user/username/projects/myproject/src/struct.d.ts", "--forceConsistentCasingInFileNames", "--explainFiles"],
sys: () =>
TestServerHost.createWatchedSystem({
"/user/username/projects/myproject/src/struct.d.ts": dedent`
import * as xs1 from "fp-ts/lib/Struct";
import * as xs2 from "fp-ts/lib/struct";
import * as xs3 from "./Struct";
import * as xs4 from "./struct";
`,
"/src/project/node_modules/fp-ts/lib/struct.d.ts": `export function foo(): void`,
}),
"/user/username/projects/myproject/node_modules/fp-ts/lib/struct.d.ts": `export function foo(): void`,
}, { currentDirectory: "/user/username/projects/myproject" }),
});
verifyTsc({
scenario: "forceConsistentCasingInFileNames",
subScenario: "when file is included from multiple places with different casing",
commandLineArgs: ["-p", "/home/src/projects/project/tsconfig.json", "--explainFiles"],
fs: () => loadProjectFromFiles(getFsContentsForMultipleErrorsForceConsistentCasingInFileNames()),
commandLineArgs: ["--explainFiles"],
sys: getSysForMultipleErrorsForceConsistentCasingInFileNames,
});
verifyTsc({
scenario: "forceConsistentCasingInFileNames",
subScenario: "with type ref from file",
commandLineArgs: ["-p", "/src/project/src", "--explainFiles", "--traceResolution"],
fs: () =>
loadProjectFromFiles({
"/src/project/src/fileOne.d.ts": `declare class c { }`,
"/src/project/src/file2.d.ts": dedent`
commandLineArgs: ["-p", "/user/username/projects/myproject", "--explainFiles", "--traceResolution"],
sys: () =>
TestServerHost.createWatchedSystem({
"/user/username/projects/myproject/src/fileOne.d.ts": `declare class c { }`,
"/user/username/projects/myproject/src/file2.d.ts": dedent`
/// <reference types="./fileOne.d.ts"/>
declare const y: c;
`,
"/src/project/src/tsconfig.json": "{ }",
}),
"/user/username/projects/myproject/tsconfig.json": "{ }",
}, { currentDirectory: "/user/username/projects/myproject" }),
});
});
+208 -221
View File
@@ -1,10 +1,7 @@
import * as ts from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
compilerOptionsToConfigJson,
libContent,
} from "../helpers/contents.js";
import { compilerOptionsToConfigJson } from "../helpers/contents.js";
import {
noChangeOnlyRuns,
noChangeRun,
@@ -12,67 +9,63 @@ import {
verifyTsc,
} from "../helpers/tsc.js";
import {
appendText,
loadProjectFromFiles,
prependText,
replaceText,
} from "../helpers/vfs.js";
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: incremental::", () => {
verifyTsc({
scenario: "incremental",
subScenario: "when passing filename for buildinfo on commandline",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo", "--explainFiles"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "es5",
module: "commonjs",
},
include: [
"src/**/*.ts",
],
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--incremental", "--tsBuildInfoFile", ".tsbuildinfo", "--explainFiles"],
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "incremental",
subScenario: "when passing rootDir from commandline",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": dedent`
{
"compilerOptions": {
"incremental": true,
"outDir": "dist",
},
}`,
}),
commandLineArgs: ["--p", "src/project", "--rootDir", "src/project/src"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
incremental: true,
outDir: "dist",
},
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--rootDir", "src"],
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "incremental",
subScenario: "with only dts files",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.d.ts": "export const x = 10;",
"/src/project/src/another.d.ts": "export const y = 10;",
"/src/project/tsconfig.json": "{}",
}),
commandLineArgs: ["--incremental", "--p", "src/project"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.d.ts": "export const x = 10;",
"/home/src/workspaces/project/src/another.d.ts": "export const y = 10;",
"/home/src/workspaces/project/tsconfig.json": "{}",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--incremental"],
edits: [
noChangeRun,
{
caption: "incremental-declaration-doesnt-change",
edit: fs => appendText(fs, "/src/project/src/main.d.ts", "export const xy = 100;"),
edit: sys => sys.appendFile("/home/src/workspaces/project/src/main.d.ts", "export const xy = 100;"),
},
],
});
@@ -80,90 +73,88 @@ describe("unittests:: tsc:: incremental::", () => {
verifyTsc({
scenario: "incremental",
subScenario: "when passing rootDir is in the tsconfig",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": dedent`
{
"compilerOptions": {
"incremental": true,
"outDir": "./built",
"rootDir": "./"
},
}`,
}),
commandLineArgs: ["--p", "src/project"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
incremental: true,
outDir: "./built",
rootDir: "./",
},
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ts.emptyArray,
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "incremental",
subScenario: "tsbuildinfo has error",
fs: () =>
loadProjectFromFiles({
"/src/project/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": "{}",
"/src/project/tsconfig.tsbuildinfo": "Some random string",
}),
commandLineArgs: ["--p", "src/project", "-i"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": "{}",
"/home/src/workspaces/project/tsconfig.tsbuildinfo": "Some random string",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-i"],
edits: [{
caption: "tsbuildinfo written has error",
edit: fs => prependText(fs, "/src/project/tsconfig.tsbuildinfo", "Some random string"),
edit: sys => sys.prependFile("/home/src/workspaces/project/tsconfig.tsbuildinfo", "Some random string"),
}],
});
verifyTsc({
scenario: "incremental",
subScenario: `when global file is added, the signatures are updated`,
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": dedent`
/// <reference path="./filePresent.ts"/>
/// <reference path="./fileNotFound.ts"/>
function main() { }
`,
"/src/project/src/anotherFileWithSameReferenes.ts": dedent`
"/home/src/workspaces/project/src/anotherFileWithSameReferenes.ts": dedent`
/// <reference path="./filePresent.ts"/>
/// <reference path="./fileNotFound.ts"/>
function anotherFileWithSameReferenes() { }
`,
"/src/project/src/filePresent.ts": `function something() { return 10; }`,
"/src/project/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/src/filePresent.ts": `function something() { return 10; }`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: { composite: true },
include: ["src/**/*.ts"],
}),
}),
commandLineArgs: ["--p", "src/project"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ts.emptyArray,
edits: [
noChangeRun,
{
caption: "Modify main file",
edit: fs => appendText(fs, `/src/project/src/main.ts`, `something();`),
edit: sys => sys.appendFile(`/home/src/workspaces/project/src/main.ts`, `something();`),
},
{
caption: "Modify main file again",
edit: fs => appendText(fs, `/src/project/src/main.ts`, `something();`),
edit: sys => sys.appendFile(`/home/src/workspaces/project/src/main.ts`, `something();`),
},
{
caption: "Add new file and update main file",
edit: fs => {
fs.writeFileSync(`/src/project/src/newFile.ts`, "function foo() { return 20; }");
prependText(
fs,
`/src/project/src/main.ts`,
edit: sys => {
sys.writeFile(`/home/src/workspaces/project/src/newFile.ts`, "function foo() { return 20; }");
sys.prependFile(
`/home/src/workspaces/project/src/main.ts`,
`/// <reference path="./newFile.ts"/>
`,
);
appendText(fs, `/src/project/src/main.ts`, `foo();`);
sys.appendFile(`/home/src/workspaces/project/src/main.ts`, `foo();`);
},
},
{
caption: "Write file that could not be resolved",
edit: fs => fs.writeFileSync(`/src/project/src/fileNotFound.ts`, "function something2() { return 20; }"),
edit: sys => sys.writeFile(`/home/src/workspaces/project/src/fileNotFound.ts`, "function something2() { return 20; }"),
},
{
caption: "Modify main file",
edit: fs => appendText(fs, `/src/project/src/main.ts`, `something();`),
edit: sys => sys.appendFile(`/home/src/workspaces/project/src/main.ts`, `something();`),
},
],
baselinePrograms: true,
@@ -186,48 +177,48 @@ declare global {
}
verifyTsc({
scenario: "react-jsx-emit-mode",
subScenario: "with no backing types found doesn't crash",
fs: () =>
loadProjectFromFiles({
"/src/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result
"/src/project/node_modules/@types/react/index.d.ts": getJsxLibraryContent(), // doesn't contain a jsx-runtime definition
"/src/project/src/index.tsx": `export const App = () => <div propA={true}></div>;`,
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }),
}),
commandLineArgs: ["--p", "src/project"],
scenario: "incremental",
subScenario: "react-jsx-emit-mode with no backing types found doesn't crash",
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result
"/home/src/workspaces/project/node_modules/@types/react/index.d.ts": getJsxLibraryContent(), // doesn't contain a jsx-runtime definition
"/home/src/workspaces/project/src/index.tsx": `export const App = () => <div propA={true}></div>;`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ts.emptyArray,
});
verifyTsc({
scenario: "react-jsx-emit-mode",
subScenario: "with no backing types found doesn't crash under --strict",
fs: () =>
loadProjectFromFiles({
"/src/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result
"/src/project/node_modules/@types/react/index.d.ts": getJsxLibraryContent(), // doesn't contain a jsx-runtime definition
"/src/project/src/index.tsx": `export const App = () => <div propA={true}></div>;`,
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }),
}),
commandLineArgs: ["--p", "src/project", "--strict"],
scenario: "incremental",
subScenario: "react-jsx-emit-mode with no backing types found doesn't crash under --strict",
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result
"/home/src/workspaces/project/node_modules/@types/react/index.d.ts": getJsxLibraryContent(), // doesn't contain a jsx-runtime definition
"/home/src/workspaces/project/src/index.tsx": `export const App = () => <div propA={true}></div>;`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--strict"],
});
});
verifyTsc({
scenario: "incremental",
subScenario: "when new file is added to the referenced project",
commandLineArgs: ["-i", "-p", `src/projects/project2`],
fs: () =>
loadProjectFromFiles({
"/src/projects/project1/tsconfig.json": jsonToReadableText({
commandLineArgs: ["-i", "-p", "project2"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/projects/project1/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "none",
composite: true,
},
exclude: ["temp"],
}),
"/src/projects/project1/class1.ts": `class class1 {}`,
"/src/projects/project1/class1.d.ts": `declare class class1 {}`,
"/src/projects/project2/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/projects/project1/class1.ts": `class class1 {}`,
"/home/src/workspaces/projects/project1/class1.d.ts": `declare class class1 {}`,
"/home/src/workspaces/projects/project2/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "none",
composite: true,
@@ -236,12 +227,12 @@ declare global {
{ path: "../project1" },
],
}),
"/src/projects/project2/class2.ts": `class class2 {}`,
}),
"/home/src/workspaces/projects/project2/class2.ts": `class class2 {}`,
}, { currentDirectory: "/home/src/workspaces/projects" }),
edits: [
{
caption: "Add class3 to project1 and build it",
edit: fs => fs.writeFileSync("/src/projects/project1/class3.ts", `class class3 {}`, "utf-8"),
edit: sys => sys.writeFile("/home/src/workspaces/projects/project1/class3.ts", `class class3 {}`),
discrepancyExplanation: () => [
"Ts buildinfo will not be updated in incremental build so it will have semantic diagnostics cached from previous build",
"But in clean build because of global diagnostics, semantic diagnostics are not queried so not cached in tsbuildinfo",
@@ -249,22 +240,22 @@ declare global {
},
{
caption: "Add output of class3",
edit: fs => fs.writeFileSync("/src/projects/project1/class3.d.ts", `declare class class3 {}`, "utf-8"),
edit: sys => sys.writeFile("/home/src/workspaces/projects/project1/class3.d.ts", `declare class class3 {}`),
},
{
caption: "Add excluded file to project1",
edit: fs => {
fs.mkdirSync("/src/projects/project1/temp");
fs.writeFileSync("/src/projects/project1/temp/file.d.ts", `declare class file {}`, "utf-8");
edit: sys => {
sys.ensureFileOrFolder({ path: "/home/src/workspaces/projects/project1/temp" });
sys.writeFile("/home/src/workspaces/projects/project1/temp/file.d.ts", `declare class file {}`);
},
},
{
caption: "Delete output for class3",
edit: fs => fs.unlinkSync("/src/projects/project1/class3.d.ts"),
edit: sys => sys.deleteFile("/home/src/workspaces/projects/project1/class3.d.ts"),
},
{
caption: "Create output for class3",
edit: fs => fs.writeFileSync("/src/projects/project1/class3.d.ts", `declare class class3 {}`, "utf-8"),
edit: sys => sys.writeFile("/home/src/workspaces/projects/project1/class3.d.ts", `declare class class3 {}`),
},
],
});
@@ -272,10 +263,10 @@ declare global {
verifyTsc({
scenario: "incremental",
subScenario: "serializing error chains",
commandLineArgs: ["-p", `src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
commandLineArgs: ts.emptyArray,
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
incremental: true,
strict: true,
@@ -283,7 +274,7 @@ declare global {
module: "esnext",
},
}),
"/src/project/index.tsx": dedent`
"/home/src/workspaces/project/index.tsx": dedent`
declare namespace JSX {
interface ElementChildrenAttribute { children: {}; }
interface IntrinsicElements { div: {} }
@@ -297,16 +288,17 @@ declare global {
<div />
<div />
</Component>)`,
}, `\ninterface ReadonlyArray<T> { readonly length: number }`),
[libFile.path]: `${libFile.content}\ninterface ReadonlyArray<T> { readonly length: number }`,
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: noChangeOnlyRuns,
});
verifyTsc({
scenario: "incremental",
subScenario: "ts file with no-default-lib that augments the global scope",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": dedent`
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
@@ -317,56 +309,52 @@ declare global {
export {};
`,
"/src/project/tsconfig.json": dedent`
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"incremental": true,
"outDir": "dist",
},
}`,
}),
commandLineArgs: ["--p", "src/project", "--rootDir", "src/project/src"],
modifyFs: fs => {
fs.writeFileSync("/lib/lib.esnext.d.ts", libContent);
},
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
target: "ESNext",
module: "ESNext",
incremental: true,
outDir: "dist",
},
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--rootDir", "src"],
});
verifyTsc({
scenario: "incremental",
subScenario: "change to type that gets used as global through export in another file",
commandLineArgs: ["-p", `src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: { composite: true } }),
"/src/project/class1.ts": `const a: MagicNumber = 1;
commandLineArgs: ts.emptyArray,
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: { composite: true } }),
"/home/src/workspaces/project/class1.ts": `const a: MagicNumber = 1;
console.log(a);`,
"/src/project/constants.ts": "export default 1;",
"/src/project/types.d.ts": `type MagicNumber = typeof import('./constants').default`,
}),
"/home/src/workspaces/project/constants.ts": "export default 1;",
"/home/src/workspaces/project/types.d.ts": `type MagicNumber = typeof import('./constants').default`,
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "Modify imports used in global file",
edit: fs => fs.writeFileSync("/src/project/constants.ts", "export default 2;"),
edit: sys => sys.writeFile("/home/src/workspaces/project/constants.ts", "export default 2;"),
}],
});
verifyTsc({
scenario: "incremental",
subScenario: "change to type that gets used as global through export in another file through indirect import",
commandLineArgs: ["-p", `src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: { composite: true } }),
"/src/project/class1.ts": `const a: MagicNumber = 1;
commandLineArgs: ts.emptyArray,
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: { composite: true } }),
"/home/src/workspaces/project/class1.ts": `const a: MagicNumber = 1;
console.log(a);`,
"/src/project/constants.ts": "export default 1;",
"/src/project/reexport.ts": `export { default as ConstantNumber } from "./constants"`,
"/src/project/types.d.ts": `type MagicNumber = typeof import('./reexport').ConstantNumber`,
}),
"/home/src/workspaces/project/constants.ts": "export default 1;",
"/home/src/workspaces/project/reexport.ts": `export { default as ConstantNumber } from "./constants"`,
"/home/src/workspaces/project/types.d.ts": `type MagicNumber = typeof import('./reexport').ConstantNumber`,
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "Modify imports used in global file",
edit: fs => fs.writeFileSync("/src/project/constants.ts", "export default 2;"),
edit: sys => sys.writeFile("/home/src/workspaces/project/constants.ts", "export default 2;"),
}],
});
@@ -374,16 +362,16 @@ console.log(a);`,
verifyTsc({
scenario: "incremental",
subScenario: `change to modifier of class expression field${declaration ? " with declaration emit enabled" : ""}`,
commandLineArgs: ["-p", "src/project", "--incremental"],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: { declaration } }),
"/src/project/main.ts": dedent`
commandLineArgs: ["--incremental"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: { declaration } }),
"/home/src/workspaces/project/main.ts": dedent`
import MessageablePerson from './MessageablePerson.js';
function logMessage( person: MessageablePerson ) {
console.log( person.message );
}`,
"/src/project/MessageablePerson.ts": dedent`
"/home/src/workspaces/project/MessageablePerson.ts": dedent`
const Messageable = () => {
return class MessageableClass {
public message = 'hello';
@@ -392,11 +380,10 @@ console.log(a);`,
const wrapper = () => Messageable();
type MessageablePerson = InstanceType<ReturnType<typeof wrapper>>;
export default MessageablePerson;`,
}),
modifyFs: fs =>
appendText(
fs,
"/lib/lib.d.ts",
}, { currentDirectory: "/home/src/workspaces/project" }),
modifySystem: sys =>
sys.appendFile(
libFile.path,
dedent`
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;`,
@@ -405,12 +392,12 @@ console.log(a);`,
noChangeRun,
{
caption: "modify public to protected",
edit: fs => replaceText(fs, "/src/project/MessageablePerson.ts", "public", "protected"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/MessageablePerson.ts", "public", "protected"),
},
noChangeRun,
{
caption: "modify protected to public",
edit: fs => replaceText(fs, "/src/project/MessageablePerson.ts", "protected", "public"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/MessageablePerson.ts", "protected", "public"),
},
noChangeRun,
],
@@ -424,7 +411,7 @@ console.log(a);`,
return {
caption,
edit: ts.noop,
commandLineArgs: ["--p", "/src/project", ...options],
commandLineArgs: [...options],
};
}
function noChangeWithSubscenario(caption: string): TestTscEdit {
@@ -451,25 +438,25 @@ console.log(a);`,
function localChange(): TestTscEdit {
return {
caption: "local change",
edit: fs => replaceText(fs, "/src/project/a.ts", "Local = 1", "Local = 10"),
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "Local = 1", "Local = 10"),
};
}
function fs(options: ts.CompilerOptions) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/src/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project/d.ts": `import { b } from "./b";export const d = b;`,
});
function sys(options: ts.CompilerOptions) {
return TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/home/src/workspaces/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/home/src/workspaces/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/home/src/workspaces/project/c.ts": `import { a } from "./a";export const c = a;`,
"/home/src/workspaces/project/d.ts": `import { b } from "./b";export const d = b;`,
}, { currentDirectory: "/home/src/workspaces/project" });
}
function enableDeclarationMap(): TestTscEdit {
return {
caption: "declarationMap enabling",
edit: fs => {
const config = JSON.parse(fs.readFileSync("/src/project/tsconfig.json", "utf-8"));
edit: sys => {
const config = JSON.parse(sys.readFile("/home/src/workspaces/project/tsconfig.json")!);
config.compilerOptions.declarationMap = true;
fs.writeFileSync("/src/project/tsconfig.json", jsonToReadableText(config));
sys.writeFile("/home/src/workspaces/project/tsconfig.json", jsonToReadableText(config));
},
};
}
@@ -480,8 +467,8 @@ console.log(a);`,
verifyTsc({
scenario: "incremental",
subScenario: scenarioName("different options"),
fs: () => fs({ composite: true, ...options }),
commandLineArgs: ["--p", "/src/project"],
sys: () => sys({ composite: true, ...options }),
commandLineArgs: ts.emptyArray,
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
@@ -503,8 +490,8 @@ console.log(a);`,
verifyTsc({
scenario: "incremental",
subScenario: scenarioName("different options with incremental"),
fs: () => fs({ incremental: true, ...options }),
commandLineArgs: ["--p", "/src/project"],
sys: () => sys({ incremental: true, ...options }),
commandLineArgs: ts.emptyArray,
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
@@ -531,22 +518,22 @@ console.log(a);`,
verifyTsc({
scenario: "incremental",
subScenario: "when file is deleted",
commandLineArgs: ["-p", `/src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
commandLineArgs: ts.emptyArray,
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
outDir: "outDir",
},
}),
"/src/project/file1.ts": `export class C { }`,
"/src/project/file2.ts": `export class D { }`,
}),
"/home/src/workspaces/project/file1.ts": `export class C { }`,
"/home/src/workspaces/project/file2.ts": `export class D { }`,
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [
{
caption: "delete file with imports",
edit: fs => fs.unlinkSync("/src/project/file2.ts"),
edit: sys => sys.deleteFile("/home/src/workspaces/project/file2.ts"),
},
],
});
@@ -554,10 +541,10 @@ console.log(a);`,
verifyTsc({
scenario: "incremental",
subScenario: "generates typerefs correctly",
commandLineArgs: ["-p", `/src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
commandLineArgs: ts.emptyArray,
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
outDir: "outDir",
@@ -565,12 +552,12 @@ console.log(a);`,
},
include: ["src"],
}),
"/src/project/src/box.ts": dedent`
"/home/src/workspaces/project/src/box.ts": dedent`
export interface Box<T> {
unbox(): T
}
`,
"/src/project/src/bug.js": dedent`
"/home/src/workspaces/project/src/bug.js": dedent`
import * as B from "./box.js"
import * as W from "./wrap.js"
@@ -590,15 +577,15 @@ console.log(a);`,
export const bug = wrap({ n: box(1) });
`,
"/src/project/src/wrap.ts": dedent`
"/home/src/workspaces/project/src/wrap.ts": dedent`
export type Wrap<C> = {
[K in keyof C]: { wrapped: C[K] }
}
`,
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [{
caption: "modify js file",
edit: fs => appendText(fs, "/src/project/src/bug.js", `export const something = 1;`),
edit: sys => sys.appendFile("/home/src/workspaces/project/src/bug.js", `export const something = 1;`),
}],
});
@@ -609,20 +596,20 @@ console.log(a);`,
DifferentFile = "aliased in different file ",
}
function fileWithEnum(withAlias: AliasType) {
return withAlias !== AliasType.DifferentFile ? "/src/project/b.d.ts" : "/src/project/worker.d.ts";
return withAlias !== AliasType.DifferentFile ? "/home/src/workspaces/project/b.d.ts" : "/home/src/workspaces/project/worker.d.ts";
}
function verify(withAlias: AliasType, preserveConstEnums: boolean) {
verifyTsc({
scenario: "incremental",
subScenario: `with ${withAlias}const enums${preserveConstEnums ? " with preserveConstEnums" : ""}`,
commandLineArgs: ["-i", `/src/project/a.ts`, "--tsbuildinfofile", "/src/project/a.tsbuildinfo", ...preserveConstEnums ? ["--preserveConstEnums"] : []],
fs: () =>
loadProjectFromFiles({
"/src/project/a.ts": dedent`
commandLineArgs: ["-i", `a.ts`, "--tsbuildinfofile", "a.tsbuildinfo", ...preserveConstEnums ? ["--preserveConstEnums"] : []],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a.ts": dedent`
import {A} from "./c"
let a = A.ONE
`,
"/src/project/b.d.ts": withAlias === AliasType.SameFile ?
"/home/src/workspaces/project/b.d.ts": withAlias === AliasType.SameFile ?
dedent`
declare const enum AWorker {
ONE = 1
@@ -638,33 +625,33 @@ console.log(a);`,
ONE = 1
}
`,
"/src/project/c.ts": dedent`
"/home/src/workspaces/project/c.ts": dedent`
import {A} from "./b"
let b = A.ONE
export {A}
`,
"/src/project/worker.d.ts": dedent`
"/home/src/workspaces/project/worker.d.ts": dedent`
export const enum AWorker {
ONE = 1
}
`,
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: [
{
caption: "change enum value",
edit: fs => replaceText(fs, fileWithEnum(withAlias), "1", "2"),
edit: sys => sys.replaceFileText(fileWithEnum(withAlias), "1", "2"),
},
{
caption: "change enum value again",
edit: fs => replaceText(fs, fileWithEnum(withAlias), "2", "3"),
edit: sys => sys.replaceFileText(fileWithEnum(withAlias), "2", "3"),
},
{
caption: "something else changes in b.d.ts",
edit: fs => appendText(fs, "/src/project/b.d.ts", "export const randomThing = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/project/b.d.ts", "export const randomThing = 10;"),
},
{
caption: "something else changes in b.d.ts again",
edit: fs => appendText(fs, "/src/project/b.d.ts", "export const randomThing2 = 10;"),
edit: sys => sys.appendFile("/home/src/workspaces/project/b.d.ts", "export const randomThing2 = 10;"),
},
],
});
@@ -1,36 +1,35 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { libContent } from "../helpers/contents.js";
import {
forEachLibResolutionScenario,
getCommandLineArgsForLibResolution,
getFsForLibResolution,
getFsForLibResolutionUnknown,
getSysForLibResolutionUnknown,
} from "../helpers/libraryResolution.js";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: libraryResolution:: library file resolution", () => {
function verify(libRedirection?: true, withoutConfig?: true) {
verifyTsc({
scenario: "libraryResolution",
subScenario: `${withoutConfig ? "without" : "with"} config${libRedirection ? " with redirection" : ""}`,
fs: () => getFsForLibResolution(libRedirection),
commandLineArgs: getCommandLineArgsForLibResolution(withoutConfig),
baselinePrograms: true,
});
function verify(withoutConfig?: true) {
forEachLibResolutionScenario(/*forTsserver*/ false, withoutConfig, (subScenario, sys) =>
verifyTsc({
scenario: "libraryResolution",
subScenario,
sys,
commandLineArgs: getCommandLineArgsForLibResolution(withoutConfig),
baselinePrograms: true,
}));
}
verify();
verify(/*libRedirection*/ true);
verify(/*libRedirection*/ undefined, /*withoutConfig*/ true);
verify(/*libRedirection*/ true, /*withoutConfig*/ true);
verify(/*withoutConfig*/ true);
verifyTsc({
scenario: "libraryResolution",
subScenario: "unknown lib",
fs: () => getFsForLibResolutionUnknown(),
sys: getSysForLibResolutionUnknown,
commandLineArgs: getCommandLineArgsForLibResolution(/*withoutConfig*/ undefined),
baselinePrograms: true,
});
@@ -38,24 +37,23 @@ describe("unittests:: tsc:: libraryResolution:: library file resolution", () =>
verifyTsc({
scenario: "libraryResolution",
subScenario: "when noLib toggles",
fs: () =>
loadProjectFromFiles({
"/src/a.d.ts": `declare const a = "hello";`,
"/src/b.ts": `const b = 10;`,
"/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a.d.ts": `declare const a = "hello";`,
"/home/src/workspaces/project/b.ts": `const b = 10;`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
declaration: true,
incremental: true,
lib: ["es6"],
},
}),
"/lib/lib.es2015.d.ts": libContent,
}),
commandLineArgs: ["-p", "/src/tsconfig.json"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: emptyArray,
edits: [
{
...noChangeRun,
commandLineArgs: ["-p", "/src/tsconfig.json", "--noLib"],
commandLineArgs: ["--noLib"],
},
],
baselinePrograms: true,
@@ -64,22 +62,21 @@ describe("unittests:: tsc:: libraryResolution:: library file resolution", () =>
verifyTsc({
scenario: "libraryResolution",
subScenario: "when one of the file skips default lib inclusion",
fs: () =>
loadProjectFromFiles({
"/src/a.d.ts": dedent`
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/a.d.ts": dedent`
/// <reference no-default-lib="true"/>
/// <reference lib="es6"/>
declare const a = "hello";
`,
"/src/b.d.ts": `export const b = 10;`,
"/src/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/b.d.ts": `export const b = 10;`,
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
lib: ["es6", "dom"],
},
}),
"/lib/lib.es2015.d.ts": libContent,
}),
commandLineArgs: ["-p", "/src/tsconfig.json", "-i", "--explainFiles"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-i", "--explainFiles"],
baselinePrograms: true,
});
});
+14 -27
View File
@@ -1,51 +1,38 @@
import * as Utils from "../../_namespaces/Utils.js";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: listFilesOnly::", () => {
verifyTsc({
scenario: "listFilesOnly",
subScenario: "combined with watch",
fs: () =>
loadProjectFromFiles({
"/src/test.ts": Utils.dedent`
export const x = 1;`,
}),
commandLineArgs: ["/src/test.ts", "--watch", "--listFilesOnly"],
});
verifyTsc({
scenario: "listFilesOnly",
subScenario: "loose file",
fs: () =>
loadProjectFromFiles({
"/src/test.ts": Utils.dedent`
export const x = 1;`,
}),
commandLineArgs: ["/src/test.ts", "--listFilesOnly"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/test.ts": "export const x = 1;",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["test.ts", "--listFilesOnly"],
});
verifyTsc({
scenario: "listFilesOnly",
subScenario: "combined with incremental",
fs: () =>
loadProjectFromFiles({
"/src/test.ts": `export const x = 1;`,
"/src/tsconfig.json": "{}",
}),
commandLineArgs: ["-p", "/src", "--incremental", "--listFilesOnly"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/test.ts": "export const x = 1;",
"/home/src/workspaces/project/tsconfig.json": "{}",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--incremental", "--listFilesOnly"],
edits: [
{
...noChangeRun,
commandLineArgs: ["-p", "/src", "--incremental"],
commandLineArgs: ["--incremental"],
},
noChangeRun,
{
...noChangeRun,
commandLineArgs: ["-p", "/src", "--incremental"],
commandLineArgs: ["--incremental"],
},
],
});
+27 -144
View File
@@ -1,91 +1,34 @@
import {
emptyArray,
ModuleKind,
ScriptTarget,
} from "../../_namespaces/ts.js";
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import {
getFsConentsForAlternateResultAtTypesPackageJson,
getFsContentsForAlternateResult,
getFsContentsForAlternateResultDts,
getFsContentsForAlternateResultPackageJson,
} from "../helpers/alternateResult.js";
import {
compilerOptionsToConfigJson,
libContent,
} from "../helpers/contents.js";
import { verifyAlternateResultScenario } from "../helpers/alternateResult.js";
import { compilerOptionsToConfigJson } from "../helpers/contents.js";
import { verifyTsc } from "../helpers/tsc.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import {
createWatchedSystem,
libFile,
} from "../helpers/virtualFileSystemWithWatch.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: moduleResolution::", () => {
verifyTsc({
scenario: "moduleResolution",
subScenario: "alternateResult",
fs: () => loadProjectFromFiles(getFsContentsForAlternateResult()),
commandLineArgs: ["-p", "/home/src/projects/project"],
baselinePrograms: true,
edits: [
{
caption: "delete the alternateResult in @types",
edit: fs => fs.unlinkSync("/home/src/projects/project/node_modules/@types/bar/index.d.ts"),
},
{
caption: "delete the ndoe10Result in package/types",
edit: fs => fs.unlinkSync("/home/src/projects/project/node_modules/foo/index.d.ts"),
},
{
caption: "add the alternateResult in @types",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/@types/bar/index.d.ts", getFsContentsForAlternateResultDts("bar")),
},
{
caption: "add the ndoe10Result in package/types",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/foo/index.d.ts", getFsContentsForAlternateResultDts("foo")),
},
{
caption: "update package.json from @types so error is fixed",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/@types/bar/package.json", getFsConentsForAlternateResultAtTypesPackageJson("bar", /*addTypesCondition*/ true)),
},
{
caption: "update package.json so error is fixed",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/foo/package.json", getFsContentsForAlternateResultPackageJson("foo", /*addTypes*/ true, /*addTypesCondition*/ true)),
},
{
caption: "update package.json from @types so error is introduced",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/@types/bar2/package.json", getFsConentsForAlternateResultAtTypesPackageJson("bar2", /*addTypesCondition*/ false)),
},
{
caption: "update package.json so error is introduced",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/foo2/package.json", getFsContentsForAlternateResultPackageJson("foo2", /*addTypes*/ true, /*addTypesCondition*/ false)),
},
{
caption: "delete the alternateResult in @types",
edit: fs => fs.unlinkSync("/home/src/projects/project/node_modules/@types/bar2/index.d.ts"),
},
{
caption: "delete the ndoe10Result in package/types",
edit: fs => fs.unlinkSync("/home/src/projects/project/node_modules/foo2/index.d.ts"),
},
{
caption: "add the alternateResult in @types",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/@types/bar2/index.d.ts", getFsContentsForAlternateResultDts("bar2")),
},
{
caption: "add the ndoe10Result in package/types",
edit: fs => fs.writeFileSync("/home/src/projects/project/node_modules/foo2/index.d.ts", getFsContentsForAlternateResultDts("foo2")),
},
],
});
verifyAlternateResultScenario(
/*forTsserver*/ false,
(subScenario, sys, edits) =>
verifyTsc({
scenario: "moduleResolution",
subScenario,
sys,
commandLineArgs: emptyArray,
baselinePrograms: true,
edits: edits(),
}),
);
verifyTscWatch({
verifyTsc({
scenario: "moduleResolution",
subScenario: "pnpm style layout",
sys: () =>
createWatchedSystem({
TestServerHost.createWatchedSystem({
// button@0.0.1
"/home/src/projects/component-type-checker/node_modules/.pnpm/@component-type-checker+button@0.0.1/node_modules/@component-type-checker/button/src/index.ts": dedent`
export interface Button {
@@ -219,75 +162,16 @@ describe("unittests:: tsc:: moduleResolution::", () => {
"/home/src/projects/component-type-checker/packages/app/node_modules/@component-type-checker/sdk": {
symLink: "/home/src/projects/component-type-checker/packages/sdk",
},
"/a/lib/lib.es5.d.ts": libContent,
}, { currentDirectory: "/home/src/projects/component-type-checker/packages/app" }),
commandLineArgs: ["--traceResolution", "--explainFiles"],
});
verifyTscWatch({
scenario: "moduleResolution",
subScenario: "late discovered dependency symlink",
sys: () =>
createWatchedSystem({
"/workspace/packageA/index.d.ts": dedent`
export declare class Foo {
private f: any;
}`,
"/workspace/packageB/package.json": dedent`
{
"private": true,
"dependencies": {
"package-a": "file:../packageA"
}
}`,
"/workspace/packageB/index.d.ts": dedent`
import { Foo } from "package-a";
export declare function invoke(): Foo;`,
"/workspace/packageC/package.json": dedent`
{
"private": true,
"dependencies": {
"package-b": "file:../packageB",
"package-a": "file:../packageA"
}
}`,
"/workspace/packageC/index.ts": dedent`
import * as pkg from "package-b";
export const a = pkg.invoke();`,
"/workspace/packageC/node_modules/package-a": { symLink: "/workspace/packageA" },
"/workspace/packageB/node_modules/package-a": { symLink: "/workspace/packageA" },
"/workspace/packageC/node_modules/package-b": { symLink: "/workspace/packageB" },
"/a/lib/lib.d.ts": libContent,
"/workspace/packageC/tsconfig.json": jsonToReadableText({
compilerOptions: {
declaration: true,
},
}),
}, { currentDirectory: "/workspace/packageC" }),
commandLineArgs: ["--traceResolution", "--explainFiles", "--watch"],
edits: [
{
caption: "change index.ts",
edit: fs =>
fs.writeFile(
"/workspace/packageC/index.ts",
dedent`
import * as pkg from "package-b";
export const aa = pkg.invoke();`,
),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
});
verifyTsc({
scenario: "moduleResolution",
subScenario: "package json scope",
fs: () =>
loadProjectFromFiles({
"/src/projects/project/src/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/tsconfig.json": jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson({
target: ScriptTarget.ES2016,
composite: true,
@@ -300,20 +184,19 @@ describe("unittests:: tsc:: moduleResolution::", () => {
"fileB.mts",
],
}),
"/src/projects/project/src/main.ts": "export const x = 10;",
"/src/projects/project/src/fileA.ts": dedent`
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/src/fileA.ts": dedent`
import { foo } from "./fileB.mjs";
foo();
`,
"/src/projects/project/src/fileB.mts": "export function foo() {}",
"/src/projects/project/package.json": jsonToReadableText({ name: "app", version: "1.0.0" }),
"/lib/lib.es2016.full.d.ts": libFile.content,
}, { cwd: "/src/projects/project" }),
"/home/src/workspaces/project/src/fileB.mts": "export function foo() {}",
"/home/src/workspaces/project/package.json": jsonToReadableText({ name: "app", version: "1.0.0" }),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-p", "src", "--explainFiles", "--extendedDiagnostics"],
edits: [
{
caption: "Delete package.json",
edit: fs => fs.unlinkSync(`/src/projects/project/package.json`),
edit: sys => sys.deleteFile("/home/src/workspaces/project/package.json"),
},
],
});
+7 -7
View File
@@ -8,7 +8,7 @@ import {
noChangeOnlyRuns,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: noEmit::", () => {
forEachNoEmitChanges(["--p"]);
@@ -16,17 +16,17 @@ describe("unittests:: tsc:: noEmit::", () => {
verifyTsc({
scenario: "noEmit",
subScenario: "when project has strict true",
commandLineArgs: ["-noEmit", "-p", `src/project`],
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
commandLineArgs: ["-noEmit"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
incremental: true,
strict: true,
},
}),
"/src/project/class1.ts": `export class class1 {}`,
}),
"/home/src/workspaces/project/class1.ts": `export class class1 {}`,
}, { currentDirectory: "/home/src/workspaces/project" }),
edits: noChangeOnlyRuns,
baselinePrograms: true,
});
+40 -42
View File
@@ -1,10 +1,8 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { forEachNoEmitOnErrorScenarioTsc } from "../helpers/noEmitOnError.js";
import { verifyTsc } from "../helpers/tsc.js";
import {
loadProjectFromFiles,
replaceText,
} from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: noEmitOnError::", () => {
forEachNoEmitOnErrorScenarioTsc([]);
@@ -12,24 +10,24 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
verifyTsc({
scenario: "noEmitOnError",
subScenario: `multiFile/when declarationMap changes`,
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
noEmitOnError: true,
declaration: true,
composite: true,
},
}),
"/src/project/a.ts": "const x = 10;",
"/src/project/b.ts": "const y = 10;",
}),
commandLineArgs: ["--p", "/src/project"],
"/home/src/workspaces/project/a.ts": "const x = 10;",
"/home/src/workspaces/project/b.ts": "const y = 10;",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: emptyArray,
edits: [
{
caption: "error and enable declarationMap",
edit: fs => replaceText(fs, "/src/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--declarationMap"],
discrepancyExplanation: () => [
`Clean build does not emit any file so will have emitSignatures with all files since they are not emitted`,
`Incremental build has emitSignatures from before, so it will have a.ts with signature since file.version isnt same`,
@@ -38,8 +36,8 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
},
{
caption: "fix error declarationMap",
edit: fs => replaceText(fs, "/src/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--declarationMap"],
},
],
});
@@ -47,9 +45,9 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
verifyTsc({
scenario: "noEmitOnError",
subScenario: `outFile/when declarationMap changes`,
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
noEmitOnError: true,
declaration: true,
@@ -57,15 +55,15 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
outFile: "../outFile.js",
},
}),
"/src/project/a.ts": "const x = 10;",
"/src/project/b.ts": "const y = 10;",
}),
commandLineArgs: ["--p", "/src/project"],
"/home/src/workspaces/project/a.ts": "const x = 10;",
"/home/src/workspaces/project/b.ts": "const y = 10;",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: emptyArray,
edits: [
{
caption: "error and enable declarationMap",
edit: fs => replaceText(fs, "/src/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--declarationMap"],
discrepancyExplanation: () => [
`Clean build does not emit any file so will not have outSignature`,
`Incremental build has outSignature from before`,
@@ -73,8 +71,8 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
},
{
caption: "fix error declarationMap",
edit: fs => replaceText(fs, "/src/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
edit: sys => sys.replaceFileText("/home/src/workspaces/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--declarationMap"],
},
],
});
@@ -82,21 +80,21 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
verifyTsc({
scenario: "noEmitOnError",
subScenario: "multiFile/file deleted before fixing error with noEmitOnError",
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "outDir",
noEmitOnError: true,
},
}),
"/src/project/file1.ts": `export const x: 30 = "hello";`,
"/src/project/file2.ts": `export class D { }`,
}),
commandLineArgs: ["--p", "/src/project", "-i"],
"/home/src/workspaces/project/file1.ts": `export const x: 30 = "hello";`,
"/home/src/workspaces/project/file2.ts": `export class D { }`,
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-i"],
edits: [{
caption: "delete file without error",
edit: fs => fs.unlinkSync("/src/project/file2.ts"),
edit: sys => sys.deleteFile("/home/src/workspaces/project/file2.ts"),
}],
baselinePrograms: true,
});
@@ -104,22 +102,22 @@ describe("unittests:: tsc:: noEmitOnError::", () => {
verifyTsc({
scenario: "noEmitOnError",
subScenario: "outFile/file deleted before fixing error with noEmitOnError",
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outFile: "../outFile.js",
module: "amd",
noEmitOnError: true,
},
}),
"/src/project/file1.ts": `export const x: 30 = "hello";`,
"/src/project/file2.ts": `export class D { }`,
}),
commandLineArgs: ["--p", "/src/project", "-i"],
"/home/src/workspaces/project/file1.ts": `export const x: 30 = "hello";`,
"/home/src/workspaces/project/file2.ts": `export class D { }`,
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["-i"],
edits: [{
caption: "delete file without error",
edit: fs => fs.unlinkSync("/src/project/file2.ts"),
edit: sys => sys.deleteFile("/home/src/workspaces/project/file2.ts"),
}],
baselinePrograms: true,
});
@@ -1,15 +1,16 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: projectReferences::", () => {
verifyTsc({
scenario: "projectReferences",
subScenario: "when project contains invalid project reference",
fs: () =>
loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/src/main.ts": "export const x = 10;",
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "amd",
outFile: "theApp.js",
@@ -18,42 +19,42 @@ describe("unittests:: tsc:: projectReferences::", () => {
{ path: "../Util/Dates" },
],
}),
}),
commandLineArgs: ["--p", "src/project"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: emptyArray,
});
verifyTsc({
scenario: "projectReferences",
subScenario: "when project references composite project with noEmit",
fs: () =>
loadProjectFromFiles({
"/src/utils/index.ts": "export const x = 10;",
"/src/utils/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/src/utils/index.ts": "export const x = 10;",
"/home/src/workspaces/solution/src/utils/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
noEmit: true,
},
}),
"/src/project/index.ts": `import { x } from "../utils";`,
"/src/project/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/index.ts": `import { x } from "../utils";`,
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
references: [
{ path: "../utils" },
],
}),
}),
commandLineArgs: ["--p", "src/project"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--p", "project"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "default import interop uses referenced project settings",
fs: () =>
loadProjectFromFiles({
"/node_modules/ambiguous-package/package.json": `{ "name": "ambiguous-package" }`,
"/node_modules/ambiguous-package/index.d.ts": "export declare const ambiguous: number;",
"/node_modules/esm-package/package.json": `{ "name": "esm-package", "type": "module" }`,
"/node_modules/esm-package/index.d.ts": "export declare const esm: number;",
"/lib/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/node_modules/ambiguous-package/package.json": jsonToReadableText({ name: "ambiguous-package" }),
"/home/src/workspaces/project/node_modules/ambiguous-package/index.d.ts": "export declare const ambiguous: number;",
"/home/src/workspaces/project/node_modules/esm-package/package.json": jsonToReadableText({ name: "esm-package", type: "module" }),
"/home/src/workspaces/project/node_modules/esm-package/index.d.ts": "export declare const esm: number;",
"/home/src/workspaces/project/lib/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
@@ -64,9 +65,9 @@ describe("unittests:: tsc:: projectReferences::", () => {
},
include: ["src"],
}),
"/lib/src/a.ts": "export const a = 0;",
"/lib/dist/a.d.ts": "export declare const a = 0;",
"/app/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/project/lib/src/a.ts": "export const a = 0;",
"/home/src/workspaces/project/lib/dist/a.d.ts": "export declare const a = 0;",
"/home/src/workspaces/project/app/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "esnext",
moduleResolution: "bundler",
@@ -78,33 +79,33 @@ describe("unittests:: tsc:: projectReferences::", () => {
{ path: "../lib" },
],
}),
"/app/src/local.ts": "export const local = 0;",
"/app/src/index.ts": `
"/home/src/workspaces/project/app/src/local.ts": "export const local = 0;",
"/home/src/workspaces/project/app/src/index.ts": `
import local from "./local"; // Error
import esm from "esm-package"; // Error
import referencedSource from "../../lib/src/a"; // Error
import referencedDeclaration from "../../lib/dist/a"; // Error
import ambiguous from "ambiguous-package"; // Ok`,
}),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "app", "--pretty", "false"],
});
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({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/utils/index.ts": "export const enum E { A = 1 }",
"/home/src/workspaces/solution/utils/index.d.ts": "export declare const enum E { A = 1 }",
"/home/src/workspaces/solution/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({
"/home/src/workspaces/solution/project/index.ts": `import { E } from "../utils"; E.A;`,
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
isolatedModules: true,
},
@@ -112,35 +113,35 @@ describe("unittests:: tsc:: projectReferences::", () => {
{ path: "../utils" },
],
}),
}),
commandLineArgs: ["--p", "src/project"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--p", "project"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "importing const enum from referenced project with preserveConstEnums and verbatimModuleSyntax",
fs: () =>
loadProjectFromFiles({
"/src/preserve/index.ts": "export const enum E { A = 1 }",
"/src/preserve/index.d.ts": "export declare const enum E { A = 1 }",
"/src/preserve/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/solution/preserve/index.ts": "export const enum E { A = 1 }",
"/home/src/workspaces/solution/preserve/index.d.ts": "export declare const enum E { A = 1 }",
"/home/src/workspaces/solution/preserve/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
preserveConstEnums: true,
},
}),
"/src/no-preserve/index.ts": "export const enum E { A = 1 }",
"/src/no-preserve/index.d.ts": "export declare const enum F { A = 1 }",
"/src/no-preserve/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/no-preserve/index.ts": "export const enum E { A = 1 }",
"/home/src/workspaces/solution/no-preserve/index.d.ts": "export declare const enum F { A = 1 }",
"/home/src/workspaces/solution/no-preserve/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
preserveConstEnums: false,
},
}),
"/src/project/index.ts": `import { E } from "../preserve";\nimport { F } from "../no-preserve";\nE.A; F.A;`,
"/src/project/tsconfig.json": jsonToReadableText({
"/home/src/workspaces/solution/project/index.ts": `import { E } from "../preserve";\nimport { F } from "../no-preserve";\nE.A; F.A;`,
"/home/src/workspaces/solution/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "preserve",
verbatimModuleSyntax: true,
@@ -150,7 +151,7 @@ describe("unittests:: tsc:: projectReferences::", () => {
{ path: "../no-preserve" },
],
}),
}),
commandLineArgs: ["--p", "src/project", "--pretty", "false"],
}, { currentDirectory: "/home/src/workspaces/solution" }),
commandLineArgs: ["--p", "project", "--pretty", "false"],
});
});
@@ -1,7 +1,7 @@
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
function emptyModule() {
return "export { };";
@@ -36,201 +36,201 @@ function getConfig({ references, options, config }: {
});
}
describe("unittests:: config:: project-references meta check", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references meta check", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "default setup was created correctly",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig(),
"/primary/a.ts": emptyModule(),
"/secondary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig(),
"/home/src/workspaces/project/primary/a.ts": emptyModule(),
"/home/src/workspaces/project/secondary/tsconfig.json": getConfig({
references: ["../primary"],
}),
"/secondary/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"],
"/home/src/workspaces/project/secondary/b.ts": moduleImporting("../primary/a"),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "primary/tsconfig.json"],
});
});
/**
* Validate that we enforce the basic settings constraints for referenced projects
*/
describe("unittests:: config:: project-references constraint checking for settings", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references constraint checking for settings", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when declaration = false",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig({
options: {
declaration: false,
},
}),
"/primary/a.ts": emptyModule(),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"],
"/home/src/workspaces/project/primary/a.ts": emptyModule(),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "primary/tsconfig.json"],
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the referenced project doesnt have composite",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig({
options: {
composite: false,
},
}),
"/primary/a.ts": emptyModule(),
"/reference/tsconfig.json": getConfig({
"/home/src/workspaces/project/primary/a.ts": emptyModule(),
"/home/src/workspaces/project/reference/tsconfig.json": getConfig({
references: ["../primary"],
config: {
files: ["b.ts"],
},
}),
"/reference/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/reference/tsconfig.json"],
"/home/src/workspaces/project/reference/b.ts": moduleImporting("../primary/a"),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "reference/tsconfig.json"],
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "does not error when the referenced project doesnt have composite if its a container project",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig({
options: {
composite: false,
},
}),
"/primary/a.ts": emptyModule(),
"/reference/tsconfig.json": getConfig({
"/home/src/workspaces/project/primary/a.ts": emptyModule(),
"/home/src/workspaces/project/reference/tsconfig.json": getConfig({
references: ["../primary"],
config: {
files: [],
},
}),
"/reference/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/reference/tsconfig.json"],
"/home/src/workspaces/project/reference/b.ts": moduleImporting("../primary/a"),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "reference/tsconfig.json"],
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the file list is not exhaustive",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig({
config: {
files: ["a.ts"],
},
}),
"/primary/a.ts": "import * as b from './b'",
"/primary/b.ts": "export {}",
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"],
"/home/src/workspaces/project/primary/a.ts": "import * as b from './b'",
"/home/src/workspaces/project/primary/b.ts": "export {}",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "primary/tsconfig.json"],
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the referenced project doesnt exist",
fs: () =>
loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/primary/tsconfig.json": getConfig({
references: ["../foo"],
}),
"/primary/a.ts": emptyModule(),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"],
"/home/src/workspaces/project/primary/a.ts": emptyModule(),
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "primary/tsconfig.json"],
});
});
/**
* Path mapping behavior
*/
describe("unittests:: config:: project-references path mapping", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references path mapping", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "redirects to the output dts file",
fs: () =>
loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/alpha/bin/a.d.ts": emptyModule(),
"/beta/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/alpha/tsconfig.json": getConfig(),
"/home/src/workspaces/project/alpha/a.ts": "export const m: number = 3;",
"/home/src/workspaces/project/alpha/bin/a.d.ts": emptyModule(),
"/home/src/workspaces/project/beta/tsconfig.json": getConfig({
references: ["../alpha"],
}),
"/beta/b.ts": "import { m } from '../alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json", "--explainFiles"],
"/home/src/workspaces/project/beta/b.ts": "import { m } from '../alpha/a'",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "beta/tsconfig.json", "--explainFiles"],
});
});
describe("unittests:: config:: project-references nice-behavior", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references nice-behavior", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "issues a nice error when the input file is missing",
fs: () =>
loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/beta/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/alpha/tsconfig.json": getConfig(),
"/home/src/workspaces/project/alpha/a.ts": "export const m: number = 3;",
"/home/src/workspaces/project/beta/tsconfig.json": getConfig({
references: ["../alpha"],
}),
"/beta/b.ts": "import { m } from '../alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json"],
"/home/src/workspaces/project/beta/b.ts": "import { m } from '../alpha/a'",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "beta/tsconfig.json"],
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "issues a nice error when the input file is missing when module reference is not relative",
fs: () =>
loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/beta/tsconfig.json": getConfig({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/alpha/tsconfig.json": getConfig(),
"/home/src/workspaces/project/alpha/a.ts": "export const m: number = 3;",
"/home/src/workspaces/project/beta/tsconfig.json": getConfig({
references: ["../alpha"],
options: {
baseUrl: "./",
paths: {
"@alpha/*": ["/alpha/*"],
"@alpha/*": ["/home/src/workspaces/project/alpha/*"],
},
},
}),
"/beta/b.ts": "import { m } from '@alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json"],
"/home/src/workspaces/project/beta/b.ts": "import { m } from '@alpha/a'",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "beta/tsconfig.json"],
});
});
/**
* 'composite' behavior
*/
describe("unittests:: config:: project-references behavior changes under composite: true", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references behavior changes under composite: true", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "doesnt infer the rootDir from source paths",
fs: () =>
loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/src/a.ts": "export const m: number = 3;",
}),
commandLineArgs: ["--p", "/alpha/tsconfig.json"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/alpha/tsconfig.json": getConfig(),
"/home/src/workspaces/project/alpha/src/a.ts": "export const m: number = 3;",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "alpha/tsconfig.json"],
});
});
describe("unittests:: config:: project-references errors when a file in a composite project occurs outside the root", () => {
describe("unittests:: tsc:: projectReferencesConfig:: project-references errors when a file in a composite project occurs outside the root", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when a file is outside the rootdir",
fs: () =>
loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/src/a.ts": "import * as b from '../../beta/b'",
"/beta/b.ts": "export { }",
}),
commandLineArgs: ["--p", "/alpha/tsconfig.json"],
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/alpha/tsconfig.json": getConfig(),
"/home/src/workspaces/project/alpha/src/a.ts": "import * as b from '../../beta/b'",
"/home/src/workspaces/project/beta/b.ts": "export { }",
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: ["--p", "alpha/tsconfig.json"],
});
});
+13 -12
View File
@@ -1,14 +1,15 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: redirect::", () => {
verifyTsc({
scenario: "redirect",
subScenario: "when redirecting ts file",
fs: () =>
loadProjectFromFiles({
"/src/project/tsconfig.json": jsonToReadableText({
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
outDir: "out",
},
@@ -17,21 +18,21 @@ describe("unittests:: tsc:: redirect::", () => {
"copy2/node_modules/target/*",
],
}),
"/src/project/copy1/node_modules/target/index.ts": "export const a = 1;",
"/src/project/copy1/node_modules/target/import.ts": `import {} from "./";`,
"/src/project/copy1/node_modules/target/package.json": jsonToReadableText({
"/home/src/workspaces/project/copy1/node_modules/target/index.ts": "export const a = 1;",
"/home/src/workspaces/project/copy1/node_modules/target/import.ts": `import {} from "./";`,
"/home/src/workspaces/project/copy1/node_modules/target/package.json": jsonToReadableText({
name: "target",
version: "1.0.0",
main: "index.js",
}),
"/src/project/copy2/node_modules/target/index.ts": "export const a = 1;",
"/src/project/copy2/node_modules/target/import.ts": `import {} from "./";`,
"/src/project/copy2/node_modules/target/package.json": jsonToReadableText({
"/home/src/workspaces/project/copy2/node_modules/target/index.ts": "export const a = 1;",
"/home/src/workspaces/project/copy2/node_modules/target/import.ts": `import {} from "./";`,
"/home/src/workspaces/project/copy2/node_modules/target/package.json": jsonToReadableText({
name: "target",
version: "1.0.0",
main: "index.js",
}),
}),
commandLineArgs: ["-p", "src/project"],
}, { currentDirectory: "/home/src/workspaces/project" }),
commandLineArgs: emptyArray,
});
});
+19 -9
View File
@@ -1,27 +1,37 @@
import { emptyArray } from "../../_namespaces/ts.js";
import { verifyTsc } from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc:: runWithoutArgs::", () => {
verifyTsc({
scenario: "runWithoutArgs",
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped",
fs: () => loadProjectFromFiles({}),
commandLineArgs: [],
environmentVariables: { TS_TEST_TERMINAL_WIDTH: "120" },
sys: () =>
TestServerHost.createWatchedSystem(emptyArray, {
currentDirectory: "/home/src/workspaces/project",
environmentVariables: new Map([["TS_TEST_TERMINAL_WIDTH", "120"]]),
}),
commandLineArgs: emptyArray,
});
verifyTsc({
scenario: "runWithoutArgs",
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped when host can't provide terminal width",
fs: () => loadProjectFromFiles({}),
commandLineArgs: [],
sys: () =>
TestServerHost.createWatchedSystem(emptyArray, {
currentDirectory: "/home/src/workspaces/project",
}),
commandLineArgs: emptyArray,
});
verifyTsc({
scenario: "runWithoutArgs",
subScenario: "does not add color when NO_COLOR is set",
fs: () => loadProjectFromFiles({}),
commandLineArgs: [],
environmentVariables: { NO_COLOR: "true" },
sys: () =>
TestServerHost.createWatchedSystem(emptyArray, {
currentDirectory: "/home/src/workspaces/project",
environmentVariables: new Map([["NO_COLOR", "true"]]),
}),
commandLineArgs: emptyArray,
});
});
@@ -1,22 +1,21 @@
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { createBaseline } from "../helpers/baseline.js";
import {
createBaseline,
createWatchCompilerHostOfConfigFileForBaseline,
runWatchBaseline,
TscWatchCompileChange,
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc-watch:: console clearing", () => {
describe("unittests:: tscWatch:: consoleClearing::", () => {
const scenario = "consoleClearing";
const file: File = {
path: "/f.ts",
path: "/user/username/projects/myproject/f.ts",
content: "",
};
@@ -31,7 +30,7 @@ describe("unittests:: tsc-watch:: console clearing", () => {
scenario,
subScenario,
commandLineArgs: ["--w", file.path, ...commandLineOptions || ts.emptyArray],
sys: () => createWatchedSystem([file, libFile]),
sys: () => TestServerHost.createWatchedSystem([file], { currentDirectory: ts.getDirectoryPath(file.path) }),
edits: makeChangeToFile,
});
}
@@ -46,12 +45,11 @@ describe("unittests:: tsc-watch:: console clearing", () => {
preserveWatchOutput: true,
};
const configFile: File = {
path: "/tsconfig.json",
path: "/user/username/projects/myproject/tsconfig.json",
content: jsonToReadableText({ compilerOptions }),
};
const files = [file, configFile, libFile];
it("using createWatchOfConfigFile ", () => {
const baseline = createBaseline(createWatchedSystem(files));
const baseline = createBaseline(sys());
const watch = ts.createWatchProgram(createWatchCompilerHostOfConfigFileForBaseline({
system: baseline.sys,
cb: baseline.cb,
@@ -72,8 +70,15 @@ describe("unittests:: tsc-watch:: console clearing", () => {
scenario,
subScenario: "when preserveWatchOutput is true in config file/when createWatchProgram is invoked with configFileParseResult on WatchCompilerHostOfConfigFile",
commandLineArgs: ["--w", "-p", configFile.path],
sys: () => createWatchedSystem(files),
sys,
edits: makeChangeToFile,
});
function sys() {
return TestServerHost.createWatchedSystem(
[file, configFile],
{ currentDirectory: ts.getDirectoryPath(configFile.path) },
);
}
});
});
+87 -75
View File
@@ -5,68 +5,65 @@ import {
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
const scenario = "emit";
describe("unittests:: tsc-watch:: emit with outFile or out setting", () => {
describe("unittests:: tscWatch:: emit:: with outFile or out setting", () => {
function verifyOutAndOutFileSetting(subScenario: string, out?: string, outFile?: string) {
verifyTscWatch({
scenario,
subScenario: `emit with outFile or out setting/${subScenario}`,
commandLineArgs: ["--w", "-p", "/a/tsconfig.json"],
commandLineArgs: ["--w", "-p", "/home/src/projects/a/tsconfig.json"],
sys: () =>
createWatchedSystem({
"/a/a.ts": "let x = 1",
"/a/b.ts": "let y = 1",
"/a/tsconfig.json": jsonToReadableText({ compilerOptions: { out, outFile } }),
[libFile.path]: libFile.content,
}),
TestServerHost.createWatchedSystem({
"/home/src/projects/a/a.ts": "let x = 1",
"/home/src/projects/a/b.ts": "let y = 1",
"/home/src/projects/a/tsconfig.json": jsonToReadableText({ compilerOptions: { out, outFile } }),
}, { currentDirectory: "/home/src/projects/a" }),
edits: [
{
caption: "Make change in the file",
edit: sys => sys.writeFile("/a/a.ts", "let x = 11"),
edit: sys => sys.writeFile("/home/src/projects/a/a.ts", "let x = 11"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "Make change in the file again",
edit: sys => sys.writeFile("/a/a.ts", "let xy = 11"),
edit: sys => sys.writeFile("/home/src/projects/a/a.ts", "let xy = 11"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
});
}
verifyOutAndOutFileSetting("config does not have out or outFile");
verifyOutAndOutFileSetting("config has out", "/a/out.js");
verifyOutAndOutFileSetting("config has outFile", /*out*/ undefined, "/a/out.js");
verifyOutAndOutFileSetting("config has out", "/home/src/projects/a/out.js");
verifyOutAndOutFileSetting("config has outFile", /*out*/ undefined, "/home/src/projects/a/out.js");
function verifyFilesEmittedOnce(subScenario: string, useOutFile: boolean) {
verifyTscWatch({
scenario,
subScenario: `emit with outFile or out setting/${subScenario}`,
commandLineArgs: ["--w", "-p", "/a/b/project/tsconfig.json"],
commandLineArgs: ["--w"],
sys: () => {
const file1: File = {
path: "/a/b/output/AnotherDependency/file1.d.ts",
path: "/home/src/projects/a/b/output/AnotherDependency/file1.d.ts",
content: "declare namespace Common.SomeComponent.DynamicMenu { enum Z { Full = 0, Min = 1, Average = 2, } }",
};
const file2: File = {
path: "/a/b/dependencies/file2.d.ts",
path: "/home/src/projects/a/b/dependencies/file2.d.ts",
content: "declare namespace Dependencies.SomeComponent { export class SomeClass { version: string; } }",
};
const file3: File = {
path: "/a/b/project/src/main.ts",
path: "/home/src/projects/a/b/project/src/main.ts",
content: "namespace Main { export function fooBar() {} }",
};
const file4: File = {
path: "/a/b/project/src/main2.ts",
path: "/home/src/projects/a/b/project/src/main2.ts",
content: "namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }",
};
const configFile: File = {
path: "/a/b/project/tsconfig.json",
path: "/home/src/projects/a/b/project/tsconfig.json",
content: jsonToReadableText({
compilerOptions: useOutFile ?
{ outFile: "../output/common.js", target: "es5" } :
@@ -74,7 +71,10 @@ describe("unittests:: tsc-watch:: emit with outFile or out setting", () => {
files: [file1.path, file2.path, file3.path, file4.path],
}),
};
return createWatchedSystem([file1, file2, file3, file4, libFile, configFile]);
return TestServerHost.createWatchedSystem(
[file1, file2, file3, file4, configFile],
{ currentDirectory: "/home/src/projects/a/b/project" },
);
},
});
}
@@ -82,13 +82,13 @@ describe("unittests:: tsc-watch:: emit with outFile or out setting", () => {
verifyFilesEmittedOnce("without --outFile and multiple declaration files in the program", /*useOutFile*/ false);
});
describe("unittests:: tsc-watch:: emit for configured projects", () => {
const file1Consumer1Path = "/a/b/file1Consumer1.ts";
const file1Consumer2Path = "/a/b/file1Consumer2.ts";
const moduleFile1Path = "/a/b/moduleFile1.ts";
const moduleFile2Path = "/a/b/moduleFile2.ts";
const globalFilePath = "/a/b/globalFile3.ts";
const configFilePath = "/a/b/tsconfig.json";
describe("unittests:: tscWatch:: emit:: for configured projects", () => {
const file1Consumer1Path = "/home/src/projects/a/b/file1Consumer1.ts";
const file1Consumer2Path = "/home/src/projects/a/b/file1Consumer2.ts";
const moduleFile1Path = "/home/src/projects/a/b/moduleFile1.ts";
const moduleFile2Path = "/home/src/projects/a/b/moduleFile2.ts";
const globalFilePath = "/home/src/projects/a/b/globalFile3.ts";
const configFilePath = "/home/src/projects/a/b/tsconfig.json";
interface VerifyTscWatchEmit {
subScenario: string;
/** custom config file options */
@@ -109,7 +109,7 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
verifyTscWatch({
scenario,
subScenario: `emit for configured projects/${subScenario}`,
commandLineArgs: ["--w", "-p", configFilePath],
commandLineArgs: ["--w"],
sys: () => {
const moduleFile1: File = {
path: moduleFile1Path,
@@ -140,11 +140,12 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
content: jsonToReadableText(configObj || {}),
};
const additionalFiles = getAdditionalFileOrFolder?.() || ts.emptyArray;
const files = [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile, ...additionalFiles];
return createWatchedSystem(
const files = [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, ...additionalFiles];
return TestServerHost.createWatchedSystem(
firstReloadFileList ?
ts.map(firstReloadFileList, fileName => ts.find(files, file => file.path === fileName)!) :
files,
{ currentDirectory: ts.getDirectoryPath(configFilePath) },
);
},
edits: changes,
@@ -224,7 +225,7 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
{
caption: "change moduleFile1 shape and create file1Consumer3",
edit: sys => {
sys.writeFile("/a/b/file1Consumer3.ts", `import {Foo} from "./moduleFile1"; let y = Foo();`);
sys.writeFile("/home/src/projects/a/b/file1Consumer3.ts", `import {Foo} from "./moduleFile1"; let y = Foo();`);
modifyModuleFile1Shape(sys);
},
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
@@ -266,7 +267,7 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
verifyTscWatchEmit({
subScenario: "should always return the file itself if '--out' or '--outFile' is specified",
configObj: { compilerOptions: { module: "system", outFile: "/a/b/out.js" } },
configObj: { compilerOptions: { module: "system", outFile: "/home/src/projects/a/b/out.js" } },
changes: [
changeModuleFile1Shape,
],
@@ -275,7 +276,7 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
verifyTscWatchEmit({
subScenario: "should return cascaded affected file list",
getAdditionalFileOrFolder: () => [{
path: "/a/b/file1Consumer1Consumer1.ts",
path: "/home/src/projects/a/b/file1Consumer1Consumer1.ts",
content: `import {y} from "./file1Consumer1";`,
}],
changes: [
@@ -300,21 +301,21 @@ describe("unittests:: tsc-watch:: emit for configured projects", () => {
subScenario: "should work fine for files with circular references",
getAdditionalFileOrFolder: () => [
{
path: "/a/b/file1.ts",
path: "/home/src/projects/a/b/file1.ts",
content: `/// <reference path="./file2.ts" />
export var t1 = 10;`,
},
{
path: "/a/b/file2.ts",
path: "/home/src/projects/a/b/file2.ts",
content: `/// <reference path="./file1.ts" />
export var t2 = 10;`,
},
],
firstReloadFileList: [libFile.path, "/a/b/file1.ts", "/a/b/file2.ts", configFilePath],
firstReloadFileList: ["/home/src/projects/a/b/file1.ts", "/home/src/projects/a/b/file2.ts", configFilePath],
changes: [
{
caption: "change file1",
edit: sys => sys.appendFile("/a/b/file1.ts", "export var t3 = 10;"),
edit: sys => sys.appendFile("/home/src/projects/a/b/file1.ts", "export var t3 = 10;"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
@@ -323,11 +324,11 @@ export var t2 = 10;`,
verifyTscWatchEmit({
subScenario: "should detect removed code file",
getAdditionalFileOrFolder: () => [{
path: "/a/b/referenceFile1.ts",
path: "/home/src/projects/a/b/referenceFile1.ts",
content: `/// <reference path="./moduleFile1.ts" />
export var x = Foo();`,
}],
firstReloadFileList: [libFile.path, "/a/b/referenceFile1.ts", moduleFile1Path, configFilePath],
firstReloadFileList: ["/home/src/projects/a/b/referenceFile1.ts", moduleFile1Path, configFilePath],
changes: [
{
caption: "delete moduleFile1",
@@ -340,15 +341,15 @@ export var x = Foo();`,
verifyTscWatchEmit({
subScenario: "should detect non existing code file",
getAdditionalFileOrFolder: () => [{
path: "/a/b/referenceFile1.ts",
path: "/home/src/projects/a/b/referenceFile1.ts",
content: `/// <reference path="./moduleFile2.ts" />
export var x = Foo();`,
}],
firstReloadFileList: [libFile.path, "/a/b/referenceFile1.ts", configFilePath],
firstReloadFileList: ["/home/src/projects/a/b/referenceFile1.ts", configFilePath],
changes: [
{
caption: "edit refereceFile1",
edit: sys => sys.appendFile("/a/b/referenceFile1.ts", "export var yy = Foo();"),
edit: sys => sys.appendFile("/home/src/projects/a/b/referenceFile1.ts", "export var yy = Foo();"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
@@ -360,27 +361,24 @@ export var x = Foo();`,
});
});
describe("unittests:: tsc-watch:: emit file content", () => {
describe("unittests:: tscWatch:: emit:: file content", () => {
function verifyNewLine(subScenario: string, newLine: string) {
verifyTscWatch({
scenario,
subScenario: `emit file content/${subScenario}`,
commandLineArgs: ["--w", "/a/app.ts"],
commandLineArgs: ["--w", "/home/src/projects/a/app.ts"],
sys: () =>
createWatchedSystem(
[
{
path: "/a/app.ts",
content: ["var x = 1;", "var y = 2;"].join(newLine),
},
libFile,
],
{ newLine },
TestServerHost.createWatchedSystem(
[{
path: "/home/src/projects/a/app.ts",
content: ["var x = 1;", "var y = 2;"].join(newLine),
}],
{ newLine, currentDirectory: "/home/src/projects/a" },
),
edits: [
{
caption: "Append a line",
edit: sys => sys.appendFile("/a/app.ts", newLine + "var z = 3;"),
edit: sys => sys.appendFile("/home/src/projects/a/app.ts", newLine + "var z = 3;"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
@@ -392,38 +390,41 @@ describe("unittests:: tsc-watch:: emit file content", () => {
verifyTscWatch({
scenario,
subScenario: "emit file content/should emit specified file",
commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json"],
commandLineArgs: ["-w", "-p", "/home/src/projects/a/b/tsconfig.json"],
sys: () => {
const file1 = {
path: "/a/b/f1.ts",
path: "/home/src/projects/a/b/f1.ts",
content: `export function Foo() { return 10; }`,
};
const file2 = {
path: "/a/b/f2.ts",
path: "/home/src/projects/a/b/f2.ts",
content: `import {Foo} from "./f1"; export let y = Foo();`,
};
const file3 = {
path: "/a/b/f3.ts",
path: "/home/src/projects/a/b/f3.ts",
content: `import {y} from "./f2"; let x = y;`,
};
const configFile = {
path: "/a/b/tsconfig.json",
path: "/home/src/projects/a/b/tsconfig.json",
content: "{}",
};
return createWatchedSystem([file1, file2, file3, configFile, libFile]);
return TestServerHost.createWatchedSystem(
[file1, file2, file3, configFile],
{ currentDirectory: ts.getDirectoryPath(configFile.path) },
);
},
edits: [
{
caption: "Append content to f1",
edit: sys => sys.appendFile("/a/b/f1.ts", "export function foo2() { return 2; }"),
edit: sys => sys.appendFile("/home/src/projects/a/b/f1.ts", "export function foo2() { return 2; }"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "Again Append content to f1",
edit: sys => sys.appendFile("/a/b/f1.ts", "export function fooN() { return 2; }"),
edit: sys => sys.appendFile("/home/src/projects/a/b/f1.ts", "export function fooN() { return 2; }"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
@@ -447,7 +448,10 @@ describe("unittests:: tsc-watch:: emit file content", () => {
path: `${currentDirectory}/file3.ts`,
content: `import { E2 } from "./file2"; const v: E2 = E2.V;`,
};
return createWatchedSystem([file1, file2, file3, libFile]);
return TestServerHost.createWatchedSystem(
[file1, file2, file3],
{ currentDirectory },
);
},
edits: [
{
@@ -463,7 +467,7 @@ describe("unittests:: tsc-watch:: emit file content", () => {
subScenario: "emit file content/file is deleted and created as part of change",
commandLineArgs: ["-w"],
sys: () => {
const projectLocation = "/home/username/project";
const projectLocation = "/home/username/projects/project";
const file: File = {
path: `${projectLocation}/app/file.ts`,
content: "var a = 10;",
@@ -476,27 +480,32 @@ describe("unittests:: tsc-watch:: emit file content", () => {
],
}),
};
const files = [file, configFile, libFile];
return createWatchedSystem(files, { currentDirectory: projectLocation, useCaseSensitiveFileNames: true });
return TestServerHost.createWatchedSystem(
[file, configFile],
{
currentDirectory: projectLocation,
useCaseSensitiveFileNames: true,
},
);
},
edits: [
{
caption: "file is deleted and then created to modify content",
edit: sys => sys.appendFile("/home/username/project/app/file.ts", "\nvar b = 10;", { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
edit: sys => sys.appendFile("/home/username/projects/project/app/file.ts", "\nvar b = 10;", { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
});
});
describe("unittests:: tsc-watch:: emit with when module emit is specified as node", () => {
describe("unittests:: tscWatch:: emit:: with when module emit is specified as node", () => {
verifyTscWatch({
scenario,
subScenario: "when module emit is specified as node/when instead of filechanged recursive directory watcher is invoked",
commandLineArgs: ["--w", "--p", "/a/rootFolder/project/tsconfig.json"],
commandLineArgs: ["--w"],
sys: () => {
const configFile: File = {
path: "/a/rootFolder/project/tsconfig.json",
path: "/home/src/projects/a/rootFolder/project/tsconfig.json",
content: jsonToReadableText({
compilerOptions: {
module: "none",
@@ -509,21 +518,24 @@ describe("unittests:: tsc-watch:: emit with when module emit is specified as nod
}),
};
const file1: File = {
path: "/a/rootFolder/project/Scripts/TypeScript.ts",
path: "/home/src/projects/a/rootFolder/project/Scripts/TypeScript.ts",
content: "var z = 10;",
};
const file2: File = {
path: "/a/rootFolder/project/Scripts/Javascript.js",
path: "/home/src/projects/a/rootFolder/project/Scripts/Javascript.js",
content: "var zz = 10;",
};
return createWatchedSystem([configFile, file1, file2, libFile]);
return TestServerHost.createWatchedSystem(
[configFile, file1, file2],
{ currentDirectory: ts.getDirectoryPath(configFile.path) },
);
},
edits: [
{
caption: "Modify typescript file",
edit: sys =>
sys.modifyFile(
"/a/rootFolder/project/Scripts/TypeScript.ts",
"/home/src/projects/a/rootFolder/project/Scripts/TypeScript.ts",
"var zz30 = 100;",
{ invokeDirectoryWatcherInsteadOfFileChanged: true },
),
@@ -1,41 +1,34 @@
import { jsonToReadableText } from "../helpers.js";
import { FsContents } from "../helpers/contents.js";
import {
TscWatchCompileChange,
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
FileOrFolderOrSymLinkMap,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => {
describe("unittests:: tscWatch:: emitAndErrorUpdates:: Emit times and Error updates in builder after program changes", () => {
const config: File = {
path: `/user/username/projects/myproject/tsconfig.json`,
content: `{}`,
};
interface VerifyEmitAndErrorUpdates {
subScenario: string;
files: () => FsContents | readonly File[];
currentDirectory?: string;
files: () => FileOrFolderOrSymLinkMap | readonly File[];
changes: TscWatchCompileChange[];
}
function verifyEmitAndErrorUpdates({
subScenario,
files,
currentDirectory,
changes,
}: VerifyEmitAndErrorUpdates) {
verifyTscWatch({
scenario: "emitAndErrorUpdates",
subScenario: `default/${subScenario}`,
commandLineArgs: ["--w"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
@@ -44,11 +37,7 @@ describe("unittests:: tsc-watch:: Emit times and Error updates in builder after
scenario: "emitAndErrorUpdates",
subScenario: `defaultAndD/${subScenario}`,
commandLineArgs: ["--w", "--d"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
@@ -57,11 +46,7 @@ describe("unittests:: tsc-watch:: Emit times and Error updates in builder after
scenario: "emitAndErrorUpdates",
subScenario: `isolatedModules/${subScenario}`,
commandLineArgs: ["--w", "--isolatedModules"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
@@ -70,11 +55,7 @@ describe("unittests:: tsc-watch:: Emit times and Error updates in builder after
scenario: "emitAndErrorUpdates",
subScenario: `isolatedModulesAndD/${subScenario}`,
commandLineArgs: ["--w", "--isolatedModules", "--d"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
@@ -83,11 +64,7 @@ describe("unittests:: tsc-watch:: Emit times and Error updates in builder after
scenario: "emitAndErrorUpdates",
subScenario: `assumeChangesOnlyAffectDirectDependencies/${subScenario}`,
commandLineArgs: ["--w", "--assumeChangesOnlyAffectDirectDependencies"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
@@ -96,14 +73,17 @@ describe("unittests:: tsc-watch:: Emit times and Error updates in builder after
scenario: "emitAndErrorUpdates",
subScenario: `assumeChangesOnlyAffectDirectDependenciesAndD/${subScenario}`,
commandLineArgs: ["--w", "--assumeChangesOnlyAffectDirectDependencies", "--d"],
sys: () =>
createWatchedSystem(
files(),
{ currentDirectory: currentDirectory || "/user/username/projects/myproject" },
),
sys,
edits: changes,
baselineIncremental: true,
});
function sys() {
return TestServerHost.createWatchedSystem(
files(),
{ currentDirectory: "/user/username/projects/myproject" },
);
}
}
describe("deep import changes", () => {
@@ -118,7 +98,7 @@ console.log(b.c.d);`,
function verifyDeepImportChange(subScenario: string, bFile: File, cFile: File) {
verifyEmitAndErrorUpdates({
subScenario: `deepImportChanges/${subScenario}`,
files: () => [aFile, bFile, cFile, config, libFile],
files: () => [aFile, bFile, cFile, config],
changes: [
{
caption: "Rename property d to d2 of class C to initialize signatures",
@@ -226,7 +206,7 @@ getPoint().c.x;`,
};
verifyEmitAndErrorUpdates({
subScenario: "file not exporting a deep multilevel import that changes",
files: () => [aFile, bFile, cFile, dFile, eFile, config, libFile],
files: () => [aFile, bFile, cFile, dFile, eFile, config],
changes: [
{
caption: "Rename property x2 to x of interface Coords to initialize signatures",
@@ -297,7 +277,7 @@ export class Data {
function verifyTransitiveExports(subScenario: string, files: readonly File[]) {
verifyEmitAndErrorUpdates({
subScenario: `transitive exports/${subScenario}`,
files: () => [lib1ToolsInterface, lib1ToolsPublic, app, lib2Public, lib1Public, ...files, config, libFile],
files: () => [lib1ToolsInterface, lib1ToolsPublic, app, lib2Public, lib1Public, ...files, config],
changes: [
{
caption: "Rename property title to title2 of interface ITest to initialize signatures",
+13 -15
View File
@@ -1,12 +1,10 @@
import {
getConfigDirExtendsSys,
forConfigDirExtendsSysScenario,
getSymlinkedExtendsSys,
modifyFirstExtendedConfigOfConfigDirExtendsSys,
} from "../helpers/extends.js";
import { verifyTscWatch } from "../helpers/tscWatch.js";
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc-watch:: extends::", () => {
describe("unittests:: tscWatch:: extends::", () => {
verifyTscWatch({
scenario: "extends",
subScenario: "resolves the symlink path",
@@ -14,15 +12,15 @@ describe("unittests:: tsc-watch:: extends::", () => {
commandLineArgs: ["-w", "-p", "src", "--extendedDiagnostics"],
});
verifyTscWatch({
scenario: "extends",
subScenario: "configDir template",
sys: () => createWatchedSystem(getConfigDirExtendsSys(), { currentDirectory: "/home/src/projects/myproject" }),
commandLineArgs: ["-w", "--extendedDiagnostics", "--explainFiles"],
edits: [{
caption: "edit extended config file",
edit: modifyFirstExtendedConfigOfConfigDirExtendsSys,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}],
});
forConfigDirExtendsSysScenario(
/*forTsserver*/ false,
(subScenario, sys, edits) =>
verifyTscWatch({
scenario: "extends",
subScenario,
sys,
commandLineArgs: ["-w", "--extendedDiagnostics", "--explainFiles"],
edits: edits(),
}),
);
});
@@ -1,18 +1,17 @@
import { dedent } from "../../_namespaces/Utils.js";
import { jsonToReadableText } from "../helpers.js";
import { getFsContentsForMultipleErrorsForceConsistentCasingInFileNames } from "../helpers/forceConsistentCasingInFileNames.js";
import { getSysForMultipleErrorsForceConsistentCasingInFileNames } from "../helpers/forceConsistentCasingInFileNames.js";
import {
TscWatchCompileChange,
verifyTscWatch,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
SymLink,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc-watch:: forceConsistentCasingInFileNames::", () => {
describe("unittests:: tscWatch:: forceConsistentCasingInFileNames::", () => {
const loggerFile: File = {
path: `/user/username/projects/myproject/logger.ts`,
content: `export class logger { }`,
@@ -33,7 +32,11 @@ describe("unittests:: tsc-watch:: forceConsistentCasingInFileNames::", () => {
scenario: "forceConsistentCasingInFileNames",
subScenario,
commandLineArgs: ["--w", "--p", tsconfig.path],
sys: () => createWatchedSystem([loggerFile, anotherFile, tsconfig, libFile]),
sys: () =>
TestServerHost.createWatchedSystem(
[loggerFile, anotherFile, tsconfig],
{ currentDirectory: "/user/username/projects/myproject" },
),
edits: changes,
});
}
@@ -81,7 +84,10 @@ describe("unittests:: tsc-watch:: forceConsistentCasingInFileNames::", () => {
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { forceConsistentCasingInFileNames: true } }),
};
return createWatchedSystem([moduleA, moduleB, moduleC, libFile, tsconfig], { currentDirectory: "/user/username/projects/myproject" });
return TestServerHost.createWatchedSystem(
[moduleA, moduleB, moduleC, tsconfig],
{ currentDirectory: "/user/username/projects/myproject" },
);
},
edits: [
{
@@ -102,8 +108,7 @@ describe("unittests:: tsc-watch:: forceConsistentCasingInFileNames::", () => {
subScenario: "jsxImportSource option changed",
commandLineArgs: ["--w", "--p", ".", "--explainFiles"],
sys: () =>
createWatchedSystem([
libFile,
TestServerHost.createWatchedSystem([
{
path: `/user/username/projects/myproject/node_modules/react/Jsx-runtime/index.d.ts`,
content: `export namespace JSX {
@@ -141,36 +146,43 @@ export const Fragment: unique symbol;
verifyTscWatch({
scenario: "forceConsistentCasingInFileNames",
subScenario,
commandLineArgs: ["--w", "--p", `${windowsStyleRoot}/${projectRootRelative}`, "--explainFiles"],
commandLineArgs: ["--w", "--p", `${windowsStyleRoot}workspaces/solution/${projectRootRelative}`, "--explainFiles"],
sys: () => {
const moduleA: File = {
path: `${windowsStyleRoot}/${projectRootRelative}/a.ts`,
path: `${windowsStyleRoot}workspaces/solution/${projectRootRelative}/a.ts`,
content: `
export const a = 1;
export const b = 2;
`,
};
const moduleB: File = {
path: `${windowsStyleRoot}/${projectRootRelative}/b.ts`,
path: `${windowsStyleRoot}workspaces/solution/${projectRootRelative}/b.ts`,
content: `
import { a } from "${windowsStyleRoot.toLocaleUpperCase()}/${projectRootRelative}/a"
import { b } from "${windowsStyleRoot.toLocaleLowerCase()}/${projectRootRelative}/a"
import { a } from "${windowsStyleRoot.toLocaleUpperCase()}workspaces/solution/${projectRootRelative}/a"
import { b } from "${windowsStyleRoot.toLocaleLowerCase()}workspaces/solution/${projectRootRelative}/a"
a;b;
`,
};
const tsconfig: File = {
path: `${windowsStyleRoot}/${projectRootRelative}/tsconfig.json`,
path: `${windowsStyleRoot}workspaces/solution/${projectRootRelative}/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { forceConsistentCasingInFileNames: true } }),
};
return createWatchedSystem([moduleA, moduleB, libFile, tsconfig], { windowsStyleRoot, useCaseSensitiveFileNames: false });
return TestServerHost.createWatchedSystem(
[moduleA, moduleB, tsconfig],
{
windowsStyleRoot,
useCaseSensitiveFileNames: false,
currentDirectory: `${windowsStyleRoot}workspaces/solution`,
},
);
},
edits: [
{
caption: "Prepend a line to moduleA",
edit: sys =>
sys.prependFile(
`${windowsStyleRoot}/${projectRootRelative}/a.ts`,
`${windowsStyleRoot}workspaces/solution/${projectRootRelative}/a.ts`,
`// some comment
`,
),
@@ -213,7 +225,10 @@ a;b;
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { forceConsistentCasingInFileNames: true } }),
};
return createWatchedSystem([moduleA, symlinkA, moduleB, libFile, tsconfig], { currentDirectory: "/user/username/projects/myproject" });
return TestServerHost.createWatchedSystem(
[moduleA, symlinkA, moduleB, tsconfig],
{ currentDirectory: "/user/username/projects/myproject" },
);
},
edits: [
{
@@ -267,7 +282,10 @@ a;b;
// Use outFile because otherwise the real and linked files will have the same output path
content: jsonToReadableText({ compilerOptions: { forceConsistentCasingInFileNames: true, outFile: "out.js", module: "system" } }),
};
return createWatchedSystem([moduleA, symlinkA, moduleB, libFile, tsconfig], { currentDirectory: "/user/username/projects/myproject" });
return TestServerHost.createWatchedSystem(
[moduleA, symlinkA, moduleB, tsconfig],
{ currentDirectory: "/user/username/projects/myproject" },
);
},
edits: [
{
@@ -295,7 +313,7 @@ a;b;
subScenario: "with nodeNext resolution",
commandLineArgs: ["--w", "--explainFiles"],
sys: () =>
createWatchedSystem({
TestServerHost.createWatchedSystem({
"/Users/name/projects/web/src/bin.ts": `import { foo } from "yargs";`,
"/Users/name/projects/web/node_modules/@types/yargs/index.d.ts": "export function foo(): void;",
"/Users/name/projects/web/node_modules/@types/yargs/index.d.mts": "export function foo(): void;",
@@ -318,7 +336,6 @@ a;b;
traceResolution: true,
},
}),
[libFile.path]: libFile.content,
}, { currentDirectory: "/Users/name/projects/web" }),
});
@@ -327,7 +344,7 @@ a;b;
subScenario: "self name package reference",
commandLineArgs: ["-w", "--explainFiles"],
sys: () =>
createWatchedSystem({
TestServerHost.createWatchedSystem({
"/Users/name/projects/web/package.json": jsonToReadableText({
name: "@this/package",
type: "module",
@@ -350,7 +367,6 @@ a;b;
traceResolution: true,
},
}),
"/a/lib/lib.esnext.full.d.ts": libFile.content,
}, { currentDirectory: "/Users/name/projects/web" }),
});
@@ -359,7 +375,7 @@ a;b;
subScenario: "package json is looked up for file",
commandLineArgs: ["-w", "--explainFiles"],
sys: () =>
createWatchedSystem({
TestServerHost.createWatchedSystem({
"/Users/name/projects/lib-boilerplate/package.json": jsonToReadableText({
name: "lib-boilerplate",
version: "0.0.2",
@@ -380,7 +396,6 @@ a;b;
traceResolution: true,
},
}),
"/a/lib/lib.es2021.full.d.ts": libFile.content,
}, { currentDirectory: "/Users/name/projects/lib-boilerplate" }),
});
@@ -388,11 +403,7 @@ a;b;
scenario: "forceConsistentCasingInFileNames",
subScenario: "when file is included from multiple places with different casing",
commandLineArgs: ["-w", "--explainFiles"],
sys: () =>
createWatchedSystem(
getFsContentsForMultipleErrorsForceConsistentCasingInFileNames(),
{ currentDirectory: "/home/src/projects/project" },
),
sys: getSysForMultipleErrorsForceConsistentCasingInFileNames,
edits: [
{
caption: "change to reuse imports",
@@ -1,22 +1,22 @@
import * as Harness from "../../_namespaces/Harness.js";
import { Baseline } from "../../_namespaces/Harness.js";
import * as ts from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import { CommandLineProgram } from "../helpers/baseline.js";
import { libContent } from "../helpers/contents.js";
import {
applyEdit,
CommandLineProgram,
createBaseline,
} from "../helpers/baseline.js";
import {
verifyTscWatch,
watchBaseline,
} from "../helpers/tscWatch.js";
import {
createWatchedSystem,
File,
libFile,
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch.js";
describe("unittests:: tsc-watch:: emit file --incremental", () => {
describe("unittests:: tscWatch:: incremental:: emit file --incremental", () => {
const project = "/users/username/projects/project";
const configFile: File = {
@@ -45,7 +45,10 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
{ subScenario, files, optionsToExtend, modifyFs }: VerifyIncrementalWatchEmitInput,
incremental: boolean,
) {
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem(files(), { currentDirectory: project }));
const { sys, baseline, cb, getPrograms } = createBaseline(TestServerHost.createWatchedSystem(
files(),
{ currentDirectory: project },
));
if (incremental) sys.exit = exitCode => sys.exitCode = exitCode;
const argsToPass = [incremental ? "-i" : "-w", ...(optionsToExtend || ts.emptyArray)];
baseline.push(`${sys.getExecutingFilePath()} ${argsToPass.join(" ")}`);
@@ -57,7 +60,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
build();
}
Harness.Baseline.runBaseline(`${ts.isBuild(argsToPass) ? "tsbuild/watchMode" : "tscWatch"}/incremental/${subScenario.split(" ").join("-")}-${incremental ? "incremental" : "watch"}.js`, baseline.join("\r\n"));
Baseline.runBaseline(`${ts.isBuild(argsToPass) ? "tsbuild/watchMode" : "tscWatch"}/incremental/${subScenario.split(" ").join("-")}-${incremental ? "incremental" : "watch"}.js`, baseline.join("\r\n"));
function build() {
const closer = ts.executeCommandLine(
@@ -88,7 +91,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
function verify(subScenario: string, optionsToExtend?: readonly string[]) {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
files: () => [libFile, file1, file2, configFile],
files: () => [file1, file2, configFile],
optionsToExtend,
subScenario: `own file emit without errors/${subScenario}`,
modifyFs: host => host.writeFile(file2.path, modifiedFile2Content),
@@ -99,7 +102,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
});
verifyIncrementalWatchEmit({
files: () => [libFile, file1, configFile, {
files: () => [file1, configFile, {
path: file2.path,
content: `const y: string = 20;`,
}],
@@ -108,7 +111,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
});
verifyIncrementalWatchEmit({
files: () => [libFile, file1, file2, {
files: () => [file1, file2, {
path: configFile.path,
content: jsonToReadableText({ compilerOptions: { incremental: true, outFile: "out.js" } }),
}],
@@ -131,7 +134,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
};
verifyIncrementalWatchEmit({
files: () => [libFile, file1, file2, config],
files: () => [file1, file2, config],
subScenario: "module compilation/own file emit without errors",
modifyFs: host => host.writeFile(file2.path, file2.content.replace("y", "z").replace("20", "10")),
});
@@ -143,13 +146,16 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
};
verifyIncrementalWatchEmit({
files: () => [libFile, file1, fileModified, config],
files: () => [file1, fileModified, config],
subScenario: "module compilation/own file emit with errors",
modifyFs: host => host.writeFile(file1.path, file1.content.replace("x = 10", "z = 10")),
});
it("verify that state is read correctly", () => {
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
const system = TestServerHost.createWatchedSystem(
[file1, fileModified, config],
{ currentDirectory: project },
);
const reportDiagnostic = ts.createDiagnosticReporter(system);
const parsedConfig = ts.parseConfigFileWithSystem("tsconfig.json", {}, /*extendedConfigCache*/ undefined, /*watchOptionsToExtend*/ undefined, system, reportDiagnostic)!;
ts.performIncrementalCompilation({
@@ -173,7 +179,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
assert.equal(builderProgram.state.changedFilesSet!.size, 0, "changes");
assert.equal(builderProgram.state.fileInfos.size, 3, "FileInfo size");
assert.deepEqual(builderProgram.state.fileInfos.get(libFile.path as ts.Path), {
assert.deepEqual(builderProgram.state.fileInfos.get(ts.toFileNameLowerCase(libFile.path) as ts.Path), {
version: system.createHash(libFile.content),
signature: system.createHash(libFile.content),
affectsGlobalScope: true,
@@ -201,7 +207,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
assert.equal(ts.arrayFrom(builderProgram.state.referencedMap!.keys()).length, 0);
assert.equal(builderProgram.state.semanticDiagnosticsPerFile.size, 3);
assert.deepEqual(builderProgram.state.semanticDiagnosticsPerFile.get(libFile.path as ts.Path), ts.emptyArray);
assert.deepEqual(builderProgram.state.semanticDiagnosticsPerFile.get(ts.toFileNameLowerCase(libFile.path) as ts.Path), ts.emptyArray);
assert.deepEqual(builderProgram.state.semanticDiagnosticsPerFile.get(file1.path as ts.Path), ts.emptyArray);
assert.deepEqual(builderProgram.state.semanticDiagnosticsPerFile.get(file2.path as ts.Path), [{
file: builderProgram.state.program!.getSourceFileByPath(file2.path as ts.Path)!,
@@ -220,7 +226,7 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
});
verifyIncrementalWatchEmit({
files: () => [libFile, file1, file2, {
files: () => [file1, file2, {
path: configFile.path,
content: jsonToReadableText({ compilerOptions: { incremental: true, module: "amd", outFile: "out.js" } }),
}],
@@ -273,7 +279,7 @@ export { B } from "./b";
export { C } from "./c";
`,
};
return [libFile, aTs, bTs, cTs, indexTs, config];
return [aTs, bTs, cTs, indexTs, config];
},
subScenario: "incremental with circular references",
modifyFs: host =>
@@ -291,7 +297,6 @@ export interface A {
verifyIncrementalWatchEmit({
subScenario: "when file with ambient global declaration file is deleted",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/globals.d.ts`, content: `declare namespace Config { const value: string;} ` },
{ path: `${project}/index.ts`, content: `console.log(Config.value);` },
{ path: configFile.path, content: jsonToReadableText({ compilerOptions: { incremental: true } }) },
@@ -317,7 +322,6 @@ export const Fragment: unique symbol;
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource option changed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: jsonToReadableText({ name: "react", version: "0.0.1" }) },
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
@@ -332,7 +336,6 @@ export const Fragment: unique symbol;
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types added",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: jsonToReadableText({ compilerOptions: jsxImportSourceOptions }) },
],
@@ -348,7 +351,6 @@ export const Fragment: unique symbol;
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: jsonToReadableText({ name: "react", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
@@ -363,7 +365,6 @@ export const Fragment: unique symbol;
verifyIncrementalWatchEmit({
subScenario: "importHelpers backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/tslib/index.d.ts`, content: "export function __assign(...args: any[]): any;" },
{ path: `${project}/node_modules/tslib/package.json`, content: jsonToReadableText({ name: "tslib", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const x = {...{}};` },
@@ -380,7 +381,6 @@ export const Fragment: unique symbol;
verifyIncrementalWatchEmit({
subScenario: "editing module augmentation",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/classnames/index.d.ts`, content: `export interface Result {} export default function classNames(): Result;` },
{ path: `${project}/src/types/classnames.d.ts`, content: `export {}; declare module "classnames" { interface Result { foo } }` },
{ path: `${project}/src/index.ts`, content: `import classNames from "classnames"; classNames().foo;` },
@@ -397,12 +397,11 @@ export const Fragment: unique symbol;
scenario: "incremental",
subScenario: "tsbuildinfo has error",
sys: () =>
createWatchedSystem({
"/src/project/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": "{}",
"/src/project/tsconfig.tsbuildinfo": "Some random string",
[libFile.path]: libFile.content,
}),
commandLineArgs: ["--p", "src/project", "-i", "-w"],
TestServerHost.createWatchedSystem({
"/home/src/projects/project/main.ts": "export const x = 10;",
"/home/src/projects/project/tsconfig.json": "{}",
"/home/src/projects/project/tsconfig.tsbuildinfo": "Some random string",
}, { currentDirectory: "/home/src/projects/project" }),
commandLineArgs: ["-i", "-w"],
});
});

Some files were not shown because too many files have changed in this diff Show More