mirror of https://github.com/status-im/metro.git
[react-packger] Add a timeout on transform jobs
Summary: There's been a case where Babel can hang indefinitely on a file parse/transform. Possibly related to https://github.com/babel/babel/issues/2211 This adds a timeout to transform jobs and throws an error informing the user of the offending file. The timeout interval defaults to 10 seconds, but can be changed via an option.
This commit is contained in:
parent
c5b2dcaf9d
commit
54a8fe9156
|
@ -71,7 +71,11 @@ const validateOpts = declareOpts({
|
||||||
assetServer: {
|
assetServer: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Bundler {
|
class Bundler {
|
||||||
|
|
|
@ -17,6 +17,14 @@ const workerFarm = require('worker-farm');
|
||||||
|
|
||||||
const readFile = Promise.denodeify(fs.readFile);
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
|
// Avoid memory leaks caused in workers. This number seems to be a good enough number
|
||||||
|
// to avoid any memory leak while not slowing down initial builds.
|
||||||
|
// TODO(amasad): Once we get bundle splitting, we can drive this down a bit more.
|
||||||
|
const MAX_CALLS_PER_WORKER = 600;
|
||||||
|
|
||||||
|
// Worker will timeout if one of the callers timeout.
|
||||||
|
const DEFAULT_MAX_CALL_TIME = 10000;
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
@ -37,16 +45,15 @@ const validateOpts = declareOpts({
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
default: DEFAULT_MAX_CALL_TIME,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Avoid memory leaks caused in workers. This number seems to be a good enough number
|
|
||||||
// to avoid any memory leak while not slowing down initial builds.
|
|
||||||
// TODO(amasad): Once we get bundle splitting, we can drive this down a bit more.
|
|
||||||
const MAX_CALLS_PER_WORKER = 600;
|
|
||||||
|
|
||||||
class Transformer {
|
class Transformer {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
const opts = validateOpts(options);
|
const opts = this._opts = validateOpts(options);
|
||||||
|
|
||||||
this._cache = opts.cache;
|
this._cache = opts.cache;
|
||||||
|
|
||||||
|
@ -55,6 +62,7 @@ class Transformer {
|
||||||
autoStart: true,
|
autoStart: true,
|
||||||
maxConcurrentCallsPerWorker: 1,
|
maxConcurrentCallsPerWorker: 1,
|
||||||
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
||||||
|
maxCallTime: opts.transformTimeoutInterval,
|
||||||
}, opts.transformModulePath);
|
}, opts.transformModulePath);
|
||||||
|
|
||||||
this._transform = Promise.denodeify(this._workers);
|
this._transform = Promise.denodeify(this._workers);
|
||||||
|
@ -74,21 +82,21 @@ class Transformer {
|
||||||
return Promise.reject(new Error('No transfrom module'));
|
return Promise.reject(new Error('No transfrom module'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var transform = this._transform;
|
return this._cache.get(
|
||||||
return this._cache.get(filePath, 'transformedSource', function() {
|
filePath,
|
||||||
|
'transformedSource',
|
||||||
// TODO: use fastfs to avoid reading file from disk again
|
// TODO: use fastfs to avoid reading file from disk again
|
||||||
return readFile(filePath)
|
() => readFile(filePath).then(
|
||||||
.then(function(buffer) {
|
buffer => {
|
||||||
var sourceCode = buffer.toString();
|
const sourceCode = buffer.toString('utf8');
|
||||||
|
|
||||||
return transform({
|
return this._transform({
|
||||||
sourceCode: sourceCode,
|
sourceCode,
|
||||||
filename: filePath,
|
filename: filePath,
|
||||||
}).then(
|
}).then(res => {
|
||||||
function(res) {
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Error property on the result value form the transformer',
|
'Error property on the result value from the transformer',
|
||||||
'module is deprecated and will be removed in future versions.',
|
'module is deprecated and will be removed in future versions.',
|
||||||
'Please pass an error object as the first argument to the callback'
|
'Please pass an error object as the first argument to the callback'
|
||||||
);
|
);
|
||||||
|
@ -101,15 +109,25 @@ class Transformer {
|
||||||
sourcePath: filePath,
|
sourcePath: filePath,
|
||||||
sourceCode: sourceCode,
|
sourceCode: sourceCode,
|
||||||
});
|
});
|
||||||
}
|
}).catch(err => {
|
||||||
);
|
if (err.type === 'TimeoutError') {
|
||||||
}).catch(function(err) {
|
const timeoutErr = new Error(
|
||||||
throw formatError(err, filePath);
|
`TimeoutError: transforming ${filePath} took longer than `
|
||||||
});
|
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
|
||||||
});
|
`You can adjust timeout via the 'transformTimeoutInterval' option`
|
||||||
|
);
|
||||||
|
timeoutErr.type = 'TimeoutError';
|
||||||
|
throw timeoutErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw formatError(err, filePath);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = Transformer;
|
module.exports = Transformer;
|
||||||
|
|
||||||
Transformer.TransformError = TransformError;
|
Transformer.TransformError = TransformError;
|
||||||
|
|
|
@ -61,6 +61,10 @@ const validateOpts = declareOpts({
|
||||||
type: 'array',
|
type: 'array',
|
||||||
default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
|
default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
|
||||||
},
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const bundleOpts = declareOpts({
|
const bundleOpts = declareOpts({
|
||||||
|
|
Loading…
Reference in New Issue