/* @flow */
import type {JSCompilerConfig} from './typedef';
import {JSCompiler} from './JSCompiler';
import type {NativeProcess} from './NativeProcess';
import {JSLint} from './JSLint';
import noop from 'lodash/noop';
import {logError, logLintingErrors} from './logger';
import {findBinary} from './findBinary';
/**
* JavaScript compilation tools.
*
* Wraps {@link JSCompiler} to add static analysis and linting. If you don't want that, use {@link JSCompiler} directly.
*
* @class JS
* @param {JSCompilerConfig} [options={}] - configuration object
* @param {string} [configFile="webcompiler/.eslintrc.yaml"] - path to the ESLint configuration file
* @example
* import {JS} from 'webcompiler';
* // or - import {JS} from 'webcompiler/lib/JS';
* // or - var JS = require('webcompiler').JS;
* // or - var JS = require('webcompiler/lib/JS').JS;
* import {join} from 'path';
*
* const srcDir = join(__dirname, 'src'),
* libDir = join(__dirname, 'lib');
*
* const js = new JS();
*
* // compile for the browser
* js.fe(join(srcDir, 'script.js'), join(libDir, 'script.js'));
*
* // compile for Node.js
* js.be(join(srcDir, 'script.js'), join(libDir, 'script.js'));
*
* // compile entire directories for Node.js (non-JavaScript files are simply copied over)
* js.be(srcDir, libDir);
*/
export class JS {
/**
* JavaScript compiler
*
* @member {JSCompiler} compiler
* @memberof JS
* @private
* @instance
*/
compiler: JSCompiler;
/**
* JavaScript linter
*
* @member {JSLint} linter
* @memberof JS
* @private
* @instance
*/
linter: JSLint;
// eslint-disable-next-line require-jsdoc
constructor(options?: JSCompilerConfig, configFile?: string) {
this.compiler = new JSCompiler(options);
this.linter = new JSLint(configFile);
}
/**
* Performs static analysis
*
* @memberof JS
* @static
* @method typecheck
* @param {Function} callback - a callback function, invoked only when successfully typechecked
*/
static typecheck(callback: () => void) {
findBinary('flow', (error, flow: NativeProcess) => {
if (error) {
return logError(error);
}
flow.run((_, stdout) => {
if (!JSON.parse(stdout).passed) {
return flow.run(noop, [], {stdio: 'inherit'});
}
callback();
}, ['--json']);
});
}
/**
* Performs linting
*
* @memberof JS
* @instance
* @method lint
* @param {Array<string>} paths - an array of paths to files/directories to lint
* @param {Function} callback - a callback function, invoked only when successfully linted
*/
lint(paths: string[], callback: () => void) {
this.linter.run(paths, linterErr => {
if (linterErr) {
return logLintingErrors(linterErr, 'JavaScript');
}
callback();
});
}
/**
* Performs static analysis and linting
*
* @memberof JS
* @instance
* @private
* @method validate
* @param {string} inPath - the input file (will also be linted)
* @param {Array<string>} lintPaths - an array of paths to files/directories to lint
* @param {Function} callback - a callback function, invoked only when successfully validated
*/
validate(inPath: string, lintPaths: string[], callback: () => void) {
JS.typecheck(() => {
this.lint(lintPaths.concat([inPath]), callback);
});
}
/**
* Wraps {@link JSCompiler#be} to add static analysis and linting.
*
* If `inPath` is a directory, `outPath` has to be also.
*
* @memberOf JS
* @instance
* @method be
* @param {string} inPath - the input file/directory path
* @param {string} outPath - the output file/directory path
* @param {Array<string>} [lintPaths=[]] - an array of paths to files/directories to lint
* @param {Function} [callback=function () {}] - a callback function
*/
be(inPath: string, outPath: string, lintPaths: string[] = [], callback: () => void = noop) {
this.validate(inPath, lintPaths, () => {
this.compiler.be(inPath, outPath, callback);
});
}
/**
* Wraps {@link JSCompiler#fe} to add static analysis and linting
*
* @memberOf JS
* @instance
* @method fe
* @param {string} inPath - the input file path
* @param {string} outPath - the output file path
* @param {Array<string>} [lintPaths=[]] - an array of paths to files/directories to lint
* @param {Function} [callback=function () {}] - a callback function
*/
fe(inPath: string, outPath: string, lintPaths: string[] = [], callback: () => void = noop) {
this.validate(inPath, lintPaths, () => {
this.compiler.fe(inPath, outPath, callback);
});
}
}