astx
Version:
super powerful structural search and replace for JavaScript and TypeScript to automate your refactoring
875 lines (761 loc) • 28.8 kB
JavaScript
var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault')
var _typeof = require('@babel/runtime/helpers/typeof')
Object.defineProperty(exports, '__esModule', {
value: true,
})
exports['default'] = void 0
var _taggedTemplateLiteral2 = _interopRequireDefault(
require('@babel/runtime/helpers/taggedTemplateLiteral')
)
var _regenerator = _interopRequireDefault(require('@babel/runtime/regenerator'))
var _asyncToGenerator2 = _interopRequireDefault(
require('@babel/runtime/helpers/asyncToGenerator')
)
var _path = _interopRequireDefault(require('path'))
var _chalk = _interopRequireDefault(require('chalk'))
var _formatDiff = _interopRequireDefault(require('../util/formatDiff'))
var _lodash = require('lodash')
var _inquirer = _interopRequireDefault(require('inquirer'))
var _fsExtra = _interopRequireDefault(require('fs-extra'))
var _dedentJs = _interopRequireDefault(require('dedent-js'))
var _CodeFrameError = _interopRequireDefault(require('../util/CodeFrameError'))
var _formatMatches = require('../util/formatMatches')
var _node = require('../node')
var _ipc = require('../node/ipc')
var _ansiEscapes = _interopRequireDefault(require('ansi-escapes'))
var _spinner = require('./spinner')
require('../node/registerTsNode')
var _isInteractive = _interopRequireDefault(require('../util/isInteractive'))
var _templateObject, _templateObject2
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== 'function') return null
var cacheBabelInterop = new WeakMap()
var cacheNodeInterop = new WeakMap()
return (_getRequireWildcardCache = function _getRequireWildcardCache(
nodeInterop
) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop
})(nodeInterop)
}
function _interopRequireWildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj
}
if (
obj === null ||
(_typeof(obj) !== 'object' && typeof obj !== 'function')
) {
return {
default: obj,
}
}
var cache = _getRequireWildcardCache(nodeInterop)
if (cache && cache.has(obj)) {
return cache.get(obj)
}
var newObj = {}
var hasPropertyDescriptor =
Object.defineProperty && Object.getOwnPropertyDescriptor
for (var key in obj) {
if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor
? Object.getOwnPropertyDescriptor(obj, key)
: null
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc)
} else {
newObj[key] = obj[key]
}
}
}
newObj['default'] = obj
if (cache) {
cache.set(obj, newObj)
}
return newObj
}
function _asyncIterator(iterable) {
var method,
async,
sync,
retry = 2
for (
'undefined' != typeof Symbol &&
((async = Symbol.asyncIterator), (sync = Symbol.iterator));
retry--;
) {
if (async && null != (method = iterable[async]))
return method.call(iterable)
if (sync && null != (method = iterable[sync]))
return new AsyncFromSyncIterator(method.call(iterable))
;(async = '@@asyncIterator'), (sync = '@@iterator')
}
throw new TypeError('Object is not async iterable')
}
function AsyncFromSyncIterator(s) {
function AsyncFromSyncIteratorContinuation(r) {
if (Object(r) !== r)
return Promise.reject(new TypeError(r + ' is not an object.'))
var done = r.done
return Promise.resolve(r.value).then(function (value) {
return {
value: value,
done: done,
}
})
}
return (
(AsyncFromSyncIterator = function AsyncFromSyncIterator(s) {
;(this.s = s), (this.n = s.next)
}),
(AsyncFromSyncIterator.prototype = {
s: null,
n: null,
next: function next() {
return AsyncFromSyncIteratorContinuation(
this.n.apply(this.s, arguments)
)
},
return: function _return(value) {
var ret = this.s['return']
return void 0 === ret
? Promise.resolve({
value: value,
done: !0,
})
: AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments))
},
throw: function _throw(value) {
var thr = this.s['return']
return void 0 === thr
? Promise.reject(value)
: AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments))
},
}),
new AsyncFromSyncIterator(s)
)
}
var transform = {
command: '$0 [filesAndDirectories..]',
describe: 'apply a transform to the given files and directories',
builder: function builder(yargs) {
return yargs
.positional('filesAndDirectories', {
type: 'string',
array: true,
})
.option('transform', {
alias: 't',
describe:
"path to the transform file. Can be either a local path or url. Defaults to ./astx.ts or ./astx.js if --find isn't given",
})
.options('parser', {
describe:
'parser to use (options: babel, babel/auto, recast/babel, recast/babel/auto)',
type: 'string',
})
.options('parserOptions', {
describe: 'options for parser',
type: 'string',
})
.option('find', {
alias: 'f',
describe: 'search pattern',
type: 'string',
})
.option('replace', {
alias: 'r',
describe: 'replace pattern',
type: 'string',
})
.option('yes', {
alias: 'y',
describe: "don't ask for confirmation before writing changes",
type: 'boolean',
})
.option('gitignore', {
type: 'boolean',
describe: 'ignore gitignored files',
default: true,
})
.option('workers', {
type: 'number',
describe: 'number of worker threads to use',
})
.option('debugConfig', {
type: 'boolean',
describe: 'print found config and location',
})
},
handler: (function () {
var _handler = (0, _asyncToGenerator2['default'])(
/*#__PURE__*/ _regenerator['default'].mark(function _callee2(argv) {
var _ref2, _argv$workers
var startTime,
configResult,
config,
paths,
_yield,
transform,
transformFile,
parser,
parserOptions,
gitignore,
results,
errorCount,
changedCount,
unchangedCount,
progress,
progressDisplayed,
clearProgress,
showProgress,
spinnerInterval,
interactive,
workers,
pool,
runTransformOptions,
_iteratorAbruptCompletion,
_didIteratorError,
_iteratorError,
_loop,
_iterator,
_step,
_ret,
apply,
file
return _regenerator['default'].wrap(
function _callee2$(_context2) {
while (1) {
switch ((_context2.prev = _context2.next)) {
case 0:
showProgress = function _showProgress() {
clearProgress()
progressDisplayed = true
var _progress = progress,
completed = _progress.completed,
total = _progress.total,
globDone = _progress.globDone
process.stderr.write(
_chalk['default'].magenta(
''
.concat((0, _spinner.spinner)(), ' Running... ')
.concat(completed, '/')
.concat(total)
.concat(
globDone && total
? ' ('.concat(
((completed * 100) / total).toFixed(1),
'%)'
)
: '',
' '
)
.concat(
((Date.now() - startTime) / 1000).toFixed(2),
's'
)
)
)
}
clearProgress = function _clearProgress() {
if (progressDisplayed) {
process.stderr.write(
_ansiEscapes['default'].cursorLeft +
_ansiEscapes['default'].eraseLine
)
progressDisplayed = false
}
}
startTime = Date.now()
_context2.next = 5
return _node.astxCosmiconfig.search()
case 5:
configResult = _context2.sent
if (argv.debugConfig) {
console.log(JSON.stringify(configResult, null, 2))
process.exit(0)
}
config =
configResult === null || configResult === void 0
? void 0
: configResult.config
paths = (argv.filesAndDirectories || []).filter(function (x) {
return typeof x === 'string'
})
_context2.next = 11
return (0, _asyncToGenerator2['default'])(
/*#__PURE__*/ _regenerator['default'].mark(
function _callee() {
var _transformFile,
getOpt,
find,
replace,
files,
_i,
_files,
_transformFile2
return _regenerator['default'].wrap(function _callee$(
_context
) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
if (!argv.transform) {
_context.next = 9
break
}
_transformFile = _path['default'].resolve(
argv.transform
)
_context.t0 = _transformFile
_context.next = 5
return Promise.resolve(
''.concat(_transformFile)
).then(function (s) {
return _interopRequireWildcard(require(s))
})
case 5:
_context.t1 = _context.sent
return _context.abrupt('return', {
transformFile: _context.t0,
transform: _context.t1,
})
case 9:
if (!argv.find) {
_context.next = 16
break
}
getOpt = function getOpt(regex) {
var index = process.argv.findIndex(function (
a
) {
return regex.test(a)
})
return index >= 0
? process.argv[index + 1]
: undefined
} // yargs Eats quotes, not cool...
find = getOpt(/^(-f|--find)$/)
replace = getOpt(/^(-r|--replace)$/)
return _context.abrupt('return', {
transform: {
find: find,
replace: replace,
},
})
case 16:
files = [
_path['default'].resolve('astx.ts'),
_path['default'].resolve('astx.js'),
]
;(_i = 0), (_files = files)
case 18:
if (!(_i < _files.length)) {
_context.next = 31
break
}
_transformFile2 = _files[_i]
_context.next = 22
return _fsExtra['default'].pathExists(
_transformFile2
)
case 22:
if (!_context.sent) {
_context.next = 28
break
}
_context.t2 = _transformFile2
_context.next = 26
return Promise.resolve(
''.concat(_transformFile2)
).then(function (s) {
return _interopRequireWildcard(require(s))
})
case 26:
_context.t3 = _context.sent
return _context.abrupt('return', {
transformFile: _context.t2,
transform: _context.t3,
})
case 28:
_i++
_context.next = 18
break
case 31:
throw new Error(
'missing transform file: '.concat(
files.join(' or ')
)
)
case 32:
case 'end':
return _context.stop()
}
}
},
_callee)
}
)
)()
case 11:
_yield = _context2.sent
transform = _yield.transform
transformFile = _yield.transformFile
;(parser = argv.parser),
(parserOptions = argv.parserOptions),
(gitignore = argv.gitignore)
results = {}
errorCount = 0
changedCount = 0
unchangedCount = 0
progress = {
type: 'progress',
completed: 0,
total: 0,
globDone: false,
}
progressDisplayed = false
interactive = (0, _isInteractive['default'])()
workers =
(_ref2 =
(_argv$workers = argv.workers) !== null &&
_argv$workers !== void 0
? _argv$workers
: process.env.ASTX_WORKERS
? parseInt(process.env.ASTX_WORKERS)
: undefined) !== null && _ref2 !== void 0
? _ref2
: config === null || config === void 0
? void 0
: config.workers
pool =
workers === 0
? null
: new _node.AstxWorkerPool({
capacity: workers,
})
_context2.prev = 24
if (interactive) {
spinnerInterval = setInterval(showProgress, 30)
}
runTransformOptions = {
gitignore: gitignore ? undefined : null,
transform: transform,
transformFile: transformFile,
paths: paths,
config: {
parser: parser,
parserOptions: parserOptions
? JSON.parse(parserOptions)
: undefined,
},
}
_iteratorAbruptCompletion = false
_didIteratorError = false
_context2.prev = 29
_loop = function _loop() {
var _event = _step.value
var event =
!pool && _event.type === 'result'
? {
type: 'result',
result: (0, _ipc.makeIpcTransformResult)(_event),
}
: _event
if (event.type === 'progress') {
progress = event
if (interactive) showProgress()
return 'continue'
}
clearProgress()
var _event$result = event.result,
file = _event$result.file,
source = _event$result.source,
transformed = _event$result.transformed,
reports = _event$result.reports,
matches = _event$result.matches,
_error = _event$result.error
var error = _error
? (0, _ipc.invertIpcError)(_error)
: undefined
var relpath = _path['default'].relative(process.cwd(), file)
var logHeader = (0, _lodash.once)(function (logFn) {
return logFn(
_chalk['default'].blue(
(0, _dedentJs['default'])(
_templateObject ||
(_templateObject = (0,
_taggedTemplateLiteral2['default'])([
'\n ',
'\n ',
'\n ',
'\n ',
])),
'='.repeat(relpath.length),
_chalk['default'].bold(relpath),
'='.repeat(relpath.length)
)
)
)
})
if (error) {
errorCount++
logHeader(console.error)
if (error instanceof _CodeFrameError['default']) {
console.error(
error.format({
highlightCode: true,
forceColor: true,
stack: true,
})
)
} else {
console.error(_chalk['default'].red(error.stack))
}
} else if (
source &&
transformed &&
source !== transformed
) {
changedCount++
results[file] = transformed
if (!argv.yes) {
logHeader(console.log)
console.log(
(0, _formatDiff['default'])(source, transformed)
)
}
} else if (
matches !== null &&
matches !== void 0 &&
matches.length &&
source &&
transform.find &&
!transform.replace &&
!transform.astx
) {
logHeader(console.log)
console.log(
(0, _formatMatches.formatIpcMatches)(source, matches)
)
} else {
unchangedCount++
}
if (
reports !== null &&
reports !== void 0 &&
reports.length &&
!transform.onReport
) {
logHeader(console.error)
console.error(
_chalk['default'].blue(
(0, _dedentJs['default'])(
_templateObject2 ||
(_templateObject2 = (0,
_taggedTemplateLiteral2['default'])([
'\n Reports\n -------\n ',
]))
)
)
)
reports === null || reports === void 0
? void 0
: reports.forEach(function (r) {
return console.error(r)
})
}
}
_iterator = _asyncIterator(
pool
? pool.runTransform(runTransformOptions)
: (0, _node.runTransform)(runTransformOptions)
)
case 32:
_context2.next = 34
return _iterator.next()
case 34:
if (
!(_iteratorAbruptCompletion = !(_step = _context2.sent)
.done)
) {
_context2.next = 41
break
}
_ret = _loop()
if (!(_ret === 'continue')) {
_context2.next = 38
break
}
return _context2.abrupt('continue', 38)
case 38:
_iteratorAbruptCompletion = false
_context2.next = 32
break
case 41:
_context2.next = 47
break
case 43:
_context2.prev = 43
_context2.t0 = _context2['catch'](29)
_didIteratorError = true
_iteratorError = _context2.t0
case 47:
_context2.prev = 47
_context2.prev = 48
if (
!(_iteratorAbruptCompletion && _iterator['return'] != null)
) {
_context2.next = 52
break
}
_context2.next = 52
return _iterator['return']()
case 52:
_context2.prev = 52
if (!_didIteratorError) {
_context2.next = 55
break
}
throw _iteratorError
case 55:
return _context2.finish(52)
case 56:
return _context2.finish(47)
case 57:
_context2.next = 63
break
case 59:
_context2.prev = 59
_context2.t1 = _context2['catch'](24)
console.error(
_chalk['default'].red(
_context2.t1 instanceof Error
? _context2.t1.stack
: String(_context2.t1)
)
)
process.exit(1)
case 63:
_context2.prev = 63
if (spinnerInterval != null) clearInterval(spinnerInterval)
clearProgress()
return _context2.finish(63)
case 67:
if (transform.replace || transform.astx) {
console.error(
_chalk['default'].yellow(
''
.concat(changedCount, ' file')
.concat(changedCount === 1 ? '' : 's', ' changed')
)
)
console.error(
_chalk['default'].green(
''
.concat(unchangedCount, ' file')
.concat(unchangedCount === 1 ? '' : 's', ' unchanged')
)
)
if (errorCount > 0) {
console.error(
_chalk['default'].red(
''
.concat(errorCount, ' file')
.concat(errorCount === 1 ? '' : 's', ' errored')
)
)
}
} else if (transform.find) {
console.error(
_chalk['default'].yellow(
'\n'
.concat(unchangedCount, ' file')
.concat(
changedCount === 1 ? '' : 's',
' had no matches'
)
)
)
}
if ((0, _lodash.isEmpty)(results)) {
_context2.next = 87
break
}
if (!argv.yes) {
_context2.next = 73
break
}
_context2.t2 = true
_context2.next = 76
break
case 73:
_context2.next = 75
return _inquirer['default'].prompt([
{
type: 'confirm',
name: 'apply',
message: 'Apply changes',
default: false,
},
])
case 75:
_context2.t2 = _context2.sent.apply
case 76:
apply = _context2.t2
if (!apply) {
_context2.next = 86
break
}
_context2.t3 = _regenerator['default'].keys(results)
case 79:
if ((_context2.t4 = _context2.t3()).done) {
_context2.next = 86
break
}
file = _context2.t4.value
_context2.next = 83
return _fsExtra['default'].writeFile(
file,
results[file],
'utf8'
)
case 83:
console.error('Wrote '.concat(file))
_context2.next = 79
break
case 86:
if (process.send)
process.send({
exit: 0,
})
case 87:
_context2.next = 89
return pool === null || pool === void 0 ? void 0 : pool.end()
case 89:
process.exit(0)
case 90:
case 'end':
return _context2.stop()
}
}
},
_callee2,
null,
[
[24, 59, 63, 67],
[29, 43, 47, 57],
[48, , 52, 56],
]
)
})
)
function handler(_x) {
return _handler.apply(this, arguments)
}
return handler
})(),
}
var _default = transform
exports['default'] = _default