@spark-ui/cli-utils 9.4.8 → 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.
Files changed (29) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/bin/spark.mjs +0 -1
  3. package/package.json +3 -4
  4. package/src/index.doc.mdx +0 -70
  5. package/test/spark-generate.test.ts +6 -10
  6. package/bin/spark-scan.mjs +0 -22
  7. package/src/generate/templates/component/[.npmignore].js +0 -3
  8. package/src/generate/templates/component/[LICENSE.md].js +0 -22
  9. package/src/generate/templates/component/[README.md].js +0 -18
  10. package/src/generate/templates/component/[package.json].js +0 -46
  11. package/src/generate/templates/component/[tsconfig.json].js +0 -5
  12. package/src/generate/templates/component/[vite.config.ts].js +0 -7
  13. package/src/scan/config.cjs +0 -37
  14. package/src/scan/index.mjs +0 -125
  15. package/src/scan/index.test.ts +0 -157
  16. package/src/scan/loadConfig.mjs +0 -41
  17. package/src/scan/scanCallback.mjs +0 -62
  18. package/src/scan/utils/extract-imports.mjs +0 -28
  19. package/src/scan/utils/file-contains-import.mjs +0 -17
  20. package/src/scan/utils/get-csv.mjs +0 -0
  21. package/src/scan/utils/get-formated-timestamp.mjs +0 -7
  22. package/src/scan/utils/index.mjs +0 -4
  23. package/src/scan/utils/scan-directories.mjs +0 -45
  24. /package/src/generate/templates/component/{src/[Component.doc.mdx].js → [Component.doc.mdx].js} +0 -0
  25. /package/src/generate/templates/component/{src/[Component.stories.tsx].js → [Component.stories.tsx].js} +0 -0
  26. /package/src/generate/templates/component/{src/[Component.styles.ts].js → [Component.styles.ts].js} +0 -0
  27. /package/src/generate/templates/component/{src/[Component.test.tsx].js → [Component.test.tsx].js} +0 -0
  28. /package/src/generate/templates/component/{src/[Component.tsx].js → [Component.tsx].js} +0 -0
  29. /package/src/generate/templates/component/{src/[index.ts].js → [index.ts].js} +0 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
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
+
6
17
  ## [9.4.8](https://github.com/leboncoin/spark-web/compare/v9.4.7...v9.4.8) (2025-03-19)
7
18
 
8
19
  **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": "9.4.8",
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": "3a8f5a2f87c650895c2475a9cc34d511eba426e4"
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
- '/.npmignore',
38
- '/package.json',
39
- '/src/index.ts',
40
- '/src/Bar.styles.ts',
41
- '/src/Bar.tsx',
42
- '/vite.config.ts',
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)
@@ -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,3 +0,0 @@
1
- export default () => `src
2
- **/*.stories.*
3
- `
@@ -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
- [![storybook](https://img.shields.io/badge/storybook-black?logo=storybook)](https://sparkui.vercel.app/?path=/docs/components-${name
8
- .split('-')
9
- .join('')}--docs)
10
- [![issue](https://img.shields.io/badge/report%20a%20bug-black?logo=openbugbounty&logoColor=red)](https://github.com/leboncoin/spark-web/issues/new?&projects=4&template=bug-report.yml&assignees=&labels=Component,Component%3A%20${name})
11
- [![npm](https://img.shields.io/npm/dt/%40spark-ui/${name}?logo=npm&labelColor=black)](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
- [![Issues open](https://img.shields.io/github/issues-search/leboncoin/spark-web?query=is%3Aopen%20label%3A%22Component%3A%20${name}%22&logo=openbugbounty&logoColor=red&label=issues%20open&color=red)](https://github.com/leboncoin/spark-web/issues?q=is%3Aopen+label%3AComponent%3A%20${name})
17
- [![NPM](https://img.shields.io/npm/l/%40spark-ui%2F${name})](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
- `
@@ -1,5 +0,0 @@
1
- export default () => `{
2
- "extends": "../../../tsconfig.json",
3
- "include": ["src/**/*", "../../../global.d.ts"]
4
- }
5
- `
@@ -1,7 +0,0 @@
1
- export default () => `import path from 'path'
2
- import { getComponentConfiguration } from '../../../config/index'
3
-
4
- const { name } = require(path.resolve(__dirname, 'package.json'))
5
-
6
- export default getComponentConfiguration(process.cwd(), name)
7
- `
@@ -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
- }
@@ -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 }
@@ -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
- })
@@ -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
@@ -1,7 +0,0 @@
1
- export function getFormatedTimestamp() {
2
- const d = new Date()
3
- const date = d.toISOString().split('T')[0]
4
- const time = d.toTimeString().split(' ')[0].replace(/:/g, '-')
5
-
6
- return `${date} ${time}`
7
- }
@@ -1,4 +0,0 @@
1
- export { extractImports } from './extract-imports.mjs'
2
- export { fileContainsImport } from './file-contains-import.mjs'
3
- export { getFormatedTimestamp } from './get-formated-timestamp.mjs'
4
- export { scanDirectories } from './scan-directories.mjs'
@@ -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
- }