diff --git a/tsc/internal/execute/incremental/affectedfileshandler.go b/tsc/internal/execute/incremental/affectedfileshandler.go index cc1b382a6ca19..73289380cc51d 100644 --- a/tsc/internal/execute/incremental/affectedfileshandler.go +++ b/tsc/internal/execute/incremental/affectedfileshandler.go @@ -98,7 +98,9 @@ func (h *affectedFilesHandler) updateShapeSignature(file *ast.SourceFile, useFil info, _ := h.program.snapshot.fileInfos.Load(file.Path()) prevSignature := info.signature - if !file.IsDeclarationFile && !useFileVersionAsSignature { + // JSON files have no declaration output from which to compute a shape + // signature, so use the file version to conservatively invalidate dependents. + if !file.IsDeclarationFile && !ast.IsJsonSourceFile(file) && !useFileVersionAsSignature { update.signature = h.computeDtsSignature(file) } // Default is to use file version as signature diff --git a/tsc/internal/execute/tsctests/tsc_test.go b/tsc/internal/execute/tsctests/tsc_test.go index 43e4949f3ba3c..24b664f8cc78d 100644 --- a/tsc/internal/execute/tsctests/tsc_test.go +++ b/tsc/internal/execute/tsctests/tsc_test.go @@ -2270,6 +2270,44 @@ func TestTscIncremental(t *testing.T) { }, commandLineArgs: []string{"--noEmit"}, }, + { + subScenario: "json module diagnostics are cleared after fixing the json file", + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "noEmit": true, + "incremental": true, + "resolveJsonModule": true, + "esModuleInterop": true + } + }`), + "/home/src/workspaces/project/data.json": `{ "title": "hello" }`, + "/home/src/workspaces/project/check.ts": stringtestutil.Dedent(` + import type data from "./data.json"; + + type Shape = { title: string }; + type Covers = T; + + export type Check = Covers; + `), + }, + edits: []*tscEdit{ + { + caption: "remove required property", + edit: func(sys *TestSys) { + sys.writeFileNoError("/home/src/workspaces/project/data.json", `{}`) + }, + }, + { + caption: "restore required property", + edit: func(sys *TestSys) { + sys.writeFileNoError("/home/src/workspaces/project/data.json", `{ "title": "fixed" }`) + }, + }, + }, + }, } for _, test := range testCases { diff --git a/tsc/testdata/baselines/reference/tsc/incremental/json-module-diagnostics-are-cleared-after-fixing-the-json-file.js b/tsc/testdata/baselines/reference/tsc/incremental/json-module-diagnostics-are-cleared-after-fixing-the-json-file.js new file mode 100644 index 0000000000000..b1e76dd2c0392 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/incremental/json-module-diagnostics-are-cleared-after-fixing-the-json-file.js @@ -0,0 +1,353 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/check.ts] *new* +import type data from "./data.json"; + +type Shape = { title: string }; +type Covers = T; + +export type Check = Covers; +//// [/home/src/workspaces/project/data.json] *new* +{ "title": "hello" } +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "noEmit": true, + "incremental": true, + "resolveJsonModule": true, + "esModuleInterop": true + } +} + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *new* +{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","./data.json","./check.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"c770d6bc113c67c24136745dc262fd43-{ \"title\": \"hello\" }"},"87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;"],"fileIdsList":[[2]],"options":{"strict":true,"esModuleInterop":true},"referencedMap":[[3,1]],"affectedFilesPendingEmit":[3]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *new* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./check.ts" + ], + "original": 3 + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./data.json", + "./check.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./data.json", + "version": "c770d6bc113c67c24136745dc262fd43-{ \"title\": \"hello\" }", + "signature": "c770d6bc113c67c24136745dc262fd43-{ \"title\": \"hello\" }", + "impliedNodeFormat": "None", + "original": { + "version": "c770d6bc113c67c24136745dc262fd43-{ \"title\": \"hello\" }" + } + }, + { + "fileName": "./check.ts", + "version": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "signature": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "impliedNodeFormat": "CommonJS" + } + ], + "fileIdsList": [ + [ + "./data.json" + ] + ], + "options": { + "strict": true, + "esModuleInterop": true + }, + "referencedMap": { + "./check.ts": [ + "./data.json" + ] + }, + "affectedFilesPendingEmit": [ + [ + "./check.ts", + "Js", + 3 + ] + ], + "size": 1270 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/tslibs/TS/Lib/lib.es2025.full.d.ts +*refresh* /home/src/workspaces/project/data.json +*refresh* /home/src/workspaces/project/check.ts +Signatures:: + + +Edit [0]:: remove required property +//// [/home/src/workspaces/project/data.json] *modified* +{} + +tsgo +ExitStatus:: DiagnosticsPresent_OutputsGenerated +Output:: +check.ts:6:28 - error TS2741: Property 'title' is missing in type '{}' but required in type 'Shape'. + +6 export type Check = Covers; +   ~~~~~~~~~~~ + + check.ts:3:16 - 'title' is declared here. + 3 type Shape = { title: string }; +    ~~~~~ + + +Found 1 error in check.ts:6 + +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* +{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","./data.json","./check.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"dc7048f8f8747f561349cde127705c16-{}"},{"version":"87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;","signature":"f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"strict":true,"esModuleInterop":true},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"pos":132,"end":143,"code":2741,"category":1,"messageKey":"Property_0_is_missing_in_type_1_but_required_in_type_2_2741","messageArgs":["title","{}","Shape"],"relatedInformation":[{"pos":53,"end":58,"code":2728,"category":3,"messageKey":"_0_is_declared_here_2728","messageArgs":["title"]}]}]]],"affectedFilesPendingEmit":[3]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./check.ts" + ], + "original": 3 + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./data.json", + "./check.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./data.json", + "version": "dc7048f8f8747f561349cde127705c16-{}", + "signature": "dc7048f8f8747f561349cde127705c16-{}", + "impliedNodeFormat": "None", + "original": { + "version": "dc7048f8f8747f561349cde127705c16-{}" + } + }, + { + "fileName": "./check.ts", + "version": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "signature": "f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n", + "impliedNodeFormat": "CommonJS", + "original": { + "version": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "signature": "f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n", + "impliedNodeFormat": 1 + } + } + ], + "fileIdsList": [ + [ + "./data.json" + ] + ], + "options": { + "strict": true, + "esModuleInterop": true + }, + "referencedMap": { + "./check.ts": [ + "./data.json" + ] + }, + "semanticDiagnosticsPerFile": [ + [ + "./check.ts", + [ + { + "pos": 132, + "end": 143, + "code": 2741, + "category": 1, + "messageKey": "Property_0_is_missing_in_type_1_but_required_in_type_2_2741", + "messageArgs": [ + "title", + "{}", + "Shape" + ], + "relatedInformation": [ + { + "pos": 53, + "end": 58, + "code": 2728, + "category": 3, + "messageKey": "_0_is_declared_here_2728", + "messageArgs": [ + "title" + ] + } + ] + } + ] + ] + ], + "affectedFilesPendingEmit": [ + [ + "./check.ts", + "Js", + 3 + ] + ], + "size": 1827 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/workspaces/project/data.json +*refresh* /home/src/workspaces/project/check.ts +Signatures:: +(used version) /home/src/workspaces/project/data.json +(computed .d.ts) /home/src/workspaces/project/check.ts + + +Edit [1]:: restore required property +//// [/home/src/workspaces/project/data.json] *modified* +{ "title": "fixed" } + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* +{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","./data.json","./check.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"f985880635a046d0ccc31aa189849b95-{ \"title\": \"fixed\" }"},{"version":"87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;","signature":"f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"strict":true,"esModuleInterop":true},"referencedMap":[[3,1]],"affectedFilesPendingEmit":[3]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./check.ts" + ], + "original": 3 + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./data.json", + "./check.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./data.json", + "version": "f985880635a046d0ccc31aa189849b95-{ \"title\": \"fixed\" }", + "signature": "f985880635a046d0ccc31aa189849b95-{ \"title\": \"fixed\" }", + "impliedNodeFormat": "None", + "original": { + "version": "f985880635a046d0ccc31aa189849b95-{ \"title\": \"fixed\" }" + } + }, + { + "fileName": "./check.ts", + "version": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "signature": "f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n", + "impliedNodeFormat": "CommonJS", + "original": { + "version": "87d7b1214f92d2704171976612b58fb3-import type data from \"./data.json\";\n\ntype Shape = { title: string };\ntype Covers = T;\n\nexport type Check = Covers;", + "signature": "f171e5fef0d977f93110bee26e539dc9-import type data from \"./data.json\";\ntype Shape = {\n title: string;\n};\ntype Covers = T;\nexport type Check = Covers;\nexport {};\n", + "impliedNodeFormat": 1 + } + } + ], + "fileIdsList": [ + [ + "./data.json" + ] + ], + "options": { + "strict": true, + "esModuleInterop": true + }, + "referencedMap": { + "./check.ts": [ + "./data.json" + ] + }, + "affectedFilesPendingEmit": [ + [ + "./check.ts", + "Js", + 3 + ] + ], + "size": 1521 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/workspaces/project/data.json +*refresh* /home/src/workspaces/project/check.ts +Signatures:: +(used version) /home/src/workspaces/project/data.json +(computed .d.ts) /home/src/workspaces/project/check.ts