Skip to content

Commit f4e0783

Browse files
committed
feat: add CLI
1 parent 5a6fa31 commit f4e0783

File tree

15 files changed

+345
-63
lines changed

15 files changed

+345
-63
lines changed

package.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
"name": "astx",
33
"version": "0.0.0-development",
44
"description": "JS/TS find and replace API/CLI for jscodeshift",
5+
"bin": {
6+
"astx": "cli/index.js"
7+
},
58
"main": "index.js",
69
"sideEffects": false,
710
"scripts": {
11+
"cli": "ts-node src/cli/index.ts",
812
"lint": "eslint $npm_package_config_lint",
913
"lint:fix": "eslint $npm_package_config_lint",
1014
"prettier": "prettier --write .babelrc.js *.json *.md *.ts '{src,test}/**/*.{js,ts}'",
@@ -101,9 +105,11 @@
101105
"@jedwards1211/commitlint-config": "^1.0.1",
102106
"@jedwards1211/eslint-config-typescript": "^1.0.0",
103107
"@types/chai": "^4.2.0",
108+
"@types/inquirer": "^7.3.1",
104109
"@types/jscodeshift": "^0.7.1",
105110
"@types/mocha": "^5.2.7",
106111
"@types/node": "^12.12.6",
112+
"@types/yargs": "^15.0.10",
107113
"babel-eslint": "^10.0.1",
108114
"babel-plugin-istanbul": "^5.1.0",
109115
"chai": "^4.2.0",
@@ -123,11 +129,17 @@
123129
"require-glob": "^3.2.0",
124130
"rimraf": "^2.6.0",
125131
"semantic-release": "^17.1.2",
126-
"typescript": "^3.7.2"
132+
"ts-node": "^9.0.0",
133+
"typescript": "^4.1.2"
127134
},
128135
"dependencies": {
129136
"@babel/runtime": "^7.1.5",
130-
"jscodeshift": "^0.11.0"
137+
"chalk": "^4.1.0",
138+
"inquirer": "^7.3.3",
139+
"jscodeshift": "^0.11.0",
140+
"print-diff": "^1.0.0",
141+
"smart-restart": "^2.5.0",
142+
"yargs": "^16.1.1"
131143
},
132144
"renovate": {
133145
"extends": [

src/cli/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env node
2+
3+
import launch from 'smart-restart'
4+
import path from 'path'
5+
6+
launch({
7+
main: path.resolve(__dirname, 'runner' + path.extname(__filename)),
8+
command: __filename.endsWith('.ts') ? 'ts-node' : 'node',
9+
args: process.argv.slice(2),
10+
restartOnError: false,
11+
restartOnExit: false,
12+
})

src/cli/runner.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/* eslint-disable no-console */
2+
3+
import chalk from 'chalk'
4+
import path from 'path'
5+
import fs from 'fs'
6+
import printDiff from 'print-diff'
7+
import jscodeshift from 'jscodeshift'
8+
import yargs from 'yargs'
9+
import inquirer from 'inquirer'
10+
import parseFindOrReplace from '../util/parseFindOrReplace'
11+
import replace from '../replace'
12+
import * as astx from '../index'
13+
14+
const argv = yargs.option('transform', {
15+
alias: 't',
16+
describe: 'path to the transform file. Can be either a local path or url',
17+
default: './astx.js',
18+
}).argv
19+
20+
const { _: files } = argv
21+
22+
// eslint-disable-next-line @typescript-eslint/no-var-requires
23+
const transform = require(path.resolve(argv.transform))
24+
25+
const results: Record<string, string> = {}
26+
27+
const j = transform.parser
28+
? jscodeshift.withParser(transform.parser)
29+
: jscodeshift
30+
31+
let parsedFind, parsedReplace
32+
if (transform.find) {
33+
try {
34+
parsedFind = parseFindOrReplace(j, transform.find)
35+
} catch (error) {
36+
console.error(`failed to parse find: ${error.message}`)
37+
process.exit(1)
38+
}
39+
}
40+
if (transform.replace) {
41+
try {
42+
parsedReplace =
43+
typeof transform.replace === 'function'
44+
? transform.replace
45+
: parseFindOrReplace(j, transform.replace)
46+
} catch (error) {
47+
console.error(`failed to parse replace: ${error.message}`)
48+
process.exit(1)
49+
}
50+
}
51+
52+
for (const file of files) {
53+
const source = fs.readFileSync(file, 'utf8')
54+
let result
55+
const reports: any[] = []
56+
if (typeof transform === 'function') {
57+
result = transform(
58+
{
59+
source,
60+
path: file,
61+
},
62+
{
63+
jscodeshift: transform.parser
64+
? jscodeshift.withParser(transform.parser)
65+
: jscodeshift,
66+
// eslint-disable-next-line @typescript-eslint/no-empty-function
67+
stats: () => {},
68+
report: (msg: any) => reports.push(msg),
69+
astx,
70+
},
71+
{}
72+
)
73+
} else if (parsedFind && parsedReplace) {
74+
const code = fs.readFileSync(file, 'utf8')
75+
const root = j(code)
76+
replace(root, parsedFind, parsedReplace)
77+
result = root.toSource()
78+
}
79+
if (source !== result) results[file] = result
80+
if (files.length > 1) {
81+
console.error(
82+
chalk.blue(`
83+
==========================================
84+
${file}
85+
==========================================
86+
`)
87+
)
88+
}
89+
printDiff(source, result)
90+
if (reports.length) {
91+
console.error(chalk.blue`
92+
Reports
93+
-------
94+
`)
95+
reports.forEach(r => console.error(...r))
96+
}
97+
}
98+
99+
if (Object.keys(results).length) {
100+
inquirer
101+
.prompt([
102+
{
103+
type: 'confirm',
104+
name: 'apply',
105+
message: 'Apply changes',
106+
default: false,
107+
},
108+
])
109+
.then(({ apply }: { apply: boolean }) => {
110+
if (apply) {
111+
for (const file in results) {
112+
fs.writeFileSync(file, results[file], 'utf8')
113+
console.error(`Wrote ${file}`)
114+
}
115+
}
116+
if (process.send) process.send({ exit: 0 })
117+
})
118+
}

src/index.spec.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import find, { Match } from './find'
2+
import replace, { replaceMatches } from './replace'
3+
4+
export { find, replace, replaceMatches, Match }

test/find/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import jscodeshift, { ASTPath } from 'jscodeshift'
55
import find from '../../src/find'
66
import requireGlob from 'require-glob'
77
import mapValues from '../../src/util/mapValues'
8-
import parseFindOrReplace from '../util/parseFindOrReplace'
8+
import parseFindOrReplace from '../../src/util/parseFindOrReplace'
99

1010
type Fixture = {
1111
input: string

test/replace/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import jscodeshift, { ASTNode } from 'jscodeshift'
55
import replace from '../../src/replace'
66
import requireGlob from 'require-glob'
77
import { Match } from '../../src/find'
8-
import parseFindOrReplace from '../util/parseFindOrReplace'
8+
import parseFindOrReplace from '../../src/util/parseFindOrReplace'
99

1010
type Fixture = {
1111
input: string

tsconfig.json

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
22
"include": ["./src"],
3-
"exclude": ["node_modules", "./src/**/*.spec.ts", "./test"],
3+
"exclude": ["node_modules"],
44
"compilerOptions": {
5+
/* Visit https://aka.ms/tsconfig.json to read more about this file */
6+
57
/* Basic Options */
6-
"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
7-
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
8+
// "incremental": true, /* Enable incremental compilation */
9+
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
10+
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
811
// "lib": [], /* Specify library files to be included in the compilation. */
912
// "allowJs": true, /* Allow javascript files to be compiled. */
1013
// "checkJs": true, /* Report errors in .js files. */
@@ -16,6 +19,7 @@
1619
"outDir": "./" /* Redirect output structure to the directory. */,
1720
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
1821
// "composite": true, /* Enable project compilation */
22+
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
1923
// "removeComments": true, /* Do not emit comments to output. */
2024
// "noEmit": true, /* Do not emit outputs. */
2125
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
@@ -27,6 +31,7 @@
2731
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
2832
// "strictNullChecks": true, /* Enable strict null checks. */
2933
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
34+
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
3035
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
3136
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
3237
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
@@ -36,21 +41,26 @@
3641
// "noUnusedParameters": true, /* Report errors on unused parameters. */
3742
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
3843
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
44+
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
3945

4046
/* Module Resolution Options */
4147
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
4248
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
4349
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
4450
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
45-
// "typeRoots": [], /* List of folders to include type definitions from. */
51+
"typeRoots": [
52+
"./node_modules/@types",
53+
"./types"
54+
] /* List of folders to include type definitions from. */,
4655
// "types": [], /* Type declaration files to be included in compilation. */
47-
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
48-
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
56+
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
57+
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
4958
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
59+
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
5060

5161
/* Source Map Options */
52-
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
53-
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
62+
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
63+
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
5464
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
5565
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
5666

@@ -59,6 +69,7 @@
5969
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
6070

6171
/* Advanced Options */
62-
// "declarationDir": "lib" /* Output directory for generated declaration files. */
72+
"skipLibCheck": true /* Skip type checking of declaration files. */,
73+
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
6374
}
6475
}

types/require-glob/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare module 'require-glob' {
2+
export function sync(path: string): Record<string, any>
3+
}

0 commit comments

Comments
 (0)