Skip to content

Add noCheck API option #57934

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
79961d4
Add --noCheck flag, mirroring --noEmit, allowing emitting transpiling…
weswigham Mar 24, 2024
540ce28
Test .d.ts emit on noCheck (js is pretty hopelessly broken without di…
weswigham Mar 24, 2024
3f89f5c
Fix harness issues, js declaration emit issues with out of order chec…
weswigham Mar 24, 2024
bf82544
Add errors on using noCheck with noEmit and without emitDeclarationOn…
weswigham Mar 25, 2024
a3d4f73
Format
weswigham Mar 25, 2024
ce65c79
Merge branch 'main' into nocheck
weswigham Mar 27, 2024
736eac2
Update baselines with main
weswigham Mar 27, 2024
5c5e8e0
Remove command-line accesible noCheck option, API only until JS is done
weswigham Mar 27, 2024
b33afe7
Merge branch 'main' into nocheck
weswigham Mar 27, 2024
1a05d96
Remove unused baseline
weswigham Mar 27, 2024
051a1d1
Add private option description so if `build` is invoked via API with …
weswigham Mar 28, 2024
1231be1
Increase comment verbosity
weswigham Mar 28, 2024
3e29a42
Merge branch 'main' into nocheck
weswigham Apr 10, 2024
2bda238
Add -b test for noCheck, change strategy used to make flag api-only
weswigham Apr 11, 2024
7332613
Comment, capture current normal behavior of build/incremental (an opt…
weswigham Apr 11, 2024
41f82f1
Diffs
weswigham Apr 11, 2024
5c9c333
Format
weswigham Apr 11, 2024
72bd93c
Actually accept more useful diff names
weswigham Apr 11, 2024
e0d7b4e
Cleanup
weswigham Apr 11, 2024
8c667f4
Merge branch 'main' into nocheck
weswigham Apr 12, 2024
e0dfe56
Merge branch 'main' into nocheck
weswigham Apr 24, 2024
2f65540
Enable noCheck for transpileDeclaration
weswigham Apr 24, 2024
0f1b4c6
Remove builder change
weswigham Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8792,6 +8792,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function serializeSymbol(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean): void {
void getPropertiesOfType(getTypeOfSymbol(symbol)); // resolve symbol's type and properties, which should trigger any required merges
// cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but
// still skip reserializing it if we encounter the merged product later on
const visitedSym = getMergedSymbol(symbol);
Expand Down Expand Up @@ -48846,6 +48847,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!sym) {
return !node.locals ? [] : nodeBuilder.symbolTableToDeclarationStatements(node.locals, node, flags, tracker);
}
resolveExternalModuleSymbol(sym); // ensures cjs export assignment is setup
return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker);
},
isImportRequiredByAugmentation,
Expand Down
14 changes: 14 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,20 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
defaultValueDescription: false,
description: Diagnostics.Disable_emitting_comments,
},
{
name: "noCheck",
type: "boolean",
showInSimplifiedHelpView: false,
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Disable_full_type_checking_only_critical_parse_and_emit_errors_will_be_reported,
transpileOptionValue: undefined,
defaultValueDescription: false,
affectsSemanticDiagnostics: true,
affectsBuildInfo: true,
extraValidation() {
return [Diagnostics.Unknown_compiler_option_0, "noCheck"];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For anyone reading, this validation is what blocks this option from being passed on the CLI. This is a nicer way to do it than our usual method (just adding an entry to the CompilerOptions interface), since it still provides metadata for .buildinfo serialization and showConfig, plus makes it easier to test as though it were CLI-allowed, like is done in the noCheck unittest.

},
},
{
name: "noEmit",
type: "boolean",
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -6388,6 +6388,10 @@
"category": "Message",
"code": 6804
},
"Disable full type checking (only critical parse and emit errors will be reported).": {
"category": "Message",
"code": 6805
},

