Skip to content

Instantly share code, notes, and snippets.

@azarus
Last active March 7, 2022 20:31
Show Gist options
  • Save azarus/f369ee2ab0283ba0793b0ccf0e9ec590 to your computer and use it in GitHub Desktop.
Save azarus/f369ee2ab0283ba0793b0ccf0e9ec590 to your computer and use it in GitHub Desktop.
Gulp Task to transform Typescript path imports into relative paths using the tsconfig
// Gulp Task
gulp.task("compile:source", function(done) {
gutil.log(gutil.colors.yellow("[Typescript]"), gutil.colors.magenta('Transpiling Source'));
var tsProject = ts.createProject('tsconfig.json', {
typescript: require('typescript'),
});
gulp.src("source/**/*")
.pipe(tsProject())
.pipe(tsimport(tsProject.config.compilerOptions))
.on('error', function(error, callback)
{
gutil.log(gutil.colors.red("[Typescript] Server Error:"), error.stack);
this.emit("end");
})
.pipe(gulp.dest("build/"))
.on("end", done);
});
var through = require('through2');
var replacePath = require("./replace-path.js");
module.exports = function(importOptions)
{
return function(file)
{
return through(function (buffer, enc, cb)
{
var code = buffer.toString("utf8");
code = replacePath(code, file, importOptions.baseUrl, importOptions.paths);
buffer = new Buffer(code);
this.push(buffer);
cb();
});
}
};
var fs = require("fs");
var path = require("path");
module.exports = function(code, filePath, rootPath, targetPaths)
{
var tscpaths = Object.keys(targetPaths);
var lines = code.split("\n");
return lines.map((line) =>
{
var matches = [];
var require_matches = line.match(/require\(('|")(.*)('|")\)/g);
// var import_matches = line.match(/import ('|")(.*)('|")/g);
Array.prototype.push.apply(matches, require_matches);
if(!matches)
{
return line;
}
// Go through each require statement
for(var match of matches)
{
// Find each paths
for(var tscpath of tscpaths)
{
// Find required module & check if its path matching what is described in the paths config.
var requiredModules = match.match(new RegExp(tscpath, "g"));
if(requiredModules && requiredModules.length > 0)
{
for(var requiredModule of requiredModules)
{
// Skip if it resolves to the node_modules folder
var modulePath = path.resolve('./node_modules/' + tscpath);
if (fs.existsSync(modulePath))
{
continue;
}
// Get relative path and replace
var sourcePath = path.dirname(filePath);
var targetPath = path.dirname(path.resolve(rootPath + "/" + targetPaths[tscpath]));
var relativePath = path.relative(sourcePath, targetPath)
line = line.replace(new RegExp(tscpath, "g"), "./" + relativePath + "/");
}
}
}
}
return line;
}).join("\n");
};
var through = require('through2');
var replacePath = require("./replace-path.js");
module.exports = function(importOptions)
{
return through.obj(function (file, enc, cb)
{
var code = file.contents.toString('utf8');
code = replacePath(code, file.history.toString(), importOptions.baseUrl, importOptions.paths);
file.contents = new Buffer(code);
this.push(file);
cb();
});
};
var tsify = require('tsify');
var importify = require("./typescript/importify.js");
var tsconfig = require("../tsconfig.json");
function CompileSources(entryFile)
{
var bundler = watchify( browserify({
entries: entryFile,
debug: false,
// defining transforms here will avoid crashing your stream
})
.plugin(tsify, tsconfig.compilerOptions))
.transform(importify(tsconfig.compilerOptions))
// .. etc
}
@azarus
Copy link
Author

azarus commented Jun 4, 2017

So what the above code does is transforms all the path and baseurl options specified in the tsconfig.json file!

So lets say you have something like this specified in your tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./source/",
	"paths": {
            "@Test/*": [
                "./Test/*"
            ]
    },
  }
}

then you can use
import Test from "@Test/whatever";

it will be translated to an absolute url such as:

./whatever
or

./../whatever

depending on the location of the file.

The following code work for Browserify+watchify+tsify and Gulp-Typescript

Have fun getting rid of the relative path nightmare!!!

@prograhammer
Copy link

Is this in an npm package anywhere? That's so weird that typescript doesn't do this!??

@kirakishin
Copy link

@prograhammer i think the exact same thing !

@spentak
Copy link

spentak commented Nov 29, 2017

Yo this is cool but it doesn't parse directories within directories. Some bad regex I think

@salchichongallo
Copy link

salchichongallo commented Dec 17, 2017

Hi! I just built a package based on this. It's called path-alias-resolver

@yadue
Copy link

yadue commented Dec 21, 2017

@salchichongallo how to use your plug-in?

@jazzfog
Copy link

jazzfog commented Feb 6, 2018

Awesome!
One small bug on Windows though: Windows-style back-slash that comes from path.relative in relativePath breaks require().
Need to replace it to regular slash (/):

in replace-path.js
after
var relativePath = path.relative(sourcePath, targetPath)
add
relativePath = relativePath.replace(/\\/g, '/');

@Alex66955
Copy link

I have two issues with your script:

  • If the tsconfig.json provides an outDir attribute not even ".":
    • the relative path replacement didn't work correctly. It generates the relative path from outDir to sourceDir which should not happened.
    • the import replacement should be independent from the outDir attribute
    • my current workaround is to remove the outDir attribute in tsconfig and using the gulp.dest.
  • On my linux machine (ubuntu 16.04) the import replacement makes some mistakes:
    • There is a syntax error causes by a double "/"
      • tsconfig: .."@backend-TP*": ["./platform/TP/*"]..
      • src: import { CategoryTraceListener } from '@backend-TP/shared/diagnostics';
      • generated: const diagnostics_1 = require("./platform/TP//shared/diagnostics");
    • My current workaround in replace-path.js line = line.replace(new RegExp(tscpath+"/", "g"), "./" + relativePath + "/");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment