├── .babelrc ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── node.js.yml ├── .gitignore ├── .npmrc ├── license ├── package.json ├── readme.md └── src ├── babel6.spec.js ├── babel7.spec.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.js] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [{package.json,.travis.yml}] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.x, 12.x, 14.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm run build --if-present 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | package-lock.json 36 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sigurd Fosseng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-rename-import", 3 | "version": "2.3.0", 4 | "description": "Replace import sources", 5 | "main": "lib/index", 6 | "scripts": { 7 | "build": "babel src/. -d lib/. --ignore=spec.js", 8 | "test:babel6": "node -r @babel/register src/babel6.spec.js", 9 | "test:babel7": "node -r @babel/register src/babel7.spec.js", 10 | "test": "npm-run-all test:*", 11 | "prepublish": "npm run build" 12 | }, 13 | "author": { 14 | "name": "Sigurd Fosseng", 15 | "email": "sigurd@fosseng.net", 16 | "url": "http://laat.io" 17 | }, 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/laat/babel-plugin-transform-rename-import.git" 22 | }, 23 | "devDependencies": { 24 | "@babel/cli": "^7.0.0", 25 | "@babel/core": "^7.0.0", 26 | "@babel/node": "^7.0.0", 27 | "@babel/preset-env": "^7.0.0", 28 | "@babel/register": "^7.0.0", 29 | "@babel/traverse": "^7.0.0", 30 | "assert-simple-tap": "^3.0.0", 31 | "babel-core": "^6.26.3", 32 | "npm-run-all": "^4.0.2", 33 | "prettier": "^3.0.0" 34 | }, 35 | "files": [ 36 | "lib" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transform-rename-import [![npm][npm-image]][npm-url] 2 | 3 | [npm-image]: https://img.shields.io/npm/v/babel-plugin-transform-rename-import.svg?style=flat 4 | [npm-url]: https://npmjs.org/package/babel-plugin-transform-rename-import 5 | 6 | > replace import sources 7 | 8 | ## Install 9 | 10 | ``` 11 | $ npm install --save babel-plugin-transform-rename-import 12 | ``` 13 | 14 | ## babelrc 15 | 16 | ```js 17 | { 18 | "plugins": [["transform-rename-import", { original: 'assert', replacement: 'power-assert' }]] 19 | } 20 | ``` 21 | 22 | or multiple replacements: 23 | 24 | ```js 25 | { 26 | "plugins": [ 27 | ["transform-rename-import", { 28 | replacements: [ 29 | { original: 'replace-me', replacement: 'replaced' }, 30 | { original: 'replace-me2', replacement: 'replaced2' } 31 | ] 32 | } 33 | ]] 34 | } 35 | ``` 36 | 37 | RegExp: 38 | 39 | ```js 40 | { 41 | "plugins": [["transform-rename-import", { original: '^(.+?)\\.less$', replacement: '$1.css' }]] 42 | } 43 | ``` 44 | 45 | ## Programatic Usage 46 | 47 | ```javascript test 48 | import plugin from "babel-plugin-transform-rename-import"; 49 | import { transform } from "@babel/core"; 50 | 51 | function replace(code, original, replacement) { 52 | return transform(code, { 53 | babelrc: false, 54 | plugins: [[plugin, { original, replacement }]] 55 | }).code; 56 | } 57 | 58 | replace("require('foo')", "foo", "bar"); 59 | //=> 'require("bar");' 60 | 61 | replace("import foo from 'foo'", "foo", "bar"); 62 | //=> 'import foo from "bar";' 63 | 64 | replace("require('foo/thingy')", "foo", "bar"); 65 | //=> 'require("bar/thingy");' 66 | 67 | replace("require('foo/thingy.less')", "^(.+?)\\.less$", "$1.css"); 68 | //=> 'require("foo/thingy.css");' 69 | ``` 70 | 71 | ## License 72 | 73 | MIT © [Sigurd Fosseng](https://github.com/laat) 74 | -------------------------------------------------------------------------------- /src/babel6.spec.js: -------------------------------------------------------------------------------- 1 | import * as babel from "babel-core"; 2 | import assert from "assert-simple-tap"; 3 | import plugin from "./index"; 4 | 5 | const testGeneration = (message, code, expectedCode) => { 6 | const transformedCode = babel.transform(code, { 7 | babelrc: false, 8 | plugins: [[plugin, { replacement: ".", original: "foobar" }]] 9 | }).code; 10 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 11 | }; 12 | 13 | testGeneration( 14 | "replace normal imports", 15 | ` 16 | import foo from 'foobar'; 17 | `, 18 | ` 19 | import foo from '.'; 20 | ` 21 | ); 22 | 23 | testGeneration( 24 | "replace * imports", 25 | ` 26 | import * as foo from 'foobar'; 27 | `, 28 | ` 29 | import * as foo from '.'; 30 | ` 31 | ); 32 | 33 | testGeneration( 34 | "replace {} imports", 35 | ` 36 | import { foo } from 'foobar'; 37 | `, 38 | ` 39 | import { foo } from '.'; 40 | ` 41 | ); 42 | 43 | testGeneration( 44 | "replace {default as foobar} imports", 45 | ` 46 | import { default as foobar } from 'foobar'; 47 | `, 48 | ` 49 | import { default as foobar } from '.'; 50 | ` 51 | ); 52 | 53 | testGeneration( 54 | "replace require", 55 | ` 56 | require('foobar') 57 | `, 58 | ` 59 | require('.'); 60 | ` 61 | ); 62 | 63 | testGeneration( 64 | "support addressing files in module", 65 | ` 66 | require('foobar/file'); 67 | `, 68 | ` 69 | require('./file'); 70 | ` 71 | ); 72 | 73 | testGeneration( 74 | "support importing of files within a module", 75 | ` 76 | import foo from 'foobar/file'; 77 | `, 78 | ` 79 | import foo from './file'; 80 | ` 81 | ); 82 | 83 | testGeneration( 84 | "support importing inside export statement", 85 | ` 86 | export { something } from 'foobar'; 87 | `, 88 | ` 89 | export { something } from '.'; 90 | ` 91 | ); 92 | 93 | const testMultipleReplacements = (message, code, expectedCode) => { 94 | const transformedCode = babel.transform(code, { 95 | babelrc: false, 96 | plugins: [ 97 | [ 98 | plugin, 99 | [ 100 | { replacement: ".", original: "foobar" }, 101 | { replacement: "baz", original: "bar" } 102 | ] 103 | ] 104 | ] 105 | }).code; 106 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 107 | }; 108 | 109 | testMultipleReplacements( 110 | "support importing of files within a module", 111 | ` 112 | import foo from 'bar'; 113 | require('foobar'); 114 | `, 115 | ` 116 | import foo from 'baz'; 117 | require('.'); 118 | ` 119 | ); 120 | 121 | const testMultipleReplacementsBabel7 = (message, code, expectedCode) => { 122 | const transformedCode = babel.transform(code, { 123 | babelrc: false, 124 | plugins: [ 125 | [ 126 | plugin, 127 | { 128 | replacements: [ 129 | { replacement: ".", original: "foobar" }, 130 | { replacement: "baz", original: "bar" } 131 | ] 132 | } 133 | ] 134 | ] 135 | }).code; 136 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 137 | }; 138 | 139 | testMultipleReplacementsBabel7( 140 | "support importing of files within a module", 141 | ` 142 | import foo from 'bar'; 143 | require('foobar'); 144 | `, 145 | ` 146 | import foo from 'baz'; 147 | require('.'); 148 | ` 149 | ); 150 | 151 | const testRegexp = (message, { original, replacement }, code, expectedCode) => { 152 | const transformedCode = babel.transform(code, { 153 | babelrc: false, 154 | plugins: [[plugin, [{ replacement, original }]]] 155 | }).code; 156 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 157 | }; 158 | 159 | testRegexp( 160 | "replaces with RegExp", 161 | { 162 | original: "^(.+?)\\.less$", 163 | replacement: "$1.css" 164 | }, 165 | ` 166 | import css1 from './foo.less'; 167 | const css2 = require('../bar.less'); 168 | `, 169 | ` 170 | import css1 from './foo.css'; 171 | const css2 = require('../bar.css'); 172 | ` 173 | ); 174 | -------------------------------------------------------------------------------- /src/babel7.spec.js: -------------------------------------------------------------------------------- 1 | import * as babel from "@babel/core"; 2 | import assert from "assert-simple-tap"; 3 | import plugin from "./index"; 4 | 5 | const testGeneration = (message, code, expectedCode) => { 6 | const transformedCode = babel.transform(code, { 7 | babelrc: false, 8 | plugins: [[plugin, { replacement: ".", original: "foobar" }]], 9 | }).code; 10 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 11 | }; 12 | 13 | testGeneration( 14 | "replace normal imports", 15 | ` 16 | import foo from "foobar"; 17 | `, 18 | ` 19 | import foo from "."; 20 | `, 21 | ); 22 | 23 | testGeneration( 24 | "replace * imports", 25 | ` 26 | import * as foo from "foobar"; 27 | `, 28 | ` 29 | import * as foo from "."; 30 | `, 31 | ); 32 | 33 | testGeneration( 34 | "replace {} imports", 35 | ` 36 | import { foo } from "foobar"; 37 | `, 38 | ` 39 | import { foo } from "."; 40 | `, 41 | ); 42 | 43 | testGeneration( 44 | "replace {default as foobar} imports", 45 | ` 46 | import { default as foobar } from "foobar"; 47 | `, 48 | ` 49 | import { default as foobar } from "."; 50 | `, 51 | ); 52 | 53 | testGeneration( 54 | "replace require", 55 | ` 56 | require("foobar") 57 | `, 58 | ` 59 | require("."); 60 | `, 61 | ); 62 | 63 | testGeneration( 64 | "support addressing files in module", 65 | ` 66 | require("foobar/file"); 67 | `, 68 | ` 69 | require("./file"); 70 | `, 71 | ); 72 | 73 | testGeneration( 74 | "support importing of files within a module", 75 | ` 76 | import foo from "foobar/file"; 77 | `, 78 | ` 79 | import foo from "./file"; 80 | `, 81 | ); 82 | 83 | testGeneration( 84 | "support importing inside export statement", 85 | ` 86 | export { something } from "foobar"; 87 | `, 88 | ` 89 | export { something } from "."; 90 | `, 91 | ); 92 | 93 | const testMultipleReplacements = (message, code, expectedCode) => { 94 | const transformedCode = babel.transform(code, { 95 | babelrc: false, 96 | plugins: [ 97 | [ 98 | plugin, 99 | { 100 | replacements: [ 101 | { replacement: ".", original: "foobar" }, 102 | { replacement: "baz", original: "bar" }, 103 | ], 104 | }, 105 | ], 106 | ], 107 | }).code; 108 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 109 | }; 110 | 111 | testMultipleReplacements( 112 | "support importing of files within a module", 113 | ` 114 | import foo from "bar"; 115 | require("foobar"); 116 | `, 117 | ` 118 | import foo from "baz"; 119 | require("."); 120 | `, 121 | ); 122 | 123 | const testMultipleReplacementsBabel7 = (message, code, expectedCode) => { 124 | const transformedCode = babel.transform(code, { 125 | babelrc: false, 126 | plugins: [ 127 | [ 128 | plugin, 129 | { 130 | replacements: [ 131 | { replacement: ".", original: "foobar" }, 132 | { replacement: "baz", original: "bar" }, 133 | ], 134 | }, 135 | ], 136 | ], 137 | }).code; 138 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 139 | }; 140 | 141 | testMultipleReplacementsBabel7( 142 | "support importing of files within a module", 143 | ` 144 | import foo from "bar"; 145 | require("foobar"); 146 | `, 147 | ` 148 | import foo from "baz"; 149 | require("."); 150 | `, 151 | ); 152 | 153 | const testRegexp = (message, { original, replacement }, code, expectedCode) => { 154 | const transformedCode = babel.transform(code, { 155 | babelrc: false, 156 | plugins: [[plugin, { replacements: [{ replacement, original }] }]], 157 | }).code; 158 | assert.equal(transformedCode.trim(), expectedCode.trim(), message); 159 | }; 160 | 161 | testRegexp( 162 | "replaces with RegExp", 163 | { 164 | original: "^(.+?)\\.less$", 165 | replacement: "$1.css", 166 | }, 167 | ` 168 | import css1 from "./foo.less"; 169 | const css2 = require("../bar.less"); 170 | `, 171 | ` 172 | import css1 from "./foo.css"; 173 | const css2 = require("../bar.css"); 174 | `, 175 | ); 176 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | function isModule(value, original) { 3 | const pattern = new RegExp(`^(${original}|${original}/.*)$`); 4 | return pattern.test(value); 5 | } 6 | 7 | function replace(value, original, replacement) { 8 | const pattern = new RegExp(`^${original}`); 9 | return value.replace(pattern, replacement); 10 | } 11 | 12 | function getReplacements(state) { 13 | if (state.opts instanceof Array) { 14 | return state.opts; 15 | } else if (state.opts && state.opts.replacements instanceof Array) { 16 | return state.opts.replacements; 17 | } 18 | return [state.opts]; 19 | } 20 | 21 | export default function visitor({ types: t }) { 22 | const source = (value, original, replacement) => 23 | t.stringLiteral(replace(value, original, replacement)); 24 | return { 25 | visitor: { 26 | ImportDeclaration(path, state) { 27 | const replacements = getReplacements(state); 28 | replacements.forEach(({ original, replacement }) => { 29 | const { value } = path.node.source; 30 | if (isModule(value, original)) { 31 | path.node.source = source(value, original, replacement); 32 | } 33 | }); 34 | }, 35 | 36 | ExportDeclaration(path, state) { 37 | const replacements = getReplacements(state); 38 | replacements.forEach(({ original, replacement }) => { 39 | const { node } = path; 40 | if (node.source && isModule(node.source.value, original)) { 41 | path.node.source = source(node.source.value, original, replacement); 42 | } 43 | }); 44 | }, 45 | 46 | CallExpression(path, state) { 47 | const replacements = getReplacements(state); 48 | replacements.forEach(({ original, replacement }) => { 49 | const { node } = path; 50 | if ( 51 | node.callee.name === "require" && 52 | node.arguments && 53 | node.arguments.length === 1 && 54 | t.isStringLiteral(node.arguments[0]) && 55 | isModule(node.arguments[0].value, original) 56 | ) { 57 | path.node.arguments = [ 58 | source(node.arguments[0].value, original, replacement) 59 | ]; 60 | } 61 | }); 62 | } 63 | } 64 | }; 65 | } 66 | --------------------------------------------------------------------------------