picslim 0.0.7 → 1.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 (4) hide show
  1. package/README.md +44 -8
  2. package/config.json +8 -0
  3. package/package.json +23 -2
  4. package/picslim.js +152 -133
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # PicSlim
2
2
 
3
- **Picslim** is a Node.js package that allows you to optimize images in a specified directory. It supports JPEG and PNG images, and you can control the quality and resizing of the images during optimization.
3
+ **Picslim** is a Node.js package that allows you to efficiently optimize images within a specified directory. It supports **JPEG** and **PNG** images, image formats, offering fine-grained control over image quality and resizing options during the optimization process. With **Picslim**, you can effortlessly reduce file sizes and enhance the loading performance of your images.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,6 +10,12 @@ You can install picslim globally using npm:
10
10
  npm install -g picslim
11
11
  ```
12
12
 
13
+ or
14
+
15
+ ```bash
16
+ npx picslim
17
+ ```
18
+
13
19
  # Usage
14
20
 
15
21
  Once installed, you can use the optimizimage command in your terminal. Here's how you can use it:
@@ -18,17 +24,43 @@ Once installed, you can use the optimizimage command in your terminal. Here's ho
18
24
  picslim [options]
19
25
  ```
20
26
 
21
- Options:
27
+ ### Options
28
+
29
+ - `-c, --config <path>`: Path to the configuration file. (default: 'config.json')
30
+ - `-q, --quality <number>`: Image quality (0-100).
31
+ - `-l, --compressionLevel <number>`: PNG compression level (0-9).
32
+ - `-w, --maxWidth <number>`: Maximum width allowed for images.
33
+ - `-h, --maxHeight <number>`: Maximum height allowed for images.
34
+ - `-i, --input <path>`: Path to the input directory.
35
+ - `-o, --output <path>`: Path to the output directory.
36
+
37
+ ### Configuration File
38
+
39
+ You can create a `config.json` file in your project directory to specify default settings. Here's an example configuration:
40
+
41
+ ```json
42
+ {
43
+ "inputDir": "./in",
44
+ "outputDir": "./min",
45
+ "quality": 80,
46
+ "maxWidth": null,
47
+ "maxHeight": null,
48
+ "compressionLevel": 9
49
+ }
50
+ ```
22
51
 
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
- **-c, --compressionLevel [value]:** Set the compression level (0 to 9, default: 9).
52
+ ### Example:
27
53
 
28
- Example:
54
+ Optimize images using default settings from the configuration file:
29
55
 
30
56
  ```bash
31
- picslim -q 90 -w 1920
57
+ picslim
58
+ ```
59
+
60
+ Optimize images with custom settings:
61
+
62
+ ```bash
63
+ picslim -q 90 -w 800 -h 600 -l 4 -i input_images -o output_images
32
64
  ```
33
65
 
34
66
  This will optimize all JPEG and PNG images in the current directory, and the optimized images will be saved in a 'min' directory.
@@ -65,3 +97,7 @@ This project is licensed under the MIT License. See the LICENSE file for details
65
97
  ### Author
66
98
 
67
99
  Ivan Mercedes
100
+
101
+ ### Contributors
102
+
103
+ - [Elminson De Oleo Baez](https://github.com/elminson)
package/config.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "inputDir": "./in",
3
+ "outputDir": "./min",
4
+ "quality": 80,
5
+ "maxWidth": null,
6
+ "maxHeight": null,
7
+ "compressionLevel": 9
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picslim",
3
- "version": "0.0.7",
3
+ "version": "1.0.0",
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,36 +1,76 @@
1
1
  #!/usr/bin/env node
2
- const fs = require("fs").promises;
3
- const sharp = require("sharp");
4
- const yargs = require("yargs");
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
- 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;
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
+ q: {
19
+ alias: 'quality',
20
+ describe: 'Image quality',
21
+ demandOption: false,
22
+ type: 'number',
23
+ default: null
24
+ },
25
+ l: {
26
+ alias: 'compressionLevel',
27
+ describe: 'PNG compression level',
28
+ demandOption: false,
29
+ type: 'number',
30
+ default: null
31
+ },
32
+ w: {
33
+ alias: 'maxWidth',
34
+ describe: 'Maximum width allowed',
35
+ demandOption: false,
36
+ type: 'number',
37
+ default: null
38
+ },
39
+ h: {
40
+ alias: 'maxHeight',
41
+ describe: 'Maximum height allowed',
42
+ demandOption: false,
43
+ type: 'number',
44
+ default: null
45
+ },
46
+ i: {
47
+ alias: 'input',
48
+ describe: 'Path to the input directory',
49
+ demandOption: false,
50
+ type: 'string',
51
+ default: null
52
+ },
53
+ o: {
54
+ alias: 'output',
55
+ describe: 'Path to the output directory',
56
+ demandOption: false,
57
+ type: 'string',
58
+ default: null
59
+ }
60
+ }).argv
20
61
  }
21
62
 
22
63
  /**
23
- * Verifies if the output directory exists; if not, creates it.
24
- *
25
- * @param {string} dir - The directory to verify.
64
+ * Creates an output directory if it doesn't exist.
65
+ * @param {string} dir - The directory path to create.
66
+ * @returns {Promise<void>}
26
67
  */
