astro-lqip 1.7.0 → 1.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-lqip",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Native extended Astro components for generating low quality image placeholders (LQIP).",
5
5
  "keywords": [
6
6
  "astro",
@@ -38,18 +38,18 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint/js": "9.39.1",
41
- "@typescript-eslint/parser": "8.46.4",
42
- "astro": "5.15.5",
43
- "bumpp": "10.3.1",
41
+ "@typescript-eslint/parser": "8.48.0",
42
+ "astro": "5.16.1",
43
+ "bumpp": "10.3.2",
44
44
  "eslint": "9.39.1",
45
45
  "eslint-plugin-astro": "1.5.0",
46
46
  "eslint-plugin-jsonc": "2.21.0",
47
47
  "eslint-plugin-jsx-a11y": "6.10.2",
48
- "eslint-plugin-package-json": "0.71.0",
48
+ "eslint-plugin-package-json": "0.85.0",
49
49
  "eslint-plugin-yml": "1.19.0",
50
50
  "globals": "16.5.0",
51
51
  "jiti": "2.6.1",
52
- "nano-staged": "0.8.0",
52
+ "nano-staged": "0.9.0",
53
53
  "neostandard": "0.12.2",
54
54
  "simple-git-hooks": "2.13.1"
55
55
  },
@@ -57,11 +57,14 @@
57
57
  "astro": ">=3.3.0"
58
58
  },
59
59
  "scripts": {
60
- "build": "pnpm -r build",
61
- "dev": "pnpm -r dev",
60
+ "build:docs": "pnpm --filter ./docs -r build",
61
+ "dev": "pnpm --filter=!./docs -r dev",
62
+ "dev:docs": "pnpm --filter ./docs -r dev",
62
63
  "lint": "eslint --cache .",
63
64
  "lint:fix": "eslint --cache --fix .",
64
65
  "preview": "pnpm -r preview",
65
- "release": "bumpp -r"
66
+ "release": "bumpp",
67
+ "test:fixtures": "pnpm --filter \"./tests/fixtures/**\" -r build",
68
+ "test:fixtures:preview": "pnpm --filter \"./tests/fixtures/**\" -r preview"
66
69
  }
67
70
  }
@@ -13,6 +13,19 @@ function normalizeFsPath(path: string) {
13
13
  return path
14
14
  }
15
15
 
16
+ function isNode() {
17
+ return typeof process !== 'undefined' && !!process.versions?.node
18
+ }
19
+
20
+ async function readIfExists(path: string): Promise<Buffer | undefined> {
21
+ if (!isNode()) return undefined
22
+ try {
23
+ return await readFile(path)
24
+ } catch {
25
+ return undefined
26
+ }
27
+ }
28
+
16
29
  export async function generateLqip(
17
30
  imagePath: string,
18
31
  lqipType: LqipType,
@@ -22,7 +35,7 @@ export async function generateLqip(
22
35
  try {
23
36
  const normalizedPath = normalizeFsPath(imagePath)
24
37
 
25
- const buffer = await readFile(normalizedPath)
38
+ const buffer = await readIfExists(normalizedPath)
26
39
 
27
40
  if (!buffer) {
28
41
  console.warn(`${PREFIX} image not found for:`, imagePath)
@@ -15,9 +15,38 @@ function isRemoteUrl(url: string) {
15
15
  const CACHE_DIR = join(process.cwd(), 'node_modules', '.cache', 'astro-lqip')
16
16
  const EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'avif']
17
17
  const SEARCH_ROOT = ['src']
18
+ const HASHED_FILENAME_REGEX = /\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9]+$/
19
+ const HASHED_FILENAME_CAPTURE_REGEX = /^(.+?)\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9]+$/
20
+
21
+ const BASE_URL = (() => {
22
+ try {
23
+ const base = (import.meta.env?.BASE_URL ?? '/') as string
24
+ if (!base || base === '/') return '/'
25
+ return base.endsWith('/') ? base.slice(0, -1) : base
26
+ } catch {
27
+ return '/'
28
+ }
29
+ })()
18
30
 
19
31
  const searchCache = new Map<string, string | null>()
20
32
 
33
+ function stripBasePath(src: string) {
34
+ if (typeof src !== 'string') return src
35
+
36
+ const queryIndex = src.indexOf('?')
37
+ let pathOnly = queryIndex >= 0 ? src.slice(0, queryIndex) : src
38
+
39
+ if (BASE_URL !== '/' && pathOnly.startsWith(BASE_URL)) {
40
+ pathOnly = pathOnly.slice(BASE_URL.length) || '/'
41
+ }
42
+
43
+ if (!pathOnly.startsWith('/')) {
44
+ pathOnly = `/${pathOnly}`
45
+ }
46
+
47
+ return pathOnly
48
+ }
49
+
21
50
  async function ensureCacheDir() {
22
51
  if (!existsSync(CACHE_DIR)) {
23
52
  await mkdir(CACHE_DIR, { recursive: true })
@@ -27,7 +56,7 @@ async function ensureCacheDir() {
27
56
  function extractOriginalFileName(filename: string) {
28
57
  const file = filename.split('/').pop() || ''
29
58
 
30
- const match = file.match(/^(.+?)\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9]+$/)
59
+ const match = file.match(HASHED_FILENAME_CAPTURE_REGEX)
31
60
  if (match) return match[1]
32
61
 
33
62
  const parts = file.split('.')
@@ -126,24 +155,25 @@ export async function getLqip(
126
155
 
127
156
  if (!isDevelopment) {
128
157
  const src = imagePath.src
129
-
130
- const clean = src.replace(/^\//, '')
131
-
132
- const candidatePaths = [
133
- join(process.cwd(), 'dist', 'client', clean),
134
- join(process.cwd(), 'dist', clean),
135
- join(process.cwd(), 'dist', '_astro', clean.replace(/^_astro\//, ''))
136
- ]
137
-
138
- for (const path of candidatePaths) {
139
- if (existsSync(path)) {
140
- return await generateLqip(path, lqipType, lqipSize, isDevelopment)
158
+ const normalizedSrc = stripBasePath(src)
159
+ const clean = normalizedSrc.replace(/^\//, '')
160
+
161
+ if (clean) {
162
+ const candidatePaths = [
163
+ join(process.cwd(), 'dist', 'client', clean),
164
+ join(process.cwd(), 'dist', clean)
165
+ ]
166
+
167
+ for (const path of candidatePaths) {
168
+ if (existsSync(path)) {
169
+ return await generateLqip(path, lqipType, lqipSize, isDevelopment)
170
+ }
141
171
  }
142
172
  }
143
173
 
144
- // if not found, try to recursively find the original source
145
- if (src.startsWith('/_astro/')) {
146
- const originalBase = extractOriginalFileName(src)
174
+ const fileName = normalizedSrc.split('/').pop() ?? ''
175
+ if (HASHED_FILENAME_REGEX.test(fileName)) {
176
+ const originalBase = extractOriginalFileName(normalizedSrc)
147
177
  const originalSource = await recursiveFind(originalBase)
148
178
 
149
179
  if (originalSource) {