1 | var fs = require('fs');
|
2 | var path = require('path');
|
3 | var commander = require('commander');
|
4 | var mkdirp = require('mkdirp');
|
5 | var meta = require('../package.json');
|
6 | var continuation = require('../continuation');
|
7 |
|
8 | var options = {
|
9 | compileMark: false
|
10 | };
|
11 |
|
12 |
|
13 | try {
|
14 | var coffee = require('coffee-script');
|
15 | } catch (e) {
|
16 | coffee = null;
|
17 | }
|
18 |
|
19 | try {
|
20 | var live = require('LiveScript');
|
21 | } catch (e) {
|
22 | live = null;
|
23 | }
|
24 |
|
25 | var cacheDefaultPath = '/tmp/continuation';
|
26 |
|
27 | var initialize = function () {
|
28 | commander.version(meta.version);
|
29 | commander.usage('[options] <file.js/file.coffee> [arguments]');
|
30 | commander.option('-p, --print', 'compile script file and print it');
|
31 | commander.option('-o, --output <filename>', 'compile script file and save as <filename>');
|
32 | commander.option('-e, --explicit', 'compile only if "use continuation" is explicitly declared');
|
33 | commander.option('-c, --cache [directory]', 'run and cache compiled sources to [directory], by default [directory] is ' + cacheDefaultPath);
|
34 | commander.option('-v, --verbose', 'print verbosal information to stderr');
|
35 | commander.parse(process.argv);
|
36 | };
|
37 |
|
38 | var main = exports.main = function () {
|
39 | initialize();
|
40 |
|
41 | var filename = commander.args[0];
|
42 | if (commander.explicit) {
|
43 | options.compileMark = true;
|
44 | }
|
45 | if (commander.cache) {
|
46 | options.cache = commander.cache;
|
47 | if (options.cache === true) {
|
48 | options.cache = cacheDefaultPath;
|
49 | }
|
50 | }
|
51 | if (commander.verbose) {
|
52 | options.verbose = true;
|
53 | }
|
54 |
|
55 | try {
|
56 | if (!filename) throw new Error('You should specify a script file.');
|
57 | filename = fs.realpathSync(filename);
|
58 | } catch (e) {
|
59 | console.error(e.toString());
|
60 | console.error(commander.helpInformation());
|
61 | process.exit(-1);
|
62 | }
|
63 | var code = readAndCompile(filename);
|
64 |
|
65 | var print = false;
|
66 | if (commander.print)
|
67 | print = true;
|
68 | if (commander.output)
|
69 | print = true;
|
70 |
|
71 | if (print) {
|
72 | outputCode(code);
|
73 | } else {
|
74 | runCode(code, filename);
|
75 | }
|
76 | };
|
77 |
|
78 | var outputCode = function (code) {
|
79 | if (commander.output) {
|
80 | fs.writeFile(commander.output, code, function (err) {
|
81 | if (err) throw err;
|
82 | });
|
83 | } else {
|
84 | console.log(code);
|
85 | }
|
86 | };
|
87 |
|
88 | var runCode = function (code, filename) {
|
89 |
|
90 | var mainModule = require.main;
|
91 | mainModule.filename = filename;
|
92 | mainModule.moduleCache = {};
|
93 | mainModule.children = [];
|
94 | mainModule.paths = calculatePaths(filename);
|
95 |
|
96 |
|
97 | require.extensions['.js'] = compileAndRun;
|
98 | if (coffee !== null) {
|
99 | require.extensions['.coffee'] = compileAndRun;
|
100 | }
|
101 |
|
102 |
|
103 | var args = commander.args.slice(1);
|
104 | process.argv = [process.argv[0], filename].concat(args);
|
105 |
|
106 |
|
107 | mainModule._compile(code, filename);
|
108 | };
|
109 |
|
110 | var readAndCompile = function (filename) {
|
111 | if (options.verbose) {
|
112 | console.error('Load ' + filename);
|
113 | }
|
114 |
|
115 | if (options.cache) {
|
116 | code = readCache(filename);
|
117 | if (code !== null) {
|
118 | if (options.verbose) {
|
119 | console.error('Cache hit');
|
120 | }
|
121 | return code;
|
122 | }
|
123 | }
|
124 |
|
125 | var code = fs.readFileSync(filename, 'utf-8');
|
126 |
|
127 | try {
|
128 | var ext = path.extname(filename);
|
129 | if (ext === '.coffee') {
|
130 |
|
131 | if (coffee !== null) {
|
132 | code = coffee.compile(code);
|
133 | } else {
|
134 | throw new Error('Can not find CoffeeScript module');
|
135 | }
|
136 | } else if (ext === '.ls') {
|
137 |
|
138 | if (live !== null) {
|
139 | code = live.compile(code);
|
140 | } else {
|
141 | throw new Error('Can not find LiveScript module');
|
142 | }
|
143 | }
|
144 | code = continuation.compile(code, options);
|
145 | } catch (err) {
|
146 | console.error('In file', filename);
|
147 | console.error(err.stack);
|
148 | process.exit(-1);
|
149 | }
|
150 |
|
151 | if (options.cache) {
|
152 | if (options.verbose) {
|
153 | console.error('Cache updated');
|
154 | }
|
155 | writeCache(filename, code);
|
156 | }
|
157 |
|
158 | return code;
|
159 | };
|
160 |
|
161 | var compileAndRun = function (module, filename) {
|
162 | global.currentFilename = filename;
|
163 | var code = readAndCompile(filename);
|
164 | module._compile(code, filename);
|
165 | };
|
166 |
|
167 | var calculatePaths = function (filename) {
|
168 | var paths = [];
|
169 | var pathSec = path.dirname(filename).split(path.sep);
|
170 | while (pathSec.length > 0) {
|
171 | var modulePath = pathSec.join(path.sep);
|
172 | modulePath += path.sep + 'node_modules';
|
173 | paths.push(modulePath);
|
174 | pathSec.pop();
|
175 | }
|
176 | return paths;
|
177 | };
|
178 |
|
179 | var getCachedFilePath = function (filename) {
|
180 | return path.join(options.cache, filename + '_c.js');
|
181 | };
|
182 |
|
183 | var readCache = function (filename) {
|
184 | var cachedFilePath = getCachedFilePath(filename);
|
185 | var exists = fs.existsSync(cachedFilePath);
|
186 | if (!exists) {
|
187 | return null;
|
188 | }
|
189 |
|
190 | var stat = fs.lstatSync(cachedFilePath);
|
191 | var cacheMtime = stat.mtime;
|
192 | var stat = fs.lstatSync(filename);
|
193 | var sourceMtime = stat.mtime;
|
194 |
|
195 | if (sourceMtime > cacheMtime) {
|
196 | return null;
|
197 | }
|
198 |
|
199 | return fs.readFileSync(cachedFilePath, 'utf-8');
|
200 | };
|
201 |
|
202 | var writeCache = function (filename, code) {
|
203 | var cachedFilePath = getCachedFilePath(filename);
|
204 | mkdirp.sync(path.dirname(cachedFilePath), 0777);
|
205 | fs.writeFileSync(cachedFilePath, code);
|
206 | };
|