From a03de01b2367f06f2360d45ccca03c8ca17d5293 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Thu, 5 Apr 2018 15:41:55 +0200 Subject: [PATCH 1/8] feat: make Flow linting possible With this change it's possible to lint Flow based modules. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b95b98d6f..a1f432d38 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "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", From a9d8aa84e675b63ac85b3cdf361cb637867d30ae Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Sat, 7 Apr 2018 19:28:27 +0200 Subject: [PATCH 2/8] fix: proper stack traces from tests With this change the stack traces when running the Node.js tests now display the correct source line of the error and not the line of the build. --- package.json | 1 + src/test/node.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a1f432d38..5cf7201d6 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,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/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 = [ From 65c74c3c7777856b4dfe0c7d2f919429fe5eba04 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Mon, 9 Apr 2018 13:55:45 +0200 Subject: [PATCH 3/8] fix: transpile webpack bundle with Babel --- package.json | 1 + src/config/webpack/index.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/package.json b/package.json index 5cf7201d6..ae5ef079c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "license": "MIT", "dependencies": { "async": "^2.6.0", + "babel-loader": "^7.1.4", "browserify-zlib": "^0.2.0", "chalk": "^2.3.0", "clean-documentation-theme": "^0.5.2", 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, From b4af0e6ecdb921e5d48f1518c0bf2133339bd9c6 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Tue, 10 Apr 2018 17:12:42 +0200 Subject: [PATCH 4/8] feat: Rename Browser build to dist `aegir build` builds a package for Browsers in the `dist` directory. The distinction between Browser and Node.js isn't that clear anymore. Hence this command is renamed to `dist`. --- cmds/build.js | 6 +++--- src/build/{browser.js => dist.js} | 24 +++++------------------- src/build/index.js | 26 ++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 24 deletions(-) rename src/build/{browser.js => dist.js} (75%) diff --git a/cmds/build.js b/cmds/build.js index 0dd4685ef..60f7ca3e6 100644 --- a/cmds/build.js +++ b/cmds/build.js @@ -7,9 +7,9 @@ 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 }, node: { 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..cf510597f 100644 --- a/src/build/index.js +++ b/src/build/index.js @@ -1,5 +1,27 @@ 'use strict' -const browserTasks = require('./browser') +const Listr = require('listr') -module.exports = browserTasks +const clean = require('../clean') +const dist = require('./dist') +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 +}], utils.getListrConfig()) + +module.exports = TASKS From 3920ace71abbab90ee16f8fb008c64bab309131b Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Tue, 10 Apr 2018 17:30:36 +0200 Subject: [PATCH 5/8] feat: Add Babel transpile step All files in the `src` diectory are now transpiled with Babel into the `lib` directory. --- cmds/build.js | 8 +++--- package.json | 1 + src/build/index.js | 5 ++++ src/build/lib.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/build/lib.js diff --git a/cmds/build.js b/cmds/build.js index 60f7ca3e6..5ce215ace 100644 --- a/cmds/build.js +++ b/cmds/build.js @@ -12,10 +12,10 @@ module.exports = { describe: 'Build dist package', default: true }, - node: { - alias: 'n', - describe: 'Build for node usage', - default: false + lib: { + alias: 'l', + describe: 'Transpile src to lib', + default: true } }, handler (argv) { diff --git a/package.json b/package.json index ae5ef079c..52524859a 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "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", "clean-documentation-theme": "^0.5.2", diff --git a/src/build/index.js b/src/build/index.js index cf510597f..c0f1411f3 100644 --- a/src/build/index.js +++ b/src/build/index.js @@ -4,6 +4,7 @@ const Listr = require('listr') const clean = require('../clean') const dist = require('./dist') +const lib = require('./lib') const utils = require('../utils') const TASKS = new Listr([{ @@ -22,6 +23,10 @@ const TASKS = new Listr([{ 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..47544a4dc --- /dev/null +++ b/src/build/lib.js @@ -0,0 +1,61 @@ +// Babel transform all *.js files from `src` to `lib` +'use strict' + +const path = require('path') + +const babelCore = require('babel-core') +const fs = require('fs-extra') +const glob = require('glob') +const pify = require('pify') + +const babelConfig = { + env: { + development: { + sourceMaps: 'inline', + comments: false, + presets: [ + [ + 'env', + { + targets: { + node: 'current' + } + } + ], + 'flow-node' + ] + } + } +} + +const transform = (src, dest) => { + // 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) + }) +} + +const babel = () => { + const src = 'src' + const dest = 'lib' + + return pify(glob)('./**/*.js', { + cwd: path.join(process.cwd(), src) + }).then((filenames) => { + return Promise.all(filenames.map((filename) => { + return transform(path.join(src, filename), path.join(dest, filename)) + })) + }) +} + +module.exports = babel From 82a00b6999ef89ad00d3c950664d6f955a224cb2 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Wed, 11 Apr 2018 14:57:57 +0200 Subject: [PATCH 6/8] feat: add watcher to Babel transpiling It is no possible to watch for changes in the source directory and get the files automatically transpiled. Run AEgir as aegir build --no-dist --watch --- cmds/build.js | 10 +++++++++ package.json | 1 + src/build/lib.js | 57 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/cmds/build.js b/cmds/build.js index 5ce215ace..05d31839d 100644 --- a/cmds/build.js +++ b/cmds/build.js @@ -1,7 +1,12 @@ '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', @@ -16,6 +21,11 @@ module.exports = { alias: 'l', describe: 'Transpile src to lib', default: true + }, + watch: { + alias: 'w', + describe: 'Keep watching source files for changes', + default: false } }, handler (argv) { diff --git a/package.json b/package.json index 52524859a..7dd306dcc 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "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", diff --git a/src/build/lib.js b/src/build/lib.js index 47544a4dc..9ebd4d1dc 100644 --- a/src/build/lib.js +++ b/src/build/lib.js @@ -4,9 +4,10 @@ const path = require('path') const babelCore = require('babel-core') +const chokidar = require('chokidar') const fs = require('fs-extra') -const glob = require('glob') -const pify = require('pify') + +const utils = require('../utils') const babelConfig = { env: { @@ -28,7 +29,17 @@ const babelConfig = { } } -const transform = (src, dest) => { +/** + * Babel transpiles a file from `src` to `lib` + * + * @param {string} filename The filename relative to the `src` directory + * + * @returns {Promise} + */ +const transform = (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) @@ -45,16 +56,38 @@ const transform = (src, dest) => { }) } -const babel = () => { - const src = 'src' - const dest = 'lib' +const babel = (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('Transpile file: ' + relative) + transform(relative) + }) + }) - return pify(glob)('./**/*.js', { - cwd: path.join(process.cwd(), src) - }).then((filenames) => { - return Promise.all(filenames.map((filename) => { - return transform(path.join(src, filename), path.join(dest, filename)) - })) + 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) + }) }) } From d590f4fa5336cb90327df5fef9f369e9445348a3 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Fri, 4 May 2018 00:27:45 +0200 Subject: [PATCH 7/8] feat: add Flow type checking Add `flow check` to check for type errors as additional linting step. The `.flowconfig` file in the root of AEgir is needed as else the linting would fail (although AEgir doesn't define any Flow types). This is intentional as this branch should only be used with projects using Flow. There this error message is really helpful. --- .flowconfig | 9 +++++++++ package.json | 1 + src/lint.js | 22 +++++++++++++++++++++- test/lint.spec.js | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .flowconfig 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/package.json b/package.json index 7dd306dcc..33f23410a 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "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", 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/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 }) From 6710eb0bd22254ecee0858e92ca00b8e8437b6a7 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Fri, 4 May 2018 00:46:48 +0200 Subject: [PATCH 8/8] feat: add copying Flow files In order to make the Flow types usable for the tooling, the flow files need to reside in the `lib` directory. The files are now copied when calling `aegir build`. Watching files is also supported. --- src/build/lib.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/build/lib.js b/src/build/lib.js index 9ebd4d1dc..b71ad169c 100644 --- a/src/build/lib.js +++ b/src/build/lib.js @@ -36,7 +36,7 @@ const babelConfig = { * * @returns {Promise} */ -const transform = (filename) => { +const babel = (filename) => { const src = path.join('src', filename) const dest = path.join('lib', filename) @@ -56,7 +56,21 @@ const transform = (filename) => { }) } -const babel = (ctx) => { +/** + * 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) => { @@ -74,8 +88,9 @@ const babel = (ctx) => { ;['add', 'change'].forEach((type) => { watcher.on(type, (filename) => { const relative = path.relative(srcDir, filename) - console.log('Transpile file: ' + relative) - transform(relative) + console.log('Transform file: ' + relative) + babel(relative) + flowCopy(relative) }) }) @@ -91,4 +106,4 @@ const babel = (ctx) => { }) } -module.exports = babel +module.exports = transform