Skip to content

Add a resolveImportPaths option that resolves import paths configured in paths options #45862

@funkydev

Description

@funkydev

Suggestion

Add a resolveImportPaths option that resolves import paths configured in paths options,

While transpiling code into the outDir using tsc -p ./tsconfig.json

✅ Viability Checklist

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This feature would agree with the rest of TypeScript's Design Goals
    • Align with current and future ECMAScript proposals.
    • Be a cross-platform development tool.

📃 Example

  1. Project structure
  2. Steps to reproduce
  3. Actual result
  4. Expected result

Project structure

dist
src
  |- common
  |  |- value-objects.ts
  |- some
     |- extra
        |- nested
          |- folder
            |- greeting.builder.ts
package.json
package-lock.json
tsconfig.json

Source of the package.json file:

{
  "name": "demo-for-typescript",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc -p ./tsconfig.json",
    "start": "node ./dist/some/extra/nested/folder/greeting.builder.js",
  },
  "dependencies": {
    "typescript": "^4.4.3"
  },
  "devDependencies": {
    "@types/node": "^16.9.1"
  }
}

Source of the tsconfig.json file:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2017",
    "outDir": "dist",
    "baseUrl": "src",
    "paths": {
      "@common/*": ["common/*"]
    },
  }
}

Source of the src/common/value-objects.ts file:

export class DescriptionValueObject {
  constructor(value: any) {
    if (typeof value !== 'string') {
      throw new Error('Description value has to be of type string');
    }
  }
}

Source of the src/some/extra/nested/folder/greeting.builder.ts file:

import { GreetingValueObject } from "@common/value-objects";
// using paths instead of: import { GreetingValueObject } from "../../../../common/value-objects";
// not only for esthetic purposes, but it helps to resolve conflicts after move files and helps to maintain the large codebase

class GreetingBuilder {
  private name: string;

  withName(name: string) {
    this.name = name;
    return this;
  }

  build(): GreetingValueObject {
    if (!this.name) {
      throw new Error('Set name first!')
    }

    return new GreetingValueObject(`Greetings, ${ this.name }!`);
  }
}

Steps to reproduce

  1. Run npm install (tested on npm@7.6.0 and node@15.11.0)
  2. Run npm run build (it runs tsc -p ./tsconfig.json script)
  3. Run npm start (it runs node ./dist/some/extra/nested/folder/greeting.builder.js file and simulates server-side run)

Actual results

Node returns an error because tsc didn't resolve paths configured in the tsconfig.json for server-side script:

Error: Cannot find module '@common/value-objects'
Source of the ./dist/some/extra/nested/folder/greeting.builder.js:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const value_objects_1 = require("@common/value-objects"); // <-- server side script does not understand it
// ... other transpiled code

Expected result

tsc should provide the resolveImportPaths option for developers to choose if transpiler should resolve paths using the provided paths configuration

  1. When the resolveImportPaths option was set as true, tsc should emit code with relative paths.

    For this example the require statement should look like: require("../../../../common/value-objects")

    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    const value_objects_1 = require("../../../../common/value-objects");
    // ... other transpiled code
  2. When the resolveImportPaths option was set as false (by default), tsc should emit code as it does now

💻 Use Cases

Before you decline this request, you have to differentiate two different scenarios of using typescript:

  1. For transpiling client-side apps (like react, angular):
    Understandably, you won't support such an option for this case because paths are mainly used for combining external libraries.

    In this scenario, other tools (like webpack or babel), which are already used in the project, could resolve such mappings and serve correct modules.

  2. For transpiling server-side node apps:
    It is needed to have such an option because developers (as many of them showed you in other Issues) don't want to use other heavy tools only for resolving paths in the imports. That's overkill for the thing that should be handled by the transpiler.

    The worst thing after you declined many of previous requests for such options, is that the developers started to use scripts for resolving imports in node.js, that override the private API of the built-in node packages.

    Here is an example of this script: https://github.com/ilearnio/module-alias/blob/dev/index.js
    Check the weekly downloads (464 428) on NPM website https://www.npmjs.com/package/module-alias

    Wasn't typescript developed to introduce good practices into the code?

    Aliasing paths is needed especially for large codebase and helps to solve GIT conflicts after moving/refactoring the modules. Maybe it's time to change the assumptions from this comment and adjust typescript to community needs? You may see that it's needed by developers by reading related issues and checking sh***y workarounds usage statistics.

Related issues

Notice that it's not a duplication of those issues but the continuation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions