feat: external config loading

+refactor many pieces
This commit is contained in:
Igor Randjelovic
2020-11-21 13:34:09 +01:00
committed by Nathan Walker
parent 04d989c2e6
commit 575130c712
15 changed files with 358 additions and 107 deletions

View File

@ -97,9 +97,9 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -107,6 +107,10 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -120,6 +124,16 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -142,22 +156,27 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/android/app/src/main/assets/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: true,
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process',
profile: '() => {}',
__DEV__: 'true',
__TEST__: 'false',
'process.env.NODE_ENV': '\\"development\\"'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin(),
/* config.plugin('ReactRefreshWebpackPlugin') */
@ -273,9 +292,9 @@ exports[`react configuration > android > base config 1`] = `
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -283,6 +302,10 @@ exports[`react configuration > android > base config 1`] = `
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -296,6 +319,16 @@ exports[`react configuration > android > base config 1`] = `
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -318,22 +351,27 @@ exports[`react configuration > android > base config 1`] = `
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/android/app/src/main/assets/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: true,
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process',
profile: '() => {}',
__DEV__: 'true',
__TEST__: 'false',
'process.env.NODE_ENV': '\\"development\\"'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin()
],
@ -442,9 +480,9 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -452,6 +490,10 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -465,6 +507,16 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -487,22 +539,27 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/ios/__jest__/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: false,
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process',
profile: '() => {}',
__DEV__: 'true',
__TEST__: 'false',
'process.env.NODE_ENV': '\\"development\\"'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin(),
/* config.plugin('ReactRefreshWebpackPlugin') */
@ -621,9 +678,9 @@ exports[`react configuration > ios > base config 1`] = `
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -631,6 +688,10 @@ exports[`react configuration > ios > base config 1`] = `
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -644,6 +705,16 @@ exports[`react configuration > ios > base config 1`] = `
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -666,22 +737,27 @@ exports[`react configuration > ios > base config 1`] = `
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/ios/__jest__/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: false,
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process',
profile: '() => {}',
__DEV__: 'true',
__TEST__: 'false',
'process.env.NODE_ENV': '\\"development\\"'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin()
],

View File

@ -88,9 +88,9 @@ exports[`vue configuration for android 1`] = `
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -98,6 +98,10 @@ exports[`vue configuration for android 1`] = `
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -124,6 +128,16 @@ exports[`vue configuration for android 1`] = `
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -148,19 +162,25 @@ exports[`vue configuration for android 1`] = `
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/android/app/src/main/assets/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: true,
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process',
profile: '() => {}'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin()
],
@ -260,9 +280,9 @@ exports[`vue configuration for ios 1`] = `
{
loader: 'apply-css-loader'
},
/* config.module.rule('css').use('css-loader') */
/* config.module.rule('css').use('css2json-loader') */
{
loader: 'css-loader'
loader: 'css2json-loader'
}
]
},
@ -270,6 +290,10 @@ exports[`vue configuration for ios 1`] = `
{
test: /\\\\.scss$/,
use: [
/* config.module.rule('scss').use('apply-css-loader') */
{
loader: 'apply-css-loader'
},
/* config.module.rule('scss').use('css2json-loader') */
{
loader: 'css2json-loader'
@ -296,6 +320,16 @@ exports[`vue configuration for ios 1`] = `
]
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendor: {
test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/,
priority: -10,
name: 'vendor',
chunks: 'all'
}
}
},
minimizer: [
/* config.optimization.minimizer('TerserPlugin') */
new TerserPlugin(
@ -320,19 +354,25 @@ exports[`vue configuration for ios 1`] = `
cleanOnceBeforeBuildPatterns: [
'__jest__/platforms/ios/__jest__/app/**/*'
],
verbose: true
verbose: false
}
),
/* config.plugin('DefinePlugin') */
new DefinePlugin(
{
'global.NS_WEBPACK': true,
__DEV__: true,
__NS_WEBPACK__: true,
__CSS_PARSER__: '\\"css-tree\\"',
__ANDROID__: false,
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process',
profile: '() => {}'
}
),
/* config.plugin('BundleAnalyzerPlugin') */
new BundleAnalyzerPlugin(),
/* config.plugin('WatchStateLoggerPlugin') */
new WatchStateLoggerPlugin()
],

View File