"one of:": {
"category": "Message",
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -848,8 +848,8 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson);
// Setup and perform the transformation to retrieve declarations from the input files
const inputListOrBundle = compilerOptions.outFile ? [factory.createBundle(filesForEmit)] : filesForEmit;
if (emitOnly && !getEmitDeclarations(compilerOptions)) {
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
if ((emitOnly && !getEmitDeclarations(compilerOptions)) || compilerOptions.noCheck) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need to add forceDtsEmit check here? shouldnt signature generation behave like noCheck?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK it already does - the || compilerOptions.noCheck being tacked on here is so that noCheck builds do the same data collection signature generation runs already do.

// Checker wont collect the linked aliases since thats only done when declaration is enabled and checking is performed.
// Do that here when emitting only dts files
filesForEmit.forEach(collectLinkedAliases);
}
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4424,6 +4424,15 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
}

if (options.noCheck) {
if (options.noEmit) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think noEmitOnError and noCheck should also be combo that does not make sense.

Copy link
Member Author

@weswigham weswigham Apr 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noEmitOnError blocks emit on the presence of options, syntactic, global, semantic, and declaration diagnostics. noCheck only blocks semantic from being generated, so noEmitOnError does still have meaning alongside noCheck, though it certainly will block emit less.

createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noCheck", "noEmit");
}
if (!options.emitDeclarationOnly) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "noCheck", "emitDeclarationOnly");
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now we should disable this for incremental as the build info generated will not handle this correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we need to make sure noCheck recalculates semantic diagnostics and stores the that flag into buildinfo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in I should add an error for using noCheck alongside incremental?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we need to make sure noCheck recalculates semantic diagnostics and stores the that flag into buildinfo

That's just setting affectsSemanticDiagnostics in the option descriptor when we add it back to being enabled for cli/tsconfig in the future, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"in option" yes but now it needs to be handled in builder or reported as error to have incremental and nocheck as buildinfo written will not be correct. It will show no errors and next time you run tsc it will not report errors . (without storing noCheck value in buildInfo and checking for that in builder)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 051a1d1 along the lines of what you were thinking? Since it's not in the commonOptionsWithBuild, commandOptionsWithoutBuild, or optionDeclarations lists, it's never included in help or valid on the CLI/in a config, but since it's built into the OptionsNameMap, all the normal option serializing/deserializing machinery works with it - build info included.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need test cases:

  1. Run through API with --no-check , followed by tsc .(with and without incremental).
  2. Run through API with --no-check , followed by tsc --b (with and without incremental) - have a feeling that tsc - in non incremental is going to find that everything is uptodate (there is no buildinfo to check, only output file stamps) and will not report errors?

You can add these testcases by using verifyTsc where the initial fs is generated through running API with --no-check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run through API with --no-check

We.... don't have the harness for this right now. At least not a harness that then feeds into the existing verifyTsc stuff (obviously, all the compiler-style tests build against the API without using the executeCommandLine entrypoint). This is quite a lot to put together for something that's only supposed to be API-only temporarily... I started putting it together, and such a harness is looking like it's going to be more than the entire rest of the PR. 😵

So I'm not going to. This is gonna be a normal command line option, accepted on both the CLI and via tsconfig, except with a

        extraValidation(value) {
            return [Diagnostics.Unknown_compiler_option_0, "noCheck"];
        },

so anything that runs validation (API methods that take CompilerOptions obviously do not) reports that it doesn't exist, but otherwise, it uses all the usual pipelines for things. And then in the test where I'd like to consider it a real option, I'll override that validator with a noop temporarily, which I can do for the tsc -b style tests, to validate it will work correctly with -b once it's actually public and usable via.... anything other than the API.


if (
options.emitDecoratorMetadata &&
!options.experimentalDecorators
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7270,6 +7270,7 @@ export interface CompilerOptions {
moduleDetection?: ModuleDetectionKind;
newLine?: NewLineKind;
noEmit?: boolean;
/** @internal */ noCheck?: boolean;
/** @internal */ noEmitForJsFiles?: boolean;
noEmitHelpers?: boolean;
noEmitOnError?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9967,6 +9967,7 @@ export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOption
// '/// <reference no-default-lib="true"/>' directive.
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
options.noCheck ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this here also skips grammar checks .. May be i am remembering it incorrectly but i thought we wanted to do grammar checks ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this here also skips grammar checks

Not... quite. More in a bit.

May be i am remembering it incorrectly but i thought we wanted to do grammar checks ?

I wanna say "no". We ruminated on it briefly, but "grammar checks" aren't a defined subset of the diagnostics we publicly recognize (and some things you'd think are in that category are emitted in the parser, binder, or checker passes over the AST - roll a d6 to find out where), so while are somewhat well-defined in ECMA-262, are incredibly poorly defined for us. Additionally, what diagnostics we skip vs emit isn't in any way what's important for this flag, what is is that we skip the checker's full tree walk (and all the work it does) for diagnostics and jump right to emit (which is what inclusion in this function does). Doing a walk specifically for grammar errors would run pretty counter to that goal, though I admit if you're in a checker context and already traversing an AST most grammar-style errors are usually trivial to emit. Again, not terribly important for this, though - we just want to skip the big walk that does all the expensive work. (Usually so it can be done asynchronously.)

host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
}

