├── .eslintignore ├── .eslintrc ├── src ├── cjs.js ├── WorkerError.js ├── readBuffer.js ├── workerPools.js ├── index.js ├── worker.js └── WorkerPool.js ├── example ├── _shared.scss ├── style.scss ├── package.json ├── .babelrc ├── index.js ├── webpack.config.js └── package-lock.json ├── .gitattributes ├── test ├── __snapshots__ │ ├── pitch.test.js.snap │ └── workerPool.test.js.snap ├── pitch.test.js ├── readBuffer.test.js └── workerPool.test.js ├── .editorconfig ├── .gitignore ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── appveyor.yml ├── .babelrc ├── LICENSE ├── .travis.yml ├── package.json ├── README.md └── CHANGELOG.md /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "webpack" 3 | } -------------------------------------------------------------------------------- /src/cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./index'); 2 | -------------------------------------------------------------------------------- /example/_shared.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /example/style.scss: -------------------------------------------------------------------------------- 1 | @import '_shared'; 2 | 3 | body { 4 | background: red; 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | yarn.lock -diff 2 | * text=auto 3 | bin/* eol=lf 4 | package-lock.json -diff -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "lodash-es": "^4.17.4", 4 | "react": "^16.6.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/__snapshots__/pitch.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`runs pitch unsuccessfully when workPool throw an error 1`] = `"Unexpected Error"`; 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /test/__snapshots__/workerPool.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`workerPool should throw an error when worker.stdio is undefined 1`] = `"Failed to create the worker pool with workerId: 1 and configuration: {}. Please verify if you hit the OS open files limit."`; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | .eslintcache 6 | /coverage 7 | /dist 8 | /example/dist 9 | /example/node_modules 10 | /local 11 | /reports 12 | /node_modules 13 | .DS_Store 14 | Thumbs.db 15 | .idea 16 | .vscode 17 | *.sublime-project 18 | *.sublime-workspace 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | init: 5 | - git config --global core.autocrlf input 6 | environment: 7 | matrix: 8 | - nodejs_version: '8' 9 | webpack_version: latest 10 | job_part: test 11 | - nodejs_version: '6' 12 | webpack_version: latest 13 | job_part: test 14 | build: 'off' 15 | matrix: 16 | fast_finish: true 17 | install: 18 | - ps: Install-Product node $env:nodejs_version x64 19 | - npm i -g npm@latest 20 | - npm install 21 | before_test: 22 | - cmd: npm install webpack@%webpack_version% 23 | test_script: 24 | - node --version 25 | - npm --version 26 | - cmd: npm run appveyor:%job_part% 27 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "useBuiltIns": true, 7 | "targets": { 8 | "node": "6.9.0" 9 | }, 10 | "exclude": [ 11 | "transform-async-to-generator", 12 | "transform-regenerator" 13 | ] 14 | } 15 | ] 16 | ], 17 | "plugins": [ 18 | [ 19 | "transform-object-rest-spread", 20 | { 21 | "useBuiltIns": true 22 | } 23 | ] 24 | ], 25 | "env": { 26 | "test": { 27 | "presets": [ 28 | "env" 29 | ], 30 | "plugins": [ 31 | "transform-object-rest-spread" 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "useBuiltIns": true, 7 | "targets": { 8 | "node": "6.9.0" 9 | }, 10 | "exclude": [ 11 | "transform-async-to-generator", 12 | "transform-regenerator" 13 | ] 14 | } 15 | ] 16 | ], 17 | "plugins": [ 18 | [ 19 | "transform-object-rest-spread", 20 | { 21 | "useBuiltIns": true 22 | } 23 | ] 24 | ], 25 | "env": { 26 | "test": { 27 | "presets": [ 28 | "env" 29 | ], 30 | "plugins": [ 31 | "transform-object-rest-spread" 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WorkerError.js: -------------------------------------------------------------------------------- 1 | const stack = (err, worker, workerId) => { 2 | const originError = (err.stack || '') 3 | .split('\n') 4 | .filter(line => line.trim().startsWith('at')); 5 | 6 | const workerError = worker 7 | .split('\n') 8 | .filter(line => line.trim().startsWith('at')); 9 | 10 | const diff = workerError.slice(0, workerError.length - originError.length).join('\n'); 11 | 12 | originError.unshift(diff); 13 | originError.unshift(err.message); 14 | originError.unshift(`Thread Loader (Worker ${workerId})`); 15 | 16 | return originError.join('\n'); 17 | }; 18 | 19 | class WorkerError extends Error { 20 | constructor(err, workerId) { 21 | super(err); 22 | this.name = err.name; 23 | this.message = err.message; 24 | 25 | Error.captureStackTrace(this, this.constructor); 26 | 27 | this.stack = stack(err, this.stack, workerId); 28 | } 29 | } 30 | 31 | export default WorkerError; 32 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | // some file 3 | import './style.scss?00'; 4 | import './style.scss?01'; 5 | import './style.scss?02'; 6 | import './style.scss?03'; 7 | import './style.scss?04'; 8 | import './style.scss?05'; 9 | import './style.scss?06'; 10 | import './style.scss?07'; 11 | import './style.scss?08'; 12 | import './style.scss?09'; 13 | 14 | import './style.scss?10'; 15 | import './style.scss?11'; 16 | import './style.scss?12'; 17 | import './style.scss?13'; 18 | import './style.scss?14'; 19 | import './style.scss?15'; 20 | import './style.scss?16'; 21 | import './style.scss?17'; 22 | import './style.scss?18'; 23 | import './style.scss?19'; 24 | 25 | import './style.scss?20'; 26 | import './style.scss?21'; 27 | import './style.scss?22'; 28 | import './style.scss?23'; 29 | import './style.scss?24'; 30 | import './style.scss?25'; 31 | import './style.scss?26'; 32 | import './style.scss?27'; 33 | import './style.scss?28'; 34 | import './style.scss?29'; 35 | -------------------------------------------------------------------------------- /src/readBuffer.js: -------------------------------------------------------------------------------- 1 | export default function readBuffer(pipe, length, callback) { 2 | if (length === 0) { 3 | callback(null, Buffer.alloc(0)); 4 | return; 5 | } 6 | 7 | let remainingLength = length; 8 | const buffers = []; 9 | 10 | const readChunk = () => { 11 | const onChunk = (arg) => { 12 | let chunk = arg; 13 | let overflow; 14 | if (chunk.length > remainingLength) { 15 | overflow = chunk.slice(remainingLength); 16 | chunk = chunk.slice(0, remainingLength); 17 | remainingLength = 0; 18 | } else { 19 | remainingLength -= chunk.length; 20 | } 21 | buffers.push(chunk); 22 | if (remainingLength === 0) { 23 | pipe.removeListener('data', onChunk); 24 | pipe.pause(); 25 | 26 | if (overflow) { 27 | pipe.unshift(overflow); 28 | } 29 | 30 | callback(null, Buffer.concat(buffers, length)); 31 | } 32 | }; 33 | 34 | pipe.on('data', onChunk); 35 | pipe.resume(); 36 | }; 37 | readChunk(); 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/workerPools.js: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import WorkerPool from './WorkerPool'; 3 | 4 | const workerPools = Object.create(null); 5 | 6 | function calculateNumberOfWorkers() { 7 | // There are situations when this call will return undefined so 8 | // we are fallback here to 1. 9 | // More info on: https://github.com/nodejs/node/issues/19022 10 | const cpus = os.cpus() || { length: 1 }; 11 | 12 | return Math.max(1, cpus.length - 1); 13 | } 14 | 15 | function getPool(options) { 16 | const workerPoolOptions = { 17 | name: options.name || '', 18 | numberOfWorkers: options.workers || calculateNumberOfWorkers(), 19 | workerNodeArgs: options.workerNodeArgs, 20 | workerParallelJobs: options.workerParallelJobs || 20, 21 | poolTimeout: options.poolTimeout || 500, 22 | poolParallelJobs: options.poolParallelJobs || 200, 23 | poolRespawn: options.poolRespawn || false, 24 | }; 25 | const tpKey = JSON.stringify(workerPoolOptions); 26 | workerPools[tpKey] = workerPools[tpKey] || new WorkerPool(workerPoolOptions); 27 | const workerPool = workerPools[tpKey]; 28 | return workerPool; 29 | } 30 | 31 | export { getPool }; // eslint-disable-line import/prefer-default-export 32 | -------------------------------------------------------------------------------- /test/pitch.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { pitch } from '../src/cjs'; 3 | import { getPool } from '../src/workerPools'; 4 | 5 | jest.mock('../src/workerPools', () => { 6 | return { 7 | getPool: jest.fn(), 8 | }; 9 | }); 10 | 11 | const runGetPoolMock = (error) => { 12 | getPool.mockImplementationOnce(() => { 13 | return { 14 | isAbleToRun: () => true, 15 | run: jest.fn((opts, cb) => { 16 | cb(error, { 17 | fileDependencies: [], 18 | contextDependencies: [], 19 | result: {}, 20 | }); 21 | }), 22 | }; 23 | }); 24 | }; 25 | 26 | const runPitch = options => pitch.call( 27 | Object.assign( 28 | {}, 29 | { 30 | query: options, 31 | loaders: [], 32 | rootContext: path.resolve('../'), 33 | async: () => (error) => { 34 | if (error) { 35 | throw error; 36 | } 37 | }, 38 | }, 39 | ), 40 | ); 41 | 42 | it('runs pitch successfully when workPool not throw an error', () => { 43 | runGetPoolMock(null); 44 | expect(() => runPitch({})).not.toThrow(); 45 | }); 46 | 47 | it('runs pitch unsuccessfully when workPool throw an error', () => { 48 | runGetPoolMock(new Error('Unexpected Error')); 49 | expect(() => runPitch({})).toThrowErrorMatchingSnapshot(); 50 | }); 51 | -------------------------------------------------------------------------------- /test/readBuffer.test.js: -------------------------------------------------------------------------------- 1 | const stream = require('stream'); 2 | const readBuffer = require('../dist/readBuffer'); 3 | 4 | test('data is read', (done) => { 5 | expect.assertions(3); 6 | let eventCount = 0; 7 | function read() { 8 | eventCount += 1; 9 | if (eventCount <= 8) { 10 | return this.push(Buffer.from(eventCount.toString())); 11 | } 12 | return this.push(null); 13 | } 14 | const mockEventStream = new stream.Readable({ 15 | objectMode: true, 16 | read, 17 | }); 18 | function cb(err, data) { 19 | expect(err).toBe(null); 20 | expect(data.length).toBe(8); 21 | expect(String.fromCharCode(data[0])).toBe('1'); 22 | done(); 23 | } 24 | readBuffer.default(mockEventStream, 8, cb); 25 | }); 26 | 27 | test('no data is read when early quit but no error is thrown', (done) => { 28 | expect.assertions(1); 29 | let eventCount = 0; 30 | function read() { 31 | eventCount += 1; 32 | if (eventCount <= 5) { 33 | return this.push(Buffer.from(eventCount.toString())); 34 | } 35 | return this.push(null); 36 | } 37 | const mockEventStream = new stream.Readable({ 38 | objectMode: true, 39 | read, 40 | }); 41 | 42 | const cb = jest.fn(); 43 | readBuffer.default(mockEventStream, 8, cb); 44 | 45 | expect(cb).not.toHaveBeenCalled(); 46 | done(); 47 | }); 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import loaderUtils from 'loader-utils'; 2 | import { getPool } from './workerPools'; 3 | 4 | function pitch() { 5 | const options = loaderUtils.getOptions(this) || {}; 6 | const workerPool = getPool(options); 7 | if (!workerPool.isAbleToRun()) { 8 | return; 9 | } 10 | const callback = this.async(); 11 | workerPool.run({ 12 | loaders: this.loaders.slice(this.loaderIndex + 1).map((l) => { 13 | return { 14 | loader: l.path, 15 | options: l.options, 16 | ident: l.ident, 17 | }; 18 | }), 19 | resource: this.resourcePath + (this.resourceQuery || ''), 20 | sourceMap: this.sourceMap, 21 | emitError: this.emitError, 22 | emitWarning: this.emitWarning, 23 | resolve: this.resolve, 24 | target: this.target, 25 | minimize: this.minimize, 26 | resourceQuery: this.resourceQuery, 27 | optionsContext: this.rootContext || this.options.context, 28 | }, (err, r) => { 29 | if (r) { 30 | r.fileDependencies.forEach(d => this.addDependency(d)); 31 | r.contextDependencies.forEach(d => this.addContextDependency(d)); 32 | } 33 | if (err) { 34 | callback(err); 35 | return; 36 | } 37 | callback(null, ...r.result); 38 | }); 39 | } 40 | 41 | function warmup(options, requires) { 42 | const workerPool = getPool(options); 43 | workerPool.warmup(requires); 44 | } 45 | 46 | export { pitch, warmup }; // eslint-disable-line import/prefer-default-export 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | language: node_js 4 | branches: 5 | only: 6 | - master 7 | jobs: 8 | fast_finish: true 9 | allow_failures: 10 | - env: WEBPACK_VERSION=canary 11 | include: 12 | - &test-latest 13 | stage: Webpack latest 14 | node_js: 6 15 | env: WEBPACK_VERSION=latest JOB_PART=test 16 | script: npm run travis:$JOB_PART 17 | - <<: *test-latest 18 | node_js: 8 19 | env: WEBPACK_VERSION=latest JOB_PART=lint 20 | script: npm run travis:$JOB_PART 21 | - <<: *test-latest 22 | node_js: 10 23 | env: WEBPACK_VERSION=latest JOB_PART=coverage 24 | script: npm run travis:$JOB_PART 25 | after_success: 'bash <(curl -s https://codecov.io/bash)' 26 | - <<: *test-latest 27 | node_js: 10 28 | env: WEBPACK_VERSION=latest JOB_PART=test 29 | script: npm run travis:$JOB_PART 30 | - stage: Webpack canary 31 | before_script: npm i --no-save git://github.com/webpack/webpack.git#master 32 | script: npm run travis:$JOB_PART 33 | node_js: 6 34 | env: WEBPACK_VERSION=canary JOB_PART=test 35 | before_install: 36 | - 'if [[ `npm -v` != 5* ]]; then npm i -g npm@^5.0.0; fi' 37 | - nvm --version 38 | - node --version 39 | - npm --version 40 | before_script: 41 | - |- 42 | if [ "$WEBPACK_VERSION" ]; then 43 | npm i --no-save webpack@$WEBPACK_VERSION 44 | fi 45 | script: 46 | - 'npm run travis:$JOB_PART' 47 | after_success: 48 | - 'bash <(curl -s https://codecov.io/bash)' 49 | -------------------------------------------------------------------------------- /test/workerPool.test.js: -------------------------------------------------------------------------------- 1 | import childProcess from 'child_process'; 2 | import stream from 'stream'; 3 | import WorkerPool from '../src/WorkerPool'; 4 | 5 | jest.mock('child_process', () => { 6 | return { 7 | spawn: jest.fn(() => { 8 | return { 9 | unref: jest.fn(), 10 | }; 11 | }), 12 | }; 13 | }); 14 | 15 | describe('workerPool', () => { 16 | it('should throw an error when worker.stdio is undefined', () => { 17 | const workerPool = new WorkerPool({}); 18 | expect(() => workerPool.createWorker()).toThrowErrorMatchingSnapshot(); 19 | expect(() => workerPool.createWorker()).toThrowError('Please verify if you hit the OS open files limit'); 20 | }); 21 | 22 | it('should not throw an error when worker.stdio is defined', () => { 23 | childProcess.spawn.mockImplementationOnce(() => { 24 | return { 25 | stdio: new Array(5).fill(new stream.PassThrough()), 26 | unref: jest.fn(), 27 | }; 28 | }); 29 | 30 | const workerPool = new WorkerPool({}); 31 | expect(() => workerPool.createWorker()).not.toThrow(); 32 | }); 33 | 34 | it('should be able to run if the worker pool was not terminated', () => { 35 | const workerPool = new WorkerPool({}); 36 | expect(workerPool.isAbleToRun()).toBe(true); 37 | }); 38 | 39 | it('should not be able to run if the worker pool was terminated', () => { 40 | const workerPool = new WorkerPool({}); 41 | workerPool.terminate(); 42 | expect(workerPool.isAbleToRun()).toBe(false); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // eslint-disable-line import/no-extraneous-dependencies 3 | const threadLoader = require('../src'); // eslint-disable-line import/no-extraneous-dependencies 4 | 5 | module.exports = (env) => { 6 | const workerPool = { 7 | workers: +env.threads, 8 | poolTimeout: env.watch ? Infinity : 2000, 9 | }; 10 | const workerPoolSass = { 11 | workers: +env.threads, 12 | workerParallelJobs: 2, 13 | poolTimeout: env.watch ? Infinity : 2000, 14 | }; 15 | if (+env.threads > 0) { 16 | threadLoader.warmup(workerPool, ['babel-loader', 'babel-preset-env']); 17 | threadLoader.warmup(workerPoolSass, ['sass-loader', 'css-loader']); 18 | } 19 | return { 20 | mode: 'none', 21 | context: __dirname, 22 | entry: ['react', 'lodash-es', './index.js'], 23 | output: { 24 | path: path.resolve('dist'), 25 | filename: 'bundle.js', 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | test: /\.js$/, 31 | use: [ 32 | env.threads !== 0 && { 33 | loader: path.resolve(__dirname, '../src/index.js'), 34 | options: workerPool, 35 | }, 36 | 'babel-loader', 37 | ].filter(Boolean), 38 | }, 39 | { 40 | test: /\.scss$/, 41 | use: [ 42 | MiniCssExtractPlugin.loader, 43 | env.threads !== 0 && { 44 | loader: path.resolve(__dirname, '../src/index.js'), 45 | options: workerPoolSass, 46 | }, 47 | 'css-loader', 48 | 'sass-loader', 49 | ].filter(Boolean), 50 | }, 51 | ], 52 | }, 53 | plugins: [ 54 | new MiniCssExtractPlugin({ 55 | filename: 'style.css', 56 | }), 57 | ], 58 | stats: { 59 | children: false, 60 | }, 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "js-tokens": { 6 | "version": "4.0.0", 7 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 8 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 9 | }, 10 | "lodash-es": { 11 | "version": "4.17.11", 12 | "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", 13 | "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" 14 | }, 15 | "loose-envify": { 16 | "version": "1.4.0", 17 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 18 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 19 | "requires": { 20 | "js-tokens": "^3.0.0 || ^4.0.0" 21 | } 22 | }, 23 | "object-assign": { 24 | "version": "4.1.1", 25 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 26 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 27 | }, 28 | "react": { 29 | "version": "16.6.3", 30 | "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz", 31 | "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==", 32 | "requires": { 33 | "loose-envify": "^1.1.0", 34 | "object-assign": "^4.1.1", 35 | "prop-types": "^15.6.2", 36 | "scheduler": "^0.11.2" 37 | }, 38 | "dependencies": { 39 | "prop-types": { 40 | "version": "15.6.2", 41 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", 42 | "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", 43 | "requires": { 44 | "loose-envify": "^1.3.1", 45 | "object-assign": "^4.1.1" 46 | } 47 | }, 48 | "scheduler": { 49 | "version": "0.11.3", 50 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.3.tgz", 51 | "integrity": "sha512-i9X9VRRVZDd3xZw10NY5Z2cVMbdYg6gqFecfj79USv1CFN+YrJ3gIPRKf1qlY+Sxly4djoKdfx1T+m9dnRB8kQ==", 52 | "requires": { 53 | "loose-envify": "^1.1.0", 54 | "object-assign": "^4.1.1" 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thread-loader", 3 | "version": "2.1.2", 4 | "description": "Runs the following loaders in a worker pool", 5 | "author": "Tobias Koppers @sokra", 6 | "license": "MIT", 7 | "main": "dist/cjs.js", 8 | "files": [ 9 | "dist" 10 | ], 11 | "scripts": { 12 | "start": "npm run build -- -w", 13 | "clean": "del-cli dist", 14 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/**/*.test.js'", 15 | "lint": "eslint --cache src test", 16 | "lint-staged": "lint-staged", 17 | "prebuild": "npm run clean", 18 | "prepare": "npm run build", 19 | "release": "standard-version", 20 | "security": "npm audit", 21 | "test": "jest", 22 | "test:watch": "jest --watch", 23 | "test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage", 24 | "travis:coverage": "npm run test:coverage -- --runInBand", 25 | "travis:lint": "npm run lint && npm run security", 26 | "travis:test": "npm run test -- --runInBand", 27 | "appveyor:test": "npm run test", 28 | "webpack-defaults": "webpack-defaults" 29 | }, 30 | "dependencies": { 31 | "neo-async": "^2.6.0", 32 | "loader-runner": "^2.3.1", 33 | "loader-utils": "^1.1.0" 34 | }, 35 | "devDependencies": { 36 | "babel-cli": "^6.26.0", 37 | "babel-core": "^6.26.3", 38 | "babel-jest": "^23.6.0", 39 | "babel-loader": "^7.1.5", 40 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 41 | "babel-polyfill": "^6.26.0", 42 | "babel-preset-env": "^1.7.0", 43 | "cross-env": "^5.2.0", 44 | "css-loader": "^1.0.1", 45 | "del-cli": "^1.1.0", 46 | "eslint": "^5.10.0", 47 | "eslint-config-webpack": "^1.2.5", 48 | "eslint-plugin-import": "^2.14.0", 49 | "jest": "^23.6.0", 50 | "lint-staged": "^8.1.0", 51 | "lodash": "^4.17.11", 52 | "mini-css-extract-plugin": "^0.5.0", 53 | "nodemon": "^1.18.8", 54 | "node-sass": "^4.11.0", 55 | "pre-commit": "^1.2.2", 56 | "sass-loader": "^7.1.0", 57 | "standard-version": "^4.4.0", 58 | "webpack": "^4.27.1", 59 | "webpack-cli": "^3.1.2", 60 | "webpack-defaults": "^1.6.0" 61 | }, 62 | "engines": { 63 | "node": ">= 6.9.0 <7.0.0 || >= 8.9.0" 64 | }, 65 | "peerDependencies": { 66 | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" 67 | }, 68 | "repository": "https://github.com/webpack-contrib/thread-loader.git", 69 | "bugs": "https://github.com/webpack-contrib/thread-loader/issues", 70 | "homepage": "https://github.com/webpack-contrib/thread-loader", 71 | "pre-commit": "lint-staged", 72 | "lint-staged": { 73 | "*.js": [ 74 | "eslint --fix", 75 | "git add" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm][npm]][npm-url] 2 | [![deps][deps]][deps-url] 3 | [![test][test]][test-url] 4 | [![coverage][cover]][cover-url] 5 | [![chat][chat]][chat-url] 6 | 7 |
14 | 15 |
127 |
128 |
129 |
130 | sokra
131 |
132 | |
133 |