diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 000000000..57c6722d5 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,9 @@ +[ignore] +.*/node_modules/documentation/* + +[libs] + +[include] + +[options] +suppress_comment= \\(.\\|\n\\)*\\@FlowIgnore diff --git a/cmds/build.js b/cmds/build.js index 0dd4685ef..05d31839d 100644 --- a/cmds/build.js +++ b/cmds/build.js @@ -1,20 +1,30 @@ 'use strict' +const path = require('path') + +const chokidar = require('chokidar') + const build = require('../src/build') const onError = require('../src/error-handler') +const utils = require('../src/utils') module.exports = { command: 'build', desc: 'Build ready to release', builder: { - browser: { - alias: 'b', - describe: 'Build for browser usage', + dist: { + alias: 'd', + describe: 'Build dist package', + default: true + }, + lib: { + alias: 'l', + describe: 'Transpile src to lib', default: true }, - node: { - alias: 'n', - describe: 'Build for node usage', + watch: { + alias: 'w', + describe: 'Keep watching source files for changes', default: false } }, diff --git a/package.json b/package.json index b95b98d6f..33f23410a 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,11 @@ "license": "MIT", "dependencies": { "async": "^2.6.0", + "babel-loader": "^7.1.4", + "babel-preset-flow-node": "^2.0.1", "browserify-zlib": "^0.2.0", "chalk": "^2.3.0", + "chokidar": "^2.0.3", "clean-documentation-theme": "^0.5.2", "codecov": "^3.0.0", "conventional-changelog": "^1.1.7", @@ -41,10 +44,11 @@ "documentation": "^5.3.3", "es6-promisify": "^5.0.0", "eslint": "^4.13.0", - "eslint-config-aegir": "^1.0.1", + "eslint-config-aegir": "git+https://github.com/ipfs/eslint-config-aegir#flow", "execa": "^0.8.0", "filesize": "^3.5.11", "findup-sync": "^2.0.0", + "flow-bin": "^0.71.0", "fs-extra": "^4.0.3", "gh-pages": "^1.1.0", "glob": "^7.1.2", @@ -75,6 +79,7 @@ "safe-buffer": "^5.1.1", "semver": "^5.4.1", "simple-git": "^1.85.0", + "source-map-support": "^0.5.4", "stream-http": "^2.7.2", "through": "^2.3.8", "transform-loader": "^0.2.4", diff --git a/src/build/browser.js b/src/build/dist.js similarity index 75% rename from src/build/browser.js rename to src/build/dist.js index 118ad738f..e891badd3 100644 --- a/src/build/browser.js +++ b/src/build/dist.js @@ -3,13 +3,10 @@ const webpack = require('webpack') const Uglify = require('uglify-es') const path = require('path') -const Listr = require('listr') const fs = require('fs-extra') const filesize = require('filesize') const pify = require('pify') -const clean = require('../clean') -const utils = require('../utils') const config = require('../config/webpack') function webpackBuild (ctx, task) { @@ -59,19 +56,8 @@ function minify (ctx, task) { }) } -const TASKS = new Listr([{ - title: 'Clean ./dist', - task: () => clean('dist') -}, { - title: 'Webpack Build', - task: webpackBuild -}, { - title: 'Write stats to disk', - task: writeStats, - enabled: (ctx) => ctx.webpackResult != null && ctx.stats -}, { - title: 'Minify', - task: minify -}], utils.getListrConfig()) - -module.exports = TASKS +module.exports = { + webpackBuild: webpackBuild, + writeStats: writeStats, + minify: minify +} diff --git a/src/build/index.js b/src/build/index.js index 156696467..c0f1411f3 100644 --- a/src/build/index.js +++ b/src/build/index.js @@ -1,5 +1,32 @@ 'use strict' -const browserTasks = require('./browser') +const Listr = require('listr') -module.exports = browserTasks +const clean = require('../clean') +const dist = require('./dist') +const lib = require('./lib') +const utils = require('../utils') + +const TASKS = new Listr([{ + title: 'Clean ./dist', + task: () => clean('dist'), + enabled: (ctx) => ctx.dist +}, { + title: 'Webpack Build', + task: dist.webpackBuild, + enabled: (ctx) => ctx.dist +}, { + title: 'Write stats to disk', + task: dist.writeStats, + enabled: (ctx) => ctx.dist && ctx.webpackResult != null && ctx.stats +}, { + title: 'Minify', + task: dist.minify, + enabled: (ctx) => ctx.dist +}, { + title: 'Transpile src to lib', + task: lib, + enabled: (ctx) => ctx.lib +}], utils.getListrConfig()) + +module.exports = TASKS diff --git a/src/build/lib.js b/src/build/lib.js new file mode 100644 index 000000000..b71ad169c --- /dev/null +++ b/src/build/lib.js @@ -0,0 +1,109 @@ +// Babel transform all *.js files from `src` to `lib` +'use strict' + +const path = require('path') + +const babelCore = require('babel-core') +const chokidar = require('chokidar') +const fs = require('fs-extra') + +const utils = require('../utils') + +const babelConfig = { + env: { + development: { + sourceMaps: 'inline', + comments: false, + presets: [ + [ + 'env', + { + targets: { + node: 'current' + } + } + ], + 'flow-node' + ] + } + } +} + +/** + * Babel transpiles a file from `src` to `lib` + * + * @param {string} filename The filename relative to the `src` directory + * + * @returns {Promise} + */ +const babel = (filename) => { + const src = path.join('src', filename) + const dest = path.join('lib', filename) + + // To have the right filename in the source map + babelConfig.sourceFileName = path.join('..', src) + + return new Promise((resolve, reject) => { + babelCore.transformFile( + src, babelConfig, (err, result) => { + if (err) { + return reject(err) + } + resolve(result.code) + }) + }).then((code) => { + return fs.outputFile(dest, code) + }) +} + +/** + * Copies a file from `src` to `lib` with flow extension + * + * @param {string} filename The filename relative to the `src` directory + * + * @returns {Promise} + */ +const flowCopy = (filename) => { + const src = path.join('src', filename) + const dest = path.join('lib', filename + '.flow') + + return fs.copy(src, dest) +} + +const transform = (ctx) => { + const srcDir = path.join(utils.getBasePath(), 'src') + + return new Promise((resolve, reject) => { + // The watcher code is based on the babel-cli code (MIT licensed): + // https://github.com/babel/babel/blob/6597a472b30419493f123bff1e9194e4c09e488e/packages/babel-cli/src/babel/dir.js#L164-L188` + const watcher = chokidar.watch(srcDir, { + persistent: ctx.watch, + ignoreInitial: false, + awaitWriteFinish: { + stabilityThreshold: 50, + pollInterval: 10 + } + }) + + ;['add', 'change'].forEach((type) => { + watcher.on(type, (filename) => { + const relative = path.relative(srcDir, filename) + console.log('Transform file: ' + relative) + babel(relative) + flowCopy(relative) + }) + }) + + watcher + .on('ready', () => { + // Finish the task after the initial scan. If files are watched, the + // task will keep running though. + resolve() + }) + .on('error', (err) => { + reject(err) + }) + }) +} + +module.exports = transform diff --git a/src/config/webpack/index.js b/src/config/webpack/index.js index 1dc027af3..c4250b6bd 100644 --- a/src/config/webpack/index.js +++ b/src/config/webpack/index.js @@ -32,6 +32,13 @@ function webpackConfig (env) { entry ], devtool: sourcemap, + module: { + rules: [{ + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader' + }] + }, output: { filename: path.basename(entry), library: libraryName, diff --git a/src/lint.js b/src/lint.js index 84ec4c19a..8c35a76eb 100644 --- a/src/lint.js +++ b/src/lint.js @@ -1,6 +1,10 @@ 'use strict' +const util = require('util') +const execFile = util.promisify(require('child_process').execFile) + const CLIEngine = require('eslint').CLIEngine +const flow = require('flow-bin') const path = require('path') const formatter = CLIEngine.getFormatter() @@ -18,7 +22,7 @@ const FILES = [ '!**/node_modules/**' ] -function lint (opts) { +const eslint = (opts) => { return new Promise((resolve, reject) => { const cli = new CLIEngine({ useEslintrc: true, @@ -37,4 +41,20 @@ function lint (opts) { }) } +const typecheck = async (_opts) => { + try { + const { stdout } = await execFile(flow, ['check', '--color=always']) + console.log(stdout) + } catch (err) { + console.error(err.stdout) + console.error(err.stderr) + throw new Error('Type check errors') + } +} + +const lint = async (opts) => { + await eslint(opts) + await typecheck(opts) +} + module.exports = lint diff --git a/src/test/node.js b/src/test/node.js index 5e7bcd543..2bf35f64c 100644 --- a/src/test/node.js +++ b/src/test/node.js @@ -27,7 +27,8 @@ function testNode (ctx) { let args = [ '--ui', 'bdd', - '--colors' + '--colors', + '--require', 'source-map-support/register' ] let files = [ diff --git a/test/lint.spec.js b/test/lint.spec.js index 0198ea8cb..6f9691976 100644 --- a/test/lint.spec.js +++ b/test/lint.spec.js @@ -6,7 +6,7 @@ const lint = require('../src/lint') describe('lint', () => { it('passes', function () { // slow ci is slow, appveyor is even slower... - this.timeout(5000) + this.timeout(20000) return lint({ fix: false })