All files index.js

96.15% Statements 50/52
93.33% Branches 42/45
90% Functions 9/10
97.95% Lines 48/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123            1x     1x 37x 37x       1x 96x 91x     5x 5x 4x   1x 1x           1x   90x 4x     86x 86x   86x         2x       84x 18x     66x 40x     26x 7x     19x       18x 90x 90x 90x     90x 90x   90x     90x 36x 36x 36x   36x 36x 8x   28x 3x   28x     36x                 6x 6x           27x         9x                  
 
import { declare } from '@babel/helper-plugin-utils';
import { existsSync, lstatSync } from 'fs';
import { resolve, extname, dirname } from 'path';
import { types } from '@babel/core';
 
const { importDeclaration, exportNamedDeclaration, exportAllDeclaration, stringLiteral } = types
 
// Checks if the module has an active extension
const isActiveExtension = (module, observedScriptExtensions) => {
  const ext = extname(module).toLowerCase().replace(/[^a-z]/, '');
  return observedScriptExtensions.includes(ext);
};
 
// Checks if the module is a Node module
export const isNodeModule = (module) => {
  if (module.startsWith('.') || module.startsWith('/')) {
    return false;
  }
 
  try {
    require.resolve(module);
    return true;
  } catch (e) {
    Eif (e.code === 'MODULE_NOT_FOUND') {
      return false;
    }
    // console.error(e);
  }
};
 
const skipModule = (module, { replace, extension, skipUnlistedExtensions, observedScriptExtensions }) => {
  // If the module path does not start with "." (not a relative path) or is a Node module, skip it directly
  if (!module.startsWith('.') || isNodeModule(module)) {
    return true;
  }
  // Get the module's extension and convert it to lowercase
  const moduleExtension = extname(module).toLowerCase();
  const moduleExtensionWithoutDot = moduleExtension.replace(/^\./, '');
  // If the module's extension is not in the list of observed script extensions, skip it
  if (
    skipUnlistedExtensions == true 
    && moduleExtension != ""
    && !observedScriptExtensions.includes(moduleExtensionWithoutDot)
  ) {
    return true;
  }
 
  // If the module has no extension, do not skip it (because an extension might need to be added)
  if (!moduleExtension) {
    return false;
  }
  // If the module already has the target extension (after converting to lowercase), skip it
  if (moduleExtension === `.${extension.toLowerCase()}`) {
    return true;
  }
  // When replace is true, allow replacing the existing extension, so do not skip
  if (replace) {
    return false; // Do not skip, allow replacing the extension
  }
  // When replace is false, skip if the module has an "active" extension
  return isActiveExtension(module, observedScriptExtensions);
};
  
// Generates a module path declaration
export const makeDeclaration = ({ declaration, args }) => (path, state) => {
  const { node } = path;
  const { source, exportKind, importKind } = node;
  const { replace = false, extension = 'js', skipUnlistedExtensions = false, observedScriptExtensions = ['js', 'ts', 'jsx', 'tsx', 'mjs', 'cjs'] } = state.opts;
 
  // If there is no source or it's a type-only import/export, return directly
  const isTypeOnly = exportKind === 'type' || importKind === 'type';
  Iif (!source || isTypeOnly) return;
 
  const module = source.value;
 
  // If the module should be skipped, return directly
  if (skipModule(module, { replace, extension, skipUnlistedExtensions, observedScriptExtensions })) return;
  const dirPath = resolve(dirname(module), module)
  const hasModuleExt = extname(module).length && isActiveExtension(module, observedScriptExtensions);
  let newModuleName = hasModuleExt ? module.slice(0, -extname(module).length) : module;
  // Generates the new path based on the module path
  const pathLiteral = () => {
      if (existsSync(dirPath) && lstatSync(dirPath).isDirectory()) {
          return `${module}${newModuleName.endsWith('/') ? '' : '/'}index.${extension}`;
      }
      if (newModuleName.endsWith('/')) {
          newModuleName = `${module}index`;
      }
      return `${newModuleName}.${extension}`;
  };
 
  path.replaceWith(
      declaration(
      ...args(path),
      stringLiteral(pathLiteral())
      )
  );
};
 
export default declare((api, options) => {
    api.assertVersion(7);
    return {
        name: 'add-import-extension',
        visitor: {
            ImportDeclaration: makeDeclaration({
                ...options,
                declaration: importDeclaration,
                args: ({ node: { specifiers } }) => [specifiers]
            }),
            ExportNamedDeclaration: makeDeclaration({
                ...options,
                declaration: exportNamedDeclaration,
                args: ({ node: { declaration, specifiers } }) => [declaration, specifiers]
            }),
            ExportAllDeclaration: makeDeclaration({
                ...options,
                declaration: exportAllDeclaration,
                args: () => []
            })
        }
    };
})