picslim 0.0.7 → 0.0.8
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/config.json +8 -0
- package/package.json +23 -2
- package/picslim.js +115 -127
package/config.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picslim",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Picslim is a Node.js package that allows you to optimize images in a specified directory.",
|
|
5
5
|
"main": "picslim.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"source": "picslim.js",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"start": "node picslim.js -q 80"
|
|
11
|
+
"start": "node picslim.js -q 80",
|
|
12
|
+
"format": "standard --fix",
|
|
13
|
+
"prepare": "husky install"
|
|
12
14
|
},
|
|
13
15
|
"keywords": [
|
|
14
16
|
"optimization",
|
|
@@ -26,11 +28,30 @@
|
|
|
26
28
|
"license": "MIT",
|
|
27
29
|
"files": [
|
|
28
30
|
"picslim.js",
|
|
31
|
+
"config.json",
|
|
29
32
|
"package.json",
|
|
30
33
|
"README.md"
|
|
31
34
|
],
|
|
35
|
+
"eslintConfig": {
|
|
36
|
+
"extends": "./node_modules/standard/eslintrc.json"
|
|
37
|
+
},
|
|
38
|
+
"husky": {
|
|
39
|
+
"hooks": {
|
|
40
|
+
"pre-commit": "lint-staged"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"lint-staged": {
|
|
44
|
+
"*.{js}": [
|
|
45
|
+
"standard --fix"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
32
48
|
"dependencies": {
|
|
33
49
|
"sharp": "^0.32.6",
|
|
34
50
|
"yargs": "^17.7.2"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"husky": "^8.0.0",
|
|
54
|
+
"lint-staged": "^15.0.2",
|
|
55
|
+
"standard": "^17.1.0"
|
|
35
56
|
}
|
|
36
57
|
}
|
package/picslim.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const fs = require(
|
|
3
|
-
const sharp = require(
|
|
4
|
-
const yargs = require(
|
|
2
|
+
const fs = require('fs').promises
|
|
3
|
+
const sharp = require('sharp')
|
|
4
|
+
const yargs = require('yargs')
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Parses command line arguments using yargs library.
|
|
8
8
|
*/
|
|
9
|
-
function parseArguments()
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}).argv;
|
|
9
|
+
function parseArguments () {
|
|
10
|
+
return yargs.options({
|
|
11
|
+
c: {
|
|
12
|
+
alias: 'config',
|
|
13
|
+
describe: 'Path to the configuration file',
|
|
14
|
+
demandOption: false,
|
|
15
|
+
type: 'string',
|
|
16
|
+
default: 'config.json'
|
|
17
|
+
}
|
|
18
|
+
}).argv
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -24,13 +23,12 @@ function parseArguments()
|
|
|
24
23
|
*
|
|
25
24
|
* @param {string} dir - The directory to verify.
|
|
26
25
|
*/
|
|
27
|
-
async function createOutputDirectory(dir)
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
26
|
+
async function createOutputDirectory (dir) {
|
|
27
|
+
try {
|
|
28
|
+
await fs.access(dir)
|
|
29
|
+
} catch (error) {
|
|
30
|
+
await fs.mkdir(dir)
|
|
31
|
+
}
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
/**
|
|
@@ -44,90 +42,83 @@ async function createOutputDirectory(dir)
|
|
|
44
42
|
* @param {number} quality - Image quality.
|
|
45
43
|
* @param {number} compressionLevel - PNG compression level.
|
|
46
44
|
*/
|
|
47
|
-
async function processImage(inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel)
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
45
|
+
async function processImage (inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel) {
|
|
46
|
+
try {
|
|
47
|
+
const image = sharp(inputPath)
|
|
48
|
+
const metadata = await image.metadata()
|
|
49
|
+
const originalWidth = metadata.width
|
|
50
|
+
const originalHeight = metadata.height
|
|
51
|
+
const imageProcessor = image.resize(
|
|
52
|
+
originalWidth > maxWidth ? maxWidth : null,
|
|
53
|
+
originalHeight > maxHeight ? maxHeight : null
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if (file.match(/\.(jpg|jpeg)$/i)) {
|
|
57
|
+
await imageProcessor.jpeg({ quality }).toFile(outputPath)
|
|
58
|
+
console.log(`Optimized JPEG image: ${file}`)
|
|
59
|
+
} else if (file.match(/\.(png)$/i)) {
|
|
60
|
+
await imageProcessor.png({ quality, compressionLevel }).toFile(outputPath)
|
|
61
|
+
console.log(`Optimized PNG image: ${file}`)
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`Optimization error ${file}: `, error)
|
|
65
|
+
}
|
|
69
66
|
}
|
|
70
67
|
|
|
71
|
-
|
|
72
68
|
/**
|
|
73
69
|
* Load settings from a configuration file.
|
|
74
70
|
*
|
|
75
71
|
* @param {string} configFile - Path to the configuration file.
|
|
76
72
|
*/
|
|
77
|
-
async function loadConfig(configFile)
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
73
|
+
async function loadConfig (configFile) {
|
|
74
|
+
try {
|
|
75
|
+
const configData = await fs.readFile(configFile, 'utf8')
|
|
76
|
+
|
|
77
|
+
if (configData.trim() === '') {
|
|
78
|
+
console.error('Configuration file is empty.')
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!validateJson(configData)) {
|
|
83
|
+
console.error('Configuration file is not a valid JSON object.')
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const config = JSON.parse(configData)
|
|
88
|
+
|
|
89
|
+
validateConfig(config)
|
|
90
|
+
|
|
91
|
+
if (typeof config !== 'object' || Array.isArray(config)) {
|
|
92
|
+
console.error('Configuration file is not a valid JSON object.')
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!config.inputDir || !config.outputDir) {
|
|
97
|
+
console.error("Configuration error: 'inputDir' and 'outputDir' are required fields.")
|
|
98
|
+
process.exit(1)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return config
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Error loading or parsing the configuration file: ', error)
|
|
104
|
+
process.exit(1)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
101
107
|
|
|
108
|
+
function validateJson (json) {
|
|
109
|
+
const isJson = (str) => {
|
|
110
|
+
try {
|
|
111
|
+
JSON.parse(str)
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Error
|
|
114
|
+
// JSON is not okay
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
102
117
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
} catch ( error ) {
|
|
110
|
-
console.error("Error loading or parsing the configuration file: ", error);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function validateJson(json)
|
|
116
|
-
{
|
|
117
|
-
const isJson = (str) =>
|
|
118
|
-
{
|
|
119
|
-
try {
|
|
120
|
-
JSON.parse(str);
|
|
121
|
-
} catch ( e ) {
|
|
122
|
-
//Error
|
|
123
|
-
//JSON is not okay
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return isJson(json);
|
|
121
|
+
return isJson(json)
|
|
131
122
|
}
|
|
132
123
|
|
|
133
124
|
/**
|
|
@@ -135,42 +126,39 @@ function validateJson(json)
|
|
|
135
126
|
*
|
|
136
127
|
* @param {object} config - The loaded configuration object.
|
|
137
128
|
*/
|
|
138
|
-
function validateConfig(config)
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
129
|
+
function validateConfig (config) {
|
|
130
|
+
if (!config.inputDir || !config.outputDir) {
|
|
131
|
+
console.error("Configuration error: 'inputDir' and 'outputDir' are required fields.")
|
|
132
|
+
process.exit(1)
|
|
133
|
+
}
|
|
145
134
|
}
|
|
146
135
|
|
|
147
136
|
/**
|
|
148
137
|
* The main function that processes all image files in the input directory.
|
|
149
138
|
*/
|
|
150
|
-
async function main()
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
139
|
+
async function main () {
|
|
140
|
+
const argv = parseArguments()
|
|
141
|
+
const config = await loadConfig(argv.config)
|
|
142
|
+
|
|
143
|
+
const inputDir = config.inputDir
|
|
144
|
+
const outputDir = config.outputDir
|
|
145
|
+
const quality = config.quality
|
|
146
|
+
const maxWidth = config.maxWidth
|
|
147
|
+
const maxHeight = config.maxHeight
|
|
148
|
+
const compressionLevel = config.compressionLevel
|
|
149
|
+
|
|
150
|
+
await createOutputDirectory(outputDir)
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const files = await fs.readdir(inputDir)
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
const inputPath = `${inputDir}/${file}`
|
|
156
|
+
const outputPath = `${outputDir}/${file}`
|
|
157
|
+
await processImage(inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel)
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error('Error reading input directory: ', error)
|
|
161
|
+
}
|
|
174
162
|
}
|
|
175
163
|
|
|
176
|
-
main()
|
|
164
|
+
main()
|