Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
New: Transform relative import paths 🤩 (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
DHedgecock committed May 20, 2020
1 parent 98429ab commit 0625df0
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 31 deletions.
31 changes: 31 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
// Ref: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest(Service): Single File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["${relativeFile}"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"env": {
"NODE_ENV": "test"
}
},
{
"type": "node",
"request": "launch",
"name": "Jest(Service): Test Suite",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"env": {
"NODE_ENV": "test"
}
}
]
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"test:unit": "jest src",
"format": "prettier --write '*.{js,json,md}' '**/*.{js,json,md,mdx,scss}' !CHANGELOG.md"
},
"dependencies": {
"@babel/types": "^7.9.6"
},
"devDependencies": {
"@babel/core": "7.9.0",
"@crystal-ball/commitizen-base": "2.6.0",
Expand Down
4 changes: 2 additions & 2 deletions src/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`transforms imports 1`] = `
"import Rad from \\"/User/Rad/Code/src/dux/rad\\";
"import Rad from \\"../../dux/rad\\";
import { css } from '@emotion/core';
import { ENV } from \\"/tmp/env/dev\\";
const HomeScreen = import(
/* webpackChunkName: \\"HomeScreen\\" */
\\"/User/Rad/Code/src/screens/HomeScreen/HomeScreen\\");
\\"../../screens/HomeScreen/HomeScreen\\");
function load() {}
Expand Down
74 changes: 46 additions & 28 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,60 @@
'use strict'

module.exports = function transformAlias({ types }) {
/**
* Check if the import value matches a configured alias and replace with
* alias path if matches
* @param {Object} aliases
* @param {Object} importSource
* @param {string} value
*/
function checkAndReplaceImport(aliases, importSource, value) {
Object.keys(aliases).forEach((alias) => {
if (value.startsWith(`${alias}/`)) {
importSource.replaceWith(
types.stringLiteral(value.replace(alias, aliases[alias])),
const t = require('@babel/types')

/**
* Check if the import value matches a configured alias and replace with
* alias path if matches
* @param {Object} aliases Import alias to paths configuration map
* @param {Object} importStringLiteral The StringLiteral node of the import expression
* @param {string} filename Full path to the file currently being compiled
*/
function checkAndReplaceImport(aliases, importStringLiteral, filename) {
const { value: originalImportPath } = importStringLiteral.node

Object.keys(aliases).forEach((alias) => {
if (originalImportPath.startsWith(`${alias}/`)) {
const aliasPath = aliases[alias]

// Relative to alias import
if (filename.startsWith(aliasPath)) {
const aliasRelativePath = filename.replace(aliasPath, '')
const directories = aliasRelativePath.match(/\//g)
// There's an extra directory from the filename in the path that needs
// to be ignored
directories.pop()
const relativePath = directories.map(() => '../').join('')
// Replace alias + /
importStringLiteral.replaceWith(
t.stringLiteral(originalImportPath.replace(`${alias}/`, relativePath)),
)
} else {
importStringLiteral.replaceWith(
t.stringLiteral(originalImportPath.replace(alias, aliasPath)),
)
}
})
}
}
})
}

module.exports = function transformImportAliases() {
return {
visitor: {
ImportDeclaration(path, state) {
const { aliases } = state.opts
const importSource = path.get('source')
const { value } = importSource.node
ImportDeclaration(path, { filename, opts }) {
const { aliases } = opts

checkAndReplaceImport(aliases, importSource, value)
// In import declaration the import string is the source,
// eg `import utils from '@/utils'`
checkAndReplaceImport(aliases, path.get('source'), filename)
},

CallExpression(path, state) {
const { aliases } = state.opts
const callee = path.get('callee')

if (callee.isImport()) {
const importSource = path.get('arguments.0')
const { value } = importSource.node
CallExpression(path, { filename, opts }) {
const { aliases } = opts

checkAndReplaceImport(aliases, importSource, value)
if (path.get('callee').isImport()) {
// In dynamic imports the import string is the first argument to
// `import`, eg: import('@/utils')
checkAndReplaceImport(aliases, path.get('arguments.0'), filename)
}
},
},
Expand Down
4 changes: 3 additions & 1 deletion src/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ const FakeScreen = load(/* webpackChunkName: "HomeScreen" */ '@/screens/HomeScre
`

it('transforms imports', () => {
const { code } = babel.transform(content, {
const { code } = babel.transformSync(content, {
plugins: [[plugin, { aliases: { '@': '/User/Rad/Code/src', 'ENV': '/tmp/env' } }]],
// Tests the relative alias transforms
filename: '/User/Rad/Code/src/components/App/App.ts',
})
expect(code).toMatchSnapshot()
})

0 comments on commit 0625df0

Please sign in to comment.