Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored to use canonical file names #171

Merged
merged 5 commits into from
Aug 5, 2016
Merged
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
171 changes: 132 additions & 39 deletions lib/Host.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
var commondir = require('commondir');
var events = require('events');
var fs = require('fs');
var realpath = require('fs.realpath')
var log = require('util').debuglog(require('../package').name);
var trace = require('util').debuglog(require('../package').name + '-trace');
var os = require('os');
var path = require('path');
var util = require('util');

module.exports = function (ts) {
function Host(currentDirectory, languageVersion) {
this.currentDirectory = currentDirectory;
function Host(currentDirectory, outputDirectory, languageVersion) {
this.currentDirectory = this.getCanonicalFileName(path.resolve(currentDirectory));
this.outputDirectory = this.getCanonicalFileName(path.resolve(outputDirectory));
this.languageVersion = languageVersion;
this.files = {};
this.previousFiles = {};
Expand All @@ -31,13 +34,21 @@ module.exports = function (ts) {
log('Resetting (version %d)', this.version);
};

Host.prototype._normalizedRelative = function(filename) {
return ts.normalizePath(path.relative(this.currentDirectory, path.resolve(filename)));
};

Host.prototype._addFile = function (filename, root) {
var normalized = this._normalizedRelative(filename);
log('Parsing %s (norm: %s)', filename, normalized);

// Ensure that the relative, non-canonical file name is what's passed
// to 'createSourceFile', as that's the name that will be used in error
// messages, etc.

var relative = ts.normalizeSlashes(path.relative(
this.currentDirectory,
path.resolve(
this.currentDirectory,
filename
)
));
var canonical = this._canonical(filename);
trace('Parsing %s', canonical);

var text;
try {
Expand All @@ -47,52 +58,44 @@ module.exports = function (ts) {
}

var file;
var current = this.files[normalized];
var previous = this.previousFiles[normalized];
var current = this.files[canonical];
var previous = this.previousFiles[canonical];
var version;

if (current && current.contents === text) {
file = current.ts;
version = current.version;
log('Reused current file %s (version %d)', normalized, version);
trace('Reused current file %s (version %d)', canonical, version);
} else if (previous && previous.contents === text) {
file = previous.ts;
version = previous.version;
log('Reused previous file %s (version %d)', normalized, version);
trace('Reused previous file %s (version %d)', canonical, version);
} else {
file = ts.createSourceFile(filename, text, this.languageVersion, true);
file = ts.createSourceFile(relative, text, this.languageVersion, true);
version = this.version;
log('New version of source file %s (version %d)', normalized, version);
trace('New version of source file %s (version %d)', canonical, version);
}

this.files[normalized] = {
filename: filename,
this.files[canonical] = {
filename: relative,
contents: text,
ts: file,
root: root,
version: version
};

this._emitFile(normalized);
this.emit('file', canonical, relative);

return file;
};

Host.prototype._emitFile = function (normalized) {
var idPath = './' + normalized;
var fullPath = path.resolve(idPath);
this.emit('file', fullPath, idPath);
}

Host.prototype.getSourceFile = function (filename) {
var normalized = this._normalizedRelative(filename);

if (this.files[normalized])
return this.files[normalized].ts;

if (normalized === '__lib.d.ts')
if (filename === '__lib.d.ts') {
return this.libDefault;

}
var canonical = this._canonical(filename);
if (this.files[canonical]) {
return this.files[canonical].ts;
}
return this._addFile(filename, false);
};

Expand All @@ -103,17 +106,27 @@ module.exports = function (ts) {
};

Host.prototype.writeFile = function (filename, data) {
var normalized = this._normalizedRelative(filename);
log('Cache write %s (norm: %s)', filename, normalized);
this.output[normalized] = data;

var outputCanonical = this._canonical(filename);
log('Cache write %s', outputCanonical);
this.output[outputCanonical] = data;

var sourceCanonical = this._inferSourceCanonical(outputCanonical);
var sourceFollowed = this._follow(path.dirname(sourceCanonical)) + '/' + path.basename(sourceCanonical);

if (sourceFollowed !== sourceCanonical) {
outputCanonical = this._inferOutputCanonical(sourceFollowed);
log('Cache write (followed) %s', outputCanonical);
this.output[outputCanonical] = data;
}
};

Host.prototype.getCurrentDirectory = function () {
return this.currentDirectory;
};

Host.prototype.getCanonicalFileName = function (filename) {
return this._normalizedRelative(filename);
return ts.normalizeSlashes(ts.sys.useCaseSensitiveFileNames ? filename : filename.toLowerCase());
};

Host.prototype.useCaseSensitiveFileNames = function () {
Expand All @@ -130,8 +143,7 @@ module.exports = function (ts) {
};

Host.prototype.readFile = function (filename) {
var normalized = this._normalizedRelative(filename);
return ts.sys.readFile(normalized);
return ts.sys.readFile(filename);
};

Host.prototype._rootDir = function () {
Expand All @@ -140,10 +152,91 @@ module.exports = function (ts) {
if (!Object.hasOwnProperty.call(this.files, filename)) continue;
if (/\.d\.ts$/.test(filename)) continue;

dirs.push(path.dirname(filename));
dirs.push(this.getCanonicalFileName(path.dirname(filename)));
}
var result = commondir(this.currentDirectory, dirs);
return result;
return this.getCanonicalFileName(result);
};

Host.prototype._rootFilenames = function () {

var rootFilenames = [];

for (var filename in this.files) {
if (!Object.hasOwnProperty.call(this.files, filename)) continue;
if (!this.files[filename].root) continue;
rootFilenames.push(filename);
}
return rootFilenames;
}

Host.prototype._output = function (filename) {

var outputCanonical = this._inferOutputCanonical(filename);
log('Cache read %s', outputCanonical);

var output = this.output[outputCanonical];
if (!output) {
log('Cache miss on %s', outputCanonical);
}
return output;
}

Host.prototype._canonical = function (filename) {
return this.getCanonicalFileName(path.resolve(
this.currentDirectory,
filename
));
}

Host.prototype._inferOutputCanonical = function (filename) {

var sourceCanonical = this._canonical(filename);
var outputRelative = path.relative(
this._rootDir(),
sourceCanonical
);
var outputCanonical = this.getCanonicalFileName(path.resolve(
this.outputDirectory,
outputRelative
));
return outputCanonical;
}

Host.prototype._inferSourceCanonical = function (filename) {

var outputCanonical = this._canonical(filename);
var outputRelative = path.relative(
this.outputDirectory,
outputCanonical
);
var sourceCanonical = this.getCanonicalFileName(path.resolve(
this._rootDir(),
outputRelative
));
return sourceCanonical;
}

Host.prototype._follow = function (filename) {

filename = this._canonical(filename);
var basename;
var parts = [];

do {
var stats = fs.lstatSync(filename);
if (stats.isSymbolicLink()) {
filename = realpath.realpathSync(filename);
} else {
basename = path.basename(filename);
if (basename) {
parts.unshift(basename);
filename = path.dirname(filename);
}
}
} while (basename);

return ts.normalizeSlashes(filename + parts.join('/'));
};

return Host;
Expand Down
52 changes: 19 additions & 33 deletions lib/Tsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var events = require('events');
var extend = require('util')._extend;
var fs = require('fs');
var log = require('util').debuglog(require('../package').name);
var trace = require('util').debuglog(require('../package').name + '-trace');
var path = require('path');
var through = require('through2');
var time = require('./time');
Expand Down Expand Up @@ -38,10 +39,6 @@ module.exports = function (ts) {
return file.replace(/\.\w+$/i, extension);
}

function getRelativeFilename(file) {
return './' + ts.normalizeSlashes(path.relative(currentDirectory, file));
}

function fileExists(file) {
try {
var stats = fs.lstatSync(file);
Expand Down Expand Up @@ -128,7 +125,11 @@ module.exports = function (ts) {
self.opts = parsedOptions.options;
self.files = parsedOptions.fileNames;
self.bopts = bopts;
self.host = new Host(currentDirectory, this.opts.target);
self.host = new Host(
currentDirectory,
this.opts.outDir,
this.opts.target
);

self.host.on('file', function (file, id) {
self.emit('file', file, id);
Expand All @@ -140,15 +141,15 @@ module.exports = function (ts) {
Tsifier.prototype.reset = function () {
var self = this;
self.host._reset();
self.addAll(self.files.map(getRelativeFilename));
self.addFiles(self.files);
};

Tsifier.prototype.generateCache = function (files) {
this.addAll(files.map(getRelativeFilename));
this.addFiles(files);
this.compile();
};

Tsifier.prototype.addAll = function (files) {
Tsifier.prototype.addFiles = function (files) {
var self = this;
files.forEach(function (file) {
self.host._addFile(file, true);
Expand All @@ -157,12 +158,7 @@ module.exports = function (ts) {

Tsifier.prototype.compile = function () {
var self = this;
var rootFilenames = [];
for (var filename in self.host.files) {
if (!Object.hasOwnProperty.call(self.host.files, filename)) continue;
if (!self.host.files[filename].root) continue;
rootFilenames.push(filename);
}
var rootFilenames = self.host._rootFilenames();

log('Compiling files:');
rootFilenames.forEach(function (file) { log(' %s', file); });
Expand Down Expand Up @@ -252,7 +248,7 @@ module.exports = function (ts) {
Tsifier.prototype.transform = function (file) {
var self = this;

log('Transforming %s', file);
trace('Transforming %s', file);

if (isTypescriptDeclaration(file)) {
return through(transform);
Expand All @@ -271,7 +267,7 @@ module.exports = function (ts) {
if (self.host.error)
return;

var compiled = self.getCompiledFile(getRelativeFilename(file));
var compiled = self.getCompiledFile(file);
if (compiled) {
this.push(compiled);
}
Expand All @@ -282,40 +278,30 @@ module.exports = function (ts) {

Tsifier.prototype.getCompiledFile = function (inputFile, alreadyMissedCache) {
var self = this;
var normalized = ts.normalizePath(inputFile);
var rootDir = self.host._rootDir();

var outputExtension = (self.opts.jsx === ts.JsxEmit.Preserve && isTsx(inputFile)) ? '.jsx' : '.js';
var outputFile = '__tsify__/' + ts.normalizeSlashes(path.relative(
rootDir,
path.resolve(replaceFileExtension(normalized, outputExtension))
));
var output = self.host.output[outputFile];

log('Cache read %s (norm: %s)', outputFile, normalized);
var output = self.host._output(replaceFileExtension(inputFile, outputExtension));

if (!output) {
if (alreadyMissedCache)
return;
log('Cache miss on %s (norm: %s)', outputFile, normalized);
self.generateCache([inputFile]);
if (self.host.error)
return;
return self.getCompiledFile(inputFile, true);
}

if (self.opts.inlineSourceMap) {
output = self.setFullSourcePathInSourcemap(output, normalized);
output = self.setSourcePathInSourcemap(output, inputFile);
}

return output;
};

Tsifier.prototype.setFullSourcePathInSourcemap = function (output, normalized) {
Tsifier.prototype.setSourcePathInSourcemap = function (output, inputFile) {
var self = this;
if (self.bopts.basedir) {
normalized = ts.normalizeSlashes(path.relative(self.bopts.basedir, normalized));
}
var normalized = ts.normalizePath(path.relative(
self.bopts.basedir || currentDirectory,
inputFile
));

var sourcemap = convert.fromComment(output);
sourcemap.setProperty('sources', [normalized]);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"eslint": "^2.7.0",
"event-stream": "^3.3.1",
"fs-extra": "^0.28.0",
"fs.realpath": "^1.0.0",
"node-foo": "^0.2.3",
"semver": "^5.1.0",
"source-map": "^0.5.3",
Expand Down