@jsenv/core 38.4.16 → 38.4.17

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.
@@ -129,422 +129,441 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
129
129
  let importmapFound = false;
130
130
  const importmapLoaded = startLoadingImportmap(urlInfo);
131
131
 
132
- const htmlAst = parseHtml({
133
- html: urlInfo.content,
134
- url: urlInfo.url,
135
- });
136
-
137
- const mutations = [];
138
- const actions = [];
139
- const finalizeCallbacks = [];
132
+ try {
133
+ const htmlAst = parseHtml({
134
+ html: urlInfo.content,
135
+ url: urlInfo.url,
136
+ });
140
137
 
141
- const createExternalReference = (
142
- node,
143
- attributeName,
144
- attributeValue,
145
- { type, subtype, expectedType, ...rest },
146
- ) => {
147
- let position;
148
- if (getHtmlNodeAttribute(node, "jsenv-cooked-by")) {
149
- // when generated from inline content,
150
- // line, column is not "src" nor "inlined-from-src" but "original-position"
151
- position = getHtmlNodePosition(node);
152
- } else {
153
- position = getHtmlNodeAttributePosition(node, attributeName);
154
- }
155
- const {
156
- line,
157
- column,
158
- // originalLine, originalColumn
159
- } = position;
160
- const debug = getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
138
+ const mutations = [];
139
+ const actions = [];
140
+ const finalizeCallbacks = [];
161
141
 
162
- const { crossorigin, integrity } = readFetchMetas(node);
163
- const isResourceHint = [
164
- "preconnect",
165
- "dns-prefetch",
166
- "prefetch",
167
- "preload",
168
- "modulepreload",
169
- ].includes(subtype);
170
- let attributeLocation = node.sourceCodeLocation.attrs[attributeName];
171
- if (
172
- !attributeLocation &&
173
- attributeName === "href" &&
174
- (node.tagName === "use" || node.tagName === "image")
175
- ) {
176
- attributeLocation = node.sourceCodeLocation.attrs["xlink:href"];
177
- }
178
- const attributeStart = attributeLocation.startOffset;
179
- const attributeValueStart = urlInfo.content.indexOf(
142
+ const createExternalReference = (
143
+ node,
144
+ attributeName,
180
145
  attributeValue,
181
- attributeStart + `${attributeName}=`.length,
182
- );
183
- const attributeValueEnd = attributeValueStart + attributeValue.length;
184
- const reference = urlInfo.dependencies.found({
185
- type,
186
- subtype,
187
- expectedType,
188
- specifier: attributeValue,
189
- specifierLine: line,
190
- specifierColumn: column,
191
- specifierStart: attributeValueStart,
192
- specifierEnd: attributeValueEnd,
193
- isResourceHint,
194
- isWeak: isResourceHint,
195
- crossorigin,
196
- integrity,
197
- debug,
198
- astInfo: { node, attributeName },
199
- ...rest,
200
- });
201
- actions.push(async () => {
202
- await reference.readGeneratedSpecifier();
203
- mutations.push(() => {
204
- setHtmlNodeAttributes(node, {
205
- [attributeName]: reference.generatedSpecifier,
146
+ { type, subtype, expectedType, ...rest },
147
+ ) => {
148
+ let position;
149
+ if (getHtmlNodeAttribute(node, "jsenv-cooked-by")) {
150
+ // when generated from inline content,
151
+ // line, column is not "src" nor "inlined-from-src" but "original-position"
152
+ position = getHtmlNodePosition(node);
153
+ } else {
154
+ position = getHtmlNodeAttributePosition(node, attributeName);
155
+ }
156
+ const {
157
+ line,
158
+ column,
159
+ // originalLine, originalColumn
160
+ } = position;
161
+ const debug =
162
+ getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
163
+
164
+ const { crossorigin, integrity } = readFetchMetas(node);
165
+ const isResourceHint = [
166
+ "preconnect",
167
+ "dns-prefetch",
168
+ "prefetch",
169
+ "preload",
170
+ "modulepreload",
171
+ ].includes(subtype);
172
+ let attributeLocation =
173
+ node.sourceCodeLocation.attrs[attributeName];
174
+ if (
175
+ !attributeLocation &&
176
+ attributeName === "href" &&
177
+ (node.tagName === "use" || node.tagName === "image")
178
+ ) {
179
+ attributeLocation = node.sourceCodeLocation.attrs["xlink:href"];
180
+ }
181
+ const attributeStart = attributeLocation.startOffset;
182
+ const attributeValueStart = urlInfo.content.indexOf(
183
+ attributeValue,
184
+ attributeStart + `${attributeName}=`.length,
185
+ );
186
+ const attributeValueEnd =
187
+ attributeValueStart + attributeValue.length;
188
+ const reference = urlInfo.dependencies.found({
189
+ type,
190
+ subtype,
191
+ expectedType,
192
+ specifier: attributeValue,
193
+ specifierLine: line,
194
+ specifierColumn: column,
195
+ specifierStart: attributeValueStart,
196
+ specifierEnd: attributeValueEnd,
197
+ isResourceHint,
198
+ isWeak: isResourceHint,
199
+ crossorigin,
200
+ integrity,
201
+ debug,
202
+ astInfo: { node, attributeName },
203
+ ...rest,
204
+ });
205
+ actions.push(async () => {
206
+ await reference.readGeneratedSpecifier();
207
+ mutations.push(() => {
208
+ setHtmlNodeAttributes(node, {
209
+ [attributeName]: reference.generatedSpecifier,
210
+ });
206
211
  });
207
212
  });
208
- });
209
- return reference;
210
- };
211
- const visitHref = (node, referenceProps) => {
212
- const href = getHtmlNodeAttribute(node, "href");
213
- if (href) {
214
- return createExternalReference(node, "href", href, referenceProps);
215
- }
216
- return null;
217
- };
218
- const visitSrc = (node, referenceProps) => {
219
- const src = getHtmlNodeAttribute(node, "src");
220
- if (src) {
221
- return createExternalReference(node, "src", src, referenceProps);
222
- }
223
- return null;
224
- };
225
- const visitSrcset = (node, referenceProps) => {
226
- const srcset = getHtmlNodeAttribute(node, "srcset");
227
- if (srcset) {
228
- const srcCandidates = parseSrcSet(srcset);
229
- return srcCandidates.map((srcCandidate) => {
213
+ return reference;
214
+ };
215
+ const visitHref = (node, referenceProps) => {
216
+ const href = getHtmlNodeAttribute(node, "href");
217
+ if (href) {
230
218
  return createExternalReference(
231
219
  node,
232
- "srcset",
233
- srcCandidate.specifier,
220
+ "href",
221
+ href,
234
222
  referenceProps,
235
223
  );
224
+ }
225
+ return null;
226
+ };
227
+ const visitSrc = (node, referenceProps) => {
228
+ const src = getHtmlNodeAttribute(node, "src");
229
+ if (src) {
230
+ return createExternalReference(node, "src", src, referenceProps);
231
+ }
232
+ return null;
233
+ };
234
+ const visitSrcset = (node, referenceProps) => {
235
+ const srcset = getHtmlNodeAttribute(node, "srcset");
236
+ if (srcset) {
237
+ const srcCandidates = parseSrcSet(srcset);
238
+ return srcCandidates.map((srcCandidate) => {
239
+ return createExternalReference(
240
+ node,
241
+ "srcset",
242
+ srcCandidate.specifier,
243
+ referenceProps,
244
+ );
245
+ });
246
+ }
247
+ return null;
248
+ };
249
+ const createInlineReference = (
250
+ node,
251
+ inlineContent,
252
+ { type, expectedType, contentType },
253
+ ) => {
254
+ const hotAccept =
255
+ getHtmlNodeAttribute(node, "hot-accept") !== undefined;
256
+ const { line, column, isOriginal } = getHtmlNodePosition(node, {
257
+ preferOriginal: true,
236
258
  });
237
- }
238
- return null;
239
- };
240
-
241
- const createInlineReference = (
242
- node,
243
- inlineContent,
244
- { type, expectedType, contentType },
245
- ) => {
246
- const hotAccept =
247
- getHtmlNodeAttribute(node, "hot-accept") !== undefined;
248
- const { line, column, isOriginal } = getHtmlNodePosition(node, {
249
- preferOriginal: true,
250
- });
251
- const inlineContentUrl = getUrlForContentInsideHtml(node, {
252
- htmlUrl: urlInfo.url,
253
- });
254
- const debug = getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
255
- const inlineReference = urlInfo.dependencies.foundInline({
256
- type,
257
- expectedType,
258
- isOriginalPosition: isOriginal,
259
- // we remove 1 to the line because imagine the following html:
260
- // <style>body { color: red; }</style>
261
- // -> content starts same line as <style> (same for <script>)
262
- specifierLine: line - 1,
263
- specifierColumn: column,
264
- specifier: inlineContentUrl,
265
- contentType,
266
- content: inlineContent,
267
- debug,
268
- astInfo: { node },
269
- });
270
-
271
- actions.push(async () => {
272
- await inlineReference.urlInfo.cook();
273
- mutations.push(() => {
274
- if (hotAccept) {
275
- removeHtmlNodeText(node);
276
- setHtmlNodeAttributes(node, {
277
- "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
278
- });
279
- } else {
280
- setHtmlNodeText(node, inlineReference.urlInfo.content, {
281
- indentation: false, // indentation would decrease stack trace precision
282
- });
283
- setHtmlNodeAttributes(node, {
284
- "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
285
- });
286
- }
259
+ const inlineContentUrl = getUrlForContentInsideHtml(node, {
260
+ htmlUrl: urlInfo.url,
287
261
  });
288
- });
289
- return inlineReference;
290
- };
291
- const visitTextContent = (
292
- node,
293
- { type, subtype, expectedType, contentType },
294
- ) => {
295
- const inlineContent = getHtmlNodeText(node);
296
- if (!inlineContent) {
297
- return null;
298
- }
299
- return createInlineReference(node, inlineContent, {
300
- type,
301
- subtype,
302
- expectedType,
303
- contentType,
304
- });
305
- };
306
-
307
- visitHtmlNodes(htmlAst, {
308
- link: (linkNode) => {
309
- const rel = getHtmlNodeAttribute(linkNode, "rel");
310
- const type = getHtmlNodeAttribute(linkNode, "type");
311
- const ref = visitHref(linkNode, {
312
- type: "link_href",
313
- subtype: rel,
314
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#including_a_mime_type
315
- expectedContentType: type,
262
+ const debug =
263
+ getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
264
+ const inlineReference = urlInfo.dependencies.foundInline({
265
+ type,
266
+ expectedType,
267
+ isOriginalPosition: isOriginal,
268
+ // we remove 1 to the line because imagine the following html:
269
+ // <style>body { color: red; }</style>
270
+ // -> content starts same line as <style> (same for <script>)
271
+ specifierLine: line - 1,
272
+ specifierColumn: column,
273
+ specifier: inlineContentUrl,
274
+ contentType,
275
+ content: inlineContent,
276
+ debug,
277
+ astInfo: { node },
316
278
  });
317
- if (ref) {
318
- finalizeCallbacks.push(() => {
319
- if (ref.expectedType) {
320
- // might be set by other plugins, in that case respect it
279
+
280
+ actions.push(async () => {
281
+ await inlineReference.urlInfo.cook();
282
+ mutations.push(() => {
283
+ if (hotAccept) {
284
+ removeHtmlNodeText(node);
285
+ setHtmlNodeAttributes(node, {
286
+ "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
287
+ });
321
288
  } else {
322
- ref.expectedType = decideLinkExpectedType(ref, urlInfo);
289
+ setHtmlNodeText(node, inlineReference.urlInfo.content, {
290
+ indentation: false, // indentation would decrease stack trace precision
291
+ });
292
+ setHtmlNodeAttributes(node, {
293
+ "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
294
+ });
323
295
  }
324
296
  });
297
+ });
298
+ return inlineReference;
299
+ };
300
+ const visitTextContent = (
301
+ node,
302
+ { type, subtype, expectedType, contentType },
303
+ ) => {
304
+ const inlineContent = getHtmlNodeText(node);
305
+ if (!inlineContent) {
306
+ return null;
325
307
  }
326
- },
327
- style: inlineContent
328
- ? (styleNode) => {
329
- visitTextContent(styleNode, {
330
- type: "style",
331
- expectedType: "css",
332
- contentType: "text/css",
333
- });
334
- }
335
- : null,
336
- script: (scriptNode) => {
337
- const { type, subtype, contentType, extension } =
338
- analyzeScriptNode(scriptNode);
339
- if (type === "text") {
340
- // ignore <script type="whatever">foobar</script>
341
- // per HTML spec https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type
342
- return;
343
- }
344
- if (type === "importmap") {
345
- importmapFound = true;
308
+ return createInlineReference(node, inlineContent, {
309
+ type,
310
+ subtype,
311
+ expectedType,
312
+ contentType,
313
+ });
314
+ };
346
315
 
347
- const src = getHtmlNodeAttribute(scriptNode, "src");
348
- if (src) {
349
- // Browser would throw on remote importmap
350
- // and won't sent a request to the server for it
351
- // We must precook the importmap to know its content and inline it into the HTML
352
- const importmapReference = createExternalReference(
353
- scriptNode,
354
- "src",
355
- src,
356
- {
357
- type: "script",
358
- subtype: "importmap",
359
- expectedType: "importmap",
360
- },
361
- );
362
- const { line, column, isOriginal } = getHtmlNodePosition(
363
- scriptNode,
364
- {
365
- preferOriginal: true,
366
- },
367
- );
368
- const importmapInlineUrl = getUrlForContentInsideHtml(
369
- scriptNode,
370
- {
371
- htmlUrl: urlInfo.url,
372
- },
373
- );
374
- const importmapReferenceInlined = importmapReference.inline({
375
- line: line - 1,
376
- column,
377
- isOriginal,
378
- specifier: importmapInlineUrl,
379
- contentType: "application/importmap+json",
316
+ visitHtmlNodes(htmlAst, {
317
+ link: (linkNode) => {
318
+ const rel = getHtmlNodeAttribute(linkNode, "rel");
319
+ const type = getHtmlNodeAttribute(linkNode, "type");
320
+ const ref = visitHref(linkNode, {
321
+ type: "link_href",
322
+ subtype: rel,
323
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#including_a_mime_type
324
+ expectedContentType: type,
325
+ });
326
+ if (ref) {
327
+ finalizeCallbacks.push(() => {
328
+ if (ref.expectedType) {
329
+ // might be set by other plugins, in that case respect it
330
+ } else {
331
+ ref.expectedType = decideLinkExpectedType(ref, urlInfo);
332
+ }
380
333
  });
381
- const importmapInlineUrlInfo =
382
- importmapReferenceInlined.urlInfo;
383
- actions.push(async () => {
384
- await importmapInlineUrlInfo.cook();
385
- importmapLoaded(importmapInlineUrlInfo);
386
- mutations.push(() => {
387
- setHtmlNodeText(
388
- scriptNode,
389
- importmapInlineUrlInfo.content,
390
- {
391
- indentation: "auto",
392
- },
393
- );
394
- setHtmlNodeAttributes(scriptNode, {
395
- "src": undefined,
396
- "jsenv-inlined-by": "jsenv:html_reference_analysis",
397
- "inlined-from-src": src,
398
- });
334
+ }
335
+ },
336
+ style: inlineContent
337
+ ? (styleNode) => {
338
+ visitTextContent(styleNode, {
339
+ type: "style",
340
+ expectedType: "css",
341
+ contentType: "text/css",
399
342
  });
400
- });
401
- } else {
402
- const htmlNodeText = getHtmlNodeText(scriptNode);
403
- if (htmlNodeText) {
404
- const importmapReference = createInlineReference(
343
+ }
344
+ : null,
345
+ script: (scriptNode) => {
346
+ const { type, subtype, contentType, extension } =
347
+ analyzeScriptNode(scriptNode);
348
+ if (type === "text") {
349
+ // ignore <script type="whatever">foobar</script>
350
+ // per HTML spec https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type
351
+ return;
352
+ }
353
+ if (type === "importmap") {
354
+ importmapFound = true;
355
+
356
+ const src = getHtmlNodeAttribute(scriptNode, "src");
357
+ if (src) {
358
+ // Browser would throw on remote importmap
359
+ // and won't sent a request to the server for it
360
+ // We must precook the importmap to know its content and inline it into the HTML
361
+ const importmapReference = createExternalReference(
405
362
  scriptNode,
406
- htmlNodeText,
363
+ "src",
364
+ src,
407
365
  {
408
366
  type: "script",
367
+ subtype: "importmap",
409
368
  expectedType: "importmap",
410
- contentType: "application/importmap+json",
411
369
  },
412
370
  );
413
- const inlineImportmapUrlInfo = importmapReference.urlInfo;
371
+ const { line, column, isOriginal } = getHtmlNodePosition(
372
+ scriptNode,
373
+ {
374
+ preferOriginal: true,
375
+ },
376
+ );
377
+ const importmapInlineUrl = getUrlForContentInsideHtml(
378
+ scriptNode,
379
+ {
380
+ htmlUrl: urlInfo.url,
381
+ },
382
+ );
383
+ const importmapReferenceInlined = importmapReference.inline({
384
+ line: line - 1,
385
+ column,
386
+ isOriginal,
387
+ specifier: importmapInlineUrl,
388
+ contentType: "application/importmap+json",
389
+ });
390
+ const importmapInlineUrlInfo =
391
+ importmapReferenceInlined.urlInfo;
414
392
  actions.push(async () => {
415
- await inlineImportmapUrlInfo.cook();
416
- importmapLoaded(inlineImportmapUrlInfo);
393
+ try {
394
+ await importmapInlineUrlInfo.cook();
395
+ } finally {
396
+ importmapLoaded(importmapInlineUrlInfo);
397
+ }
417
398
  mutations.push(() => {
418
399
  setHtmlNodeText(
419
400
  scriptNode,
420
- inlineImportmapUrlInfo.content,
401
+ importmapInlineUrlInfo.content,
421
402
  {
422
403
  indentation: "auto",
423
404
  },
424
405
  );
425
406
  setHtmlNodeAttributes(scriptNode, {
426
- "jsenv-cooked-by": "jsenv:html_reference_analysis",
407
+ "src": undefined,
408
+ "jsenv-inlined-by": "jsenv:html_reference_analysis",
409
+ "inlined-from-src": src,
427
410
  });
428
411
  });
429
412
  });
413
+ } else {
414
+ const htmlNodeText = getHtmlNodeText(scriptNode);
415
+ if (htmlNodeText) {
416
+ const importmapReference = createInlineReference(
417
+ scriptNode,
418
+ htmlNodeText,
419
+ {
420
+ type: "script",
421
+ expectedType: "importmap",
422
+ contentType: "application/importmap+json",
423
+ },
424
+ );
425
+ const inlineImportmapUrlInfo = importmapReference.urlInfo;
426
+ actions.push(async () => {
427
+ try {
428
+ await inlineImportmapUrlInfo.cook();
429
+ } finally {
430
+ importmapLoaded(inlineImportmapUrlInfo);
431
+ }
432
+ mutations.push(() => {
433
+ setHtmlNodeText(
434
+ scriptNode,
435
+ inlineImportmapUrlInfo.content,
436
+ {
437
+ indentation: "auto",
438
+ },
439
+ );
440
+ setHtmlNodeAttributes(scriptNode, {
441
+ "jsenv-cooked-by": "jsenv:html_reference_analysis",
442
+ });
443
+ });
444
+ });
445
+ }
430
446
  }
447
+ // once this plugin knows the importmap, it will use it
448
+ // to map imports. These import specifiers will be normalized
449
+ // by "formatReference" making the importmap presence useless.
450
+ // In dev/test we keep importmap into the HTML to see it even if useless
451
+ // Duing build we get rid of it
452
+ if (urlInfo.context.build) {
453
+ mutations.push(() => {
454
+ removeHtmlNode(scriptNode);
455
+ });
456
+ }
457
+ return;
431
458
  }
432
- // once this plugin knows the importmap, it will use it
433
- // to map imports. These import specifiers will be normalized
434
- // by "formatReference" making the importmap presence useless.
435
- // In dev/test we keep importmap into the HTML to see it even if useless
436
- // Duing build we get rid of it
437
- if (urlInfo.context.build) {
438
- mutations.push(() => {
439
- removeHtmlNode(scriptNode);
440
- });
459
+ const externalRef = visitSrc(scriptNode, {
460
+ type: "script",
461
+ subtype: type,
462
+ expectedType: type,
463
+ });
464
+ if (externalRef) {
465
+ return;
441
466
  }
442
- return;
443
- }
444
- const externalRef = visitSrc(scriptNode, {
445
- type: "script",
446
- subtype: type,
447
- expectedType: type,
448
- });
449
- if (externalRef) {
450
- return;
451
- }
452
467
 
453
- // now visit the content, if any
454
- if (!inlineContent) {
455
- return;
456
- }
457
- // If the inline script was already handled by an other plugin, ignore it
458
- // - we want to preserve inline scripts generated by html supervisor during dev
459
- // - we want to avoid cooking twice a script during build
460
- if (
461
- !inlineConvertedScript &&
462
- getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") ===
463
- "jsenv:js_module_fallback"
464
- ) {
465
- return;
466
- }
468
+ // now visit the content, if any
469
+ if (!inlineContent) {
470
+ return;
471
+ }
472
+ // If the inline script was already handled by an other plugin, ignore it
473
+ // - we want to preserve inline scripts generated by html supervisor during dev
474
+ // - we want to avoid cooking twice a script during build
475
+ if (
476
+ !inlineConvertedScript &&
477
+ getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") ===
478
+ "jsenv:js_module_fallback"
479
+ ) {
480
+ return;
481
+ }
467
482
 
468
- const inlineRef = visitTextContent(scriptNode, {
469
- type: "script",
470
- subtype,
471
- expectedType: type,
472
- contentType,
473
- });
474
- if (inlineRef) {
475
- // 1. <script type="jsx"> becomes <script>
476
- if (type === "js_classic" && extension !== ".js") {
477
- mutations.push(() => {
478
- setHtmlNodeAttributes(scriptNode, {
479
- type: undefined,
483
+ const inlineRef = visitTextContent(scriptNode, {
484
+ type: "script",
485
+ subtype,
486
+ expectedType: type,
487
+ contentType,
488
+ });
489
+ if (inlineRef) {
490
+ // 1. <script type="jsx"> becomes <script>
491
+ if (type === "js_classic" && extension !== ".js") {
492
+ mutations.push(() => {
493
+ setHtmlNodeAttributes(scriptNode, {
494
+ type: undefined,
495
+ });
480
496
  });
481
- });
482
- }
483
- // 2. <script type="module/jsx"> becomes <script type="module">
484
- if (type === "js_module" && extension !== ".js") {
485
- mutations.push(() => {
486
- setHtmlNodeAttributes(scriptNode, {
487
- type: "module",
497
+ }
498
+ // 2. <script type="module/jsx"> becomes <script type="module">
499
+ if (type === "js_module" && extension !== ".js") {
500
+ mutations.push(() => {
501
+ setHtmlNodeAttributes(scriptNode, {
502
+ type: "module",
503
+ });
488
504
  });
489
- });
505
+ }
490
506
  }
491
- }
492
- },
493
- a: (aNode) => {
494
- visitHref(aNode, {
495
- type: "a_href",
496
- });
497
- },
498
- iframe: (iframeNode) => {
499
- visitSrc(iframeNode, {
500
- type: "iframe_src",
501
- });
502
- },
503
- img: (imgNode) => {
504
- visitSrc(imgNode, {
505
- type: "img_src",
506
- });
507
- visitSrcset(imgNode, {
508
- type: "img_srcset",
509
- });
510
- },
511
- source: (sourceNode) => {
512
- visitSrc(sourceNode, {
513
- type: "source_src",
514
- });
515
- visitSrcset(sourceNode, {
516
- type: "source_srcset",
517
- });
518
- },
519
- // svg <image> tag
520
- image: (imageNode) => {
521
- visitHref(imageNode, {
522
- type: "image_href",
523
- });
524
- },
525
- use: (useNode) => {
526
- visitHref(useNode, {
527
- type: "use_href",
528
- });
529
- },
530
- });
531
- if (!importmapFound) {
532
- importmapLoaded();
533
- }
534
- finalizeCallbacks.forEach((finalizeCallback) => {
535
- finalizeCallback();
536
- });
507
+ },
508
+ a: (aNode) => {
509
+ visitHref(aNode, {
510
+ type: "a_href",
511
+ });
512
+ },
513
+ iframe: (iframeNode) => {
514
+ visitSrc(iframeNode, {
515
+ type: "iframe_src",
516
+ });
517
+ },
518
+ img: (imgNode) => {
519
+ visitSrc(imgNode, {
520
+ type: "img_src",
521
+ });
522
+ visitSrcset(imgNode, {
523
+ type: "img_srcset",
524
+ });
525
+ },
526
+ source: (sourceNode) => {
527
+ visitSrc(sourceNode, {
528
+ type: "source_src",
529
+ });
530
+ visitSrcset(sourceNode, {
531
+ type: "source_srcset",
532
+ });
533
+ },
534
+ // svg <image> tag
535
+ image: (imageNode) => {
536
+ visitHref(imageNode, {
537
+ type: "image_href",
538
+ });
539
+ },
540
+ use: (useNode) => {
541
+ visitHref(useNode, {
542
+ type: "use_href",
543
+ });
544
+ },
545
+ });
546
+ if (!importmapFound) {
547
+ importmapLoaded();
548
+ }
549
+ finalizeCallbacks.forEach((finalizeCallback) => {
550
+ finalizeCallback();
551
+ });
537
552
 
538
- if (actions.length > 0) {
539
- await Promise.all(actions.map((action) => action()));
540
- actions.length = 0;
541
- }
542
- if (mutations.length === 0) {
543
- return null;
553
+ if (actions.length > 0) {
554
+ await Promise.all(actions.map((action) => action()));
555
+ actions.length = 0;
556
+ }
557
+ if (mutations.length === 0) {
558
+ return null;
559
+ }
560
+ mutations.forEach((mutation) => mutation());
561
+ mutations.length = 0;
562
+ return stringifyHtmlAst(htmlAst);
563
+ } catch (e) {
564
+ importmapLoaded();
565
+ throw e;
544
566
  }
545
- mutations.forEach((mutation) => mutation());
546
- mutations.length = 0;
547
- return stringifyHtmlAst(htmlAst);
548
567
  },
549
568
  },
550
569
  };