Skip to content

Commit 374734b

Browse files
feat: add CLI support (#27)
1 parent e2b9c7e commit 374734b

File tree

8 files changed

+85
-30
lines changed

8 files changed

+85
-30
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
],
1313
"main": "dist/cli.js",
1414
"types": "dist/cli.d.ts",
15+
"bin": "./dist/cli.js",
1516
"scripts": {
1617
"transform": "ts-node-transpile-only ./src/cli.ts",
1718
"test": "jest",
@@ -34,13 +35,15 @@
3435
},
3536
"dependencies": {
3637
"camelcase": "^6.2.0",
38+
"commander": "^8.1.0",
3739
"css-modules-loader-core": "^1.1.0",
3840
"lodash": "^4.17.21",
3941
"postcss": "^8.3.6",
4042
"postcss-nested": "^5.0.6",
4143
"postcss-scss": "^4.0.0",
4244
"postcss-selector-parser": "^6.0.6",
4345
"prettier-eslint": "^13.0.0",
46+
"signale": "^1.4.0",
4447
"stylelint": "^13.13.1",
4548
"ts-morph": "^11.0.3",
4649
"ts-node": "^10.1.0",
@@ -60,6 +63,7 @@
6063
"@types/eslint": "7.28.0",
6164
"@types/jest": "27.0.1",
6265
"@types/node": "16.4.1",
66+
"@types/signale": "1.4.2",
6367
"@types/stylelint": "13.13.2",
6468
"eslint": "^7.32.0",
6569
"husky": "^7.0.1",

src/cli.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
import path from 'path'
22

3+
import { Command, Option } from 'commander'
4+
import signale from 'signale'
35
import { Project } from 'ts-morph'
46

5-
import { globalCssToCssModule } from './transforms/globalCssToCssModule/globalCssToCssModule'
7+
import { transforms, transformNames } from './transforms'
68

7-
const TARGET_FILE = path.resolve(__dirname, './transforms/globalCssToCssModule/__tests__/fixtures/Kek.tsx')
9+
const program = new Command()
810

9-
async function main(): Promise<void> {
10-
const project = new Project()
11-
project.addSourceFilesAtPaths(TARGET_FILE)
12-
13-
const result = await globalCssToCssModule({ project, shouldWriteFiles: true })
14-
console.log(result)
11+
interface CodemodCliOptions {
12+
write: boolean
13+
format: boolean
14+
transform: keyof typeof transforms
1515
}
1616

17-
main().catch(error => {
18-
throw error
19-
})
17+
program
18+
.addOption(
19+
new Option('-t, --transform <transform>', 'Transform name').choices(transformNames).makeOptionMandatory()
20+
)
21+
.option('-w, --write [write]', 'Persist codemod changes to the filesystem', false)
22+
.argument('<fileGlob>', 'File glob or globs to change files based on')
23+
.action(async (fileGlob: string, options: CodemodCliOptions) => {
24+
const { write: shouldWriteFiles, transform } = options
25+
const projectGlob = path.isAbsolute(fileGlob) ? fileGlob : path.join(process.cwd(), fileGlob)
26+
27+
signale.start(`Starting codemod "${transform}" with project glob "${projectGlob}"`)
28+
29+
const project = new Project()
30+
project.addSourceFilesAtPaths(projectGlob)
31+
32+
const results = await transforms[transform]({ project, shouldWriteFiles })
33+
34+
if (results) {
35+
if (shouldWriteFiles) {
36+
signale.info('Persisting codemod changes to the filesystem...')
37+
await Promise.all(results.map(result => result.fsWritePromise))
38+
signale.info('Persisting codemod changes completed')
39+
} else {
40+
console.log(results)
41+
}
42+
43+
signale.success('Files are transformed!')
44+
}
45+
})
46+
47+
program.parse(process.argv)

src/transforms/globalCssToCssModule/README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,5 @@ Status: WIP
55
## Usage
66

77
```sh
8-
echo "Hello world"
8+
yarn transform -t globalCssToCssModule /client/web/src/search/home/SearchPage.tsx --write
99
```
10-
11-
## TODO
12-
13-
- [x] Run code formatters on the updated files
14-
- [x] Handle @import statements in SCSS
15-
- [ ] Add interactive CLI support

src/transforms/globalCssToCssModule/__tests__/globalCssToCssModule.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ describe('globalCssToCssModule', () => {
1010
it('transforms correctly', async () => {
1111
const project = new Project()
1212
project.addSourceFilesAtPaths(TARGET_FILE)
13-
const [transformResult] = await globalCssToCssModule({ project })
13+
const codemodResults = await globalCssToCssModule({ project })
1414

15-
expect(transformResult.css.source).toMatchSnapshot()
16-
expect(transformResult.ts.source).toMatchSnapshot()
15+
expect(codemodResults).toBeTruthy()
16+
17+
if (codemodResults) {
18+
const [transformResult] = codemodResults
19+
20+
expect(transformResult.css.source).toMatchSnapshot()
21+
expect(transformResult.ts.source).toMatchSnapshot()
22+
}
1723
}, 15000) // Timeout of 15s (default is 5s). `prettier-eslint` format is slow 😬.
1824
})

src/transforms/globalCssToCssModule/globalCssToCssModule.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { existsSync, readFileSync, promises as fsPromises } from 'fs'
22
import path from 'path'
33

4+
import signale from 'signale'
45
import { Project } from 'ts-morph'
56

67
import { formatWithPrettierEslint, formatWithStylelint, isDefined } from '../../utils'
@@ -41,7 +42,7 @@ interface CodemodResult {
4142
* 7) Add `.module.scss` import to the `.tsx` file.
4243
*
4344
*/
44-
export async function globalCssToCssModule(options: GlobalCssToCssModuleOptions): Promise<CodemodResult[]> {
45+
export async function globalCssToCssModule(options: GlobalCssToCssModuleOptions): Promise<CodemodResult[] | false> {
4546
const { project, shouldWriteFiles } = options
4647
/**
4748
* Find `.tsx` files with co-located `.scss` file.
@@ -66,10 +67,18 @@ export async function globalCssToCssModule(options: GlobalCssToCssModuleOptions)
6667
})
6768
.filter(isDefined)
6869

70+
if (itemsToProcess.length === 0) {
71+
signale.warn('No files to process!')
72+
73+
return false
74+
}
75+
6976
const codemodResultPromises = itemsToProcess.map(async ({ tsSourceFile, cssFilePath }) => {
7077
const tsFilePath = tsSourceFile.getFilePath()
7178
const parsedTsFilePath = path.parse(tsFilePath)
7279

80+
signale.info(`Processing file "${tsFilePath}"`)
81+
7382
const sourceCss = readFileSync(cssFilePath, 'utf8')
7483
const exportNameMap = await getCssModuleExportNameMap(sourceCss)
7584
const { css: cssModuleSource, filePath: cssModuleFileName } = await transformFileToCssModule({
@@ -119,11 +128,5 @@ export async function globalCssToCssModule(options: GlobalCssToCssModuleOptions)
119128
}
120129
})
121130

122-
const codemodResults = await Promise.all(codemodResultPromises)
123-
124-
if (shouldWriteFiles) {
125-
await Promise.all(codemodResults.map(result => result.fsWritePromise))
126-
}
127-
128-
return codemodResults
131+
return Promise.all(codemodResultPromises)
129132
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './globalCssToCssModule'

src/transforms/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { globalCssToCssModule } from './globalCssToCssModule/globalCssToCssModule'
2+
3+
export const transforms = {
4+
globalCssToCssModule,
5+
} as const
6+
7+
export const transformNames = Object.keys(transforms)

yarn.lock

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,13 @@
19331933
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
19341934
integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==
19351935

1936+
"@types/signale@1.4.2":
1937+
version "1.4.2"
1938+
resolved "https://registry.yarnpkg.com/@types/signale/-/signale-1.4.2.tgz#6fc36d038297fa4c1d9aa2b5c2ec80195d4c5d2f"
1939+
integrity sha512-cIqvDXQmLD5UNgtYhHvTt4dEyR+FXuCwoqQbz7mZaS7pGyJvpUFDAoMbgwymptqUXH3eL8vBcNU+dBiyauaA6Q==
1940+
dependencies:
1941+
"@types/node" "*"
1942+
19361943
"@types/stack-utils@^2.0.0":
19371944
version "2.0.1"
19381945
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
@@ -2911,6 +2918,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
29112918
dependencies:
29122919
delayed-stream "~1.0.0"
29132920

2921+
commander@^8.1.0:
2922+
version "8.1.0"
2923+
resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362"
2924+
integrity sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==
2925+
29142926
comment-parser@^0.7.6:
29152927
version "0.7.6"
29162928
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12"
@@ -7523,7 +7535,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
75237535
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
75247536
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
75257537

7526-
signale@^1.2.1:
7538+
signale@^1.2.1, signale@^1.4.0:
75277539
version "1.4.0"
75287540
resolved "https://registry.yarnpkg.com/signale/-/signale-1.4.0.tgz#c4be58302fb0262ac00fc3d886a7c113759042f1"
75297541
integrity sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==

0 commit comments

Comments
 (0)