@ -1,10 +1,8 @@
import Config from 'webpack-chain';
import { IWebpackEnv, Platform } from '../index';
import { IWebpackEnv } from '../index';
import {
getAbsoluteDistPath,
getDistPath,
getEntryPath,
getPackageJson,
getPlatform,
} from '../helpers/project';
@ -17,19 +15,21 @@ import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
export default function (config: Config, env: IWebpackEnv): Config {
const entryPath = getEntryPath();
const platform = getPlatform();
const packageJson = getPackageJson();
const mode = env.production ? 'production' : 'development';
// set mode
config.mode(mode);
// package.json is generated by the CLI with runtime options
// this ensures it's not included in the bundle
config.externals(['package.json']);
// todo: devtool
config.devtool('inline-source-map');
// todo: figure out easiest way to make "node" target work in ns,
// todo: figure out easiest way to make "node" target work in ns
// rather than the custom ns target implementation that's hard to maintain
// appears to be working - but we still have to deal with HMR
config.target('node');
config.entry('bundle').add(entryPath);
@ -62,11 +62,6 @@ export default function (config: Config, env: IWebpackEnv): Config {
priority: -10,
name: 'vendor',
chunks: 'all',
// test: (module) => {
// const moduleName = module.nameForCondition ? module.nameForCondition() : '';
// return /[\\/]node_modules[\\/]/.test(moduleName);
// },
// enforce: true
},
},
});
@ -74,6 +69,7 @@ export default function (config: Config, env: IWebpackEnv): Config {
// look for loaders in
// - node_modules/@nativescript/webpack/dist/loaders
// - node_modules
// allows for cleaner rules, without having to specify full paths to loaders
config.resolveLoader.modules
.add('node_modules/@nativescript/webpack/dist/loaders')
.add('node_modules');
@ -149,6 +145,9 @@ export default function (config: Config, env: IWebpackEnv): Config {
config.module
.rule('scss')
.test(/\.scss$/)
.use('apply-css-loader')
.loader('apply-css-loader')
.end()
.use('css2json-loader')
.loader('css2json-loader')
.end()
@ -159,18 +158,22 @@ export default function (config: Config, env: IWebpackEnv): Config {
config.plugin('CleanWebpackPlugin').use(CleanWebpackPlugin, [
{
cleanOnceBeforeBuildPatterns: [`${getAbsoluteDistPath()}/**/*`],
verbose: true,
verbose: !!env.verbose,
},
]);
// todo: refine defaults
config.plugin('DefinePlugin').use(DefinePlugin, [
{
'global.NS_WEBPACK': true,
'global.isAndroid': platform === 'android',
'global.isIOS': platform === 'ios',
__DEV__: mode === 'development',
__NS_WEBPACK__: true,
__CSS_PARSER__: JSON.stringify('css-tree'), // todo: replace from config value
__ANDROID__: platform === 'android',
__IOS__: platform === 'ios',
/* for compat only */ 'global.isAndroid': platform === 'android',
/* for compat only */ 'global.isIOS': platform === 'ios',
process: 'global.process',
profile: '() => {}',
/* todo: remove if fixed in core? */ profile: '() => {}',
},
]);
@ -183,6 +186,7 @@ export default function (config: Config, env: IWebpackEnv): Config {
// },
// ]);
// todo: make opt-in with a flag
config.plugin('BundleAnalyzerPlugin').use(BundleAnalyzerPlugin);
// add the WatchStateLogger plugin used to notify the CLI of build state

View File

@ -8,9 +8,11 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
base(config, env);
const platform = getPlatform();
const mode = env.production ? 'production' : 'development';
const production = mode === 'production';
// todo: use env
let isAnySourceMapEnabled = true;
let production = false;
config.resolve.extensions.prepend('.tsx').prepend(`.${platform}.tsx`);
config.resolve.alias.set('react-dom', 'react-nativescript');
@ -30,15 +32,12 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
config.plugin('DefinePlugin').tap((args) => {
args[0] = merge(args[0], {
/** For various libraries in the React ecosystem. */
__DEV__: production ? 'false' : 'true',
__TEST__: 'false',
/**
* Primarily for React Fast Refresh plugin, but technically the allowHmrInProduction option could be used instead.
* Worth including anyway, as there are plenty of Node libraries that use this flag.
*/
'process.env.NODE_ENV': JSON.stringify(
production ? 'production' : 'development'
),
'process.env.NODE_ENV': JSON.stringify(mode),
});
return args;

View File

@ -23,6 +23,8 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
.tap((options) => {
return {
...options,
// todo: should be a compiler object
// but we want it as an external dependency
compiler: 'nativescript-vue-template-compiler',
};
})

View File

@ -0,0 +1,24 @@
import { getPackageJson, getProjectRootPath } from './project';
import path from 'path';
export function getAllDependencies(): string[] {
const packageJSON = getPackageJson();
console.log(packageJSON);
return [
...Object.keys(packageJSON.dependencies ?? {}),
...Object.keys(packageJSON.devDependencies ?? {}),
];
}
export function getDependencyPath(dependencyName: string): string | null {
try {
const resolvedPath = require.resolve(`${dependencyName}/package.json`, {
paths: [getProjectRootPath()],
});
return path.dirname(resolvedPath);
} catch (err) {
return null;
}
}

View File

@ -1,11 +0,0 @@
// todo: refine
export function error(message: string, info?: { possibleCauses?: string[] }) {
console.error(`
NativeScript Webpack encountered an error and cannot proceed with the build:
${message}
Possible causes:
${info?.possibleCauses?.map((cause) => `- ${cause}`).join('\n')}
`);
}

View File

@ -0,0 +1,36 @@
import path from 'path';
import fs from 'fs';
import dedent from 'ts-dedent';
import * as lib from '../index';
import { error, info } from './log';
import { getAllDependencies, getDependencyPath } from './dependencies';
export function applyExternalConfigs() {
getAllDependencies().forEach((dependency) => {
const packagePath = getDependencyPath(dependency);
const configPath = path.join(packagePath, 'nativescript.webpack.js');
if (fs.existsSync(configPath)) {
info(`Discovered config: ${configPath}`);
try {
const externalConfig = require(configPath);
if (typeof externalConfig === 'function') {
externalConfig(lib);
} else {
// todo: warn user
// todo: perhaps support exported objects to merge into config?
}
} catch (err) {
error(
dedent`
Unable to apply config: ${configPath}.
Error is:
`,
err
);
}
}
});
}

View File

@ -1,18 +1,44 @@
import { defaultConfigs } from '@nativescript/webpack';
import { getAllDependencies } from './dependencies';
import { error } from './log';
import dedent from 'ts-dedent';
export function determineProjectFlavor(): keyof typeof defaultConfigs {
// todo;
// error(`
// Could not determine project flavor.
//
// Please use webpack.useConfig('<flavor>') to explicitly set the base config.
// `, {
// possibleCauses: [
// 'Not in a NativeScript project',
// 'The project is not at the current working directory'
// ]
// })
export function determineProjectFlavor(): keyof typeof defaultConfigs | false {
const dependencies = getAllDependencies();
if (dependencies.includes('nativescript-vue')) {
return 'vue';
}
if (dependencies.includes('@nativescript/angular')) {
return 'angular';
}
if (dependencies.includes('react-nativescript')) {
return 'react';
}
if (dependencies.includes('svelte-native')) {
return 'svelte';
}
// the order is important - angular, react, and svelte also include these deps
// but should return prior to this condition!
if (
dependencies.includes('@nativescript/core') &&
dependencies.includes('typescript')
) {
return 'typescript';
}
if (dependencies.includes('@nativescript/core')) {
return 'javascript';
}
error(dedent`
Could not determine project flavor.
Please use webpack.useConfig('<flavor>') to explicitly set the base config.
`);
return false;
}

View File

@ -0,0 +1,23 @@
// todo: refine
// export function error(message: string, info?: { possibleCauses?: string[] }) {
// console.error(`
// NativeScript Webpack encountered an error and cannot proceed with the build:
//
// ${message}
//
// Possible causes:
// ${info?.possibleCauses?.map((cause) => `- ${cause}`).join('\n')}
// `);
// }
export function error(...data: any) {
console.error(`[@nativescript/webpack]`, ...data);
}
export function warn(...data: any) {
console.warn(`[@nativescript/webpack]`, ...data);
}
export function info(...data: any) {
console.info(`[@nativescript/webpack]`, ...data);
}

View File

@ -1,5 +1,6 @@
import { env, Platform } from '../index';
import { resolve, basename } from 'path';
import { error } from './log';
export function getProjectRootPath(): string {
// todo: find actual path?
@ -42,8 +43,7 @@ export function getPlatform(): Platform {
return 'ios';
}
// todo: maybe no throw?
throw new Error('You need to provide a target platform!');
error('You need to provide a target platform!');
}
interface IPackageJson {

View File

@ -1,8 +1,9 @@
import Config from 'webpack-chain';
import webpack from 'webpack';
import { highlight } from 'cli-highlight';
import { configs } from './configuration';
import { determineProjectFlavor } from './helpers/flavor';
import { highlight } from 'cli-highlight';
import { applyExternalConfigs } from './helpers/externalConfigs';
export type Platform = 'android' | 'ios' | string;
@ -49,11 +50,18 @@ export function useConfig(config: keyof typeof defaultConfigs | false) {
}
}
export function chainWebpack(chainFn: (config: Config, env: IWebpackEnv) => any) {
export function chainWebpack(
chainFn: (config: Config, env: IWebpackEnv) => any
) {
webpackChains.push(chainFn);
}
export function mergeWebpack(mergeFn: (config: Partial<webpack.Configuration>, env: IWebpackEnv) => any | Partial<webpack.Configuration>) {
export function mergeWebpack(
mergeFn: (
config: Partial<webpack.Configuration>,
env: IWebpackEnv
) => any | Partial<webpack.Configuration>
) {
webpackMerges.push(mergeFn);
}
@ -64,6 +72,10 @@ export function resolveChainableConfig() {
useConfig(determineProjectFlavor());
}
// apply configs from dependencies
// todo: allow opt-out
applyExternalConfigs();
// this applies all chain configs
webpackChains.forEach((chainFn) => {
return chainFn(config, env);

View File

@ -11,13 +11,11 @@ export default function loader(content, map) {
?.slice(this.loaderIndex)
.some(({ path }) => path.includes(loader));
};
if (hasLoader('apply-css-loader')) {
// add a tag to the applied css
const tag =
this.mode === 'development'
? `, ${JSON.stringify(this.resourcePath)}`
: '';
this.mode === 'development' ? `, ${JSON.stringify(this.resourcePath)}` : '';
if (hasLoader('apply-css-loader')) {
content = dedent`
${content}
const { addTaggedAdditionalCSS } = require("@nativescript/core/ui/styling/style-scope");
@ -26,14 +24,12 @@ export default function loader(content, map) {
} else if (hasLoader('css-loader')) {
content = dedent`
${content}
// apply css
const { Application } = require("@nativescript/core");
require("@nativescript/core/ui/styling/style-scope");
const { addTaggedAdditionalCSS } = require("@nativescript/core/ui/styling/style-scope");
if (___CSS_LOADER_EXPORT___ && typeof ___CSS_LOADER_EXPORT___.forEach === "function") {
___CSS_LOADER_EXPORT___.forEach(cssExport => {
if (cssExport.length > 1 && cssExport[1]) {
// applying the second item of the export as it contains the css contents
Application.addCss(cssExport[1]);
addTaggedAdditionalCSS(cssExport[1]${tag});
}
});
}

View File

@ -13,6 +13,8 @@ export default function loader(content: string, map: any) {
const ast = parse(content);
// todo: revise if this is necessary
// todo: perhaps use postCSS and just build imports into a single file?
let dependencies = [];
getImportRules(ast)
.map(extractUrlFromRule)
@ -37,9 +39,7 @@ export default function loader(content: string, map: any) {
const code = dedent`
/* CSS2JSON */
${dependencies.join('\n')}
const ___CSS2JSON_LOADER_EXPORT___ = ${str}
export default ___CSS2JSON_LOADER_EXPORT___
`;
this.callback(

View File

@ -1,3 +1,6 @@
import webpack from 'webpack';
const id = 'WatchStateLoggerPlugin';
export enum messages {
compilationComplete = 'Webpack compilation complete.',
startWatching = 'Webpack compilation complete. Watching for file changes.',
@ -13,15 +16,20 @@ export class WatchStateLoggerPlugin {
apply(compiler) {
const plugin = this;
compiler.hooks.watchRun.tapAsync('WatchStateLoggerPlugin', function (compiler, callback) {
compiler.hooks.watchRun.tapAsync(id, function (compiler, callback) {
plugin.isRunningWatching = true;
if (plugin.isRunningWatching) {
console.log(messages.changeDetected);
}
process.send && process.send(messages.changeDetected, (error) => null);
notify(messages.changeDetected);
callback();
});
compiler.hooks.afterEmit.tapAsync('WatchStateLoggerPlugin', function (compilation, callback) {
compiler.hooks.afterEmit.tapAsync(id, function (compilation, callback) {
callback();
if (plugin.isRunningWatching) {
@ -30,18 +38,20 @@ export class WatchStateLoggerPlugin {
console.log(messages.compilationComplete);
}
const emittedFiles = Object.keys(compilation.assets).filter((assetKey) => compilation.assets[assetKey].emitted);
const emittedFiles = Object.keys(compilation.assets).filter(
(assetKey) => compilation.assets[assetKey].emitted
);
const chunkFiles = getChunkFiles(compilation);
process.send && process.send(messages.compilationComplete, (error) => null);
notify(messages.compilationComplete);
// Send emitted files so they can be LiveSynced if need be
process.send && process.send({ emittedFiles, chunkFiles, hash: compilation.hash }, (error) => null);
notify({ emittedFiles, chunkFiles, hash: compilation.hash });
});
}
}
function getChunkFiles(compilation) {
function getChunkFiles(compilation: webpack.Compilation) {
const chunkFiles = [];
try {
compilation.chunks.forEach((chunk) => {
@ -57,3 +67,17 @@ function getChunkFiles(compilation) {
return chunkFiles;
}
function notify(message: any) {
if (!process.send) {
return;
}
process.send(message, (error) => {
if (error) {
console.error(`[${id}] Process Send Error: `, error);
}
return null;
});
}