jsx
Version:
a faster, safer, easier JavaScript
448 lines (415 loc) • 13.1 kB
JSX
/***
* Platform-independent JSX compiler command
*/
/*
* Copyright (c) 2012 DeNA Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import "./meta.jsx";
import "./compiler.jsx";
import "./completion.jsx";
import "./doc.jsx";
import "./platform.jsx";
import "./emitter.jsx";
import "./jsemitter.jsx";
import "./optimizer.jsx";
import "./analysis.jsx";
class JSXCommand {
static function help() : string {
return (
"JSX compiler version " + Meta.VERSION_STRING + "\n" +
"\n" +
"Usage: jsx [options] source-file\n" +
"\n" +
"Options:\n" +
" --add-search-path path adds a path to library search paths\n" +
" --executable RUNENV adds launcher to call _Main.main(:string[]):void\n" +
" supported RUNENV is node, commonjs and web.\n" +
" --run runs _Main.main(:string[]):void after compiling\n" +
" --test runs _Test#test*():void after compiling\n" +
" --output file output file (default:stdout)\n" +
" --input-filename file names input filename\n" +
" --mode (compile|parse|doc) specifies compilation mode (default:compile)\n" +
" --target (javascript|c++) specifies target language (default:javascript)\n" +
" --release disables run-time type checking and enables optimizations (" + Optimizer.getReleaseOptimizationCommands().join(",") + ")\n" +
" --profile enables the profiler (experimental)\n" +
" --optimize cmd1,cmd2,... enables optimization commands\n" +
" --warn type1,type2,... enables warnings (all, deprecated, none)\n" +
" --disable-type-check disables run-time type checking\n" +
" --minify compresses the target JavaScript code\n" +
" --enable-source-map enables source map debugging info\n" +
" --complete line:column shows code completion at line:column\n" +
" --version displays the version and compiler identifier and exits\n" +
" --version-number displays the version as number and exits\n" +
" --help displays this help and exits\n" +
"\n" +
"Env:\n" +
" JSX_OPTS options of jsx(1)\n" +
" JSX_RUNJS JavaScript engine used by --run and --test\n" +
"");
}
static function main (platform : Platform, args : string[]) : number {
var argIndex = 0;
var getopt = function () : Nullable.<string> {
if (args.length <= argIndex)
return null;
var arg = args[argIndex++];
if (arg == "--")
return null;
if (arg.match(/^-/))
return arg;
else {
--argIndex;
return null;
}
};
var getoptarg = function () : Nullable.<string> {
if (args.length <= argIndex) {
platform.error("option " + args[argIndex - 1] + " requires a value");
return null;
}
return args[argIndex++];
};
var compiler = new Compiler(platform);
var tasks = new Array.<() -> void>;
var optimizer = null : Optimizer;
var completionRequest = null : CompletionRequest;
var emitter = null : Emitter;
var outputFile = null : Nullable.<string>;
var inputFilename = null : Nullable.<string>;
var executable = null : Nullable.<string>;
var setBootstrapMode = function (sourceFile : string) : void {};
var runImmediately = false;
var optimizeCommands = new string[];
var opt, optarg;
while ((opt = getopt()) != null) {
NEXTOPT:
switch (opt) {
case "--add-search-path":
if((optarg= getoptarg()) == null) {
return 1;
}
compiler.addSearchPath(optarg);
break;
case "--output":
if((outputFile = getoptarg()) == null) {
return 1;
}
break;
case "--input-filename":
if((inputFilename = getoptarg()) == null) {
return 1;
}
break;
case "--working-dir": // working directory
if((optarg = getoptarg()) == null) {
return 1;
}
platform.setWorkingDir(optarg);
break;
case "--mode":
if ((optarg = getoptarg()) == null) {
return 1;
}
switch (optarg) {
case "compile":
compiler.setMode(Compiler.MODE_COMPILE);
break;
case "parse":
compiler.setMode(Compiler.MODE_PARSE);
break;
case "doc":
compiler.setMode(Compiler.MODE_DOC);
break;
default:
platform.error("unknown mode: " + optarg);
return 1;
}
break;
case "--complete":
if ((optarg = getoptarg()) == null) {
return 1;
}
completionRequest = function () : CompletionRequest {
var a = optarg.split(/:/);
return new CompletionRequest((a[0] as number), (a[1] as number) - 1);
}();
compiler.setMode(Compiler.MODE_COMPLETE);
break;
case "--target":
if ((optarg = getoptarg()) == null) {
return 1;
}
switch (optarg) {
case "javascript":
emitter = new JavaScriptEmitter(platform);
break;
case "c++":
throw new Error("FIXME");
default:
platform.error("unknown target: " + optarg);
return 1;
}
break;
case "--release":
tasks.push(function () : void {
emitter.setEnableRunTimeTypeCheck(false);
optimizer.setEnableRunTimeTypeCheck(false);
});
optimizeCommands = Optimizer.getReleaseOptimizationCommands();
break;
case "--optimize":
if ((optarg = getoptarg()) == null) {
return 1;
}
if (optarg == "release") {
optimizeCommands = Optimizer.getReleaseOptimizationCommands();
} else {
optimizeCommands = optimizeCommands.concat(optarg.split(","));
}
break;
case "--disable-optimize":
if ((optarg = getoptarg()) == null) {
return 1;
}
optimizeCommands = optimizeCommands.filter((item) -> {
return optarg.split(",").indexOf(item) == -1;
});
break;
case "--warn":
if ((optarg = getoptarg()) == null) {
return 1;
}
optarg.split(",").forEach(function (type) {
switch (type) {
case "none":
compiler.getWarningFilters().unshift(function (warning : CompileWarning) : Nullable.<boolean> {
return false;
});
break;
case "all":
compiler.getWarningFilters().unshift(function (warning : CompileWarning) : Nullable.<boolean> {
return true;
});
break;
case "deprecated":
compiler.getWarningFilters().unshift(function (warning : CompileWarning) : Nullable.<boolean> {
if (warning instanceof DeprecatedWarning) {
return true;
}
return null;
});
break;
default:
platform.error("unknown warning type: " + type);
}
});
break;
case "--warn-error":
compiler.setWarningAsError(true);
break;
case "--executable":
if ((optarg = getoptarg()) == null) {
return 1;
}
switch (optarg) {
case "web": // implies JavaScriptEmitter
break;
case "commonjs": // implies JavaScriptEmitter
break;
case "node": // implies JavaScriptEmitter
break;
default:
platform.error("unknown executable type (node|web)");
return 1;
}
setBootstrapMode = function (sourceFile) {
(emitter as JavaScriptEmitter).setBootstrapMode(JavaScriptEmitter.BOOTSTRAP_EXECUTABLE, sourceFile, executable);
};
executable = optarg;
break;
case "--run":
setBootstrapMode = function (sourceFile) {
(emitter as JavaScriptEmitter).setBootstrapMode(JavaScriptEmitter.BOOTSTRAP_EXECUTABLE, sourceFile, executable);
};
executable = executable ?: "node";
runImmediately = true;
break;
case "--test":
setBootstrapMode = function (sourceFile) {
(emitter as JavaScriptEmitter).setBootstrapMode(JavaScriptEmitter.BOOTSTRAP_TEST, sourceFile, executable);
};
executable = executable ?: "node";
runImmediately = true;
break;
case "--profile":
tasks.push(function () : void {
emitter.setEnableProfiler(true);
});
break;
case "--minify":
tasks.push(function () {
emitter.setEnableMinifier(true);
if (optimizeCommands.length != 0 && optimizeCommands[0] != "strip")
optimizeCommands.unshift("strip");
if (optimizeCommands[optimizeCommands.length - 1] != "strip")
optimizeCommands.push("strip");
});
break;
case "--compilation-server":
if ((optarg = getoptarg()) == null) {
return 1;
}
return platform.runCompilationServer(optarg);
case "--version":
platform.log(Meta.IDENTIFIER);
return 0;
case "--version-number":
platform.log(Meta.VERSION_NUMBER as string);
return 0;
case "--help":
platform.log(JSXCommand.help());
return 0;
default:
var switchOpt = opt.match(new RegExp("^--(enable|disable)-(.*)$"));
if (switchOpt != null) {
var mode = switchOpt[1] == "enable";
switch (switchOpt[2]) {
case "type-check":
tasks.push(function (mode : boolean) : () -> void {
return function () {
emitter.setEnableRunTimeTypeCheck(mode);
optimizer.setEnableRunTimeTypeCheck(mode);
};
}(mode));
break NEXTOPT;
case "source-map":
tasks.push(function (mode : boolean) : () -> void {
return function () {
emitter.setEnableSourceMap(mode);
};
}(mode));
break NEXTOPT;
default:
break;
}
}
platform.error("unknown option: " + opt);
return 1;
}
}
if (argIndex == args.length) {
platform.error("no files");
return 1;
}
var sourceFile = args[argIndex++];
if (inputFilename != null) {
platform.setFileContent(inputFilename, platform.load(sourceFile));
sourceFile = inputFilename;
}
compiler.addSourceFile(null, sourceFile, completionRequest);
switch (compiler.getMode()) {
case Compiler.MODE_PARSE:
if (compiler.compile()) {
platform.save(outputFile, JSON.stringify(compiler.getAST()));
return 0;
} else {
return 1;
}
}
if (emitter == null)
emitter = new JavaScriptEmitter(platform);
setBootstrapMode(sourceFile);
compiler.setEmitter(emitter);
switch (compiler.getMode()) {
case Compiler.MODE_DOC:
if (outputFile == null) {
platform.error("--output is mandatory for --mode doc");
return 1;
}
if (compiler.compile()) {
new DocumentGenerator(compiler, platform.getRoot() + "/src/doc", outputFile)
.setResourceFiles(["style.css"])
.setPathFilter(function (sourcePath) {
if (sourcePath.indexOf("system:") == 0) {
return false;
}
if (sourcePath.charAt(0) == "/") {
return false;
}
if (sourcePath.indexOf("../") == 0) {
return false;
}
if (sourcePath.indexOf("/../") != -1) {
return false;
}
return true;
})
.buildDoc();
return 0;
} else {
return 1;
}
}
optimizer = new Optimizer();
tasks.forEach(function(proc) { proc(); });
if (emitter.getEnableMinifier() && emitter.getEnableSourceMap()) {
platform.error("--minify and --source-map cannot be specified at the same time");
return 1;
}
var err = optimizer.setup(optimizeCommands);
if (err != null) {
platform.error(err);
return 1;
}
emitter.setOutputFile(outputFile);
compiler.setOptimizer(optimizer);
var result = compiler.compile();
if (completionRequest != null) {
platform.save(null, JSON.stringify(completionRequest.getCandidates()));
return 0;
}
if (! result)
return 65; // compile error (EX_DATAERR of FreeBSD sysexits(3))
var output = emitter.getOutput();
if (emitter instanceof JavaScriptEmitter) {
if (! runImmediately || outputFile != null) { // compile and save
platform.save(outputFile, output);
if (outputFile != null) {
var map = emitter.getSourceMappingFiles();
for (var filename in map) {
platform.save(filename, map[filename]);
}
if (executable != null) {
platform.makeFileExecutable(outputFile, executable);
}
}
}
else { // compile and run immediately
platform.execute(sourceFile, output, args.slice(argIndex));
}
}
else {
throw new Error("FIXME: C++ emitter");
}
return 0;
}
}
// vim: set noexpandtab: