esbuild-fix-imports-plugin
Version:
An ESBuild plugin that fixes import paths by applying fixAliasPlugin, fixFolderImportsPlugin, and fixExtensionsPlugin. It ensures correct file extensions, resolves path aliases, and fixes directory imports in your build output when using 'tsup' with 'bund
1 lines • 8.76 kB
Source Map (JSON)
{"version":3,"sources":["../../src/fixExtensionsPlugin.ts"],"sourcesContent":["import { type Plugin } from \"esbuild\";\n\n/**\n * ESBuild plugin that ensures all relative import paths in the output files\n * have the correct file extensions (.mjs or .cjs), appending them if missing.\n * This is useful for environments that require explicit file extensions in imports.\n *\n * @returns {import('esbuild').Plugin} An ESBuild plugin object.\n */\nexport const fixExtensionsPlugin = (): Plugin => ({\n name: \"fixExtensionsPlugin\",\n setup: (build) => {\n // Determine if the output format is ESM (ECMAScript Module).\n const isEsm = build.initialOptions.format === \"esm\";\n\n // Determine the output file extension based on the build options.\n const outExtension = build.initialOptions.outExtension?.[\".js\"] ?? \".js\";\n\n // Hook into the 'onEnd' event of the build process.\n build.onEnd((result) => {\n // If there are build errors, do not proceed.\n if (result.errors.length > 0) {\n return;\n }\n\n // Iterate over each output file generated by ESBuild.\n for (const outputFile of result.outputFiles ?? []) {\n // Only target files with the specified output extension.\n // This ignores additional files emitted, like sourcemaps (e.g., \"*.js.map\").\n if (!outputFile.path.endsWith(outExtension)) {\n continue;\n }\n\n // Get the original file contents.\n const fileContents = outputFile.text;\n // Modify the file contents by appending the correct file extensions.\n const nextFileContents = modifyRelativeImports(\n fileContents,\n isEsm,\n outExtension\n );\n\n // Update the output file contents with the modified contents.\n outputFile.contents = Buffer.from(nextFileContents);\n }\n });\n },\n});\n\n/**\n * Regular expression to match CommonJS require statements with relative paths.\n * Captures the import path and an optional semicolon.\n */\nconst CJS_RELATIVE_IMPORT_EXP = /require\\s*\\(\\s*[\"'](\\..+?)[\"']\\s*\\)(;?)/g;\n\n/**\n * Regular expression to match ESM import statements with relative paths.\n * Captures the import path and an optional semicolon.\n */\nconst ESM_RELATIVE_IMPORT_EXP = /from\\s*([\"'])(\\.[^\"']+)\\1([^;]*;?)/g;\n\n/**\n * Regular expression to detect if the import path already contains an explicit .js extension\n */\nconst hasJSExtensionRegex = /\\.(?:js)$/i;\n\n/**\n * Regular expression to detect if the import path already has an extension (that is not .js),\n * such as .png, .svg, .jpeg, .jpg, etc.\n */\nconst hasNonJSExtensionRegex =\n /\\.(?:png|svg|css|scss|csv|tsv|xml|toml|ini|jpe?g|json|md|mdx|json|yaml|gif|webp|ico|mp4|webm|ogg|wav|mp3|m4a|aac|webm|woff2?|eot|ttf|otf|wasm)$/i;\n\n/**\n * Modifies the contents of a file by appending the correct file extensions\n * to relative import paths, depending on the module format.\n *\n * @param {string} contents - The contents of the file to modify.\n * @param {boolean} isEsm - Indicates whether the module format is ESM.\n * @returns {string} The modified file contents.\n */\nconst modifyRelativeImports = (\n contents: string,\n isEsm: boolean,\n outExtension: string\n) =>\n isEsm\n ? modifyEsmImports(contents, outExtension)\n : modifyCjsImports(contents, outExtension);\n\n/**\n * Modifies ESM import statements in the file contents by ensuring that\n * all relative import paths have the correct .mjs extension appended.\n *\n * @param {string} contents - The contents of the file to modify.\n * @returns {string} The modified file contents with updated import paths.\n */\nconst modifyEsmImports = (contents: string, outExtension: string) => {\n return contents.replace(\n ESM_RELATIVE_IMPORT_EXP,\n (_, quote, importPath, rest = \"\") => {\n // If the import path ends with '.' or '/', it likely refers to a directory.\n if (importPath.endsWith(\".\") || importPath.endsWith(\"/\")) {\n // Append '/index.mjs' to the path.\n return `from ${quote}${importPath}/index${outExtension}${quote}${rest}`;\n }\n\n // If the import path already ends with '.mjs', leave it as is.\n if (importPath.endsWith(outExtension)) {\n return `from ${quote}${importPath}${quote}${rest}`;\n }\n\n // If the import path has an explicit .js extension and the out extension is different, replace the existing extension.\n if (hasJSExtensionRegex.test(importPath) && outExtension !== \".js\") {\n const updatedImportPath = importPath.replace(\n hasJSExtensionRegex,\n outExtension\n );\n return `from ${quote}${updatedImportPath}${quote}${rest}`;\n }\n\n // If the import path has an existing extension (e.g., .png, .svg), leave it as is.\n if (hasNonJSExtensionRegex.test(importPath)) {\n return `from ${quote}${importPath}${quote}${rest}`;\n }\n\n // Otherwise, append '.mjs' to the import path.\n return `from ${quote}${importPath}${outExtension}${quote}${rest}`;\n }\n );\n};\n\n/**\n * Modifies CommonJS require statements in the file contents by ensuring that\n * all relative import paths have the correct .cjs extension appended.\n *\n * @param {string} contents - The contents of the file to modify.\n * @returns {string} The modified file contents with updated require paths.\n */\nconst modifyCjsImports = (contents: string, outExtension: string) => {\n return contents.replace(\n CJS_RELATIVE_IMPORT_EXP,\n (_, importPath, maybeSemicolon = \"\") => {\n // If the import path ends with '.' or '/', it likely refers to a directory.\n if (importPath.endsWith(\".\") || importPath.endsWith(\"/\")) {\n // Append '/index.cjs' to the path.\n return `require('${importPath}/index${outExtension}')${maybeSemicolon}`;\n }\n\n // If the import path has an explicit .js extension and the out extension is different, replace the existing extension.\n if (hasJSExtensionRegex.test(importPath) && outExtension !== \".js\") {\n const updatedImportPath = importPath.replace(\n hasJSExtensionRegex,\n outExtension\n );\n return `require('${updatedImportPath}')${maybeSemicolon}`;\n }\n\n // If the import path already ends with '.cjs', leave it as is.\n if (importPath.endsWith(outExtension)) {\n return `require('${importPath}')${maybeSemicolon}`;\n }\n\n // If the import path has an existing extension (e.g., .png, .svg), leave it as is.\n if (hasNonJSExtensionRegex.test(importPath)) {\n return `require('${importPath}')${maybeSemicolon}`;\n }\n\n // Otherwise, append '.cjs' to the import path.\n return `require('${importPath}${outExtension}')${maybeSemicolon}`;\n }\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,MAAM,sBAAsB,OAAe;AAAA,EAChD,MAAM;AAAA,EACN,OAAO,CAAC,UAAU;AAEhB,UAAM,QAAQ,MAAM,eAAe,WAAW;AAG9C,UAAM,eAAe,MAAM,eAAe,eAAe,KAAK,KAAK;AAGnE,UAAM,MAAM,CAAC,WAAW;AAEtB,UAAI,OAAO,OAAO,SAAS,GAAG;AAC5B;AAAA,MACF;AAGA,iBAAW,cAAc,OAAO,eAAe,CAAC,GAAG;AAGjD,YAAI,CAAC,WAAW,KAAK,SAAS,YAAY,GAAG;AAC3C;AAAA,QACF;AAGA,cAAM,eAAe,WAAW;AAEhC,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAGA,mBAAW,WAAW,OAAO,KAAK,gBAAgB;AAAA,MACpD;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAMA,MAAM,0BAA0B;AAMhC,MAAM,0BAA0B;AAKhC,MAAM,sBAAsB;AAM5B,MAAM,yBACJ;AAUF,MAAM,wBAAwB,CAC5B,UACA,OACA,iBAEA,QACI,iBAAiB,UAAU,YAAY,IACvC,iBAAiB,UAAU,YAAY;AAS7C,MAAM,mBAAmB,CAAC,UAAkB,iBAAyB;AACnE,SAAO,SAAS;AAAA,IACd;AAAA,IACA,CAAC,GAAG,OAAO,YAAY,OAAO,OAAO;AAEnC,UAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,GAAG,GAAG;AAExD,eAAO,QAAQ,KAAK,GAAG,UAAU,SAAS,YAAY,GAAG,KAAK,GAAG,IAAI;AAAA,MACvE;AAGA,UAAI,WAAW,SAAS,YAAY,GAAG;AACrC,eAAO,QAAQ,KAAK,GAAG,UAAU,GAAG,KAAK,GAAG,IAAI;AAAA,MAClD;AAGA,UAAI,oBAAoB,KAAK,UAAU,KAAK,iBAAiB,OAAO;AAClE,cAAM,oBAAoB,WAAW;AAAA,UACnC;AAAA,UACA;AAAA,QACF;AACA,eAAO,QAAQ,KAAK,GAAG,iBAAiB,GAAG,KAAK,GAAG,IAAI;AAAA,MACzD;AAGA,UAAI,uBAAuB,KAAK,UAAU,GAAG;AAC3C,eAAO,QAAQ,KAAK,GAAG,UAAU,GAAG,KAAK,GAAG,IAAI;AAAA,MAClD;AAGA,aAAO,QAAQ,KAAK,GAAG,UAAU,GAAG,YAAY,GAAG,KAAK,GAAG,IAAI;AAAA,IACjE;AAAA,EACF;AACF;AASA,MAAM,mBAAmB,CAAC,UAAkB,iBAAyB;AACnE,SAAO,SAAS;AAAA,IACd;AAAA,IACA,CAAC,GAAG,YAAY,iBAAiB,OAAO;AAEtC,UAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,GAAG,GAAG;AAExD,eAAO,YAAY,UAAU,SAAS,YAAY,KAAK,cAAc;AAAA,MACvE;AAGA,UAAI,oBAAoB,KAAK,UAAU,KAAK,iBAAiB,OAAO;AAClE,cAAM,oBAAoB,WAAW;AAAA,UACnC;AAAA,UACA;AAAA,QACF;AACA,eAAO,YAAY,iBAAiB,KAAK,cAAc;AAAA,MACzD;AAGA,UAAI,WAAW,SAAS,YAAY,GAAG;AACrC,eAAO,YAAY,UAAU,KAAK,cAAc;AAAA,MAClD;AAGA,UAAI,uBAAuB,KAAK,UAAU,GAAG;AAC3C,eAAO,YAAY,UAAU,KAAK,cAAc;AAAA,MAClD;AAGA,aAAO,YAAY,UAAU,GAAG,YAAY,KAAK,cAAc;AAAA,IACjE;AAAA,EACF;AACF;","names":[]}