27
- async function createOutputDirectory(dir)
28
- {
29
- try {
30
- await fs.access(dir);
31
- } catch ( error ) {
32
- await fs.mkdir(dir);
33
- }
68
+ async function createOutputDirectory (dir) {
69
+ try {
70
+ await fs.access(dir)
71
+ } catch (error) {
72
+ await fs.mkdir(dir)
73
+ }
34
74
  }
35
75
 
36
76
  /**
@@ -44,90 +84,56 @@ async function createOutputDirectory(dir)
44
84
  * @param {number} quality - Image quality.
45
85
  * @param {number} compressionLevel - PNG compression level.
46
86
  */
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
- }
87
+ async function processImage (inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel) {
88
+ try {
89
+ const image = sharp(inputPath)
90
+ const metadata = await image.metadata()
91
+ const originalWidth = metadata.width
92
+ const originalHeight = metadata.height
93
+ const imageProcessor = image.resize(
94
+ originalWidth > maxWidth ? maxWidth : null,
95
+ originalHeight > maxHeight ? maxHeight : null
96
+ )
97
+
98
+ if (file.match(/\.(jpg|jpeg)$/i)) {
99
+ await imageProcessor.jpeg({ quality }).toFile(outputPath)
100
+ console.log(`Optimized JPEG image: ${file}`)
101
+ } else if (file.match(/\.(png)$/i)) {
102
+ await imageProcessor.png({ quality, compressionLevel }).toFile(outputPath)
103
+ console.log(`Optimized PNG image: ${file}`)
104
+ }
105
+ } catch (error) {
106
+ // console.error(`Optimization error ${file}: `, error)
107
+ }
69
108
  }
70
109
 
71
-
72
110
  /**
73
- * Load settings from a configuration file.
111
+ * Load settings from a configuration file. If the specified configuration file doesn't exist,
112
+ * it will use the default 'config.json' from the package.
74
113
  *
75
114
  * @param {string} configFile - Path to the configuration file.
76
115
  */
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);
116
+ async function loadConfig (configFile) {
117
+ let config
118
+ try {
119
+ const configData = await fs.readFile(configFile, 'utf8')
120
+ config = JSON.parse(configData)
121
+ } catch (error) {
122
+ console.error('Using the default configuration from the package. ')
123
+ config = require('./config.json') // Load default configuration from the package
124
+ }
125
+
126
+ if (!validateConfigObject(config)) {
127
+ console.error('Invalid configuration object.')
128
+ process.exit(1)
129
+ }
130
+
131
+ if (!config.inputDir || !config.outputDir) {
132
+ console.error("Configuration error: 'inputDir' and 'outputDir' are required fields.")
133
+ process.exit(1)
134
+ }
135
+
136
+ return config
131
137
  }
132
138
 
133
139
  /**
@@ -135,42 +141,55 @@ function validateJson(json)
135
141
  *
136
142
  * @param {object} config - The loaded configuration object.
137
143
  */
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
- }
144
+ function validateConfigObject (config) {
145
+ return (
146
+ typeof config === 'object' &&
147
+ !Array.isArray(config) &&
148
+ config.inputDir &&
149
+ config.outputDir
150
+ )
145
151
  }
146
152
 
147
153
  /**
148
- * The main function that processes all image files in the input directory.
154
+ * Returns the input directory path based on the provided arguments and configuration.
155
+ * If the provided arguments or configuration are '.', returns the current working directory.
156
+ * @param {string} argvInput - The input directory path provided as an argument.
157
+ * @param {string} configInputDir - The input directory path provided in the configuration.
158
+ * @returns {string} - The input directory path.
149
159
  */
150
- async function main()
151
- {
152
- const argv = parseArguments();
153
- const config = await loadConfig(argv.config);
160
+ function getInputDirectory (argvInput, configInputDir) {
161
+ if (argvInput === '.' || configInputDir === '.') {
162
+ return process.cwd()
163
+ }
154
164
 
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);
165
+ return argvInput || configInputDir
166
+ }
163
167
 
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
- }
168
+ /**
169
+ * The main function that processes all image files in the input directory.
170
+ */
171
+ async function main () {
172
+ const argv = parseArguments()
173
+ const config = await loadConfig(argv.config)
174
+ const inputDir = getInputDirectory(argv.input, config.inputDir)
175
+ const outputDir = getInputDirectory(argv.output, config.outputDir)
176
+ const quality = argv.quality ? argv.quality : config.quality
177
+ const maxWidth = argv.maxWidth ? argv.maxWidth : config.maxWidth
178
+ const maxHeight = argv.maxHeight ? argv.maxHeight : config.maxHeight
179
+ const compressionLevel = argv.compressionLevel ? argv.compressionLevel : config.compressionLevel
180
+
181
+ await createOutputDirectory(outputDir)
182
+
183
+ try {
184
+ const files = await fs.readdir(inputDir)
185
+ for (const file of files) {
186
+ const inputPath = `${inputDir}/${file}`
187
+ const outputPath = `${outputDir}/${file}`
188
+ await processImage(inputPath, outputPath, file, maxWidth, maxHeight, quality, compressionLevel)
189
+ }
190
+ } catch (error) {
191
+ console.error('Error reading input directory: ', error)
192
+ }
174
193
  }
175
194
 
176
- main();
195
+ main()