UNPKG

coffeescript-concat

Version:

A utility for combining coffeescript files and resolving their dependencies.

296 lines (273 loc) 10.9 kB
// Generated by CoffeeScript 1.10.0 (function() { var _, argv, concatFiles, concatenate, findClassDependencies, findClasses, findExternClasses, findFileDependencies, fs, getFileNamesInDirs, getFileNamesInDirsR, includeDirectories, includeDirectoriesRecursive, mapDependencies, options, path, removeDirectives, sourceFiles, util, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; util = require('util'); fs = require('fs'); path = require('path'); _ = require('underscore'); findClasses = function(file) { var classNames, classRegex, result; file = '\n' + file; classRegex = /\n[^#\n]*class\s@?([A-Za-z_$-][A-Za-z0-9_$-.]*)/g; classNames = []; while ((result = classRegex.exec(file)) !== null) { classNames.push(result[1]); } return classNames; }; findExternClasses = function(file) { var classNames, externRegex, result; file = '\n' + file; externRegex = /#=\s*extern\s+([A-Za-z_$-][A-Za-z0-9_$-.]*)/g; classNames = []; while ((result = externRegex.exec(file)) !== null) { classNames.push(result[1]); } return classNames; }; findClassDependencies = function(file) { var classDirectiveRegex, dependencies, dependencyRegex, result; file = '\n' + file; dependencyRegex = /\n[^#\n]*extends\s([A-Za-z_$-][A-Za-z0-9_$-.]*)/g; dependencies = []; while ((result = dependencyRegex.exec(file)) !== null) { if (result[1] !== "this") { dependencies.push(result[1]); } } file = file.replace(dependencyRegex, ''); classDirectiveRegex = /#=\s*require\s+([A-Za-z_$-][A-Za-z0-9_$-]*)/g; while ((result = classDirectiveRegex.exec(file)) !== null) { dependencies.push(result[1]); } return dependencies; }; findFileDependencies = function(file) { var dependencies, fileDirectiveRegex, result; file = '\n' + file; dependencies = []; fileDirectiveRegex = /#=\s*require\s+<([A-Za-z0-9_$-][A-Za-z0-9_$-.]*)>/g; while ((result = fileDirectiveRegex.exec(file)) !== null) { dependencies.push(result[1]); } return dependencies; }; getFileNamesInDirsR = function(dirs, filesFound, callback) { var nextDir; if (dirs.length > 0) { nextDir = dirs[dirs.length - 1]; return fs.readdir(nextDir, function(err, files) { var directories, file, filePath, i, len, stats; directories = []; if (err) { throw err; } else { for (i = 0, len = files.length; i < len; i++) { file = files[i]; filePath = nextDir.replace(/\/$/, '') + '/' + file; stats = fs.statSync(filePath); if (stats.isDirectory()) { directories.push(filePath); } else if (stats.isFile()) { filesFound.push(filePath); } } dirs.splice(dirs.length - 1, 1); dirs = dirs.concat(directories); return getFileNamesInDirsR(dirs, filesFound, function(innerFilesFound) { return callback(innerFilesFound); }); } }); } else { return callback(filesFound); } }; getFileNamesInDirs = function(dirs, callback) { return getFileNamesInDirsR(dirs, [], callback); }; mapDependencies = function(sourceFiles, searchDirectories, searchDirectoriesRecursive, callback) { var dir, f, files, i, len; files = sourceFiles; for (i = 0, len = searchDirectories.length; i < len; i++) { dir = searchDirectories[i]; files = files.concat((function() { var j, len1, ref, results; ref = fs.readdirSync(dir); results = []; for (j = 0, len1 = ref.length; j < len1; j++) { f = ref[j]; results.push(path.join(dir, f)); } return results; })()); } return getFileNamesInDirs(searchDirectoriesRecursive, function(filesFound) { var classes, contents, dependencies, extern, file, fileDef, fileDefs, fileDependencies, j, len1; files = files.concat(filesFound); fileDefs = []; for (j = 0, len1 = files.length; j < len1; j++) { file = files[j]; if (!(/\.coffee$/.test(file))) { continue; } contents = fs.readFileSync(file).toString(); classes = findClasses(contents); extern = findExternClasses(contents); dependencies = findClassDependencies(contents); fileDependencies = findFileDependencies(contents); dependencies = _.select(dependencies, function(d) { return _.indexOf(classes, d) === -1; }); dependencies = _.select(dependencies, function(d) { return _.indexOf(extern, d) === -1; }); fileDef = { name: file, classes: classes, extern: extern, dependencies: dependencies, fileDependencies: fileDependencies, contents: contents }; fileDefs.push(fileDef); } return callback(fileDefs); }); }; concatFiles = function(sourceFiles, fileDefs, listFilesOnly) { var allFileDefs, fd, fileDefStack, fileProp, findFileDefByClass, findFileDefByName, i, len, nextFileDef, output, resolveDependencies, resolvedDef, sourceFileDefs, usedFiles; usedFiles = []; allFileDefs = fileDefs.slice(0); if (sourceFiles.length > 0) { sourceFileDefs = (function() { var i, len, ref, results; results = []; for (i = 0, len = fileDefs.length; i < len; i++) { fd = fileDefs[i]; if (ref = fd.name, indexOf.call(sourceFiles, ref) >= 0) { results.push(fd); } } return results; })(); } else { sourceFileDefs = fileDefs; } findFileDefByClass = function(className) { var c, fileDef, i, j, len, len1, searchInClasses; for (i = 0, len = allFileDefs.length; i < len; i++) { fileDef = allFileDefs[i]; searchInClasses = fileDef.classes.concat(fileDef.extern); for (j = 0, len1 = searchInClasses.length; j < len1; j++) { c = searchInClasses[j]; if (c === className) { return fileDef; } } } return null; }; findFileDefByName = function(fileName) { var fileDef, i, len, name, temp; for (i = 0, len = allFileDefs.length; i < len; i++) { fileDef = allFileDefs[i]; temp = fileDef.name.split('/'); name = temp[temp.length - 1].split('.')[0]; if (fileName === name) { return fileDef; } } return null; }; resolveDependencies = function(fileDef) { var depFileDef, dependenciesStack, dependency, i, j, len, len1, neededFile, neededFileDef, neededFileName, nextStack, ref, ref1; dependenciesStack = []; if (_.indexOf(usedFiles, fileDef.name) !== -1) { return null; } else if (fileDef.dependencies.length === 0 && fileDef.fileDependencies.length === 0) { dependenciesStack.push(fileDef); usedFiles.push(fileDef.name); } else { dependenciesStack = []; ref = fileDef.dependencies; for (i = 0, len = ref.length; i < len; i++) { dependency = ref[i]; depFileDef = findFileDefByClass(dependency); if (depFileDef === null) { console.error("Error: couldn't find needed class: " + dependency); } else { nextStack = resolveDependencies(depFileDef); dependenciesStack = dependenciesStack.concat(nextStack !== null ? nextStack : []); } } ref1 = fileDef.fileDependencies; for (j = 0, len1 = ref1.length; j < len1; j++) { neededFile = ref1[j]; neededFileName = neededFile.split('.')[0]; neededFileDef = findFileDefByName(neededFileName); if (neededFileDef === null) { console.error("Error: couldn't find needed file: " + neededFileName); } else { nextStack = resolveDependencies(neededFileDef); dependenciesStack = dependenciesStack.concat(nextStack !== null ? nextStack : []); } } if (_.indexOf(usedFiles, fileDef.name) === -1) { dependenciesStack.push(fileDef); usedFiles.push(fileDef.name); } } return dependenciesStack; }; fileDefStack = []; while (sourceFileDefs.length > 0) { nextFileDef = sourceFileDefs.pop(); resolvedDef = resolveDependencies(nextFileDef); if (resolvedDef) { fileDefStack = fileDefStack.concat(resolvedDef); } } output = ''; fileProp = listFilesOnly ? 'name' : 'contents'; for (i = 0, len = fileDefStack.length; i < len; i++) { nextFileDef = fileDefStack[i]; output += nextFileDef[fileProp] + '\n'; } return output; }; removeDirectives = function(file) { var classDirectiveRegex, fileDirectiveRegex; fileDirectiveRegex = /#=\s*require\s+<([A-Za-z_$-][A-Za-z0-9_$-.]*)>/g; classDirectiveRegex = /#=\s*require\s+([A-Za-z_$-][A-Za-z0-9_$-]*)/g; file = file.replace(fileDirectiveRegex, ''); file = file.replace(classDirectiveRegex, ''); return file; }; concatenate = function(sourceFiles, includeDirectories, includeDirectoriesRecursive, outputFile, listFilesOnly) { return mapDependencies(sourceFiles, includeDirectories, includeDirectoriesRecursive, function(deps) { var output; output = concatFiles(sourceFiles, deps, listFilesOnly); output = removeDirectives(output); if (outputFile) { return fs.writeFile(outputFile, output, function(err) { if (err) { return console.error(err); } }); } else { return console.log(output); } }); }; options = require('yargs').usage("Usage: coffeescript-concat [options] a.coffee b.coffee ...\nIf no output file is specified, the resulting source will sent to stdout").describe('h', 'display this help').alias('h', 'help').describe('I', 'directory to search for files').alias('I', 'include-dir').describe('R', 'directory to search for files recursively').alias('R', 'include-dir-recursive').describe('o', 'output file name').alias('o', 'output-file').describe('list-files', 'list file names instead of outputting file contents'); argv = options.argv; includeDirectories = typeof argv.I === 'string' ? [argv.I] : argv.I || []; includeDirectoriesRecursive = typeof argv.R === 'string' ? [argv.R] : argv.R || []; sourceFiles = typeof argv._ === 'string' ? [argv._] : argv._; if (argv.help || (includeDirectories.length === 0 && includeDirectoriesRecursive.length === 0 && sourceFiles.length === 0)) { options.showHelp(); } concatenate(sourceFiles, includeDirectories, includeDirectoriesRecursive, argv.o, argv['list-files']); }).call(this);