astx
Version:
super powerful structural search and replace for JavaScript and TypeScript to automate your refactoring
365 lines (334 loc) • 36.5 kB
JavaScript
import path from 'path'
import chalk from 'chalk'
import formatDiff from '../util/formatDiff.mjs'
import { isEmpty, once } from 'lodash-es'
import inquirer from 'inquirer'
import fs from 'fs-extra'
import dedent from 'dedent-js'
import CodeFrameError from '../util/CodeFrameError.mjs'
import { formatIpcMatches } from '../util/formatMatches.mjs'
import {
AstxWorkerPool,
astxCosmiconfig,
runTransform,
} from '../node/index.mjs'
import { invertIpcError, makeIpcTransformResult } from '../node/ipc.mjs'
import ansiEscapes from 'ansi-escapes'
import { spinner } from './spinner.mjs'
import '../node/registerTsNode.mjs'
import isInteractive from '../util/isInteractive.mjs'
/* eslint-disable no-console */
const transform = {
command: '$0 [filesAndDirectories..]',
describe: 'apply a transform to the given files and directories',
builder: (yargs) =>
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: async (argv) => {
var _ref, _argv$workers
const startTime = Date.now()
const configResult = await astxCosmiconfig.search()
if (argv.debugConfig) {
console.log(JSON.stringify(configResult, null, 2))
process.exit(0)
}
const config =
configResult === null || configResult === void 0
? void 0
: configResult.config
const paths = (argv.filesAndDirectories || []).filter(
(x) => typeof x === 'string'
)
const { transform, transformFile } = await (async () => {
if (argv.transform) {
const transformFile = path.resolve(argv.transform)
return {
transformFile,
transform: await import(transformFile),
}
} else if (argv.find) {
const getOpt = (regex) => {
const index = process.argv.findIndex((a) => regex.test(a))
return index >= 0 ? process.argv[index + 1] : undefined
} // yargs Eats quotes, not cool...
const find = getOpt(/^(-f|--find)$/)
const replace = getOpt(/^(-r|--replace)$/)
return {
transform: {
find,
replace,
},
}
} else {
const files = [path.resolve('astx.ts'), path.resolve('astx.js')]
for (const transformFile of files) {
if (await fs.pathExists(transformFile)) {
return {
transformFile,
transform: await import(transformFile),
}
}
}
throw new Error(`missing transform file: ${files.join(' or ')}`)
}
})()
const { parser, parserOptions, gitignore } = argv
const results = {}
let errorCount = 0
let changedCount = 0
let unchangedCount = 0
let progress = {
type: 'progress',
completed: 0,
total: 0,
globDone: false,
}
let progressDisplayed = false
function clearProgress() {
if (progressDisplayed) {
process.stderr.write(ansiEscapes.cursorLeft + ansiEscapes.eraseLine)
progressDisplayed = false
}
}
function showProgress() {
clearProgress()
progressDisplayed = true
const { completed, total, globDone } = progress
process.stderr.write(
chalk.magenta(
`${spinner()} Running... ${completed}/${total}${
globDone && total
? ` (${((completed * 100) / total).toFixed(1)}%)`
: ''
} ${((Date.now() - startTime) / 1000).toFixed(2)}s`
)
)
}
let spinnerInterval
const interactive = isInteractive()
const workers =
(_ref =
(_argv$workers = argv.workers) !== null && _argv$workers !== void 0
? _argv$workers
: process.env.ASTX_WORKERS
? parseInt(process.env.ASTX_WORKERS)
: undefined) !== null && _ref !== void 0
? _ref
: config === null || config === void 0
? void 0
: config.workers
const pool =
workers === 0
? null
: new AstxWorkerPool({
capacity: workers,
})
try {
if (interactive) {
spinnerInterval = setInterval(showProgress, 30)
}
const runTransformOptions = {
gitignore: gitignore ? undefined : null,
transform,
transformFile,
paths,
config: {
parser: parser,
parserOptions: parserOptions ? JSON.parse(parserOptions) : undefined,
},
}
for await (const _event of pool
? pool.runTransform(runTransformOptions)
: runTransform(runTransformOptions)) {
const event =
!pool && _event.type === 'result'
? {
type: 'result',
result: makeIpcTransformResult(_event),
}
: _event
if (event.type === 'progress') {
progress = event
if (interactive) showProgress()
continue
}
clearProgress()
const {
file,
source,
transformed,
reports,
matches,
error: _error,
} = event.result
const error = _error ? invertIpcError(_error) : undefined
const relpath = path.relative(process.cwd(), file)
const logHeader = once((logFn) =>
logFn(
chalk.blue(dedent`
${'='.repeat(relpath.length)}
${chalk.bold(relpath)}
${'='.repeat(relpath.length)}
`)
)
)
if (error) {
errorCount++
logHeader(console.error)
if (error instanceof CodeFrameError) {
console.error(
error.format({
highlightCode: true,
forceColor: true,
stack: true,
})
)
} else {
console.error(chalk.red(error.stack))
}
} else if (source && transformed && source !== transformed) {
changedCount++
results[file] = transformed
if (!argv.yes) {
logHeader(console.log)
console.log(formatDiff(source, transformed))
}
} else if (
matches !== null &&
matches !== void 0 &&
matches.length &&
source &&
transform.find &&
!transform.replace &&
!transform.astx
) {
logHeader(console.log)
console.log(formatIpcMatches(source, matches))
} else {
unchangedCount++
}
if (
reports !== null &&
reports !== void 0 &&
reports.length &&
!transform.onReport
) {
logHeader(console.error)
console.error(
chalk.blue(dedent`
Reports
-------
`)
)
reports === null || reports === void 0
? void 0
: reports.forEach((r) => console.error(r))
}
}
} catch (error) {
console.error(
chalk.red(error instanceof Error ? error.stack : String(error))
)
process.exit(1)
} finally {
if (spinnerInterval != null) clearInterval(spinnerInterval)
clearProgress()
}
if (transform.replace || transform.astx) {
console.error(
chalk.yellow(
`${changedCount} file${changedCount === 1 ? '' : 's'} changed`
)
)
console.error(
chalk.green(
`${unchangedCount} file${unchangedCount === 1 ? '' : 's'} unchanged`
)
)
if (errorCount > 0) {
console.error(
chalk.red(`${errorCount} file${errorCount === 1 ? '' : 's'} errored`)
)
}
} else if (transform.find) {
console.error(
chalk.yellow(
`\n${unchangedCount} file${
changedCount === 1 ? '' : 's'
} had no matches`
)
)
}
if (!isEmpty(results)) {
const apply = argv.yes
? true
: (
await inquirer.prompt([
{
type: 'confirm',
name: 'apply',
message: 'Apply changes',
default: false,
},
])
).apply
if (apply) {
for (const file in results) {
await fs.writeFile(file, results[file], 'utf8')
console.error(`Wrote ${file}`)
}
}
if (process.send)
process.send({
exit: 0,
})
}
await (pool === null || pool === void 0 ? void 0 : pool.end())
process.exit(0)
},
}
export default transform //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJwYXRoIiwiY2hhbGsiLCJmb3JtYXREaWZmIiwiaXNFbXB0eSIsIm9uY2UiLCJpbnF1aXJlciIsImZzIiwiZGVkZW50IiwiQ29kZUZyYW1lRXJyb3IiLCJmb3JtYXRJcGNNYXRjaGVzIiwiQXN0eFdvcmtlclBvb2wiLCJhc3R4Q29zbWljb25maWciLCJydW5UcmFuc2Zvcm0iLCJpbnZlcnRJcGNFcnJvciIsIm1ha2VJcGNUcmFuc2Zvcm1SZXN1bHQiLCJhbnNpRXNjYXBlcyIsInNwaW5uZXIiLCJpc0ludGVyYWN0aXZlIiwidHJhbnNmb3JtIiwiY29tbWFuZCIsImRlc2NyaWJlIiwiYnVpbGRlciIsInlhcmdzIiwicG9zaXRpb25hbCIsInR5cGUiLCJhcnJheSIsIm9wdGlvbiIsImFsaWFzIiwib3B0aW9ucyIsImRlZmF1bHQiLCJoYW5kbGVyIiwiYXJndiIsInN0YXJ0VGltZSIsIkRhdGUiLCJub3ciLCJjb25maWdSZXN1bHQiLCJzZWFyY2giLCJkZWJ1Z0NvbmZpZyIsImNvbnNvbGUiLCJsb2ciLCJKU09OIiwic3RyaW5naWZ5IiwicHJvY2VzcyIsImV4aXQiLCJjb25maWciLCJwYXRocyIsImZpbGVzQW5kRGlyZWN0b3JpZXMiLCJmaWx0ZXIiLCJ4IiwidHJhbnNmb3JtRmlsZSIsInJlc29sdmUiLCJmaW5kIiwiZ2V0T3B0IiwicmVnZXgiLCJpbmRleCIsImZpbmRJbmRleCIsImEiLCJ0ZXN0IiwidW5kZWZpbmVkIiwicmVwbGFjZSIsImZpbGVzIiwicGF0aEV4aXN0cyIsIkVycm9yIiwiam9pbiIsInBhcnNlciIsInBhcnNlck9wdGlvbnMiLCJnaXRpZ25vcmUiLCJyZXN1bHRzIiwiZXJyb3JDb3VudCIsImNoYW5nZWRDb3VudCIsInVuY2hhbmdlZENvdW50IiwicHJvZ3Jlc3MiLCJjb21wbGV0ZWQiLCJ0b3RhbCIsImdsb2JEb25lIiwicHJvZ3Jlc3NEaXNwbGF5ZWQiLCJjbGVhclByb2dyZXNzIiwic3RkZXJyIiwid3JpdGUiLCJjdXJzb3JMZWZ0IiwiZXJhc2VMaW5lIiwic2hvd1Byb2dyZXNzIiwibWFnZW50YSIsInRvRml4ZWQiLCJzcGlubmVySW50ZXJ2YWwiLCJpbnRlcmFjdGl2ZSIsIndvcmtlcnMiLCJlbnYiLCJBU1RYX1dPUktFUlMiLCJwYXJzZUludCIsInBvb2wiLCJjYXBhY2l0eSIsInNldEludGVydmFsIiwicnVuVHJhbnNmb3JtT3B0aW9ucyIsInBhcnNlIiwiX2V2ZW50IiwiZXZlbnQiLCJyZXN1bHQiLCJmaWxlIiwic291cmNlIiwidHJhbnNmb3JtZWQiLCJyZXBvcnRzIiwibWF0Y2hlcyIsImVycm9yIiwiX2Vycm9yIiwicmVscGF0aCIsInJlbGF0aXZlIiwiY3dkIiwibG9nSGVhZGVyIiwibG9nRm4iLCJibHVlIiwicmVwZWF0IiwibGVuZ3RoIiwiYm9sZCIsImZvcm1hdCIsImhpZ2hsaWdodENvZGUiLCJmb3JjZUNvbG9yIiwic3RhY2siLCJyZWQiLCJ5ZXMiLCJhc3R4Iiwib25SZXBvcnQiLCJmb3JFYWNoIiwiciIsIlN0cmluZyIsImNsZWFySW50ZXJ2YWwiLCJ5ZWxsb3ciLCJncmVlbiIsImFwcGx5IiwicHJvbXB0IiwibmFtZSIsIm1lc3NhZ2UiLCJ3cml0ZUZpbGUiLCJzZW5kIiwiZW5kIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaS90cmFuc2Zvcm0udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQXJndW1lbnRzLCBBcmd2LCBDb21tYW5kTW9kdWxlIH0gZnJvbSAneWFyZ3MnXG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJ1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJ1xuaW1wb3J0IGZvcm1hdERpZmYgZnJvbSAnLi4vdXRpbC9mb3JtYXREaWZmJ1xuaW1wb3J0IHsgaXNFbXB0eSwgb25jZSB9IGZyb20gJ2xvZGFzaCdcbmltcG9ydCBpbnF1aXJlciBmcm9tICdpbnF1aXJlcidcbmltcG9ydCBmcyBmcm9tICdmcy1leHRyYSdcbmltcG9ydCBkZWRlbnQgZnJvbSAnZGVkZW50LWpzJ1xuaW1wb3J0IENvZGVGcmFtZUVycm9yIGZyb20gJy4uL3V0aWwvQ29kZUZyYW1lRXJyb3InXG5pbXBvcnQgeyBmb3JtYXRJcGNNYXRjaGVzIH0gZnJvbSAnLi4vdXRpbC9mb3JtYXRNYXRjaGVzJ1xuaW1wb3J0IHtcbiAgQXN0eFdvcmtlclBvb2wsXG4gIGFzdHhDb3NtaWNvbmZpZyxcbiAgcnVuVHJhbnNmb3JtLFxuICBQcm9ncmVzcyxcbn0gZnJvbSAnLi4vbm9kZSdcbmltcG9ydCB7XG4gIGludmVydElwY0Vycm9yLFxuICBJcGNUcmFuc2Zvcm1SZXN1bHQsXG4gIG1ha2VJcGNUcmFuc2Zvcm1SZXN1bHQsXG59IGZyb20gJy4uL25vZGUvaXBjJ1xuaW1wb3J0IHsgVHJhbnNmb3JtIH0gZnJvbSAnLi4vQXN0eCdcbmltcG9ydCBhbnNpRXNjYXBlcyBmcm9tICdhbnNpLWVzY2FwZXMnXG5pbXBvcnQgeyBzcGlubmVyIH0gZnJvbSAnLi9zcGlubmVyJ1xuaW1wb3J0ICcuLi9ub2RlL3JlZ2lzdGVyVHNOb2RlJ1xuaW1wb3J0IGlzSW50ZXJhY3RpdmUgZnJvbSAnLi4vdXRpbC9pc0ludGVyYWN0aXZlJ1xuXG4vKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5cbnR5cGUgT3B0aW9ucyA9IHtcbiAgdHJhbnNmb3JtPzogc3RyaW5nXG4gIHBhcnNlcj86IHN0cmluZ1xuICBwYXJzZXJPcHRpb25zPzogc3RyaW5nXG4gIGZpbmQ/OiBzdHJpbmdcbiAgcmVwbGFjZT86IHN0cmluZ1xuICBmaWxlc0FuZERpcmVjdG9yaWVzPzogc3RyaW5nW11cbiAgeWVzPzogYm9vbGVhblxuICBnaXRpZ25vcmU/OiBib29sZWFuXG4gIHRocmVhZHM/OiBudW1iZXJcbiAgZGVidWdDb25maWc/OiBib29sZWFuXG59XG5cbmNvbnN0IHRyYW5zZm9ybTogQ29tbWFuZE1vZHVsZTxPcHRpb25zPiA9IHtcbiAgY29tbWFuZDogJyQwIFtmaWxlc0FuZERpcmVjdG9yaWVzLi5dJyxcbiAgZGVzY3JpYmU6ICdhcHBseSBhIHRyYW5zZm9ybSB0byB0aGUgZ2l2ZW4gZmlsZXMgYW5kIGRpcmVjdG9yaWVzJyxcbiAgYnVpbGRlcjogKHlhcmdzOiBBcmd2PE9wdGlvbnM+KSA9PlxuICAgIHlhcmdzXG4gICAgICAucG9zaXRpb25hbCgnZmlsZXNBbmREaXJlY3RvcmllcycsIHtcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgIGFycmF5OiB0cnVlLFxuICAgICAgfSlcbiAgICAgIC5vcHRpb24oJ3RyYW5zZm9ybScsIHtcbiAgICAgICAgYWxpYXM6ICd0JyxcbiAgICAgICAgZGVzY3JpYmU6IGBwYXRoIHRvIHRoZSB0cmFuc2Zvcm0gZmlsZS4gQ2FuIGJlIGVpdGhlciBhIGxvY2FsIHBhdGggb3IgdXJsLiBEZWZhdWx0cyB0byAuL2FzdHgudHMgb3IgLi9hc3R4LmpzIGlmIC0tZmluZCBpc24ndCBnaXZlbmAsXG4gICAgICB9KVxuICAgICAgLm9wdGlvbnMoJ3BhcnNlcicsIHtcbiAgICAgICAgZGVzY3JpYmU6XG4gICAgICAgICAgJ3BhcnNlciB0byB1c2UgKG9wdGlvbnM6IGJhYmVsLCBiYWJlbC9hdXRvLCByZWNhc3QvYmFiZWwsIHJlY2FzdC9iYWJlbC9hdXRvKScsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgfSlcbiAgICAgIC5vcHRpb25zKCdwYXJzZXJPcHRpb25zJywge1xuICAgICAgICBkZXNjcmliZTogJ29wdGlvbnMgZm9yIHBhcnNlcicsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgfSlcbiAgICAgIC5vcHRpb24oJ2ZpbmQnLCB7XG4gICAgICAgIGFsaWFzOiAnZicsXG4gICAgICAgIGRlc2NyaWJlOiAnc2VhcmNoIHBhdHRlcm4nLFxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIH0pXG4gICAgICAub3B0aW9uKCdyZXBsYWNlJywge1xuICAgICAgICBhbGlhczogJ3InLFxuICAgICAgICBkZXNjcmliZTogJ3JlcGxhY2UgcGF0dGVybicsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgfSlcbiAgICAgIC5vcHRpb24oJ3llcycsIHtcbiAgICAgICAgYWxpYXM6ICd5JyxcbiAgICAgICAgZGVzY3JpYmU6IGBkb24ndCBhc2sgZm9yIGNvbmZpcm1hdGlvbiBiZWZvcmUgd3JpdGluZyBjaGFuZ2VzYCxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgfSlcbiAgICAgIC5vcHRpb24oJ2dpdGlnbm9yZScsIHtcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBkZXNjcmliZTogYGlnbm9yZSBnaXRpZ25vcmVkIGZpbGVzYCxcbiAgICAgICAgZGVmYXVsdDogdHJ1ZSxcbiAgICAgIH0pXG4gICAgICAub3B0aW9uKCd3b3JrZXJzJywge1xuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZGVzY3JpYmU6ICdudW1iZXIgb2Ygd29ya2VyIHRocmVhZHMgdG8gdXNlJyxcbiAgICAgIH0pXG4gICAgICAub3B0aW9uKCdkZWJ1Z0NvbmZpZycsIHtcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBkZXNjcmliZTogJ3ByaW50IGZvdW5kIGNvbmZpZyBhbmQgbG9jYXRpb24nLFxuICAgICAgfSksXG5cbiAgaGFuZGxlcjogYXN5bmMgKGFyZ3Y6IEFyZ3VtZW50czxPcHRpb25zPikgPT4ge1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KClcblxuICAgIGNvbnN0IGNvbmZpZ1Jlc3VsdCA9IGF3YWl0IGFzdHhDb3NtaWNvbmZpZy5zZWFyY2goKVxuICAgIGlmIChhcmd2LmRlYnVnQ29uZmlnKSB7XG4gICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShjb25maWdSZXN1bHQsIG51bGwsIDIpKVxuICAgICAgcHJvY2Vzcy5leGl0KDApXG4gICAgfVxuICAgIGNvbnN0IGNvbmZpZyA9IGNvbmZpZ1Jlc3VsdD8uY29uZmlnXG5cbiAgICBjb25zdCBwYXRocyA9IChhcmd2LmZpbGVzQW5kRGlyZWN0b3JpZXMgfHwgW10pLmZpbHRlcihcbiAgICAgICh4KSA9PiB0eXBlb2YgeCA9PT0gJ3N0cmluZydcbiAgICApIGFzIHN0cmluZ1tdXG5cbiAgICBjb25zdCB7IHRyYW5zZm9ybSwgdHJhbnNmb3JtRmlsZSB9ID0gYXdhaXQgKGFzeW5jICgpOiBQcm9taXNlPHtcbiAgICAgIHRyYW5zZm9ybTogVHJhbnNmb3JtXG4gICAgICB0cmFuc2Zvcm1GaWxlPzogc3RyaW5nXG4gICAgfT4gPT4ge1xuICAgICAgaWYgKGFyZ3YudHJhbnNmb3JtKSB7XG4gICAgICAgIGNvbnN0IHRyYW5zZm9ybUZpbGUgPSBwYXRoLnJlc29sdmUoYXJndi50cmFuc2Zvcm0pXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdHJhbnNmb3JtRmlsZSxcbiAgICAgICAgICB0cmFuc2Zvcm06IGF3YWl0IGltcG9ydCh0cmFuc2Zvcm1GaWxlKSxcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChhcmd2LmZpbmQpIHtcbiAgICAgICAgY29uc3QgZ2V0T3B0ID0gKHJlZ2V4OiBSZWdFeHApOiBzdHJpbmcgfCB1bmRlZmluZWQgPT4ge1xuICAgICAgICAgIGNvbnN0IGluZGV4ID0gcHJvY2Vzcy5hcmd2LmZpbmRJbmRleCgoYSkgPT4gcmVnZXgudGVzdChhKSlcbiAgICAgICAgICByZXR1cm4gaW5kZXggPj0gMCA/IHByb2Nlc3MuYXJndltpbmRleCArIDFdIDogdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICAgICAgLy8geWFyZ3MgRWF0cyBxdW90ZXMsIG5vdCBjb29sLi4uXG4gICAgICAgIGNvbnN0IGZpbmQgPSBnZXRPcHQoL14oLWZ8LS1maW5kKSQvKVxuICAgICAgICBjb25zdCByZXBsYWNlID0gZ2V0T3B0KC9eKC1yfC0tcmVwbGFjZSkkLylcbiAgICAgICAgcmV0dXJuIHsgdHJhbnNmb3JtOiB7IGZpbmQsIHJlcGxhY2UgfSB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBmaWxlcyA9IFtwYXRoLnJlc29sdmUoJ2FzdHgudHMnKSwgcGF0aC5yZXNvbHZlKCdhc3R4LmpzJyldXG4gICAgICAgIGZvciAoY29uc3QgdHJhbnNmb3JtRmlsZSBvZiBmaWxlcykge1xuICAgICAgICAgIGlmIChhd2FpdCBmcy5wYXRoRXhpc3RzKHRyYW5zZm9ybUZpbGUpKSB7XG4gICAgICAgICAgICByZXR1cm4geyB0cmFuc2Zvcm1GaWxlLCB0cmFuc2Zvcm06IGF3YWl0IGltcG9ydCh0cmFuc2Zvcm1GaWxlKSB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgbWlzc2luZyB0cmFuc2Zvcm0gZmlsZTogJHtmaWxlcy5qb2luKCcgb3IgJyl9YClcbiAgICAgIH1cbiAgICB9KSgpXG4gICAgY29uc3QgeyBwYXJzZXIsIHBhcnNlck9wdGlvbnMsIGdpdGlnbm9yZSB9ID0gYXJndlxuXG4gICAgY29uc3QgcmVzdWx0czogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9XG4gICAgbGV0IGVycm9yQ291bnQgPSAwXG4gICAgbGV0IGNoYW5nZWRDb3VudCA9IDBcbiAgICBsZXQgdW5jaGFuZ2VkQ291bnQgPSAwXG5cbiAgICBsZXQgcHJvZ3Jlc3M6IFByb2dyZXNzID0ge1xuICAgICAgdHlwZTogJ3Byb2dyZXNzJyxcbiAgICAgIGNvbXBsZXRlZDogMCxcbiAgICAgIHRvdGFsOiAwLFxuICAgICAgZ2xvYkRvbmU6IGZhbHNlLFxuICAgIH1cblxuICAgIGxldCBwcm9ncmVzc0Rpc3BsYXllZCA9IGZhbHNlXG4gICAgZnVuY3Rpb24gY2xlYXJQcm9ncmVzcygpIHtcbiAgICAgIGlmIChwcm9ncmVzc0Rpc3BsYXllZCkge1xuICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShhbnNpRXNjYXBlcy5jdXJzb3JMZWZ0ICsgYW5zaUVzY2FwZXMuZXJhc2VMaW5lKVxuICAgICAgICBwcm9ncmVzc0Rpc3BsYXllZCA9IGZhbHNlXG4gICAgICB9XG4gICAgfVxuICAgIGZ1bmN0aW9uIHNob3dQcm9ncmVzcygpIHtcbiAgICAgIGNsZWFyUHJvZ3Jlc3MoKVxuICAgICAgcHJvZ3Jlc3NEaXNwbGF5ZWQgPSB0cnVlXG4gICAgICBjb25zdCB7IGNvbXBsZXRlZCwgdG90YWwsIGdsb2JEb25lIH0gPSBwcm9ncmVzc1xuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgIGNoYWxrLm1hZ2VudGEoXG4gICAgICAgICAgYCR7c3Bpbm5lcigpfSBSdW5uaW5nLi4uICR7Y29tcGxldGVkfS8ke3RvdGFsfSR7XG4gICAgICAgICAgICBnbG9iRG9uZSAmJiB0b3RhbFxuICAgICAgICAgICAgICA/IGAgKCR7KChjb21wbGV0ZWQgKiAxMDApIC8gdG90YWwpLnRvRml4ZWQoMSl9JSlgXG4gICAgICAgICAgICAgIDogJydcbiAgICAgICAgICB9ICR7KChEYXRlLm5vdygpIC0gc3RhcnRUaW1lKSAvIDEwMDApLnRvRml4ZWQoMil9c2BcbiAgICAgICAgKVxuICAgICAgKVxuICAgIH1cbiAgICBsZXQgc3Bpbm5lckludGVydmFsXG5cbiAgICBjb25zdCBpbnRlcmFjdGl2ZSA9IGlzSW50ZXJhY3RpdmUoKVxuICAgIGNvbnN0IHdvcmtlcnMgPVxuICAgICAgYXJndi53b3JrZXJzID8/XG4gICAgICAocHJvY2Vzcy5lbnYuQVNUWF9XT1JLRVJTXG4gICAgICAgID8gcGFyc2VJbnQocHJvY2Vzcy5lbnYuQVNUWF9XT1JLRVJTKVxuICAgICAgICA6IHVuZGVmaW5lZCkgPz9cbiAgICAgIGNvbmZpZz8ud29ya2Vyc1xuICAgIGNvbnN0IHBvb2wgPVxuICAgICAgd29ya2VycyA9PT0gMCA/IG51bGwgOiBuZXcgQXN0eFdvcmtlclBvb2woeyBjYXBhY2l0eTogd29ya2VycyB9KVxuICAgIHRyeSB7XG4gICAgICBpZiAoaW50ZXJhY3RpdmUpIHtcbiAgICAgICAgc3Bpbm5lckludGVydmFsID0gc2V0SW50ZXJ2YWwoc2hvd1Byb2dyZXNzLCAzMClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJ1blRyYW5zZm9ybU9wdGlvbnMgPSB7XG4gICAgICAgIGdpdGlnbm9yZTogZ2l0aWdub3JlID8gdW5kZWZpbmVkIDogbnVsbCxcbiAgICAgICAgdHJhbnNmb3JtLFxuICAgICAgICB0cmFuc2Zvcm1GaWxlLFxuICAgICAgICBwYXRocyxcbiAgICAgICAgY29uZmlnOiB7XG4gICAgICAgICAgcGFyc2VyOiBwYXJzZXIgYXMgYW55LFxuICAgICAgICAgIHBhcnNlck9wdGlvbnM6IHBhcnNlck9wdGlvbnMgPyBKU09OLnBhcnNlKHBhcnNlck9wdGlvbnMpIDogdW5kZWZpbmVkLFxuICAgICAgICB9LFxuICAgICAgfVxuICAgICAgZm9yIGF3YWl0IChjb25zdCBfZXZlbnQgb2YgcG9vbFxuICAgICAgICA/IHBvb2wucnVuVHJhbnNmb3JtKHJ1blRyYW5zZm9ybU9wdGlvbnMpXG4gICAgICAgIDogcnVuVHJhbnNmb3JtKHJ1blRyYW5zZm9ybU9wdGlvbnMpKSB7XG4gICAgICAgIGNvbnN0IGV2ZW50OiB7IHR5cGU6ICdyZXN1bHQnOyByZXN1bHQ6IElwY1RyYW5zZm9ybVJlc3VsdCB9IHwgUHJvZ3Jlc3MgPVxuICAgICAgICAgICFwb29sICYmIF9ldmVudC50eXBlID09PSAncmVzdWx0J1xuICAgICAgICAgICAgPyB7IHR5cGU6ICdyZXN1bHQnLCByZXN1bHQ6IG1ha2VJcGNUcmFuc2Zvcm1SZXN1bHQoX2V2ZW50IGFzIGFueSkgfVxuICAgICAgICAgICAgOiAoX2V2ZW50IGFzIGFueSlcbiAgICAgICAgaWYgKGV2ZW50LnR5cGUgPT09ICdwcm9ncmVzcycpIHtcbiAgICAgICAgICBwcm9ncmVzcyA9IGV2ZW50XG4gICAgICAgICAgaWYgKGludGVyYWN0aXZlKSBzaG93UHJvZ3Jlc3MoKVxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgICAgY2xlYXJQcm9ncmVzcygpXG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICBmaWxlLFxuICAgICAgICAgIHNvdXJjZSxcbiAgICAgICAgICB0cmFuc2Zvcm1lZCxcbiAgICAgICAgICByZXBvcnRzLFxuICAgICAgICAgIG1hdGNoZXMsXG4gICAgICAgICAgZXJyb3I6IF9lcnJvcixcbiAgICAgICAgfSA9IGV2ZW50LnJlc3VsdFxuICAgICAgICBjb25zdCBlcnJvciA9IF9lcnJvciA/IGludmVydElwY0Vycm9yKF9lcnJvcikgOiB1bmRlZmluZWRcbiAgICAgICAgY29uc3QgcmVscGF0aCA9IHBhdGgucmVsYXRpdmUocHJvY2Vzcy5jd2QoKSwgZmlsZSlcbiAgICAgICAgY29uc3QgbG9nSGVhZGVyID0gb25jZSgobG9nRm46ICh2YWx1ZTogc3RyaW5nKSA9PiBhbnkpID0+XG4gICAgICAgICAgbG9nRm4oXG4gICAgICAgICAgICBjaGFsay5ibHVlKGRlZGVudGBcbiAgICAgICAgICAgICR7Jz0nLnJlcGVhdChyZWxwYXRoLmxlbmd0aCl9XG4gICAgICAgICAgICAke2NoYWxrLmJvbGQocmVscGF0aCl9XG4gICAgICAgICAgICAkeyc9Jy5yZXBlYXQocmVscGF0aC5sZW5ndGgpfVxuICAgICAgICAgIGApXG4gICAgICAgICAgKVxuICAgICAgICApXG5cbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgZXJyb3JDb3VudCsrXG4gICAgICAgICAgbG9nSGVhZGVyKGNvbnNvbGUuZXJyb3IpXG4gICAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgQ29kZUZyYW1lRXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICAgIGVycm9yLmZvcm1hdCh7XG4gICAgICAgICAgICAgICAgaGlnaGxpZ2h0Q29kZTogdHJ1ZSxcbiAgICAgICAgICAgICAgICBmb3JjZUNvbG9yOiB0cnVlLFxuICAgICAgICAgICAgICAgIHN0YWNrOiB0cnVlLFxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZChlcnJvci5zdGFjaykpXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHNvdXJjZSAmJiB0cmFuc2Zvcm1lZCAmJiBzb3VyY2UgIT09IHRyYW5zZm9ybWVkKSB7XG4gICAgICAgICAgY2hhbmdlZENvdW50KytcbiAgICAgICAgICByZXN1bHRzW2ZpbGVdID0gdHJhbnNmb3JtZWRcbiAgICAgICAgICBpZiAoIWFyZ3YueWVzKSB7XG4gICAgICAgICAgICBsb2dIZWFkZXIoY29uc29sZS5sb2cpXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhmb3JtYXREaWZmKHNvdXJjZSwgdHJhbnNmb3JtZWQpKVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICBtYXRjaGVzPy5sZW5ndGggJiZcbiAgICAgICAgICBzb3VyY2UgJiZcbiAgICAgICAgICB0cmFuc2Zvcm0uZmluZCAmJlxuICAgICAgICAgICF0cmFuc2Zvcm0ucmVwbGFjZSAmJlxuICAgICAgICAgICF0cmFuc2Zvcm0uYXN0eFxuICAgICAgICApIHtcbiAgICAgICAgICBsb2dIZWFkZXIoY29uc29sZS5sb2cpXG4gICAgICAgICAgY29uc29sZS5sb2coZm9ybWF0SXBjTWF0Y2hlcyhzb3VyY2UsIG1hdGNoZXMpKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHVuY2hhbmdlZENvdW50KytcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXBvcnRzPy5sZW5ndGggJiYgIXRyYW5zZm9ybS5vblJlcG9ydCkge1xuICAgICAgICAgIGxvZ0hlYWRlcihjb25zb2xlLmVycm9yKVxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICBjaGFsay5ibHVlKGRlZGVudGBcbiAgICAgICAgICAgIFJlcG9ydHNcbiAgICAgICAgICAgIC0tLS0tLS1cbiAgICAgICAgICBgKVxuICAgICAgICAgIClcbiAgICAgICAgICByZXBvcnRzPy5mb3JFYWNoKChyOiBhbnkpID0+IGNvbnNvbGUuZXJyb3IocikpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgY2hhbGsucmVkKGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5zdGFjayA6IFN0cmluZyhlcnJvcikpXG4gICAgICApXG4gICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKHNwaW5uZXJJbnRlcnZhbCAhPSBudWxsKSBjbGVhckludGVydmFsKHNwaW5uZXJJbnRlcnZhbClcbiAgICAgIGNsZWFyUHJvZ3Jlc3MoKVxuICAgIH1cblxuICAgIGlmICh0cmFuc2Zvcm0ucmVwbGFjZSB8fCB0cmFuc2Zvcm0uYXN0eCkge1xuICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgIGAke2NoYW5nZWRDb3VudH0gZmlsZSR7Y2hhbmdlZENvdW50ID09PSAxID8gJycgOiAncyd9IGNoYW5nZWRgXG4gICAgICAgIClcbiAgICAgIClcbiAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgIGNoYWxrLmdyZWVuKFxuICAgICAgICAgIGAke3VuY2hhbmdlZENvdW50fSBmaWxlJHt1bmNoYW5nZWRDb3VudCA9PT0gMSA/ICcnIDogJ3MnfSB1bmNoYW5nZWRgXG4gICAgICAgIClcbiAgICAgIClcbiAgICAgIGlmIChlcnJvckNvdW50ID4gMCkge1xuICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgIGNoYWxrLnJlZChgJHtlcnJvckNvdW50fSBmaWxlJHtlcnJvckNvdW50ID09PSAxID8gJycgOiAncyd9IGVycm9yZWRgKVxuICAgICAgICApXG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0cmFuc2Zvcm0uZmluZCkge1xuICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgIGBcXG4ke3VuY2hhbmdlZENvdW50fSBmaWxlJHtcbiAgICAgICAgICAgIGNoYW5nZWRDb3VudCA9PT0gMSA/ICcnIDogJ3MnXG4gICAgICAgICAgfSBoYWQgbm8gbWF0Y2hlc2BcbiAgICAgICAgKVxuICAgICAgKVxuICAgIH1cblxuICAgIGlmICghaXNFbXB0eShyZXN1bHRzKSkge1xuICAgICAgY29uc3QgYXBwbHkgPSBhcmd2Lnllc1xuICAgICAgICA/IHRydWVcbiAgICAgICAgOiAoXG4gICAgICAgICAgICBhd2FpdCBpbnF1aXJlci5wcm9tcHQoW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdHlwZTogJ2NvbmZpcm0nLFxuICAgICAgICAgICAgICAgIG5hbWU6ICdhcHBseScsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogJ0FwcGx5IGNoYW5nZXMnLFxuICAgICAgICAgICAgICAgIGRlZmF1bHQ6IGZhbHNlLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgXSlcbiAgICAgICAgICApLmFwcGx5XG4gICAgICBpZiAoYXBwbHkpIHtcbiAgICAgICAgZm9yIChjb25zdCBmaWxlIGluIHJlc3VsdHMpIHtcbiAgICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUoZmlsZSwgcmVzdWx0c1tmaWxlXSwgJ3V0ZjgnKVxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYFdyb3RlICR7ZmlsZX1gKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAocHJvY2Vzcy5zZW5kKSBwcm9jZXNzLnNlbmQoeyBleGl0OiAwIH0pXG4gICAgfVxuICAgIGF3YWl0IHBvb2w/LmVuZCgpXG4gICAgcHJvY2Vzcy5leGl0KDApXG4gIH0sXG59XG5cbmV4cG9ydCBkZWZhdWx0IHRyYW5zZm9ybVxuIl0sIm1hcHBpbmdzIjoiO0FBQ0EsT0FBT0EsSUFBUCxNQUFpQixNQUFqQjtBQUNBLE9BQU9DLEtBQVAsTUFBa0IsT0FBbEI7QUFDQSxPQUFPQyxVQUFQLE1BQXVCLG9CQUF2QjtBQUNBLFNBQVNDLE9BQVQsRUFBa0JDLElBQWxCLFFBQThCLFFBQTlCO0FBQ0EsT0FBT0MsUUFBUCxNQUFxQixVQUFyQjtBQUNBLE9BQU9DLEVBQVAsTUFBZSxVQUFmO0FB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