Skip to content

Commit

Permalink
Feature/2.0.0 (#21)
Browse files Browse the repository at this point in the history
* update to 2.0.0

* add bundler

* add some error handling

* normalize the source before splitting

* handle absolute path and fix other issues

* fix tests

* update readme and change version

* update to use vite 5

* fix error message when viteConfig is not object

* remove unnecessary log
  • Loading branch information
pzmosquito authored Jan 30, 2024
1 parent 6709967 commit e0c140b
Show file tree
Hide file tree
Showing 8 changed files with 2,165 additions and 1,088 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
dist/
7 changes: 7 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.editorconfig
.gitignore
index.js
index.test.js
jest.config.js
Makefile
vite.config.js
77 changes: 17 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,88 +2,45 @@

Vite module resolution plugin for `eslint-plugin-import`. This plugin will resolve the `resolve.alias` option.

> #### Version `2.0.0-beta.3` is available. See [what's changed](https://github.com/pzmosquito/eslint-import-resolver-vite/releases/tag/2.0.0-beta.1).
> ```sh
> npm install --save-dev [email protected]
> ```

### Installation
```sh
npm install --save-dev eslint-import-resolver-vite
# install vite-plugin-eslint if you don't already have it
npm install --save-dev vite-plugin-eslint
```


### Config Options
- **viteConfig**: The Vite config object.
- Required: Yes
- Type: object


### How to use
```js
/**
* vite config file
*/
import eslintPlugin from "vite-plugin-eslint";

export default {
#### Vite config file
```js
export const viteConfigObj = {
resolve: {
alias: {
_: path.resolve(__dirname, "src")
}
},
plugins: [
eslintPlugin()
]
};
```

/**
* eslint config file
*/
#### ESLint config file
NOTE:
- Since `eslint-plugin-import` doesn't support an async resolver, Vite's [ResolvedConfig API](https://vitejs.dev/guide/api-javascript.html#resolvedconfig) cannot be utilized.
- This plugin accepts a Vite config object to accommodate various setups, e.g. CJS, ESM, or mixed.
```js
module.exports = {
settings: {
// This uses the default `vite.config.js` file and the Vite configuration is an object.
"import/resolver": "vite",

// OR use custom config (see Config Options below):
"import/resolver": {
vite: {
configPath: "./app1/vite.confg.ts"
viteConfig: require("./vite.config").viteConfigObj,
}
}
}
}

```

### Config Options
- **configPath**: vite config file path.
- Required: No
- Type: string
- Default: "vite.config.js"
- By default, the plugin assumes the vite config file and eslintrc file are in the same directory.
- **namedExport**: named export of vite config object.
- Required: No
- Type: string
- Default: [No Default]
- **If you use a function as vite config, you must export a named vite config object. This is a result of the limitation of `eslint-plugin-import`.**
```js
/**
* vite config file
*/
export const viteConfig = {};

export default ({ command, mode }) => {
// conditional config
return viteConfig;
}

/**
* eslintrc file
*/
module.exports = {
settings: {
"import/resolver": {
vite: {
namedExport: "viteConfig"
}
}
}
}
```
152 changes: 71 additions & 81 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,107 +1,97 @@
const path = require("path");
const resolve = require("resolve");
const fs = require("fs");
const debug = require("debug");

const namespace = "eslint-plugin-import:resolver:vite";

const log = debug(namespace);

const logError = (message) => {
log(message);
console.error(`[${namespace}] ${message}`);
const processAlias = (alias, source) => {
if (alias) {
const pathParts = path.normalize(source).split(path.sep);
if (Array.isArray(alias)) {
for (let i = 0; i < pathParts.length; i++) {
alias.forEach(({find, replacement}) => {
if (pathParts[i] === find) {
pathParts[i] = replacement;
}
});
}
}
else if (typeof alias === "object") {
for (let i = 0; i < pathParts.length; i++) {
if (alias.hasOwnProperty(pathParts[i])) {
pathParts[i] = alias[pathParts[i]];
}
}
}
else {
throw new Error("The alias must be either an object, or an array of objects.");
}
return pathParts.join(path.sep);
}
return source;
};

const resolveSync = (source, resolveOptions, label) => {
log("resolving:\t", `(${label})`, source);
const resolvedPath = resolve.sync(source, resolveOptions);
log("resolved:\t", resolvedPath);
return { found: true, path: resolvedPath };
};

exports.interfaceVersion = 2;

exports.resolve = (source, file, config) => {
log("resolving:", source);
log("in file:", file);
log("\nin file:\t", file);

if (resolve.isCore(source)) {
log("resolved:\t", source);
return { found: true, path: null };
}

try {
// combine default config with user defined config
const pluginConfig = {
configPath: "vite.config.js",
...(config ?? {}),
};

// load vite config
const viteConfigPath = path.resolve(pluginConfig.configPath);
if (!fs.existsSync(viteConfigPath)) {
throw new Error(`Vite config file doesn't exist at '${viteConfigPath}'`)
}
const viteConfigFile = require(viteConfigPath);

