Skip to content
This repository has been archived by the owner on Mar 30, 2023. It is now read-only.

.js extension is not appended when using typescript v4.9.3 #21

Open
marinrusu1997 opened this issue Dec 7, 2022 · 6 comments
Open

.js extension is not appended when using typescript v4.9.3 #21

marinrusu1997 opened this issue Dec 7, 2022 · 6 comments

Comments

@marinrusu1997
Copy link

I recently updated typescript to v4.9.3 and generated files no longer have .js extension in relative imports.
When reverting typescript to v4.8.4 .js extension is appended as expected.

@MicahZoltu
Copy link
Member

This makes me very sad, but I'm not surprised. There are two possible causes:

  1. ttypescript is no longer working with latest version of TypeScript. The author/maintainer of ttypescript has stopped maintaining it and it has had stuff broken for a while (like watching).
  2. tsc made some changes that make this plugin no longer compatible.

Unfortunately, I suspect this may be the final straw for me that causes me to mark this repository as deprecated. 😢 I have been lobbying for Microsoft to build this into the compiler for a long time, and my hope was that this could serve as a stop-gap until they were finally convinced to make the change. Unfortunately, they just keep doubling down on their refusal to support it and I think at this point it may be a lost cause.

If someone wants to dig into this bug and figure out what the cause is and propose a fix I'm open to reviewing and potentially merging it (depending on how complicated the fix is), but I think this project is nearing the end of its lifespan. 😢

@carlocorradini
Copy link

I've never written a TypeScript plugin but this is working for me:

import path from "node:path";
import typescript from "typescript";

// eslint-disable-next-line import/no-default-export
export default function transformer(_: typescript.Program) {
  return (transformationContext: typescript.TransformationContext) =>
    (sourceFile: typescript.SourceFile) => {
      function shouldMutateModuleSpecifier(node: typescript.Node): node is (
        | typescript.ImportDeclaration
        | typescript.ExportDeclaration
      ) & {
        moduleSpecifier: typescript.StringLiteral;
      } {
        if (!typescript.isImportDeclaration(node) && !typescript.isExportDeclaration(node)) {
          return false;
        }
        if (node.moduleSpecifier === undefined) {
          return false;
        }
        // Only when module specifier is valid
        if (!typescript.isStringLiteral(node.moduleSpecifier)) {
          return false;
        }
        // Only when path is relative
        if (
          !node.moduleSpecifier.text.startsWith("./") &&
          !node.moduleSpecifier.text.startsWith("../")
        ) {
          return false;
        }
        // Only when module specifier hasn't specific extensions or has no extension
        if (
          [
            ".js",
            ".jsx",
            ".ts",
            ".tsx",
            ".mts",
            ".cts",
            ".json",
            ".css",
            ".less",
            ".htm",
            ".html",
            ".scss",
            ".sass",
          ].includes(path.extname(node.moduleSpecifier.text)) === true ||
          (path.extname(node.moduleSpecifier.text) !== "" &&
            path.extname(node.moduleSpecifier.text).length <= 4)
        ) {
          return false;
        }

        return true;
      }

      function visitNode(node: typescript.Node): typescript.VisitResult<typescript.Node> {
        if (shouldMutateModuleSpecifier(node)) {
          if (typescript.isImportDeclaration(node)) {
            const newModuleSpecifier = typescript.factory.createStringLiteral(
              `${node.moduleSpecifier.text}.js`,
            );

            return typescript.factory.updateImportDeclaration(
              node,
              node.modifiers,
              node.importClause,
              newModuleSpecifier,
              undefined,
            );
          }

          if (typescript.isExportDeclaration(node)) {
            const newModuleSpecifier = typescript.factory.createStringLiteral(
              `${node.moduleSpecifier.text}.js`,
            );

            return typescript.factory.updateExportDeclaration(
              node,
              node.modifiers,
              false,
              node.exportClause,
              newModuleSpecifier,
              undefined,
            );
          }
        }

        return typescript.visitEachChild(node, visitNode, transformationContext);
      }

      return typescript.visitNode(sourceFile, visitNode);
    };
}

@carlocorradini
Copy link

carlocorradini commented Mar 25, 2023

The code is based on https://github.com/nvandamme/typescript-transformer-append-js-extension and I've updated all the deprecated functions. I'm using TypeScript 4.9.3 and the output files are correctly appended with the .js file extension.

PS: I'm using ts-patch instead of ttypescript

@carlocorradini
Copy link

Moreover, I'm using typescript-transform-paths and there are no conflicts.
Source:

import { AuthMiddleware } from "@/helpers/auth-middleware";

Compiled:

import { AuthMiddleware } from "../helpers/auth-middleware.js";

@carlocorradini
Copy link

Am I missing something or do you want me to create a PR?

@MicahZoltu
Copy link
Member

If this works then it probably is a reasonable solution. This was my first (and only) TSC plugin and the change is sufficiently large compared to my previous code that I cannot provide very useful feedback on it unfortunately.

Since this issue was filed, I have stopped using this plugin myself and capitulated with TypeScript's recommendation of "appending .js to all of you relative imports". Because of that, I won't be maintaining this repository any longer.

My recommendation is to fork this repository, make the change you propose, and publish a new package to NPM (a fairly easy process).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants