@spark-ui/cli-utils 9.4.7 → 10.0.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 +15 -0
- package/bin/spark.mjs +0 -1
- package/package.json +3 -4
- package/src/index.doc.mdx +0 -70
- package/test/spark-generate.test.ts +6 -10
- package/bin/spark-scan.mjs +0 -22
- package/src/generate/templates/component/[.npmignore].js +0 -3
- package/src/generate/templates/component/[LICENSE.md].js +0 -22
- package/src/generate/templates/component/[README.md].js +0 -18
- package/src/generate/templates/component/[package.json].js +0 -46
- package/src/generate/templates/component/[tsconfig.json].js +0 -5
- package/src/generate/templates/component/[vite.config.ts].js +0 -7
- package/src/scan/config.cjs +0 -37
- package/src/scan/index.mjs +0 -125
- package/src/scan/index.test.ts +0 -157
- package/src/scan/loadConfig.mjs +0 -41
- package/src/scan/scanCallback.mjs +0 -62
- package/src/scan/utils/extract-imports.mjs +0 -28
- package/src/scan/utils/file-contains-import.mjs +0 -17
- package/src/scan/utils/get-csv.mjs +0 -0
- package/src/scan/utils/get-formated-timestamp.mjs +0 -7
- package/src/scan/utils/index.mjs +0 -4
- package/src/scan/utils/scan-directories.mjs +0 -45
- /package/src/generate/templates/component/{src/[Component.doc.mdx].js → [Component.doc.mdx].js} +0 -0
- /package/src/generate/templates/component/{src/[Component.stories.tsx].js → [Component.stories.tsx].js} +0 -0
- /package/src/generate/templates/component/{src/[Component.styles.ts].js → [Component.styles.ts].js} +0 -0
- /package/src/generate/templates/component/{src/[Component.test.tsx].js → [Component.test.tsx].js} +0 -0
- /package/src/generate/templates/component/{src/[Component.tsx].js → [Component.tsx].js} +0 -0
- /package/src/generate/templates/component/{src/[index.ts].js → [index.ts].js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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
|
+
# [10.0.0](https://github.com/leboncoin/spark-web/compare/v9.4.8...v10.0.0) (2025-03-24)
|
|
7
|
+
|
|
8
|
+
### Code Refactoring
|
|
9
|
+
|
|
10
|
+
- grouped all components into a single package ([d521202](https://github.com/leboncoin/spark-web/commit/d5212023e4db8b88bc565deb3b6b92d23d894793))
|
|
11
|
+
|
|
12
|
+
### BREAKING CHANGES
|
|
13
|
+
|
|
14
|
+
- new package is @spark-ui/ui and enables individual imports by doing
|
|
15
|
+
@spark-ui/ui/button
|
|
16
|
+
|
|
17
|
+
## [9.4.8](https://github.com/leboncoin/spark-web/compare/v9.4.7...v9.4.8) (2025-03-19)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @spark-ui/cli-utils
|
|
20
|
+
|
|
6
21
|
## [9.4.7](https://github.com/leboncoin/spark-web/compare/v9.4.6...v9.4.7) (2025-03-19)
|
|
7
22
|
|
|
8
23
|
**Note:** Version bump only for package @spark-ui/cli-utils
|
package/bin/spark.mjs
CHANGED
|
@@ -9,6 +9,5 @@ const { version } = require('../package.json')
|
|
|
9
9
|
program.version(version, '--version')
|
|
10
10
|
|
|
11
11
|
program.command('generate', 'Generate a component scaffolding').alias('g')
|
|
12
|
-
program.command('scan', 'Scan a directory for components').alias('s')
|
|
13
12
|
|
|
14
13
|
program.parse(process.argv)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spark-ui/cli-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"description": "Spark CLI utils",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"bin": {
|
|
14
14
|
".": "./bin/spark.mjs",
|
|
15
15
|
"spark": "./bin/spark.mjs",
|
|
16
|
-
"spark-generate": "./bin/spark-generate.mjs"
|
|
17
|
-
"spark-scan": "./bin/spark-scan.mjs"
|
|
16
|
+
"spark-generate": "./bin/spark-generate.mjs"
|
|
18
17
|
},
|
|
19
18
|
"type": "module",
|
|
20
19
|
"repository": {
|
|
@@ -44,5 +43,5 @@
|
|
|
44
43
|
"devDependencies": {
|
|
45
44
|
"@types/fs-extra": "11.0.4"
|
|
46
45
|
},
|
|
47
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "799814308844b640f4b52c82786ad5bdd7cbe108"
|
|
48
47
|
}
|
package/src/index.doc.mdx
CHANGED
|
@@ -40,73 +40,3 @@ Then, a command prompt will guide you through the process by asking you for:
|
|
|
40
40
|
- the package name (required),
|
|
41
41
|
- the template used (required, only `Component` template available right now)
|
|
42
42
|
- and the package description (optional).
|
|
43
|
-
|
|
44
|
-
## Scanning directory adoption
|
|
45
|
-
|
|
46
|
-
For viewing the adoption of packages in a project directory, the following command can be executed:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
$ spark scan adoption
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Options
|
|
53
|
-
|
|
54
|
-
#### Configuration
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
$ spark scan adoption --configuration <filename>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
alias
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
$ spark scan adoption -c <filename>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
example
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
spark scan adoption -c "./spark-ui.cjs"
|
|
70
|
-
````
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
##### configuration filename structure
|
|
74
|
-
|
|
75
|
-
```js
|
|
76
|
-
// .spark-ui.cjs
|
|
77
|
-
module.exports = {
|
|
78
|
-
adoption: {
|
|
79
|
-
details: true,
|
|
80
|
-
sort: 'count', // 'count' or 'alphabetical'
|
|
81
|
-
imports: ['@spark-ui'],
|
|
82
|
-
extensions: ['.tsx', '.ts'],
|
|
83
|
-
directory: './packages',
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/***
|
|
88
|
-
- `details` (boolean) - whether to show the details of the adoption or not. Default: false
|
|
89
|
-
- `sort` ('count' | 'alphabetical') - packages are sorted alphabetically. Default: false means sorted by adoption number
|
|
90
|
-
- `imports` (array) - the imports to be scanned.
|
|
91
|
-
- `extensions` (array) - the extensions to be scanned
|
|
92
|
-
- `directory` (string) - the directory to be scanned. Default: '.' means the current directory
|
|
93
|
-
***/
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
#### Output
|
|
97
|
-
The output option is used to save the adoption data to a file. It is optional
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
$ spark scan adoption --output <filename>
|
|
101
|
-
```
|
|
102
|
-
alias
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
$ spark scan adoption -o <filename>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
example
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
spark scan adoption -o "./adoption.$(date +"%Y%m%d_%H:%M:%S").json"
|
|
112
|
-
````
|
|
@@ -34,16 +34,12 @@ describe('CLI `spark generate` (component package)', () => {
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
;[
|
|
37
|
-
'
|
|
38
|
-
'/
|
|
39
|
-
'/
|
|
40
|
-
'/
|
|
41
|
-
'/
|
|
42
|
-
'/
|
|
43
|
-
'/src/Bar.doc.mdx',
|
|
44
|
-
'/src/Bar.test.tsx',
|
|
45
|
-
'/src/Bar.stories.tsx',
|
|
46
|
-
'/tsconfig.json',
|
|
37
|
+
'/index.ts',
|
|
38
|
+
'/Bar.styles.ts',
|
|
39
|
+
'/Bar.tsx',
|
|
40
|
+
'/Bar.doc.mdx',
|
|
41
|
+
'/Bar.test.tsx',
|
|
42
|
+
'/Bar.stories.tsx',
|
|
47
43
|
].forEach((filePath: string) => {
|
|
48
44
|
expect(response.toString()).toContain(`Created ${packagePath}${filePath}`)
|
|
49
45
|
expect(fse.pathExistsSync(`${packagePath}${filePath}`)).toBe(true)
|
package/bin/spark-scan.mjs
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander'
|
|
4
|
-
|
|
5
|
-
import { adoption, config } from '../src/scan/index.mjs'
|
|
6
|
-
|
|
7
|
-
const program = new Command()
|
|
8
|
-
|
|
9
|
-
program
|
|
10
|
-
.command('adoption')
|
|
11
|
-
.description('Scan @spark-ui adoption for .tsx files with given imports')
|
|
12
|
-
.option('-c, --configuration <config>', 'configuration file route')
|
|
13
|
-
.option('-o, --output <output>', 'output file route')
|
|
14
|
-
.option('-v, --verbose', 'output log information', false)
|
|
15
|
-
.option('-d, --details', 'output information about each match', config.details)
|
|
16
|
-
.option('-s, --sort <sort>', 'sort results (count or alphabetical)', config.sort)
|
|
17
|
-
.option('-dir, --directory <directory>', 'directory to parse', config.directory)
|
|
18
|
-
.option('-ext, --extensions <extensions...>', 'file extensions to parse', config.extensions)
|
|
19
|
-
.option('-i, --imports <imports...>', 'import patterns to identify', config.imports)
|
|
20
|
-
.action(adoption)
|
|
21
|
-
|
|
22
|
-
program.parse(process.argv)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export default () => `MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Leboncoin.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
`
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export default ({ name }) => `# ${name
|
|
2
|
-
.split('-')
|
|
3
|
-
.map(str => str.charAt(0).toUpperCase() + str.slice(1))
|
|
4
|
-
.join(' ')}
|
|
5
|
-
> @spark-ui/${name}
|
|
6
|
-
|
|
7
|
-
[](https://sparkui.vercel.app/?path=/docs/components-${name
|
|
8
|
-
.split('-')
|
|
9
|
-
.join('')}--docs)
|
|
10
|
-
[](https://github.com/leboncoin/spark-web/issues/new?&projects=4&template=bug-report.yml&assignees=&labels=Component,Component%3A%20${name})
|
|
11
|
-
[](https://www.npmjs.com/package/@spark-ui/${name})
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
This package is part of the [\`@spark-ui\`](https://github.com/leboncoin/spark-web) react-js user interface component library project.
|
|
15
|
-
|
|
16
|
-
[](https://github.com/leboncoin/spark-web/issues?q=is%3Aopen+label%3AComponent%3A%20${name})
|
|
17
|
-
[](https://github.com/leboncoin/spark-web/blob/main/packages/components/${name}/LICENSE.md)
|
|
18
|
-
`
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export default ({ name, description }) => `{
|
|
2
|
-
"name": "@spark-ui/${name}",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"description": "${description}",
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"keywords": [
|
|
9
|
-
"@spark-ui",
|
|
10
|
-
"react",
|
|
11
|
-
"component",
|
|
12
|
-
"accessible",
|
|
13
|
-
"accessibility",
|
|
14
|
-
"wai-aria",
|
|
15
|
-
"aria",
|
|
16
|
-
"a11y",
|
|
17
|
-
"${name}"
|
|
18
|
-
],
|
|
19
|
-
"main": "./dist/index.js",
|
|
20
|
-
"module": "./dist/index.mjs",
|
|
21
|
-
"types": "./dist/index.d.ts",
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "vite build"
|
|
24
|
-
},
|
|
25
|
-
"peerDependencies": {
|
|
26
|
-
"@spark-ui/theme-utils": "latest",
|
|
27
|
-
"react": "^19.0",
|
|
28
|
-
"react-dom": "^19.0",
|
|
29
|
-
"tailwindcss": "^4.0.0"
|
|
30
|
-
},
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "https://github.com/leboncoin/spark-web.git",
|
|
34
|
-
"directory": "packages/components/${name}"
|
|
35
|
-
},
|
|
36
|
-
"config": {
|
|
37
|
-
"title": "${name}",
|
|
38
|
-
"category": "components"
|
|
39
|
-
},
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://github.com/leboncoin/spark-web/issues?q=is%3Aopen+label%3A%22Component%3A+${name}%22"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://sparkui.vercel.app",
|
|
44
|
-
"license": "MIT"
|
|
45
|
-
}
|
|
46
|
-
`
|
package/src/scan/config.cjs
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
adoption: {
|
|
3
|
-
/**
|
|
4
|
-
* Shows (or not) details about each package adoption
|
|
5
|
-
* @param {boolean}
|
|
6
|
-
* @default false
|
|
7
|
-
*/
|
|
8
|
-
details: false,
|
|
9
|
-
/**
|
|
10
|
-
* Sorts packages list, based on count or alphabetical order
|
|
11
|
-
* @param {'count'|'alphabetical'}
|
|
12
|
-
* @default 'count'
|
|
13
|
-
*/
|
|
14
|
-
sort: 'count',
|
|
15
|
-
/**
|
|
16
|
-
* Packages pattern to be scanned
|
|
17
|
-
* @params {string[]}
|
|
18
|
-
*/
|
|
19
|
-
imports: ['@spark-ui'],
|
|
20
|
-
/**
|
|
21
|
-
* File extensions to be scanned
|
|
22
|
-
* @params {string[]}
|
|
23
|
-
*/
|
|
24
|
-
extensions: ['.tsx', '.ts'],
|
|
25
|
-
/**
|
|
26
|
-
* Directory to be scanned
|
|
27
|
-
* @param {string}
|
|
28
|
-
* @default '.''
|
|
29
|
-
*/
|
|
30
|
-
directory: '.',
|
|
31
|
-
/**
|
|
32
|
-
* Output file path to export the results
|
|
33
|
-
* @param {string}
|
|
34
|
-
*/
|
|
35
|
-
output: null,
|
|
36
|
-
},
|
|
37
|
-
}
|
package/src/scan/index.mjs
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/* eslint-disable complexity */
|
|
2
|
-
import * as process from 'node:process'
|
|
3
|
-
|
|
4
|
-
import { existsSync, mkdirSync, writeFileSync } from 'fs'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
|
|
7
|
-
import { Logger } from '../core/index.mjs'
|
|
8
|
-
import defaultConfig from './config.cjs'
|
|
9
|
-
import { loadConfig } from './loadConfig.mjs'
|
|
10
|
-
import { scanCallback } from './scanCallback.mjs'
|
|
11
|
-
import { scanDirectories } from './utils/index.mjs'
|
|
12
|
-
|
|
13
|
-
export async function adoption(options = {}) {
|
|
14
|
-
const { configuration, ...optionsConfig } = options
|
|
15
|
-
|
|
16
|
-
const logger = new Logger({ verbose: optionsConfig.verbose })
|
|
17
|
-
let config = await loadConfig(configuration, { logger })
|
|
18
|
-
|
|
19
|
-
config = {
|
|
20
|
-
adoption: {
|
|
21
|
-
...config.adoption,
|
|
22
|
-
...optionsConfig,
|
|
23
|
-
imports: optionsConfig.imports || config.adoption.imports,
|
|
24
|
-
extensions: optionsConfig.extensions || config.adoption.extensions,
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let importCount = 0
|
|
29
|
-
const importResults = {}
|
|
30
|
-
let importsUsed = {}
|
|
31
|
-
let importsCount = {}
|
|
32
|
-
|
|
33
|
-
const { details, directory, extensions, imports, sort, output } = config.adoption
|
|
34
|
-
|
|
35
|
-
imports.forEach(moduleName => {
|
|
36
|
-
logger.info(`ℹ️ Scanning adoption for ${moduleName}`)
|
|
37
|
-
const directoryPath = path.join(process.cwd(), directory)
|
|
38
|
-
|
|
39
|
-
const response = scanDirectories(directoryPath, moduleName, extensions, scanCallback, {
|
|
40
|
-
importCount,
|
|
41
|
-
importResults,
|
|
42
|
-
importsUsed,
|
|
43
|
-
importsCount,
|
|
44
|
-
})
|
|
45
|
-
if (importCount !== response.importCount) {
|
|
46
|
-
logger.success(
|
|
47
|
-
`🎉 Found ${response.importCount - importCount} imports with "${moduleName}" modules across directory ${directoryPath}.`
|
|
48
|
-
)
|
|
49
|
-
} else {
|
|
50
|
-
logger.warning(
|
|
51
|
-
`⚠️ No files found with "${moduleName}" imports across directory ${directoryPath}.`
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
importCount = response.importCount
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
// Sort importsUsed by alphabet
|
|
58
|
-
if (sort === 'alphabetical') {
|
|
59
|
-
importsUsed = Object.fromEntries(
|
|
60
|
-
Object.entries(importsUsed)
|
|
61
|
-
.sort(([pkgNameA], [pkgNameB]) => pkgNameA.localeCompare(pkgNameB))
|
|
62
|
-
.map(([pkgName, content]) => {
|
|
63
|
-
return [
|
|
64
|
-
pkgName,
|
|
65
|
-
{
|
|
66
|
-
default: Object.fromEntries(
|
|
67
|
-
Object.entries(content.default).sort(([a], [b]) => a.localeCompare(b))
|
|
68
|
-
),
|
|
69
|
-
named: Object.fromEntries(
|
|
70
|
-
Object.entries(content.named).sort(([a], [b]) => a.localeCompare(b))
|
|
71
|
-
),
|
|
72
|
-
importsCount: content.importsCount,
|
|
73
|
-
},
|
|
74
|
-
]
|
|
75
|
-
})
|
|
76
|
-
)
|
|
77
|
-
} else if (sort === 'count') {
|
|
78
|
-
// Sort importsUsed by most used
|
|
79
|
-
importsUsed = Object.fromEntries(
|
|
80
|
-
Object.entries(importsUsed)
|
|
81
|
-
.sort(([, contentA], [, contentB]) => contentB.importsCount - contentA.importsCount)
|
|
82
|
-
.map(([pkgName, content]) => {
|
|
83
|
-
return [
|
|
84
|
-
pkgName,
|
|
85
|
-
{
|
|
86
|
-
default: Object.fromEntries(
|
|
87
|
-
Object.entries(content.default).sort(([, a], [, b]) => b - a)
|
|
88
|
-
),
|
|
89
|
-
named: Object.fromEntries(
|
|
90
|
-
Object.entries(content.named).sort(([, a], [, b]) => b - a)
|
|
91
|
-
),
|
|
92
|
-
importsCount: content.importsCount,
|
|
93
|
-
},
|
|
94
|
-
]
|
|
95
|
-
})
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
importsCount = Object.fromEntries(Object.entries(importsCount).sort(([, a], [, b]) => b - a))
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const result = Object.fromEntries(
|
|
102
|
-
Object.entries(importsUsed).map(([pkgName, value]) => [
|
|
103
|
-
pkgName,
|
|
104
|
-
{ ...value, ...(details && { results: importResults[pkgName] }) },
|
|
105
|
-
])
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
if (output) {
|
|
109
|
-
try {
|
|
110
|
-
const { dir } = path.parse(path.join(process.cwd(), output))
|
|
111
|
-
if (!existsSync(dir)) {
|
|
112
|
-
mkdirSync(dir, { recursive: true })
|
|
113
|
-
}
|
|
114
|
-
writeFileSync(`${path.join(process.cwd(), output)}`, JSON.stringify(result, null, 2))
|
|
115
|
-
} catch (error) {
|
|
116
|
-
logger.error(`💥 Error writing file: ${error}`)
|
|
117
|
-
process.exit(1)
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
logger.force().info(JSON.stringify(result, null, 2))
|
|
121
|
-
process.exit(0)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export const config = { ...defaultConfig }
|
package/src/scan/index.test.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
|
|
3
|
-
import fse from 'fs-extra'
|
|
4
|
-
import { fileURLToPath } from 'url'
|
|
5
|
-
import { beforeEach, describe, expect, it } from 'vitest'
|
|
6
|
-
|
|
7
|
-
import cmd, { ENTER } from '../../test/utils/cmd'
|
|
8
|
-
|
|
9
|
-
const __dirname = fileURLToPath(import.meta.url)
|
|
10
|
-
const cliPath = path.join(__dirname, '../../../bin/spark.mjs')
|
|
11
|
-
|
|
12
|
-
const cliProcess = cmd.create(cliPath)
|
|
13
|
-
|
|
14
|
-
describe('CLI `spark scan`', () => {
|
|
15
|
-
const OUTPUT_FILE = 'report.json'
|
|
16
|
-
|
|
17
|
-
const EXT_CONFIG_FILE_PATH = 'packages/utils/cli/test/stubs/scanConfigWithCustomExtensions.js'
|
|
18
|
-
const SORT_CONFIG_FILE_PATH = 'packages/utils/cli/test/stubs/scanConfigWithCustomSorting.js'
|
|
19
|
-
const DETAILED_OUTPUT_CONFIG_FILE_PATH =
|
|
20
|
-
'packages/utils/cli/test/stubs/scanConfigWithDetailedOutput.js'
|
|
21
|
-
const IMPORTS_CONFIG_FILE_PATH = 'packages/utils/cli/test/stubs/scanConfigWithCustomImports.js'
|
|
22
|
-
|
|
23
|
-
const adoptionCommand = async (args: string[] = []) => {
|
|
24
|
-
/**
|
|
25
|
-
* We define here some common options such as custom directory (for performance reasons).
|
|
26
|
-
*/
|
|
27
|
-
return await cliProcess.execute(
|
|
28
|
-
['scan', 'adoption', '-dir', './packages/components', ...args],
|
|
29
|
-
[ENTER]
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
if (fse.existsSync(OUTPUT_FILE)) fse.removeSync(OUTPUT_FILE)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
afterAll(() => {
|
|
38
|
-
if (fse.existsSync(OUTPUT_FILE)) fse.removeSync(OUTPUT_FILE)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe('Adoption (components adoption)', () => {
|
|
42
|
-
it('should execute command with default config', async () => {
|
|
43
|
-
const response = await adoptionCommand(['-v'])
|
|
44
|
-
|
|
45
|
-
expect(response).toMatch(/Loading default configuration/i)
|
|
46
|
-
expect(response).toMatch(/Scanning adoption for @spark-ui/i)
|
|
47
|
-
|
|
48
|
-
expect(response).toMatch(/Found/i)
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* With no output option the adoption report will be simply logged
|
|
52
|
-
*/
|
|
53
|
-
expect(
|
|
54
|
-
response.filter(
|
|
55
|
-
(entry: string | Record<string, unknown>) =>
|
|
56
|
-
entry['@spark-ui/button' as keyof typeof entry]
|
|
57
|
-
)
|
|
58
|
-
).toBeDefined()
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
describe('Results details with output', () => {
|
|
62
|
-
it('should output results with packages details from command option', async () => {
|
|
63
|
-
await adoptionCommand(['-d', '-o', OUTPUT_FILE])
|
|
64
|
-
|
|
65
|
-
expect(fse.pathExistsSync(OUTPUT_FILE)).toBe(true)
|
|
66
|
-
|
|
67
|
-
const outputContent = JSON.parse(fse.readFileSync(OUTPUT_FILE, 'utf-8'))
|
|
68
|
-
|
|
69
|
-
Object.keys(outputContent).forEach(outputItem => {
|
|
70
|
-
expect(Object.keys(outputContent[outputItem]).includes('results')).toBeTruthy()
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('should output results with packages details from configuration file', async () => {
|
|
75
|
-
await adoptionCommand(['-c', DETAILED_OUTPUT_CONFIG_FILE_PATH])
|
|
76
|
-
|
|
77
|
-
expect(fse.pathExistsSync(OUTPUT_FILE)).toBe(true)
|
|
78
|
-
|
|
79
|
-
const outputContent = JSON.parse(fse.readFileSync(OUTPUT_FILE, 'utf-8'))
|
|
80
|
-
|
|
81
|
-
Object.keys(outputContent).forEach(outputItem => {
|
|
82
|
-
expect(Object.keys(outputContent[outputItem]).includes('results')).toBeTruthy()
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
describe('Results sorting', () => {
|
|
88
|
-
it('should sort results alphabetically from command option', async () => {
|
|
89
|
-
const response = await adoptionCommand(['-v', '-s', 'alphabetical'])
|
|
90
|
-
|
|
91
|
-
expect(response).toMatch(/Loading default configuration/i)
|
|
92
|
-
expect(response).toMatch(/Scanning adoption for @spark-ui/i)
|
|
93
|
-
|
|
94
|
-
const pkgList = Object.keys(
|
|
95
|
-
response.filter((entry: string | Record<string, unknown>) => typeof entry !== 'string')[0]
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
const sparkInputIndex = pkgList.indexOf('@spark-ui/input')
|
|
99
|
-
const sparkFormFieldIndex = pkgList.indexOf('@spark-ui/form-field')
|
|
100
|
-
|
|
101
|
-
expect(sparkInputIndex).toBeGreaterThan(sparkFormFieldIndex)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should sort results alphabetically from configuration file', async () => {
|
|
105
|
-
const response = await adoptionCommand(['-v', '-c', SORT_CONFIG_FILE_PATH])
|
|
106
|
-
|
|
107
|
-
expect(response).toMatch(/Loading spark-ui custom configuration file/i)
|
|
108
|
-
expect(response).toMatch(/Scanning adoption for @spark-ui/i)
|
|
109
|
-
|
|
110
|
-
const pkgList = Object.keys(
|
|
111
|
-
response.filter((entry: string | Record<string, unknown>) => typeof entry !== 'string')[0]
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
const sparkInputIndex = pkgList.indexOf('@spark-ui/input')
|
|
115
|
-
const sparkFormFieldIndex = pkgList.indexOf('@spark-ui/form-field')
|
|
116
|
-
|
|
117
|
-
expect(sparkInputIndex).toBeGreaterThan(sparkFormFieldIndex)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('File extensions', () => {
|
|
122
|
-
it('should execute command with custom extensions from command option', async () => {
|
|
123
|
-
const response = await adoptionCommand(['-v', '-ext', '.css'])
|
|
124
|
-
|
|
125
|
-
expect(response).toMatch(/Loading default configuration/i)
|
|
126
|
-
expect(response).toMatch(/Scanning adoption for @spark-ui/i)
|
|
127
|
-
|
|
128
|
-
expect(response).toMatch(/No files found with "@spark-ui" imports across directory/i)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('should execute command with custom extensions from configuration file', async () => {
|
|
132
|
-
const response = await adoptionCommand(['-v', '-c', EXT_CONFIG_FILE_PATH])
|
|
133
|
-
|
|
134
|
-
expect(response).toMatch(/Loading spark-ui custom configuration file/i)
|
|
135
|
-
expect(response).toMatch(/Scanning adoption for @spark-ui/i)
|
|
136
|
-
|
|
137
|
-
expect(response).toMatch(/No files found with "@spark-ui" imports across directory/i)
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
describe('Imports patterns', () => {
|
|
142
|
-
it('should execute command for custom import patterns from command option', async () => {
|
|
143
|
-
const response = await adoptionCommand(['-v', '-i', '@react-aria', '@react-stately'])
|
|
144
|
-
|
|
145
|
-
expect(response).toMatch(/Scanning adoption for @react-aria/i)
|
|
146
|
-
expect(response).toMatch(/Scanning adoption for @react-stately/i)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('should execute command for custom import patterns from configuration file', async () => {
|
|
150
|
-
const response = await adoptionCommand(['-v', '-c', IMPORTS_CONFIG_FILE_PATH])
|
|
151
|
-
|
|
152
|
-
expect(response).toMatch(/Scanning adoption for @react-aria/i)
|
|
153
|
-
expect(response).toMatch(/Scanning adoption for @react-stately/i)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
})
|
package/src/scan/loadConfig.mjs
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
|
|
3
|
-
import { existsSync } from 'fs'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
|
|
6
|
-
import defaultConfig from './config.cjs'
|
|
7
|
-
|
|
8
|
-
export async function loadConfig(configuration, { logger }) {
|
|
9
|
-
try {
|
|
10
|
-
const configFileRoute = path.join(process.cwd(), configuration || '.spark-ui.cjs')
|
|
11
|
-
|
|
12
|
-
if (existsSync(configFileRoute)) {
|
|
13
|
-
logger.info('ℹ️ Loading spark-ui custom configuration file')
|
|
14
|
-
const { default: customConfig } = await import(configFileRoute)
|
|
15
|
-
|
|
16
|
-
const config = {
|
|
17
|
-
adoption: {
|
|
18
|
-
...defaultConfig.adoption,
|
|
19
|
-
...customConfig.adoption,
|
|
20
|
-
imports: customConfig.adoption.imports || defaultConfig.adoption.imports,
|
|
21
|
-
extensions: customConfig.adoption.extensions || defaultConfig.adoption.extensions,
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return config
|
|
26
|
-
} else {
|
|
27
|
-
if (configuration) {
|
|
28
|
-
logger.error('⚠️ No custom configuration file found:', configFileRoute)
|
|
29
|
-
process.exit(1)
|
|
30
|
-
}
|
|
31
|
-
logger.warning('⚠️ No custom configuration file found')
|
|
32
|
-
logger.info('ℹ️ Loading default configuration')
|
|
33
|
-
|
|
34
|
-
return { ...defaultConfig }
|
|
35
|
-
}
|
|
36
|
-
} catch (error) {
|
|
37
|
-
logger.error('💥 Something went wrong loading the custom configuration file', error)
|
|
38
|
-
|
|
39
|
-
return { ...defaultConfig }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import extractImports from './utils/extract-imports.mjs'
|
|
2
|
-
|
|
3
|
-
export function scanCallback(
|
|
4
|
-
f,
|
|
5
|
-
moduleName,
|
|
6
|
-
{ importCount, importResults, importsUsed, importsCount }
|
|
7
|
-
) {
|
|
8
|
-
const response = { importCount, importResults, importsUsed, importsCount }
|
|
9
|
-
if (!f.fileContent) return response
|
|
10
|
-
|
|
11
|
-
const imports = extractImports(f.filePath, moduleName)
|
|
12
|
-
|
|
13
|
-
Object.entries(imports).forEach(([key, importDeclarations]) => {
|
|
14
|
-
const moduleName = key.split('/').splice(0, 2).join('/')
|
|
15
|
-
importDeclarations.forEach(importDeclaration => {
|
|
16
|
-
const statement = importDeclaration.getText()
|
|
17
|
-
const defaultImport = importDeclaration.getDefaultImport()?.getText() || null
|
|
18
|
-
const namedImports = importDeclaration.getNamedImports().map(n => n.getText())
|
|
19
|
-
|
|
20
|
-
if (!importResults[moduleName]) {
|
|
21
|
-
importResults[moduleName] = []
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
importResults[moduleName].push({
|
|
25
|
-
path: f.filePath,
|
|
26
|
-
statement,
|
|
27
|
-
hasDefault: !!defaultImport,
|
|
28
|
-
hasNamed: !!namedImports.length,
|
|
29
|
-
defaultImport,
|
|
30
|
-
namedImports,
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
if (!importsUsed[moduleName]) {
|
|
34
|
-
importsUsed[moduleName] = {
|
|
35
|
-
default: {},
|
|
36
|
-
named: {},
|
|
37
|
-
importsCount: 0,
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (defaultImport) {
|
|
42
|
-
importsUsed[moduleName].default[defaultImport] =
|
|
43
|
-
importsUsed[moduleName].default[defaultImport] + 1 || 1
|
|
44
|
-
importsUsed.importsCount = importsCount[defaultImport] + 1
|
|
45
|
-
|
|
46
|
-
importsCount[defaultImport] = importsCount[defaultImport] + 1 || 1
|
|
47
|
-
response.importCount++
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (namedImports.length) {
|
|
51
|
-
namedImports.forEach(n => {
|
|
52
|
-
importsUsed[moduleName].named[n] = importsUsed[moduleName].named[n] + 1 || 1
|
|
53
|
-
importsUsed[moduleName].importsCount = importsUsed[moduleName].importsCount + 1
|
|
54
|
-
importsCount[n] = importsCount[n] + 1 || 1
|
|
55
|
-
response.importCount++
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
return response
|
|
62
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Project } from 'ts-morph'
|
|
2
|
-
|
|
3
|
-
export function extractImports(filePath, requestedModuleName) {
|
|
4
|
-
const project = new Project()
|
|
5
|
-
const sourceFile = project.addSourceFileAtPath(filePath)
|
|
6
|
-
|
|
7
|
-
const importStatements = {}
|
|
8
|
-
|
|
9
|
-
const importNodes = sourceFile.getImportDeclarations()
|
|
10
|
-
|
|
11
|
-
importNodes
|
|
12
|
-
.filter(node => {
|
|
13
|
-
const moduleName = node.getModuleSpecifierValue()
|
|
14
|
-
|
|
15
|
-
return moduleName.includes(requestedModuleName)
|
|
16
|
-
})
|
|
17
|
-
.forEach(node => {
|
|
18
|
-
const moduleName = node.getModuleSpecifierValue()
|
|
19
|
-
if (!importStatements[moduleName]) {
|
|
20
|
-
importStatements[moduleName] = []
|
|
21
|
-
}
|
|
22
|
-
importStatements[moduleName].push(node)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
return importStatements
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default extractImports
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Check if a file contains an import from a given import name.
|
|
5
|
-
* @param filePath The path to the file to check.
|
|
6
|
-
* @param importName The name of the import to check for.
|
|
7
|
-
* @returns Whether the file contains an import from the given import name.
|
|
8
|
-
*/
|
|
9
|
-
export function fileContainsImport(filePath, importName) {
|
|
10
|
-
const fileContent = fs.readFileSync(filePath, 'utf8')
|
|
11
|
-
|
|
12
|
-
if (new RegExp(`import.*from\\s+["']${importName}.*["']`, 'm').test(fileContent)) {
|
|
13
|
-
return { filePath, fileContent }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return { filePath }
|
|
17
|
-
}
|
|
File without changes
|
package/src/scan/utils/index.mjs
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
|
|
4
|
-
import { fileContainsImport } from './file-contains-import.mjs'
|
|
5
|
-
|
|
6
|
-
export function scanDirectories(
|
|
7
|
-
directoryPath,
|
|
8
|
-
importName,
|
|
9
|
-
extensions,
|
|
10
|
-
scanningCallback,
|
|
11
|
-
{ importCount, importResults, importsUsed, importsCount }
|
|
12
|
-
) {
|
|
13
|
-
const files = fs.readdirSync(directoryPath)
|
|
14
|
-
|
|
15
|
-
let response = {
|
|
16
|
-
importCount,
|
|
17
|
-
importResults,
|
|
18
|
-
importsUsed,
|
|
19
|
-
importsCount,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
for (const file of files) {
|
|
23
|
-
const filePath = path.join(directoryPath, file)
|
|
24
|
-
const stats = fs.statSync(filePath)
|
|
25
|
-
|
|
26
|
-
if (stats.isDirectory()) {
|
|
27
|
-
response = scanDirectories(filePath, importName, extensions, scanningCallback, response)
|
|
28
|
-
} else if (stats.isFile() && extensions.includes(path.extname(filePath))) {
|
|
29
|
-
const f = fileContainsImport(filePath, importName)
|
|
30
|
-
|
|
31
|
-
if (f) {
|
|
32
|
-
response = scanningCallback(
|
|
33
|
-
{
|
|
34
|
-
filePath: f.filePath,
|
|
35
|
-
fileContent: f.fileContent,
|
|
36
|
-
},
|
|
37
|
-
importName,
|
|
38
|
-
response
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return response
|
|
45
|
-
}
|
/package/src/generate/templates/component/{src/[Component.doc.mdx].js → [Component.doc.mdx].js}
RENAMED
|
File without changes
|
|
File without changes
|
/package/src/generate/templates/component/{src/[Component.styles.ts].js → [Component.styles.ts].js}
RENAMED
|
File without changes
|
/package/src/generate/templates/component/{src/[Component.test.tsx].js → [Component.test.tsx].js}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|