Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Watch mode without dev server #1427

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-ligers-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'preact-cli': minor
---

This change allows a user to disable the Webpack dev server while using the CLI's 'watch' mode. Useful for developing apps for SSR as this is much quicker without the production optimizations and will auto-run on changes to the source.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Spin up a development server with multiple features like `hot-module-replacement
$ preact watch

--src Specify source directory (default src)
--dest Specify output directory if dev server is disabled (default build)
--cwd A directory to use instead of $PWD (default .)
--esm Builds ES-2015 bundles for your code (default false)
--clear Clear the console (default true)
Expand All @@ -150,6 +151,7 @@ $ preact watch
--prerenderUrls Path to pre-rendered routes config (default prerender-urls.json)
--template Path to custom HTML template
--refresh Will use [`Preact-refresh`](https://github.com/JoviDeCroock/preact-refresh) to do hot-reloading
--devServer Determine if dev server should be enabled (default true)
-c, --config Path to custom CLI config (default preact.config.js)
-H, --host Set server hostname (default 0.0.0.0)
-p, --port Set server port (default 8080)
Expand Down
15 changes: 14 additions & 1 deletion packages/cli/lib/commands/watch.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
const runWebpack = require('../lib/webpack/run-webpack');
const rimraf = require('rimraf');
const { resolve } = require('path');
const { promisify } = require('util');
const { warn } = require('../util');
const runWebpack = require('../lib/webpack/run-webpack');

const toBool = (val) => val === void 0 || (val === 'false' ? false : val);

module.exports = async function (src, argv) {
if (argv.rhl) {
delete argv.rhl;
argv.refresh = argv.rhl;
}

argv.src = src || argv.src;
argv.production = false;
argv.devServer = toBool(argv.devServer);
if (!argv.devServer) argv.prerender = true;

if (argv.https || process.env.HTTPS) {
let { key, cert, cacert } = argv;
Expand All @@ -18,5 +26,10 @@ module.exports = async function (src, argv) {
}
}

if (argv.clean === void 0) {
let dest = resolve(argv.cwd, argv.dest);
await promisify(rimraf)(dest);
}

return runWebpack(argv, true);
};
6 changes: 6 additions & 0 deletions packages/cli/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ prog
.command('watch [src]')
.describe('Start a live-reload server for development')
.option('--src', 'Specify source directory', 'src')
.option(
'--dest',
'Specify output directory if dev server is disabled',
'build'
)
.option('--cwd', 'A directory to use instead of $PWD', '.')
.option('--esm', 'Builds ES-2015 bundles for your code', false)
.option('--clear', 'Clear the console', true)
Expand All @@ -101,6 +106,7 @@ prog
'Enables experimental preact-refresh functionality',
false
)
.option('--devServer', 'Determine if dev server should be enabled', true)
.option('-c, --config', 'Path to custom CLI config', 'preact.config.js')
.option('-H, --host', 'Set server hostname', '0.0.0.0')
.option('-p, --port', 'Set server port', 8080)
Expand Down
28 changes: 27 additions & 1 deletion packages/cli/lib/lib/webpack/run-webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ const { error, isDir, warn } = require('../../util');

async function devBuild(env) {
let config = await clientConfig(env);

await transformConfig(env, config);

const shouldRunDevServer = env.devServer;

if (!shouldRunDevServer && env.prerender) {
let ssrConfig = serverConfig(env);
await transformConfig(env, ssrConfig, true);
let serverCompiler = webpack(ssrConfig);
await runWatch(serverCompiler);
}

let userPort =
parseInt(process.env.PORT || config.devServer.port, 10) || 8080;
let port = await getPort({ port: userPort });
Expand All @@ -39,6 +47,8 @@ async function devBuild(env) {
});

compiler.hooks.done.tap('CliDevPlugin', (stats) => {
if (!shouldRunDevServer) return;

let devServer = config.devServer;
let protocol = process.env.HTTPS || devServer.https ? 'https' : 'http';
let host = process.env.HOST || devServer.host || 'localhost';
Expand Down Expand Up @@ -75,6 +85,8 @@ async function devBuild(env) {
stats: { colors: true },
});

if (!shouldRunDevServer) return res(runWatch(compiler, c));

let server = new DevServer(compiler, c);
server.listen(port);
res(server);
Expand Down Expand Up @@ -121,6 +133,20 @@ function runCompiler(compiler) {
});
}

