ng-annotate
Version:
add, remove and rebuild angularjs dependency injection annotations
221 lines (191 loc) • 6.32 kB
JavaScript
// ng-annotate.js
// MIT licensed, see LICENSE file
// Copyright (c) 2013-2016 Olov Lassus <olov.lassus@gmail.com>
"use strict";
const t0 = Date.now();
const fs = require("fs");
const fmt = require("simple-fmt");
const tryor = require("tryor");
const ngAnnotate = require("./ng-annotate-main");
const version = require("./package.json").version;
const optimist = require("optimist")
.usage("ng-annotate v" + version + "\n\nUsage: ng-annotate OPTIONS <file>\n\n" +
"provide - instead of <file> to read from stdin\n" +
"use -a and -r together to remove and add (rebuild) annotations in one go")
.options("a", {
alias: "add",
boolean: true,
describe: "add dependency injection annotations where non-existing",
})
.options("r", {
alias: "remove",
boolean: true,
describe: "remove all existing dependency injection annotations",
})
.options("o", {
describe: "write output to <file>. output is written to stdout by default",
})
.options("sourcemap", {
boolean: true,
describe: "generate an inline sourcemap"
})
.options("sourceroot", {
describe: "set the sourceRoot property of the generated sourcemap"
})
.options("single_quotes", {
boolean: true,
describe: "use single quotes (') instead of double quotes (\")",
})
.options("regexp", {
describe: "detect short form myMod.controller(...) iff myMod matches regexp",
})
.options("rename", {
describe: "rename declarations and annotated references\n" +
"oldname1 newname1 oldname2 newname2 ...",
default: ""
})
.options("plugin", {
describe: "use plugin with path (experimental)",
})
.options("enable", {
describe: "enable optional with name",
})
.options("list", {
describe: "list all optional names",
boolean: true,
})
.options("stats", {
boolean: true,
describe: "print statistics on stderr (experimental)",
});
const argv = optimist.argv;
function exit(msg) {
if (msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
process.exit(-1);
}
// special-case for --list
if (argv.list) {
const list = ngAnnotate("", {list: true}).list;
if (list.length >= 1) {
process.stdout.write(list.join("\n") + "\n");
}
process.exit(0);
}
// validate options
if (argv._.length !== 1) {
optimist.showHelp();
exit("error: no input file provided");
}
if (!argv.add && !argv.remove) {
optimist.showHelp();
exit("error: missing option --add and/or --remove");
}
const filename = argv._.shift();
(filename === "-" ? slurpStdin : slurpFile)(runAnnotate);
function slurpStdin(cb) {
let buf = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", function(d) {
buf += d;
});
process.stdin.on("end", function() {
cb(null, buf);
});
process.stdin.resume();
}
function slurpFile(cb) {
if (!fs.existsSync(filename)) {
cb(new Error(fmt('error: file not found {0}', filename)));
}
fs.readFile(filename, cb);
}
function runAnnotate(err, src) {
if (err) {
exit(err.message);
}
src = String(src);
const config = tryor(function() {
return JSON.parse(String(fs.readFileSync("ng-annotate-config.json")));
}, {});
if (filename !== "-") {
config.inFile = filename;
}
["add", "remove", "o", "regexp", "rename", "single_quotes", "plugin", "enable", "stats"].forEach(function(opt) {
if (opt in argv) {
config[opt] = argv[opt];
}
});
if (argv.sourcemap) {
config.map = { inline: true, sourceRoot: argv.sourceroot };
if (filename !== "-") {
config.map.inFile = filename;
}
};
if (config.enable && !Array.isArray(config.enable)) {
config.enable = [config.enable];
}
if (config.plugin) {
if (!Array.isArray(config.plugin)) {
config.plugin = [config.plugin];
}
config.plugin = config.plugin.map(function(path) {
const absPath = tryor(fs.realpathSync.bind(fs, path), null);
if (!absPath) {
exit(fmt('error: plugin file not found {0}', path));
}
// the require below may throw an exception on parse-error
try {
return require(absPath);
} catch (e) {
// node will already print file:line and offending line to stderr
exit(fmt("error: couldn't require(\"{0}\")", absPath));
}
});
}
const trimmedRename = config.rename && config.rename.trim();
if (trimmedRename) {
const flattenRename = trimmedRename.split(" ");
const renameArray = [];
for (let i = 0; i < flattenRename.length; i = i + 2) {
renameArray.push({
"from": flattenRename[i],
"to": flattenRename[i + 1],
});
}
config.rename = renameArray;
} else {
config.rename = null;
}
const run_t0 = Date.now();
const ret = ngAnnotate(src, config);
const run_t1 = Date.now();
if (ret.errors) {
exit(ret.errors.join("\n"));
}
const stats = ret._stats;
if (config.stats && stats) {
const t1 = Date.now();
const all = t1 - t0;
const run_parser = stats.parser_parse_t1 - stats.parser_parse_t0;
const all_parser = run_parser + (stats.parser_require_t1 - stats.parser_require_t0);
const nga_run = (run_t1 - run_t0) - run_parser;
const nga_init = all - all_parser - nga_run;
const pct = function(n) {
return Math.round(100 * n / all);
}
process.stderr.write(fmt("[{0} ms] parser: {1}, nga init: {2}, nga run: {3}\n", all, all_parser, nga_init, nga_run));
process.stderr.write(fmt("[%] parser: {0}, nga init: {1}, nga run: {2}\n", pct(all_parser), pct(nga_init), pct(nga_run)));
}
if (ret.src && config.o) {
try {
fs.writeFileSync(config.o, ret.src);
} catch (e) {
exit(e.message);
}
} else if (ret.src) {
process.stdout.write(ret.src);
}
}