Skip to content

Commit

Permalink
refactor: Separate internal concepts of config and env (#1734)
Browse files Browse the repository at this point in the history
* refactor: Separate internal concepts of config and env

* docs: Adding changeset
  • Loading branch information
rschristian committed Jan 6, 2023
1 parent d486d1d commit d78f1ee
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 102 deletions.
9 changes: 9 additions & 0 deletions .changeset/great-dryers-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'preact-cli': major
---

Reduces the `env` parameter of `preact.config.js` to only contain 3 values: `isProd`, `isWatch`, and `isServer`.

Previously, `env` contained many semi-duplicated values (`production` and `isProd`, etc) as well as values that were unlikely to be of much use to many users (what flags were set, for instance). Because of this, the signal-to-noise ratio was rather low which we didn't like. As such, we reduced `env` down to the most basic environment info: what type of build is `preact-cli` doing and for which environement?

If you customize your Webpack config using a `preact.config.js`, please be aware that you may need to update which values you consume from `env`.
3 changes: 1 addition & 2 deletions packages/cli/src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ exports.build = async function buildCommand(src, argv) {
argv.src = src || argv.src;
// add `default:true`s, `--no-*` disables
argv.prerender = toBool(argv.prerender);
argv.production = toBool(argv.production);

let cwd = resolve(argv.cwd);

Expand All @@ -23,7 +22,7 @@ exports.build = async function buildCommand(src, argv) {
await promisify(rimraf)(dest);
}

let stats = await runWebpack(argv, false);
let stats = await runWebpack(argv, true);

if (argv.json) {
await runWebpack.writeJsonStats(cwd, stats);
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/commands/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ exports.watch = async function watchCommand(src, argv) {
argv.refresh = argv.rhl;
}
argv.src = src || argv.src;
argv.production = false;
if (argv.sw) {
argv.sw = toBool(argv.sw);
}
Expand All @@ -33,7 +32,7 @@ exports.watch = async function watchCommand(src, argv) {
}
}

return runWebpack(argv, true);
return runWebpack(argv, false);
};

const determinePort = (exports.determinePort = async function (port) {
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/src/lib/babel-config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const { tryResolveConfig } = require('../util');

module.exports = function (env) {
const { babelConfig, cwd, isProd, refresh } = env;
/**
* @param {boolean} isProd
*/
module.exports = function (config, isProd) {
const { babelConfig, cwd, refresh } = config;

const resolvedConfig =
babelConfig &&
Expand Down
15 changes: 7 additions & 8 deletions packages/cli/src/lib/webpack/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const stackTrace = require('stack-trace');
const URL = require('url');
const { SourceMapConsumer } = require('source-map');

module.exports = function (env, params) {
module.exports = function (config, params) {
params = params || {};

let entry = resolve(env.dest, './ssr-build/ssr-bundle.js');
let entry = resolve(config.dest, './ssr-build/ssr-bundle.js');
let url = params.url || '/';

global.history = {};
Expand All @@ -25,10 +25,9 @@ module.exports = function (env, params) {
);
return '';
}
const { cwd } = env;
const preact = require(require.resolve('preact', { paths: [cwd] }));
const preact = require(require.resolve('preact', { paths: [config.cwd] }));
const renderToString = require(require.resolve('preact-render-to-string', {
paths: [cwd],
paths: [config.cwd],
}));
return renderToString(preact.h(app, { ...params, url }));
} catch (err) {
Expand All @@ -37,11 +36,11 @@ module.exports = function (env, params) {
throw err;
}

handlePrerenderError(err, env, stack, entry);
handlePrerenderError(err, config, stack, entry);
}
};

async function handlePrerenderError(err, env, stack, entry) {
async function handlePrerenderError(err, config, stack, entry) {
let errorMessage = err.toString();
let isReferenceError = errorMessage.startsWith('ReferenceError');
let methodName = stack.getMethodName();
Expand Down Expand Up @@ -70,7 +69,7 @@ async function handlePrerenderError(err, env, stack, entry) {
.replace(/^(.*?\/node_modules\/(@[^/]+\/)?[^/]+)(\/.*)$/, '$1')
);

sourcePath = resolve(env.src, position.source);
sourcePath = resolve(config.cwd, position.source);
sourceLines;
try {
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
Expand Down
9 changes: 7 additions & 2 deletions packages/cli/src/lib/webpack/render-html-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ function read(path) {
return readFileSync(resolve(__dirname, path), 'utf-8');
}

module.exports = async function renderHTMLPlugin(config) {
/**
* @param {import('../../../types').Env} env
*/
module.exports = async function renderHTMLPlugin(config, env) {
const { cwd, dest, src } = config;

const inProjectTemplatePath = resolve(src, 'template.html');
let template = defaultTemplate;
if (existsSync(inProjectTemplatePath)) {
Expand Down Expand Up @@ -91,9 +95,10 @@ module.exports = async function renderHTMLPlugin(config) {
manifest: config.manifest,
inlineCss: config['inline-css'],
config,
env,
preRenderData: values,
CLI_DATA: { preRenderData: { url, ...routeData } },
ssr: config.prerender ? prerender({ cwd, dest, src }, values) : '',
ssr: config.prerender ? prerender(config, values) : '',
entrypoints,
},
htmlWebpackPlugin: {
Expand Down
71 changes: 41 additions & 30 deletions packages/cli/src/lib/webpack/run-webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@ const serverConfig = require('./webpack-server-config');
const transformConfig = require('./transform-config');
const { error, isDir, warn } = require('../../util');

async function devBuild(env) {
let config = await clientConfig(env);
/**
* @param {import('../../../types').Env} env
*/
async function devBuild(config, env) {
const webpackConfig = await clientConfig(config, env);

await transformConfig(env, config);
await transformConfig(webpackConfig, config, env);

let compiler = webpack(config);
let compiler = webpack(webpackConfig);
return new Promise((res, rej) => {
compiler.hooks.beforeCompile.tap('CliDevPlugin', () => {
if (env['clear']) clear(true);
if (config['clear']) clear(true);
});

compiler.hooks.done.tap('CliDevPlugin', stats => {
let devServer = config.devServer;
let devServer = webpackConfig.devServer;
let protocol = process.env.HTTPS || devServer.https ? 'https' : 'http';
let host = process.env.HOST || devServer.host || 'localhost';
if (host === '0.0.0.0' && process.platform === 'win32') {
host = 'localhost';
}
let serverAddr = `${protocol}://${host}:${bold(env.port)}`;
let localIpAddr = `${protocol}://${ip.address()}:${bold(env.port)}`;
let serverAddr = `${protocol}://${host}:${bold(config.port)}`;
let localIpAddr = `${protocol}://${ip.address()}:${bold(config.port)}`;

if (stats.hasErrors()) {
process.stdout.write(red('Build failed!\n\n'));
Expand All @@ -45,26 +48,28 @@ async function devBuild(env) {

compiler.hooks.failed.tap('CliDevPlugin', rej);

let server = new DevServer(config.devServer, compiler);
let server = new DevServer(webpackConfig.devServer, compiler);
server.start();
res(server);
});
}

async function prodBuild(env) {
env = { ...env, isServer: false, dev: !env.production, ssr: false };
let config = await clientConfig(env);
await transformConfig(env, config);
/**
* @param {import('../../../types').Env} env
*/
async function prodBuild(config, env) {
if (config.prerender) {
const serverEnv = { ...env, isServer: true };

if (env.prerender) {
const serverEnv = Object.assign({}, env, { isServer: true, ssr: true });
let ssrConfig = serverConfig(serverEnv);
await transformConfig(serverEnv, ssrConfig);
let serverCompiler = webpack(ssrConfig);
const serverWebpackConfig = serverConfig(config, serverEnv);
await transformConfig(serverWebpackConfig, config, serverEnv);
const serverCompiler = webpack(serverWebpackConfig);
await runCompiler(serverCompiler);
}

let clientCompiler = webpack(config);
const clientWebpackConfig = await clientConfig(config, env);
await transformConfig(clientWebpackConfig, config, env);
const clientCompiler = webpack(clientWebpackConfig);

try {
let stats = await runCompiler(clientCompiler);
Expand Down Expand Up @@ -220,20 +225,26 @@ function stripLoaderFromModuleNames(m) {
return m;
}

module.exports = function (env, watch = false) {
env.isProd = env.production; // shorthand
env.isWatch = !!watch; // use HMR?
env.cwd = resolve(env.cwd || process.cwd());

// env.src='src' via `build` default
let src = resolve(env.cwd, env.src);
env.src = isDir(src) ? src : env.cwd;
/**
* @param {boolean} isProd
*/
module.exports = function (argv, isProd) {
const env = {
isProd,
isWatch: !isProd,
isServer: false,
};
const config = argv;
config.cwd = resolve(argv.cwd || process.cwd());

// config.src='src' via `build` default
const src = resolve(config.cwd, argv.src);
config.src = isDir(src) ? src : config.cwd;

// attach sourcing helper
env.source = dir => resolve(env.src, dir);
config.source = dir => resolve(config.src, dir);

// determine build-type to run
return (watch ? devBuild : prodBuild)(env);
return (isProd ? prodBuild : devBuild)(config, env);
};

module.exports.writeJsonStats = writeJsonStats;
27 changes: 15 additions & 12 deletions packages/cli/src/lib/webpack/transform-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ const { error, esmImport, tryResolveConfig, warn } = require('../../util');
const FILE = 'preact.config';
const EXTENSIONS = ['js', 'json'];

async function findConfig(env) {
async function findConfig(cwd) {
let idx = 0;
for (idx; idx < EXTENSIONS.length; idx++) {
let config = `${FILE}.${EXTENSIONS[idx]}`;
let path = resolve(env.cwd, config);
let configFile = `${FILE}.${EXTENSIONS[idx]}`;
let path = resolve(cwd, configFile);
try {
await stat(path);
return { configFile: config, isDefault: true };
return { configFile, isDefault: true };
} catch (e) {}
}

Expand Down Expand Up @@ -90,17 +90,20 @@ function parseConfig(config) {
return transformers;
}

module.exports = async function (env, webpackConfig) {
/**
* @param {import('../../../types').Env} env
*/
module.exports = async function (webpackConfig, config, env) {
const { configFile, isDefault } =
env.config !== 'preact.config.js'
? { configFile: env.config, isDefault: false }
: await findConfig(env);
config.config !== 'preact.config.js'
? { configFile: config.config, isDefault: false }
: await findConfig(config.cwd);

const cliConfig = tryResolveConfig(
env.cwd,
config.cwd,
configFile,
isDefault,
env.verbose
config.verbose
);

if (!cliConfig) return;
Expand All @@ -111,15 +114,15 @@ module.exports = async function (env, webpackConfig) {
} catch (error) {
warn(
`Failed to load preact-cli config file, using default!\n${
env.verbose ? error.stack : error.message
config.verbose ? error.stack : error.message
}`
);
return;
}

const transformers = parseConfig((m && m.default) || m);

const helpers = new WebpackConfigHelpers(env.cwd);
const helpers = new WebpackConfigHelpers(config.cwd);
for (let [transformer, options] of transformers) {
try {
await transformer(webpackConfig, env, helpers, options);
Expand Down
19 changes: 11 additions & 8 deletions packages/cli/src/lib/webpack/webpack-base-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,17 @@ function resolveTsconfig(cwd, isProd) {
}

/**
* @param {import('../../../types').Env} env
* @returns {import('webpack').Configuration}
*/
module.exports = function createBaseConfig(env) {
const { cwd, isProd, src, source } = env;
// Apply base-level `env` values
env.dest = resolve(cwd, env.dest || 'build');
env.manifest = readJson(source('manifest.json')) || {};
env.pkg = readJson(resolve(cwd, 'package.json')) || {};
module.exports = function createBaseConfig(config, env) {
const { cwd, src, source } = config;
const { isProd, isServer } = env;

// Apply base-level `config` values
config.dest = resolve(cwd, config.dest || 'build');
config.manifest = readJson(source('manifest.json')) || {};
config.pkg = readJson(resolve(cwd, 'package.json')) || {};

// use browserslist config environment, config default, or default browsers
// default browsers are '> 0.5%, last 2 versions, Firefox ESR, not dead'
Expand Down Expand Up @@ -137,7 +140,7 @@ module.exports = function createBaseConfig(env) {
use: [
{
loader: require.resolve('babel-loader'),
options: createBabelConfig(env),
options: createBabelConfig(config, isProd),
},
require.resolve('source-map-loader'),
],
Expand Down Expand Up @@ -288,7 +291,7 @@ module.exports = function createBaseConfig(env) {
new RemoveEmptyScriptsPlugin(),
new MiniCssExtractPlugin({
filename:
isProd && !env.isServer ? '[name].[contenthash:5].css' : '[name].css',
isProd && !isServer ? '[name].[contenthash:5].css' : '[name].css',
chunkFilename: isProd
? '[name].chunk.[contenthash:5].css'
: '[name].chunk.css',
Expand Down
Loading

0 comments on commit d78f1ee

Please sign in to comment.