vite-plugin-sri4 1.0.0 → 1.1.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 CHANGED
@@ -1,5 +1,8 @@
1
1
  # vite-plugin-sri4
2
2
 
3
+ ![NPM Version](https://img.shields.io/npm/v/vite-plugin-sri4)
4
+ [![codecov](https://codecov.io/gh/7a6163/vite-plugin-sri4/branch/main/graph/badge.svg)](https://codecov.io/gh/7a6163/vite-plugin-sri4)
5
+
3
6
  A Vite plugin to generate Subresource Integrity (SRI) hashes for your assets during the build process. This plugin computes SRI hashes for JavaScript and CSS files and injects them as `integrity` and `crossorigin="anonymous"` attributes into your HTML, ensuring your resources have not been tampered with when loaded by browsers.
4
7
 
5
8
  ## Features
package/dist/index.cjs CHANGED
@@ -1,191 +1,169 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
3
+ var node_crypto = require('node:crypto');
4
4
  var fetch = require('node-fetch');
5
5
 
6
- /**
7
- * Compute the SRI (Subresource Integrity) hash for the given content.
8
- * @param {string | Buffer} content - The content to hash.
9
- * @param {string} algorithm - The SHA algorithm to use (default is 'sha384').
10
- * @returns {string} - The SRI string in the format "algorithm-base64hash".
11
- */
6
+ const DEFAULT_OPTIONS = {
7
+ algorithm: 'sha384',
8
+ bypassDomains: [],
9
+ crossorigin: 'anonymous',
10
+ debug: false
11
+ };
12
+
13
+ function log(message, options) {
14
+ if (options.debug) {
15
+ console.log(`[vite-plugin-sri4] ${message}`);
16
+ }
17
+ }
18
+
12
19
  function computeSri(content, algorithm = 'sha384') {
13
- const hash = crypto.createHash(algorithm)
14
- .update(content)
15
- .digest('base64');
16
- return `${algorithm}-${hash}`;
20
+ try {
21
+ const hash = node_crypto.createHash(algorithm)
22
+ .update(content)
23
+ .digest('base64');
24
+ return `${algorithm}-${hash}`;
25
+ } catch (error) {
26
+ console.error(`[vite-plugin-sri4] Failed to compute SRI hash: ${error}`);
27
+ return null;
28
+ }
17
29
  }
18
30
 
19
- /**
20
- * Check if an external resource supports CORS.
21
- * It sends a HEAD request to the given URL and examines the "Access-Control-Allow-Origin" header.
22
- * Adjust the logic to match your security policy.
23
- * @param {string} url - The URL to check.
24
- * @returns {Promise<boolean>} - A promise that resolves to true if CORS is enabled.
25
- */
26
- async function externalResourceIsCorsEnabled(url) {
31
+ async function externalResourceIsCorsEnabled(url, options) {
27
32
  try {
28
- const response = await fetch(url, { method: 'HEAD' });
33
+ const response = await fetch(url, {
34
+ method: 'HEAD',
35
+ timeout: 5000 // 5 seconds timeout
36
+ });
29
37
  const acao = response.headers.get('access-control-allow-origin');
30
- // Adjust the check as needed. This example allows "*" or domains including 'your-domain.com'.
31
- if (acao && (acao === '*' || acao.includes('your-domain.com'))) {
38
+ if (acao && (acao === '*' || acao.includes(options.domain || ''))) {
32
39
  return true;
33
40
  }
34
41
  return false;
35
42
  } catch (error) {
36
- console.warn(`Failed to fetch CORS headers from ${url}:`, error);
43
+ log(`Failed to fetch CORS headers from ${url}: ${error}`, options);
37
44
  return false;
38
45
  }
39
46
  }
40
47
 
41
- /**
42
- * Helper function to perform an asynchronous replacement in a string.
43
- * @param {string} str - The input string.
44
- * @param {RegExp} regex - The regular expression to match parts of the string.
45
- * @param {Function} asyncFn - An async function to compute the replacement.
46
- * @returns {Promise<string>} - The string with replaced values.
47
- */
48
48
  async function replaceAsync(str, regex, asyncFn) {
49
+ const promises = [];
49
50
  const matches = [];
51
+
50
52
  str.replace(regex, (...args) => {
51
53
  matches.push(args);
54
+ promises.push(asyncFn(...args));
52
55
  return '';
53
56
  });
54
- for (const args of matches) {
55
- const match = args[0];
56
- const replacement = await asyncFn(...args);
57
- str = str.replace(match, replacement);
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;
58
68
  }
59
- return str;
69
+
70
+ result += str.slice(lastIndex);
71
+ return result;
60
72
  }
61
73
 
62
- /**
63
- * Determines if the URL belongs to a domain specified in the bypassDomains array.
64
- * If so, the SRI injection will be skipped for that resource.
65
- * @param {string} url - The URL to check.
66
- * @param {Array<string>} bypassDomains - Array of domains to bypass SRI injection.
67
- * @returns {boolean} - True if the URL should bypass SRI injection.
68
- */
69
74
  function isBypassDomain(url, bypassDomains = []) {
70
75
  if (!bypassDomains.length) return false;
71
76
  try {
72
- // If url starts with '//' assume default protocol 'http:'
73
- const parsedUrl = url.startsWith('//') ? new URL(url, 'http://dummy') : new URL(url);
74
- // Checks if the hostname ends with any of the bypass domains.
75
- return bypassDomains.some((domain) => parsedUrl.hostname === domain || parsedUrl.hostname.endsWith(`.${domain}`));
77
+ const parsedUrl = url.startsWith('//')
78
+ ? new URL(url, 'http://dummy')
79
+ : new URL(url);
80
+ return bypassDomains.some(
81
+ (domain) => parsedUrl.hostname === domain ||
82
+ parsedUrl.hostname.endsWith(`.${domain}`)
83
+ );
76
84
  } catch (e) {
77
85
  return false;
78
86
  }
79
87
  }
80
88
 
81
- /**
82
- * vite-plugin-sri4
83
- *
84
- * Plugin options:
85
- * - algorithm: The algorithm used to compute SRI hash (default: 'sha384').
86
- * - bypassDomains: Array of domains for which to skip injecting the integrity attribute.
87
- *
88
- * This plugin works during the build process:
89
- * 1. In the generateBundle hook, it calculates the SRI hash for all assets/chunks and stores them in sriMap.
90
- * 2. In the transformIndexHtml hook, it injects the integrity and crossorigin attributes into the HTML.
91
- * For external links, it verifies via a CORS check if the resource supports cross-origin access.
92
- *
93
- * @param {Object} options - Plugin configuration options.
94
- * @returns {Object} - The Vite plugin.
95
- */
96
- function sri(options = {}) {
97
- // Use the provided algorithm from options, defaulting to 'sha384' if not specified.
98
- const algorithm = options.algorithm || 'sha384';
99
- // Array for domains to bypass SRI injection.
100
- const bypassDomains = options.bypassDomains || [];
101
- // Map to store SRI hashes for assets; key is the file name.
102
- const sriMap = {};
89
+ function sri(userOptions = {}) {
90
+ const options = { ...DEFAULT_OPTIONS, ...userOptions };
91
+ const sriMap = new Map();
103
92
 
104
93
  return {
105
94
  name: 'vite-plugin-sri4',
106
95
  apply: 'build',
107
96
 
108
- /**
109
- * The generateBundle hook iterates through each asset or chunk in the bundle,
110
- * computes its SRI hash, and stores it in the sriMap.
111
- */
97
+ configResolved(config) {
98
+ options.domain = config.server?.host || '';
99
+ },
100
+
112
101
  async generateBundle(_, bundle) {
113
102
  for (const fileName in bundle) {
114
103
  const chunk = bundle[fileName];
115
104
  if (chunk.type === 'chunk' || chunk.type === 'asset') {
116
105
  const content = chunk.code || chunk.source;
117
106
  if (content) {
118
- sriMap[fileName] = computeSri(content, algorithm);
119
- console.log(`Computed SRI for ${fileName}: ${sriMap[fileName]}`);
107
+ const hash = computeSri(content, options.algorithm);
108
+ if (hash) {
109
+ sriMap.set(fileName, hash);
110
+ log(`Computed SRI for ${fileName}: ${hash}`, options);
111
+ }
120
112
  }
121
113
  }
122
114
  }
123
115
  },
124
116
 
125
- /**
126
- * The transformIndexHtml hook processes the generated HTML and injects the integrity and crossorigin attributes.
127
- * For external resources, a CORS check is performed first.
128
- * If the URL belongs to a bypass domain, the injection is skipped.
129
- * @param {string} html - The HTML content to transform.
130
- * @returns {Promise<string>} - The transformed HTML.
131
- */
132
117
  async transformIndexHtml(html) {
133
- // Determines if a URL is external by checking if it starts with http://, https://, or //
118
+ const hasIntegrity = (tag) => /integrity=/i.test(tag);
134
119
  const isExternalUrl = (url) => /^(https?:)?\/\//i.test(url);
135
120
 
136
- // Process <script> tags.
137
- html = await replaceAsync(
138
- html,
139
- /(<script[^>]+src="([^"]+)"[^>]*>)/g,
140
- async (match, tag, src) => {
141
- // Skip SRI injection for external URLs that are in the bypass list.
142
- if (isExternalUrl(src) && isBypassDomain(src, bypassDomains)) {
143
- console.log(`Skipping SRI injection for bypass domain: ${src}`);
121
+ const processTag = async (match, tag, src, moduleSrc) => {
122
+ const actualSrc = src || moduleSrc;
123
+
124
+ if (hasIntegrity(tag)) {
125
+ return tag;
126
+ }
127
+
128
+ if (isExternalUrl(actualSrc) &&
129
+ isBypassDomain(actualSrc, options.bypassDomains)) {
130
+ log(`Skipping SRI injection for bypass domain: ${actualSrc}`, options);
131
+ return tag;
132
+ }
133
+
134
+ if (isExternalUrl(actualSrc)) {
135
+ const corsOk = await externalResourceIsCorsEnabled(actualSrc, options);
136
+ if (!corsOk) {
137
+ log(`External resource ${actualSrc} does not support CORS`, options);
144
138
  return tag;
145
139
  }
140
+ }
146
141
 
147
- if (isExternalUrl(src)) {
148
- // For external links not bypassed, perform a CORS check.
149
- const corsOk = await externalResourceIsCorsEnabled(src);
150
- if (!corsOk) {
151
- console.warn(`External resource ${src} does not support CORS. Skipping SRI injection.`);
152
- return tag;
153
- }
154
- }
155
- // For relative URLs or valid external URLs, use the file name as the key.
156
- const fileName = src.startsWith('/') ? src.slice(1) : src;
157
- if (sriMap[fileName]) {
158
- return tag.replace(/>$/, ` integrity="${sriMap[fileName]}" crossorigin="anonymous">`);
159
- }
160
- return tag;
142
+ const fileName = actualSrc.startsWith('/') ? actualSrc.slice(1) : actualSrc;
143
+ const integrity = sriMap.get(fileName);
144
+
145
+ if (integrity) {
146
+ return tag.replace(
147
+ />$/,
148
+ ` integrity="${integrity}" crossorigin="${options.crossorigin}">`
149
+ );
161
150
  }
151
+
152
+ return tag;
153
+ };
154
+
155
+ // Process scripts
156
+ html = await replaceAsync(
157
+ html,
158
+ /(<script[^>]+(?:src="([^"]+)"[^>]*|type="module"[^>]*src="([^"]+)"[^>]*)>)/g,
159
+ processTag
162
160
  );
163
161
 
164
- // Process <link> tags.
162
+ // Process links
165
163
  html = await replaceAsync(
166
164
  html,
167
165
  /(<link[^>]+href="([^"]+)"[^>]*>)/g,
168
- async (match, tag, href) => {
169
- // Skip SRI injection for external URLs that are in the bypass list.
170
- if (isExternalUrl(href) && isBypassDomain(href, bypassDomains)) {
171
- console.log(`Skipping SRI injection for bypass domain: ${href}`);
172
- return tag;
173
- }
174
-
175
- if (isExternalUrl(href)) {
176
- // For external links not in the bypass list, perform a CORS check.
177
- const corsOk = await externalResourceIsCorsEnabled(href);
178
- if (!corsOk) {
179
- console.warn(`External resource ${href} does not support CORS. Skipping SRI injection.`);
180
- return tag;
181
- }
182
- }
183
- const fileName = href.startsWith('/') ? href.slice(1) : href;
184
- if (sriMap[fileName]) {
185
- return tag.replace(/>$/, ` integrity="${sriMap[fileName]}" crossorigin="anonymous">`);
186
- }
187
- return tag;
188
- }
166
+ processTag
189
167
  );
190
168
 
191
169
  return html;
package/dist/index.js CHANGED
@@ -1,189 +1,167 @@
1
- import { createHash } from 'crypto';
1
+ import { createHash } from 'node:crypto';
2
2
  import fetch from 'node-fetch';
3
3
 
4
- /**
5
- * Compute the SRI (Subresource Integrity) hash for the given content.
6
- * @param {string | Buffer} content - The content to hash.
7
- * @param {string} algorithm - The SHA algorithm to use (default is 'sha384').
8
- * @returns {string} - The SRI string in the format "algorithm-base64hash".
9
- */
4
+ const DEFAULT_OPTIONS = {
5
+ algorithm: 'sha384',
6
+ bypassDomains: [],
7
+ crossorigin: 'anonymous',
8
+ debug: false
9
+ };
10
+
11
+ function log(message, options) {
12
+ if (options.debug) {
13
+ console.log(`[vite-plugin-sri4] ${message}`);
14
+ }
15
+ }
16
+
10
17
  function computeSri(content, algorithm = 'sha384') {
11
- const hash = createHash(algorithm)
12
- .update(content)
13
- .digest('base64');
14
- return `${algorithm}-${hash}`;
18
+ try {
19
+ const hash = createHash(algorithm)
20
+ .update(content)
21
+ .digest('base64');
22
+ return `${algorithm}-${hash}`;
23
+ } catch (error) {
24
+ console.error(`[vite-plugin-sri4] Failed to compute SRI hash: ${error}`);
25
+ return null;
26
+ }
15
27
  }
16
28
 
17
- /**
18
- * Check if an external resource supports CORS.
19
- * It sends a HEAD request to the given URL and examines the "Access-Control-Allow-Origin" header.
20
- * Adjust the logic to match your security policy.
21
- * @param {string} url - The URL to check.
22
- * @returns {Promise<boolean>} - A promise that resolves to true if CORS is enabled.
23
- */
24
- async function externalResourceIsCorsEnabled(url) {
29
+ async function externalResourceIsCorsEnabled(url, options) {
25
30
  try {
26
- const response = await fetch(url, { method: 'HEAD' });
31
+ const response = await fetch(url, {
32
+ method: 'HEAD',
33
+ timeout: 5000 // 5 seconds timeout
34
+ });
27
35
  const acao = response.headers.get('access-control-allow-origin');
28
- // Adjust the check as needed. This example allows "*" or domains including 'your-domain.com'.
29
- if (acao && (acao === '*' || acao.includes('your-domain.com'))) {
36
+ if (acao && (acao === '*' || acao.includes(options.domain || ''))) {
30
37
  return true;
31
38
  }
32
39
  return false;
33
40
  } catch (error) {
34
- console.warn(`Failed to fetch CORS headers from ${url}:`, error);
41
+ log(`Failed to fetch CORS headers from ${url}: ${error}`, options);
35
42
  return false;
36
43
  }
37
44
  }
38
45
 
39
- /**
40
- * Helper function to perform an asynchronous replacement in a string.
41
- * @param {string} str - The input string.
42
- * @param {RegExp} regex - The regular expression to match parts of the string.
43
- * @param {Function} asyncFn - An async function to compute the replacement.
44
- * @returns {Promise<string>} - The string with replaced values.
45
- */
46
46
  async function replaceAsync(str, regex, asyncFn) {
47
+ const promises = [];
47
48
  const matches = [];
49
+
48
50
  str.replace(regex, (...args) => {
49
51
  matches.push(args);
52
+ promises.push(asyncFn(...args));
50
53
  return '';
51
54
  });
52
- for (const args of matches) {
53
- const match = args[0];
54
- const replacement = await asyncFn(...args);
55
- str = str.replace(match, replacement);
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;
56
66
  }
57
- return str;
67
+
68
+ result += str.slice(lastIndex);
69
+ return result;
58
70
  }
59
71
 
60
- /**
61
- * Determines if the URL belongs to a domain specified in the bypassDomains array.
62
- * If so, the SRI injection will be skipped for that resource.
63
- * @param {string} url - The URL to check.
64
- * @param {Array<string>} bypassDomains - Array of domains to bypass SRI injection.
65
- * @returns {boolean} - True if the URL should bypass SRI injection.
66
- */
67
72
  function isBypassDomain(url, bypassDomains = []) {
68
73
  if (!bypassDomains.length) return false;
69
74
  try {
70
- // If url starts with '//' assume default protocol 'http:'
71
- const parsedUrl = url.startsWith('//') ? new URL(url, 'http://dummy') : new URL(url);
72
- // Checks if the hostname ends with any of the bypass domains.
73
- return bypassDomains.some((domain) => parsedUrl.hostname === domain || parsedUrl.hostname.endsWith(`.${domain}`));
75
+ const parsedUrl = url.startsWith('//')
76
+ ? new URL(url, 'http://dummy')
77
+ : new URL(url);
78
+ return bypassDomains.some(
79
+ (domain) => parsedUrl.hostname === domain ||
80
+ parsedUrl.hostname.endsWith(`.${domain}`)
81
+ );
74
82
  } catch (e) {
75
83
  return false;
76
84
  }
77
85
  }
78
86
 
79
- /**
80
- * vite-plugin-sri4
81
- *
82
- * Plugin options:
83
- * - algorithm: The algorithm used to compute SRI hash (default: 'sha384').
84
- * - bypassDomains: Array of domains for which to skip injecting the integrity attribute.
85
- *
86
- * This plugin works during the build process:
87
- * 1. In the generateBundle hook, it calculates the SRI hash for all assets/chunks and stores them in sriMap.
88
- * 2. In the transformIndexHtml hook, it injects the integrity and crossorigin attributes into the HTML.
89
- * For external links, it verifies via a CORS check if the resource supports cross-origin access.
90
- *
91
- * @param {Object} options - Plugin configuration options.
92
- * @returns {Object} - The Vite plugin.
93
- */
94
- function sri(options = {}) {
95
- // Use the provided algorithm from options, defaulting to 'sha384' if not specified.
96
- const algorithm = options.algorithm || 'sha384';
97
- // Array for domains to bypass SRI injection.
98
- const bypassDomains = options.bypassDomains || [];
99
- // Map to store SRI hashes for assets; key is the file name.
100
- const sriMap = {};
87
+ function sri(userOptions = {}) {
88
+ const options = { ...DEFAULT_OPTIONS, ...userOptions };
89
+ const sriMap = new Map();
101
90
 
102
91
  return {
103
92
  name: 'vite-plugin-sri4',
104
93
  apply: 'build',
105
94
 
106
- /**
107
- * The generateBundle hook iterates through each asset or chunk in the bundle,
108
- * computes its SRI hash, and stores it in the sriMap.
109
- */
95
+ configResolved(config) {
96
+ options.domain = config.server?.host || '';
97
+ },
98
+
110
99
  async generateBundle(_, bundle) {
111
100
  for (const fileName in bundle) {
112
101
  const chunk = bundle[fileName];
113
102
  if (chunk.type === 'chunk' || chunk.type === 'asset') {
114
103
  const content = chunk.code || chunk.source;
115
104
  if (content) {
116
- sriMap[fileName] = computeSri(content, algorithm);
117
- console.log(`Computed SRI for ${fileName}: ${sriMap[fileName]}`);
105
+ const hash = computeSri(content, options.algorithm);
106
+ if (hash) {
107
+ sriMap.set(fileName, hash);
108
+ log(`Computed SRI for ${fileName}: ${hash}`, options);
109
+ }
118
110
  }
119
111
  }
120
112
  }
121
113
  },
122
114
 
123
- /**
124
- * The transformIndexHtml hook processes the generated HTML and injects the integrity and crossorigin attributes.
125
- * For external resources, a CORS check is performed first.
126
- * If the URL belongs to a bypass domain, the injection is skipped.
127
- * @param {string} html - The HTML content to transform.
128
- * @returns {Promise<string>} - The transformed HTML.
129
- */
130
115
  async transformIndexHtml(html) {
131
- // Determines if a URL is external by checking if it starts with http://, https://, or //
116
+ const hasIntegrity = (tag) => /integrity=/i.test(tag);
132
117
  const isExternalUrl = (url) => /^(https?:)?\/\//i.test(url);
133
118
 
134
- // Process <script> tags.
135
- html = await replaceAsync(
136
- html,
137
- /(<script[^>]+src="([^"]+)"[^>]*>)/g,
138
- async (match, tag, src) => {
139
- // Skip SRI injection for external URLs that are in the bypass list.
140
- if (isExternalUrl(src) && isBypassDomain(src, bypassDomains)) {
141
- console.log(`Skipping SRI injection for bypass domain: ${src}`);
119
+ const processTag = async (match, tag, src, moduleSrc) => {
120
+ const actualSrc = src || moduleSrc;
121
+
122
+ if (hasIntegrity(tag)) {
123
+ return tag;
124
+ }
125
+
126
+ if (isExternalUrl(actualSrc) &&
127
+ isBypassDomain(actualSrc, options.bypassDomains)) {
128
+ log(`Skipping SRI injection for bypass domain: ${actualSrc}`, options);
129
+ return tag;
130
+ }
131
+
132
+ if (isExternalUrl(actualSrc)) {
133
+ const corsOk = await externalResourceIsCorsEnabled(actualSrc, options);
134
+ if (!corsOk) {
135
+ log(`External resource ${actualSrc} does not support CORS`, options);
142
136
  return tag;
143
137
  }
138
+ }
144
139
 
145
- if (isExternalUrl(src)) {
146
- // For external links not bypassed, perform a CORS check.
147
- const corsOk = await externalResourceIsCorsEnabled(src);
148
- if (!corsOk) {
149
- console.warn(`External resource ${src} does not support CORS. Skipping SRI injection.`);
150
- return tag;
151
- }
152
- }
153
- // For relative URLs or valid external URLs, use the file name as the key.
154
- const fileName = src.startsWith('/') ? src.slice(1) : src;
155
- if (sriMap[fileName]) {
156
- return tag.replace(/>$/, ` integrity="${sriMap[fileName]}" crossorigin="anonymous">`);
157
- }
158
- return tag;
140
+ const fileName = actualSrc.startsWith('/') ? actualSrc.slice(1) : actualSrc;
141
+ const integrity = sriMap.get(fileName);
142
+
143
+ if (integrity) {
144
+ return tag.replace(
145
+ />$/,
146
+ ` integrity="${integrity}" crossorigin="${options.crossorigin}">`
147
+ );
159
148
  }
149
+
150
+ return tag;
151
+ };
152
+
153
+ // Process scripts
154
+ html = await replaceAsync(
155
+ html,
156
+ /(<script[^>]+(?:src="([^"]+)"[^>]*|type="module"[^>]*src="([^"]+)"[^>]*)>)/g,
157
+ processTag
160
158
  );
161
159
 
162
- // Process <link> tags.
160
+ // Process links
163
161
  html = await replaceAsync(
164
162
  html,
165
163
  /(<link[^>]+href="([^"]+)"[^>]*>)/g,
166
- async (match, tag, href) => {
167
- // Skip SRI injection for external URLs that are in the bypass list.
168
- if (isExternalUrl(href) && isBypassDomain(href, bypassDomains)) {
169
- console.log(`Skipping SRI injection for bypass domain: ${href}`);
170
- return tag;
171
- }
172
-
173
- if (isExternalUrl(href)) {
174
- // For external links not in the bypass list, perform a CORS check.
175
- const corsOk = await externalResourceIsCorsEnabled(href);
176
- if (!corsOk) {
177
- console.warn(`External resource ${href} does not support CORS. Skipping SRI injection.`);
178
- return tag;
179
- }
180
- }
181
- const fileName = href.startsWith('/') ? href.slice(1) : href;
182
- if (sriMap[fileName]) {
183
- return tag.replace(/>$/, ` integrity="${sriMap[fileName]}" crossorigin="anonymous">`);
184
- }
185
- return tag;
186
- }
164
+ processTag
187
165
  );
188
166
 
189
167
  return html;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-sri4",
3
- "version": "1.0.0",
3
+ "version": "1.1.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",