Expand Down
35 changes: 32 additions & 3 deletions src/harness/harnessIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export namespace Compiler {
{ name: "noTypesAndSymbols", type: "boolean", defaultValueDescription: false },
// Emitted js baseline will print full paths for every output file
{ name: "fullEmitPaths", type: "boolean", defaultValueDescription: false },
{ name: "noCheck", type: "boolean", defaultValueDescription: false },
{ name: "reportDiagnostics", type: "boolean", defaultValueDescription: false }, // used to enable error collection in `transpile` baselines
];

Expand Down Expand Up @@ -371,6 +372,8 @@ export namespace Compiler {
fileOptions?: any;
}

export type CompileFilesResult = compiler.CompilationResult & { repeat(newOptions: TestCaseParser.CompilerSettings): CompileFilesResult; };

export function compileFiles(
inputFiles: TestFile[],
otherFiles: TestFile[],
Expand All @@ -379,7 +382,8 @@ export namespace Compiler {
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
currentDirectory: string | undefined,
symlinks?: vfs.FileSet,
): compiler.CompilationResult {
): CompileFilesResult {
const originalCurrentDirectory = currentDirectory;
const options: ts.CompilerOptions & HarnessOptions = compilerOptions ? ts.cloneCompilerOptions(compilerOptions) : { noResolve: false };
options.newLine = options.newLine || ts.NewLineKind.CarriageReturnLineFeed;
options.noErrorTruncation = true;
Expand Down Expand Up @@ -428,7 +432,8 @@ export namespace Compiler {
const host = new fakes.CompilerHost(fs, options);
const result = compiler.compileFiles(host, programFileNames, options, typeScriptVersion);
result.symlinks = symlinks;
return result;
(result as CompileFilesResult).repeat = newOptions => compileFiles(inputFiles, otherFiles, { ...harnessSettings, ...newOptions }, compilerOptions, originalCurrentDirectory, symlinks);
return result as CompileFilesResult;
}

export interface DeclarationCompilationContext {
Expand Down Expand Up @@ -944,7 +949,7 @@ export namespace Compiler {
return "\n//// https://sokra.github.io/source-map-visualization" + hash + "\n";
}

export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: compiler.CompilationResult, tsConfigFiles: readonly TestFile[], toBeCompiled: readonly TestFile[], otherFiles: readonly TestFile[], harnessSettings: TestCaseParser.CompilerSettings) {
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompileFilesResult, tsConfigFiles: readonly TestFile[], toBeCompiled: readonly TestFile[], otherFiles: readonly TestFile[], harnessSettings: TestCaseParser.CompilerSettings) {
if (!options.noEmit && !options.emitDeclarationOnly && result.js.size === 0 && result.diagnostics.length === 0) {
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
}
Expand Down Expand Up @@ -996,9 +1001,33 @@ export namespace Compiler {
jsCode += "\r\n\r\n";
jsCode += getErrorBaseline(tsConfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.diagnostics);
}
else if (!options.noCheck && !options.noEmit && (options.composite || options.declaration || options.emitDeclarationOnly)) {
const withoutChecking = result.repeat({ noCheck: "true", emitDeclarationOnly: "true" });
compareResultFileSets(withoutChecking.dts, result.dts);
}

// eslint-disable-next-line no-restricted-syntax
Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ts.Extension.Js), jsCode.length > 0 ? tsCode + "\r\n\r\n" + jsCode : null);

function compareResultFileSets(a: ReadonlyMap<string, documents.TextDocument>, b: ReadonlyMap<string, documents.TextDocument>) {
a.forEach((doc, key) => {
const original = b.get(key);
if (!original) {
jsCode += `\r\n\r\n!!!! File ${Utils.removeTestPathPrefixes(doc.file)} missing from original emit, but present in noCheck emit\r\n`;
jsCode += fileOutput(doc, harnessSettings);
}
else if (original.text !== doc.text) {
jsCode += `\r\n\r\n!!!! File ${Utils.removeTestPathPrefixes(doc.file)} differs from original emit in noCheck emit\r\n`;
const Diff = require("diff");
const expected = original.text;
const actual = doc.text;
const patch = Diff.createTwoFilesPatch("Expected", "Actual", expected, actual, "The full check baseline", "with noCheck set");
const fileName = harnessSettings.fullEmitPaths ? Utils.removeTestPathPrefixes(doc.file) : ts.getBaseFileName(doc.file);
jsCode += "//// [" + fileName + "]\r\n";
jsCode += patch;
}
});
}
}

