PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/preprocessors/compiler.js

https://github.com/skysbird/js.io
JavaScript | 240 lines | 237 code | 2 blank | 1 comment | 0 complexity | a65f01f54865a0f32c197722d302e18c MD5 | raw file
  1. jsio('import std.js as JS');
  2. // compiler should be able to compile itself, so use a different name for calls to jsio that we don't want to try to compile
  3. var JSIO = jsio.__jsio;
  4. var sourceCache = /jsio.__srcCache\s*=\s*\{\s*\}/,
  5. jsioAddPath = /jsio\.path\.add\s*\(\s*(['"][^'"]+?['"])\s*\)/g,
  6. jsioNormal = /jsio\s*\(\s*(['"].+?['"])\s*(,\s*\{[^}]+\})?\)/g,
  7. jsioDynamic = /jsio\s*\(\s*DYNAMIC_IMPORT_(.*?)\s*(,\s*\{[^}]+\})?\)/g,
  8. gSrcTable = {};
  9. exports = function(path, moduleDef, opts) {
  10. opts = opts || {};
  11. if (gSrcTable[moduleDef.path]) {
  12. moduleDef.src = '';
  13. return;
  14. }
  15. logger.info('compiling', moduleDef.path);
  16. // prevent double import
  17. gSrcTable[moduleDef.path] = true;
  18. var self = moduleDef.path;
  19. if (opts.path) {
  20. if (JS.isArray(opts.path)) {
  21. for (var i = 0, len = opts.path.length; i < len; ++i) {
  22. jsio.path.add(opts.path);
  23. }
  24. } else if (typeof opts.path == 'string') {
  25. jsio.path.add(opts.path);
  26. }
  27. }
  28. if (opts.autoDetectPaths) {
  29. jsioAddPath.lastIndex = 0;
  30. logger.debug('detecting paths for', self);
  31. while (true) {
  32. var match = jsioAddPath.exec(moduleDef.src);
  33. if (!match) { break; }
  34. try {
  35. jsio.path.add(eval(match[1]));
  36. logger.info('added path ' + match[1]);
  37. } catch(e) {
  38. logger.info('failed to add path ' + match[1]);
  39. }
  40. }
  41. }
  42. jsioNormal.lastIndex = 0;
  43. while (true) {
  44. var match = jsioNormal.exec(moduleDef.src);
  45. if (!match) { break; }
  46. logger.debug('detected', match[0])
  47. var cmd = match[1],
  48. inlineOpts = match[2] ? match[2].substring(1) : '';
  49. try {
  50. cmd = eval(cmd);
  51. } catch(e) {
  52. logger.warn('could not compile import from', self + ':', cmd);
  53. continue;
  54. }
  55. try {
  56. inlineOpts = eval(inlineOpts);
  57. } catch(e) {
  58. logger.warn('could not parse opts for jsio in', self + ':', inlineOpts);
  59. inlineOpts = {};
  60. }
  61. run(moduleDef, cmd, opts, inlineOpts);
  62. }
  63. jsioDynamic.lastIndex = 0;
  64. while(true) {
  65. var match = jsioDynamic.exec(moduleDef.src);
  66. if (!match) { break; }
  67. var cmd = match[1],
  68. inlineOpts = match[2] || '';
  69. if (opts.dynamicImports && cmd in opts.dynamicImports) {
  70. var dynamicImports = opts.dynamicImports[cmd];
  71. if (!dynamicImports) {
  72. logger.debug('Dynamic import ' + cmd + ': <nothing>');
  73. continue;
  74. } else if (JS.isArray(dynamicImports)) {
  75. for (var j = 0, line; line = dynamicImports[j]; ++j) {
  76. logger.debug('Dynamic import ' + cmd + ': ' + line);
  77. run(moduleDef, line, opts, inlineOpts);
  78. }
  79. } else {
  80. logger.debug('Dynamic import ' + cmd + ': ' + dynamicImports);
  81. run(moduleDef, dynamicImports, opts, inlineOpts);
  82. }
  83. } else {
  84. logger.error('Missing: import definition\nConstant', cmd, 'for DYNAMIC_IMPORT_' + cmd, ' was not provided to the compiler');
  85. }
  86. }
  87. gSrcTable[moduleDef.path] = JS.shallowCopy(moduleDef);
  88. moduleDef.src = '';
  89. }
  90. exports.setDebugLevel = function(debugLevel) { logger.setLevel(debugLevel); }
  91. exports.reset = function() {
  92. gSrcTable = {};
  93. }
  94. /**
  95. * set the compressor function, which has the signature:
  96. * function (string source, function callback)
  97. */
  98. var gActiveCompressor;
  99. exports.setCompressor = function(compressor) { gActiveCompressor = compressor; }
  100. /**
  101. * opts.compressSources: compress each source file ** requires an active compressor (see exports.setCompressor)
  102. * opts.compressResult: compress the resulting file ** requires an active compressor (see exports.setCompressor)
  103. * opts.includeJsio: include a copy of jsio.js in the output
  104. * opts.preserveJsioSource: don't modify the included copy of jsio.js (useful if the code will be used in another run of the jsio compiler)
  105. */
  106. exports.generateSrc = function(opts, callback) {
  107. var opts = JS.merge(opts, {
  108. compressSources: false,
  109. includeJsio: true,
  110. preserveJsioSource: false
  111. });
  112. var cb = bind(this, buildJsio, opts, callback);
  113. if (opts.compressSources) {
  114. compressTable(gSrcTable, opts, cb);
  115. } else {
  116. cb();
  117. }
  118. }
  119. exports.getPathJS = function() {
  120. return 'jsio.path.set(' + JSON.stringify(jsio.path.get()) + ');jsio.path.cache=' + JSON.stringify(jsio.path.cache) + ';';
  121. }
  122. function buildJsio(opts, callback) {
  123. function getJsioSrc() {
  124. var src = jsio.__jsio.__init__.toString(-1);
  125. if (src.substring(0, 8) == 'function') {
  126. src = 'jsio=(' + src + ')();';
  127. }
  128. return src;
  129. }
  130. var src,
  131. jsioSrc = (opts.includeJsio ? getJsioSrc() : '')
  132. + exports.getPathJS();
  133. // if we're not allowed to modify the jsio source or we're not including the jsio source
  134. // then use jsio.setCachedSrc to include the source strings
  135. if (opts.preserveJsioSource || !opts.includeJsio) {
  136. logger.info('source include method: jsio.setCachedSrc');
  137. var lines = [];
  138. for (var i in gSrcTable) {
  139. lines.push("jsio.setCachedSrc('" + gSrcTable[i].path + "'," + JSON.stringify(gSrcTable[i].src) + ");");
  140. }
  141. src = jsioSrc + lines.join('\n');
  142. } else {
  143. logger.info('source include method: setting jsio.__srcCache');
  144. // otherwise we can just look for the jsio.__srcCache variable and inline the compiled
  145. // source as a JSON string. We need to use a function here to avoid some ugly escaping
  146. // of '$' in the replacement string.
  147. src = jsioSrc.replace(sourceCache, function(match) { return "jsio.__srcCache=" + JSON.stringify(gSrcTable); });
  148. }
  149. if (opts.compressResult) {
  150. logger.info('compressing final code...');
  151. gActiveCompressor(null, src, callback, opts);
  152. } else {
  153. callback(src);
  154. }
  155. }
  156. function compressTable(table, opts, callback) {
  157. logger.info('compressing sources');
  158. var queue = [];
  159. for (var i in table) { queue.push(i); }
  160. compressStep(queue, table, opts, queue.pop(), callback);
  161. }
  162. function compressStep(queue, table, opts, key, callback) {
  163. if (key) {
  164. logger.log('compressing', key + '...');
  165. gActiveCompressor(key, table[key].src, function(result) {
  166. table[key].src = result;
  167. compressStep(queue, table, opts, queue.pop(), callback);
  168. }, opts);
  169. } else {
  170. callback();
  171. }
  172. }
  173. exports.getTable = function() { return gSrcTable; }
  174. exports.compile = function(statement, opts) {
  175. var newOpts = mergeOpts({reload: true}, opts);
  176. JSIO(statement, newOpts);
  177. }
  178. function run(moduleDef, cmd, opts, inlineOpts) {
  179. var newOpts = mergeOpts(opts, inlineOpts);
  180. JSIO.__importer({}, moduleDef.directory, moduleDef.filename, cmd, newOpts);
  181. }
  182. function mergeOpts(opts, inlineOpts) {
  183. var newOpts = JS.merge(JS.shallowCopy(opts), inlineOpts);
  184. // add compiler to the end of the preprocessors list
  185. if (newOpts.preprocessors) {
  186. for (var i = 0, len = newOpts.preprocessors.length; i < len; ++i) {
  187. if (newOpts.preprocessors[i] == 'compiler') {
  188. break;
  189. }
  190. }
  191. if (i == len) {
  192. newOpts.preprocessors.push('compiler');
  193. }
  194. } else {
  195. newOpts.preprocessors = ['compiler'];
  196. }
  197. return newOpts;
  198. }