UNPKG

astx

Version:

super powerful structural search and replace for JavaScript and TypeScript to automate your refactoring

875 lines (761 loc) 28.8 kB
'use strict' 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