function runWatch(compiler, options = {}) {
return new Promise((res, rej) => {
compiler.watch(options, (err, stats) => {
showStats(stats, false);

if (err || (stats && stats.hasErrors())) {
rej(`${red('\n\nBuild failed! \n\n')} ${err || ''}`);
}

res(stats);
});
});
}

function showStats(stats, isProd) {
if (stats) {
if (stats.hasErrors()) {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/lib/lib/webpack/webpack-base-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function getSassConfiguration(...includePaths) {
}

module.exports = function (env) {
const { cwd, isProd, isWatch, src, source } = env;
const { cwd, isProd, isWatch, devServer, src, source } = env;
const babelConfigFile = env.babelConfig || '.babelrc';
const IS_SOURCE_PREACT_X_OR_ABOVE = isInstalledVersionPreactXOrAbove(cwd);
// Apply base-level `env` values
Expand Down Expand Up @@ -227,7 +227,7 @@ module.exports = function (env) {
test: /\.(p?css|less|s[ac]ss|styl)$/,
include: [source('components'), source('routes')],
use: [
isWatch ? 'style-loader' : MiniCssExtractPlugin.loader,
devServer ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
Expand All @@ -253,7 +253,7 @@ module.exports = function (env) {
test: /\.(p?css|less|s[ac]ss|styl)$/,
exclude: [source('components'), source('routes')],
use: [
isWatch ? 'style-loader' : MiniCssExtractPlugin.loader,
devServer ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/lib/lib/webpack/webpack-client-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ function isProd(config) {
'process.env.ESM': config.esm,
'process.env.PRERENDER': config.prerender,
}),
new SizePlugin()
new SizePlugin(),
],

optimization: {
Expand Down
13 changes: 9 additions & 4 deletions packages/cli/tests/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const argv = {
'inline-css': true,
};

exports.create = async function(template, name) {
exports.create = async function (template, name) {
let dest = tmpDir();
name = name || `test-${template}`;

Expand All @@ -35,7 +35,7 @@ exports.create = async function(template, name) {
return dest;
};

exports.build = function(cwd, options, installNodeModules = false) {
exports.build = function (cwd, options, installNodeModules = false) {
if (!installNodeModules) {
mkdirp.sync(join(cwd, 'node_modules')); // ensure exists, avoid exit()
linkPackage('preact', root, cwd);
Expand All @@ -48,7 +48,12 @@ exports.build = function(cwd, options, installNodeModules = false) {
return cmd.build(argv.src, Object.assign({}, opts, options));
};

exports.watch = function(cwd, port, host = '127.0.0.1') {
let opts = Object.assign({ cwd, host, port, https: false }, argv);
exports.watch = function (cwd, port, host = '127.0.0.1', devServer = true) {
if (!devServer) {
mkdirp.sync(join(cwd, 'node_modules')); // ensure exists, avoid exit()
linkPackage('preact', root, cwd);
linkPackage('preact-render-to-string', root, cwd);
}
let opts = Object.assign({ cwd, host, port, https: false, devServer }, argv);
return cmd.watch(argv.src, opts);
};
25 changes: 25 additions & 0 deletions packages/cli/tests/watch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const fs = require('../lib/fs');
const { resolve } = require('path');
const startChrome = require('./lib/chrome');
const { create, watch } = require('./lib/cli');
const { sleep } = require('./lib/utils');

const { loadPage, waitUntilExpression } = startChrome;
let chrome, server;
Expand Down Expand Up @@ -33,4 +34,28 @@ describe('preact', () => {

server.close();
});

it('should hot reload and prerender without a development server', async () => {
let dir = await create('default');
await watch(dir, undefined, undefined, false);

const initialContent = await fs.readFile(
resolve(dir, './build/bundle.js'),
'utf8'
);

let header = resolve(dir, './src/components/header/index.js');
let original = await fs.readFile(header, 'utf8');
let update = original.replace('<h1>Preact App</h1>', '<h1>Test App</h1>');
await fs.writeFile(header, update);

await sleep(1500); // wait for a rebuild
const updatedContent = await fs.readFile(
resolve(dir, './build/bundle.js'),
'utf8'
);

expect(updatedContent).not.toEqual(initialContent);
expect(updatedContent).not.toContain('Preact App');
});
});