vite-plugin-sri4 1.6.0 → 1.7.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 (3) hide show
  1. package/dist/index.cjs +140 -90
  2. package/dist/index.js +140 -90
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3,6 +3,8 @@
3
3
  var node_crypto = require('node:crypto');
4
4
  var fetch = require('cross-fetch');
5
5
 
6
+ const LOG_PREFIX = '[vite-plugin-sri4]';
7
+
6
8
  const DEFAULT_OPTIONS = {
7
9
  algorithm: 'sha384',
8
10
  bypassDomains: [],
@@ -10,12 +12,23 @@ const DEFAULT_OPTIONS = {
10
12
  debug: false
11
13
  };
12
14
 
15
+ /**
16
+ * Log debug messages if debug mode is enabled
17
+ * @param {string} message - Message to log
18
+ * @param {Object} options - Plugin options
19
+ */
13
20
  function log(message, options) {
14
21
  if (options.debug) {
15
- console.log(`[vite-plugin-sri4] ${message}`);
22
+ console.log(`${LOG_PREFIX} ${message}`);
16
23
  }
17
24
  }
18
25
 
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
+ */
19
32
  function computeSri(content, algorithm = 'sha384') {
20
33
  try {
21
34
  const hash = node_crypto.createHash(algorithm)
@@ -23,11 +36,17 @@ function computeSri(content, algorithm = 'sha384') {
23
36
  .digest('base64');
24
37
  return `${algorithm}-${hash}`;
25
38
  } catch (error) {
26
- console.error(`[vite-plugin-sri4] Failed to compute SRI hash: ${error}`);
39
+ console.error(`${LOG_PREFIX} Failed to compute SRI hash: ${error}`);
27
40
  return null;
28
41
  }
29
42
  }
30
43
 
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
+ */
31
50
  async function externalResourceIsCorsEnabled(url, options) {
32
51
  try {
33
52
  const response = await fetch(url, {
@@ -45,38 +64,18 @@ async function externalResourceIsCorsEnabled(url, options) {
45
64
  }
46
65
  }
47
66
 
48
- async function replaceAsync(str, regex, asyncFn) {
49
- const promises = [];
50
- const matches = [];
51
-
52
- str.replace(regex, (...args) => {
53
- matches.push(args);
54
- promises.push(asyncFn(...args));
55
- return '';
56
- });
57
-
58
- const results = await Promise.all(promises);
59
-
60
- let lastIndex = 0;
61
- let result = '';
62
-
63
- for (let i = 0; i < matches.length; i++) {
64
- const match = matches[i][0];
65
- const index = str.indexOf(match, lastIndex);
66
- result += str.slice(lastIndex, index) + results[i];
67
- lastIndex = index + match.length;
68
- }
69
-
70
- result += str.slice(lastIndex);
71
- return result;
72
- }
73
-
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
+ */
74
73
  function isBypassDomain(url, bypassDomains = []) {
75
74
  if (!bypassDomains.length) return false;
76
75
  try {
77
76
  const parsedUrl = url.startsWith('//')
78
- ? new URL(url, 'http://dummy')
79
- : new URL(url);
77
+ ? new URL(`http:${url}`)
78
+ : new URL(url, 'http://dummy');
80
79
  return bypassDomains.some(
81
80
  (domain) => parsedUrl.hostname === domain ||
82
81
  parsedUrl.hostname.endsWith(`.${domain}`)
@@ -86,96 +85,147 @@ function isBypassDomain(url, bypassDomains = []) {
86
85
  }
87
86
  }
88
87
 
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
+ */
89
93
  function hasCrossOriginAttr(tag) {
90
94
  return /(?:^|\s)crossorigin(?:=["']?[^"'\s>]*["']?)?(?:\s|>|$)/i.test(tag);
91
95
  }
92
96
 
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
+ */
105
+ async function processTag(tag, url, options, sriMap) {
106
+ if (tag.includes('integrity=')) {
107
+ log(`Skip tag with existing integrity attribute: ${tag}`, options);
108
+ return tag;
109
+ }
110
+
111
+ // Handle external resources
112
+ if (/^(https?:)?\/\//i.test(url)) {
113
+ if (isBypassDomain(url, options.bypassDomains)) {
114
+ log(`Skip SRI for bypass domain: ${url}`, options);
115
+ return tag;
116
+ }
117
+
118
+ const corsOk = await externalResourceIsCorsEnabled(url, options);
119
+ if (!corsOk) {
120
+ log(`External resource ${url} does not support CORS`, options);
121
+ return tag;
122
+ }
123
+
124
+ try {
125
+ const response = await fetch(url);
126
+ const content = await response.arrayBuffer();
127
+ const hash = computeSri(Buffer.from(content), options.algorithm);
128
+ if (hash) {
129
+ log(`Computing SRI for external resource ${url}: ${hash}`, options);
130
+ const hasCrossOrigin = hasCrossOriginAttr(tag);
131
+ const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
132
+ return tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
133
+ }
134
+ } catch (error) {
135
+ log(`Failed to process external resource ${url}: ${error}`, options);
136
+ }
137
+ return tag;
138
+ }
139
+
140
+ // Handle local resources
141
+ const fileName = url.startsWith('/') ? url.slice(1) : url;
142
+ const integrity = sriMap.get(fileName);
143
+ if (integrity) {
144
+ log(`Using precomputed SRI for ${fileName}: ${integrity}`, options);
145
+ const hasCrossOrigin = hasCrossOriginAttr(tag);
146
+ const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
147
+ return tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
148
+ }
149
+
150
+ log(`No SRI hash found for ${fileName}`, options);
151
+ return tag;
152
+ }
153
+
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
+ */
93
159
  function sri(userOptions = {}) {
94
160
  const options = { ...DEFAULT_OPTIONS, ...userOptions };
95
161
  const sriMap = new Map();
162
+ let isBuild = false;
96
163
 
97
164
  return {
98
165
  name: 'vite-plugin-sri4',
99
166
  apply: 'build',
167
+ enforce: 'post',
100
168
 
101
169
  configResolved(config) {
102
170
  options.domain = config.server?.host || '';
171
+ isBuild = config.command === 'build';
172
+ log('Plugin configured in ' + (isBuild ? 'build' : 'dev') + ' mode', options);
173
+ },
174
+
175
+ async renderChunk(code, chunk) {
176
+ if (!isBuild) return null;
177
+
178
+ const hash = computeSri(code, options.algorithm);
179
+ if (hash) {
180
+ sriMap.set(chunk.fileName, hash);
181
+ log(`Computing SRI for chunk ${chunk.fileName}: ${hash}`, options);
182
+ }
183
+ return null;
103
184
  },
104
185
 
105
186
  async generateBundle(_, bundle) {
187
+ if (!isBuild) return;
188
+
106
189
  for (const fileName in bundle) {
107
190
  const chunk = bundle[fileName];
108
- if (chunk.type === 'chunk' || chunk.type === 'asset') {
109
- const content = chunk.code || chunk.source;
110
- if (content) {
111
- const hash = computeSri(content, options.algorithm);
112
- if (hash) {
113
- sriMap.set(fileName, hash);
114
- log(`Computed SRI for ${fileName}: ${hash}`, options);
115
- }
191
+ if (chunk.type === 'asset' && !sriMap.has(fileName)) {
192
+ const hash = computeSri(chunk.source, options.algorithm);
193
+ if (hash) {
194
+ sriMap.set(fileName, hash);
195
+ log(`Computing SRI for asset ${fileName}: ${hash}`, options);
116
196
  }
117
197
  }
118
198
  }
119
199
  },
120
200
 
121
201
  async transformIndexHtml(html) {
122
- const hasIntegrity = (tag) => /integrity=/i.test(tag);
123
- const isExternalUrl = (url) => /^(https?:)?\/\//i.test(url);
124
-
125
- const processTag = async (match, tag, src, moduleSrc) => {
126
- const actualSrc = src || moduleSrc;
127
-
128
- if (hasIntegrity(tag)) {
129
- return tag;
130
- }
202
+ if (!isBuild || !html) {
203
+ log('Skipping HTML transform in dev mode or empty HTML', options);
204
+ return html;
205
+ }
131
206
 
132
- if (isExternalUrl(actualSrc) &&
133
- isBypassDomain(actualSrc, options.bypassDomains)) {
134
- log(`Skipping SRI injection for bypass domain: ${actualSrc}`, options);
135
- return tag;
207
+ try {
208
+ // Process script tags
209
+ const scriptTags = html.match(/<script[^>]+src=["']([^"']+)["'][^>]*>/g) || [];
210
+ for (const tag of scriptTags) {
211
+ const url = tag.match(/src=["']([^"']+)["']/)[1];
212
+ const newTag = await processTag(tag, url, options, sriMap);
213
+ html = html.replace(tag, newTag);
136
214
  }
137
215
 
138
- if (isExternalUrl(actualSrc)) {
139
- const corsOk = await externalResourceIsCorsEnabled(actualSrc, options);
140
- if (!corsOk) {
141
- log(`External resource ${actualSrc} does not support CORS`, options);
142
- return tag;
143
- }
144
- }
145
-
146
- const fileName = actualSrc.startsWith('/') ? actualSrc.slice(1) : actualSrc;
147
- const integrity = sriMap.get(fileName);
148
-
149
- if (integrity) {
150
- const hasCrossOrigin = hasCrossOriginAttr(tag);
151
- if (!hasCrossOrigin) {
152
- return tag.replace(
153
- />$/,
154
- ` integrity="${integrity}" crossorigin="${options.crossorigin}">`
155
- );
156
- }
157
- return tag.replace(
158
- />$/,
159
- ` integrity="${integrity}">`
160
- );
216
+ // Process link tags
217
+ const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
218
+ for (const tag of linkTags) {
219
+ const url = tag.match(/href=["']([^"']+)["']/)[1];
220
+ const newTag = await processTag(tag, url, options, sriMap);
221
+ html = html.replace(tag, newTag);
161
222
  }
162
223
 
163
- return tag;
164
- };
165
-
166
- html = await replaceAsync(
167
- html,
168
- /(<script[^>]+(?:src="([^"]+)"[^>]*|type="module"[^>]*src="([^"]+)"[^>]*)>)/g,
169
- processTag
170
- );
171
-
172
- html = await replaceAsync(
173
- html,
174
- /(<link[^>]+href="([^"]+)"[^>]*>)/g,
175
- processTag
176
- );
177
-
178
- return html;
224
+ return html;
225
+ } catch (error) {
226
+ console.error(`${LOG_PREFIX} Failed to transform HTML: ${error}`);
227
+ return html;
228
+ }
179
229
  }
180
230
  };
181
231
  }
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import fetch from 'cross-fetch';
3
3
 
4
+ const LOG_PREFIX = '[vite-plugin-sri4]';
5
+
4
6
  const DEFAULT_OPTIONS = {
5
7
  algorithm: 'sha384',
6
8
  bypassDomains: [],
@@ -8,12 +10,23 @@ const DEFAULT_OPTIONS = {
8
10
  debug: false
9
11
  };
10
12
 
13
+ /**
14
+ * Log debug messages if debug mode is enabled
15
+ * @param {string} message - Message to log
16
+ * @param {Object} options - Plugin options
17
+ */
11
18
  function log(message, options) {
12
19
  if (options.debug) {
13
- console.log(`[vite-plugin-sri4] ${message}`);
20
+ console.log(`${LOG_PREFIX} ${message}`);
14
21
  }
15
22
  }
16
23
 
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
+ */
17
30
  function computeSri(content, algorithm = 'sha384') {
18
31
  try {
19
32
  const hash = createHash(algorithm)
@@ -21,11 +34,17 @@ function computeSri(content, algorithm = 'sha384') {
21
34
  .digest('base64');
22
35
  return `${algorithm}-${hash}`;
23
36
  } catch (error) {
24
- console.error(`[vite-plugin-sri4] Failed to compute SRI hash: ${error}`);
37
+ console.error(`${LOG_PREFIX} Failed to compute SRI hash: ${error}`);
25
38
  return null;
26
39
  }
27
40
  }
28
41
 
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
+ */
29
48
  async function externalResourceIsCorsEnabled(url, options) {
30
49
  try {
31
50
  const response = await fetch(url, {
@@ -43,38 +62,18 @@ async function externalResourceIsCorsEnabled(url, options) {
43
62
  }
44
63
  }
45
64
 
46
- async function replaceAsync(str, regex, asyncFn) {
47
- const promises = [];
48
- const matches = [];
49
-
50
- str.replace(regex, (...args) => {
51
- matches.push(args);
52
- promises.push(asyncFn(...args));
53
- return '';
54
- });
55
-
56
- const results = await Promise.all(promises);
57
-
58
- let lastIndex = 0;
59
- let result = '';
60
-
61
- for (let i = 0; i < matches.length; i++) {
62
- const match = matches[i][0];
63
- const index = str.indexOf(match, lastIndex);
64
- result += str.slice(lastIndex, index) + results[i];
65
- lastIndex = index + match.length;
66
- }
67
-
68
- result += str.slice(lastIndex);
69
- return result;
70
- }
71
-
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
+ */
72
71
  function isBypassDomain(url, bypassDomains = []) {
73
72
  if (!bypassDomains.length) return false;
74
73
  try {
75
74
  const parsedUrl = url.startsWith('//')
76
- ? new URL(url, 'http://dummy')
77
- : new URL(url);
75
+ ? new URL(`http:${url}`)
76
+ : new URL(url, 'http://dummy');
78
77
  return bypassDomains.some(
79
78
  (domain) => parsedUrl.hostname === domain ||
80
79
  parsedUrl.hostname.endsWith(`.${domain}`)
@@ -84,96 +83,147 @@ function isBypassDomain(url, bypassDomains = []) {
84
83
  }
85
84
  }
86
85
 
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
+ */
87
91
  function hasCrossOriginAttr(tag) {
88
92
  return /(?:^|\s)crossorigin(?:=["']?[^"'\s>]*["']?)?(?:\s|>|$)/i.test(tag);
89
93
  }
90
94
 
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
+ */
103
+ async function processTag(tag, url, options, sriMap) {
104
+ if (tag.includes('integrity=')) {
105
+ log(`Skip tag with existing integrity attribute: ${tag}`, options);
106
+ return tag;
107
+ }
108
+
109
+ // Handle external resources
110
+ if (/^(https?:)?\/\//i.test(url)) {
111
+ if (isBypassDomain(url, options.bypassDomains)) {
112
+ log(`Skip SRI for bypass domain: ${url}`, options);
113
+ return tag;
114
+ }
115
+
116
+ const corsOk = await externalResourceIsCorsEnabled(url, options);
117
+ if (!corsOk) {
118
+ log(`External resource ${url} does not support CORS`, options);
119
+ return tag;
120
+ }
121
+
122
+ try {
123
+ const response = await fetch(url);
124
+ const content = await response.arrayBuffer();
125
+ const hash = computeSri(Buffer.from(content), options.algorithm);
126
+ if (hash) {
127
+ log(`Computing SRI for external resource ${url}: ${hash}`, options);
128
+ const hasCrossOrigin = hasCrossOriginAttr(tag);
129
+ const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
130
+ return tag.replace(/>$/, ` integrity="${hash}"${crossOriginAttr}>`);
131
+ }
132
+ } catch (error) {
133
+ log(`Failed to process external resource ${url}: ${error}`, options);
134
+ }
135
+ return tag;
136
+ }
137
+
138
+ // Handle local resources
139
+ const fileName = url.startsWith('/') ? url.slice(1) : url;
140
+ const integrity = sriMap.get(fileName);
141
+ if (integrity) {
142
+ log(`Using precomputed SRI for ${fileName}: ${integrity}`, options);
143
+ const hasCrossOrigin = hasCrossOriginAttr(tag);
144
+ const crossOriginAttr = hasCrossOrigin ? '' : ` crossorigin="${options.crossorigin}"`;
145
+ return tag.replace(/>$/, ` integrity="${integrity}"${crossOriginAttr}>`);
146
+ }
147
+
148
+ log(`No SRI hash found for ${fileName}`, options);
149
+ return tag;
150
+ }
151
+
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
+ */
91
157
  function sri(userOptions = {}) {
92
158
  const options = { ...DEFAULT_OPTIONS, ...userOptions };
93
159
  const sriMap = new Map();
160
+ let isBuild = false;
94
161
 
95
162
  return {
96
163
  name: 'vite-plugin-sri4',
97
164
  apply: 'build',
165
+ enforce: 'post',
98
166
 
99
167
  configResolved(config) {
100
168
  options.domain = config.server?.host || '';
169
+ isBuild = config.command === 'build';
170
+ log('Plugin configured in ' + (isBuild ? 'build' : 'dev') + ' mode', options);
171
+ },
172
+
173
+ async renderChunk(code, chunk) {
174
+ if (!isBuild) return null;
175
+
176
+ const hash = computeSri(code, options.algorithm);
177
+ if (hash) {
178
+ sriMap.set(chunk.fileName, hash);
179
+ log(`Computing SRI for chunk ${chunk.fileName}: ${hash}`, options);
180
+ }
181
+ return null;
101
182
  },
102
183
 
103
184
  async generateBundle(_, bundle) {
185
+ if (!isBuild) return;
186
+
104
187
  for (const fileName in bundle) {
105
188
  const chunk = bundle[fileName];
106
- if (chunk.type === 'chunk' || chunk.type === 'asset') {
107
- const content = chunk.code || chunk.source;
108
- if (content) {
109
- const hash = computeSri(content, options.algorithm);
110
- if (hash) {
111
- sriMap.set(fileName, hash);
112
- log(`Computed SRI for ${fileName}: ${hash}`, options);
113
- }
189
+ if (chunk.type === 'asset' && !sriMap.has(fileName)) {
190
+ const hash = computeSri(chunk.source, options.algorithm);
191
+ if (hash) {
192
+ sriMap.set(fileName, hash);
193
+ log(`Computing SRI for asset ${fileName}: ${hash}`, options);
114
194
  }
115
195
  }
116
196
  }
117
197
  },
118
198
 
119
199
  async transformIndexHtml(html) {
120
- const hasIntegrity = (tag) => /integrity=/i.test(tag);
121
- const isExternalUrl = (url) => /^(https?:)?\/\//i.test(url);
122
-
123
- const processTag = async (match, tag, src, moduleSrc) => {
124
- const actualSrc = src || moduleSrc;
125
-
126
- if (hasIntegrity(tag)) {
127
- return tag;
128
- }
200
+ if (!isBuild || !html) {
201
+ log('Skipping HTML transform in dev mode or empty HTML', options);
202
+ return html;
203
+ }
129
204
 
130
- if (isExternalUrl(actualSrc) &&
131
- isBypassDomain(actualSrc, options.bypassDomains)) {
132
- log(`Skipping SRI injection for bypass domain: ${actualSrc}`, options);
133
- return tag;
205
+ try {
206
+ // Process script tags
207
+ const scriptTags = html.match(/<script[^>]+src=["']([^"']+)["'][^>]*>/g) || [];
208
+ for (const tag of scriptTags) {
209
+ const url = tag.match(/src=["']([^"']+)["']/)[1];
210
+ const newTag = await processTag(tag, url, options, sriMap);
211
+ html = html.replace(tag, newTag);
134
212
  }
135
213
 
136
- if (isExternalUrl(actualSrc)) {
137
- const corsOk = await externalResourceIsCorsEnabled(actualSrc, options);
138
- if (!corsOk) {
139
- log(`External resource ${actualSrc} does not support CORS`, options);
140
- return tag;
141
- }
142
- }
143
-
144
- const fileName = actualSrc.startsWith('/') ? actualSrc.slice(1) : actualSrc;
145
- const integrity = sriMap.get(fileName);
146
-
147
- if (integrity) {
148
- const hasCrossOrigin = hasCrossOriginAttr(tag);
149
- if (!hasCrossOrigin) {
150
- return tag.replace(
151
- />$/,
152
- ` integrity="${integrity}" crossorigin="${options.crossorigin}">`
153
- );
154
- }
155
- return tag.replace(
156
- />$/,
157
- ` integrity="${integrity}">`
158
- );
214
+ // Process link tags
215
+ const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
216
+ for (const tag of linkTags) {
217
+ const url = tag.match(/href=["']([^"']+)["']/)[1];
218
+ const newTag = await processTag(tag, url, options, sriMap);
219
+ html = html.replace(tag, newTag);
159
220
  }
160
221
 
161
- return tag;
162
- };
163
-
164
- html = await replaceAsync(
165
- html,
166
- /(<script[^>]+(?:src="([^"]+)"[^>]*|type="module"[^>]*src="([^"]+)"[^>]*)>)/g,
167
- processTag
168
- );
169
-
170
- html = await replaceAsync(
171
- html,
172
- /(<link[^>]+href="([^"]+)"[^>]*>)/g,
173
- processTag
174
- );
175
-
176
- return html;
222
+ return html;
223
+ } catch (error) {
224
+ console.error(`${LOG_PREFIX} Failed to transform HTML: ${error}`);
225
+ return html;
226
+ }
177
227
  }
178
228
  };
179
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-sri4",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
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",