@spark-ui/cli-utils 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/bin/spark-generate.mjs +1 -1
- package/bin/spark-scan.mjs +9 -3
- package/package.json +2 -2
- package/src/core/Logger.mjs +39 -9
- package/src/scan/config.mjs +7 -0
- package/src/scan/index.mjs +40 -40
- package/src/scan/loadConfig.mjs +40 -0
- package/src/scan/utils/index.mjs +0 -1
- package/test/spark-generate.test.ts +22 -29
- package/src/scan/utils/logger.mjs +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.15.0](https://github.com/adevinta/spark/compare/@spark-ui/cli-utils@2.14.0...@spark-ui/cli-utils@2.15.0) (2024-07-03)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **cli-utils:** add some feedback when not loading a given config file ([1231fe6](https://github.com/adevinta/spark/commit/1231fe6cb94c15643a11278c3210836e81059364))
|
|
11
|
+
- **cli-utils:** fix config merge method to allow proper option override ([8b6a124](https://github.com/adevinta/spark/commit/8b6a124c6e70f05769305bf6ca84137c7a05510c))
|
|
12
|
+
- **cli-utils:** fix logger issue on generate script after changes ([083c9b0](https://github.com/adevinta/spark/commit/083c9b043d1837a807b159a8143fd16bf8d7087e))
|
|
13
|
+
- **cli-utils:** merge values ([a7e8129](https://github.com/adevinta/spark/commit/a7e81292d452d4ee026f3d4705198ac4ecc6363e))
|
|
14
|
+
- **cli-utils:** merging configs ([aeb6251](https://github.com/adevinta/spark/commit/aeb6251d23bc506674e4545ebad928081292f520))
|
|
15
|
+
- **cli-utils:** missalignment on the default config values nesting ([79729f7](https://github.com/adevinta/spark/commit/79729f722f4ecf6f0c671e2c1c1d5929a87d98c9))
|
|
16
|
+
- **cli-utils:** output file creates new content ([973ffa8](https://github.com/adevinta/spark/commit/973ffa85e6987dbd87a119e9253db57556a705d4))
|
|
17
|
+
- **cli-utils:** remove betta tagging ([96cafae](https://github.com/adevinta/spark/commit/96cafae45e4a4759ef9ff4a763963f26cb898cb6))
|
|
18
|
+
- **cli-utils:** remove extra logs ([f588649](https://github.com/adevinta/spark/commit/f588649eb299fc42ef9d6dbf8f8f4eebd0cfd6a9))
|
|
19
|
+
- **cli-utils:** remove unnecesary log ([614d834](https://github.com/adevinta/spark/commit/614d834d5d7a0f3084b3097e2dee5436b369fbb6))
|
|
20
|
+
- log config setted ([190a058](https://github.com/adevinta/spark/commit/190a0581ab6219591c5d3a3ecde2ee46d504d7ca))
|
|
21
|
+
- priority of commands ([ed178a5](https://github.com/adevinta/spark/commit/ed178a51a8cea0255c96467b0d8760daf8f851dd))
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
- **cli-utils:** exoposes all configuration parameters as a build argument ([3e88a99](https://github.com/adevinta/spark/commit/3e88a998c05da026cd196b3dd54a2ad50506266c))
|
|
26
|
+
- mixing parameters ([da1bf1d](https://github.com/adevinta/spark/commit/da1bf1d838df2c577755eb3c651cd0bba8d07203))
|
|
27
|
+
|
|
6
28
|
# [2.14.0](https://github.com/adevinta/spark/compare/@spark-ui/cli-utils@2.13.8...@spark-ui/cli-utils@2.14.0) (2024-06-14)
|
|
7
29
|
|
|
8
30
|
### Features
|
package/bin/spark-generate.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { TemplateGenerator } from '../src/generate/generators/index.mjs'
|
|
|
6
6
|
import { Logger, System } from '../src/core/index.mjs'
|
|
7
7
|
import { DescriptionValidator, NameValidator } from '../src/generate/validators/index.mjs'
|
|
8
8
|
|
|
9
|
-
const logger = new Logger()
|
|
9
|
+
const logger = new Logger({ verbose: true })
|
|
10
10
|
const system = new System({ logger })
|
|
11
11
|
const generator = new TemplateGenerator({ system })
|
|
12
12
|
|
package/bin/spark-scan.mjs
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander'
|
|
4
|
-
import { adoption } from '../src/scan/index.mjs'
|
|
4
|
+
import { adoption, config } from '../src/scan/index.mjs'
|
|
5
5
|
|
|
6
6
|
const program = new Command()
|
|
7
7
|
|
|
8
8
|
program
|
|
9
9
|
.command('adoption')
|
|
10
10
|
.description('Scan @spark-ui adoption for .tsx files with given imports')
|
|
11
|
-
.option('-c, --configuration <
|
|
12
|
-
.option('-o, --output <
|
|
11
|
+
.option('-c, --configuration <config>', 'configuration file route')
|
|
12
|
+
.option('-o, --output <output>', 'output file route')
|
|
13
|
+
.option('-v, --verbose', 'output log information', false)
|
|
14
|
+
.option('-d, --details', 'output information about each match', config.details)
|
|
15
|
+
.option('-s, --sort <sort>', 'sort results (count or alphabetical)', config.sort)
|
|
16
|
+
.option('-dir, --directory <directory>', 'directory to parse', config.directory)
|
|
17
|
+
.option('-ext, --extensions <extensions...>', 'file extensions to parse', config.extensions)
|
|
18
|
+
.option('-i, --imports <imports...>', 'import patterns to identify', config.imports)
|
|
13
19
|
.action(adoption)
|
|
14
20
|
|
|
15
21
|
program.parse(process.argv)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spark-ui/cli-utils",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "Spark CLI utils",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/fs-extra": "11.0.4"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "8a312bf6a9f20c8dd0cc3cc10649359c4313a2a7"
|
|
49
49
|
}
|
package/src/core/Logger.mjs
CHANGED
|
@@ -1,20 +1,50 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import chalk from 'chalk'
|
|
3
2
|
|
|
4
3
|
export class Logger {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
#force = false
|
|
5
|
+
|
|
6
|
+
constructor({ verbose } = {}) {
|
|
7
|
+
this.verbose = verbose
|
|
8
|
+
|
|
9
|
+
if (typeof Logger.instance === 'object') {
|
|
10
|
+
return Logger.instance
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Logger.instance = this
|
|
14
|
+
|
|
15
|
+
return this
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#log({ type = v => v, force, verbose }, ...args) {
|
|
19
|
+
if (force || verbose) {
|
|
20
|
+
console.log(type(...args)) // eslint-disable-line no-console
|
|
21
|
+
}
|
|
22
|
+
this.#force = false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
force() {
|
|
26
|
+
this.#force = true
|
|
27
|
+
|
|
28
|
+
return this
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
error(...args) {
|
|
32
|
+
this.#log({ type: chalk.red, force: this.#force, verbose: this.verbose }, ...args)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
warning(...args) {
|
|
36
|
+
this.#log({ type: chalk.yellow, force: this.#force, verbose: this.verbose }, ...args)
|
|
7
37
|
}
|
|
8
38
|
|
|
9
|
-
|
|
10
|
-
|
|
39
|
+
info(...args) {
|
|
40
|
+
this.#log({ type: chalk.cyan, force: this.#force, verbose: this.verbose }, ...args)
|
|
11
41
|
}
|
|
12
42
|
|
|
13
|
-
|
|
14
|
-
|
|
43
|
+
success(...args) {
|
|
44
|
+
this.#log({ type: chalk.green, force: this.#force, verbose: this.verbose }, ...args)
|
|
15
45
|
}
|
|
16
46
|
|
|
17
|
-
|
|
18
|
-
|
|
47
|
+
break() {
|
|
48
|
+
this.#log({ type: chalk.green, force: this.#force, verbose: this.verbose }, '')
|
|
19
49
|
}
|
|
20
50
|
}
|
package/src/scan/index.mjs
CHANGED
|
@@ -1,49 +1,41 @@
|
|
|
1
|
+
/* eslint-disable complexity */
|
|
2
|
+
/* eslint-disable max-lines-per-function */
|
|
1
3
|
import * as process from 'node:process'
|
|
2
4
|
|
|
3
|
-
import {
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs'
|
|
4
6
|
import path from 'path'
|
|
5
7
|
|
|
8
|
+
import { Logger } from '../core/index.mjs'
|
|
9
|
+
import * as defaultConfig from './config.mjs'
|
|
10
|
+
import { loadConfig } from './loadConfig.mjs'
|
|
6
11
|
import { scanCallback } from './scanCallback.mjs'
|
|
7
|
-
import {
|
|
12
|
+
import { scanDirectories } from './utils/index.mjs'
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
details: false,
|
|
12
|
-
sort: 'count',
|
|
13
|
-
imports: ['@spark-ui'],
|
|
14
|
-
extensions: ['.tsx', '.ts'],
|
|
15
|
-
directory: '.',
|
|
16
|
-
},
|
|
17
|
-
}
|
|
14
|
+
export async function adoption(options = {}) {
|
|
15
|
+
const { configuration, ...optionsConfig } = options
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
let config =
|
|
17
|
+
const logger = new Logger({ verbose: optionsConfig.verbose })
|
|
18
|
+
let config = await loadConfig(configuration, { logger })
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
config = structuredClone(customConfig, DEFAULT_CONFIG)
|
|
30
|
-
} else {
|
|
31
|
-
logger.warn('⚠️ No custom configuration file found')
|
|
32
|
-
logger.info('ℹ️ Loading default configuration')
|
|
33
|
-
}
|
|
34
|
-
} catch (error) {
|
|
35
|
-
logger.error('💥 Something went wrong loading the custom configuration file')
|
|
20
|
+
config = {
|
|
21
|
+
adoption: {
|
|
22
|
+
...config.adoption,
|
|
23
|
+
...optionsConfig,
|
|
24
|
+
imports: optionsConfig.imports || config.adoption.imports,
|
|
25
|
+
extensions: optionsConfig.extensions || config.adoption.extensions,
|
|
26
|
+
},
|
|
36
27
|
}
|
|
37
28
|
|
|
38
|
-
const extensions = config.adoption.extensions
|
|
39
|
-
|
|
40
29
|
let importCount = 0
|
|
41
30
|
const importResults = {}
|
|
42
31
|
let importsUsed = {}
|
|
43
32
|
let importsCount = {}
|
|
44
|
-
|
|
33
|
+
|
|
34
|
+
const { details, directory, extensions, imports, sort, output } = config.adoption
|
|
35
|
+
|
|
36
|
+
imports.forEach(moduleName => {
|
|
45
37
|
logger.info(`ℹ️ Scanning adoption for ${moduleName}`)
|
|
46
|
-
const directoryPath = path.join(process.cwd(),
|
|
38
|
+
const directoryPath = path.join(process.cwd(), directory)
|
|
47
39
|
|
|
48
40
|
const response = scanDirectories(directoryPath, moduleName, extensions, scanCallback, {
|
|
49
41
|
importCount,
|
|
@@ -56,7 +48,7 @@ export async function adoption(options) {
|
|
|
56
48
|
`🎉 Found ${response.importCount - importCount} imports with "${moduleName}" modules across directory ${directoryPath}.`
|
|
57
49
|
)
|
|
58
50
|
} else {
|
|
59
|
-
logger.
|
|
51
|
+
logger.warning(
|
|
60
52
|
`⚠️ No files found with "${moduleName}" imports across directory ${directoryPath}.`
|
|
61
53
|
)
|
|
62
54
|
}
|
|
@@ -64,7 +56,7 @@ export async function adoption(options) {
|
|
|
64
56
|
})
|
|
65
57
|
|
|
66
58
|
// Sort importsUsed by alphabet
|
|
67
|
-
if (
|
|
59
|
+
if (sort === 'alphabetical') {
|
|
68
60
|
importsUsed = Object.fromEntries(
|
|
69
61
|
Object.entries(importsUsed)
|
|
70
62
|
.sort(([pkgNameA], [pkgNameB]) => pkgNameA.localeCompare(pkgNameB))
|
|
@@ -83,7 +75,7 @@ export async function adoption(options) {
|
|
|
83
75
|
]
|
|
84
76
|
})
|
|
85
77
|
)
|
|
86
|
-
} else if (
|
|
78
|
+
} else if (sort === 'count') {
|
|
87
79
|
// Sort importsUsed by most used
|
|
88
80
|
importsUsed = Object.fromEntries(
|
|
89
81
|
Object.entries(importsUsed)
|
|
@@ -110,17 +102,25 @@ export async function adoption(options) {
|
|
|
110
102
|
const result = Object.fromEntries(
|
|
111
103
|
Object.entries(importsUsed).map(([pkgName, value]) => [
|
|
112
104
|
pkgName,
|
|
113
|
-
{ ...value, ...(
|
|
105
|
+
{ ...value, ...(details && { results: importResults[pkgName] }) },
|
|
114
106
|
])
|
|
115
107
|
)
|
|
116
108
|
|
|
117
|
-
if (
|
|
109
|
+
if (output) {
|
|
118
110
|
try {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
const { dir } = path.parse(path.join(process.cwd(), output))
|
|
112
|
+
if (!existsSync(dir)) {
|
|
113
|
+
mkdirSync(dir, { recursive: true })
|
|
114
|
+
}
|
|
115
|
+
writeFileSync(`${path.join(process.cwd(), output)}`, JSON.stringify(result, null, 2))
|
|
116
|
+
} catch (error) {
|
|
117
|
+
logger.error(`💥 Error writing file: ${error}`)
|
|
118
|
+
process.exit(1)
|
|
122
119
|
}
|
|
123
120
|
} else {
|
|
124
|
-
logger.info(JSON.stringify(result, null, 2))
|
|
121
|
+
logger.force().info(JSON.stringify(result, null, 2))
|
|
122
|
+
process.exit(0)
|
|
125
123
|
}
|
|
126
124
|
}
|
|
125
|
+
|
|
126
|
+
export const config = { ...defaultConfig }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { existsSync } from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
import * as defaultConfig from './config.mjs'
|
|
7
|
+
|
|
8
|
+
export async function loadConfig(configuration, { logger }) {
|
|
9
|
+
try {
|
|
10
|
+
const configFileRoute = path.join(process.cwd(), configuration || '.spark-ui.cjs')
|
|
11
|
+
if (existsSync(configFileRoute)) {
|
|
12
|
+
logger.info('ℹ️ Loading spark-ui custom configuration file')
|
|
13
|
+
const { default: customConfig } = await import(configFileRoute)
|
|
14
|
+
|
|
15
|
+
const config = {
|
|
16
|
+
adoption: {
|
|
17
|
+
...defaultConfig,
|
|
18
|
+
...customConfig.adoption,
|
|
19
|
+
imports: customConfig.adoption.imports || defaultConfig.imports,
|
|
20
|
+
extensions: customConfig.adoption.extensions || defaultConfig.extensions,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return config
|
|
25
|
+
} else {
|
|
26
|
+
if (configuration) {
|
|
27
|
+
logger.error('⚠️ No custom configuration file found:', configFileRoute)
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
logger.warning('⚠️ No custom configuration file found')
|
|
31
|
+
logger.info('ℹ️ Loading default configuration')
|
|
32
|
+
|
|
33
|
+
return { ...defaultConfig }
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.error('💥 Something went wrong loading the custom configuration file', error)
|
|
37
|
+
|
|
38
|
+
return { ...defaultConfig }
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/scan/utils/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { extractImports } from './extract-imports.mjs'
|
|
2
2
|
export { fileContainsImport } from './file-contains-import.mjs'
|
|
3
3
|
export { getFormatedTimestamp } from './get-formated-timestamp.mjs'
|
|
4
|
-
export { logger } from './logger.mjs'
|
|
5
4
|
export { scanDirectories } from './scan-directories.mjs'
|
|
@@ -12,21 +12,28 @@ const cliPath = path.join(__dirname, '../../bin/spark.mjs')
|
|
|
12
12
|
const cliProcess = cmd.create(cliPath)
|
|
13
13
|
|
|
14
14
|
describe('CLI `spark generate` (component package)', () => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const packageType = 'component'
|
|
16
|
+
|
|
17
|
+
const contextPath = TemplateGenerator.CONTEXTS[packageType] as string
|
|
18
|
+
|
|
19
|
+
const packagePath = path.join(process.cwd(), 'packages', contextPath, 'bar')
|
|
20
|
+
const invalidPackagePath = path.join(process.cwd(), 'packages', contextPath, '123')
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
if (fse.existsSync(packagePath)) fse.removeSync(packagePath)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
if (fse.existsSync(packagePath)) fse.removeSync(packagePath)
|
|
28
|
+
})
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
it('should properly generate package from CLI when arguments are valid', async () => {
|
|
21
31
|
const response = await cliProcess.execute(
|
|
22
32
|
['generate'],
|
|
23
|
-
[
|
|
33
|
+
['bar', ENTER, packageType, ENTER, 'This is my foo component', ENTER]
|
|
24
34
|
)
|
|
25
|
-
const contextPath = TemplateGenerator.CONTEXTS[packageType] as string
|
|
26
|
-
const packagePath = path.join(process.cwd(), 'packages', contextPath, packageName)
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
const expectedFiles = [
|
|
36
|
+
;[
|
|
30
37
|
'/.npmignore',
|
|
31
38
|
'/package.json',
|
|
32
39
|
'/src/index.ts',
|
|
@@ -37,33 +44,19 @@ describe('CLI `spark generate` (component package)', () => {
|
|
|
37
44
|
'/src/Bar.test.tsx',
|
|
38
45
|
'/src/Bar.stories.tsx',
|
|
39
46
|
'/tsconfig.json',
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
const assertExpectedFiles = (filePath: string) => {
|
|
47
|
+
].forEach((filePath: string) => {
|
|
43
48
|
expect(response).toContain(`Created ${packagePath}${filePath}`)
|
|
44
49
|
expect(fse.pathExistsSync(`${packagePath}${filePath}`)).toBe(true)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
expectedFiles.forEach(assertExpectedFiles)
|
|
48
|
-
|
|
49
|
-
// Finally we clean up the generated component package
|
|
50
|
-
fse.removeSync(packagePath)
|
|
50
|
+
})
|
|
51
51
|
})
|
|
52
52
|
|
|
53
53
|
it('should prevent generating package when argument are invalid', async () => {
|
|
54
|
-
|
|
55
|
-
const packageName = '123'
|
|
56
|
-
const packageType = 'component'
|
|
57
|
-
|
|
58
|
-
// WHEN we try to create it
|
|
59
|
-
const response = await cliProcess.execute(['generate'], [packageName, ENTER])
|
|
60
|
-
const contextPath = TemplateGenerator.CONTEXTS[packageType] as string
|
|
61
|
-
const packagePath = path.join(process.cwd(), 'packages', contextPath, packageName)
|
|
54
|
+
const response = await cliProcess.execute(['generate'], ['123', ENTER])
|
|
62
55
|
|
|
63
|
-
// THEN it throws an error and fails to create files for this package
|
|
64
56
|
expect(response).toContain(
|
|
65
57
|
'Name name must contain letters and dash symbols only (ex: "my-package")'
|
|
66
58
|
)
|
|
67
|
-
|
|
59
|
+
|
|
60
|
+
expect(fse.pathExistsSync(invalidPackagePath)).toBe(false)
|
|
68
61
|
})
|
|
69
62
|
})
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
|
|
3
|
-
export const logger = {
|
|
4
|
-
error(...args) {
|
|
5
|
-
console.log(chalk.red(...args))
|
|
6
|
-
},
|
|
7
|
-
warn(...args) {
|
|
8
|
-
console.log(chalk.yellow(...args))
|
|
9
|
-
},
|
|
10
|
-
info(...args) {
|
|
11
|
-
console.log(chalk.cyan(...args))
|
|
12
|
-
},
|
|
13
|
-
success(...args) {
|
|
14
|
-
console.log(chalk.green(...args))
|
|
15
|
-
},
|
|
16
|
-
break() {
|
|
17
|
-
console.log('')
|
|
18
|
-
},
|
|
19
|
-
}
|