babel-plugin-esm-resolver
Advanced tools
Comparing version 1.2.0 to 2.0.0
215
lib/index.js
@@ -16,2 +16,26 @@ "use strict"; | ||
let moduleIsBuiltinCache = null; | ||
function moduleIsBuiltin(name) { | ||
// Node v9.3.0+ | ||
const list = _module.default.builtinModules; | ||
if (list) { | ||
moduleIsBuiltinCache = moduleIsBuiltinCache || new Set(list); | ||
return moduleIsBuiltinCache.has(name); | ||
} // Older versions: | ||
if (_module.default._resolveFilename) { | ||
try { | ||
return _module.default._resolveFilename(name) === name; | ||
} catch (err) {// Do nothing. | ||
} | ||
return false; | ||
} | ||
throw new Error('Cannot lookup builtin modules'); | ||
} | ||
function pathStat(path) { | ||
@@ -26,11 +50,20 @@ try { | ||
function importPathIsUrl(path) { | ||
function readJson(path) { | ||
// eslint-disable-next-line no-sync | ||
return JSON.parse(_fs.default.readFileSync(path, 'utf8')); | ||
} | ||
function trimDotSlash(path) { | ||
return path.replace(/^(\.\/)+/, ''); | ||
} | ||
function importIsUrl(path) { | ||
return /^[^/]+:\/\//.test(path); | ||
} | ||
function importPathIsFile(path) { | ||
function importIsFile(path) { | ||
return path === '.' || path === '..' || /^\.?\.?\//.test(path); | ||
} | ||
function parseModulePath(path) { | ||
function importBareParse(path) { | ||
const ns = path.match(/^(@[^/]+\/[^/]+)([\s\S]*)$/); | ||
@@ -57,3 +90,3 @@ | ||
function mapExtensions(e) { | ||
function expandExtensions(e) { | ||
let src = null; | ||
@@ -77,18 +110,2 @@ let dst = null; | ||
function optExtentions(opts) { | ||
return (opts.extensions || []).map(mapExtensions); | ||
} | ||
function optExtentionsSubmodule(opts) { | ||
return (opts.extensionsSubmodule || []).map(mapExtensions); | ||
} | ||
function optIgnoreUnresolved(opts) { | ||
return opts.ignoreUnresolved || false; | ||
} | ||
function optIgnoreUnresolvedSubmodule(opts) { | ||
return opts.ignoreUnresolvedSubmodule || false; | ||
} | ||
function modulePathsForFile(file) { | ||
@@ -120,6 +137,6 @@ // Node v12.2.0+: | ||
return null; | ||
throw new Error(`Failed to resolve directory for module: ${name} in: ${file}`); | ||
} | ||
function resolveExtension(base, extensions) { | ||
function resolveExtension(base, extensions, expand = false) { | ||
const stat = pathStat(base); | ||
@@ -137,7 +154,10 @@ const paths = ['']; | ||
for (const path of paths) { | ||
for (const _ref of extensions) { | ||
for (const extension of extensions) { | ||
const { | ||
srcs, | ||
dst | ||
} = _ref; | ||
} = expand ? expandExtensions(extension) : { | ||
srcs: [extension], | ||
dst: extension | ||
}; | ||
@@ -157,3 +177,3 @@ for (const src of srcs) { | ||
function visitDeclarationFilePath(nodePath, state) { | ||
function visitDeclarationPath(nodePath, state) { | ||
const { | ||
@@ -164,6 +184,11 @@ source | ||
const opts = state.opts || {}; | ||
const extensions = optExtentions(opts); | ||
const ignoreUnresolved = optIgnoreUnresolved(opts); // Resolve the file base. | ||
const optsSource = (state.opts || {}).source; | ||
if (!optsSource) { | ||
return; | ||
} | ||
const extensions = optsSource.extensions || []; | ||
const ignoreUnresolved = optsSource.ignoreUnresolved || false; // Resolve the file base. | ||
const { | ||
@@ -176,3 +201,3 @@ filename | ||
const resolved = resolveExtension(resolveBase, extensions); | ||
const resolved = resolveExtension(resolveBase, extensions, true); | ||
@@ -188,3 +213,3 @@ if (resolved === null) { | ||
function visitDeclarationModulePath(nodePath, state, modulePath) { | ||
function visitDeclarationBarePath(nodePath, state, bareImport) { | ||
const { | ||
@@ -195,22 +220,18 @@ source | ||
const opts = state.opts || {}; | ||
const extensions = optExtentionsSubmodule(opts); | ||
const ignoreUnresolved = optIgnoreUnresolvedSubmodule(opts); // Resolve the module base, or fail. | ||
const optsSubmodule = (state.opts || {}).submodule; | ||
if (!optsSubmodule) { | ||
return; | ||
} | ||
const extensions = optsSubmodule.extensions || []; | ||
const ignoreUnresolved = optsSubmodule.ignoreUnresolved || false; // Resolve the module base, or fail. | ||
const { | ||
filename | ||
} = state.file.opts; | ||
const moduleName = modulePath.name; | ||
const moduleDir = resolveModuleDir(moduleName, filename); | ||
const moduleName = bareImport.name; | ||
const moduleDir = resolveModuleDir(moduleName, filename); // Resolve the file then resolve extension. | ||
if (!moduleDir) { | ||
if (!ignoreUnresolved) { | ||
throw new Error(`Failed to resolve module: ${src} in: ${filename}`); | ||
} | ||
return; | ||
} // Resolve the file then resolve extension. | ||
const resolveBase = `${moduleDir}${modulePath.path}`; | ||
const resolveBase = `${moduleDir}${bareImport.path}`; | ||
const resolved = resolveExtension(resolveBase, extensions); | ||
@@ -230,2 +251,81 @@ | ||
function visitDeclarationBareMain(nodePath, state, bareImport) { | ||
// Parse options. | ||
const optsModule = (state.opts || {}).module; | ||
if (!optsModule) { | ||
return; | ||
} | ||
const entry = optsModule.entry || []; | ||
if (!entry.length) { | ||
return; | ||
} // Ignore module if a builtin module. | ||
const moduleName = bareImport.name; | ||
if (moduleIsBuiltin(moduleName)) { | ||
return; | ||
} // Resolve the module base, or fail. | ||
const { | ||
filename | ||
} = state.file.opts; | ||
const moduleDir = resolveModuleDir(moduleName, filename); // Try different entry resolvers. | ||
let pkg = null; | ||
for (const info of entry) { | ||
const entryType = info.type; | ||
let filePath = null; | ||
let extensions = []; // Handle the different types. | ||
switch (entryType) { | ||
case 'package.json': | ||
{ | ||
pkg = pkg || readJson(_path.default.join(moduleDir, 'package.json')); | ||
filePath = pkg ? pkg[info.field] : null; | ||
extensions = info.extensions || []; | ||
break; | ||
} | ||
case 'file': | ||
{ | ||
filePath = info.path; | ||
extensions = info.extensions || []; | ||
break; | ||
} | ||
default: | ||
{ | ||
throw new Error(`Unknown entry type: ${entryType}`); | ||
} | ||
} // Skip if empty path. | ||
if (!filePath) { | ||
continue; | ||
} // Resolve entry if possible. | ||
const resolveBase = _path.default.join(moduleDir, filePath); | ||
const resolved = resolveExtension(resolveBase, extensions); | ||
if (resolved === null) { | ||
continue; | ||
} // Update path and finish. | ||
const { | ||
source | ||
} = nodePath.node; | ||
source.value += `/${trimDotSlash(filePath)}${resolved}`; | ||
break; | ||
} | ||
} | ||
function visitDeclaration(nodePath, state) { | ||
@@ -241,17 +341,24 @@ const { | ||
const src = source.value; | ||
const src = source.value; // Ignore any URL imports. | ||
if (importPathIsUrl(src)) { | ||
if (importIsUrl(src)) { | ||
return; | ||
} | ||
} // Check if file path. | ||
if (importPathIsFile(src)) { | ||
visitDeclarationFilePath(nodePath, state); | ||
if (importIsFile(src)) { | ||
visitDeclarationPath(nodePath, state); | ||
return; | ||
} | ||
} // Check if bare import (a module or submodule). | ||
const modulePath = parseModulePath(src); | ||
if (modulePath && modulePath.path) { | ||
visitDeclarationModulePath(nodePath, state, modulePath); | ||
const bareImport = importBareParse(src); | ||
if (bareImport) { | ||
if (bareImport.path) { | ||
visitDeclarationBarePath(nodePath, state, bareImport); | ||
} else { | ||
visitDeclarationBareMain(nodePath, state, bareImport); | ||
} | ||
return; | ||
@@ -258,0 +365,0 @@ } |
{ | ||
"name": "babel-plugin-esm-resolver", | ||
"description": "A Babel plugin for resolving ESM import and export paths", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"keywords": [ | ||
@@ -38,8 +38,8 @@ "babel", | ||
"devDependencies": { | ||
"@babel/cli": "^7.5.0", | ||
"@babel/core": "^7.5.4", | ||
"@babel/preset-env": "^7.5.4", | ||
"@babel/register": "^7.4.4", | ||
"@babel/cli": "^7.5.5", | ||
"@babel/core": "^7.5.5", | ||
"@babel/preset-env": "^7.5.5", | ||
"@babel/register": "^7.5.5", | ||
"del": "^5.0.0", | ||
"eslint": "^6.0.1", | ||
"eslint": "^6.1.0", | ||
"execa": "^2.0.3", | ||
@@ -46,0 +46,0 @@ "fs-extra": "^8.1.0", |
193
README.md
@@ -19,7 +19,9 @@ # babel-plugin-esm-resolver | ||
Must be configured to work properly, and will throw an error if any modules cannot be resolved (this can optionally be turned off). | ||
Since it resolves the statements before the modules are transpiled, it can also be used to resolve the paths for other transpiled module loaders. | ||
Must be configured to work properly, it will not do anything by default. | ||
There are three classes of resolver that can be enabled (`source`, `module`, `submodule`) depending on the modules that need to be resloved. | ||
# Usage | ||
@@ -29,188 +31,7 @@ | ||
### `extensions` | ||
- [`source`](options/source.md) Source modules (`import foo from './bar'`). | ||
- [`module`](options/module.md) Module main entry points (`import foo from 'bar'`). | ||
- [`submodule`](options/submodule.md) Module submodules (`import foo from 'bar/foo'`). | ||
A list of extensions to resolve, and optionally the extension that should be used once transpiled. | ||
Takes the form of one of the following. | ||
1. Resolve `.js` or `.json` in that order. | ||
```json | ||
[ | ||
".js", | ||
".json" | ||
] | ||
``` | ||
2. Resolve `.ts` or `.json` in that order, uses `.js` for imports of `.ts` modules in transpiled code. | ||
```json | ||
[ | ||
[".ts", ".js"], | ||
".json" | ||
] | ||
``` | ||
3. Resolve `.ts`, `.tsx`, or `.json` in that order, uses `.js` for imports of `.ts` and `.tsx` modules in transpiled code. | ||
```json | ||
[ | ||
[[".ts", ".tsx"], ".js"], | ||
".json" | ||
] | ||
``` | ||
### `extensionsSubmodule` | ||
A list of extensions to resolve when importing a module's submodules (`import a from 'aaa/submod';`, `import b from '@org/bbb/submod';`). | ||
```json | ||
[ | ||
".js", | ||
".json" | ||
] | ||
``` | ||
### `ignoreUnresolved` | ||
Set to `true` to ignore any modules that cannot be resolved. | ||
By default an error is throw for any modules that cannot be resolved. | ||
### `ignoreUnresolvedSubmodule` | ||
Set to `true` to ignore any modules that cannot be resolved in submodules. | ||
By default an error is throw for any submodules that cannot be resolved. | ||
## Examples | ||
### `.js` -> `.js` | ||
`.babelrc` | ||
```json | ||
{ | ||
"presets": [ | ||
["@babel/preset-env", { | ||
"modules": false, | ||
"targets": { | ||
"node": "current" | ||
} | ||
}] | ||
], | ||
"plugins": [ | ||
["esm-resolver", { | ||
"extensions": [ | ||
".js" | ||
] | ||
}] | ||
] | ||
} | ||
``` | ||
`src/main.js` | ||
```js | ||
import {foo} from './bar'; | ||
``` | ||
`src/bar.js` | ||
```js | ||
export const foo = 123; | ||
``` | ||
**output:** | ||
```js | ||
import { foo } from "./bar.js"; | ||
``` | ||
### `.ts` -> `.js` | ||
(In practice the TypeScript preset would probably also be used) | ||
`.babelrc` | ||
```json | ||
{ | ||
"presets": [ | ||
["@babel/preset-env", { | ||
"modules": false, | ||
"targets": { | ||
"node": "current" | ||
} | ||
}] | ||
], | ||
"plugins": [ | ||
["esm-resolver", { | ||
"extensions": [ | ||
[".ts", ".js"] | ||
] | ||
}] | ||
] | ||
} | ||
``` | ||
**output:** | ||
```js | ||
import { foo } from "./bar.js"; | ||
``` | ||
### `.ts` + `.js` -> `.ts` | ||
`.babelrc` | ||
```json | ||
{ | ||
"presets": [ | ||
["@babel/preset-env", { | ||
"modules": false, | ||
"targets": { | ||
"node": "current" | ||
} | ||
}] | ||
], | ||
"plugins": [ | ||
["esm-resolver", { | ||
"extensions": [ | ||
[[".ts", ".js"], ".js"] | ||
] | ||
}] | ||
] | ||
} | ||
``` | ||
`src/main.ts` | ||
```js | ||
import {foo} from './bar'; | ||
import {bar} from './foo'; | ||
``` | ||
`src/bar.ts` | ||
```js | ||
export const foo = 123; | ||
``` | ||
`src/foo.js` | ||
```js | ||
export const bar = 123; | ||
``` | ||
**output:** | ||
```js | ||
import { foo } from "./bar.js"; | ||
import { bar } from "./foo.js"; | ||
``` | ||
# Bugs | ||
@@ -217,0 +38,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
69881
553
47