cli.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/usr/bin/env node
  2. /*
  3. * EJS Embedded JavaScript templates
  4. * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. */
  19. let path = require('path');
  20. let program = require('jake').program;
  21. delete global.jake; // NO NOT WANT
  22. program.setTaskNames = function (n) { this.taskNames = n; };
  23. let ejs = require('../lib/ejs');
  24. let { hyphenToCamel } = require('../lib/utils');
  25. let fs = require('fs');
  26. let args = process.argv.slice(2);
  27. let usage = fs.readFileSync(`${__dirname}/../usage.txt`).toString();
  28. const CLI_OPTS = [
  29. { full: 'output-file',
  30. abbr: 'o',
  31. expectValue: true,
  32. },
  33. { full: 'data-file',
  34. abbr: 'f',
  35. expectValue: true,
  36. },
  37. { full: 'data-input',
  38. abbr: 'i',
  39. expectValue: true,
  40. },
  41. { full: 'delimiter',
  42. abbr: 'm',
  43. expectValue: true,
  44. passThrough: true,
  45. },
  46. { full: 'open-delimiter',
  47. abbr: 'p',
  48. expectValue: true,
  49. passThrough: true,
  50. },
  51. { full: 'close-delimiter',
  52. abbr: 'c',
  53. expectValue: true,
  54. passThrough: true,
  55. },
  56. { full: 'strict',
  57. abbr: 's',
  58. expectValue: false,
  59. allowValue: false,
  60. passThrough: true,
  61. },
  62. { full: 'no-with',
  63. abbr: 'n',
  64. expectValue: false,
  65. allowValue: false,
  66. },
  67. { full: 'locals-name',
  68. abbr: 'l',
  69. expectValue: true,
  70. passThrough: true,
  71. },
  72. { full: 'rm-whitespace',
  73. abbr: 'w',
  74. expectValue: false,
  75. allowValue: false,
  76. passThrough: true,
  77. },
  78. { full: 'debug',
  79. abbr: 'd',
  80. expectValue: false,
  81. allowValue: false,
  82. passThrough: true,
  83. },
  84. { full: 'help',
  85. abbr: 'h',
  86. passThrough: true,
  87. },
  88. { full: 'version',
  89. abbr: 'V',
  90. passThrough: true,
  91. },
  92. // Alias lowercase v
  93. { full: 'version',
  94. abbr: 'v',
  95. passThrough: true,
  96. },
  97. ];
  98. let preempts = {
  99. version: function () {
  100. program.die(ejs.VERSION);
  101. },
  102. help: function () {
  103. program.die(usage);
  104. }
  105. };
  106. let stdin = '';
  107. process.stdin.setEncoding('utf8');
  108. process.stdin.on('readable', () => {
  109. let chunk;
  110. while ((chunk = process.stdin.read()) !== null) {
  111. stdin += chunk;
  112. }
  113. });
  114. function run() {
  115. program.availableOpts = CLI_OPTS;
  116. program.parseArgs(args);
  117. let templatePath = program.taskNames[0];
  118. let pVals = program.envVars;
  119. let pOpts = {};
  120. for (let p in program.opts) {
  121. let name = hyphenToCamel(p);
  122. pOpts[name] = program.opts[p];
  123. }
  124. let opts = {};
  125. let vals = {};
  126. // Same-named 'passthrough' opts
  127. CLI_OPTS.forEach((opt) => {
  128. let optName = hyphenToCamel(opt.full);
  129. if (opt.passThrough && typeof pOpts[optName] != 'undefined') {
  130. opts[optName] = pOpts[optName];
  131. }
  132. });
  133. // Bail out for help/version
  134. for (let p in opts) {
  135. if (preempts[p]) {
  136. return preempts[p]();
  137. }
  138. }
  139. // Ensure there's a template to render
  140. if (!templatePath) {
  141. throw new Error('Please provide a template path. (Run ejs -h for help)');
  142. }
  143. if (opts.strict) {
  144. pOpts.noWith = true;
  145. }
  146. if (pOpts.noWith) {
  147. opts._with = false;
  148. }
  149. // Grab and parse any input data, in order of precedence:
  150. // 1. Stdin
  151. // 2. CLI arg via -i
  152. // 3. Data file via -f
  153. // Any individual vals passed at the end (e.g., foo=bar) will override
  154. // any vals previously set
  155. let input;
  156. let err = new Error('Please do not pass data multiple ways. Pick one of stdin, -f, or -i.');
  157. if (stdin) {
  158. input = stdin;
  159. }
  160. else if (pOpts.dataInput) {
  161. if (input) {
  162. throw err;
  163. }
  164. input = decodeURIComponent(pOpts.dataInput);
  165. }
  166. else if (pOpts.dataFile) {
  167. if (input) {
  168. throw err;
  169. }
  170. input = fs.readFileSync(pOpts.dataFile).toString();
  171. }
  172. if (input) {
  173. vals = JSON.parse(input);
  174. }
  175. // Override / set any individual values passed from the command line
  176. for (let p in pVals) {
  177. vals[p] = pVals[p];
  178. }
  179. opts.filename = path.resolve(process.cwd(), templatePath);
  180. let template = fs.readFileSync(opts.filename).toString();
  181. let output = ejs.render(template, vals, opts);
  182. if (pOpts.outputFile) {
  183. fs.writeFileSync(pOpts.outputFile, output);
  184. }
  185. else {
  186. process.stdout.write(output);
  187. }
  188. process.exit();
  189. }
  190. // Defer execution so that stdin can be read if necessary
  191. setImmediate(run);