vite-plugin-sri4 1.7.0 → 1.8.1

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/dist/index.cjs +113 -57
  2. package/dist/index.js +113 -57
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -9,28 +9,21 @@ const DEFAULT_OPTIONS = {
9
9
  algorithm: 'sha384',
10
10
  bypassDomains: [],
11
11
  crossorigin: 'anonymous',
12
- debug: false
12
+ debug: false,
13
+ inlineScripts: false
13
14
  };
14
15
 
15
- /**
16
- * Log debug messages if debug mode is enabled
17
- * @param {string} message - Message to log
18
- * @param {Object} options - Plugin options
19
- */
20
16
  function log(message, options) {
21
17
  if (options.debug) {
22
18
  console.log(`${LOG_PREFIX} ${message}`);
23
19
  }
24
20
  }
25
21
 
26
- /**
27
- * Compute SRI hash for given content
28
- * @param {string|Buffer} content - Content to hash
29
- * @param {string} algorithm - Hash algorithm to use
30
- * @returns {string|null} SRI hash string or null if failed
31
- */
32
22
  function computeSri(content, algorithm = 'sha384') {
33
23
  try {
24
+ if (typeof content === 'string') {
25
+ content = Buffer.from(content, 'utf-8');
26
+ }
34
27
  const hash = node_crypto.createHash(algorithm)
35
28
  .update(content)
36
29
  .digest('base64');
@@ -41,17 +34,10 @@ function computeSri(content, algorithm = 'sha384') {
41
34
  }
42
35
  }
43
36
 
44
- /**
45
- * Check if external resource has CORS enabled
46
- * @param {string} url - URL to check
47
- * @param {Object} options - Plugin options
48
- * @returns {Promise<boolean>} Whether CORS is enabled
49
- */
50
37
  async function externalResourceIsCorsEnabled(url, options) {
51
38
  try {
52
39
  const response = await fetch(url, {
53
- method: 'HEAD',
54
- timeout: 5000
40
+ method: 'HEAD'
55
41
  });
56
42
  const acao = response.headers.get('access-control-allow-origin');
57
43
  if (acao && (acao === '*' || acao.includes(options.domain || ''))) {
@@ -64,50 +50,60 @@ async function externalResourceIsCorsEnabled(url, options) {
64
50
  }
65
51
  }
66
52
 
67
- /**
68
- * Check if URL belongs to bypass domains
69
- * @param {string} url - URL to check
70
- * @param {string[]} bypassDomains - List of domains to bypass
71
- * @returns {boolean} Whether URL belongs to bypass domains
72
- */
73
53
  function isBypassDomain(url, bypassDomains = []) {
74
54
  if (!bypassDomains.length) return false;
75
55
  try {
76
- const parsedUrl = url.startsWith('//')
77
- ? new URL(`http:${url}`)
78
- : new URL(url, 'http://dummy');
56
+ let parsedUrl;
57
+ if (url.startsWith('//')) {
58
+ parsedUrl = new URL(`http:${url}`);
59
+ } else if (url.startsWith('http://') || url.startsWith('https://')) {
60
+ parsedUrl = new URL(url);
61
+ } else {
62
+ parsedUrl = new URL(url, 'http://dummy');
63
+ }
64
+
79
65
  return bypassDomains.some(
80
66
  (domain) => parsedUrl.hostname === domain ||
81
67
  parsedUrl.hostname.endsWith(`.${domain}`)
82
68
  );
83
69
  } catch (e) {
70
+ console.error(`${LOG_PREFIX} Failed to parse URL: ${url}`, e);
84
71
  return false;
85
72
  }
86
73
  }
87
74
 
88
- /**
89
- * Check if tag already has crossorigin attribute
90
- * @param {string} tag - HTML tag to check
91
- * @returns {boolean} Whether tag has crossorigin attribute
92
- */
93
75
  function hasCrossOriginAttr(tag) {
94
76
  return /(?:^|\s)crossorigin(?:=["']?[^"'\s>]*["']?)?(?:\s|>|$)/i.test(tag);
95
77
  }
96
78
 
97
- /**
98
- * Process individual HTML tags and add SRI attributes
99
- * @param {string} tag - HTML tag to process
100
- * @param {string} url - Resource URL
101
- * @param {Object} options - Plugin options
102
- * @param {Map} sriMap - Map of file names to SRI hashes
103
- * @returns {Promise<string>} Processed HTML tag
104
- */
79
+ function getAllPossiblePaths(url) {
80
+ const paths = [
81
+ url,
82
+ url.startsWith('/') ? url.slice(1) : url,
83
+ url.startsWith('/static/') ? url.slice(8) : url,
84
+ !url.startsWith('/static/') ? `static/${url}` : url,
85
+ !url.startsWith('/static/') ? `/static/${url}` : url,
86
+ url.replace(/^\/static\//, ''),
87
+ url.replace(/^static\//, '')
88
+ ];
89
+
90
+ const withoutHash = url.replace(/-[a-zA-Z0-9]+\./, '.');
91
+ if (withoutHash !== url) {
92
+ paths.push(...getAllPossiblePaths(withoutHash));
93
+ }
94
+
95
+ return [...new Set(paths)];
96
+ }
97
+
105
98
  async function processTag(tag, url, options, sriMap) {
106
99
  if (tag.includes('integrity=')) {
107
100
  log(`Skip tag with existing integrity attribute: ${tag}`, options);
108
101
  return tag;
109
102
  }
110
103
 
104
+ log(`Processing tag: ${tag}`, options);
105
+ log(`URL: ${url}`, options);
106
+
111
107
  // Handle external resources
112
108
  if (/^(https?:)?\/\//i.test(url)) {
113
109
  if (isBypassDomain(url, options.bypassDomains)) {
@@ -129,7 +125,9 @@ async function processTag(tag, url, options, sriMap) {
129
125
  log(`Computing SRI for external resource ${url}: ${hash}`, options);
130
126
  const hasCrossOrigin = hasCrossOriginAttr(tag);
131
127
  const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
132
- return tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
128
+ const newTag = tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
129
+ log(`New tag: ${newTag}`, options);
130
+ return newTag;
133
131
  }
134
132
  } catch (error) {
135
133
  log(`Failed to process external resource ${url}: ${error}`, options);
@@ -138,24 +136,56 @@ async function processTag(tag, url, options, sriMap) {
138
136
  }
139
137
 
140
138
  // Handle local resources
141
- const fileName = url.startsWith('/') ? url.slice(1) : url;
142
- const integrity = sriMap.get(fileName);
139
+ const possiblePaths = getAllPossiblePaths(url);
140
+ let integrity = null;
141
+
142
+ log(`Checking possible paths for ${url}:`, options);
143
+ for (const path of possiblePaths) {
144
+ log(`- Checking path: ${path}`, options);
145
+ if (sriMap.has(path)) {
146
+ integrity = sriMap.get(path);
147
+ log(`Found integrity for path ${path}: ${integrity}`, options);
148
+ break;
149
+ }
150
+ }
151
+
143
152
  if (integrity) {
144
- log(`Using precomputed SRI for ${fileName}: ${integrity}`, options);
153
+ log(`Using precomputed SRI for ${url}: ${integrity}`, options);
145
154
  const hasCrossOrigin = hasCrossOriginAttr(tag);
146
155
  const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
147
- return tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
156
+ const newTag = tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
157
+ log(`New tag: ${newTag}`, options);
158
+ return newTag;
159
+ }
160
+
161
+ log(`No SRI hash found for ${url}`, options);
162
+ log(`Available paths in sriMap: ${Array.from(sriMap.keys()).join(', ')}`, options);
163
+ return tag;
164
+ }
165
+
166
+ async function processInlineScript(tag, options) {
167
+ if (tag.includes('integrity=')) {
168
+ log(`Skip inline script with existing integrity attribute: ${tag}`, options);
169
+ return tag;
170
+ }
171
+
172
+ const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
173
+ if (!content) {
174
+ log(`Skip empty inline script: ${tag}`, options);
175
+ return tag;
176
+ }
177
+
178
+ const hash = computeSri(content, options.algorithm);
179
+ if (hash) {
180
+ log(`Computing SRI for inline script: ${hash}`, options);
181
+ const newTag = tag.replace('>', ` integrity="${hash}">`);
182
+ log(`New inline script tag: ${newTag}`, options);
183
+ return newTag;
148
184
  }
149
185
 
150
- log(`No SRI hash found for ${fileName}`, options);
151
186
  return tag;
152
187
  }
153
188
 
154
- /**
155
- * Vite plugin for adding Subresource Integrity (SRI) hashes to assets
156
- * @param {Object} userOptions - Plugin options
157
- * @returns {import('vite').Plugin} Vite plugin object
158
- */
159
189
  function sri(userOptions = {}) {
160
190
  const options = { ...DEFAULT_OPTIONS, ...userOptions };
161
191
  const sriMap = new Map();
@@ -177,8 +207,10 @@ function sri(userOptions = {}) {
177
207
 
178
208
  const hash = computeSri(code, options.algorithm);
179
209
  if (hash) {
180
- sriMap.set(chunk.fileName, hash);
181
- log(`Computing SRI for chunk ${chunk.fileName}: ${hash}`, options);
210
+ for (const path of getAllPossiblePaths(chunk.fileName)) {
211
+ sriMap.set(path, hash);
212
+ log(`Stored SRI for path ${path}: ${hash}`, options);
213
+ }
182
214
  }
183
215
  return null;
184
216
  },
@@ -191,11 +223,18 @@ function sri(userOptions = {}) {
191
223
  if (chunk.type === 'asset' && !sriMap.has(fileName)) {
192
224
  const hash = computeSri(chunk.source, options.algorithm);
193
225
  if (hash) {
194
- sriMap.set(fileName, hash);
195
- log(`Computing SRI for asset ${fileName}: ${hash}`, options);
226
+ for (const path of getAllPossiblePaths(fileName)) {
227
+ sriMap.set(path, hash);
228
+ log(`Computing SRI for asset ${path}: ${hash}`, options);
229
+ }
196
230
  }
197
231
  }
198
232
  }
233
+
234
+ log('Final sriMap contents:', options);
235
+ for (const [key, value] of sriMap.entries()) {
236
+ log(`${key} => ${value}`, options);
237
+ }
199
238
  },
200
239
 
201
240
  async transformIndexHtml(html) {
@@ -205,18 +244,35 @@ function sri(userOptions = {}) {
205
244
  }
206
245
 
207
246
  try {
247
+ log('Starting HTML transformation', options);
248
+ log(`SRI Map size: ${sriMap.size}`, options);
249
+
208
250
  // Process script tags
209
251
  const scriptTags = html.match(/<script[^>]+src=["']([^"']+)["'][^>]*>/g) || [];
252
+ log(`Found ${scriptTags.length} script tags`, options);
210
253
  for (const tag of scriptTags) {
211
254
  const url = tag.match(/src=["']([^"']+)["']/)[1];
255
+ log(`Processing script: ${url}`, options);
212
256
  const newTag = await processTag(tag, url, options, sriMap);
213
257
  html = html.replace(tag, newTag);
214
258
  }
215
259
 
260
+ // Process inline scripts if enabled
261
+ if (options.inlineScripts) {
262
+ const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
263
+ log(`Found ${inlineScripts.length} inline script tags`, options);
264
+ for (const tag of inlineScripts) {
265
+ const newTag = await processInlineScript(tag, options);
266
+ html = html.replace(tag, newTag);
267
+ }
268
+ }
269
+
216
270
  // Process link tags
217
271
  const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
272
+ log(`Found ${linkTags.length} link tags`, options);
218
273
  for (const tag of linkTags) {
219
274
  const url = tag.match(/href=["']([^"']+)["']/)[1];
275
+ log(`Processing link: ${url}`, options);
220
276
  const newTag = await processTag(tag, url, options, sriMap);
221
277
  html = html.replace(tag, newTag);
222
278
  }
package/dist/index.js CHANGED
@@ -7,28 +7,21 @@ const DEFAULT_OPTIONS = {
7
7
  algorithm: 'sha384',
8
8
  bypassDomains: [],
9
9
  crossorigin: 'anonymous',
10
- debug: false
10
+ debug: false,
11
+ inlineScripts: false
11
12
  };
12
13
 
13
- /**
14
- * Log debug messages if debug mode is enabled
15
- * @param {string} message - Message to log
16
- * @param {Object} options - Plugin options
17
- */
18
14
  function log(message, options) {
19
15
  if (options.debug) {
20
16
  console.log(`${LOG_PREFIX} ${message}`);
21
17
  }
22
18
  }
23
19
 
24
- /**
25
- * Compute SRI hash for given content
26
- * @param {string|Buffer} content - Content to hash
27
- * @param {string} algorithm - Hash algorithm to use
28
- * @returns {string|null} SRI hash string or null if failed
29
- */
30
20
  function computeSri(content, algorithm = 'sha384') {
31
21
  try {
22
+ if (typeof content === 'string') {
23
+ content = Buffer.from(content, 'utf-8');
24
+ }
32
25
  const hash = createHash(algorithm)
33
26
  .update(content)
34
27
  .digest('base64');
@@ -39,17 +32,10 @@ function computeSri(content, algorithm = 'sha384') {
39
32
  }
40
33
  }
41
34
 
42
- /**
43
- * Check if external resource has CORS enabled
44
- * @param {string} url - URL to check
45
- * @param {Object} options - Plugin options
46
- * @returns {Promise<boolean>} Whether CORS is enabled
47
- */
48
35
  async function externalResourceIsCorsEnabled(url, options) {
49
36
  try {
50
37
  const response = await fetch(url, {
51
- method: 'HEAD',
52
- timeout: 5000
38
+ method: 'HEAD'
53
39
  });
54
40
  const acao = response.headers.get('access-control-allow-origin');
55
41
  if (acao && (acao === '*' || acao.includes(options.domain || ''))) {
@@ -62,50 +48,60 @@ async function externalResourceIsCorsEnabled(url, options) {
62
48
  }
63
49
  }
64
50
 
65
- /**
66
- * Check if URL belongs to bypass domains
67
- * @param {string} url - URL to check
68
- * @param {string[]} bypassDomains - List of domains to bypass
69
- * @returns {boolean} Whether URL belongs to bypass domains
70
- */
71
51
  function isBypassDomain(url, bypassDomains = []) {
72
52
  if (!bypassDomains.length) return false;
73
53
  try {
74
- const parsedUrl = url.startsWith('//')
75
- ? new URL(`http:${url}`)
76
- : new URL(url, 'http://dummy');
54
+ let parsedUrl;
55
+ if (url.startsWith('//')) {
56
+ parsedUrl = new URL(`http:${url}`);
57
+ } else if (url.startsWith('http://') || url.startsWith('https://')) {
58
+ parsedUrl = new URL(url);
59
+ } else {
60
+ parsedUrl = new URL(url, 'http://dummy');
61
+ }
62
+
77
63
  return bypassDomains.some(
78
64
  (domain) => parsedUrl.hostname === domain ||
79
65
  parsedUrl.hostname.endsWith(`.${domain}`)
80
66
  );
81
67
  } catch (e) {
68
+ console.error(`${LOG_PREFIX} Failed to parse URL: ${url}`, e);
82
69
  return false;
83
70
  }
84
71
  }
85
72
 
86
- /**
87
- * Check if tag already has crossorigin attribute
88
- * @param {string} tag - HTML tag to check
89
- * @returns {boolean} Whether tag has crossorigin attribute
90
- */
91
73
  function hasCrossOriginAttr(tag) {
92
74
  return /(?:^|\s)crossorigin(?:=["']?[^"'\s>]*["']?)?(?:\s|>|$)/i.test(tag);
93
75
  }
94
76
 
95
- /**
96
- * Process individual HTML tags and add SRI attributes
97
- * @param {string} tag - HTML tag to process
98
- * @param {string} url - Resource URL
99
- * @param {Object} options - Plugin options
100
- * @param {Map} sriMap - Map of file names to SRI hashes
101
- * @returns {Promise<string>} Processed HTML tag
102
- */
77
+ function getAllPossiblePaths(url) {
78
+ const paths = [
79
+ url,
80
+ url.startsWith('/') ? url.slice(1) : url,
81
+ url.startsWith('/static/') ? url.slice(8) : url,
82
+ !url.startsWith('/static/') ? `static/${url}` : url,
83
+ !url.startsWith('/static/') ? `/static/${url}` : url,
84
+ url.replace(/^\/static\//, ''),
85
+ url.replace(/^static\//, '')
86
+ ];
87
+
88
+ const withoutHash = url.replace(/-[a-zA-Z0-9]+\./, '.');
89
+ if (withoutHash !== url) {
90
+ paths.push(...getAllPossiblePaths(withoutHash));
91
+ }
92
+
93
+ return [...new Set(paths)];
94
+ }
95
+
103
96
  async function processTag(tag, url, options, sriMap) {
104
97
  if (tag.includes('integrity=')) {
105
98
  log(`Skip tag with existing integrity attribute: ${tag}`, options);
106
99
  return tag;
107
100
  }
108
101
 
102
+ log(`Processing tag: ${tag}`, options);
103
+ log(`URL: ${url}`, options);
104
+
109
105
  // Handle external resources
110
106
  if (/^(https?:)?\/\//i.test(url)) {
111
107
  if (isBypassDomain(url, options.bypassDomains)) {
@@ -127,7 +123,9 @@ async function processTag(tag, url, options, sriMap) {
127
123
  log(`Computing SRI for external resource ${url}: ${hash}`, options);
128
124
  const hasCrossOrigin = hasCrossOriginAttr(tag);
129
125
  const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
130
- return tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
126
+ const newTag = tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
127
+ log(`New tag: ${newTag}`, options);
128
+ return newTag;
131
129
  }
132
130
  } catch (error) {
133
131
  log(`Failed to process external resource ${url}: ${error}`, options);
@@ -136,24 +134,56 @@ async function processTag(tag, url, options, sriMap) {
136
134
  }
137
135
 
138
136
  // Handle local resources
139
- const fileName = url.startsWith('/') ? url.slice(1) : url;
140
- const integrity = sriMap.get(fileName);
137
+ const possiblePaths = getAllPossiblePaths(url);
138
+ let integrity = null;
139
+
140
+ log(`Checking possible paths for ${url}:`, options);
141
+ for (const path of possiblePaths) {
142
+ log(`- Checking path: ${path}`, options);
143
+ if (sriMap.has(path)) {
144
+ integrity = sriMap.get(path);
145
+ log(`Found integrity for path ${path}: ${integrity}`, options);
146
+ break;
147
+ }
148
+ }
149
+
141
150
  if (integrity) {
142
- log(`Using precomputed SRI for ${fileName}: ${integrity}`, options);
151
+ log(`Using precomputed SRI for ${url}: ${integrity}`, options);
143
152
  const hasCrossOrigin = hasCrossOriginAttr(tag);
144
153
  const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
145
- return tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
154
+ const newTag = tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
155
+ log(`New tag: ${newTag}`, options);
156
+ return newTag;
157
+ }
158
+
159
+ log(`No SRI hash found for ${url}`, options);
160
+ log(`Available paths in sriMap: ${Array.from(sriMap.keys()).join(', ')}`, options);
161
+ return tag;
162
+ }
163
+
164
+ async function processInlineScript(tag, options) {
165
+ if (tag.includes('integrity=')) {
166
+ log(`Skip inline script with existing integrity attribute: ${tag}`, options);
167
+ return tag;
168
+ }
169
+
170
+ const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
171
+ if (!content) {
172
+ log(`Skip empty inline script: ${tag}`, options);
173
+ return tag;
174
+ }
175
+
176
+ const hash = computeSri(content, options.algorithm);
177
+ if (hash) {
178
+ log(`Computing SRI for inline script: ${hash}`, options);
179
+ const newTag = tag.replace('>', ` integrity="${hash}">`);
180
+ log(`New inline script tag: ${newTag}`, options);
181
+ return newTag;
146
182
  }
147
183
 
148
- log(`No SRI hash found for ${fileName}`, options);
149
184
  return tag;
150
185
  }
151
186
 
152
- /**
153
- * Vite plugin for adding Subresource Integrity (SRI) hashes to assets
154
- * @param {Object} userOptions - Plugin options
155
- * @returns {import('vite').Plugin} Vite plugin object
156
- */
157
187
  function sri(userOptions = {}) {
158
188
  const options = { ...DEFAULT_OPTIONS, ...userOptions };
159
189
  const sriMap = new Map();
@@ -175,8 +205,10 @@ function sri(userOptions = {}) {
175
205
 
176
206
  const hash = computeSri(code, options.algorithm);
177
207
  if (hash) {
178
- sriMap.set(chunk.fileName, hash);
179
- log(`Computing SRI for chunk ${chunk.fileName}: ${hash}`, options);
208
+ for (const path of getAllPossiblePaths(chunk.fileName)) {
209
+ sriMap.set(path, hash);
210
+ log(`Stored SRI for path ${path}: ${hash}`, options);
211
+ }
180
212
  }
181
213
  return null;
182
214
  },
@@ -189,11 +221,18 @@ function sri(userOptions = {}) {
189
221
  if (chunk.type === 'asset' && !sriMap.has(fileName)) {
190
222
  const hash = computeSri(chunk.source, options.algorithm);
191
223
  if (hash) {
192
- sriMap.set(fileName, hash);
193
- log(`Computing SRI for asset ${fileName}: ${hash}`, options);
224
+ for (const path of getAllPossiblePaths(fileName)) {
225
+ sriMap.set(path, hash);
226
+ log(`Computing SRI for asset ${path}: ${hash}`, options);
227
+ }
194
228
  }
195
229
  }
196
230
  }
231
+
232
+ log('Final sriMap contents:', options);
233
+ for (const [key, value] of sriMap.entries()) {
234
+ log(`${key} => ${value}`, options);
235
+ }
197
236
  },
198
237
 
199
238
  async transformIndexHtml(html) {
@@ -203,18 +242,35 @@ function sri(userOptions = {}) {
203
242
  }
204
243
 
205
244
  try {
245
+ log('Starting HTML transformation', options);
246
+ log(`SRI Map size: ${sriMap.size}`, options);
247
+
206
248
  // Process script tags
207
249
  const scriptTags = html.match(/<script[^>]+src=["']([^"']+)["'][^>]*>/g) || [];
250
+ log(`Found ${scriptTags.length} script tags`, options);
208
251
  for (const tag of scriptTags) {
209
252
  const url = tag.match(/src=["']([^"']+)["']/)[1];
253
+ log(`Processing script: ${url}`, options);
210
254
  const newTag = await processTag(tag, url, options, sriMap);
211
255
  html = html.replace(tag, newTag);
212
256
  }
213
257
 
258
+ // Process inline scripts if enabled
259
+ if (options.inlineScripts) {
260
+ const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
261
+ log(`Found ${inlineScripts.length} inline script tags`, options);
262
+ for (const tag of inlineScripts) {
263
+ const newTag = await processInlineScript(tag, options);
264
+ html = html.replace(tag, newTag);
265
+ }
266
+ }
267
+
214
268
  // Process link tags
215
269
  const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
270
+ log(`Found ${linkTags.length} link tags`, options);
216
271
  for (const tag of linkTags) {
217
272
  const url = tag.match(/href=["']([^"']+)["']/)[1];
273
+ log(`Processing link: ${url}`, options);
218
274
  const newTag = await processTag(tag, url, options, sriMap);
219
275
  html = html.replace(tag, newTag);
220
276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-sri4",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "A Vite plugin to generate Subresource Integrity (SRI) hashes for output files.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",