Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13.7k
Validate JSON imports into ESM in --module nodenext#60019
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10517,9 +10517,10 @@ export function getDeclarationFileExtension(fileName: string): string | undefine | ||
| return standardExtension; | ||
| } | ||
| if (fileExtensionIs(fileName, Extension.Ts)) { | ||
| const index = getBaseFileName(fileName).lastIndexOf(".d."); | ||
| const baseName = getBaseFileName(fileName); | ||
| const index = baseName.lastIndexOf(".d."); | ||
| if (index >= 0) { | ||
| return fileName.substring(index); | ||
| return baseName.substring(index); | ||
MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was extremely bad; I guess we were only really using this to determine whether something was a declaration file name, not to actually use the extension. | ||
| } | ||
| } | ||
| return undefined; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| /loosey.cts(1,36): error TS2856: Import attributes are not allowed on statements that compile to CommonJS 'require' calls. | ||
| /loosey.cts(6,9): error TS2339: Property 'default' does not exist on type '{ version: number; }'. | ||
| /main.mts(2,22): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| /main.mts(3,19): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| /main.mts(7,21): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| /main.mts(8,10): error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'. | ||
| /main.mts(10,9): error TS2339: Property 'version' does not exist on type '{ default: { version: number; }; }'. | ||
| ==== /node_modules/not.json/package.json (0 errors) ==== | ||
| { | ||
| "name": "not.json", | ||
| "version": "1.0.0", | ||
| "type": "module", | ||
| "exports": "./index.js" | ||
| } | ||
| ==== /node_modules/not.json/index.d.ts (0 errors) ==== | ||
| export function oops(json: string): any; | ||
| ==== /node_modules/actually-json/package.json (0 errors) ==== | ||
| { | ||
| "name": "actually-json", | ||
| "version": "1.0.0", | ||
| "type": "module", | ||
| "exports": { | ||
| ".": "./index.json", | ||
| "./typed": "./typed.d.json.ts" | ||
| } | ||
| } | ||
| ==== /node_modules/actually-json/index.json (0 errors) ==== | ||
| {} | ||
| ==== /node_modules/actually-json/typed.d.json.ts (0 errors) ==== | ||
| declare const _default: {}; | ||
| export default _default; | ||
| ==== /config.json (0 errors) ==== | ||
| { | ||
| "version": 1 | ||
| } | ||
| ==== /main.mts (5 errors) ==== | ||
| import { oops } from "not.json"; // Ok | ||
| import moreOops from "actually-json"; // Error | ||
| ~~~~~~~~~~~~~~~ | ||
| !!! error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| import typed from "actually-json/typed"; // Error | ||
| ~~~~~~~~~~~~~~~~~~~~~ | ||
| !!! error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| import config from "./config.json" with { type: "json" }; // Ok | ||
| import { default as config1 } from "./config.json" with { type: "json" }; // Ok | ||
| import config2 from "./config.json"; // Error, no attribute | ||
| ~~~~~~~~~~~~~~~ | ||
| !!! error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. | ||
| import { version } from "./config.json" with { type: "json" }; // Error, named import | ||
| ~~~~~~~ | ||
| !!! error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'. | ||
| import * as config3 from "./config.json" with { type: "json" }; | ||
| config3.version; // Error | ||
| ~~~~~~~ | ||
| !!! error TS2339: Property 'version' does not exist on type '{ default: { version: number; }; }'. | ||
| config3.default; // Ok | ||
| ==== /loosey.cts (2 errors) ==== | ||
| import config from "./config.json" with { type: "json" }; // Error | ||
| ~~~~~~~~~~~~~~~~~~~~~ | ||
| !!! error TS2856: Import attributes are not allowed on statements that compile to CommonJS 'require' calls. | ||
| import config2 from "./config.json"; // Ok | ||
| import { version } from "./config.json"; // Ok | ||
| import * as config3 from "./config.json"; | ||
| config3.version; // Ok | ||
| config3.default; // Error | ||
| ~~~~~~~ | ||
| !!! error TS2339: Property 'default' does not exist on type '{ version: number; }'. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| //// [tests/cases/conformance/node/nodeModulesJson.ts] //// | ||
| === /node_modules/not.json/index.d.ts === | ||
| export function oops(json: string): any; | ||
| >oops : Symbol(oops, Decl(index.d.ts, 0, 0)) | ||
| >json : Symbol(json, Decl(index.d.ts, 0, 21)) | ||
| === /node_modules/actually-json/index.json === | ||
| {} | ||
| === /node_modules/actually-json/typed.d.json.ts === | ||
| declare const _default: {}; | ||
| >_default : Symbol(_default, Decl(typed.d.json.ts, 0, 13)) | ||
| export default _default; | ||
| >_default : Symbol(_default, Decl(typed.d.json.ts, 0, 13)) | ||
| === /config.json === | ||
| { | ||
| "version": 1 | ||
| >"version" : Symbol("version", Decl(config.json, 0, 1)) | ||
| } | ||
| === /main.mts === | ||
| import { oops } from "not.json"; // Ok | ||
| >oops : Symbol(oops, Decl(main.mts, 0, 8)) | ||
| import moreOops from "actually-json"; // Error | ||
| >moreOops : Symbol(moreOops, Decl(main.mts, 1, 6)) | ||
| import typed from "actually-json/typed"; // Error | ||
| >typed : Symbol(typed, Decl(main.mts, 2, 6)) | ||
| import config from "./config.json" with { type: "json" }; // Ok | ||
| >config : Symbol(config, Decl(main.mts, 4, 6)) | ||
| import { default as config1 } from "./config.json" with { type: "json" }; // Ok | ||
| >default : Symbol(config, Decl(config.json, 0, 0)) | ||
| >config1 : Symbol(config1, Decl(main.mts, 5, 8)) | ||
| import config2 from "./config.json"; // Error, no attribute | ||
| >config2 : Symbol(config2, Decl(main.mts, 6, 6)) | ||
| import { version } from "./config.json" with { type: "json" }; // Error, named import | ||
| >version : Symbol(version, Decl(main.mts, 7, 8)) | ||
| import * as config3 from "./config.json" with { type: "json" }; | ||
| >config3 : Symbol(config3, Decl(main.mts, 8, 6)) | ||
| config3.version; // Error | ||
| >config3 : Symbol(config3, Decl(main.mts, 8, 6)) | ||
| config3.default; // Ok | ||
| >config3.default : Symbol("/config") | ||
| >config3 : Symbol(config3, Decl(main.mts, 8, 6)) | ||
| >default : Symbol("/config") | ||
| === /loosey.cts === | ||
| import config from "./config.json" with { type: "json" }; // Error | ||
| >config : Symbol(config, Decl(loosey.cts, 0, 6)) | ||
| import config2 from "./config.json"; // Ok | ||
| >config2 : Symbol(config2, Decl(loosey.cts, 1, 6)) | ||
| import { version } from "./config.json"; // Ok | ||
| >version : Symbol(version, Decl(loosey.cts, 2, 8)) | ||
| import * as config3 from "./config.json"; | ||
| >config3 : Symbol(config3, Decl(loosey.cts, 3, 6)) | ||
| config3.version; // Ok | ||
| >config3.version : Symbol(version, Decl(config.json, 0, 1)) | ||
| >config3 : Symbol(config3, Decl(loosey.cts, 3, 6)) | ||
| >version : Symbol(version, Decl(config.json, 0, 1)) | ||
| config3.default; // Error | ||
| >config3 : Symbol(config3, Decl(loosey.cts, 3, 6)) | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The text of the module specifier is not sufficient to know whether we're importing a JSON file; this produced both false positives and false negatives (see imports of
"actually-json"and"not.json"in the test)