Skip to content
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

[5.5 beta] package.json "type" lookup in non-Node.js module is unexpected #58663

Closed
nicolo-ribaudo opened this issue May 26, 2024 · 11 comments
Closed
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@nicolo-ribaudo
Copy link

πŸ”Ž Search Terms

moduleDetection force legacy

πŸ•— Version & Regression Information

  • This changed in version 5.5-beta

⏯ Playground Link

No response

πŸ’» Code

package.json:

{
  "name": "ts-test",
  "version": "1.0.0",
  "description": "",
  "type": "commonjs",
  "devDependencies": {
    "typescript": "^5.5.0-beta"
  }
}

tsconfig.json:

{
  "include": ["src/index.ts"],
  "compilerOptions": {
    "module": "Preserve",
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "emitDeclarationOnly": true,
    "declaration": true,
    "declarationDir": "./dts"
  }
}

src/index.ts:

export let a = 1;

πŸ™ Actual behavior

src/index.ts:1:1 - error TS1287: A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled.

1 export let a = 1;

πŸ™‚ Expected behavior

Given that I'm using "moduleDetection": "force", that file is an ES module (regardless of what package.json#type says) and thus I shouldn't see that error. This was the behavior in 5.4.

Additional information about the issue

No response

@nicolo-ribaudo nicolo-ribaudo changed the title [5.5 beta] The moduleDetection option is ignored [5.5 beta] The moduleDetection option is ignored for verbatimModuleSyntax error May 26, 2024
@jakebailey
Copy link
Member

That option doesn't control anything to do with ESM versus CJS, it's controlling "module" (uses some form of exports, declarations are local) versus "script" (all globals, like a script tag).

@robpalme
Copy link

robpalme commented May 27, 2024

This sounds similar (but not the same) as the 5.5 issue we hit of *.ts ES modules being emitted as CommonJS despite all tsconfig options conveying that input and output should all be ESM, e.g. "module": "esnext".

The root cause seems to be the intentional decision to respect package.json's "type": "commonjs" so that it takes precedence over tsconfig, i.e. input and output will be assumed to be CJS if the source file is *.ts and there's no way in tsconfig to stop this.

In our case we have a build system that up to now intentionally ignored package.json so this coupling was unwelcome, but hey, maybe we just have an odd use-case. @nicolo-ribaudo what is your use-case for wanting this decoupling of tsconfig and package.json semantics?

@nicolo-ribaudo
Copy link
Author

nicolo-ribaudo commented May 27, 2024

I am using TypeScript only for type checking, and I'm using Babel to do all transpilation. My build system will transpile those files either to ESM or to CJS and put them in appropriate folders with their own package.json file that has the correct package.json#type.

These restriction seem to be only necessary when TypeScript is emitting JS files, and even in that case they should be based on the package.json relative to the output files and not to the input files.

In the new TS5.5 behavior, how would I have to update my tsconfig to keep being able to have the following, when not emitting JS files through tsc?

  • ESM syntax in .ts files
  • export = in .cts files
  • package.json#type: commonjs in the root
  • package.json#type: module in the ESM dist folder

What I find surprising about this new behavior is that my module option is not set to one of the possible node* values, so I would expect it to not be affected by the Node.js-specific package.json#type.


EDIT: It seems I can just disable verbatimModuleSyntax: true, module: ESNext and @ts-ignore the errors about using require as a variable name, I need to check if this affects anything else.

EDIT2: That doesn't work because then TS errors on import handleMessage = ... in .cts files

@nicolo-ribaudo
Copy link
Author

nicolo-ribaudo commented May 27, 2024

I guess what I would like to have is an option to tell TS "I know what I'm doing about modules syntax, please don't error when you think you won't be able to emit it correctly", or an option to restore the previous behavior.

@fatcerberus
Copy link

and even in that case they should be based on the package.json relative to the output files and not to the input files.

IIRC this was actually tried. I don’t remember the exact reasoning but it turned out to not really be viable. See discussion at #54593 and other issues linked therein.

@acutmore
Copy link
Contributor

This sounds similar (but not the same) as the 5.5 issue we hit of *.ts ES modules being emitted as CommonJS despite all tsconfig options conveying that input and output should all be ESM, e.g. "module": "esnext".

Some more context here: #58587 (comment).
Our tooling does not emit a Node.js package, the build output won't contain a package.json. So any inference made about the content of the .ts files based on the package.json can cause us issues. In the past this was mostly limited to npm auto-complete suggestions appearing which won't be available in this particular runtime. However with 5.5 this now extends to causing build errors which can only be addressed by modifying the package.json which just so happens to be in a parent directory of the project.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Jun 6, 2024
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 5.6.0 milestone Jun 6, 2024
@andrewbranch
Copy link
Member

Why are folks using an explicit "type": "commonjs" over omitting "type"? Preemptive performance optimization for --experimental-detect-module?

@nicolo-ribaudo
Copy link
Author

Just because type: commonjs and no type should have the same meaning, and in this case I prefer to be explicit.

Does TS handle those two cases differently?

@andrewbranch
Copy link
Member

It does now outside of node16/nodenext. "type": "commonjs" is a more explicit signal than no "type". If you’re not targeting Node.js, it might be reasonable to leave that field unset and have ESM files, since Bun and most bundlers have no issue with that. But if you set "type": "commonjs", we take that as a signal that you at least want compatibility with Node.js, so we will use that signal to resolve ambiguities and prevent you from emitting something that will totally break it.

they should be based on the package.json relative to the output files and not to the input files

See #54546

@andrewbranch andrewbranch changed the title [5.5 beta] The moduleDetection option is ignored for verbatimModuleSyntax error [5.5 beta] package.json "type" lookup in non-Node.js module is unexpected Jun 10, 2024
@andrewbranch
Copy link
Member

@nicolo-ribaudo @acutmore could you try the build at #58825 and see if that addresses your issues? I think it should, but getting some definite feedback on real projects would be great since we’re so close to the final 5.5 release here.

@andrewbranch
Copy link
Member

Closed by #58831 / #58857

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

7 participants