let viteConfig;
if (pluginConfig.namedExport) {
viteConfig = viteConfigFile[pluginConfig.namedExport]
}
else {
const viteConfigObj = viteConfigFile.default ?? viteConfigFile
viteConfig = typeof viteConfigObj === "function" ? viteConfigObj() : viteConfigObj;
}
const { viteConfig } = config;
if (!viteConfig) {
throw new Error("'viteConfig' option must be a vite config object.");
}

const defaultExtensions = [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json"];
const { alias, extensions = defaultExtensions } = viteConfig.resolve ?? {};
const defaultExtensions = [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json"];
const { alias, extensions = defaultExtensions } = viteConfig.resolve ?? {};
const resolveOptions = { basedir: path.dirname(file), extensions };

let actualSource = path.normalize(source);
// try to resolve the source as is
try {
return resolveSync(source, resolveOptions, "as is");
}
catch {}

// parse and replace alias
if (alias) {
const pathParts = actualSource.split(path.sep);
if (Array.isArray(alias)) {
for (let i = 0; i < pathParts.length; i++) {
alias.forEach(({find, replacement}) => {
if (pathParts[i] === find) {
pathParts[i] = replacement;
}
});
}
}
else if (typeof alias === "object") {
for (let i = 0; i < pathParts.length; i++) {
if (alias.hasOwnProperty(pathParts[i])) {
pathParts[i] = alias[pathParts[i]];
}
}
}
else {
throw new Error("The alias must be either an object, or an array of objects.");
}
actualSource = pathParts.join(path.sep);
// try to resolve the source with alias
const parsedSource = processAlias(alias, source);
if (parsedSource !== source) {
try {
return resolveSync(parsedSource, resolveOptions, "with alias");
}
catch {}
}

// resolve module
let resolvedPath = "";
// try to resolve the source if it is an absolute path
if (path.isAbsolute(parsedSource)) {
const root = viteConfig.root ?? process.cwd();
const absoluteSource = path.join(path.resolve(root), parsedSource);
try {
resolvedPath = resolve.sync(actualSource, {
basedir: path.dirname(file),
extensions,
});
}
catch (err) {
if (viteConfig.publicDir !== false) {
const publicDir = viteConfig.publicDir ?? "public";
const publicActualSource = path.join(path.resolve(publicDir), actualSource);
resolvedPath = resolve.sync(publicActualSource, {
basedir: path.dirname(file),
extensions,
});
}
else {
throw new Error("source cannot be resolved in actual path nor in 'Public' path.");
}
return resolveSync(absoluteSource, resolveOptions, "absolute path");
}

log("resolved to:", resolvedPath);
return { found: true, path: resolvedPath };
catch {}
}
catch (err) {
logError(err.message);
return { found: false };

// try to resolve the source in public directory if all above failed
if (viteConfig.publicDir !== false) {
const publicDir = viteConfig.publicDir ?? "public";
const publicSource = path.join(path.resolve(publicDir), parsedSource);
try {
return resolveSync(publicSource, resolveOptions, "in public directory");
}
catch {}
}

log("ERROR:\t", "Unable to resolve");
return { found: false };
};
Loading

0 comments on commit e0c140b

Please sign in to comment.