Skip to content

Commit

Permalink
feat: 🎸 babel-loader cache, clean option for CLI commands
Browse files Browse the repository at this point in the history
  • Loading branch information
jsimck committed Jan 4, 2022
1 parent 84f0070 commit c19147b
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 89 deletions.
6 changes: 6 additions & 0 deletions packages/cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"css-minimizer-webpack-plugin": "^3.3.0",
"esbuild": "^0.14.5",
"fast-glob": "^3.2.7",
"find-cache-dir": "^3.3.2",
"glob-import-loader": "^1.1.4",
"less": "^4.1.2",
"less-loader": "^10.2.0",
Expand All @@ -76,6 +77,7 @@
"devDependencies": {
"@ima/server": "*",
"@types/express": "^4.17.13",
"@types/find-cache-dir": "^3.2.1",
"@types/mini-css-extract-plugin": "^2.4.0",
"@types/node": "^17.0.0",
"@types/nodemon": "^1.19.1",
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/src/lib/SharedArgs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { CommandBuilder } from 'yargs';

const SharedArgs: CommandBuilder = {
clean: {
desc: 'Clean build folder before building the application',
type: 'boolean'
},
verbose: {
desc: 'Use default webpack CLI output instead of custom one',
type: 'boolean',
default: false
type: 'boolean'
},
publicPath: {
desc: 'Webpack public path to specify base for all assets in the app',
Expand Down
169 changes: 115 additions & 54 deletions packages/cli/src/lib/compiler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import { WebpackError, Configuration, MultiCompiler } from 'webpack';
import {
WebpackError,
Configuration,
MultiCompiler,
MultiStats
} from 'webpack';

import { CliArgs } from '../types';
import logger from './logger';
Expand Down Expand Up @@ -36,6 +43,75 @@ async function closeCompiler(compiler: MultiCompiler): Promise<Error | void> {
);
}

/**
* Cleans output (/build) directory if it exits. Defaults to
* true for production environments.
*/
function cleanOutputDir(args: CliArgs): void {
if (!(args.clean ?? (args.isProduction && args.command === 'build'))) {
return;
}

const outputDir = path.join(args.rootDir, 'build');

if (!fs.existsSync(outputDir)) {
return;
}

const elapsedClean = time();

logger.info('Cleaning the build directory...', false);
fs.rmSync(outputDir, { recursive: true });
logger.write(chalk.gray(` [${elapsedClean()}]`));
}

/**
* Watch and run compiler promise handler. Takes care of formatting
* and printing output from webpack stats.
*/
function handleCompiler({
firstRun,
elapsed,
error,
compiler,
args,
imaConfig,
stats,
reject,
resolve
}: {
firstRun: boolean;
elapsed: ReturnType<typeof time>;
error: Error | undefined;
compiler: MultiCompiler;
args: CliArgs;
imaConfig: ImaConfig;
stats: MultiStats | undefined;
reject: (compiler: MultiCompiler) => void;
resolve: (compiler: MultiCompiler) => void;
}): void {
if (firstRun) {
// Print elapsed time for first run
elapsed && logger.write(chalk.gray(` [${elapsed()}]`));
firstRun = false;
}

// Reject with compiler
if (error) {
return reject(compiler);
}

// Format stats after plugin done callback
formatStats(stats, args);

// Call onDone callback
imaConfig?.plugins?.forEach(plugin =>
plugin?.onDone?.({ firstRun, args, imaConfig, compiler })
);

return resolve(compiler);
}

/**
* Runs webpack compiler with given configuration.
*
Expand All @@ -49,38 +125,26 @@ async function runCompiler(
args: CliArgs,
imaConfig: ImaConfig
): Promise<MultiCompiler> {
cleanOutputDir(args);

const elapsed = time();
logger.info('Running webpack compiler...', false);

return new Promise((resolve, reject) => {
compiler.run((error, stats) =>
closeCompiler(compiler).then(() => {
// Print elapsed time
elapsed && logger.write(chalk.gray(` [${elapsed()}]`));

if (error) {
// Call error callback
imaConfig?.plugins?.forEach(plugin =>
plugin?.onError?.({ error, args, imaConfig, compiler })
);

return reject(compiler);
}

// Format stats after plugin done callback
formatStats(stats, args);

// Call onDone callback
imaConfig?.plugins?.forEach(plugin =>
plugin?.onDone?.({
args,
imaConfig,
compiler
})
);

return resolve(compiler);
})
closeCompiler(compiler).then(() =>
handleCompiler({
firstRun: true,
args,
compiler,
elapsed,
error,
imaConfig,
reject,
resolve,
stats
})
)
);
});
}
Expand All @@ -101,9 +165,10 @@ async function watchCompiler(
imaConfig: ImaConfig,
watchOptions: Configuration['watchOptions'] = {}
): Promise<MultiCompiler> {
const elapsed = time();
let firstRun = true;
cleanOutputDir(args);

const elapsed = time();
logger.info(
`Running webpack watch compiler${
args.legacy
Expand All @@ -115,32 +180,28 @@ async function watchCompiler(

return new Promise<MultiCompiler>((resolve, reject) => {
compiler.watch(watchOptions, (error, stats) => {
if (firstRun) {
// Print elapsed time
elapsed && logger.write(chalk.gray(` [${elapsed()}]`));
firstRun = false;
}

if (error) {
// Call error callback
imaConfig?.plugins?.forEach(plugin =>
plugin?.onError?.({ error, args, imaConfig, compiler })
);

return reject(compiler);
}

// Format stats after plugin done callback
formatStats(stats, args);

// Call onDone callback
imaConfig?.plugins?.forEach(plugin =>
plugin?.onDone?.({ firstRun, args, imaConfig, compiler })
);

return resolve(compiler);
handleCompiler({
firstRun,
args,
compiler,
elapsed,
error,
imaConfig,
reject,
resolve,
stats
});

// Update first run flag
firstRun = false;
});
});
}

export { closeCompiler, runCompiler, watchCompiler, handleError };
export {
closeCompiler,
runCompiler,
watchCompiler,
handleError,
cleanOutputDir
};
5 changes: 0 additions & 5 deletions packages/cli/src/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,5 @@ export const describe = 'Build an application for production';
export const handler = handlerFactory(build);
export const builder: CommandBuilder = {
...SharedArgs,
clean: {
desc: 'Clean build folder before building the application',
type: 'boolean',
default: true
},
...resolveCliPluginArgs(CMD)
};
21 changes: 8 additions & 13 deletions packages/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface DevBuildArgs extends BaseArgs {
* Dev (ima dev) script args
*/
export interface DevArgs extends DevBuildArgs {
clean?: boolean;
open?: boolean;
legacy?: boolean;
forceSPA?: boolean;
Expand All @@ -55,9 +56,7 @@ export interface DevArgs extends DevBuildArgs {
/**
* Build (ima build) script args
*/
export interface BuildArgs extends DevBuildArgs {
clean?: boolean;
}
export type BuildArgs = DevBuildArgs;

/**
* Arguments passed across ima cli and into webpack config
Expand Down Expand Up @@ -122,16 +121,6 @@ export interface ImaCliPlugin {
imaConfig: ImaConfig;
compiler: MultiCompiler;
}) => void;

/**
* Optional error callback which is called if webpack run/watch commands fail.
*/
onError?: (params: {
error: Error;
args: CliArgs;
imaConfig: ImaConfig;
compiler: MultiCompiler;
}) => void;
}

/**
Expand Down Expand Up @@ -162,6 +151,12 @@ export type ImaConfig = {
*/
publicPath: string;

/**
* Set to true to generate source maps in production builds
* (dev/watch build always generate source maps to work properly with error overlay).
*/
useSourceMaps?: boolean;

/**
* Array of compression algorithms used for assets in production build. [default=['brotliCompress', 'gzip']]
*/
Expand Down
Loading

0 comments on commit c19147b

Please sign in to comment.