UNPKG

grunt-ts

Version:

Compile and manage your TypeScript project

246 lines (200 loc) 10.3 kB
/// <reference path="../../defs/tsd.d.ts"/> import _ = require('lodash'); import _str = require('underscore.string'); import path = require('path'); import fs = require('fs'); import utils = require('./utils'); var grunt = utils.grunt; var pathSeperator = path.sep; ///////////////////////////////////////////////////////////////////// // AMD Loader, creates a js file that loads a few files in order // and the rest un orderded, based on the reference.ts spec //////////////////////////////////////////////////////////////////// export interface IReferences { all: string[]; before: string[]; generated: string[]; unordered: string[]; after: string[]; } export const enum ReferenceOrder { before, unordered, after } export function getReferencesInOrder(referenceFile: string, referencePath: string, generatedFiles: string[]): IReferences { var toreturn: IReferences = { all: [], before: [], generated: [], unordered: [], after: [] }; var sortedGeneratedFiles = _.sortBy(generatedFiles); function isGeneratedFile(filename: string): boolean { return _.indexOf(sortedGeneratedFiles, filename, true) !== -1; } // When reading var referenceMatch = /\/\/\/ <reference path=\"(.*?)\"/; // When writing var referenceIntro = '/// <reference path="'; // var referenceEnd = '" />'; // The section of unordered files var ourSignatureStart = '//grunt-start'; var ourSignatureEnd = '//grunt-end'; var lines = fs.readFileSync(referenceFile).toString().split('\n'); // Which of the three sections we are in var loopState = ReferenceOrder.before; for (var i = 0; i < lines.length; i++) { var line = _str.trim(lines[i]); if (_str.include(line, ourSignatureStart)) { // Wait for the end signature: loopState = ReferenceOrder.unordered; } if (_str.include(line, ourSignatureEnd)) { loopState = ReferenceOrder.after; } // Fetch the existing reference's filename if any: if (_str.include(line, referenceIntro)) { var match = line.match(referenceMatch); var filename = match[1]; switch (loopState) { case ReferenceOrder.before: toreturn.before.push(filename); break; case ReferenceOrder.unordered: if (isGeneratedFile(filename)) { toreturn.generated.push(filename); } else { toreturn.unordered.push(filename); } break; case ReferenceOrder.after: toreturn.after.push(filename); break; } } } // Fix the references to be absolute: toreturn.before = _.map(toreturn.before, (relativePath) => path.resolve(referencePath, relativePath)); toreturn.generated = _.map(toreturn.generated, (relativePath) => path.resolve(referencePath, relativePath)); toreturn.unordered = _.map(toreturn.unordered, (relativePath) => path.resolve(referencePath, relativePath)); toreturn.after = _.map(toreturn.after, (relativePath) => path.resolve(referencePath, relativePath)); toreturn.all = Array.prototype.concat.call([], toreturn.before, toreturn.generated, toreturn.unordered, toreturn.after); return toreturn; } // It updates based on the order of reference files export function updateAmdLoader(referenceFile: string, files: IReferences, loaderFile: string, loaderPath: string, outDir: string, newLine = utils.eol) { // Read the original file if it exists if (fs.existsSync(referenceFile)) { grunt.log.verbose.writeln('Generating amdloader from reference file ' + referenceFile); // Filter.d.ts, if (files.all.length > 0) { grunt.log.verbose.writeln('Files: ' + files.all.map((f) => f.cyan).join(', ')); } else { grunt.warn('No files in reference file: ' + referenceFile); } if (files.before.length > 0) { files.before = _.filter(files.before, (file) => { return !utils.endsWith(file, '.d.ts'); }); grunt.log.verbose.writeln('Before: ' + files.before.map((f) => f.cyan).join(', ')); } if (files.generated.length > 0) { files.generated = _.filter(files.generated, (file) => { return !utils.endsWith(file, '.d.ts'); }); grunt.log.verbose.writeln('Generated: ' + files.generated.map((f) => f.cyan).join(', ')); } if (files.unordered.length > 0) { files.unordered = _.filter(files.unordered, (file) => { return !utils.endsWith(file, '.d.ts'); }); grunt.log.verbose.writeln('Unordered: ' + files.unordered.map((f) => f.cyan).join(', ')); } if (files.after.length > 0) { files.after = _.filter(files.after, (file) => { return !utils.endsWith(file, '.d.ts'); }); grunt.log.verbose.writeln('After: ' + files.after.map((f) => f.cyan).join(', ')); } // If target has outDir we need to make adjust the path // c:/somefolder/ts/a , c:/somefolder/ts/inside/b + c:/somefolder/build/js => c:/somefolder/build/js/a , c:/somefolder/build/js/inside/b // Logic: // find the common structure in the source files ,and remove it // Finally: outDir path + remainder section if (outDir) { // Find common path var commonPath = utils.findCommonPath(files.before.concat(files.generated.concat(files.unordered.concat(files.after))), pathSeperator); grunt.log.verbose.writeln('Found common path: ' + commonPath); // Make sure outDir is absolute: outDir = path.resolve(outDir); grunt.log.verbose.writeln('Using outDir: ' + outDir); function makeRelativeToOutDir(files: string[]) { files = _.map(files, (file) => { // Remove common path and replace with absolute outDir file = file.replace(commonPath, outDir); // remove ts extension '.ts': file = file.substr(0, file.length - 3); // Make relative to amd loader file = utils.makeRelativePath(loaderPath, file); // Prepend "./" to prevent "basePath" requirejs setting from interferring: file = './' + file; return file; }); return files; } grunt.log.verbose.writeln('Making files relative to outDir...'); files.before = makeRelativeToOutDir(files.before); files.generated = makeRelativeToOutDir(files.generated); files.unordered = makeRelativeToOutDir(files.unordered); files.after = makeRelativeToOutDir(files.after); var mainTemplate = _.template('define(function (require) { ' + newLine + '<%= body %>' + newLine + '});'); // The order in the before and after files is important var singleRequireTemplate = _.template('\t require([<%= filename %>],function (){' + newLine + '<%= subitem %>' + newLine + '\t });'); // initial sub item var subitem = ''; // Write out a binary file: var binaryTemplate = _.template('define(["<%= filenames %>"],function () {});'); var binaryFilesNames = files.before.concat(files.generated.concat(files.unordered.concat(files.after))); var binaryContent = binaryTemplate({ filenames: binaryFilesNames.join('","') }); var binFileExtension = '.bin.js'; var loaderFileWithoutExtension = path.dirname(loaderFile) + pathSeperator + path.basename(loaderFile, '.js'); var binFilename = loaderFileWithoutExtension + binFileExtension; grunt.file.write(binFilename, binaryContent); grunt.log.verbose.writeln('Binary AMD loader written ' + binFilename.cyan); // // Notice that we build inside out in the below sections: // // Generate fileTemplate from inside out // Start with after // Build the subitem for ordered after items files.after = files.after.reverse(); // Important to build inside out _.forEach(files.after, (file) => { subitem = singleRequireTemplate({ filename: '"' + file + '"', subitem: subitem }); }); // Next up add the unordered items: // For these we will use just one require call if (files.unordered.length > 0) { var unorderFileNames = files.unordered.join('",' + newLine + '\t\t "'); subitem = singleRequireTemplate({ filename: '"' + unorderFileNames + '"', subitem: subitem }); } // Next the generated files // For these we will use just one require call var generatedFileNames = files.generated.join('",' + newLine + '\t\t "'); subitem = singleRequireTemplate({ filename: '"' + generatedFileNames + '"', subitem: subitem }); // Build the subitem for ordered before items files.before = files.before.reverse(); _.forEach(files.before, (file) => { subitem = singleRequireTemplate({ filename: '"' + file + '"', subitem: subitem }); }); // The last subitem is now the body var output = mainTemplate({ body: subitem }); // Finally write it out grunt.file.write(loaderFile, output); grunt.log.verbose.writeln('AMD loader written ' + loaderFile.cyan); } } else { grunt.log.writeln('Cannot generate amd loader unless a reference file is present'.red); } }