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

How to resolve and inline dynamic imports? #732

Open
meandmax opened this issue Oct 13, 2019 · 4 comments
Open

How to resolve and inline dynamic imports? #732

meandmax opened this issue Oct 13, 2019 · 4 comments

Comments

@meandmax
Copy link

Am I able to resolve import("myComponent") and inline it with ts-morph?

@lazarljubenovic
Copy link
Contributor

Pretty inlining is not trivial mostly because of dependencies of the thing you want to import and name clashes when you copy over text. If you're sure that you can statically resolve some imports, I suggest doing a transformation where you change const module = await import('module') into import * as module from 'module', then the code remains the same semantically, and you can use something like Rollup to inline them.

@meandmax
Copy link
Author

Thank you. Is there a way to get the text behind the import inside of ts-morph? I'm asking because just adding it is in my case not enough. Before adding it I wanted to analyse the declaration behind the import. So I'm only analyzing declaration files.

Any way to resolve it? Thanks for the help! :)
Max

@OmarOmeiri
Copy link

OmarOmeiri commented Oct 30, 2021

Hey, @meandmax!

I guess I'm 2 years late to this party, but I was trying to solve the same issue today and figured something out.
Please note that I have not tested this a lot. I just finished it now.

If you are still in need of this, here it goes.

This is the file I was testing on:

async function dynaImport(): void {
  const { SuppliersService } = await import('#services/ServiceSuppliers');

  const test = new SuppliersService()

  const somedef, {
    attrs: IAttrs,
    IFormattedAttrs,
  } = await import('#data/Typings/attrs/attrs');

  const something = await import('#services/ServiceSuppliers');
}

Check this before.

/**
 * Counts identifiers by text value
 * of a given sourceFile or a block, if provided
 */
export function countIndentifierByText(
  text: string,
  sourceFile: SourceFile,
  block?: Node,
): number {
  return (block ?? sourceFile).getDescendantsOfKind(SyntaxKind.Identifier)
    .reduce((acc, idf) => {
      if (idf.getText().replace(ESCAPE_STRING, '') === text.replace(ESCAPE_STRING, '')) {
        return acc + 1;
      }
      return acc;
    }, 0);
}

/**
 * Checks is a sourceFile is an external library
 * @param sourceFile
 */
function isImportALib(
  sourceFile: SourceFile,
): boolean {
  return sourceFile.getFilePath().includes('node_modules')
    || sourceFile.isInNodeModules()
    || sourceFile.isFromExternalLibrary();
}

/**
 * Gets the source file's dynamic imports.
 * @example
 * const something = await import('some/where')
 *
 */
export function getDynamicImports(
  sourceFile: SourceFile,
): Omit<IImports, 'id'>[] {
  return sourceFile
    .getDescendantsOfKind(SyntaxKind.VariableStatement)
    .filter((varStmt) => varStmt.getDescendantsOfKind(SyntaxKind.ImportKeyword).length)
    .map((dinamicImport) => {
      const parentBlock = dinamicImport
        .getParentWhile((node) => node.getKind() === SyntaxKind.Block);

      const defaultImport = dinamicImport
        .getDescendantsOfKind(SyntaxKind.VariableDeclaration)
        .filter((varDecl) => !varDecl.getDescendantsOfKind(SyntaxKind.ObjectBindingPattern).length)
        .map((defaultImprt) => defaultImprt.getFirstChildByKind(SyntaxKind.Identifier)?.getText())[0];


      const namedImports = dinamicImport.getDescendantsOfKind(SyntaxKind.BindingElement)
        .map((destruct) => destruct.getDescendantsOfKind(SyntaxKind.Identifier))
        .map((idf) => {
          if (idf.length === 1) {
            return {
              name: idf[0].getText(),
              isUsed: countIndentifierByText(idf[0].getText(), sourceFile, parentBlock) > 1,
            };
          }
          return {
            name: idf[0].getText(),
            alias: idf[1].getText(),
            isUsed: countIndentifierByText(idf[1].getText(), sourceFile, parentBlock) > 1,
          };
        });

      const specifier = dinamicImport
        .getDescendantsOfKind(SyntaxKind.SyntaxList)
        .map((synList) => synList.getChildrenOfKind(SyntaxKind.StringLiteral))
        .flat()[0];

      const importSourceFile = specifier
        ? (
          specifier
            .getSymbol()?.getDeclarations()[0].getSourceFile()
        )
        : undefined;

      const isLib = importSourceFile
        ? isImportALib(importSourceFile)
        : undefined;

      const isDefaultUsed = defaultImport
        ? countIndentifierByText(defaultImport, sourceFile, parentBlock) > 1
        : undefined;

      return {
        defaultImport,
        namedImports,
        specifier: specifier?.getText(),
        isTypeOnly: false,
        isLib,
        isUsed: isDefaultUsed,
        resolvedPath: importSourceFile ? importSourceFile.getFilePath() : undefined,
      };
    });
}

This whole ugly thing outputs this:

[
  {
    namedImports: [
      {
        name: 'SuppliersService',
        isUsed: true,
      },
    ],
    specifier: "'#services/ServiceSuppliers'",
    isTypeOnly: false,
    isLib: false,
    resolvedPath: 'E:/Documents/CODE/WebDev/Lullo/backend/src/Services/Services/ServiceSuppliers.ts',
  },
  {
    defaultImport: 'somedef',
    namedImports: [
      {
        name: 'attrs',
        alias: 'IAttrs',
        isUsed: false,
      },
      {
        name: 'IFormattedAttrs',
        isUsed: false,
      },
    ],
    specifier: "'#data/Typings/attrs/attrs'",
    isTypeOnly: false,
    isLib: false,
    isUsed: false,
    resolvedPath: 'E:/Documents/CODE/WebDev/Lullo/backend/src/Data/Typings/attrs/attrs.ts',
  },
  {
    defaultImport: 'something',
    namedImports: [],
    specifier: "'#services/ServiceSuppliers'",
    isTypeOnly: false,
    isLib: false,
    isUsed: false,
    resolvedPath: 'E:/Documents/CODE/WebDev/Lullo/backend/src/Services/Services/ServiceSuppliers.ts',
  },
]

Hope this helps someone in the future.

@futurechallenger
Copy link

futurechallenger commented Jan 13, 2022

I'm guessing that if you want know if it works in ts-morph, you can try it in vscode, as they are all based on the ts lib. Correct me, if anything wrong.

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

No branches or pull requests

4 participants