picslim 0.0.5 → 0.0.7

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 (3) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/picslim.js +161 -104
package/README.md CHANGED
@@ -20,9 +20,9 @@ picslim [options]
20
20
 
21
21
  Options:
22
22
 
23
- **-q, --quality [value]:** Set the image quality (1 to 100, default: 80).
24
- **-mw, --maxWidth [value]:** Set the maximum width allowed (default: null).
25
- **-mh, --maxHeight [value]:** Set the maximum height allowed (default: null).
23
+ **-q, --quality [value]:** Set the image quality (1 to 100, default: 80).<br>
24
+ **-mw, --maxWidth [value]:** Set the maximum width allowed (default: null).<br>
25
+ **-mh, --maxHeight [value]:** Set the maximum height allowed (default: null).<br>
26
26
  **-c, --compressionLevel [value]:** Set the compression level (0 to 9, default: 9).
27
27
 
28
28
  Example:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picslim",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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": {
package/picslim.js CHANGED
@@ -1,119 +1,176 @@
1
1
  #!/usr/bin/env node
2
- const fs = require("fs");
2
+ const fs = require("fs").promises;
3
3
  const sharp = require("sharp");
4
4
  const yargs = require("yargs");
5
5
 
6
6
  /**
7
7
  * Parses command line arguments using yargs library.
8
- *
9
- * @typedef {Object} Argv
10
- * @property {number} quality - Image quality (1 to 100)
11
- * @property {number} width - Maximum width allowed
12
- * @property {number} compressionLevel - PNG compression level (0 to 9)
13
8
  */
14
-
15
- const argv = yargs.options({
16
- q: {
17
- alias: "quality",
18
- describe: "Image quality (1 to 100)",
19
- demandOption: false,
20
- type: "number",
21
- default: 80,
22
- },
23
- mw: {
24
- alias: "maxWidth",
25
- describe: "Maximum width allowed",
26
- demandOption: false,
27
- type: "number",
28
- default: null,
29
- },
30
- mh: {
31
- alias: "maxHeight",
32
- describe: "Maximum height allowed",
33
- demandOption: false,
34
- type: "number",
35
- default: null,
36
- },
37
- c: {
38
- alias: "compressionLevel",
39
- describe: "PNG compression level (0 to 9)",
40
- demandOption: false,
41
- type: "number",
42
- default: 9,
43
- },
44
- }).argv;
45
-
46
- const inputDir = "./";
47
- const outputDir = "./min";
48
- const quality = argv.quality;
49
- const maxWidth = argv.maxWidth;
50
- const maxHeight = argv.maxHeight;
51
- const compressionLevel = argv.compressionLevel;
9
+ function parseArguments()
10
+ {
11
+ return yargs.options({
12
+ c: {
13
+ alias: "config",
14
+ describe: "Path to the configuration file",
15
+ demandOption: false,
16
+ type: "string",
17
+ default: "config.json",
18
+ },
19
+ }).argv;
20
+ }
52
21
 
53
22
  /**
54
23
  * Verifies if the output directory exists; if not, creates it.
55
24
  *
56
25
  * @param {string} dir - The directory to verify.
57
26
  */
58
- if (!fs.existsSync(outputDir)) {
59
- fs.mkdirSync(outputDir);
27
+ async function createOutputDirectory(dir)
28
+ {
29
+ try {
30
+ await fs.access(dir);
31
+ } catch ( error ) {
32
+ await fs.mkdir(dir);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Process and optimize an image file.
38
+ *
39
+ * @param {string} inputPath - Path to the input image file.
40
+ * @param {string} outputPath - Path to the output image file.
41
+ * @param {string} file - The file name.
42
+ * @param {number} maxWidth - Maximum width allowed.
43
+ * @param {number} maxHeight - Maximum height allowed.
44
+ * @param {number} quality - Image quality.
45
+ * @param {number} compressionLevel - PNG compression level.
46
+ */
47
+ async function processImage(inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel)
48
+ {
49
+ try {
50
+ const image = sharp(inputPath);
51
+ const metadata = await image.metadata();
52
+ const originalWidth = metadata.width;
53
+ const originalHeight = metadata.height;
54
+ const imageProcessor = image.resize(
55
+ originalWidth > maxWidth ? maxWidth : null,
56
+ originalHeight > maxHeight ? maxHeight : null
57
+ );
58
+
59
+ if ( file.match(/\.(jpg|jpeg)$/i) ) {
60
+ await imageProcessor.jpeg({quality}).toFile(outputPath);
61
+ console.log(`Optimized JPEG image: ${file}`);
62
+ } else if ( file.match(/\.(png)$/i) ) {
63
+ await imageProcessor.png({quality, compressionLevel}).toFile(outputPath);
64
+ console.log(`Optimized PNG image: ${file}`);
65
+ }
66
+ } catch ( error ) {
67
+ console.error(`Optimization error ${file}: `, error);
68
+ }
69
+ }
70
+
71
+
72
+ /**
73
+ * Load settings from a configuration file.
74
+ *
75
+ * @param {string} configFile - Path to the configuration file.
76
+ */
77
+ async function loadConfig(configFile)
78
+ {
79
+ try {
80
+
81
+ const configData = await fs.readFile(configFile, "utf8");
82
+
83
+ if ( configData.trim() === '' ) {
84
+ console.error("Configuration file is empty.");
85
+ process.exit(1);
86
+ }
87
+
88
+ if ( !validateJson(configData) ) {
89
+ console.error("Configuration file is not a valid JSON object.");
90
+ process.exit(1);
91
+ }
92
+
93
+ const config = JSON.parse(configData);
94
+
95
+ validateConfig(config);
96
+
97
+ if ( typeof config !== 'object' || Array.isArray(config) ) {
98
+ console.error("Configuration file is not a valid JSON object.");
99
+ process.exit(1);
100
+ }
101
+
102
+
103
+ if ( !config.inputDir || !config.outputDir ) {
104
+ console.error("Configuration error: 'inputDir' and 'outputDir' are required fields.");
105
+ process.exit(1);
106
+ }
107
+
108
+ return config;
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);
131
+ }
132
+
133
+ /**
134
+ * Validate the configuration object.
135
+ *
136
+ * @param {object} config - The loaded configuration object.
137
+ */
138
+ function validateConfig(config)
139
+ {
140
+
141
+ if ( !config.inputDir || !config.outputDir ) {
142
+ console.error("Configuration error: 'inputDir' and 'outputDir' are required fields.");
143
+ process.exit(1);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * The main function that processes all image files in the input directory.
149
+ */
150
+ async function main()
151
+ {
152
+ const argv = parseArguments();
153
+ const config = await loadConfig(argv.config);
154
+
155
+ const inputDir = config.inputDir;
156
+ const outputDir = config.outputDir;
157
+ const quality = config.quality;
158
+ const maxWidth = config.maxWidth;
159
+ const maxHeight = config.maxHeight;
160
+ const compressionLevel = config.compressionLevel;
161
+
162
+ await createOutputDirectory(outputDir);
163
+
164
+ try {
165
+ const files = await fs.readdir(inputDir);
166
+ for ( const file of files ) {
167
+ const inputPath = `${inputDir}/${file}`;
168
+ const outputPath = `${outputDir}/${file}`;
169
+ await processImage(inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel);
170
+ }
171
+ } catch ( error ) {
172
+ console.error("Error reading input directory: ", error);
173
+ }
60
174
  }
61
175
 
62
- fs.readdir(inputDir, (err, files) => {
63
- if (err) {
64
- console.error("Error reading input directory: ", err);
65
- return;
66
- }
67
-
68
- files.forEach((file) => {
69
- const inputPath = `${inputDir}/${file}`;
70
- const outputPath = `${outputDir}/${file}`;
71
-
72
- if (file.match(/\.(jpg|jpeg)$/i)) {
73
- sharp(inputPath)
74
- .metadata()
75
- .then((metadata) => {
76
- const originalWidth = metadata.width;
77
- const originalHeight = metadata.height;
78
- sharp(inputPath)
79
- .resize(
80
- originalWidth > maxWidth ? maxWidth : null,
81
- originalHeight > maxHeight ? maxHeight : null,
82
- )
83
- .jpeg({
84
- quality,
85
- })
86
- .toFile(outputPath, (err, info) => {
87
- if (err) {
88
- console.error(`Optimization error ${file}: `, err);
89
- } else {
90
- console.log(`Optimized PNG image: ${file}`);
91
- }
92
- });
93
- });
94
- } else if (file.match(/\.(png)$/i)) {
95
- sharp(inputPath)
96
- .metadata()
97
- .then((metadata) => {
98
- const originalWidth = metadata.width;
99
- const originalHeight = metadata.height;
100
- sharp(inputPath)
101
- .resize(
102
- originalWidth > maxWidth ? maxWidth : null,
103
- originalHeight > maxHeight ? maxHeight : null,
104
- )
105
- .png({
106
- quality,
107
- compressionLevel,
108
- })
109
- .toFile(outputPath, (err, info) => {
110
- if (err) {
111
- console.error(`Optimization error ${file}: `, err);
112
- } else {
113
- console.log(`Optimized PNG image: ${file}`);
114
- }
115
- });
116
- });
117
- }
118
- });
119
- });
176
+ main();