JS.js

  1. /* @flow */
  2. import type {JSCompilerConfig} from './typedef';
  3. import {JSCompiler} from './JSCompiler';
  4. import type {NativeProcess} from './NativeProcess';
  5. import {JSLint} from './JSLint';
  6. import noop from 'lodash/noop';
  7. import {logError, logLintingErrors} from './logger';
  8. import {findBinary} from './findBinary';
  9. /**
  10. * JavaScript compilation tools.
  11. *
  12. * Wraps {@link JSCompiler} to add static analysis and linting. If you don't want that, use {@link JSCompiler} directly.
  13. *
  14. * @class JS
  15. * @param {JSCompilerConfig} [options={}] - configuration object
  16. * @param {string} [configFile="webcompiler/.eslintrc.yaml"] - path to the ESLint configuration file
  17. * @example
  18. * import {JS} from 'webcompiler';
  19. * // or - import {JS} from 'webcompiler/lib/JS';
  20. * // or - var JS = require('webcompiler').JS;
  21. * // or - var JS = require('webcompiler/lib/JS').JS;
  22. * import {join} from 'path';
  23. *
  24. * const srcDir = join(__dirname, 'src'),
  25. * libDir = join(__dirname, 'lib');
  26. *
  27. * const js = new JS();
  28. *
  29. * // compile for the browser
  30. * js.fe(join(srcDir, 'script.js'), join(libDir, 'script.js'));
  31. *
  32. * // compile for Node.js
  33. * js.be(join(srcDir, 'script.js'), join(libDir, 'script.js'));
  34. *
  35. * // compile entire directories for Node.js (non-JavaScript files are simply copied over)
  36. * js.be(srcDir, libDir);
  37. */
  38. export class JS {
  39. /**
  40. * JavaScript compiler
  41. *
  42. * @member {JSCompiler} compiler
  43. * @memberof JS
  44. * @private
  45. * @instance
  46. */
  47. compiler: JSCompiler;
  48. /**
  49. * JavaScript linter
  50. *
  51. * @member {JSLint} linter
  52. * @memberof JS
  53. * @private
  54. * @instance
  55. */
  56. linter: JSLint;
  57. // eslint-disable-next-line require-jsdoc
  58. constructor(options?: JSCompilerConfig, configFile?: string) {
  59. this.compiler = new JSCompiler(options);
  60. this.linter = new JSLint(configFile);
  61. }
  62. /**
  63. * Performs static analysis
  64. *
  65. * @memberof JS
  66. * @static
  67. * @method typecheck
  68. * @param {Function} callback - a callback function, invoked only when successfully typechecked
  69. */
  70. static typecheck(callback: () => void) {
  71. findBinary('flow', (error, flow: NativeProcess) => {
  72. if (error) {
  73. return logError(error);
  74. }
  75. flow.run((_, stdout) => {
  76. if (!JSON.parse(stdout).passed) {
  77. return flow.run(noop, [], {stdio: 'inherit'});
  78. }
  79. callback();
  80. }, ['--json']);
  81. });
  82. }
  83. /**
  84. * Performs linting
  85. *
  86. * @memberof JS
  87. * @instance
  88. * @method lint
  89. * @param {Array<string>} paths - an array of paths to files/directories to lint
  90. * @param {Function} callback - a callback function, invoked only when successfully linted
  91. */
  92. lint(paths: string[], callback: () => void) {
  93. this.linter.run(paths, linterErr => {
  94. if (linterErr) {
  95. return logLintingErrors(linterErr, 'JavaScript');
  96. }
  97. callback();
  98. });
  99. }
  100. /**
  101. * Performs static analysis and linting
  102. *
  103. * @memberof JS
  104. * @instance
  105. * @private
  106. * @method validate
  107. * @param {string} inPath - the input file (will also be linted)
  108. * @param {Array<string>} lintPaths - an array of paths to files/directories to lint
  109. * @param {Function} callback - a callback function, invoked only when successfully validated
  110. */
  111. validate(inPath: string, lintPaths: string[], callback: () => void) {
  112. JS.typecheck(() => {
  113. this.lint(lintPaths.concat([inPath]), callback);
  114. });
  115. }
  116. /**
  117. * Wraps {@link JSCompiler#be} to add static analysis and linting.
  118. *
  119. * If `inPath` is a directory, `outPath` has to be also.
  120. *
  121. * @memberOf JS
  122. * @instance
  123. * @method be
  124. * @param {string} inPath - the input file/directory path
  125. * @param {string} outPath - the output file/directory path
  126. * @param {Array<string>} [lintPaths=[]] - an array of paths to files/directories to lint
  127. * @param {Function} [callback=function () {}] - a callback function
  128. */
  129. be(inPath: string, outPath: string, lintPaths: string[] = [], callback: () => void = noop) {
  130. this.validate(inPath, lintPaths, () => {
  131. this.compiler.be(inPath, outPath, callback);
  132. });
  133. }
  134. /**
  135. * Wraps {@link JSCompiler#fe} to add static analysis and linting
  136. *
  137. * @memberOf JS
  138. * @instance
  139. * @method fe
  140. * @param {string} inPath - the input file path
  141. * @param {string} outPath - the output file path
  142. * @param {Array<string>} [lintPaths=[]] - an array of paths to files/directories to lint
  143. * @param {Function} [callback=function () {}] - a callback function
  144. */
  145. fe(inPath: string, outPath: string, lintPaths: string[] = [], callback: () => void = noop) {
  146. this.validate(inPath, lintPaths, () => {
  147. this.compiler.fe(inPath, outPath, callback);
  148. });
  149. }
  150. }