Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ node_modules
node_modules/
test/

.vscode

# Logs
logs
*.log
Expand Down
53 changes: 28 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
{
"name": "tspath",
"version": "2.5.4",
"description": "TypeScript path alias resolver, re-writes JS files with relative paths according to @paths specified in tsconfig",
"license": "LGPL-2.1",
"scripts": {
"copy": "copyfiles package.json lib",
"tspath": "node ./tspath.js"
"name": "tspath",
"version": "2.5.4",
"description": "TypeScript path alias resolver, re-writes JS files with relative paths according to @paths specified in tsconfig",
"license": "LGPL-2.1",
"scripts": {
"copy": "copyfiles package.json lib",
"tspath": "node ./tspath.js",
"watch": "tsc -w"
},
"preferGlobal": true,
"preferGlobal": true,
"main": "index.js",
"bin": {
"bin": {
"tspath": "./tspath.js"
},
"keywords": [
"keywords": [
"tspath",
"relative",
"path",
Expand All @@ -21,26 +22,28 @@
"resolve",
"alias"
],
"author": "Patrik Forsberg <[email protected]>",
"repository": {
"author": "Patrik Forsberg <[email protected]>",
"repository": {
"type": "git",
"url": "git+https://github.com/duffman/tspath.git"
"url": "git+https://github.com/duffman/tspath.git"
},
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/chai": "^4.0.4",
"@types/mocha": "^2.2.42",
"@types/node": "^8.0.25",
"chai": "^4.1.2",
"copyfiles": "^2.4.1",
"mocha": "^3.5.0",
"prettier": "^2.7.1"
"@types/node": "^8.0.25",
"@types/supports-color": "8.1.1",
"chai": "^4.1.2",
"copyfiles": "^2.4.1",
"mocha": "^3.5.0",
"prettier": "^2.7.1"
},
"dependencies": {
"chalk": "^2.3.0",
"escodegen": "^1.8.1",
"esprima": "^4.0.0",
"dependencies": {
"chalk": "^2.3.0",
"escodegen": "^1.8.1",
"esprima-next": "^5.8.4",
"prompt-confirm": "^1.2.0",
"typescript": "^4.4.2",
"yargs": "^11.0.0"
"supports-color": "8.1.1",
"typescript": "^4.4.2",
"yargs": "^11.0.0"
}
}
159 changes: 83 additions & 76 deletions src/parser-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@

=----------------------------------------------------------------= */

let esprima = require("esprima");
let escodegen = require("escodegen");
let chalk = require("chalk");

import { Const } from "./tspath.const";
import { Logger } from "./utils/logger";
import { PathUtils } from "./utils/path.utils";
import { Utils } from "./utils/utils";
import { JsonCommentStripper } from "./utils/json-comment-stripper";
import { ProjectOptions } from "./project-options";
import * as fs from "fs";
import * as path from "path";
const escodegen = require("escodegen");

import esprima from "esprima-next";
import { Const } from "./tspath.const";
import { Logger } from "./utils/logger";
import { PathUtils } from "./utils/path.utils";
import { Utils } from "./utils/utils";
import { JsonCommentStripper } from "./utils/json-comment-stripper";
import { ProjectOptions } from "./project-options";
import * as fs from "fs";
import * as path from "path";
import { bold, green, red, underline, yellow } from "./utils/color";
import type { Node, Program, ArgumentListElement } from "esprima-next";

const log = console.log;
const testRun = false;
Expand All @@ -42,6 +43,7 @@ export class ParserEngine {
public projectPath: string;

nrFilesProcessed: number = 0;
nrMappedPaths: number = 0;
nrPathsProcessed: number = 0;
srcRoot: string;
basePath: string;
Expand All @@ -62,7 +64,7 @@ export class ParserEngine {

public setProjectPath(projectPath: string): boolean {
if (!Utils.isEmpty(projectPath) && !this.validateProjectPath(projectPath)) {
log(chalk.red.bold("Project Path \"" + chalk.underline(projectPath) + "\" is invalid!"));
log(red(bold(("Project Path \"" + underline(projectPath) + "\" is invalid!"))));
return false;
}

Expand Down Expand Up @@ -92,7 +94,7 @@ export class ParserEngine {
}

if (!fs.existsSync(configFile)) {
log("TypeScript Compiler - Configuration file " + chalk.underline(Const.TS_CONFIG) + " is missing!");
log("TypeScript Compiler - Configuration file " + underline(Const.TS_CONFIG) + " is missing!");
}

return result;
Expand Down Expand Up @@ -122,18 +124,18 @@ export class ParserEngine {
console.time(PROCESS_TIME);

if (!this.validateProjectPath(this.projectPath)) {
log(chalk.bold.red("Invalid project path!"));
log(bold(red("Invalid project path!")));
this.exit(10);
}

this.projectOptions = this.readConfig();
let projectName = this.readProjectName();

if (!Utils.isEmpty(projectName)) {
log(chalk.yellow("Parsing project: ") + chalk.bold(projectName) + " " + chalk.underline(this.projectPath));
log(yellow("Parsing project: ") + bold(projectName) + " " + underline(this.projectPath));
}
else {
log(chalk.yellow.bold("Parsing project at: ") + "\"" + this.projectPath + "\"");
log(yellow(bold("Parsing project at: ") + "\"" + this.projectPath + "\""));
}

this.distRoot = path.resolve(this.projectPath, this.projectOptions.outDir);
Expand Down Expand Up @@ -167,11 +169,12 @@ export class ParserEngine {
this.processFile(filename);
}

log(chalk.bold("Total files processed:"), this.nrFilesProcessed);
log(chalk.bold("Total paths processed:"), this.nrPathsProcessed);
log(bold("\nTotal files processed:"), this.nrFilesProcessed);
log(bold("Total paths processed:"), this.nrPathsProcessed);
log(bold("Total mapped paths resolved:"), this.nrMappedPaths);

console.timeEnd(PROCESS_TIME);
log(chalk.bold.green("Project is prepared, now run it normally!"));
log(bold(green("Project is prepared, now run it normally!")));
}

private shouldSkipFile(filename: string): boolean {
Expand All @@ -181,74 +184,73 @@ export class ParserEngine {

/**
*
* @param sourceFilename
* @param jsRequire - require in javascript source "require("jsRequire")
* @param sourceFilename - source file that the `require` statement was found in
* @param jsRequire - require in javascript source `require("jsRequire")`
* @returns {string}
*/
getRelativePathForRequiredFile(sourceFilename: string, jsRequire: string) {
let options = this.projectOptions;
const options = this.projectOptions;
// absolute path of "baseUrl" specified in tsconfig.json
const baseUrl = path.join(this.projectPath, this.projectOptions.baseUrl);

if (Const.DEBUG_MODE) {
console.log("getRelativePathForRequiredFile ::---", sourceFilename);
console.log("\ngetRelativePathForRequiredFile");
console.log("\tsourceFilename == ", sourceFilename);
console.log("\tjsRequire == ", jsRequire);
}

for (let alias in options.pathMappings) {
let mapping = options.pathMappings[ alias ];
// iterate over all of the "paths" specified in tsconfig.json
for (const aliasRaw in options.pathMappings) {
// get the mapping of this alias
const mappingRaw = options.pathMappings[aliasRaw];

//TODO: Handle * properly
alias = Utils.stripWildcard(alias);
mapping = Utils.stripWildcard(mapping);
const alias = Utils.stripWildcard(aliasRaw);
const mapping = Utils.stripWildcard(mappingRaw);

// 2018-06-02: Workaround for bug with same prefix Aliases e.g @db and @dbCore
// 2022-10-19: Workaround for if Unix paths are used on Windows machines (which they usually are)
// Cut alias prefix for mapping comparison
let requirePrefix = jsRequire.substring(0, jsRequire.indexOf(path.sep));

if (requirePrefix == alias) {
let result = jsRequire.replace(alias, mapping);
Utils.replaceDoubleSlashes(result);
const requirePrefix = jsRequire.substring(0, jsRequire.indexOf(path.sep)) || jsRequire.substring(0, jsRequire.indexOf("/"));

let absoluteJsRequire = path.join(this.basePath, result);
// if no match, go to next alias
// N.B. Please use guard clauses, like this
if (requirePrefix !== alias) continue;

if (!fs.existsSync(`${ absoluteJsRequire }.js`)) {
const newResult = jsRequire.replace(alias, "");
absoluteJsRequire = path.join(this.basePath, newResult);
}
this.nrMappedPaths++;

let sourceDir = path.dirname(sourceFilename);
// Path to required file relative to baseUrl (from tsconfig.json)
const requireMapped = jsRequire.replace(alias, mapping);
Utils.replaceDoubleSlashes(requireMapped);

if (Const.DEBUG_MODE) {
console.log("sourceDir == ", sourceDir);
console.log("absoluteJsRequire == ", absoluteJsRequire);
console.log("this.distRoot == ", this.distRoot);
console.log("sourceFilename == ", sourceFilename);
}
// let absoluteJsRequire = path.join(this.basePath, requireMapped);

const fromPath = path.dirname(sourceFilename);
const toPath = path.dirname(absoluteJsRequire + ".js");
// idk what this is for but don't seem to need it rn
// if (!fs.existsSync(`${ absoluteJsRequire }.js`)) {
// const newResult = jsRequire.replace(alias, "");
// absoluteJsRequire = path.join(this.basePath, newResult);
// }

let relativePath = PathUtils.getRelativePath(fromPath, toPath);
// directory of the source file
const sourceFileDir = path.dirname(sourceFilename);

/*
let relativePath = path.relative(fromPath, toPath);
// path of baseUrl (from tsconfig.json) relative to sourceFileDir
const pathToBase = PathUtils.getRelativePath(sourceFileDir, baseUrl);

if (!relativePath.trim().length) {
relativePath = ".";
}
// final path of required file relative to source file
const relativeJsRequire = path.join(pathToBase, requireMapped);

relativePath = Utils.ensureTrailingPathDelimiter(relativePath);
*/
if (Const.DEBUG_MODE) {
console.log("\tbaseUrl == ", baseUrl);
console.log("\tsourceFileDir == ", sourceFileDir);
console.log("\trequireMapped == ", requireMapped);
console.log("\trelativeJsRequire == ", relativeJsRequire)
// console.log("absoluteJsRequire == ", absoluteJsRequire);
}

//
// If the path does not start with .. it´ not a sub directory
// as in ../ or ..\ so assume it´ the same dir...
//
if (relativePath[ 0 ] !== ".") {
relativePath = "./" + relativePath;
}
jsRequire = relativeJsRequire;

jsRequire = relativePath + path.parse(absoluteJsRequire).base;
break;
}
break;
}

return jsRequire;
Expand All @@ -258,19 +260,19 @@ export class ParserEngine {
* Processes the filename specified in require("filename")
* @param node
* @param sourceFilename
* @returns {any}
* @returns
*/
processJsRequire(node: any, sourceFilename: string): any {
let resultNode = node;
let requireInJsFile = Utils.safeGetAstNodeValue(node);
processJsRequire(node: ArgumentListElement, sourceFilename: string): ArgumentListElement {
let resultNode: ArgumentListElement = node;
const requireInJsFile = Utils.safeGetAstNodeValue(node);

//
// Only proceed if the "require" contains a full file path, not
// single references like "inversify"
//
if (!Utils.isEmpty(requireInJsFile) && Utils.fileHavePath(requireInJsFile)) {
let relativePath = this.getRelativePathForRequiredFile(sourceFilename, requireInJsFile);
resultNode = { type: "Literal", value: relativePath, raw: relativePath };
const relativePath = this.getRelativePathForRequiredFile(sourceFilename, requireInJsFile);
resultNode = { type: esprima.Syntax.Literal, value: relativePath, raw: relativePath };

this.nrPathsProcessed++;
}
Expand All @@ -287,7 +289,7 @@ export class ParserEngine {

let scope = this;
let inputSourceCode = fs.readFileSync(filename, Const.FILE_ENCODING);
let ast = null;
let ast: Program | null = null;

try {
ast = esprima.parse(inputSourceCode); //, { raw: true, tokens: true, range: true, comment: true });
Expand All @@ -299,7 +301,12 @@ export class ParserEngine {
}

this.traverseSynTree(ast, this, (node) => {
if (node != undefined && node.type == "CallExpression" && node.callee.name == "require") {
if (
node != undefined
&& node.type == "CallExpression"
&& node.callee.type === "Identifier"
&& node.callee.name == "require"
) {
node.arguments[ 0 ] = scope.processJsRequire(node.arguments[ 0 ], filename);
}
});
Expand All @@ -313,7 +320,7 @@ export class ParserEngine {
}
}
catch (error) {
log(chalk.bold.red("Unable to write file:"), filename);
log(bold(red("Unable to write file:")), filename);
this.exit();
}
}
Expand Down Expand Up @@ -360,7 +367,7 @@ export class ParserEngine {
for (let key in reqFields) {
let field = reqFields[ key ];
if (Utils.isEmpty(field)) {
log(chalk.red.bold("Missing required field:") + " \"" + chalk.bold.underline(key) + "\"");
log(red(bold("Missing required field:")) + " \"" + bold(underline(key)) + "\"");
this.exit(22);
}
}
Expand All @@ -374,7 +381,7 @@ export class ParserEngine {
* @param scope
* @param func
*/
private traverseSynTree(ast, scope, func): void {
private traverseSynTree(ast: Node, scope: ParserEngine, func: (a: Node) => void): void {
func(ast);
for (let key in ast) {
if (ast.hasOwnProperty(key)) {
Expand Down
3 changes: 3 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { bold, red, underline } from "./utils/color";

console.log(`This is some ${red("red")} text and this is ${bold("bold and " + underline("underlined") )} text!`);
Loading