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

Support Typescript 4.7 "Node16" extension requirements #8993

Open
4 tasks done
aMediocreDad opened this issue Jul 8, 2022 · 9 comments · May be fixed by #18889
Open
4 tasks done

Support Typescript 4.7 "Node16" extension requirements #8993

aMediocreDad opened this issue Jul 8, 2022 · 9 comments · May be fixed by #18889
Labels
enhancement New feature or request

Comments

@aMediocreDad
Copy link

Description

With Typescript version 4.7, Typescript now has the option to preserve "ES Modules" if tsconfig.json specifies:

{
  "compilerOptions": {
    "module": "Node16"
  }
}

This is a step forward in supporting native ESM in Node from Typescript's direction, but it comes with the requirement that all typescript modules when imported use the .js extension.

Ref: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html

This works all fine and well in Vite when importing typescript modules in .ts-files.

The problems begin when importing .ts-files in .js, .vue, .svelte, and so on.

E.g.

// ./some-ts-file.ts
export const hello = "Hello";

// ./my-app.js
import { hello } from "./some-ts-file.js"

See reproducible example: https://github.com/aMediocreDad/vite-import-ts-as-js

Suggested solution

The expectation is that importing .ts-files as .js resolves the file like it does in .ts-files at the moment (See related "Closed" issue: #3040).

Alternative

No response

Additional context

No response

Validations

@seivan
Copy link

seivan commented Jul 9, 2022

Out of curiosity, did you notice improvements? Runtime or Buildtime?
This is going the Deno route, of requiring extensions in the imports, which always irked me, but I can learn.

@aMediocreDad
Copy link
Author

aMediocreDad commented Jul 10, 2022

Out of curiosity, did you notice improvements? Runtime or Buildtime?

I have not tested for any build/runtime improvements. However, it does provide some long awaited developer experience improvements.

Using the "Node16" module specifier makes Typescript finally use the import/export fields in package.json properly. This is a big help where packages don't use the "main" field because they do not want to indicate backwards compatibility with pre Node 12. Previously, Typescript tooling was usually difficult to set up because of how it failed to recognize newer package.json configurations (that only cater to ESM).

I do not use the tsc compiler so I won't see the improvements that the compilation options provide. But the steady move towards proper ESM handling across all tools commonly used in the same toolchain is all I really want to see.

@seivan
Copy link

seivan commented Jul 10, 2022

Thanks for explaining that, appreciate it.

@bluwy bluwy added this to Team Board Jul 11, 2022
@bluwy bluwy moved this to Discussing in Team Board Jul 11, 2022
@FossPrime
Copy link

FossPrime commented Sep 14, 2022

Running into another issue with Subpath import patterns

We can't safely use #src as defined in package.json. In bigger projects this is really helpful.

Vue project that doesn't run because of Vite...
https://stackblitz.com/edit/but-subpath-imports-vite?file=package.json%3AL11

Another example, showing tsx running correctly, but vite failing dude to an issue with TS's Node16 module loading support.
Ts vite project, runs fine in TSX

Resolved TypeScript issue supporting Subpath Imports:
microsoft/TypeScript#44848 (comment)

I wouldn't call this an enhancement, but a build breaking bug. This issue has caused me to refactor several repos that made heavy use of it. Using ssrLoadModule causes some workarounds to stop working... meaning I have no choice but to refactor the relatively old and well supported Node feature out.

@bluwy
Copy link
Member

bluwy commented Oct 14, 2022

We've discussed this in the last team meeting and decided to support .js -> .ts resolving for JS files too based on the related tsconfig.json/jsconfig.json that has the special resolution setting.

The rule seems to be that, if the moduleResolution is node16, nodenext, or above; Or else if the module is node16, nodenext, or above; Then we'd kick in the file resolution.

@bluwy bluwy added enhancement New feature or request and removed p2-to-be-discussed Enhancement under consideration (priority) labels Oct 14, 2022
@bluwy bluwy self-assigned this Oct 21, 2022
@FossPrime
Copy link

As the official Vite plugins are currently packaged using a Psuedo-module format that uses export {vuePlugin as default} rather than the ESM canonical export default function vuePlugin (...

You currently have to import some plugins with import { default as vue } from '@vitejs/plugin-vue' rather than the actual default import. As the synthetic imports may not be applied to types, but if enabled, are applied to code.

Definition using synthetic legacy default export: https://unpkg.com/browse/@vitejs/[email protected]/dist/index.d.ts
CF: https://stackblitz.com/~/github.com/FossPrime/bug-primevue-esm-types
SBC: https://stackblitz.com/edit/public-sakai-yqyw2y?file=src%2Fmain.ts%3AL13,src%2Fvite-env.d.ts%3AL11

@artemis-prime
Copy link

We've discussed this in the last team meeting and decided to support .js -> .ts resolving for JS files too based on the related tsconfig.json/jsconfig.json that has the special resolution setting.

The rule seems to be that, if the moduleResolution is node16, nodenext, or above; Or else if the module is node16, nodenext, or above; Then we'd kick in the file resolution.

When will this happen? Currently, my setup syntax highlights my local imports (without ext) as errors, but vite can build / serve them just fine. It's kind of annoying. Or am I missing some nuance of intent?

@anilanar
Copy link

This "kind of" works, but requires a full restart of the vite dev server every time a new ts file is created and later on imported from another file with a js extension.

@aaronadamsCA
Copy link

I'd like to suggest that Vite should always resolve .js to .ts in all moduleResolution modes. Both TypeScript and esbuild do this and I think Vite should do the same.

TypeScript's handbook on module theory describes its behaviour:

While the module compiler option can transform imports and exports in input files to different module formats in output files, the module specifier (the string from which you import, or pass to require) is always emitted as-written.

There is no compiler option that enables transforming, substituting, or rewriting module specifiers. Consequently, module specifiers must be written in a way that works for the code’s target runtime or bundler, and it’s TypeScript’s job to understand those output-relative specifiers.

This also clarifies why TypeScript doesn’t modify import specifiers during emit: the relationship between an import specifier and a file on disk (if one even exists) is host-defined, and TypeScript is not a host.

TypeScript should always follow an "output-relative" import specifier (like .js or .jsx) to its corresponding source file (like .ts or .tsx), regardless of the moduleResolution value. Node16 and NodeNext don't enable this behaviour, they just enforce mandatory file extensions.

I think Vite should do the same, to gradually eliminate cases where TypeScript can resolve an import but Vite can't.

Today Vite conditionally maps import specifiers if the importer is a TypeScript file, but that's backwards; all that should matter is whether the exporter could be a TypeScript file. If an HTML file, MDX file, or any other type of file wants to reference a .ts file using a .js extension, ideally Vite would resolve this the same way TypeScript does.

If you start closing this gap, I think you start eliminating an entire category of interop problems. In other words, I couldn't agree more with @aMediocreDad:

I do not use the tsc compiler so I won't see the improvements that the compilation options provide. But the steady move towards proper ESM handling across all tools commonly used in the same toolchain is all I really want to see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

7 participants