From 1984203e87c31015bb0df409c4f4cffcc1adcde4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 4 Mar 2018 15:11:36 -0500 Subject: [PATCH 1/2] vastly better logging --- package.json | 5 +- rollup.config.js | 2 + src/cli/dev.ts | 206 ++++++++++++++++++++++++++++++----------------- 3 files changed, 136 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index 89ec2b2d9..6fa301c1c 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "mkdirp": "^0.5.1", "mri": "^1.1.0", "node-fetch": "^1.7.3", + "pretty-ms": "^3.1.0", "relative": "^3.0.2", "require-relative": "^0.8.7", "rimraf": "^2.6.2", @@ -39,7 +40,8 @@ "source-map-support": "^0.5.3", "tslib": "^1.8.1", "url-parse": "^1.2.0", - "walk-sync": "^0.3.2" + "walk-sync": "^0.3.2", + "webpack-format-messages": "^1.0.1" }, "devDependencies": { "@std/esm": "^0.19.7", @@ -55,6 +57,7 @@ "nightmare": "^2.10.0", "npm-run-all": "^4.1.2", "rollup": "^0.53.0", + "rollup-plugin-commonjs": "^8.3.0", "rollup-plugin-json": "^2.3.0", "rollup-plugin-string": "^2.0.2", "rollup-plugin-typescript": "^0.8.1", diff --git a/rollup.config.js b/rollup.config.js index 1e13bb96a..6bad2d66a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,6 +1,7 @@ import typescript from 'rollup-plugin-typescript'; import string from 'rollup-plugin-string'; import json from 'rollup-plugin-json'; +import commonjs from 'rollup-plugin-commonjs'; import pkg from './package.json'; const external = [].concat( @@ -18,6 +19,7 @@ const plugins = [ include: '**/*.md' }), json(), + commonjs(), typescript({ typescript: require('typescript') }) diff --git a/src/cli/dev.ts b/src/cli/dev.ts index 2fa637586..3ade01a06 100644 --- a/src/cli/dev.ts +++ b/src/cli/dev.ts @@ -6,6 +6,8 @@ import * as child_process from 'child_process'; import * as http from 'http'; import mkdirp from 'mkdirp'; import rimraf from 'rimraf'; +import format_messages from 'webpack-format-messages'; +import prettyMs from 'pretty-ms'; import { wait_for_port } from './utils'; import { dest } from '../config'; import { create_compilers, create_app, create_routes, create_serviceworker } from 'sapper/core.js'; @@ -69,13 +71,12 @@ function create_hot_update_server(port: number, interval = 10000) { } export default async function dev() { - const dir = dest(); + process.env.NODE_ENV = 'development'; + const dir = dest(); rimraf.sync(dir); mkdirp.sync(dir); - const chokidar = require('chokidar'); - // initial build const dev_port = await require('get-port')(10000); @@ -84,20 +85,6 @@ export default async function dev() { const hot_update_server = create_hot_update_server(dev_port); - // TODO watch the configs themselves? - const compilers = create_compilers(); - - function watch_files(pattern: string, events: string[], callback: () => void) { - const watcher = chokidar.watch(pattern, { - persistent: true, - ignoreInitial: true - }); - - events.forEach(event => { - watcher.on(event, callback); - }); - } - watch_files('routes/**/*', ['add', 'unlink'], () => { const routes = create_routes(); create_app({ routes, dev_port }); @@ -116,31 +103,106 @@ export default async function dev() { client: deferred() }; - const times = { - client_start: Date.now(), - server_start: Date.now(), - serviceworker_start: Date.now() + let restarting = false; + let build = { + unique_warnings: new Set(), + unique_errors: new Set() }; - compilers.server.plugin('invalid', () => { - times.server_start = Date.now(); - // TODO print message - deferreds.server = deferred(); - }); + function restart_build(filename) { + if (restarting) return; - compilers.server.watch({}, (err: Error, stats: any) => { - if (err) { - console.error(chalk.red(err.message)); - } else if (stats.hasErrors()) { - // print errors. TODO notify client - stats.toJson().errors.forEach((error: Error) => { - console.error(error); // TODO make this look nice - }); - } else { - console.log(`built server in ${Date.now() - times.server_start}ms`); // TODO prettify + restarting = true; + build = { + unique_warnings: new Set(), + unique_errors: new Set() + }; + + process.nextTick(() => { + restarting = false; + }); + + console.log(`\n${chalk.bold.cyan(path.relative(process.cwd(), filename))} changed. rebuilding...`); + } + + // TODO watch the configs themselves? + const compilers = create_compilers(); + + function watch(compiler: any, { name, invalid = noop, error = noop, result }: { + name: string, + invalid?: (filename: string) => void; + error?: (error: Error) => void; + result: (stats: any) => void; + }) { + compiler.plugin('invalid', (filename: string) => { + invalid(filename); + }); + + compiler.watch({}, (err: Error, stats: any) => { + if (err) { + console.error(chalk.red(`✗ ${name}`)); + console.error(chalk.red(err.message)); + error(err); + } else { + const messages = format_messages(stats); + const info = stats.toJson(); + + if (messages.errors.length > 0) { + console.log(chalk.bold.red(`✗ ${name}`)); - const server_info = stats.toJson(); - fs.writeFileSync(path.join(dir, 'server_info.json'), JSON.stringify(server_info, null, ' ')); + const filtered = messages.errors.filter((message: string) => { + return !build.unique_errors.has(message); + }); + + filtered.forEach((message: string) => { + build.unique_errors.add(message); + console.log(message); + }); + + const hidden = messages.errors.length - filtered.length; + if (hidden > 0) { + console.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`); + } + } else { + if (messages.warnings.length > 0) { + console.log(chalk.bold.yellow(`• ${name}`)); + + const filtered = messages.warnings.filter((message: string) => { + return !build.unique_warnings.has(message); + }); + + filtered.forEach((message: string) => { + build.unique_warnings.add(message); + console.log(`${message}\n`); + }); + + const hidden = messages.warnings.length - filtered.length; + if (hidden > 0) { + console.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`); + } + } else { + console.log(`${chalk.bold.green(`✔ ${name}`)} ${chalk.grey(`(${prettyMs(info.time)})`)}`); + } + + result(info); + } + } + }); + } + + watch(compilers.server, { + name: 'server', + + invalid: filename => { + restart_build(filename); + // TODO print message + deferreds.server = deferred(); + }, + + result: info => { + // TODO log compile errors/warnings + + fs.writeFileSync(path.join(dir, 'server_info.json'), JSON.stringify(info, null, ' ')); deferreds.client.promise.then(() => { function restart() { @@ -162,32 +224,23 @@ export default async function dev() { } }); - compilers.client.plugin('invalid', (filename: string) => { - times.client_start = Date.now(); + watch(compilers.client, { + name: 'client', - deferreds.client = deferred(); + invalid: filename => { + restart_build(filename); + deferreds.client = deferred(); - // TODO we should delete old assets. due to a webpack bug - // i don't even begin to comprehend, this is apparently - // quite difficult - }); + // TODO we should delete old assets. due to a webpack bug + // i don't even begin to comprehend, this is apparently + // quite difficult + }, - compilers.client.watch({}, (err: Error, stats: any) => { - if (err) { - console.error(chalk.red(err.message)); - } else if (stats.hasErrors()) { - // print errors. TODO notify client - stats.toJson().errors.forEach((error: Error) => { - console.error(error); // TODO make this look nice - }); - } else { - console.log(`built client in ${Date.now() - times.client_start}ms`); // TODO prettify - - const client_info = stats.toJson(); - fs.writeFileSync(path.join(dir, 'client_info.json'), JSON.stringify(client_info, null, ' ')); + result: info => { + fs.writeFileSync(path.join(dir, 'client_info.json'), JSON.stringify(info, null, ' ')); deferreds.client.fulfil(); - const client_files = client_info.assets.map((chunk: { name: string }) => `/client/${chunk.name}`); + const client_files = info.assets.map((chunk: { name: string }) => `/client/${chunk.name}`); deferreds.server.promise.then(() => { hot_update_server.send({ @@ -208,27 +261,28 @@ export default async function dev() { ? function() { watch_serviceworker = noop; - compilers.serviceworker.plugin('invalid', (filename: string) => { - times.serviceworker_start = Date.now(); - }); - - compilers.serviceworker.watch({}, (err: Error, stats: any) => { - if (err) { - // TODO notify client - } else if (stats.hasErrors()) { - // print errors. TODO notify client - stats.toJson().errors.forEach((error: Error) => { - console.error(error); // TODO make this look nice - }); - } else { - console.log(`built service worker in ${Date.now() - times.serviceworker_start}ms`); // TODO prettify + watch(compilers.serviceworker, { + name: 'service worker', - const serviceworker_info = stats.toJson(); - fs.writeFileSync(path.join(dir, 'serviceworker_info.json'), JSON.stringify(serviceworker_info, null, ' ')); + result: info => { + fs.writeFileSync(path.join(dir, 'serviceworker_info.json'), JSON.stringify(info, null, ' ')); } }); } : noop; } -function noop() {} \ No newline at end of file +function noop() {} + +function watch_files(pattern: string, events: string[], callback: () => void) { + const chokidar = require('chokidar'); + + const watcher = chokidar.watch(pattern, { + persistent: true, + ignoreInitial: true + }); + + events.forEach(event => { + watcher.on(event, callback); + }); +} \ No newline at end of file From 368e6d5cb1fa3e5fda9df842c29123f5e234aefe Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 4 Mar 2018 15:27:35 -0500 Subject: [PATCH 2/2] bump timeout for travis --- test/common/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/test.js b/test/common/test.js index 61595b2d6..9e25b3cf0 100644 --- a/test/common/test.js +++ b/test/common/test.js @@ -25,7 +25,7 @@ Nightmare.action('init', function(done) { function run(env) { describe(`env=${env}`, function () { - this.timeout(20000); + this.timeout(30000); let PORT; let proc;