guybedford / es-module-lexer

Low-overhead lexer dedicated to ES module parsing for fast analysis
MIT License
917 stars 48 forks source link

Return array of modules found in import-statements #100

Closed MathiasWP closed 2 years ago

MathiasWP commented 2 years ago

I am currently using this library in a svelte pre-processor to optimise import-statements, and it is a perfect fit!

It would be awesome to get a list of the modules that are imported in an ImportDeclaration, so that i could skip my "hacky" step of obtaining this information from the current api.

Is this possible to add?

Thanks for reading, your github-profile is a lifesaver to my organization!

guybedford commented 2 years ago

It would add a little more overhead to the API, where keeping filesize down is a primary objective of this project as it is used in es-module-shims. If a PR were small enough it might be an option though.

daKmoR commented 2 years ago

I just stumbled over this (or a similar) use case as well 😅

I would like to find all import names.

e.g. for this given imports

import { name, foo as bar } from 'mod';
import json from './json.json' assert { type: 'json' }

I would like to get a list of

@MathiasWP can you share (here a code snippet) your "hacky" version?

daKmoR commented 2 years ago

this is my "hacky" version

/**
 * Extracts all import names for an already parsed files
 *
 * @param {import('es-module-lexer').ImportSpecifier} imports
 * @param {string} source
 * @returns {string[]}
 */
export function importsToImportNames(imports, source) {
  const allImportNames = [];
  for (const singleImport of imports) {
    const importStatement = source.substring(singleImport.ss, singleImport.se);
    const importNames = getImportNames(importStatement);
    allImportNames.push(...importNames);
  }
  return allImportNames;
}

/**
 * Extracts all import names from a full import statement
 *
 * import { html, css as litCss } from 'lit';
 * => ['html', 'litCss']
 *
 * @param {string} importStatement
 * @returns {string[]}
 */
export function getImportNames(importStatement) {
  /** @type string[] */
  const importNames = [];

  const singleLine = importStatement.trim().replace(/\n/g, '');
  const fromIndex = singleLine.indexOf('from');
  if (fromIndex >= 0) {
    const importPart = singleLine.substring(6, fromIndex);
    const cleanedImportPart = importPart.replace(/[{}]/g, '');
    const importStatementParts = cleanedImportPart
      .split(',')
      .map(el => el.trim())
      .filter(Boolean);

    for (const importName of importStatementParts) {
      if (importName.includes(' as ')) {
        importNames.push(importName.split(' as ')[1].trim());
      } else {
        importNames.push(importName);
      }
    }
    return importNames;
  }

  return importNames;
}

test example

    it('multiple imports', async () => {
      const source = [
        //
        "import { html, nothing } from 'lit';",
        "import { doIt } from 'nike';",
      ].join('\n');
      const [imports] = parse(source);
      expect(importsToImportNames(imports, source)).to.deep.equal(['html', 'nothing', 'doIt']);
    });
guybedford commented 2 years ago

This has now been added in https://github.com/guybedford/es-module-lexer/pull/119.

MathiasWP commented 2 years ago

I am struggling with figuring out how to get this with the latest release, is it possible to get a small example? 🙏