Skip to content

Instantly share code, notes, and snippets.

@Spikef
Created September 25, 2020 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Spikef/bdf8c5faf9cef9fb94765627e211e4f7 to your computer and use it in GitHub Desktop.
Save Spikef/bdf8c5faf9cef9fb94765627e211e4f7 to your computer and use it in GitHub Desktop.
支持webpack别名的模块路径解析。
const fs = require('fs');
const path = require('path');
const route = {};
const NODE_MODULES = 'node_modules';
const REGEXP_RELATIVE = /^\.{0,2}\/?/;
const coreModules = new Set(Object.keys(process.versions));
class Resolver {
constructor(module, { alias, extensions }) {
if (route[module]) return route[module];
route[module] = this;
this.cache = {};
if (!fs.existsSync(module)) {
throw new Error(`Module not exist: [${module}]`);
}
this.alias = alias || {};
this.extensions = extensions || ['.js'];
this.module = module;
this.context = fs.statSync(module).isDirectory() ? module : path.dirname(module);
}
resolve(request) {
if (coreModules.has(request)) return request;
if (this.cache[request]) return this.cache[request];
let filename;
if (REGEXP_RELATIVE.test(request)) {
const realPath = path.resolve(this.context, request);
filename = this.resolveAsFile(realPath) || this.resolveAsDirectory(realPath);
}
if (!filename) filename = this.resolveAlias(request);
if (!filename) filename = this.resolveNodeModules(request);
if (!filename) throw new Error(`Can not find module '${request}' from '${this.module}'`);
filename = path.resolve(filename);
this.cache[request] = filename;
return filename;
}
resolveIndex(x) {
let y;
let stat;
for (let i = 0; i < this.extensions.length; i++) {
y = `${x}/index${this.extensions[i]}`;
if (fs.existsSync(y)) {
stat = fs.statSync(y);
if (stat && stat.isFile()) return y;
}
}
}
resolveAsFile(x) {
let y;
let stat;
if (fs.existsSync(x)) {
stat = fs.statSync(x);
if (stat && stat.isFile()) return x;
}
for (let i = 0; i < this.extensions.length; i++) {
y = `${x}${this.extensions[i]}`;
if (fs.existsSync(y)) {
stat = fs.statSync(y);
if (stat && stat.isFile()) return y;
}
}
}
resolveAsDirectory(x) {
const y = `${x}/package.json`;
if (fs.existsSync(y)) {
const stat = fs.statSync(y);
if (stat && stat.isFile()) {
try {
const pkg = JSON.parse(fs.readFileSync(y, 'utf8'));
if (!pkg.main) return this.resolveIndex(x);
const m = path.resolve(x, pkg.main);
return this.resolveAsFile(m) || this.resolveIndex(m);
} catch (e) {
return this.resolveIndex(x);
}
}
}
return this.resolveIndex(x);
}
resolveAlias(x) {
let filename;
const paths = this.aliasPaths(x);
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
filename = this.resolveAsFile(path);
if (filename) return filename;
filename = this.resolveAsDirectory(path);
if (filename) return filename;
}
}
resolveNodeModules(x) {
let filename;
const dirs = this.nodeModulesPaths();
for (let i = 0; i < dirs.length; i++) {
const dir = dirs[i];
filename = this.resolveAsFile(path.join(dir, x));
if (filename) return filename;
filename = this.resolveAsDirectory(path.join(dir, x));
if (filename) return filename;
}
}
nodeModulesPaths() {
const dirs = [];
const parts = this.context.split(/[\\/]+/);
if (parts[0] === '') parts[0] = '/';
for (let i = parts.length - 1; i > 0; i--) {
if (parts[i] !== NODE_MODULES) {
const dir = path.join.apply(path, [...parts.slice(0, i + 1), NODE_MODULES]);
dirs.push(dir);
}
}
return dirs;
}
aliasPaths(x) {
const paths = [];
Object.keys(this.alias).forEach(key => {
const value = this.alias[key];
if (x.startsWith(key)) {
paths.push(x.replace(key, value));
}
});
return paths;
}
}
module.exports = Resolver;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment