TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

[experiment] Rewrite relative import extensions with flag

Open andrewbranch opened this issue 1 year ago • 3 comments

⚠️ This PR does not indicate intent to ship; I just needed a published experimental build of it for some experiments and demos. ⚠️

andrewbranch avatar Aug 26 '24 23:08 andrewbranch

@typescript-bot pack this

andrewbranch avatar Aug 26 '24 23:08 andrewbranch

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results

typescript-bot avatar Aug 26 '24 23:08 typescript-bot

Hey @andrewbranch, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/163457/artifacts?artifactName=tgz&fileId=2225118E5057943A4B80A3A9D8FE2EA5696226BA40FE09011C7C2241BF80844302&fileName=/typescript-5.7.0-insiders.20240826.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;

typescript-bot avatar Aug 26 '24 23:08 typescript-bot

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

typescript-bot avatar Sep 10 '24 00:09 typescript-bot

@typescript-bot pack this

andrewbranch avatar Sep 17 '24 21:09 andrewbranch

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results

typescript-bot avatar Sep 17 '24 21:09 typescript-bot

Hey @andrewbranch, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/163603/artifacts?artifactName=tgz&fileId=366D2974C150D261A1702BA0C2511DBAF6432824433FEBBEB4D1A438DE1953C602&fileName=/typescript-5.7.0-insiders.20240917.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;

typescript-bot avatar Sep 17 '24 21:09 typescript-bot

The original issue was locked; it would be nice for someone with permissions to comment there so that people who were following there get this update.

bakkot avatar Sep 28 '24 12:09 bakkot

@RyanCavanaugh I happened upon your final post here: https://github.com/microsoft/TypeScript/issues/49083#issuecomment-1435399267 minutes before you commented and referenced this PR.

I'm curious what changed from your reasoning a year ago as to why this would never be a feature?

jlmart88 avatar Sep 30 '24 17:09 jlmart88

NodeJS shipping --experimental-strip-types means there's now an actual runtime scenario where you need both JS and TS extensions

RyanCavanaugh avatar Sep 30 '24 17:09 RyanCavanaugh

Note that if you're using this in any other situation than that, you likely have a misconfiguration or misunderstanding of some kind, and I'll be telling you as much in a future issue when you report that e.g. this "doesn't work" across package boundaries.

RyanCavanaugh avatar Sep 30 '24 18:09 RyanCavanaugh

I have tested TS 5.7.0-dev.

It does not rewrite .d.ts files.

My library preset on 5.6.2:

    /* Library preset */
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "noEmit": false,
    "declaration": true,

and new one on 5.7.0-dev:

    /* Library preset */
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "allowImportingTsExtensions": true,      // <- new
    "rewriteRelativeImportExtensions": true, // <- new
    "noEmit": false,
    "declaration": true,

It works, I can use tsc (after changing .js to .ts in imports in all files).

Also, now I can run TS files with deno without using --unstable-sloppy-imports which prints annoying warnings.

However, imports inside .d.ts files have .ts ending now. There were no rewriting.

Or is it OK? (I didn't check if there would be .d.ts files to work correctly after installing a package with such .d.ts files.)

Anyway, I think that both configs should produce absolutely the same result (.js and .d.ts files) after using tsc.

AlttiRi avatar Oct 02 '24 18:10 AlttiRi

Imports in .d.ts files are allowed to use .ts extensions regardless of config, so I wouldn't really consider that a bug.

RyanCavanaugh avatar Oct 02 '24 21:10 RyanCavanaugh

It's a bug. Non-rewritten imports in d.ts. break (partially) the code assistance in an IDE.

It's OK:

Screenshot (npm install @alttiri/[email protected])

After using rewriteRelativeImportExtensions:

Screenshot_1 (npm install @alttiri/[email protected] --registry=https://npm.pkg.github.com)

In this case, the IDE no longer highlights the function names, nor does it show the function signature.

The imports in .d.ts also must be rewritten.

Because all d.ts files of libs are compiled with tsc have imports end with .js, never with .ts.

Because allowImportingTsExtensions was never compatible with "moduleResolution": "NodeNext" + "noEmit": false" (until this new rewriteRelativeImportExtensions appeared.)

AlttiRi avatar Oct 02 '24 23:10 AlttiRi

Also, it currently (?) does not support aliases which resolve to a relative path.

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["../*"]
    }
  }
}

I get the follow error for import {sleep} from "@/index.ts";:

TS2877: This import uses a '.ts' extension to resolve to an input TypeScript file, but will not be rewritten during emit because it is not a relative path.

AlttiRi avatar Oct 03 '24 00:10 AlttiRi

Also, it currently (?) does not support aliases which resolve to a relative path.

This is 100% intentional

RyanCavanaugh avatar Oct 04 '24 16:10 RyanCavanaugh

Is it a problem to resolve the alias first and only after that check if it is a relative path or not?

It doesn't seem like something that is difficult to implement. Just only one extra simple action.

AlttiRi avatar Oct 04 '24 16:10 AlttiRi

I don't think the setup you have there works under --experimental-strip-types. https://github.com/microsoft/TypeScript/pull/59767#issuecomment-2383844756

RyanCavanaugh avatar Oct 04 '24 17:10 RyanCavanaugh

Okay, I have checked it with the latest Node.js (v22.9.0) too (I tested it with Deno before).

It works (as expected).

Screenshot

AlttiRi avatar Oct 04 '24 17:10 AlttiRi

I can't replicate what you're doing there

// with
// import * as bar from "./bar.ts";

node --experimental-strip-types foo.ts
hi

vs

// with
// import * as bar from "@/bar.ts";
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/bar.ts' imported from D:\Throwaway\est-test\foo.ts

The purpose of the flag is to rewrite paths which are syntactically relative, not "semantically" relative (whatever that would even mean).

RyanCavanaugh avatar Oct 04 '24 17:10 RyanCavanaugh

Hey, I was reading the table in the PR description and I'm getting confused by these two lines:

Scenario Example input Operation Example output
Require calls in JS with qualifying literal argument require("./foo.ts") Rewrite require("./foo.js")
Require calls in TS require("./foo.ts") None require("./foo.ts")

Does it mean that require("./foo.ts") will be rewritten if it's inside a JS file, but not if it's inside a TS file?

nicolo-ribaudo avatar Oct 23 '24 12:10 nicolo-ribaudo

Yes. Plain require calls aren’t recognized as special in TS files. You’ll notice in JS that require("./foo") performs module resolution (the compiler knows the type to return), but in TS, require isn’t even allowed unless it’s declared as a global function, in which case that function’s return type is used (usually any).

andrewbranch avatar Oct 23 '24 16:10 andrewbranch

Just out of curiosity, I’m facing an issue where my .d.ts files don’t have an extension in the path. For example: export * as v1alpha2 from './v1alpha2/index.gen';. Why is the .ts extension mandatory to transform it into .js? Thank you.

jremy42 avatar Dec 02 '24 14:12 jremy42

@RyanCavanaugh

Imports in .d.ts files are allowed to use .ts extensions regardless of config, so I wouldn't really consider that a bug.

While .d.ts files may allow .ts extensions in imports without configuration, wouldn't this break backwards compatibility for libraries targeting TypeScript 4 support since allowImportingTsExtensions wasn't introduced until TypeScript 5?

nyan-left avatar Dec 06 '24 14:12 nyan-left

@andrewbranch @RyanCavanaugh Shouldn't calls to import.meta.resolve()/require.resolve() also be shimmed as part of this flag? The MDN docs suggest this should be the case.

nwidynski avatar Mar 23 '25 12:03 nwidynski