function fileOutput(file: documents.TextDocument, harnessSettings: TestCaseParser.CompilerSettings): string {
Expand Down
2 changes: 2 additions & 0 deletions src/services/transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function transpileModule(input: string, transpileOptions: TranspileOption
* - noResolve = true
* - declaration = true
* - emitDeclarationOnly = true
* - noCheck = true
* Note that this declaration file may differ from one produced by a full program typecheck,
* in that only types in the single input file are available to be used in the generated declarations.
*/
Expand Down Expand Up @@ -141,6 +142,7 @@ function transpileWorker(input: string, transpileOptions: TranspileOptions, decl
options.declaration = true;
options.emitDeclarationOnly = true;
options.isolatedDeclarations = true;
options.noCheck = true;
}
else {
options.declaration = false;
Expand Down
3 changes: 1 addition & 2 deletions src/testRunner/compilerRunner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as compiler from "./_namespaces/compiler";
import {
Baseline,
Compiler,
Expand Down Expand Up @@ -171,7 +170,7 @@ class CompilerTest {
private configuredName: string;
private harnessSettings: TestCaseParser.CompilerSettings;
private hasNonDtsFiles: boolean;
private result: compiler.CompilationResult;
private result: Compiler.CompileFilesResult;
private options: ts.CompilerOptions;
private tsConfigFiles: Compiler.TestFile[];
// equivalent to the files that will be passed on the command line
Expand Down
1 change: 1 addition & 0 deletions src/testRunner/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import "./unittests/tsbuild/lateBoundSymbol";
import "./unittests/tsbuild/libraryResolution";
import "./unittests/tsbuild/moduleResolution";
import "./unittests/tsbuild/moduleSpecifiers";
import "./unittests/tsbuild/noCheck";
import "./unittests/tsbuild/noEmit";
import "./unittests/tsbuild/noEmitOnError";
import "./unittests/tsbuild/outFile";
Expand Down
86 changes: 86 additions & 0 deletions src/testRunner/unittests/tsbuild/noCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
CommandLineOption,
optionDeclarations,
} from "../../_namespaces/ts";
import { jsonToReadableText } from "../helpers";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc";
import { loadProjectFromFiles } from "../helpers/vfs";

function verifyNoCheckFlag(variant: string) {
function verifyNoCheckWorker(subScenario: string, declAText: string, commandLineArgs: readonly string[]) {
verifyTsc({
scenario: variant,
subScenario,
fs: () =>
loadProjectFromFiles({
"/src/a.ts": getATsContent(declAText),
"/src/tsconfig.json": jsonToReadableText({
compilerOptions: { noCheck: true, emitDeclarationOnly: true, declaration: true },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this in json and not reporting error is a bug because then it doesnt become API only option per design meeting.,

The scenario i am talking is missed here because:
modifying tsconfig.json will check that config file is newer than output and hence program will be rebuilt. The problem with doing this with API is that output will be updated but not inputs which means the build will report that everything is uptodate

Here is how to write this test case
1 load project files (without noCheck option) in your test case as is . No errors
2. As part of first edit: Do these steps to emulate API usage: Change a.ts to introduce error, run API (by creating program and emitting with noCheck on, use sys= new vfs.sys(fs)) to create compiler host
3. This will fail because after edit, test framework will run tsc --build again and it will find that everything is UpToDate and not report error. (test will not create discrepancy baseline because unfortunately we don't check discrepancies in error reporting just file emit validation)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this in json and not reporting error is a bug because then it doesnt become API only option per design meeting.,

And it does normally report an error, which is what the other test block that doesn't override the validator function verifies (and naturally, these collapse into one test once it's actually exposed via CLI)~

The problem with doing this with API is that output will be updated but not inputs which means the build will report that everything is uptodate

If you build via API and pass options not in your on-disk config file (as you would need to, since noCheck is API only right now), your up-to-dateness checks for configs are going to all be invalidated anyway, since your API-fueled build will have different options applied than a commandline one. Precisely

The problem with doing this with API is that output will be updated but not inputs which means the build will report that everything is uptodate

is in no way unique to this new flag - any time you do a API build with different flags than what your on-disk configs specify this'll happen, and I'm pretty sure how -b handles that is just entirely out of scope, unless we're talking specifically incremental output, which just comes down to the options serialized in the .buildinfo which is why:

This will fail because after edit, test framework will run tsc --build again and it will find that everything is UpToDate and not report error. (test will not create discrepancy baseline because unfortunately we don't check discrepancies in error reporting just file emit validation)

It both will and won't. If --incremental isn't on, yeah, it won't build again because all the timestamps say it's newer, and build doesn't know that your last build was with different settings. Again, that's pretty out-of-scope and isn't going to get fixed, since there's no incremental artifact. Nothing new there, that issue has nothing to do with noCheck - you can replicate that with any semantic diagnostic affecting option. For --incremental, that won't be the case, because the .buildinfo emitted by the API build will have different compiler options than the new build (semantic diagnostic affecting ones), which'll force a recheck (though not reemit), and is precisely what this test verifies without needing all the API-layering hoopla by temporarily enabling it in configs/cli (since an incremental build via config is going to produce the same buildinfo as one via api). AFAIK, the only critical thing to test is that noCheck is serialized into the buildinfo just like every other semantic diagnostic-affecting option, when it's allowed, which this test verifies.

As an aside "via API" is ambiguous, since we expose executeCommandLine now, but I've taken that to mean "only allowed where we take CompilerOptions-type objects" for now, which just means any API that's post-option-parsing/validating. Absolutely none of those APIs (eg, createProgram) are even going to emit a .buildinfo file on their own without extra instrumentation on behalf of the caller of the API, so it is quite literally up to the API user to keep things up-to-date, provided our emitBuildInfo function does serialize the option for them when they call it to do that, which, again, this test does verify.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with writing this test case this way is that it is modifying timestamp of tsconfig files and hence build will take place. To check this correctly, you want to use API emit as edit as mentioned above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I could allow --noCheck as a build option (why not? seems like a reasonable thing to want to turn on/off for the whole project graph) and pass it down from the top-level CLI invocation, rather than by editing the tsconfig?

}),
}),
commandLineArgs,
edits: [
noChangeRun,
{
caption: "Fix `a` error",
edit: fs => fs.writeFileSync("/src/a.ts", getATsContent(`const a = "hello"`)),
},
noChangeRun,
{
caption: "Disable noCheck",
edit: fs =>
fs.writeFileSync(
"/src/tsconfig.json",
jsonToReadableText({
compilerOptions: { emitDeclarationOnly: true, declaration: true },
}),
),
},
noChangeRun,
],
baselinePrograms: true,
});

function getATsContent(declAText: string) {
return `const err: number = "error";
${declAText}`;
}
}

function verifyNoCheck(subScenario: string, aTsContent: string) {
verifyNoCheckWorker(subScenario, aTsContent, ["--b", "/src/tsconfig.json", "-v"]);
verifyNoCheckWorker(`${subScenario} with incremental`, aTsContent, ["--b", "/src/tsconfig.json", "-v", "--incremental"]);
}

verifyNoCheck("syntax errors", `const a = "hello`);
verifyNoCheck("semantic errors", `const a: number = "hello"`);
}

describe("unittests:: tsbuild:: noCheck", () => {
// Enable the `noCheck` option on the CLI for testing purposes, to ensure it works with incremental/build
let validate: CommandLineOption["extraValidation"];
before(() => {
for (const opt of optionDeclarations) {
if (opt.name === "noCheck") {
validate = opt.extraValidation;
opt.extraValidation = () => undefined;
}
}
});
after(() => {
for (const opt of optionDeclarations) {
if (opt.name === "noCheck") {
opt.extraValidation = validate;
}
}
});

verifyNoCheckFlag("noCheck");
});

describe("unittests:: tsbuild:: noCheck:: errors", () => {
verifyNoCheckFlag("noCheck-errors");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"compilerOptions": {}
}
17 changes: 17 additions & 0 deletions tests/baselines/reference/deferredLookupTypeResolution.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,20 @@ declare function f3(x: 'a' | 'b'): {
b: any;
x: any;
};


!!!! File deferredLookupTypeResolution.d.ts differs from original emit in noCheck emit
//// [deferredLookupTypeResolution.d.ts]
===================================================================
--- Expected The full check baseline
+++ Actual with noCheck set
@@ -15,8 +15,8 @@
[P in A | B]: any;
};
declare function f2<A extends string>(a: A): { [P in A | "x"]: any; };
declare function f3(x: 'a' | 'b'): {
+ x: any;
a: any;
b: any;
- x: any;
};
18 changes: 18 additions & 0 deletions tests/baselines/reference/indexSignatures1.js
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,21 @@ type Rec1 = {
type Rec2 = Record<Id, number>;
type K1 = keyof Rec1;
type K2 = keyof Rec2;


!!!! File indexSignatures1.d.ts differs from original emit in noCheck emit
//// [indexSignatures1.d.ts]
===================================================================
--- Expected The full check baseline
+++ Actual with noCheck set
@@ -118,9 +118,9 @@
[x: symbol]: 4 | 5;
[sym]: 4;
};
declare const obj13: {
- [x: string]: 0 | 2 | 1 | 3;
+ [x: string]: 0 | 1 | 2 | 3;
[x: number]: 2 | 3;
[x: symbol]: 4 | 5;
x: 0;
1: 2;
6 changes: 6 additions & 0 deletions tests/baselines/reference/jsDeclarationsCrossfileMerge.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ module.exports.memberName = "thing";
declare const _exports: typeof m.default;
export = _exports;
import m = require("./exporter");


!!!! File out/exporter.d.ts missing from original emit, but present in noCheck emit
//// [exporter.d.ts]
export default validate;
declare function validate(): void;
10 changes: 10 additions & 0 deletions tests/baselines/reference/noCheckDoesNotReportError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////

//// [noCheckDoesNotReportError.ts]
export const a: number = "not ok";




//// [noCheckDoesNotReportError.d.ts]
export declare const a: number;
6 changes: 6 additions & 0 deletions tests/baselines/reference/noCheckDoesNotReportError.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////

=== noCheckDoesNotReportError.ts ===
export const a: number = "not ok";
>a : Symbol(a, Decl(noCheckDoesNotReportError.ts, 0, 12))

9 changes: 9 additions & 0 deletions tests/baselines/reference/noCheckDoesNotReportError.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////

=== noCheckDoesNotReportError.ts ===
export const a: number = "not ok";
>a : number
> : ^^^^^^
>"not ok" : "not ok"
> : ^^^^^^^^

9 changes: 9 additions & 0 deletions tests/baselines/reference/noCheckNoEmit.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error TS5053: Option 'emitDeclarationOnly' cannot be specified with option 'noEmit'.
error TS5053: Option 'noCheck' cannot be specified with option 'noEmit'.


!!! error TS5053: Option 'emitDeclarationOnly' cannot be specified with option 'noEmit'.
!!! error TS5053: Option 'noCheck' cannot be specified with option 'noEmit'.
==== noCheckNoEmit.ts (0 errors) ====
export const a: number = "not ok";

6 changes: 6 additions & 0 deletions tests/baselines/reference/noCheckNoEmit.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//// [tests/cases/compiler/noCheckNoEmit.ts] ////

=== noCheckNoEmit.ts ===
export const a: number = "not ok";
>a : Symbol(a, Decl(noCheckNoEmit.ts, 0, 12))

Loading