{"id":23629678,"url":"https://github.com/ertgl/babel-plugin-transform-import-source","last_synced_at":"2025-08-22T09:39:14.467Z","repository":{"id":269487999,"uuid":"907469775","full_name":"ertgl/babel-plugin-transform-import-source","owner":"ertgl","description":"Babel plugin for transforming import sources.","archived":false,"fork":false,"pushed_at":"2025-04-08T10:08:43.000Z","size":707,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-09T22:15:31.433Z","etag":null,"topics":["babel","babel-plugin","dual-package","javascript","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ertgl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-23T16:47:26.000Z","updated_at":"2025-04-08T10:08:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"054b20da-9cee-4877-a774-67eb7b0330ef","html_url":"https://github.com/ertgl/babel-plugin-transform-import-source","commit_stats":null,"previous_names":["ertgl/babel-plugin-transform-import-source"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fbabel-plugin-transform-import-source","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fbabel-plugin-transform-import-source/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fbabel-plugin-transform-import-source/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fbabel-plugin-transform-import-source/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ertgl","download_url":"https://codeload.github.com/ertgl/babel-plugin-transform-import-source/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119290,"owners_count":21050755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel","babel-plugin","dual-package","javascript","typescript"],"created_at":"2024-12-28T01:16:51.716Z","updated_at":"2025-04-09T22:15:37.098Z","avatar_url":"https://github.com/ertgl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# babel-plugin-transform-import-source\n\n[Babel](https://babeljs.io/) plugin for transforming import sources.\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [JSON Configuration](#json-configuration)\n  - [Low-Level API](#low-level-api)\n    - [Technical Details](#technical-details)\n    - [Transformer Context](#transformer-context)\n  - [Module Methods](#module-methods)\n    - [Type Definitions for Module Methods](#type-definitions-for-module-methods)\n  - [Magic Comments](#magic-comments)\n- [Options](#options)\n  - [`moduleMethods`](#modulemethods)\n  - [`transform`](#transform)\n  - [`transformer`](#transformer)\n- [Development Notes](#development-notes)\n  - [Build Process](#build-process)\n- [License](#license)\n\n## Overview\n\nThis plugin allows transforming import sources in the `require`, `import`, and\n`export` statements either based on a set of custom rules or by applying a\ncustom function. It can be useful for modifying import sources to match the\noutput file extensions, or manipulating query parameters based on certain\nconditions at compile-time, etc.\n\n## Features\n\n- Transform import sources in `require/import` expressions and `import/export`\n  statements\n- Transform import sources outside of the statements with compile-time macros\n- Resolve index file when the import source refers to a directory\n- Exclude certain import sources to be transformed, with magic comments\n- Handle multiple rules to match and replace import sources with different\n  patterns\n- Force absolute paths to bypass test conditions (easy cross-platform support)\n- Take advantage of the low-level API to implement completely custom behavior\n\n## Installation\n\nInstall the plugin using npm or another package manager:\n\n```sh\nnpm install -D babel-plugin-transform-import-source\n```\n\n## Usage\n\nAdd the plugin to the Babel configuration and specify the transformation rules\nin the options object.\n\n```js\nmodule.exports = {\n  plugins: [\n    [\n      require.resolve(\"babel-plugin-transform-import-source\"),\n      {\n        transform: {\n          rules: [\n            {\n              // Apply this rule to explicit relative paths only.\n              test: /^[.\\\\/]+.*$/,\n              // Bypass the test condition to match absolute paths without\n              // any restrictions.\n              includeAbsolute: true,\n              // Find either the extension part or the end of the string.\n              //\n              // `$` character matches the end of the string.\n              // `?` character makes the pattern optional.\n              //\n              // As a result, this will cause the transformation to\n              // either replace the extension part with `.mjs` or, append\n              // `.mjs` to the end of the string if there is no extension.\n              //\n              // For example, the import source `./file.ts` will be\n              // transformed to `./file.mjs`, and the import source\n              // `./file` will be transformed to `./file.mjs` as well.\n              find: /(?:\\.[cm]?[jt]s[x]?)?$/iu,\n              replace: \".mjs\",\n              // If the import source refers to a directory,\n              // first append `index` suffix to the import source,\n              // then apply the transformation.\n              //\n              // This will cause the transformation to replace an import\n              // source like `./dir` with `./dir/index.mjs`, assuming\n              // `./dir` refers to a directory in this example.\n              resolveIndex: true,\n            },\n          ],\n        },\n      },\n    ],\n  ],\n};\n```\n\n### JSON Configuration\n\nIn case of using JSON configuration, the RegExp patterns can be provided as\nobjects containing the `regexp` and optional `flags` properties.\n\n```json\n{\n  \"plugins\": [\n    [\n      \"babel-plugin-transform-import-source\",\n      {\n        \"transform\": {\n          \"rules\": [\n            {\n              \"test\": {\n                \"regexp\": \"^[.\\\\\\\\/]+.*$\"\n              },\n              \"includeAbsolute\": true,\n              \"find\": {\n                \"regexp\": \"(?:\\\\.[cm]?[jt]s[x]?)?$\",\n                \"flags\": \"iu\"\n              },\n              \"replace\": \".mjs\",\n              \"resolveIndex\": true\n            }\n          ]\n        }\n      }\n    ]\n  ]\n}\n```\n\n### Low-Level API\n\nThe plugin can also be used with a custom function to transform import sources.\n\n```js\nconst { createDefaultTransformer } = require(\"babel-plugin-transform-import-source\");\n\n/**\n * @import { type TransformerContext } from \"babel-plugin-transform-import-source\";\n */\n\n// The default transformer function can be re-used in a custom one.\nconst superTransform = createDefaultTransformer();\n\nmodule.exports = {\n  plugins: [\n    [\n      require.resolve(\"babel-plugin-transform-import-source\"),\n      {\n        transformer: (\n          /**\n           * @type {TransformerContext}\n           */\n          context\n        ) =\u003e\n        {\n          // For example, Babel API or the default transformer function\n          // can be used depending on certain conditions.\n          if (condition)\n          {\n            context.nodePath.replaceWith(context.api.types.nullLiteral());\n            return undefined;\n          }\n          else if (otherCondition)\n          {\n            return `${context.importSource}?enterprise=true\u0026oss=false`;\n          }\n          return superTransform(context);\n        },\n      },\n    ],\n  ],\n};\n```\n\n#### Technical Details\n\nThe plugin handles the following types of AST nodes:\n\n- `CallExpression`\n- `ExportAllDeclaration`\n- `ExportNamedDeclaration`\n- `ImportDeclaration`\n- `ImportExpression`\n\nThe transformer function is called for each import source in the AST nodes\nmentioned above. The function receives a context object containing the\ninformation about the current import source and the AST node. See the\ntransformer context definition below for more information.\n\n#### Transformer Context\n\nThe transformer context object contains the following properties:\n\n- **dirname**: The directory path of the current file.\n- **filename**: The file path of the current file.\n- **importSource**: The import source.\n- **importSourceNode**: AST node of the import source.\n- **nodePath**: Current AST node path.\n- **options**: Plugin options.\n- **state**: Plugin state.\n\n### Module Methods\n\nThe plugin also provides compile-time `.transform` module-methods to transform\nimport sources outside of the statements.\n\nWhen the feature is enabled, the `.transform` macros can be accessed via the\n`import.meta` object or the `require` function.\n\n```js\n// Transforms `./file.ts` and returns the result.\nimport.meta.transform(\"./file.ts\");\n\n// Same as above.\nrequire.transform(\"./file.ts\");\n```\n\n#### Type Definitions for Module Methods\n\nThe type definitions for the module methods with the default property names are\nprovided in the `env.d.ts` file. They can be imported with a `reference`\ndirective.\n\nFor custom property names, TypeScript's interface merging feature can be used\nto extend the default type definitions.\n\n```ts\ndeclare module \"module\"\n{\n  declare global\n  {\n    interface ImportMeta\n    {\n      transform(source: string): string;\n    }\n  }\n}\n\ndeclare namespace NodeJS\n{\n  interface Require\n  {\n    transform(source: string): string;\n  }\n}\n```\n\n### Magic Comments\n\nTo exclude certain import sources from being transformed,\n`@babel-plugin-transform-import-source-ignore` directive can be used in\na leading comment line.\n\n```js\n// The import source will remain unchanged.\n// @babel-plugin-transform-import-source-ignore\nimport \"./file.ts\";\n\n// Same as above.\nrequire(\n  // @babel-plugin-transform-import-source-ignore\n  \"./file.ts\",\n);\n\n// The import source here will remain unchanged as well.\n// And the expression will return `./file.ts` as it is.\nimport.meta.transform(\n  // @babel-plugin-transform-import-source-ignore\n  \"./file.ts\",\n);\n```\n\n## Options\n\nThe plugin accepts an options object with the following properties.\n\n### `moduleMethods`\n\n- **Type:** `object`\n- **Description:** Configuration for the module methods.\n- **Properties:**\n  - `transformImportSource`: (Optional) Configuration for the compile-time\n  `.transform` methods.\n    - **Type:** `object`\n    - **Properties:**\n      - `importMeta`: (Optional) A boolean to enable/disable the\n        `import.meta.transform` macro. Or an `object` with `propertyName`\n        property to specify the custom property name. Default is `false`.\n      - `require`: (Optional) A boolean to enable/disable the `require.transform`\n        macro. Or an `object` with `propertyName` property to specify the custom\n        property name. Default is `false`.\n\n### `transform`\n\n- **Type:** `object`\n- **Description:** Configuration for transforming import sources.\n- **Properties:**\n  - `rules`: An array of transformation rules.\n\n#### Rule\n\nA transformation rule object can have the following properties:\n\n- **find**: A string or RegExp to match the import source to replace.\n- **replace**: A string to replace the matched import source.\n- **includeAbsolute**: (Optional) A boolean to include absolute paths by\n  bypassing the test.\n- **resolveIndex**: (Optional) A boolean to resolve index file when the import\n  source refers to a directory.\n- **test**: (Optional) A string or RegExp to test the import source.\n\n### `transformer`\n\n- **Type:** `function`\n- **Description:** A custom function to transform import sources.\n- **Arguments:**\n  - `context`: The transformer context object.\n- **Returns:** The transformed import source. If the function returns\n  `null` or `undefined`, the import source remains unchanged.\n\n## Development Notes\n\nThe plugin is written in TypeScript and compiled to both CommonJS and\nECMAScript modules, and the import sources are transformed to match the output\nfile extensions accordingly. The following notes are not specific to the plugin\nonly, but they can be helpful for understanding the motivation behind the\nplugin.\n\nTargeting both CommonJS and ECMAScript modules in a single package is a common\nuse case to provide compatibility with different environments and tools.\nAchieving this usually requires workarounds to ensure that the module types are\nresolved correctly in different environments. These workarounds are like\ndefining `type` fields in `package.json` files\n(e.g., `\"type\": \"commonjs\"` or `\"type\": \"module\"`), or writing wrapper scripts\naround the main entry points. Since using wrapper scripts can be helpful in\navoiding\n[dual-package hazards](https://github.com/nodejs/package-examples#dual-package-hazard),\nthey can also reduce the static evaluation of the package and make it less\noptimizable (assuming that the module type is CommonJS at the beginning).\n\nBy using `.cjs` or `.mjs` file extensions, the module type becomes explicit and\nindependent of the environment. This allows the module type to be recognized as\na CommonJS or ECMAScript module without relying on the environment or\nconfiguration files like `package.json`. Instead, it can be determined by the\nfile extension itself. This can be beneficial in many ways, such as making the\npackage more portable and less error-prone in different environments.\n\nWhile CommonJS allows import paths to be written without file extensions,\nECMAScript spec requires import paths to be fully specified with explicit file\nextensions. This mismatch can cause issues in certain scenarios, such as when\na package is written in TypeScript with extensionless imports, and then it's\ncompiled and consumed by ECMAScript modules, etc.\n\nTo make the module types explicit while compiling with Babel, the\n`--out-file-extension` flag can be used to set the output file extensions\n(e.g., `--out-file-extension '.cjs'` or `--out-file-extension '.mjs'`).\nHowever, -according to my tests- Babel does not use this flag for import\nsources in statements to be transformed respectively. So, the import sources\nremain unchanged, while extensions of the output files are modified. This can\ncause the exact issue mentioned above as well.\n\nThe plugin can be used to fill this gap for transforming the import sources in\nthe statements to make them match the output file extensions. This way, the\nmodule types are resolved correctly in both CommonJS and ECMAScript modules,\nand the package becomes more consistent and reliable.\n\n### Build Process\n\nThe plugin uses itself to transform its own import source strings to target the\ndesired module type explicitly.\n\nTo make the plugin able to use itself in its own build process, there is a\n[bootstrap phase](https://en.wikipedia.org/wiki/Bootstrapping_(compilers))\nthat compiles the plugin to CommonJS modules first. Since the format of the\nimport paths is extensionless in the source code, and this is acceptable in\nCommonJS modules, they don't need to be changed in the output files. But, at\nthis step, the output file names have `.js` extension. Although the default\nmodule type in Node.js is CommonJS, the `type` field in the `package.json` file\nis set to `\"type\": \"commonjs\"` to ensure that the files are recognized as\nCommonJS modules in any case. Finally, these compiled files can be imported\ninto the Babel configurations successfully.\n\nAfter the bootstrap phase, the actual build process gets started. At this\npoint, the early version of the plugin that obtained from the bootstrap phase\nis used as a part of the build process to transform the import sources in the\nstatements to match the output file extensions.\n\nThe process can be observed in the following steps:\n\n1. The `bootstrap::cjs` script defined in the [`package.json`](package.json)\nfile gets executed to compile the plugin to CommonJS modules using the\n[`babel.config.bootstrap.cjs`](babel.config.bootstrap.cjs) configuration file.\n2. [`babel.config.cjs.cjs`](babel.config.cjs.cjs) and\n[`babel.config.esm.cjs`](babel.config.esm.cjs) configuration files require the\nCommonJS modules compiled in the bootstrap phase. This is where the plugin uses\nitself to compile its own source code to target the desired module types\nexplicitly.\n3. The `build::cjs` and `build::esm` scripts defined in the\n[`package.json`](package.json) file get executed to compile the plugin to both\nCommonJS and ECMAScript modules using the corresponding configuration files\nmentioned in the previous step.\n\n## License\n\nThis project is licensed under the\n[MIT License](https://opensource.org/license/mit).\n\nSee the [LICENSE](LICENSE) file for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fbabel-plugin-transform-import-source","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fertgl%2Fbabel-plugin-transform-import-source","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fbabel-plugin-transform-import-source/lists"}