@nathievzm/lumi 1.0.1 → 1.1.1
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/.oxfmtrc.json +39 -39
- package/.oxlintrc.json +36 -35
- package/.vscode/launch.json +36 -36
- package/CHANGELOG.md +30 -0
- package/README.md +22 -15
- package/package.json +5 -5
- package/src/env.d.ts +32 -28
- package/src/index.ts +92 -73
- package/src/lib/args.ts +88 -80
- package/src/lib/folder.ts +70 -69
- package/src/lib/image.ts +164 -162
- package/src/lib/prompt.ts +86 -85
- package/src/lib/error.ts +0 -21
package/.oxfmtrc.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
-
"arrowParens": "avoid",
|
|
4
|
-
"bracketSameLine": true,
|
|
5
|
-
"bracketSpacing": true,
|
|
6
|
-
"embeddedLanguageFormatting": "auto",
|
|
7
|
-
"endOfLine": "lf",
|
|
8
|
-
"insertFinalNewline": true,
|
|
9
|
-
"jsdoc": {
|
|
10
|
-
"bracketSpacing": true,
|
|
11
|
-
"lineWrappingStyle": "balance",
|
|
12
|
-
"separateReturnsFromParam": true,
|
|
13
|
-
"capitalizeDescriptions": true,
|
|
14
|
-
"descriptionWithDot": false,
|
|
15
|
-
"commentLineStrategy": "multiline"
|
|
16
|
-
},
|
|
17
|
-
"objectWrap": "preserve",
|
|
18
|
-
"printWidth": 120,
|
|
19
|
-
"proseWrap": "always",
|
|
20
|
-
"quoteProps": "consistent",
|
|
21
|
-
"semi": false,
|
|
22
|
-
"singleQuote": true,
|
|
23
|
-
"sortImports": {
|
|
24
|
-
"newlinesBetween": true
|
|
25
|
-
},
|
|
26
|
-
"sortPackageJson": {
|
|
27
|
-
"sortScripts": true
|
|
28
|
-
},
|
|
29
|
-
"tabWidth": 4,
|
|
30
|
-
"trailingComma": "none",
|
|
31
|
-
"overrides": [
|
|
32
|
-
{
|
|
33
|
-
"files": ["*.yml", "*.yaml"],
|
|
34
|
-
"options": {
|
|
35
|
-
"tabWidth": 2
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
+
"arrowParens": "avoid",
|
|
4
|
+
"bracketSameLine": true,
|
|
5
|
+
"bracketSpacing": true,
|
|
6
|
+
"embeddedLanguageFormatting": "auto",
|
|
7
|
+
"endOfLine": "lf",
|
|
8
|
+
"insertFinalNewline": true,
|
|
9
|
+
"jsdoc": {
|
|
10
|
+
"bracketSpacing": true,
|
|
11
|
+
"lineWrappingStyle": "balance",
|
|
12
|
+
"separateReturnsFromParam": true,
|
|
13
|
+
"capitalizeDescriptions": true,
|
|
14
|
+
"descriptionWithDot": false,
|
|
15
|
+
"commentLineStrategy": "multiline"
|
|
16
|
+
},
|
|
17
|
+
"objectWrap": "preserve",
|
|
18
|
+
"printWidth": 120,
|
|
19
|
+
"proseWrap": "always",
|
|
20
|
+
"quoteProps": "consistent",
|
|
21
|
+
"semi": false,
|
|
22
|
+
"singleQuote": true,
|
|
23
|
+
"sortImports": {
|
|
24
|
+
"newlinesBetween": true
|
|
25
|
+
},
|
|
26
|
+
"sortPackageJson": {
|
|
27
|
+
"sortScripts": true
|
|
28
|
+
},
|
|
29
|
+
"tabWidth": 4,
|
|
30
|
+
"trailingComma": "none",
|
|
31
|
+
"overrides": [
|
|
32
|
+
{
|
|
33
|
+
"files": ["*.yml", "*.yaml"],
|
|
34
|
+
"options": {
|
|
35
|
+
"tabWidth": 2
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
package/.oxlintrc.json
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
-
"plugins": ["typescript", "unicorn", "oxc", "eslint", "import", "jsdoc", "node", "promise"],
|
|
4
|
-
"categories": {
|
|
5
|
-
"correctness": "error",
|
|
6
|
-
"suspicious": "error",
|
|
7
|
-
"pedantic": "warn",
|
|
8
|
-
"perf": "warn",
|
|
9
|
-
"style": "warn",
|
|
10
|
-
"restriction": "off",
|
|
11
|
-
"nursery": "off"
|
|
12
|
-
},
|
|
13
|
-
"rules": {
|
|
14
|
-
"no-nodejs-modules": "off",
|
|
15
|
-
"no-magic-numbers": "off",
|
|
16
|
-
"no-ternary": "off",
|
|
17
|
-
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
|
|
18
|
-
"consistent-type-specifier-style": ["warn", "prefer-inline"],
|
|
19
|
-
"prefer-default-export": "off",
|
|
20
|
-
"no-named-export": "off",
|
|
21
|
-
"prefer-readonly-parameter-types": ["warn", { "ignoreInferredTypes": true }],
|
|
22
|
-
"group-exports": "off",
|
|
23
|
-
"unicorn/no-useless-undefined": "off",
|
|
24
|
-
"no-continue": "off",
|
|
25
|
-
"require-param-type": "off",
|
|
26
|
-
"require-returns-type": "off"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["typescript", "unicorn", "oxc", "eslint", "import", "jsdoc", "node", "promise"],
|
|
4
|
+
"categories": {
|
|
5
|
+
"correctness": "error",
|
|
6
|
+
"suspicious": "error",
|
|
7
|
+
"pedantic": "warn",
|
|
8
|
+
"perf": "warn",
|
|
9
|
+
"style": "warn",
|
|
10
|
+
"restriction": "off",
|
|
11
|
+
"nursery": "off"
|
|
12
|
+
},
|
|
13
|
+
"rules": {
|
|
14
|
+
"no-nodejs-modules": "off",
|
|
15
|
+
"no-magic-numbers": "off",
|
|
16
|
+
"no-ternary": "off",
|
|
17
|
+
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
|
|
18
|
+
"consistent-type-specifier-style": ["warn", "prefer-inline"],
|
|
19
|
+
"prefer-default-export": "off",
|
|
20
|
+
"no-named-export": "off",
|
|
21
|
+
"prefer-readonly-parameter-types": ["warn", { "ignoreInferredTypes": true }],
|
|
22
|
+
"group-exports": "off",
|
|
23
|
+
"unicorn/no-useless-undefined": "off",
|
|
24
|
+
"no-continue": "off",
|
|
25
|
+
"require-param-type": "off",
|
|
26
|
+
"require-returns-type": "off",
|
|
27
|
+
"max-dependencies": ["warn", { "max": 12 }]
|
|
28
|
+
},
|
|
29
|
+
"env": {
|
|
30
|
+
"builtin": true
|
|
31
|
+
},
|
|
32
|
+
"options": {
|
|
33
|
+
"typeAware": true,
|
|
34
|
+
"typeCheck": true
|
|
35
|
+
}
|
|
36
|
+
}
|
package/.vscode/launch.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Use IntelliSense to learn about possible attributes.
|
|
3
|
-
// Hover to view descriptions of existing attributes.
|
|
4
|
-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"type": "bun",
|
|
9
|
-
"internalConsoleOptions": "neverOpen",
|
|
10
|
-
"request": "launch",
|
|
11
|
-
"name": "Debug File",
|
|
12
|
-
"program": "${file}",
|
|
13
|
-
"cwd": "${workspaceFolder}",
|
|
14
|
-
"stopOnEntry": false,
|
|
15
|
-
"watchMode": false
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"type": "bun",
|
|
19
|
-
"internalConsoleOptions": "neverOpen",
|
|
20
|
-
"request": "launch",
|
|
21
|
-
"name": "Run File",
|
|
22
|
-
"program": "${file}",
|
|
23
|
-
"cwd": "${workspaceFolder}",
|
|
24
|
-
"noDebug": true,
|
|
25
|
-
"watchMode": false
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"type": "bun",
|
|
29
|
-
"internalConsoleOptions": "neverOpen",
|
|
30
|
-
"request": "attach",
|
|
31
|
-
"name": "Attach Bun",
|
|
32
|
-
"url": "ws://localhost:6499/",
|
|
33
|
-
"stopOnEntry": false
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "bun",
|
|
9
|
+
"internalConsoleOptions": "neverOpen",
|
|
10
|
+
"request": "launch",
|
|
11
|
+
"name": "Debug File",
|
|
12
|
+
"program": "${file}",
|
|
13
|
+
"cwd": "${workspaceFolder}",
|
|
14
|
+
"stopOnEntry": false,
|
|
15
|
+
"watchMode": false
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "bun",
|
|
19
|
+
"internalConsoleOptions": "neverOpen",
|
|
20
|
+
"request": "launch",
|
|
21
|
+
"name": "Run File",
|
|
22
|
+
"program": "${file}",
|
|
23
|
+
"cwd": "${workspaceFolder}",
|
|
24
|
+
"noDebug": true,
|
|
25
|
+
"watchMode": false
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "bun",
|
|
29
|
+
"internalConsoleOptions": "neverOpen",
|
|
30
|
+
"request": "attach",
|
|
31
|
+
"name": "Attach Bun",
|
|
32
|
+
"url": "ws://localhost:6499/",
|
|
33
|
+
"stopOnEntry": false
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
## [1.1.1](https://github.com/nathievzm/lumi/compare/v1.1.0...v1.1.1) (2026-05-12)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- intro banner and use lightning emoji in outro message
|
|
6
|
+
([a3d4f33](https://github.com/nathievzm/lumi/commit/a3d4f3362d847fd8415027c9a73f6fc486dcd770))
|
|
7
|
+
|
|
8
|
+
# [1.1.0](https://github.com/nathievzm/lumi/compare/v1.0.1...v1.1.0) (2026-05-12)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
- add colors to logs ([#44](https://github.com/nathievzm/lumi/issues/44))
|
|
13
|
+
([2dcca2e](https://github.com/nathievzm/lumi/commit/2dcca2e3df40ac577ca5fdaeddbaedecd67d3256))
|
|
14
|
+
- add recursive arg to toggle the recursiveness when reading the input folder
|
|
15
|
+
([#25](https://github.com/nathievzm/lumi/issues/25))
|
|
16
|
+
([3644d34](https://github.com/nathievzm/lumi/commit/3644d346569aa40a2d6b7007ece64be6b787dc42))
|
|
17
|
+
- clear console on start and add a cute banner at the beginning
|
|
18
|
+
([1ad4ade](https://github.com/nathievzm/lumi/commit/1ad4adef1090224915b724374f0e537f371a93a7))
|
|
19
|
+
- **image, index:** make getImages function pure by removing any side effect
|
|
20
|
+
([addea55](https://github.com/nathievzm/lumi/commit/addea5542f6b923c36d093797d0b9387c199cfc3))
|
|
21
|
+
- **index:** replace spinnies with clack spinner for cleaner cli ([#42](https://github.com/nathievzm/lumi/issues/42))
|
|
22
|
+
([6c59c5a](https://github.com/nathievzm/lumi/commit/6c59c5a62af9d3ac13c94997f063842591f9a799))
|
|
23
|
+
- show the seconds that took to process the files in the result log ([#43](https://github.com/nathievzm/lumi/issues/43))
|
|
24
|
+
([439482e](https://github.com/nathievzm/lumi/commit/439482e075af4aaba70da2967743250456b35b20))
|
|
25
|
+
|
|
26
|
+
### Performance Improvements
|
|
27
|
+
|
|
28
|
+
- optimize path parsing by using extname and basename instead of parse
|
|
29
|
+
([962299f](https://github.com/nathievzm/lumi/commit/962299ffc519826d1c023275290525d92b7d4db2))
|
|
30
|
+
|
|
1
31
|
## [1.0.1](https://github.com/nathievzm/lumi/compare/v1.0.0...v1.0.1) (2026-05-10)
|
|
2
32
|
|
|
3
33
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A fast, interactive CLI tool for batch image processing. Resize, convert, and op
|
|
|
7
7
|
|
|
8
8
|
- **Batch Processing:** Process hundreds of images in seconds with high concurrency.
|
|
9
9
|
- **Interactive UI:** User-friendly prompts for missing configurations using `@clack/prompts`.
|
|
10
|
+
- **Recursive Processing:** Automatically finds all images in the input folder and its subdirectories.
|
|
10
11
|
- **Progress Tracking:** Real-time feedback with `spinnies` progress indicators.
|
|
11
12
|
- **Smart Resizing:** Automatically fits images while maintaining aspect ratio (`contain` fit).
|
|
12
13
|
- **Multi-Format Support:** Convert between all formats supported by Sharp (WebP, PNG, JPEG, GIF, AVIF, etc.).
|
|
@@ -38,9 +39,14 @@ lumi [options]
|
|
|
38
39
|
|
|
39
40
|
### Interactive Mode
|
|
40
41
|
|
|
41
|
-
If you run **lumi** without providing required flags (like
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
If you run **lumi** without providing required flags (like dimensions or output formats), it will guide you through the
|
|
43
|
+
configuration using cute, interactive prompts.
|
|
44
|
+
|
|
45
|
+
- **Dimensions:** You can enter a single number for square images (e.g., `1080`) or two numbers for custom dimensions
|
|
46
|
+
(e.g., `1920 1080`).
|
|
47
|
+
- **Formats:** Choose specific output formats for each unique extension found in your input!
|
|
48
|
+
- **Folders:** By default, **lumi** uses your current directory as the input and creates an `output` folder for the
|
|
49
|
+
results.
|
|
44
50
|
|
|
45
51
|
### CLI Options
|
|
46
52
|
|
|
@@ -50,15 +56,15 @@ You can bypass the prompts by providing the flags directly:
|
|
|
50
56
|
bunx @nathievzm/lumi -i ./my-vacation-pics -s 1080 -f .webp
|
|
51
57
|
```
|
|
52
58
|
|
|
53
|
-
| Flag | Shortcut | Description | Default
|
|
54
|
-
| :--------- | :------- | :---------------------------- |
|
|
55
|
-
| `--input` | `-i` | Input directory path | `INPUT_FOLDER`
|
|
56
|
-
| `--output` | `-o` | Output directory path | `OUTPUT_FOLDER`
|
|
57
|
-
| `--width` | `-w` | Target width in pixels | `WIDTH`
|
|
58
|
-
| `--height` | `-h` | Target height in pixels | `HEIGHT`
|
|
59
|
-
| `--size` | `-s` | Sets both width and height | -
|
|
60
|
-
| `--format` | `-f` | Output format (e.g., `.webp`) | `FORMAT`
|
|
61
|
-
| `--limit` | `-l` | Max concurrent operations | `LIMIT`
|
|
59
|
+
| Flag | Shortcut | Description | Default / Env |
|
|
60
|
+
| :--------- | :------- | :---------------------------- | :--------------------------- |
|
|
61
|
+
| `--input` | `-i` | Input directory path | `.` / `INPUT_FOLDER` |
|
|
62
|
+
| `--output` | `-o` | Output directory path | `./output` / `OUTPUT_FOLDER` |
|
|
63
|
+
| `--width` | `-w` | Target width in pixels | Prompt / `WIDTH` |
|
|
64
|
+
| `--height` | `-h` | Target height in pixels | Prompt / `HEIGHT` |
|
|
65
|
+
| `--size` | `-s` | Sets both width and height | - |
|
|
66
|
+
| `--format` | `-f` | Output format (e.g., `.webp`) | Prompt / `FORMAT` |
|
|
67
|
+
| `--limit` | `-l` | Max concurrent operations | `10` / `LIMIT` |
|
|
62
68
|
|
|
63
69
|
### 🌍 Environment Variables
|
|
64
70
|
|
|
@@ -97,11 +103,12 @@ bun install
|
|
|
97
103
|
|
|
98
104
|
- `bun start`: Run the application.
|
|
99
105
|
- `bun dev`: Start with hot reloading.
|
|
100
|
-
- `bun lint`: Check for code quality issues
|
|
106
|
+
- `bun lint`: Check for code quality issues using `oxlint`.
|
|
101
107
|
- `bun lint:fix`: Fix linting issues automatically.
|
|
102
|
-
- `bun fmt`: Format the codebase
|
|
108
|
+
- `bun fmt`: Format the codebase using `oxfmt`.
|
|
103
109
|
- `bun fmt:check`: Check for formatting issues.
|
|
104
|
-
- `bun
|
|
110
|
+
- `bun changelog`: Update the changelog.
|
|
111
|
+
- `bun release`: Release a new version with `bumpp` and update the changelog.
|
|
105
112
|
|
|
106
113
|
## 📄 License
|
|
107
114
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nathievzm/lumi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "a concurrent cli tool to resize and convert images using bun and sharp",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -51,21 +51,21 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@clack/prompts": "^1.3.0",
|
|
54
|
+
"@js-temporal/polyfill": "^0.5.1",
|
|
54
55
|
"image-extensions": "^1.1.0",
|
|
55
56
|
"p-limit": "^7.3.0",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
57
|
+
"picocolors": "^1.1.1",
|
|
58
|
+
"sharp": "^0.34.5"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@commitlint/cli": "^20.5.3",
|
|
61
62
|
"@commitlint/config-conventional": "^20.5.3",
|
|
62
63
|
"@types/bun": "latest",
|
|
63
|
-
"@types/spinnies": "^0.5.3",
|
|
64
64
|
"bumpp": "^11.1.0",
|
|
65
65
|
"conventional-changelog-cli": "^5.0.0",
|
|
66
66
|
"lefthook": "^2.1.6",
|
|
67
67
|
"oxfmt": "^0.47.0",
|
|
68
|
-
"oxlint": "^1.
|
|
68
|
+
"oxlint": "^1.64.0",
|
|
69
69
|
"oxlint-tsgolint": "^0.22.1",
|
|
70
70
|
"typescript": "^6.0.3"
|
|
71
71
|
}
|
package/src/env.d.ts
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
declare module 'bun' {
|
|
2
|
-
interface Env {
|
|
3
|
-
/**
|
|
4
|
-
* Default target width for resized images.
|
|
5
|
-
*/
|
|
6
|
-
WIDTH?: string
|
|
7
|
-
/**
|
|
8
|
-
* Default target height for resized images.
|
|
9
|
-
*/
|
|
10
|
-
HEIGHT?: string
|
|
11
|
-
/**
|
|
12
|
-
* Default path to the input folder containing images.
|
|
13
|
-
*/
|
|
14
|
-
INPUT_FOLDER?: string
|
|
15
|
-
/**
|
|
16
|
-
* Default path where processed images will be saved.
|
|
17
|
-
*/
|
|
18
|
-
OUTPUT_FOLDER?: string
|
|
19
|
-
/**
|
|
20
|
-
* Default global output format (e.g., '.webp', '.png').
|
|
21
|
-
*/
|
|
22
|
-
FORMAT?: string
|
|
23
|
-
/**
|
|
24
|
-
* Default concurrent processing limit.
|
|
25
|
-
*/
|
|
26
|
-
LIMIT?: string
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
declare module 'bun' {
|
|
2
|
+
interface Env {
|
|
3
|
+
/**
|
|
4
|
+
* Default target width for resized images.
|
|
5
|
+
*/
|
|
6
|
+
WIDTH?: string
|
|
7
|
+
/**
|
|
8
|
+
* Default target height for resized images.
|
|
9
|
+
*/
|
|
10
|
+
HEIGHT?: string
|
|
11
|
+
/**
|
|
12
|
+
* Default path to the input folder containing images.
|
|
13
|
+
*/
|
|
14
|
+
INPUT_FOLDER?: string
|
|
15
|
+
/**
|
|
16
|
+
* Default path where processed images will be saved.
|
|
17
|
+
*/
|
|
18
|
+
OUTPUT_FOLDER?: string
|
|
19
|
+
/**
|
|
20
|
+
* Default global output format (e.g., '.webp', '.png').
|
|
21
|
+
*/
|
|
22
|
+
FORMAT?: string
|
|
23
|
+
/**
|
|
24
|
+
* Default concurrent processing limit.
|
|
25
|
+
*/
|
|
26
|
+
LIMIT?: string
|
|
27
|
+
/**
|
|
28
|
+
* Whether to process the input directory recursively by default.
|
|
29
|
+
*/
|
|
30
|
+
RECURSIVE?: string
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,73 +1,92 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { readdir } from 'node:fs/promises'
|
|
4
|
+
import { basename, extname } from 'node:path'
|
|
5
|
+
import { exit } from 'node:process'
|
|
6
|
+
|
|
7
|
+
import { intro, log, note, outro, spinner } from '@clack/prompts'
|
|
8
|
+
import { Temporal } from '@js-temporal/polyfill'
|
|
9
|
+
import pLimit from 'p-limit'
|
|
10
|
+
import color from 'picocolors'
|
|
11
|
+
|
|
12
|
+
import { cli } from '@/args'
|
|
13
|
+
import { ensureOutputExists, getInputPath, getOutputPath } from '@/folder'
|
|
14
|
+
import { getExtensions, getImages, getWidthAndHeight, resize } from '@/image'
|
|
15
|
+
|
|
16
|
+
import pkg from '../package.json'
|
|
17
|
+
|
|
18
|
+
console.clear()
|
|
19
|
+
|
|
20
|
+
const banner = color.magenta(`
|
|
21
|
+
╭──────────────────────────────╮
|
|
22
|
+
│ │
|
|
23
|
+
│ 🩷 lumi 🩷 │
|
|
24
|
+
│ │
|
|
25
|
+
╰──────────────────────────────╯
|
|
26
|
+
`)
|
|
27
|
+
|
|
28
|
+
console.log(banner)
|
|
29
|
+
|
|
30
|
+
intro(color.magenta(`welcome to lumi v${pkg.version} 🩷`))
|
|
31
|
+
|
|
32
|
+
const input = getInputPath(cli.input)
|
|
33
|
+
|
|
34
|
+
const allFiles = await readdir(input, { recursive: cli.recursive })
|
|
35
|
+
const images = getImages(allFiles)
|
|
36
|
+
|
|
37
|
+
if (images.length === 0) {
|
|
38
|
+
log.error('yikes! no valid images found in the input folder 😭')
|
|
39
|
+
outro('please add some images to the input folder and try again 👋')
|
|
40
|
+
exit(1)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
note(`found ${color.magenta(images.length)} images to process! 🚀`)
|
|
44
|
+
|
|
45
|
+
const output = getOutputPath(cli.output)
|
|
46
|
+
await ensureOutputExists(output)
|
|
47
|
+
|
|
48
|
+
const { width, height } = await getWidthAndHeight(cli.width, cli.height)
|
|
49
|
+
|
|
50
|
+
const extensions = await getExtensions(images, cli.format)
|
|
51
|
+
const limit = pLimit({ concurrency: cli.limit || 10, rejectOnClear: true })
|
|
52
|
+
|
|
53
|
+
const spin = spinner()
|
|
54
|
+
spin.start(`processing: ${color.magenta(0)}/${color.magenta(images.length)} images 🔃`)
|
|
55
|
+
|
|
56
|
+
let processed = 0
|
|
57
|
+
|
|
58
|
+
const startTime = Temporal.Now.instant()
|
|
59
|
+
|
|
60
|
+
const promises = images.map(image =>
|
|
61
|
+
limit(async () => {
|
|
62
|
+
const ext = extname(image)
|
|
63
|
+
const name = basename(image, ext)
|
|
64
|
+
const extension = extensions['default'] ?? extensions[ext] ?? '.png'
|
|
65
|
+
|
|
66
|
+
await resize({ extension, height, image, input, name, output, width })
|
|
67
|
+
|
|
68
|
+
processed++
|
|
69
|
+
spin.message(`processing: ${color.green(processed)}/${color.magenta(images.length)} images 🔃`)
|
|
70
|
+
})
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
log.message()
|
|
74
|
+
|
|
75
|
+
const result = await Promise.allSettled(promises)
|
|
76
|
+
|
|
77
|
+
const endTime = Temporal.Now.instant()
|
|
78
|
+
const duration = startTime.until(endTime).total('seconds').toFixed(2)
|
|
79
|
+
|
|
80
|
+
let outroMessage = ''
|
|
81
|
+
|
|
82
|
+
if (result.some(pr => pr.status === 'rejected')) {
|
|
83
|
+
spin.error(
|
|
84
|
+
`yikes! finished with errors. processed ${color.red(processed)}/${color.red(images.length)} images in ${color.yellow(duration)} seconds 😢`
|
|
85
|
+
)
|
|
86
|
+
outroMessage = 'please check your input files and try again 🛠️'
|
|
87
|
+
} else {
|
|
88
|
+
spin.stop(`yay! ${color.green(images.length)} images processed in ${color.green(duration)} seconds! \u26A1\uFE0F`)
|
|
89
|
+
outroMessage = 'bye 👋'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
outro(color.magenta(outroMessage))
|