| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- function functionArgsFilter(j, path) {
- return ['err', 'error'].includes(path.get('params').value[0].name)
- }
- function functionBodyProcessor(j, path) {
- // the error variable should be the first parameter to the function
- const errorVarName = path.get('params').value[0].name
- j(path)
- // look for if statements
- .find(j.IfStatement)
- .filter(path => {
- let hasReturnError = false
- j(path)
- // find returns inside the if statement where the error from
- // the args is explicitly returned
- .find(j.ReturnStatement)
- .forEach(
- path =>
- (hasReturnError =
- path.value.argument.arguments[0].name === errorVarName)
- )
- return hasReturnError
- })
- .forEach(path => {
- j(path)
- // within the selected if blocks find calls to logger
- .find(j.CallExpression, {
- callee: {
- object: { name: 'logger' }
- }
- })
- // handle logger.warn, logger.error and logger.err
- .filter(path =>
- ['warn', 'error', 'err'].includes(
- path.get('callee').get('property').value.name
- )
- )
- // replace the logger call with the constructed OError wrapper
- .replaceWith(path => {
- // extract the error message which is the second arg for logger
- const message =
- path.value.arguments.length >= 2
- ? path.value.arguments[1].value
- : 'Error'
- // create: err = new OError(...)
- return j.assignmentExpression(
- '=',
- // assign over the existing error var
- j.identifier(errorVarName),
- j.callExpression(
- j.memberExpression(
- // create: new OError
- j.newExpression(j.identifier('OError'), [
- // create: { ... } args for new OError()
- j.objectExpression([
- // set message property with original error message
- j.property(
- 'init',
- j.identifier('message'),
- j.literal(message)
- ),
- j.property(
- 'init',
- // set info property with object { info: {} }
- j.identifier('info'),
- j.objectExpression(
- // add properties from original logger info object to the
- // OError info object, filtering out the err object itself,
- // which is typically one of the args when doing intermediate
- // error logging
- // TODO: this can fail when the property name does not match
- // the variable name. e.g. { err: error } so need to check
- // both in the filter
- path
- .get('arguments')
- .value[0].properties.filter(
- property => property.key.name !== errorVarName
- )
- )
- )
- ])
- ]),
- // add: .withCause( ) to OError
- j.identifier('withCause')
- ),
- // add original error var as argument: .withCause(err)
- [j.identifier(errorVarName)]
- )
- )
- })
- })
- }
- export default function transformer(file, api) {
- const j = api.jscodeshift
- let source = file.source
- // apply transformer to declared functions
- source = j(source)
- .find(j.FunctionDeclaration)
- .filter(path => functionArgsFilter(j, path))
- .forEach(path => functionBodyProcessor(j, path))
- .toSource()
- // apply transformer to inline-functions
- source = j(source)
- .find(j.FunctionExpression)
- .filter(path => functionArgsFilter(j, path))
- .forEach(path => functionBodyProcessor(j, path))
- .toSource()
- // apply transformer to inline-arrow-functions
- source = j(source)
- .find(j.ArrowFunctionExpression)
- .filter(path => functionArgsFilter(j, path))
- .forEach(path => functionBodyProcessor(j, path))
- .toSource()
- // do a plain text search to see if OError is used but not imported
- if (source.includes('OError') && !source.includes('@overleaf/o-error')) {
- const root = j(source)
- // assume the first variable declaration is an import
- // TODO: this should check that there is actually a require/import here
- // but in most cases it will be
- const imports = root.find(j.VariableDeclaration)
- const importOError = "const OError = require('@overleaf/o-error')\n"
- // if there were imports insert into list, format can re-order
- if (imports.length) {
- j(imports.at(0).get()).insertAfter(importOError)
- }
- // otherwise insert at beginning
- else {
- root.get().node.program.body.unshift(importOError)
- }
- source = root.toSource()
- }
- return source
- }
|