image-guard 5.2.6 → 5.3.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.
- package/README.md +7 -3
- package/package.json +4 -3
- package/src/index.js +29 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Image Guard
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/image-guard) [](https://github.com/j9t/image-guard/actions) [](https://socket.dev/npm/package/image-guard) [](https://github.com/j9t
|
|
3
|
+
[](https://www.npmjs.com/package/image-guard) [](https://github.com/j9t/image-guard/actions) [](https://socket.dev/npm/package/image-guard) [](https://github.com/sponsors/j9t)
|
|
4
4
|
|
|
5
5
|
(This project was based on [sum.cumo’s imagemin-merlin](https://github.com/sumcumo/imagemin-merlin). [Changes are documented](https://github.com/sumcumo/imagemin-merlin/compare/master...j9t:master), and include this README. Image Guard supports additional file formats—WebP, AVIF, and HEIC/HEIF—, comes with improved code and documentation, and is being maintained. For this reason, it’s [not based on any Imagemin packages](https://meiert.com/blog/image-guard-4/) anymore.)
|
|
6
6
|
|
|
@@ -101,15 +101,19 @@ npm pkg set scripts.postprepare="grep -qxF './node_modules/.bin/image-guard --st
|
|
|
101
101
|
|
|
102
102
|
(The `postprepare` script ensures that the hook is added to the repository whenever someone installs the package.)
|
|
103
103
|
|
|
104
|
+
If you give the hook options like `--ignore`, add them to the `postprepare` script as well—the hook command is the only place to configure Image Guard, and the next install rewrites that line.
|
|
105
|
+
|
|
104
106
|
**Important:** When you commit images that have not yet been compressed, the automated compression process (triggered by the pre-commit hook) will modify those image files to reduce their size. As a result, after your initial commit attempt, you will see these images appear as changed files in Git. To include the optimized images in your repository, you need to stage and commit them again. In rare cases, if further compression is possible, you may need to repeat this process until no further changes are detected. This workflow is intentional and ensures that only optimally compressed images are committed. Many editors can display diffs for images, helping you review these changes.
|
|
105
107
|
|
|
106
108
|
### Parameters
|
|
107
109
|
|
|
110
|
+
Image Guard takes an optional path to the directory to process—`npx image-guard assets`—and works on the current directory when that path is omitted.
|
|
111
|
+
|
|
108
112
|
* `--dry` allows you to run Image Guard in “dry mode.” All changes are shown in the terminal.
|
|
109
113
|
|
|
110
|
-
* `--ignore` allows you to specify paths to be ignored (as in `--ignore=example,test`). Multiple paths must be separated by commas. The option supports glob patterns (e.g., `assets/**`, `**/*.png`); matching is case-insensitive and honors `.gitignore`.
|
|
114
|
+
* `--ignore` allows you to specify paths to be ignored (as in `--ignore=example,test`). Multiple paths must be separated by commas. Naming a directory excludes everything inside it. The option supports glob patterns (e.g., `assets/**`, `**/*.png`); matching is case-insensitive and honors `.gitignore`. Patterns are relative to the directory being processed—with `--staged`, to the repository root.
|
|
111
115
|
|
|
112
|
-
* `--staged` (recommended with automated use) triggers a mode that watches PNG, JPG, GIF, WebP, and AVIF files (and HEIC/HEIF, when `--heic-to-avif` is used)
|
|
116
|
+
* `--staged` (recommended with automated use) triggers a mode that watches PNG, JPG, GIF, WebP, and AVIF files (and HEIC/HEIF, when `--heic-to-avif` is used) from staged changes (`git diff --cached`) and only processes those files—that approach makes Image Guard more efficient in operation. Because the file set comes from Git, `--staged` can’t be combined with a path.
|
|
113
117
|
|
|
114
118
|
* `--heic-to-avif` converts HEIC/HEIF files to AVIF. Because HEIC sources are already lossy-compressed (HEVC), the conversion uses lossy AVIF encoding (quality 80) to produce files smaller than the originals at near-identical visual quality. Original HEIC/HEIF files are deleted after conversion by default. (Note: The conversion maps Display P3 colors—common on iPhones—to sRGB, which may result in slightly less vivid colors.)
|
|
115
119
|
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"imagemin-merlin": "bin/image-guard.sh"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"globby": "^16.2.
|
|
9
|
+
"globby": "^16.2.3",
|
|
10
10
|
"heic-decode": "^2.1.0",
|
|
11
11
|
"sharp": "^0.35.3",
|
|
12
12
|
"simple-git": "^3.36.0"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"description": "Near-lossless image compressor that’s easy to use and easy to automate",
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@eslint/js": "^10.0.1",
|
|
17
|
-
"eslint": "^10.
|
|
17
|
+
"eslint": "^10.8.1"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"bin/image-guard.js",
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"scripts": {
|
|
51
51
|
"lint": "eslint .",
|
|
52
52
|
"lint:fix": "eslint . --fix",
|
|
53
|
+
"prepare": "git config core.hooksPath .githooks || true",
|
|
53
54
|
"test": "node --test bin/image-guard.test.js"
|
|
54
55
|
},
|
|
55
56
|
"type": "module",
|
|
56
|
-
"version": "5.
|
|
57
|
+
"version": "5.3.0"
|
|
57
58
|
}
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { globby, convertPathToPattern } from 'globby'
|
|
4
4
|
import simpleGit from 'simple-git'
|
|
5
5
|
import { parseArgs, styleText } from 'node:util'
|
|
6
|
+
import fsSync from 'node:fs'
|
|
6
7
|
import os from 'node:os'
|
|
7
8
|
import path from 'node:path'
|
|
8
9
|
import sharp from 'sharp'
|
|
@@ -23,7 +24,21 @@ export async function runImageGuard() {
|
|
|
23
24
|
staged: { type: 'boolean', default: false },
|
|
24
25
|
quiet: { type: 'boolean', default: false }
|
|
25
26
|
}
|
|
26
|
-
const { values: argv } = parseArgs({ options })
|
|
27
|
+
const { values: argv, positionals } = parseArgs({ options, allowPositionals: true })
|
|
28
|
+
|
|
29
|
+
if (positionals.length > 1) {
|
|
30
|
+
throw new Error(`Expected at most one path, got ${positionals.length}: ${positionals.join(', ')}`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const dir = positionals[0] || '.'
|
|
34
|
+
|
|
35
|
+
if (positionals.length && argv.staged) {
|
|
36
|
+
throw new Error('`--staged` takes its files from Git, not from a path—pass one or the other.')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!fsSync.existsSync(dir) || !fsSync.statSync(dir).isDirectory()) {
|
|
40
|
+
throw new Error(`No such directory: ${dir}`)
|
|
41
|
+
}
|
|
27
42
|
|
|
28
43
|
// Share status
|
|
29
44
|
const summary = (run, includesConversion = false) => {
|
|
@@ -42,7 +57,8 @@ export async function runImageGuard() {
|
|
|
42
57
|
|
|
43
58
|
const allTypes = argv['heic-to-avif'] ? [...fileTypes, ...convertTypes] : fileTypes
|
|
44
59
|
if (!argv.quiet) {
|
|
45
|
-
|
|
60
|
+
const where = dir === '.' ? '' : `, in ${dir}`
|
|
61
|
+
console.log(`(Search pattern: ${allTypes.join(', ')}${where})\n`)
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
let savedKB = 0
|
|
@@ -129,12 +145,15 @@ export async function runImageGuard() {
|
|
|
129
145
|
return hadFailures
|
|
130
146
|
}
|
|
131
147
|
|
|
148
|
+
// Plain patterns also get a “/**” variant so directories are excluded with
|
|
149
|
+
// their contents: “dir/” resolves only where globby can stat it, and a bare
|
|
150
|
+
// “dir” only covers what the walk descends into
|
|
132
151
|
const getIgnorePatterns = (ignore) => {
|
|
133
152
|
return (ignore || '')
|
|
134
153
|
.split(',')
|
|
135
|
-
.map(s => s.trim())
|
|
154
|
+
.map(s => s.trim().replace(/^!/, '').replace(/\/+$/, ''))
|
|
136
155
|
.filter(Boolean)
|
|
137
|
-
.
|
|
156
|
+
.flatMap(p => (/[*?[\]{}]/.test(p) ? [`!${p}`] : [`!${p}`, `!${p}/**`]))
|
|
138
157
|
}
|
|
139
158
|
|
|
140
159
|
const getFilePattern = (ignore, types = fileTypes) => {
|
|
@@ -152,13 +171,18 @@ export async function runImageGuard() {
|
|
|
152
171
|
return patterns
|
|
153
172
|
}
|
|
154
173
|
|
|
174
|
+
// Globbing runs with `dir` as its base—so ignore patterns and `.gitignore`
|
|
175
|
+
// lookup are relative to the searched directory, not to the shell’s—and the
|
|
176
|
+
// results are rejoined for display and file access
|
|
155
177
|
const findFiles = async (patterns, options = {}) => {
|
|
156
|
-
|
|
178
|
+
const files = await globby(patterns, {
|
|
179
|
+
cwd: dir,
|
|
157
180
|
gitignore: true,
|
|
158
181
|
onlyFiles: true,
|
|
159
182
|
caseSensitiveMatch: false,
|
|
160
183
|
...options
|
|
161
184
|
})
|
|
185
|
+
return files.map(file => path.join(dir, file))
|
|
162
186
|
}
|
|
163
187
|
|
|
164
188
|
const filterStagedFiles = async (stagedFiles, types) => {
|