ts-alias
Version:
Parse module aliases from tsconfig ; Apply / remove them from pathnames ; Generate config for webpack & module-alias.
422 lines (421 loc) • 19.6 kB
JavaScript
"use strict";
/*----------------------------------
- DEPENDANCES
----------------------------------*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Node
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
// Npm
var json5_1 = __importDefault(require("json5")); // Because tsconfig.json is closer to json5 rather than json
/*----------------------------------
- CONFIG
----------------------------------*/
var LogPrefix = '[ts-alias]';
/*----------------------------------
- MODULE
----------------------------------*/
var TsAlias = /** @class */ (function () {
function TsAlias(options) {
var _a;
if (options === void 0) { options = {}; }
this.options = options;
this.options.debug && console.log(LogPrefix, "Instanciate with the following options:", options);
var tsFile;
var tsDir;
// Aliases list already provided
// No need to search and read the tsconfig file
if ('aliases' in options) {
this.list = options.aliases.map(function (alias) { return (__assign(__assign({}, alias), { pathnames: alias.pathnames.map(function (pathname) { return path_1.default.join(process.cwd(), pathname); }) })); });
this.options.debug && console.log(LogPrefix, "Loaded aliases from object", options.aliases, '=>', this.list);
return;
}
// Use the CWD by default
if (options.rootDir === undefined)
options.rootDir = process.cwd();
else {
// Ensure the path is absolute
if (!path_1.default.isAbsolute(options.rootDir))
options.rootDir = path_1.default.join(process.cwd(), options.rootDir);
// And it exists
if (!fs_1.default.existsSync(options.rootDir))
throw new Error("The provided rootDir \"" + options.rootDir + "\" doesn't exists.");
}
// options.rootDir = config file
if (fs_1.default.lstatSync(options.rootDir).isFile()) {
tsDir = path_1.default.dirname(options.rootDir);
tsFile = path_1.default.basename(options.rootDir);
// options.rootDir = Project dir
}
else {
tsDir = options.rootDir;
tsFile = 'tsconfig.json';
}
// Module resolution directories
// Default = rootDir
if (options.modulesDir === undefined)
options.modulesDir = [path_1.default.join(options.rootDir, 'node_modules')];
options.debug && console.log(LogPrefix, "Using the following dirs for module resolution:", options.modulesDir);
// Parse the tsconfig.json file
var tsBaseDir;
(_a = this.readTsConfig(tsDir, tsFile), this.typescript = _a.paths, tsBaseDir = _a.baseUrl);
// Build the list of aliases
this.list = this.processTsAliases(this.typescript, tsBaseDir);
}
/*----------------------------------
- PARSING
----------------------------------*/
TsAlias.prototype.readTsConfig = function (dir, file) {
// TODO: prise en compte de extends si baseurl ou paths manquant
if (file === void 0) { file = 'tsconfig.json'; }
var fullpath = dir + '/' + file;
this.options.debug && console.log(LogPrefix, "Reading config " + fullpath);
var raw = fs_1.default.readFileSync(fullpath, 'utf-8');
var tsconfig = json5_1.default.parse(raw);
var _a = tsconfig.compilerOptions, paths = _a.paths, baseUrl = _a.baseUrl;
// baseUrl is specified
if (baseUrl !== undefined) {
baseUrl = path_1.default.resolve(dir, baseUrl);
// if not, try to het it from the extended config file
}
else if (tsconfig.extends) {
// If no extended file, use config file directory as base url
}
else
baseUrl = dir;
if (paths === undefined)
paths = {};
this.options.debug && console.log(LogPrefix, "Processed config: " + fullpath, paths, { baseUrl: baseUrl });
return { paths: paths, baseUrl: baseUrl };
};
TsAlias.prototype.processTsAliases = function (tsAliases, tsBaseDir) {
var e_1, _a;
var list = [];
for (var alias in tsAliases) {
var destinations = tsAliases[alias];
// Détermine if it must be exact alias
var exact = !alias.endsWith('/*');
if (!exact)
alias = alias.substring(0, alias.length - 2);
// Process each destination path
var pathnames = [];
var _loop_1 = function (destination) {
// Remove wildcard
if (destination.endsWith('*'))
destination = destination.substring(0, destination.length - 1);
// Remove trailing slash
if (destination.endsWith('/'))
destination = destination.substring(0, destination.length - 1);
// If the destination is a node module, prefix with options.modulesDir
var isNpmModule = destination[0] !== '.' && destination[0] !== '/';
if (isNpmModule)
pathnames.push.apply(pathnames, __spreadArray([], __read(this_1.options.modulesDir.map(function (dir) { return path_1.default.join(dir, destination); })), false));
// Otherwise, concat the path with the base dir (the one given in the tsconfig)
else if (destination)
pathnames.push(path_1.default.join(tsBaseDir, destination));
else
pathnames.push(tsBaseDir);
};
var this_1 = this;
try {
for (var destinations_1 = (e_1 = void 0, __values(destinations)), destinations_1_1 = destinations_1.next(); !destinations_1_1.done; destinations_1_1 = destinations_1.next()) {
var destination = destinations_1_1.value;
_loop_1(destination);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (destinations_1_1 && !destinations_1_1.done && (_a = destinations_1.return)) _a.call(destinations_1);
}
finally { if (e_1) throw e_1.error; }
}
list.push({ alias: alias, exact: exact, pathnames: pathnames });
}
this.options.debug && console.log(LogPrefix, "Processed aliases:", list, { tsBaseDir: tsBaseDir });
return list;
};
TsAlias.prototype.apply = function (realpath, strict) {
var e_2, _a;
for (var alias in this.list) {
var _b = this.list[alias], exact = _b.exact, pathnames = _b.pathnames;
try {
for (var pathnames_1 = (e_2 = void 0, __values(pathnames)), pathnames_1_1 = pathnames_1.next(); !pathnames_1_1.done; pathnames_1_1 = pathnames_1.next()) {
var pathname = pathnames_1_1.value;
if (exact) {
if (realpath === pathname)
return alias;
}
else if (realpath.startsWith(pathname + '/')) {
return alias + realpath.substring(pathname.length);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (pathnames_1_1 && !pathnames_1_1.done && (_a = pathnames_1.return)) _a.call(pathnames_1);
}
finally { if (e_2) throw e_2.error; }
}
}
// No matching alias
return strict ? null : realpath;
};
/**
* Check if the provided path can be shorten with aliases
* @param filename The path to check
* @returns If filename can be shorten an alias
*/
TsAlias.prototype.isAliased = function (filename) {
return this.apply(filename, true) !== null;
};
TsAlias.prototype.realpath = function (request, strict) {
var e_3, _a, e_4, _b;
try {
for (var _c = __values(this.list), _d = _c.next(); !_d.done; _d = _c.next()) {
var _e = _d.value, alias = _e.alias, exact = _e.exact, pathnames = _e.pathnames;
try {
for (var pathnames_2 = (e_4 = void 0, __values(pathnames)), pathnames_2_1 = pathnames_2.next(); !pathnames_2_1.done; pathnames_2_1 = pathnames_2.next()) {
var pathname = pathnames_2_1.value;
if (exact) {
if (request === alias)
return pathname;
}
else if (request.startsWith(alias + '/')) {
return pathname + request.substring(alias.length);
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (pathnames_2_1 && !pathnames_2_1.done && (_b = pathnames_2.return)) _b.call(pathnames_2);
}
finally { if (e_4) throw e_4.error; }
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_3) throw e_3.error; }
}
// No matching alias
return strict ? null : request;
};
/**
* If the provided path contains an alias
* @param filename The path to check
* @returns If filename contains an alias
*/
TsAlias.prototype.containsAlias = function (filename) {
return this.realpath(filename, true) !== null;
};
/*----------------------------------
- TRANSFORM LIST
----------------------------------*/
// https://webpack.js.org/configuration/resolve/#resolvealias
TsAlias.prototype.forWebpack = function (_a) {
var e_5, _b, e_6, _c;
var _this = this;
var modulesPath = _a.modulesPath, shortenPaths = _a.shortenPaths, nodeExternals = _a.nodeExternals;
this.options.debug && console.log(LogPrefix, "Generating webpack aliases ...");
var aliases = {};
var externalsList = {};
try {
aliasesIt: for (var _d = __values(this.list), _e = _d.next(); !_e.done; _e = _d.next()) {
var _f = _e.value, alias = _f.alias, exact = _f.exact, pathnames = _f.pathnames;
var curAliases = [];
if (modulesPath === undefined)
curAliases = pathnames;
else
try {
for (var pathnames_3 = (e_6 = void 0, __values(pathnames)), pathnames_3_1 = pathnames_3.next(); !pathnames_3_1.done; pathnames_3_1 = pathnames_3.next()) {
var pathname = pathnames_3_1.value;
// Reference to node_modules
if (shortenPaths && pathname.startsWith(modulesPath)) {
// Transforms paths to node_module into module reference
// Ex: "../node_modules/declarative-scraper" => "declarative-scraper"
pathname = pathname.substring(modulesPath.length + 1);
// Externals
if (nodeExternals === true && pathnames.length === 1) {
externalsList[alias] = { pathname: pathname, exact: exact };
continue aliasesIt;
}
}
curAliases.push(pathname);
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (pathnames_3_1 && !pathnames_3_1.done && (_c = pathnames_3.return)) _c.call(pathnames_3);
}
finally { if (e_6) throw e_6.error; }
}
// From webpack doc: « A trailing $ can also be added to the given object's keys to signify an exact match: »
if (exact)
alias += '$';
aliases[alias] = curAliases;
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
}
finally { if (e_5) throw e_5.error; }
}
this.options.debug && console.log(LogPrefix, "Webpack aliases =", aliases, 'Webpakc externals =', externalsList);
if (!nodeExternals)
return { aliases: aliases };
// https://webpack.js.org/configuration/externals/#function
var externals = function (_a, callback) {
var request = _a.request;
for (var alias in externalsList) {
var _b = externalsList[alias], pathname = _b.pathname, exact = _b.exact;
if (exact) {
if (request === alias) {
_this.options.debug && console.log(LogPrefix, request, '=>', pathname);
return callback(null, pathname);
}
}
else if (request.startsWith(alias)) {
var destination = pathname + request.substring(alias.length);
_this.options.debug && console.log(LogPrefix, request, '=>', destination);
return callback(undefined, destination);
}
}
callback();
};
this.options.debug && console.log(LogPrefix, "Webpack aliases:", aliases);
return { aliases: aliases, externals: externals };
};
// https://github.com/ilearnio/module-alias#advanced-usage
TsAlias.prototype.forModuleAlias = function (enableCache) {
var e_7, _a;
var _this = this;
if (enableCache === void 0) { enableCache = true; }
var moduleAlias = {};
var cache = {};
var _loop_2 = function (alias, exact, pathnames) {
// Create a resolver
moduleAlias[alias] = function (from, request, requestAlias) {
var e_8, _a;
// Exact alias
if (exact && request !== alias)
return requestAlias;
_this.options.debug && console.log(LogPrefix, "Resolving " + request + " from " + from);
// From cache
var cacheId = from + '::' + request;
if (enableCache && cache[cacheId] !== undefined) {
_this.options.debug && console.log(LogPrefix, 'Found from cache:', cache[cacheId]);
return cache[cacheId];
}
// Chemin du module sans l'alias
var modulePath = request.substring(requestAlias.length);
try {
// Recherche de la première destinaiton existante
for (var pathnames_4 = (e_8 = void 0, __values(pathnames)), pathnames_4_1 = pathnames_4.next(); !pathnames_4_1.done; pathnames_4_1 = pathnames_4.next()) {
var pathname = pathnames_4_1.value;
try {
var searchPath = pathname + modulePath;
_this.options.debug && console.log(LogPrefix, '- Trying:', searchPath);
// Si le chemin existe, il sera retourné
if (require.resolve(searchPath)) {
// Retourne un chemin relatif au fichier qui a importé le module
var relative = path_1.default.relative(path_1.default.dirname(from), pathname);
_this.options.debug && console.log(LogPrefix, 'Found:', relative);
cache[cacheId] = relative;
return relative;
}
}
catch (e) {
_this.options.debug && console.log(LogPrefix, 'Unable to resolve', e);
}
}
}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (pathnames_4_1 && !pathnames_4_1.done && (_a = pathnames_4.return)) _a.call(pathnames_4);
}
finally { if (e_8) throw e_8.error; }
}
_this.options.debug && console.warn("Unable to resolve alias for " + request + " from " + from);
return requestAlias;
};
};
try {
// For each registered alias
for (var _b = __values(this.list), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = _c.value, alias = _d.alias, exact = _d.exact, pathnames = _d.pathnames;
_loop_2(alias, exact, pathnames);
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_7) throw e_7.error; }
}
this.options.debug && console.log(LogPrefix, "Module AliasDefinition:", moduleAlias);
return moduleAlias;
};
return TsAlias;
}());
exports.default = TsAlias;