├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .vscode ├── last.sql └── temp.sql ├── CHANGELOG.md ├── LICENSE ├── README.md ├── jest.config.ts ├── lib ├── index.d.ts ├── index.js ├── types.d.ts └── types.js ├── package-lock.json ├── package.json ├── src ├── index.ts └── types.ts ├── tests ├── generate.test.ts ├── scripts │ ├── big_data.js │ ├── content.js │ ├── entry.js │ ├── exit-code-1.js │ ├── file.js │ └── style.css └── webpack │ ├── content.js │ ├── index.html │ ├── index.js │ └── style.css ├── tsconfig.json ├── tslint.json ├── webpack.config.js └── webpack.config.ts /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: [14.x, 16.x, 18.x, 20.x, 22.x] 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Use Node.js ${{ matrix.node-version }} 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version: ${{ matrix.node-version }} 17 | cache: 'npm' 18 | - run: npm install 19 | - run: npm test 20 | env: 21 | CI: true 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.swf 4 | .idea/ 5 | dist/ 6 | -------------------------------------------------------------------------------- /.vscode/last.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s00d/webpack-shell-plugin-next/433c77921a54c2dffe60197995bfcf8f0a811b14/.vscode/last.sql -------------------------------------------------------------------------------- /.vscode/temp.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s00d/webpack-shell-plugin-next/433c77921a54c2dffe60197995bfcf8f0a811b14/.vscode/temp.sql -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [2.3.2] - 2024-08-07 8 | ### Changed 9 | - add onBeforeCompile 10 | - update deps 11 | 12 | ## [2.2.2] - 2021-03-15 13 | ### Changed 14 | - add shell control 15 | 16 | ## [2.2.1] - 2021-03-15 17 | ### Changed 18 | - fix windows build 19 | - add log error 20 | - fex test run 21 | 22 | ## [2.1.2] - 2021-03-04 23 | 24 | ### Changed 25 | - fix env and parallel 26 | 27 | ## [2.1.1] - 2020-12-25 28 | 29 | ### Removed 30 | - package-lock.json 31 | 32 | ## [2.1.0] - 2020-12-25 33 | 34 | ### Changed 35 | - use spawn by default 36 | 37 | ### Removed 38 | - remove os check 39 | 40 | ## [2.0.5] - 2020-11-10 41 | 42 | ### Added 43 | - tests 44 | ### Changed 45 | - fix types 46 | 47 | ## [2.0.4] - 2020-10-30 48 | 49 | ### Changed 50 | - fix onAfterDone 51 | 52 | ## [2.0.3] - 2020-10-30 53 | 54 | ### Changed 55 | - add onAfterDone 56 | 57 | ## [2.0.2] - 2020-10-20 58 | 59 | ### Changed 60 | - add webpack to peerDependencies 61 | 62 | ## [2.0.1] - 2020-10-20 63 | 64 | ### Changed 65 | - update readme 66 | 67 | ## [2.0.0] - 2020-10-20 68 | 69 | ### ⚠ BREAKING CHANGES 70 | 71 | * webpack@4 no longer supported 72 | 73 | ### Bug Fixes 74 | 75 | * reduce deps 76 | 77 | ## [1.1.8] - 2020-08-14 78 | 79 | ### Changed 80 | - update deps 81 | 82 | ## [1.1.8] - 2019-12-09 83 | 84 | ### Changed 85 | - added onBeforeNormalRun 86 | 87 | ## [1.1.8] - 2019-12-09 88 | 89 | ### Changed 90 | - added onDoneWatch 91 | - Dependencies update 92 | 93 | ## [1.1.5] - 2019-12-09 94 | 95 | ### Changed 96 | - refactoring 97 | - bugfix 98 | - Dependencies update 99 | 100 | ### Removed 101 | - Section about "changelog" vs "CHANGELOG". 102 | 103 | ## [1.1.0] - 2019-09-18 104 | ### Added 105 | - function to scripts 106 | ### Changed 107 | - refactoring 108 | 109 | ## [1.0.0] - 2019-09-16 110 | ### Added 111 | - ts 112 | - onBeforeBuild and onBuildExit 113 | - logging, swallowError, dev 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/webpack-shell-plugin-next.svg)](https://badge.fury.io/js/webpack-shell-plugin-next) 2 | [![npm downloads](https://img.shields.io/npm/dw/webpack-shell-plugin-next)](https://badge.fury.io/js/webpack-shell-plugin-next) 3 | [![NPM license](https://img.shields.io/npm/l/webpack-shell-plugin-next)](https://github.com/s00d/webpack-shell-plugin-next/blob/master/LICENSE) 4 | [![npm type definitions](https://img.shields.io/npm/types/webpack-shell-plugin-next)](https://github.com/s00d/webpack-shell-plugin-next) 5 | ![Build Status](https://github.com/s00d/webpack-shell-plugin-next/workflows/Node.js%20CI/badge.svg?branch=master) 6 | [![donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.me/s00d) 7 | [![GitHub Repo stars](https://img.shields.io/github/stars/s00d/webpack-shell-plugin-next?style=social)](https://github.com/s00d/webpack-shell-plugin-next) 8 | # Webpack Shell Plugin Next 9 | 10 | fix webpack deprecated method. add typescript and other 11 | 12 | This plugin allows you to run any shell commands before or after webpack 5 builds. This will work for both webpack 5. 13 | 14 | Goes great with running cron jobs, reporting tools, or tests such as selenium, protractor, phantom, ect. 15 | 16 | ## Webpack compatibility 17 | 18 | | Webpack | webpack-shell-plugin-next | 19 | |:--------|:--------------------------| 20 | | *-4.x | 1.* | 21 | | 5.x | 2.* | 22 | 23 | ## WARNING 24 | 25 | This plugin is meant for running simple command line executions. It is not meant to be a task management tool. 26 | 27 | ## Installation 28 | 29 | `npm install --save-dev webpack-shell-plugin-next` 30 | 31 | ## Setup 32 | In `webpack.config.js`: 33 | 34 | ```js 35 | const WebpackShellPluginNext = require('webpack-shell-plugin-next'); 36 | ... 37 | module.exports = { 38 | //... 39 | plugins: [ 40 | new WebpackShellPluginNext({ 41 | onBuildStart:{ 42 | scripts: ['echo "Webpack Start"'], 43 | blocking: true, 44 | parallel: false 45 | }, 46 | onBuildEnd:{ 47 | scripts: ['echo "Webpack End"'], 48 | blocking: false, 49 | parallel: true 50 | } 51 | }) 52 | ] 53 | //... 54 | } 55 | ``` 56 | **More example in webpack.config.ts** 57 | 58 | ### API 59 | * `onBeforeBuild`: array of scripts to execute before every build. 60 | * `onBuildError`: array of scripts to execute when there is an error during compilation. 61 | * `onBuildStart`: configuration object for scripts that execute before a compilation. 62 | * `onBuildEnd`: configuration object for scripts that execute after files are emitted at the end of the compilation. 63 | * `onBuildExit`: configuration object for scripts that execute after webpack's process is complete. *Note: this event also fires in `webpack --watch` when webpack has finished updating the bundle.* 64 | * `onWatchRun`: configuration object for scripts that execute when webpack's run watch 65 | * `onDoneWatch`: configuration object for scripts that execute after files are emitted at the end of the compilation with watch. 66 | * `onBeforeNormalRun`: configuration object for scripts that execute on normal run without --watch option 67 | * `onAfterDone`: configuration object for scripts that execute after done. 68 | * `onFailedBuild`: configuration object for scripts that execute after error. 69 | * `onBeforeCompile`: configuration object for scripts that execute before complite. 70 | 71 | ***Default for all: ```{scripts: [],blocking: false,parallel: false}```*** 72 | 73 | * `blocking (onBeforeBuild, onBuildStart, onBuildEnd, onBuildExit, onBuildExit, onWatchRun)`: block webpack until scripts finish execution. 74 | * `parallel (onBeforeBuild, onBuildStart, onBuildEnd, onBuildExit, onBuildExit, onWatchRun)`: execute scripts in parallel, otherwise execute scripts in the order in which they are specified in the scripts array. 75 | 76 | **Note:** below combination is not supported. 77 | ```js 78 | { 79 | blocking: true 80 | parallel: true 81 | } 82 | ``` 83 | 84 | ***Other global params*** 85 | * `env`: Object with environment variables that will be applied to the executables **Default: { }** 86 | * `logging`: show output for internal messages. **Default: true** 87 | * `swallowError`: ignore script errors (useful in watch mode) **Default: false** 88 | * `dev`: switch for development environments. This causes scripts to execute once. Useful for running HMR on webpack-dev-server or webpack watch mode. **Default: true** 89 | * `safe`: switches script execution process from spawn to exec. If running into problems with spawn, turn this setting on. **Default: false** 90 | 91 | 92 | ```js 93 | new WebpackShellPlugin({ 94 | onBeforeNormalRun: { 95 | // ... 96 | }, 97 | dev: false, 98 | safe: false, 99 | logging: true 100 | }) 101 | ] 102 | } 103 | ``` 104 | 105 | 106 | ### TypeScript 107 | 108 | This project is written in TypeScript, and type declarations are included. You can take advantage of this if your project's webpack configuration is also using TypeScript (e.g. webpack.config.ts and webpack.config.js). 109 | 110 | ### Function in scripts 111 | 112 | how to use functions in the queue? 113 | 114 | #### Example: 115 | ```js 116 | { 117 | { 118 | scripts: [ 119 | // sync 120 | () => { 121 | console.log('run tTimeout 1'); 122 | setTimeout(() => console.log('end Timeout 1'), 1000); 123 | }, 124 | // async 125 | () => new Promise((resolve, reject) => { 126 | console.log('run async tTimeout'); 127 | setTimeout(() => { 128 | console.log('end async tTimeout'); 129 | resolve('ok'); 130 | }, 1000); 131 | }), 132 | ], 133 | blocking: true 134 | } 135 | } 136 | ``` 137 | 138 | ```js 139 | // use exec 140 | import * as os from 'os' 141 | // .. 142 | { 143 | safe: os.platform() === 'win32', // by default spawn is used everywhere. If you have problems try using safe: true 144 | scripts: [ 145 | //... 146 | ] 147 | // ... 148 | } 149 | ``` 150 | 151 | ### Developing 152 | 153 | If opening a pull request, create an issue describing a fix or feature. Have your pull request point to the issue by writing your commits with the issue number in the message. 154 | 155 | Make sure you lint your code by running `npm run lint` and you can build the library by running `npm run build`. 156 | 157 | I appreciate any feed back as well, Thanks for helping! 158 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import type { Config } from '@jest/types' 3 | import { defaults } from 'jest-config' 4 | 5 | const config: Config.InitialOptions = { 6 | verbose: true, 7 | rootDir: path.resolve(__dirname, './'), 8 | moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx', 'js'], 9 | preset: '/node_modules/ts-jest' 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @class WebpackShellPluginNext 3 | * @extends Object 4 | * Run shell commands before and after webpack builds 5 | */ 6 | import { Options } from './types'; 7 | import * as webpack from 'webpack'; 8 | export default class WebpackShellPlugin { 9 | private onBeforeNormalRun; 10 | private onBeforeBuild; 11 | private onBuildStart; 12 | private onBuildEnd; 13 | private onBuildExit; 14 | private onBuildError; 15 | private onWatchRun; 16 | private onDoneWatch; 17 | private onAfterDone; 18 | private onFailedBuild; 19 | private onBeforeCompile; 20 | private env; 21 | private dev; 22 | private shell; 23 | private safe; 24 | private logging; 25 | private swallowError; 26 | private validateEvent; 27 | constructor(options: Options); 28 | private putsAsync; 29 | private puts; 30 | private spreadStdoutAndStdErr; 31 | private serializeScript; 32 | private handleScript; 33 | private handleScriptAsync; 34 | private executeScripts; 35 | apply(compiler: webpack.Compiler): void; 36 | private readonly onBeforeRun; 37 | private readonly afterDone; 38 | private readonly afterCompile; 39 | private readonly onFailed; 40 | private readonly onBefore; 41 | private readonly onCompilation; 42 | private readonly onBeforeCompileRun; 43 | private readonly onAfterEmit; 44 | private readonly onDone; 45 | private readonly watchRun; 46 | private log; 47 | private warn; 48 | private error; 49 | } 50 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * @class WebpackShellPluginNext 4 | * @extends Object 5 | * Run shell commands before and after webpack builds 6 | */ 7 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 8 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 9 | return new (P || (P = Promise))(function (resolve, reject) { 10 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 11 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 12 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 13 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 14 | }); 15 | }; 16 | var __generator = (this && this.__generator) || function (thisArg, body) { 17 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 18 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 19 | function verb(n) { return function (v) { return step([n, v]); }; } 20 | function step(op) { 21 | if (f) throw new TypeError("Generator is already executing."); 22 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 23 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 24 | if (y = 0, t) op = [op[0] & 2, t.value]; 25 | switch (op[0]) { 26 | case 0: case 1: t = op; break; 27 | case 4: _.label++; return { value: op[1], done: false }; 28 | case 5: _.label++; y = op[1]; op = [0]; continue; 29 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 30 | default: 31 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 32 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 33 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 34 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 35 | if (t[2]) _.ops.pop(); 36 | _.trys.pop(); continue; 37 | } 38 | op = body.call(thisArg, _); 39 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 40 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 41 | } 42 | }; 43 | Object.defineProperty(exports, "__esModule", { value: true }); 44 | var child_process_1 = require("child_process"); 45 | var defaultTask = { 46 | scripts: [], 47 | blocking: false, 48 | parallel: false 49 | }; 50 | var WebpackShellPlugin = /** @class */ (function () { 51 | function WebpackShellPlugin(options) { 52 | var _this = this; 53 | this.env = {}; 54 | this.dev = true; 55 | this.shell = true; 56 | this.safe = false; 57 | this.logging = true; 58 | this.swallowError = false; 59 | this.onBeforeRun = function (_compiler, callback) { return __awaiter(_this, void 0, void 0, function () { 60 | var onBeforeNormalRun; 61 | return __generator(this, function (_a) { 62 | switch (_a.label) { 63 | case 0: 64 | onBeforeNormalRun = this.onBeforeNormalRun; 65 | if (!(onBeforeNormalRun.scripts && onBeforeNormalRun.scripts.length > 0)) return [3 /*break*/, 2]; 66 | this.log('Executing pre-run scripts'); 67 | return [4 /*yield*/, this.executeScripts(onBeforeNormalRun.scripts, onBeforeNormalRun.parallel, onBeforeNormalRun.blocking)]; 68 | case 1: 69 | _a.sent(); 70 | if (this.dev) { 71 | this.onDoneWatch = JSON.parse(JSON.stringify(defaultTask)); 72 | } 73 | _a.label = 2; 74 | case 2: 75 | if (callback) { 76 | callback(); 77 | } 78 | return [2 /*return*/]; 79 | } 80 | }); 81 | }); }; 82 | this.afterDone = function () { return __awaiter(_this, void 0, void 0, function () { 83 | var onAfterDone; 84 | return __generator(this, function (_a) { 85 | switch (_a.label) { 86 | case 0: 87 | onAfterDone = this.onAfterDone; 88 | if (!(onAfterDone.scripts && onAfterDone.scripts.length > 0)) return [3 /*break*/, 2]; 89 | this.log('Executing additional scripts before exit'); 90 | return [4 /*yield*/, this.executeScripts(onAfterDone.scripts, onAfterDone.parallel, onAfterDone.blocking)]; 91 | case 1: 92 | _a.sent(); 93 | if (this.dev) { 94 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)); 95 | } 96 | _a.label = 2; 97 | case 2: return [2 /*return*/]; 98 | } 99 | }); 100 | }); }; 101 | this.afterCompile = function (_compilation, callback) { return __awaiter(_this, void 0, void 0, function () { 102 | var onDoneWatch; 103 | return __generator(this, function (_a) { 104 | switch (_a.label) { 105 | case 0: 106 | onDoneWatch = this.onDoneWatch; 107 | if (!(onDoneWatch.scripts && onDoneWatch.scripts.length > 0)) return [3 /*break*/, 2]; 108 | this.log('Executing additional scripts before exit'); 109 | return [4 /*yield*/, this.executeScripts(onDoneWatch.scripts, onDoneWatch.parallel, onDoneWatch.blocking)]; 110 | case 1: 111 | _a.sent(); 112 | if (this.dev) { 113 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)); 114 | } 115 | _a.label = 2; 116 | case 2: 117 | if (callback) { 118 | callback(); 119 | } 120 | return [2 /*return*/]; 121 | } 122 | }); 123 | }); }; 124 | this.onFailed = function () { return __awaiter(_this, void 0, void 0, function () { 125 | var onFailedBuild; 126 | return __generator(this, function (_a) { 127 | switch (_a.label) { 128 | case 0: 129 | onFailedBuild = this.onFailedBuild; 130 | if (!(onFailedBuild.scripts && onFailedBuild.scripts.length)) return [3 /*break*/, 2]; 131 | this.log('Executing before build scripts'); 132 | return [4 /*yield*/, this.executeScripts(onFailedBuild.scripts, onFailedBuild.parallel, onFailedBuild.blocking)]; 133 | case 1: 134 | _a.sent(); 135 | if (this.dev) { 136 | this.onBeforeBuild = JSON.parse(JSON.stringify(defaultTask)); 137 | } 138 | _a.label = 2; 139 | case 2: return [2 /*return*/]; 140 | } 141 | }); 142 | }); }; 143 | this.onBefore = function (_compilation) { return __awaiter(_this, void 0, void 0, function () { 144 | var onBeforeBuild; 145 | return __generator(this, function (_a) { 146 | switch (_a.label) { 147 | case 0: 148 | onBeforeBuild = this.onBeforeBuild; 149 | if (!(onBeforeBuild.scripts && onBeforeBuild.scripts.length)) return [3 /*break*/, 2]; 150 | this.log('Executing before build scripts'); 151 | return [4 /*yield*/, this.executeScripts(onBeforeBuild.scripts, onBeforeBuild.parallel, onBeforeBuild.blocking)]; 152 | case 1: 153 | _a.sent(); 154 | if (this.dev) { 155 | this.onBeforeBuild = JSON.parse(JSON.stringify(defaultTask)); 156 | } 157 | _a.label = 2; 158 | case 2: return [2 /*return*/]; 159 | } 160 | }); 161 | }); }; 162 | this.onCompilation = function (compilation) { return __awaiter(_this, void 0, void 0, function () { 163 | var onBuildStartOption; 164 | return __generator(this, function (_a) { 165 | switch (_a.label) { 166 | case 0: 167 | onBuildStartOption = this.onBuildStart; 168 | if (!(onBuildStartOption.scripts && onBuildStartOption.scripts.length > 0)) return [3 /*break*/, 2]; 169 | this.log('Executing pre-build scripts'); 170 | return [4 /*yield*/, this.executeScripts(onBuildStartOption.scripts, onBuildStartOption.parallel, onBuildStartOption.blocking)]; 171 | case 1: 172 | _a.sent(); 173 | if (this.dev) { 174 | this.onBuildStart = JSON.parse(JSON.stringify(defaultTask)); 175 | } 176 | _a.label = 2; 177 | case 2: return [2 /*return*/]; 178 | } 179 | }); 180 | }); }; 181 | this.onBeforeCompileRun = function (_params, callback) { return __awaiter(_this, void 0, void 0, function () { 182 | var onBeforeCompile; 183 | return __generator(this, function (_a) { 184 | switch (_a.label) { 185 | case 0: 186 | onBeforeCompile = this.onBeforeCompile; 187 | if (!(onBeforeCompile.scripts && onBeforeCompile.scripts.length > 0)) return [3 /*break*/, 2]; 188 | this.log('Executing pre-build scripts'); 189 | return [4 /*yield*/, this.executeScripts(onBeforeCompile.scripts, onBeforeCompile.parallel, onBeforeCompile.blocking)]; 190 | case 1: 191 | _a.sent(); 192 | if (this.dev) { 193 | this.onBuildStart = JSON.parse(JSON.stringify(defaultTask)); 194 | } 195 | _a.label = 2; 196 | case 2: 197 | if (callback) { 198 | callback(); 199 | } 200 | return [2 /*return*/]; 201 | } 202 | }); 203 | }); }; 204 | this.onAfterEmit = function (_compilation, callback) { return __awaiter(_this, void 0, void 0, function () { 205 | var onBuildEndOption; 206 | return __generator(this, function (_a) { 207 | switch (_a.label) { 208 | case 0: 209 | onBuildEndOption = this.onBuildEnd; 210 | if (!(onBuildEndOption.scripts && onBuildEndOption.scripts.length > 0)) return [3 /*break*/, 2]; 211 | this.log('Executing post-build scripts'); 212 | return [4 /*yield*/, this.executeScripts(onBuildEndOption.scripts, onBuildEndOption.parallel, onBuildEndOption.blocking)]; 213 | case 1: 214 | _a.sent(); 215 | if (this.dev) { 216 | this.onBuildEnd = JSON.parse(JSON.stringify(defaultTask)); 217 | } 218 | _a.label = 2; 219 | case 2: 220 | if (callback) { 221 | callback(); 222 | } 223 | return [2 /*return*/]; 224 | } 225 | }); 226 | }); }; 227 | this.onDone = function (compilation, callback) { return __awaiter(_this, void 0, void 0, function () { 228 | var onBuildError, onBuildExit; 229 | return __generator(this, function (_a) { 230 | switch (_a.label) { 231 | case 0: 232 | if (!compilation.hasErrors()) return [3 /*break*/, 2]; 233 | onBuildError = this.onBuildError; 234 | if (!(onBuildError.scripts && onBuildError.scripts.length > 0)) return [3 /*break*/, 2]; 235 | this.warn('Executing error scripts before exit'); 236 | return [4 /*yield*/, this.executeScripts(onBuildError.scripts, onBuildError.parallel, onBuildError.blocking)]; 237 | case 1: 238 | _a.sent(); 239 | if (this.dev) { 240 | this.onBuildError = JSON.parse(JSON.stringify(defaultTask)); 241 | } 242 | _a.label = 2; 243 | case 2: 244 | onBuildExit = this.onBuildExit; 245 | if (!(onBuildExit.scripts && onBuildExit.scripts.length > 0)) return [3 /*break*/, 4]; 246 | this.log('Executing additional scripts before exit'); 247 | return [4 /*yield*/, this.executeScripts(onBuildExit.scripts, onBuildExit.parallel, onBuildExit.blocking)]; 248 | case 3: 249 | _a.sent(); 250 | if (this.dev) { 251 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)); 252 | } 253 | _a.label = 4; 254 | case 4: 255 | if (callback) { 256 | callback(); 257 | } 258 | return [2 /*return*/]; 259 | } 260 | }); 261 | }); }; 262 | this.watchRun = function (_compiler, callback) { return __awaiter(_this, void 0, void 0, function () { 263 | var onWatchRun; 264 | return __generator(this, function (_a) { 265 | switch (_a.label) { 266 | case 0: 267 | onWatchRun = this.onWatchRun; 268 | if (!(onWatchRun.scripts && onWatchRun.scripts.length)) return [3 /*break*/, 2]; 269 | this.log('Executing onWatchRun build scripts'); 270 | return [4 /*yield*/, this.executeScripts(onWatchRun.scripts, onWatchRun.parallel, onWatchRun.blocking)]; 271 | case 1: 272 | _a.sent(); 273 | if (this.dev) { 274 | this.onWatchRun = JSON.parse(JSON.stringify(defaultTask)); 275 | } 276 | _a.label = 2; 277 | case 2: 278 | if (callback) { 279 | callback(); 280 | } 281 | return [2 /*return*/]; 282 | } 283 | }); 284 | }); }; 285 | if (options.verbose) { 286 | this.warn("WebpackShellPluginNext [".concat(new Date(), "]: Verbose is being deprecated, please remove.")); 287 | } 288 | this.onBeforeBuild = this.validateEvent(options.onBeforeBuild); 289 | this.onBeforeNormalRun = this.validateEvent(options.onBeforeNormalRun); 290 | this.onBuildStart = this.validateEvent(options.onBuildStart); 291 | this.onBuildEnd = this.validateEvent(options.onBuildEnd); 292 | this.onBuildExit = this.validateEvent(options.onBuildExit); 293 | this.onBuildError = this.validateEvent(options.onBuildError); 294 | this.onWatchRun = this.validateEvent(options.onWatchRun); 295 | this.onDoneWatch = this.validateEvent(options.onDoneWatch); 296 | this.onAfterDone = this.validateEvent(options.onAfterDone); 297 | this.onFailedBuild = this.validateEvent(options.onFailedBuild); 298 | this.onBeforeCompile = this.validateEvent(options.onBeforeCompile); 299 | if (options.env !== undefined) { 300 | this.env = options.env; 301 | } 302 | if (options.dev !== undefined) { 303 | this.dev = options.dev; 304 | } 305 | if (options.safe !== undefined) { 306 | this.safe = options.safe; 307 | } 308 | if (options.shell !== undefined) { 309 | this.shell = options.shell; 310 | } 311 | if (options.logging !== undefined) { 312 | this.logging = options.logging; 313 | } 314 | if (options.swallowError !== undefined) { 315 | this.swallowError = options.swallowError; 316 | } 317 | this.onCompilation = this.onCompilation.bind(this); 318 | this.onBeforeRun = this.onBeforeRun.bind(this); 319 | this.onBeforeCompileRun = this.onBeforeCompileRun.bind(this); 320 | this.onAfterEmit = this.onAfterEmit.bind(this); 321 | this.onDone = this.onDone.bind(this); 322 | this.afterDone = this.afterDone.bind(this); 323 | this.onFailed = this.onFailed.bind(this); 324 | this.putsAsync = this.putsAsync.bind(this); 325 | this.puts = this.puts.bind(this); 326 | } 327 | WebpackShellPlugin.prototype.validateEvent = function (tasks) { 328 | if (!tasks) { 329 | return JSON.parse(JSON.stringify(defaultTask)); 330 | } 331 | if (typeof tasks === 'string') { 332 | return { scripts: tasks.split('&&'), blocking: false, parallel: false }; 333 | } 334 | else if (typeof tasks === 'function') { 335 | return { scripts: [tasks], blocking: false, parallel: false }; 336 | } 337 | return tasks; 338 | }; 339 | WebpackShellPlugin.prototype.putsAsync = function (resolve) { 340 | var _this = this; 341 | return function (error, _stdout, _stderr) { 342 | if (error && !_this.swallowError) { 343 | throw error; 344 | } 345 | resolve(error); 346 | }; 347 | }; 348 | WebpackShellPlugin.prototype.puts = function (error, _stdout, _stderr) { 349 | if (error && !this.swallowError) { 350 | throw error; 351 | } 352 | }; 353 | WebpackShellPlugin.prototype.spreadStdoutAndStdErr = function (proc) { 354 | if (!proc.stdout || !proc.stderr) 355 | return; 356 | proc.stdout.pipe(process.stdout); 357 | proc.stderr.pipe(process.stdout); 358 | }; 359 | WebpackShellPlugin.prototype.serializeScript = function (script) { 360 | if (typeof script === 'string') { 361 | var _a = script.split(' '), command_1 = _a[0], args_1 = _a.slice(1); 362 | return { command: command_1, args: args_1 }; 363 | } 364 | var command = script.command, args = script.args; 365 | return { command: command, args: args }; 366 | }; 367 | WebpackShellPlugin.prototype.handleScript = function (script) { 368 | if (this.safe) { 369 | return (0, child_process_1.execSync)(script, { maxBuffer: Number.MAX_SAFE_INTEGER, stdio: this.logging ? [0, 1, 2] : undefined }); 370 | } 371 | var _a = this.serializeScript(script), command = _a.command, args = _a.args; 372 | var env = Object.create(global.process.env); 373 | env = Object.assign(env, this.env); 374 | var result = (0, child_process_1.spawnSync)(command, args, { stdio: this.logging ? ['inherit', 'inherit', 'pipe'] : undefined, env: env, shell: this.shell }); 375 | if (this.logging && result.status !== 0) { 376 | this.error("stderr error ".concat(command, " ").concat(args.join(' '), ": ").concat(result.stderr)); 377 | } 378 | return result; 379 | }; 380 | WebpackShellPlugin.prototype.handleScriptAsync = function (script) { 381 | var _this = this; 382 | if (this.safe) { 383 | return new Promise(function (resolve) { 384 | _this.spreadStdoutAndStdErr((0, child_process_1.exec)(script, _this.putsAsync(resolve))); 385 | }); 386 | } 387 | var _a = this.serializeScript(script), command = _a.command, args = _a.args; 388 | var env = Object.create(global.process.env); 389 | env = Object.assign(env, this.env); 390 | var proc = (0, child_process_1.spawn)(command, args, { stdio: 'inherit', env: env, shell: this.shell }); 391 | if (this.logging) { 392 | proc.on('error', function (err) { 393 | _this.error("stderr error ".concat(command, " ").concat(args.join(' '), ": ").concat(err.message)); 394 | }); 395 | } 396 | return new Promise(function (resolve) { return proc.on('close', _this.putsAsync(resolve)); }); 397 | }; 398 | WebpackShellPlugin.prototype.executeScripts = function (scripts, parallel, blocking) { 399 | if (parallel === void 0) { parallel = false; } 400 | if (blocking === void 0) { blocking = false; } 401 | return __awaiter(this, void 0, void 0, function () { 402 | var i, script; 403 | return __generator(this, function (_a) { 404 | switch (_a.label) { 405 | case 0: 406 | if (!scripts || scripts.length <= 0) { 407 | return [2 /*return*/]; 408 | } 409 | if (blocking && parallel) { 410 | throw new Error("WebpackShellPluginNext [".concat(new Date(), "]: Not supported")); 411 | } 412 | i = 0; 413 | _a.label = 1; 414 | case 1: 415 | if (!(i < scripts.length)) return [3 /*break*/, 10]; 416 | script = scripts[i]; 417 | if (!(typeof script === 'function')) return [3 /*break*/, 5]; 418 | if (!blocking) return [3 /*break*/, 3]; 419 | return [4 /*yield*/, script()]; 420 | case 2: 421 | _a.sent(); 422 | return [3 /*break*/, 4]; 423 | case 3: 424 | script(); 425 | _a.label = 4; 426 | case 4: return [3 /*break*/, 9]; 427 | case 5: 428 | if (!blocking) return [3 /*break*/, 6]; 429 | this.handleScript(script); 430 | return [3 /*break*/, 9]; 431 | case 6: 432 | if (!!blocking) return [3 /*break*/, 9]; 433 | if (!parallel) return [3 /*break*/, 7]; 434 | this.handleScriptAsync(script); 435 | return [3 /*break*/, 9]; 436 | case 7: return [4 /*yield*/, this.handleScriptAsync(script)]; 437 | case 8: 438 | _a.sent(); 439 | _a.label = 9; 440 | case 9: 441 | i++; 442 | return [3 /*break*/, 1]; 443 | case 10: return [2 /*return*/]; 444 | } 445 | }); 446 | }); 447 | }; 448 | WebpackShellPlugin.prototype.apply = function (compiler) { 449 | compiler.hooks.beforeRun.tapAsync('webpack-shell-plugin-next', this.onBeforeRun); 450 | compiler.hooks.failed.tap('webpack-shell-plugin-next', this.onFailed); 451 | compiler.hooks.make.tap('webpack-shell-plugin-next', this.onBefore); 452 | compiler.hooks.beforeCompile.tapAsync('webpack-shell-plugin-next', this.onBeforeCompileRun); 453 | compiler.hooks.compilation.tap('webpack-shell-plugin-next', this.onCompilation); 454 | compiler.hooks.afterEmit.tapAsync('webpack-shell-plugin-next', this.onAfterEmit); 455 | compiler.hooks.done.tapAsync('webpack-shell-plugin-next', this.onDone); 456 | compiler.hooks.afterCompile.tapAsync('webpack-shell-plugin-next', this.afterCompile); 457 | compiler.hooks.afterDone.tap('webpack-shell-plugin-next', this.afterDone); 458 | compiler.hooks.watchRun.tapAsync('webpack-shell-plugin-next', this.watchRun); 459 | }; 460 | WebpackShellPlugin.prototype.log = function (text) { 461 | if (this.logging) { 462 | console.log(text); 463 | } 464 | }; 465 | WebpackShellPlugin.prototype.warn = function (text) { 466 | if (this.logging) { 467 | console.warn(text); 468 | } 469 | }; 470 | WebpackShellPlugin.prototype.error = function (text) { 471 | if (this.logging) { 472 | console.error(text); 473 | } 474 | }; 475 | return WebpackShellPlugin; 476 | }()); 477 | exports.default = WebpackShellPlugin; 478 | module.exports = WebpackShellPlugin; 479 | -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | export type Task = Function | string; 2 | export type Tasks = { 3 | scripts?: Task[]; 4 | blocking?: boolean; 5 | parallel?: boolean; 6 | }; 7 | export type Script = { 8 | command: string; 9 | args: string[]; 10 | }; 11 | export type Options = { 12 | /** 13 | * Scripts to execute before a normal run (without --watch). 14 | * Can be a Tasks object, a single script string, or a function. 15 | * Defaults to []. 16 | */ 17 | onBeforeNormalRun?: Tasks | string | Function; 18 | /** 19 | * Scripts to execute before the build process starts. 20 | * Can be a Tasks object, a single script string, or a function. 21 | * Defaults to []. 22 | */ 23 | onBeforeBuild?: Tasks | string | Function; 24 | /** 25 | * Scripts to execute when the build fails. 26 | * Can be a Tasks object, a single script string, or a function. 27 | * Defaults to []. 28 | */ 29 | onFailedBuild?: Tasks | string | Function; 30 | /** 31 | * Scripts to execute at the start of the build process. 32 | * Can be a Tasks object, a single script string, or a function. 33 | * Defaults to []. 34 | */ 35 | onBuildStart?: Tasks | string | Function; 36 | /** 37 | * Scripts to execute after files are emitted at the end of the compilation. 38 | * Can be a Tasks object, a single script string, or a function. 39 | * Defaults to []. 40 | */ 41 | onBuildEnd?: Tasks | string | Function; 42 | /** 43 | * Scripts to execute after Webpack's process completes. 44 | * Can be a Tasks object, a single script string, or a function. 45 | * Defaults to []. 46 | */ 47 | onBuildExit?: Tasks | string | Function; 48 | /** 49 | * Scripts to execute after a build error occurs. 50 | * Can be a Tasks object, a single script string, or a function. 51 | * Defaults to []. 52 | */ 53 | onBuildError?: Tasks | string | Function; 54 | /** 55 | * Scripts to execute before each watch run. 56 | * Can be a Tasks object, a single script string, or a function. 57 | * Defaults to []. 58 | */ 59 | onWatchRun?: Tasks | string | Function; 60 | /** 61 | * Scripts to execute after files are emitted at the end of each watch cycle. 62 | * Can be a Tasks object, a single script string, or a function. 63 | * Defaults to []. 64 | */ 65 | onDoneWatch?: Tasks | string | Function; 66 | /** 67 | * Scripts to execute after the entire Webpack process is done. 68 | * Can be a Tasks object, a single script string, or a function. 69 | * Defaults to []. 70 | */ 71 | onAfterDone?: Tasks | string | Function; 72 | /** 73 | * Scripts to execute before the compilation process starts. 74 | * Can be a Tasks object, a single script string, or a function. 75 | * Defaults to []. 76 | */ 77 | onBeforeCompile?: Tasks | string | Function; 78 | /** 79 | * Switch for development environments. This causes scripts to execute once. 80 | * Useful for running HMR on webpack-dev-server or webpack watch mode. 81 | * Defaults to true. 82 | */ 83 | dev?: boolean; 84 | /** 85 | * Object with environment variables that will be applied to the executables. 86 | */ 87 | env?: any; 88 | /** 89 | * Switches script execution process from spawn to exec. If running into 90 | * problems with spawn, turn this setting on. Defaults to false. 91 | */ 92 | safe?: boolean; 93 | /** 94 | * Show log messages. Defaults to true. 95 | */ 96 | logging?: boolean; 97 | /** 98 | * Ignore script errors (useful in watch mode). Defaults to false. 99 | */ 100 | swallowError?: boolean; 101 | /** 102 | * Run command in shell. Defaults to true. 103 | */ 104 | shell?: boolean; 105 | /** 106 | * DEPRECATED. Enable for verbose output. Defaults to false. 107 | */ 108 | verbose?: boolean; 109 | }; 110 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-shell-plugin-next", 3 | "version": "2.3.2", 4 | "description": "Run shell commands before and after webpack builds", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "files": [ 8 | "lib" 9 | ], 10 | "scripts": { 11 | "clean": "rimraf lib && rimraf dist", 12 | "test": "./node_modules/.bin/jest --config ./jest.config.ts", 13 | "test:watch": "webpack --watch", 14 | "test:dev": "webpack-dev-server --progress", 15 | "test:local": "webpack", 16 | "prepublish": "npm run clean && npm run build", 17 | "prepare": "npm run build", 18 | "build": "tsc", 19 | "webpack": "webpack --progress --mode production", 20 | "webpack-dev-server": "webpack-dev-server --progress", 21 | "lint": "tslint --project tsconfig.json ./src/*.ts", 22 | "lint:fix": "npm run lint -- --fix" 23 | }, 24 | "pre-commit": [ 25 | "test", 26 | "lint" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/s00d/webpack-shell-plugin-next.git" 31 | }, 32 | "keywords": [ 33 | "webpack", 34 | "shell", 35 | "plugin", 36 | "shell", 37 | "serve", 38 | "hmr", 39 | "browser", 40 | "script", 41 | "opie", 42 | "manion", 43 | "typescript" 44 | ], 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/s00d/webpack-shell-plugin-next/issues" 48 | }, 49 | "homepage": "https://github.com/s00d/webpack-shell-plugin-next", 50 | "devDependencies": { 51 | "@types/jest": "^29.5.12", 52 | "@types/node": "^20.14.14", 53 | "@types/source-map": "^0.5.7", 54 | "css-loader": "^6.11.0", 55 | "jest": "^29.7.0", 56 | "module-alias": "^2.2.3", 57 | "rimraf": "^6.0.1", 58 | "source-map": "^0.7.4", 59 | "standardx": "^7.0.0", 60 | "style-loader": "^3.3.4", 61 | "ts-jest": "^29.2.4", 62 | "ts-node": "^10.9.2", 63 | "tslint": "^6.1.3", 64 | "tslint-config-standard": "^9.0.0", 65 | "typescript": "^4.9.5", 66 | "webpack": "^5.93.0", 67 | "webpack-cli": "^5.1.4" 68 | }, 69 | "peerDependencies": { 70 | "webpack": "^5.18.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @class WebpackShellPluginNext 3 | * @extends Object 4 | * Run shell commands before and after webpack builds 5 | */ 6 | 7 | import { spawn, exec, spawnSync, execSync, ChildProcess, ExecException } from 'child_process' 8 | import { Options, Script, Tasks, Task } from './types' 9 | import * as webpack from 'webpack' 10 | import { Readable } from 'stream' 11 | 12 | const defaultTask: Tasks = { 13 | scripts: [], 14 | blocking: false, 15 | parallel: false 16 | } 17 | 18 | export default class WebpackShellPlugin { 19 | private onBeforeNormalRun: Tasks 20 | private onBeforeBuild: Tasks 21 | private onBuildStart: Tasks 22 | private onBuildEnd: Tasks 23 | private onBuildExit: Tasks 24 | private onBuildError: Tasks 25 | private onWatchRun: Tasks 26 | private onDoneWatch: Tasks 27 | private onAfterDone: Tasks 28 | private onFailedBuild: Tasks 29 | private onBeforeCompile: Tasks 30 | private env: any = {} 31 | private dev = true 32 | private shell = true 33 | private safe = false 34 | private logging = true 35 | private swallowError = false 36 | 37 | private validateEvent (tasks: Tasks | string | Function | undefined | null): Tasks { 38 | if (!tasks) { 39 | return JSON.parse(JSON.stringify(defaultTask)) 40 | } 41 | if (typeof tasks === 'string') { 42 | return { scripts: tasks.split('&&'), blocking: false, parallel: false } 43 | } else if (typeof tasks === 'function') { 44 | return { scripts: [tasks], blocking: false, parallel: false } 45 | } 46 | 47 | return tasks 48 | } 49 | 50 | constructor (options: Options) { 51 | if (options.verbose) { 52 | this.warn(`WebpackShellPluginNext [${new Date()}]: Verbose is being deprecated, please remove.`) 53 | } 54 | 55 | this.onBeforeBuild = this.validateEvent(options.onBeforeBuild) 56 | this.onBeforeNormalRun = this.validateEvent(options.onBeforeNormalRun) 57 | this.onBuildStart = this.validateEvent(options.onBuildStart) 58 | this.onBuildEnd = this.validateEvent(options.onBuildEnd) 59 | this.onBuildExit = this.validateEvent(options.onBuildExit) 60 | this.onBuildError = this.validateEvent(options.onBuildError) 61 | this.onWatchRun = this.validateEvent(options.onWatchRun) 62 | this.onDoneWatch = this.validateEvent(options.onDoneWatch) 63 | this.onAfterDone = this.validateEvent(options.onAfterDone) 64 | this.onFailedBuild = this.validateEvent(options.onFailedBuild) 65 | this.onBeforeCompile = this.validateEvent(options.onBeforeCompile) 66 | 67 | if (options.env !== undefined) { 68 | this.env = options.env 69 | } 70 | if (options.dev !== undefined) { 71 | this.dev = options.dev 72 | } 73 | if (options.safe !== undefined) { 74 | this.safe = options.safe 75 | } 76 | if (options.shell !== undefined) { 77 | this.shell = options.shell 78 | } 79 | if (options.logging !== undefined) { 80 | this.logging = options.logging 81 | } 82 | if (options.swallowError !== undefined) { 83 | this.swallowError = options.swallowError 84 | } 85 | 86 | this.onCompilation = this.onCompilation.bind(this) 87 | this.onBeforeRun = this.onBeforeRun.bind(this) 88 | this.onBeforeCompileRun = this.onBeforeCompileRun.bind(this) 89 | this.onAfterEmit = this.onAfterEmit.bind(this) 90 | this.onDone = this.onDone.bind(this) 91 | this.afterDone = this.afterDone.bind(this) 92 | this.onFailed = this.onFailed.bind(this) 93 | this.putsAsync = this.putsAsync.bind(this) 94 | this.puts = this.puts.bind(this) 95 | } 96 | 97 | private putsAsync (resolve: (val: any) => void) { 98 | return (error: ExecException | null, _stdout: string, _stderr: string) => { 99 | if (error && !this.swallowError) { 100 | throw error 101 | } 102 | resolve(error) 103 | } 104 | } 105 | 106 | private puts (error: Error, _stdout: Readable, _stderr: Readable) { 107 | if (error && !this.swallowError) { 108 | throw error 109 | } 110 | } 111 | 112 | private spreadStdoutAndStdErr (proc: ChildProcess) { 113 | if (!proc.stdout || !proc.stderr) return 114 | proc.stdout.pipe(process.stdout) 115 | proc.stderr.pipe(process.stdout) 116 | } 117 | 118 | private serializeScript (script: string | Script): Script { 119 | if (typeof script === 'string') { 120 | const [command, ...args] = script.split(' ') 121 | return { command, args } 122 | } 123 | const { command, args } = script 124 | return { command, args } 125 | } 126 | 127 | private handleScript (script: string) { 128 | if (this.safe) { 129 | return execSync(script, { maxBuffer: Number.MAX_SAFE_INTEGER, stdio: this.logging ? [0, 1, 2] : undefined }) 130 | } 131 | 132 | const { command, args } = this.serializeScript(script) 133 | let env = Object.create(global.process.env) 134 | env = Object.assign(env, this.env) 135 | const result = spawnSync(command, args, { stdio: this.logging ? ['inherit', 'inherit', 'pipe'] : undefined, env, shell: this.shell }) 136 | if (this.logging && result.status !== 0) { 137 | this.error(`stderr error ${command} ${args.join(' ')}: ${result.stderr}`) 138 | } 139 | return result 140 | } 141 | 142 | private handleScriptAsync (script: string) { 143 | if (this.safe) { 144 | return new Promise((resolve) => { 145 | this.spreadStdoutAndStdErr(exec(script, this.putsAsync(resolve))) 146 | }) 147 | } 148 | 149 | const { command, args } = this.serializeScript(script) 150 | let env = Object.create(global.process.env) 151 | env = Object.assign(env, this.env) 152 | const proc = spawn(command, args, { stdio: 'inherit', env: env, shell: this.shell }) 153 | if (this.logging) { 154 | proc.on('error', (err) => { 155 | this.error(`stderr error ${command} ${args.join(' ')}: ${err.message}`) 156 | }) 157 | } 158 | return new Promise((resolve) => proc.on('close', this.putsAsync(resolve))) 159 | } 160 | 161 | private async executeScripts (scripts: Task[], parallel = false, blocking = false) { 162 | if (!scripts || scripts.length <= 0) { 163 | return 164 | } 165 | 166 | if (blocking && parallel) { 167 | throw new Error(`WebpackShellPluginNext [${new Date()}]: Not supported`) 168 | } 169 | 170 | for (let i = 0; i < scripts.length; i++) { 171 | const script: Task = scripts[i] 172 | if (typeof script === 'function') { 173 | if (blocking) { 174 | await script() 175 | } else { 176 | script() 177 | } 178 | continue 179 | } 180 | if (blocking) { 181 | this.handleScript(script) 182 | } else if (!blocking) { 183 | if (parallel) this.handleScriptAsync(script); else await this.handleScriptAsync(script) 184 | } 185 | } 186 | } 187 | 188 | apply (compiler: webpack.Compiler): void { 189 | compiler.hooks.beforeRun.tapAsync('webpack-shell-plugin-next', this.onBeforeRun) 190 | compiler.hooks.failed.tap('webpack-shell-plugin-next', this.onFailed) 191 | compiler.hooks.make.tap('webpack-shell-plugin-next', this.onBefore) 192 | compiler.hooks.beforeCompile.tapAsync('webpack-shell-plugin-next', this.onBeforeCompileRun) 193 | compiler.hooks.compilation.tap('webpack-shell-plugin-next', this.onCompilation) 194 | compiler.hooks.afterEmit.tapAsync('webpack-shell-plugin-next', this.onAfterEmit) 195 | compiler.hooks.done.tapAsync('webpack-shell-plugin-next', this.onDone) 196 | compiler.hooks.afterCompile.tapAsync('webpack-shell-plugin-next', this.afterCompile) 197 | compiler.hooks.afterDone.tap('webpack-shell-plugin-next', this.afterDone) 198 | compiler.hooks.watchRun.tapAsync('webpack-shell-plugin-next', this.watchRun) 199 | } 200 | 201 | private readonly onBeforeRun = async (_compiler: webpack.Compiler, callback?: Function): Promise => { 202 | const onBeforeNormalRun = this.onBeforeNormalRun 203 | if (onBeforeNormalRun.scripts && onBeforeNormalRun.scripts.length > 0) { 204 | this.log('Executing pre-run scripts') 205 | await this.executeScripts(onBeforeNormalRun.scripts, onBeforeNormalRun.parallel, onBeforeNormalRun.blocking) 206 | if (this.dev) { 207 | this.onDoneWatch = JSON.parse(JSON.stringify(defaultTask)) 208 | } 209 | } 210 | if (callback) { 211 | callback() 212 | } 213 | } 214 | 215 | private readonly afterDone = async (): Promise => { 216 | const onAfterDone = this.onAfterDone 217 | if (onAfterDone.scripts && onAfterDone.scripts.length > 0) { 218 | this.log('Executing additional scripts before exit') 219 | await this.executeScripts(onAfterDone.scripts, onAfterDone.parallel, onAfterDone.blocking) 220 | if (this.dev) { 221 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)) 222 | } 223 | } 224 | } 225 | 226 | private readonly afterCompile = async (_compilation: webpack.Compilation, callback?: Function): Promise => { 227 | const onDoneWatch = this.onDoneWatch 228 | if (onDoneWatch.scripts && onDoneWatch.scripts.length > 0) { 229 | this.log('Executing additional scripts before exit') 230 | await this.executeScripts(onDoneWatch.scripts, onDoneWatch.parallel, onDoneWatch.blocking) 231 | if (this.dev) { 232 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)) 233 | } 234 | } 235 | if (callback) { 236 | callback() 237 | } 238 | }; 239 | 240 | private readonly onFailed = async (): Promise => { 241 | const onFailedBuild = this.onFailedBuild 242 | if (onFailedBuild.scripts && onFailedBuild.scripts.length) { 243 | this.log('Executing before build scripts') 244 | await this.executeScripts(onFailedBuild.scripts, onFailedBuild.parallel, onFailedBuild.blocking) 245 | if (this.dev) { 246 | this.onBeforeBuild = JSON.parse(JSON.stringify(defaultTask)) 247 | } 248 | } 249 | }; 250 | 251 | private readonly onBefore = async (_compilation: webpack.Compilation): Promise => { 252 | const onBeforeBuild = this.onBeforeBuild 253 | if (onBeforeBuild.scripts && onBeforeBuild.scripts.length) { 254 | this.log('Executing before build scripts') 255 | await this.executeScripts(onBeforeBuild.scripts, onBeforeBuild.parallel, onBeforeBuild.blocking) 256 | if (this.dev) { 257 | this.onBeforeBuild = JSON.parse(JSON.stringify(defaultTask)) 258 | } 259 | } 260 | }; 261 | 262 | private readonly onCompilation = async (compilation: webpack.Compilation): Promise => { 263 | const onBuildStartOption = this.onBuildStart 264 | if (onBuildStartOption.scripts && onBuildStartOption.scripts.length > 0) { 265 | this.log('Executing pre-build scripts') 266 | await this.executeScripts(onBuildStartOption.scripts, onBuildStartOption.parallel, onBuildStartOption.blocking) 267 | if (this.dev) { 268 | this.onBuildStart = JSON.parse(JSON.stringify(defaultTask)) 269 | } 270 | } 271 | }; 272 | 273 | private readonly onBeforeCompileRun = async (_params: any, callback: () => void): Promise => { 274 | const onBeforeCompile = this.onBeforeCompile 275 | if (onBeforeCompile.scripts && onBeforeCompile.scripts.length > 0) { 276 | this.log('Executing pre-build scripts') 277 | await this.executeScripts(onBeforeCompile.scripts, onBeforeCompile.parallel, onBeforeCompile.blocking) 278 | if (this.dev) { 279 | this.onBuildStart = JSON.parse(JSON.stringify(defaultTask)) 280 | } 281 | } 282 | if (callback) { 283 | callback() 284 | } 285 | }; 286 | 287 | private readonly onAfterEmit = async (_compilation: webpack.Compilation, callback?: Function): Promise => { 288 | const onBuildEndOption = this.onBuildEnd 289 | if (onBuildEndOption.scripts && onBuildEndOption.scripts.length > 0) { 290 | this.log('Executing post-build scripts') 291 | await this.executeScripts(onBuildEndOption.scripts, onBuildEndOption.parallel, onBuildEndOption.blocking) 292 | if (this.dev) { 293 | this.onBuildEnd = JSON.parse(JSON.stringify(defaultTask)) 294 | } 295 | } 296 | if (callback) { 297 | callback() 298 | } 299 | }; 300 | 301 | private readonly onDone = async (compilation: webpack.Stats, callback?: Function): Promise => { 302 | if (compilation.hasErrors()) { 303 | const onBuildError = this.onBuildError 304 | if (onBuildError.scripts && onBuildError.scripts.length > 0) { 305 | this.warn('Executing error scripts before exit') 306 | await this.executeScripts(onBuildError.scripts, onBuildError.parallel, onBuildError.blocking) 307 | if (this.dev) { 308 | this.onBuildError = JSON.parse(JSON.stringify(defaultTask)) 309 | } 310 | } 311 | } 312 | const onBuildExit = this.onBuildExit 313 | if (onBuildExit.scripts && onBuildExit.scripts.length > 0) { 314 | this.log('Executing additional scripts before exit') 315 | await this.executeScripts(onBuildExit.scripts, onBuildExit.parallel, onBuildExit.blocking) 316 | if (this.dev) { 317 | this.onBuildExit = JSON.parse(JSON.stringify(defaultTask)) 318 | } 319 | } 320 | if (callback) { 321 | callback() 322 | } 323 | }; 324 | 325 | private readonly watchRun = async (_compiler: webpack.Compiler, callback?: Function): Promise => { 326 | const onWatchRun = this.onWatchRun 327 | if (onWatchRun.scripts && onWatchRun.scripts.length) { 328 | this.log('Executing onWatchRun build scripts') 329 | await this.executeScripts(onWatchRun.scripts, onWatchRun.parallel, onWatchRun.blocking) 330 | if (this.dev) { 331 | this.onWatchRun = JSON.parse(JSON.stringify(defaultTask)) 332 | } 333 | } 334 | 335 | if (callback) { 336 | callback() 337 | } 338 | }; 339 | 340 | private log (text: string) { 341 | if (this.logging) { 342 | console.log(text) 343 | } 344 | } 345 | 346 | private warn (text: string) { 347 | if (this.logging) { 348 | console.warn(text) 349 | } 350 | } 351 | 352 | private error (text: string) { 353 | if (this.logging) { 354 | console.error(text) 355 | } 356 | } 357 | } 358 | 359 | module.exports = WebpackShellPlugin 360 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type Task = Function | string 2 | export type Tasks = { 3 | scripts?: Task[], 4 | blocking?: boolean, 5 | parallel?: boolean 6 | } 7 | 8 | export type Script = { 9 | command: string, 10 | args: string[] 11 | } 12 | 13 | export type Options = { 14 | /** 15 | * Scripts to execute before a normal run (without --watch). 16 | * Can be a Tasks object, a single script string, or a function. 17 | * Defaults to []. 18 | */ 19 | onBeforeNormalRun?: Tasks | string | Function, 20 | 21 | /** 22 | * Scripts to execute before the build process starts. 23 | * Can be a Tasks object, a single script string, or a function. 24 | * Defaults to []. 25 | */ 26 | onBeforeBuild?: Tasks | string | Function, 27 | 28 | /** 29 | * Scripts to execute when the build fails. 30 | * Can be a Tasks object, a single script string, or a function. 31 | * Defaults to []. 32 | */ 33 | onFailedBuild?: Tasks | string | Function, 34 | 35 | /** 36 | * Scripts to execute at the start of the build process. 37 | * Can be a Tasks object, a single script string, or a function. 38 | * Defaults to []. 39 | */ 40 | onBuildStart?: Tasks | string | Function, 41 | 42 | /** 43 | * Scripts to execute after files are emitted at the end of the compilation. 44 | * Can be a Tasks object, a single script string, or a function. 45 | * Defaults to []. 46 | */ 47 | onBuildEnd?: Tasks | string | Function, 48 | 49 | /** 50 | * Scripts to execute after Webpack's process completes. 51 | * Can be a Tasks object, a single script string, or a function. 52 | * Defaults to []. 53 | */ 54 | onBuildExit?: Tasks | string | Function, 55 | 56 | /** 57 | * Scripts to execute after a build error occurs. 58 | * Can be a Tasks object, a single script string, or a function. 59 | * Defaults to []. 60 | */ 61 | onBuildError?: Tasks | string | Function, 62 | 63 | /** 64 | * Scripts to execute before each watch run. 65 | * Can be a Tasks object, a single script string, or a function. 66 | * Defaults to []. 67 | */ 68 | onWatchRun?: Tasks | string | Function, 69 | 70 | /** 71 | * Scripts to execute after files are emitted at the end of each watch cycle. 72 | * Can be a Tasks object, a single script string, or a function. 73 | * Defaults to []. 74 | */ 75 | onDoneWatch?: Tasks | string | Function, 76 | 77 | /** 78 | * Scripts to execute after the entire Webpack process is done. 79 | * Can be a Tasks object, a single script string, or a function. 80 | * Defaults to []. 81 | */ 82 | onAfterDone?: Tasks | string | Function, 83 | /** 84 | * Scripts to execute before the compilation process starts. 85 | * Can be a Tasks object, a single script string, or a function. 86 | * Defaults to []. 87 | */ 88 | onBeforeCompile?: Tasks | string | Function, 89 | 90 | /** 91 | * Switch for development environments. This causes scripts to execute once. 92 | * Useful for running HMR on webpack-dev-server or webpack watch mode. 93 | * Defaults to true. 94 | */ 95 | dev?: boolean, 96 | 97 | /** 98 | * Object with environment variables that will be applied to the executables. 99 | */ 100 | env?: any, 101 | 102 | /** 103 | * Switches script execution process from spawn to exec. If running into 104 | * problems with spawn, turn this setting on. Defaults to false. 105 | */ 106 | safe?: boolean, 107 | 108 | /** 109 | * Show log messages. Defaults to true. 110 | */ 111 | logging?: boolean, 112 | 113 | /** 114 | * Ignore script errors (useful in watch mode). Defaults to false. 115 | */ 116 | swallowError?: boolean, 117 | 118 | /** 119 | * Run command in shell. Defaults to true. 120 | */ 121 | shell?: boolean, 122 | 123 | /** 124 | * DEPRECATED. Enable for verbose output. Defaults to false. 125 | */ 126 | verbose?: boolean 127 | } 128 | -------------------------------------------------------------------------------- /tests/generate.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import WebpackShellPlugin from '../src/index' 3 | import Webpack from 'webpack' 4 | import fs from 'fs' 5 | import WebpackShellPluginNext from '../src' 6 | import { rimraf } from 'rimraf' 7 | 8 | // tslint:disable-next-line:no-empty 9 | console.log = (data: any) => {} 10 | 11 | beforeEach(() => { 12 | return rimraf.sync(path.resolve(__dirname, 'out')) 13 | }) 14 | 15 | afterEach(() => { 16 | return rimraf.sync(path.resolve(__dirname, 'out')) 17 | }) 18 | 19 | it('Supports an entry', () => { 20 | expect(() => { 21 | Webpack({ 22 | entry: './webpack/index.js', 23 | output: { 24 | path: path.resolve(__dirname, 'out'), 25 | filename: 'bundle.js' 26 | }, 27 | plugins: [ 28 | new WebpackShellPlugin({ 29 | onBeforeNormalRun: { 30 | scripts: [ 31 | 'echo "onBeforeRun"', 32 | 'sleep 1' 33 | ], 34 | blocking: true, 35 | parallel: false 36 | } 37 | }) 38 | ] 39 | }) 40 | }).not.toThrow() 41 | }) 42 | 43 | it('Check scripts exec', (done) => { 44 | Webpack({ 45 | entry: path.resolve(__dirname, './webpack/index.js'), 46 | output: { 47 | path: path.resolve(__dirname, 'out'), 48 | filename: 'bundle.js' 49 | }, 50 | plugins: [ 51 | new WebpackShellPlugin({ 52 | onBuildExit: { 53 | scripts: [ 54 | 'node ./tests/scripts/big_data.js' 55 | ], 56 | blocking: true, 57 | parallel: false 58 | } 59 | }) 60 | ] 61 | }, (error, stats) => { 62 | done(error) 63 | }) 64 | }) 65 | 66 | it('Check scripts exit code', (done) => { 67 | Webpack({ 68 | entry: path.resolve(__dirname, './webpack/index.js'), 69 | output: { 70 | path: path.resolve(__dirname, 'out'), 71 | filename: 'bundle.js' 72 | }, 73 | plugins: [ 74 | new WebpackShellPlugin({ 75 | logging: false, 76 | onBuildExit: { 77 | scripts: [ 78 | 'node ./tests/scripts/exit-code-1.js' 79 | ], 80 | blocking: true, 81 | parallel: false 82 | } 83 | }) 84 | ] 85 | }, () => { 86 | done() 87 | }) 88 | }) 89 | 90 | it('Check scripts with file', (done) => { 91 | Webpack({ 92 | entry: path.resolve(__dirname, './webpack/index.js'), 93 | output: { 94 | path: path.resolve(__dirname, 'out'), 95 | filename: 'bundle.js' 96 | }, 97 | plugins: [ 98 | new WebpackShellPlugin({ 99 | logging: false, 100 | onBuildExit: { 101 | scripts: [ 102 | 'node ./tests/scripts/file.js' 103 | ], 104 | blocking: true, 105 | parallel: false 106 | } 107 | }) 108 | ] 109 | }, () => { 110 | expect(fs.existsSync(path.join(__dirname, './out/test.txt'))).toBe(true) 111 | done() 112 | }) 113 | }) 114 | 115 | it('Check scripts run', (done) => { 116 | Webpack({ 117 | entry: path.resolve(__dirname, './webpack/index.js'), 118 | output: { 119 | path: path.resolve(__dirname, 'out'), 120 | filename: 'bundle.js' 121 | }, 122 | plugins: [ 123 | new WebpackShellPlugin({ 124 | onBuildExit: { 125 | scripts: [ 126 | () => new Promise((resolve, reject) => { 127 | fs.writeFileSync(path.join(__dirname, './out/run.txt'), 'Hey there!') 128 | resolve('ok') 129 | }) 130 | ], 131 | blocking: true, 132 | parallel: false 133 | } 134 | }) 135 | ] 136 | }, (error, stats) => { 137 | if (error) { 138 | throw Error(error.message) 139 | } 140 | expect(fs.existsSync(path.join(__dirname, './out/run.txt'))).toBe(true) 141 | expect(fs.existsSync(path.join(__dirname, './out/bundle.js'))).toBe(true) 142 | done() 143 | }) 144 | }) 145 | 146 | describe('testEvents', () => { 147 | const error = { 148 | name: 'errorName', 149 | message: 'errorMessage' 150 | } 151 | let consoleOutput: Array = [] 152 | const mockedLog = (output: string) => consoleOutput.push(output) 153 | beforeEach(() => (consoleOutput = [])) 154 | afterEach(() => (consoleOutput = [])) 155 | 156 | it('work test', (done) => { 157 | consoleOutput = [] 158 | Webpack({ 159 | entry: path.resolve(__dirname, './webpack/index.js'), 160 | output: { 161 | path: path.resolve(__dirname, 'out'), 162 | filename: 'bundle.js' 163 | }, 164 | plugins: [ 165 | new WebpackShellPlugin({ 166 | onBuildExit: { 167 | scripts: [ 168 | () => new Promise((resolve, reject) => { 169 | mockedLog('test 1') 170 | resolve('ok') 171 | }) 172 | ], 173 | blocking: true, 174 | parallel: false 175 | } 176 | }) 177 | ] 178 | }, (error, stats) => { 179 | expect(consoleOutput).toEqual([ 180 | 'test 1' 181 | ]) 182 | done(error) 183 | }) 184 | }) 185 | 186 | it('test queue', (done) => { 187 | consoleOutput = [] 188 | Webpack({ 189 | entry: path.resolve(__dirname, './webpack/index.js'), 190 | output: { 191 | path: path.resolve(__dirname, 'out'), 192 | filename: 'bundle.js' 193 | }, 194 | plugins: [ 195 | new WebpackShellPlugin({ 196 | onBeforeNormalRun: () => mockedLog('onBeforeNormalRun'), 197 | onBeforeBuild: () => mockedLog('onBeforeBuild'), 198 | onBuildStart: () => mockedLog('onBuildStart'), 199 | onBuildEnd: () => mockedLog('onBuildEnd'), 200 | onBuildExit: () => mockedLog('onBuildExit'), 201 | onBuildError: () => mockedLog('onBuildError'), 202 | onWatchRun: () => mockedLog('onWatchRun'), 203 | onDoneWatch: () => mockedLog('onDoneWatch'), 204 | onAfterDone: () => mockedLog('onAfterDone') 205 | }) 206 | ] 207 | }, (error, stats) => { 208 | setTimeout(() => { 209 | expect(consoleOutput).toEqual([ 210 | 'onBeforeNormalRun', 211 | 'onBuildStart', 212 | 'onBeforeBuild', 213 | 'onBuildEnd', 214 | 'onBuildExit', 215 | 'onAfterDone' 216 | ]) 217 | done(error) 218 | }, 100) 219 | }) 220 | }) 221 | 222 | it('test onBeforeBuild', (done) => { 223 | consoleOutput = [] 224 | Webpack({ 225 | entry: path.resolve(__dirname, './webpack/index.js'), 226 | output: { 227 | path: path.resolve(__dirname, 'out'), 228 | filename: 'bundle.js' 229 | }, 230 | plugins: [ 231 | new WebpackShellPlugin({ 232 | onBeforeBuild: () => mockedLog('onBeforeBuild'), 233 | }) 234 | ] 235 | }, (error, stats) => { 236 | setTimeout(() => { 237 | expect(consoleOutput).toEqual([ 238 | 'onBeforeBuild', 239 | ]) 240 | done(error) 241 | }, 100) 242 | }) 243 | }) 244 | 245 | it('test onBeforeCompile', (done) => { 246 | consoleOutput = [] 247 | Webpack( 248 | { 249 | entry: path.resolve(__dirname, './webpack/index.js'), 250 | output: { 251 | path: path.resolve(__dirname, 'out'), 252 | filename: 'bundle.js', 253 | }, 254 | plugins: [ 255 | new WebpackShellPluginNext({ 256 | onAfterDone: () => mockedLog('onAfterDone'), 257 | onBeforeCompile: () => mockedLog('onBeforeCompile'), 258 | }), 259 | ], 260 | }, 261 | (error, stats) => { 262 | setTimeout(() => { 263 | expect(consoleOutput).toEqual(['onBeforeCompile', 'onAfterDone']) 264 | done(error) 265 | }, 200) 266 | } 267 | ) 268 | }) 269 | }) 270 | -------------------------------------------------------------------------------- /tests/scripts/big_data.js: -------------------------------------------------------------------------------- 1 | console.log('run bigData'); 2 | setTimeout(function () { 3 | try { 4 | return Number.MAX_SAFE_INTEGER + Number.MAX_SAFE_INTEGER 5 | } catch (e) { 6 | console.warn(e) 7 | } 8 | }, 1000); 9 | 10 | -------------------------------------------------------------------------------- /tests/scripts/content.js: -------------------------------------------------------------------------------- 1 | module.exports = 'This is my content.js.'; 2 | -------------------------------------------------------------------------------- /tests/scripts/entry.js: -------------------------------------------------------------------------------- 1 | require("!style-loader!css-loader!./style.css"); 2 | document.write(require("./content.js")); 3 | console.log('Hello'); 4 | -------------------------------------------------------------------------------- /tests/scripts/exit-code-1.js: -------------------------------------------------------------------------------- 1 | console.log('run js'); 2 | setTimeout(function () { 3 | console.log('end js'); 4 | process.exit(1); 5 | }, 1000); 6 | 7 | -------------------------------------------------------------------------------- /tests/scripts/file.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | fs.writeFileSync(path.join(__dirname, '../out/test.txt'), 'Hey there!'); 4 | process.exit(1); 5 | -------------------------------------------------------------------------------- /tests/scripts/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: slategrey; 3 | color: blueviolet; 4 | } 5 | -------------------------------------------------------------------------------- /tests/webpack/content.js: -------------------------------------------------------------------------------- 1 | module.exports = 'This is my content.js.'; -------------------------------------------------------------------------------- /tests/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/webpack/index.js: -------------------------------------------------------------------------------- 1 | require("!style-loader!css-loader!./style.css"); 2 | document.write(require("./content.js")); 3 | console.log('Hello'); 4 | -------------------------------------------------------------------------------- /tests/webpack/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: slategrey; 3 | color: blueviolet; 4 | } 5 | 6 | /*cause-webpack-error*/ 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib", 4 | "esModuleInterop": true, 5 | "declaration": true, 6 | "noImplicitAny": true, 7 | "noImplicitReturns": true, 8 | "noImplicitThis": true, 9 | "strictNullChecks": true, 10 | "target": "es5" 11 | }, 12 | "files": [ 13 | "src/index.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard" 4 | ], 5 | "linterOptions": { 6 | "exclude": [ 7 | "bin/**/*", 8 | "lib/**/*" 9 | ] 10 | }, 11 | "rules": { 12 | "typedef": [ 13 | false, 14 | "call-signature", 15 | "parameter", 16 | "property-declaration", 17 | "member-variable-declaration" 18 | ], 19 | "max-line-length": [ 20 | true, 21 | 150 22 | ], 23 | "trailing-comma": [ 24 | true 25 | ], 26 | "variable-name": [ 27 | false 28 | ], 29 | "object-shorthand-properties-first": false, 30 | "no-else-after-return": false, 31 | "prefer-array-literal": false, 32 | "import-name": false, 33 | "align": [ 34 | false 35 | ], 36 | "prefer-template": true, 37 | "semicolon": [ 38 | true, 39 | "never", 40 | "ignore-bound-class-methods" 41 | ], 42 | "ter-arrow-parens": [ 43 | true 44 | ], 45 | "ter-indent": [ 46 | false 47 | ], 48 | "quotemark": [ 49 | true, 50 | "single", 51 | "jsx-double", 52 | "avoid-escape" 53 | ], 54 | "no-increment-decrement": false, 55 | "space-before-function-paren": false 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('ts-node').register({ 2 | project: 'tsconfig.json' 3 | }); 4 | 5 | module.exports = require('./webpack.config.ts').default; 6 | -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | // webpack.config.ts 2 | import * as webpack from 'webpack' 3 | import WebpackShellPlugin from './src' 4 | import * as path from 'path' 5 | 6 | const config: webpack.Configuration = { 7 | mode: 'development', 8 | entry: path.resolve(__dirname, 'tests/scripts/entry.js'), 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'bundle.js' 12 | }, 13 | watchOptions: { 14 | ignored: ['node_modules/**'] 15 | }, 16 | module: { 17 | noParse: /node_modules\/json-schema\/lib\/validate\.js/, 18 | rules: [ 19 | { 20 | test: /\.css$/, 21 | use: [ 22 | { loader: 'style!css' } 23 | ] 24 | } 25 | ] 26 | }, 27 | plugins: [ 28 | new WebpackShellPlugin({ 29 | onBuildStart: { 30 | scripts: [ 31 | `echo "test onBuildStart"`, 32 | 'echo "Building ..."' 33 | ] 34 | }, 35 | onBuildEnd: { 36 | scripts: [ 37 | 'echo "Done!"' 38 | ] 39 | }, 40 | dev: false, 41 | safe: false, 42 | logging: true 43 | }), 44 | new WebpackShellPlugin({ 45 | onBeforeBuild: { 46 | scripts: [ 47 | `echo "test onBeforeBuild"`, 48 | 'echo "Building ..."' 49 | ] 50 | }, 51 | dev: false, 52 | safe: false, 53 | logging: true 54 | }) 55 | ] 56 | } 57 | 58 | export default config 59 | --------------------------------------------------------------------------------