UNPKG

grunt-ts

Version:

Compile and manage your TypeScript project

247 lines 11.7 kB
/// <reference path="../../defs/tsd.d.ts"/> /// <reference path="./interfaces.d.ts"/> var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var fs = require('fs'); var path = require('path'); var grunt = require('grunt'); var _str = require('underscore.string'); var _ = require('lodash'); var utils = require('./utils'); // Setup when transformers are triggered var currentTargetFiles; var currentTargetDirs; // Based on name // if a filename matches we return a filepath // If a foldername matches we return a folderpath function getImports(currentFilePath, name, targetFiles, targetDirs, getIndexIfDir) { if (getIndexIfDir === void 0) { getIndexIfDir = true; } var files = []; // Test if any filename matches var targetFile = _.find(targetFiles, function (targetFile) { return path.basename(targetFile) === name || path.basename(targetFile, '.d.ts') === name || path.basename(targetFile, '.ts') === name; }); if (targetFile) { files.push(targetFile); } // It might be worthwhile to cache this lookup // i.e. have a 'foldername':folderpath map passed in // Test if dirname matches var targetDir = _.find(targetDirs, function (targetDir) { return path.basename(targetDir) === name; }); if (targetDir) { var possibleIndexFilePath = path.join(targetDir, 'index.ts'); // If targetDir has an index file AND this is not that file then // use index.ts instead of all the files in the directory if (getIndexIfDir && fs.existsSync(possibleIndexFilePath) && path.relative(currentFilePath, possibleIndexFilePath) !== '') { files.push(path.join(targetDir, 'index.ts')); } else { var filesInDir = utils.getFiles(targetDir, function (filename) { // exclude current file if (path.relative(currentFilePath, filename) === '') { return true; } return path.extname(filename) // must have extension : do not exclude directories && (!_str.endsWith(filename, '.ts') || _str.endsWith(filename, '.d.ts')) && !fs.lstatSync(filename).isDirectory(); // for people that name directories with dots }); filesInDir.sort(); // Sort needed to increase reliability of codegen between runs files = files.concat(filesInDir); } } return files; } // Algo // Notice that the file globs come as // test/fail/ts/deep/work.ts // So simply get dirname recursively till reach root '.' function getTargetFolders(targetFiles) { var folders = {}; _.forEach(targetFiles, function (targetFile) { var dir = path.dirname(targetFile); while (dir !== '.' && !(dir in folders)) { // grunt.log.writeln(dir); folders[dir] = true; dir = path.dirname(dir); } }); return Object.keys(folders); } var BaseTransformer = (function () { function BaseTransformer(key, variableSyntax) { this.key = key; this.match = new RegExp(utils.format(BaseTransformer.tsTransformerMatch, key)); this.signature = this.tripleSlashTS() + key; this.signatureGenerated = this.signature + ':generated'; this.syntaxError = '/// Invalid syntax for ts:' + this.key + '=' + variableSyntax + ' ' + this.signatureGenerated; } BaseTransformer.prototype.tripleSlashTS = function () { // This is a function and broken into two strings to prevent the transformers module from // transforming *itself* (a-la Skynet). return '//' + '/ts:'; }; BaseTransformer.prototype.isGenerated = function (line) { return _str.contains(line, this.signatureGenerated); }; BaseTransformer.prototype.matches = function (line) { return line.match(this.match); }; BaseTransformer.containsTransformSignature = function (line) { return BaseTransformer.tsSignatureMatch.test(line); }; BaseTransformer.tsSignatureMatch = /\/\/\/\s*ts\:/; // equals sign is optional because we want to match on the signature regardless of any errors, // transformFiles() checks that the equals sign exists (by checking for the first matched capture group) // and fails if it is not found. BaseTransformer.tsTransformerMatch = '^///\\s*ts:{0}(=?)(.*)'; return BaseTransformer; })(); // This is a separate class from BaseTransformer to make it easier to add non import/export transforms in the future var BaseImportExportTransformer = (function (_super) { __extends(BaseImportExportTransformer, _super); function BaseImportExportTransformer(key, variableSyntax, template, getIndexIfDir, removeExtensionFromFilePath) { _super.call(this, key, variableSyntax); this.key = key; this.template = template; this.getIndexIfDir = getIndexIfDir; this.removeExtensionFromFilePath = removeExtensionFromFilePath; } BaseImportExportTransformer.prototype.transform = function (sourceFile, templateVars) { var _this = this; var result = []; if (templateVars) { var vars = templateVars.split(','); var requestedFileName = vars[0].trim(); var requestedVariableName = (vars.length > 1 ? vars[1].trim() : null); var sourceFileDirectory = path.dirname(sourceFile); var imports = getImports(sourceFile, requestedFileName, currentTargetFiles, currentTargetDirs, this.getIndexIfDir); if (imports.length) { _.forEach(imports, function (completePathToFile) { var filename = requestedVariableName || path.basename(path.basename(completePathToFile, '.ts'), '.d'); // If filename is index, we replace it with dirname: if (filename.toLowerCase() === 'index') { filename = path.basename(path.dirname(completePathToFile)); } var pathToFile = utils.makeRelativePath(sourceFileDirectory, _this.removeExtensionFromFilePath ? completePathToFile.replace(/(?:\.d)?\.ts$/, '') : completePathToFile, true); result.push(_this.template({ filename: filename, pathToFile: pathToFile, signatureGenerated: _this.signatureGenerated }) + ' ' + _this.signatureGenerated); }); } else { result.push('/// No file or directory matched name "' + requestedFileName + '" ' + this.signatureGenerated); } } else { result.push(this.syntaxError); } return result; }; return BaseImportExportTransformer; })(BaseTransformer); var ImportTransformer = (function (_super) { __extends(ImportTransformer, _super); function ImportTransformer() { _super.call(this, 'import', '<fileOrDirectoryName>[,<variableName>]', _.template('import <%=filename%> = require(\'<%= pathToFile %>\');'), true, true); } return ImportTransformer; })(BaseImportExportTransformer); var ExportTransformer = (function (_super) { __extends(ExportTransformer, _super); function ExportTransformer(eol) { // This code is same as import transformer // One difference : we do not short circuit to `index.ts` if found _super.call(this, 'export', '<fileOrDirectoryName>[,<variableName>]', // workaround for https://github.com/Microsoft/TypeScript/issues/512 _.template('import <%=filename%>_file = require(\'<%= pathToFile %>\'); <%= signatureGenerated %>' + eol + 'export var <%=filename%> = <%=filename%>_file;'), false, true); this.eol = eol; } return ExportTransformer; })(BaseImportExportTransformer); var ReferenceTransformer = (function (_super) { __extends(ReferenceTransformer, _super); function ReferenceTransformer() { // This code is same as export transformer // also we preserve .ts file extension _super.call(this, 'ref', '<fileOrDirectoryName>', _.template('/// <reference path="<%= pathToFile %>"/>'), false, false); } return ReferenceTransformer; })(BaseImportExportTransformer); var UnknownTransformer = (function (_super) { __extends(UnknownTransformer, _super); function UnknownTransformer() { _super.call(this, '(.*)', ''); this.key = 'unknown'; this.signatureGenerated = this.tripleSlashTS() + 'unknown:generated'; this.syntaxError = '/// Unknown transform ' + this.signatureGenerated; } UnknownTransformer.prototype.transform = function (sourceFile, templateVars) { return [this.syntaxError]; }; return UnknownTransformer; })(BaseTransformer); // This code fixes the line encoding to be per os. // I think it is the best option available at the moment. // I am open for suggestions function transformFiles(changedFiles, targetFiles, options) { currentTargetDirs = getTargetFolders(targetFiles); currentTargetFiles = targetFiles; ///////////////////////////////////// transformation var transformers = [ new ImportTransformer(), new ExportTransformer((options.newLine || utils.eol)), new ReferenceTransformer(), new UnknownTransformer() ]; _.forEach(changedFiles, function (fileToProcess) { var contents = fs.readFileSync(fileToProcess).toString().replace(/^\uFEFF/, ''); // If no signature don't bother with this file if (!BaseTransformer.containsTransformSignature(contents)) { return; } var lines = contents.split(/\r\n|\r|\n/); var outputLines = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; //// Debugging // grunt.log.writeln('line'.green); // grunt.log.writeln(line); // Skip generated lines as these will get regenerated if (_.some(transformers, function (transformer) { return transformer.isGenerated(line); })) { continue; } // Directive line if (_.some(transformers, function (transformer) { var match = transformer.matches(line); if (match) { // The code gen directive line automatically qualifies outputLines.push(line); // pass transform settings to transform (match[1] is the equals sign, ensure it exists but otherwise ignore it) outputLines.push.apply(outputLines, transformer.transform(fileToProcess, match[1] && match[2] && match[2].trim())); return true; } return false; })) { continue; } // Lines not generated or not directives outputLines.push(line); } var transformedContent = outputLines.join(utils.eol); if (transformedContent !== contents) { grunt.file.write(fileToProcess, transformedContent); } }); } exports.transformFiles = transformFiles; //# sourceMappingURL=transformers.js.map