UNPKG

@builttocreate/hot-formula-parser

Version:
101 lines (86 loc) 3.34 kB
const { types } = require('@babel/core'); const { declare } = require('@babel/helper-plugin-utils'); const { existsSync, lstatSync } = require('fs'); const { dirname, resolve } = require('path'); const VALID_EXTENSIONS = ['js', 'mjs']; const hasExtension = (moduleName) => VALID_EXTENSIONS.some(ext => moduleName.endsWith(`.${ext}`)); const isCoreJSPolyfill = (moduleName) => moduleName.startsWith('core-js'); const isLocalModule = (moduleName) => moduleName.startsWith('.'); const isNodeModule = (moduleName) => { try { require.resolve(moduleName); return true; } catch (ex) { if (ex.code === 'MODULE_NOT_FOUND') { return false; } } }; const isProcessableModule = (moduleName) => { return !hasExtension(moduleName) && (isCoreJSPolyfill(moduleName) || isLocalModule(moduleName)); } const createVisitor = ({ declaration, origArgs, extension = 'js' }) => { return (path, { file }) => { const { node: { source, exportKind, importKind } } = path; const { opts: { filename } } = file; const isTypeOnly = exportKind === 'type' || importKind === 'type'; if (!source || isTypeOnly || !isProcessableModule(source.value)) { return; } const { value: moduleName } = source; const absoluteFilePath = resolve(dirname(filename), moduleName); const finalExtension = isCoreJSPolyfill(moduleName) ? 'js' : extension; let newModulePath; // Resolves a case where "import" points to a module name which exists as a file and // as a directory. For example in this case: // ``` // import { registerPlugin } from 'plugins'; // ``` // and with this directory structure: // |- editors // |- plugins // |- filters/ // |- ... // +- index.js // |- plugins.js // |- ... // +- index.js // // the plugin will rename import declaration to point to the `plugins.js` file. if (existsSync(`${absoluteFilePath}.js`)) { newModulePath = `${moduleName}.${finalExtension}`; // In a case when the file doesn't exist and the module is a directory it will // rename to `plugins/index.js`. } else if (existsSync(absoluteFilePath) && lstatSync(absoluteFilePath).isDirectory()) { newModulePath = `${moduleName}/index.${finalExtension}`; // And for other cases it simply put the extension on the end of the module path } else { newModulePath = `${moduleName}.${finalExtension}`; } path.replaceWith(declaration(...origArgs(path), types.stringLiteral(newModulePath))); }; }; module.exports = declare((api, options) => { api.assertVersion(7); return { name: 'add-import-extension', visitor: { // It covers default and named imports ImportDeclaration: createVisitor({ extension: options.extension, declaration: types.importDeclaration, origArgs: ({ node: { specifiers } }) => [specifiers], }), ExportNamedDeclaration: createVisitor({ extension: options.extension, declaration: types.exportNamedDeclaration, origArgs: ({ node: { declaration, specifiers } }) => [declaration, specifiers], }), ExportAllDeclaration: createVisitor({ extension: options.extension, declaration: types.exportAllDeclaration, origArgs: () => [], }), } }; });