Repository: maizzle/framework (js)

Issue:
Only the first VML path is prefixed by baseURL
- Maizzle Version: 4.0.0-alpha.8
- Node.js Version: 15.14.0

Hey there! I think I found a bug related to VML images and the use of the `baseURL` config. It looks like under Maizzle 4, only the first VML path is prefixed by baseURL. (I checked and this doesn't seem to appear under Maizzle 3.) Here’s how you can reproduce it:

1. Create a new Maizzle project (`maizzle new`).
2. On the `package.json`, change Maizzle version from `^3.0.0` to `next`.
3. `npm i` to install everything.
4. In `config.js`, add `baseURL: 'https://example.com/foo/'`.
5. In the `src/templates.promotional.html` template, add the following code at the start of the `<block name="template">` block. You can also change the `src:` variable passed to the `vmlbg` component to a local image like `foobarbaz.jpg`.

```html
<!--[if mso]>
<v:image src="foo.jpg" />
<v:image src="bar.jpg" />
<v:image src="baz.jpg" />
<![endif]-->
```

6. Run `maizzle serve` or `maizzle build production`.

## Expected result

Every image in the email should have its path prefixed by `https://example.com/foo/`.

```html
<!--[if mso]>
<v:image src="https://example.com/foo/foo.jpg" />
<v:image src="https://example.com/foo/bar.jpg" />
<v:image src="https://example.com/foo/baz.jpg" />
<![endif]-->
…
<!--[if mso]>
<v:image src="https://example.com/foo/foobarbaz.jpg" xmlns:v="urn:schemas-microsoft-com:vml" style="width:600px;height:400px;" />
…
<![endif]-->
```

## Actual result

Only the very first image within the entire email has its path prefixed by the `baseURL` value.

```html
<!--[if mso]>
<v:image src="https://example.com/foo/foo.jpg" />
<v:image src="bar.jpg" />
<v:image src="baz.jpg" />
<![endif]-->
…
<!--[if mso]>
<v:image src="foobarbaz.jpg" xmlns:v="urn:schemas-microsoft-com:vml" style="width:600px;height:400px;" />
…
<![endif]-->
```

Pull request:
fix: base url for vml sources
This PR fixes #684, where only the first VML of a supported kind (`<v:image` or `<v:fill>`) would have been prefixed by `baseURL`, thus leaving any subsequent VML tags unprefixed.


Patch:
diff --git a/src/transformers/baseUrl.js b/src/transformers/baseUrl.js
index 1588ea2..59abac0 100644
--- a/src/transformers/baseUrl.js
+++ b/src/transformers/baseUrl.js
@@ -3,17 +3,41 @@ const isUrl = require('is-url-superb')
 const baseUrl = require('posthtml-base-url')
 const {get, isObject, isEmpty} = require('lodash')
 
-// VML backgrounds need regex because they're inside HTML comments :(
+/**
+ * VML backgrounds must be handled with regex because
+ * they're inside HTML comments.
+ */
 const rewriteVMLs = (html, url) => {
-  const vImageMatch = html.match(/(<v:image.+)(src=['"]([^'"]+)['"])/)
-  const vFillMatch = html.match(/(<v:fill.+)(src=['"]([^'"]+)['"])/)
+  // Handle <v:image>
+  const vImageMatches = html.match(/<v:image[^>]+src="?([^"\s]+)"/g)
 
-  if (vImageMatch && !isUrl(vImageMatch[3])) {
-    html = html.replace(vImageMatch[0], `${vImageMatch[1]}src="${url}${vImageMatch[3]}"`)
+  if (vImageMatches) {
+    vImageMatches.forEach(match => {
+      const vImage = match.match(/<v:image[^>]+src="?([^"\s]+)"/)
+      const vImageSrc = vImage[1]
+
+      if (!isUrl(vImageSrc)) {
+        const vImageSrcUrl = url + vImageSrc
+        const vImageReplace = vImage[0].replace(vImageSrc, vImageSrcUrl)
+        html = html.replace(vImage[0], vImageReplace)
+      }
+    })
   }
 
-  if (vFillMatch && !isUrl(vFillMatch[3])) {
-    html = html.replace(vFillMatch[0], `${vFillMatch[1]}src="${url}${vFillMatch[3]}"`)
+  // Handle <v:fill>
+  const vFillMatches = html.match(/<v:fill[^>]+src="?([^"\s]+)"/g)
+
+  if (vFillMatches) {
+    vFillMatches.forEach(match => {
+      const vFill = match.match(/<v:fill[^>]+src="?([^"\s]+)"/)
+      const vFillSrc = vFill[1]
+
+      if (!isUrl(vFillSrc)) {
+        const vFillSrcUrl = url + vFillSrc
+        const vFillReplace = vFill[0].replace(vFillSrc, vFillSrcUrl)
+        html = html.replace(vFill[0], vFillReplace)
+      }
+    })
   }
 
   return html
@@ -23,7 +47,7 @@ module.exports = async (html, config = {}, direct = false) => {
   const url = direct ? config : get(config, 'baseURL')
   const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
-  // `baseUrl` as a string
+  // Handle `baseUrl` as a string
   if (typeof url === 'string' && url.length > 0) {
     html = rewriteVMLs(html, url)
 
@@ -34,7 +58,7 @@ module.exports = async (html, config = {}, direct = false) => {
       .then(result => result.html)
   }
 
-  // `baseUrl: {}`
+  // Handle `baseUrl` as an object
   if (isObject(url) && !isEmpty(url)) {
     html = rewriteVMLs(html, url.url)
 


Test patch:
diff --git a/test/expected/transformers/base-image-url.html b/test/expected/transformers/base-url.html
similarity index 82%
rename from test/expected/transformers/base-image-url.html
rename to test/expected/transformers/base-url.html
index cf8b6c4..a6bc3c1 100644
--- a/test/expected/transformers/base-image-url.html
+++ b/test/expected/transformers/base-url.html
@@ -56,13 +56,21 @@
 
     <div>
       <!--[if mso]>
-      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="https://example.com/image.jpg" style="600px;height:400px;" />
-      <v:rect fill="false" stroke="false" style="position:absolute;600px;height:400px;">
+      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="https://example.com/image.jpg" style="width:600px;height:400px;" />
+      <v:rect fill="false" stroke="false" style="position:absolute;width:600px;height:400px;">
       <v:textbox inset="0,0,0,0"><div><![endif]-->
         <div>test</div>
       <!--[if mso]></div></v:textbox></v:rect><![endif]-->
     </div>
 
+    <!--[if mso]>
+      <v:image src="https://example.com/image-2.jpg" xmlns:v="urn:schemas-microsoft-com:vml" style="width:600px;height:400px;" />
+    <![endif]-->
+
+    <!--[if mso]>
+      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="https://example.com/image-3.jpg" style="width:600px;height:400px;" />
+    <![endif]-->
+
     <table>
       <tr>
         <td background="https://example.com/image.png" bgcolor="#7bceeb" width="120" height="92" valign="top">
@@ -79,5 +87,13 @@
         </td>
       </tr>
     </table>
+
+    <!--[if mso]>
+      <v:fill type="tile" src="https://example.com/image.png" color="#7bceeb" />
+    <![endif]-->
+
+    <!--[if mso]>
+      <v:fill type="tile" src="https://example.com/image.png" color="#7bceeb" />
+    <![endif]-->
   </body>
 </html>
diff --git a/test/fixtures/transformers/base-image-url.html b/test/fixtures/transformers/base-url.html
similarity index 81%
rename from test/fixtures/transformers/base-image-url.html
rename to test/fixtures/transformers/base-url.html
index fabbc51..5cff5ba 100644
--- a/test/fixtures/transformers/base-image-url.html
+++ b/test/fixtures/transformers/base-url.html
@@ -58,13 +58,21 @@
 
     <div>
       <!--[if mso]>
-      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="image.jpg" style="600px;height:400px;" />
-      <v:rect fill="false" stroke="false" style="position:absolute;600px;height:400px;">
+      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="image.jpg" style="width:600px;height:400px;" />
+      <v:rect fill="false" stroke="false" style="position:absolute;width:600px;height:400px;">
       <v:textbox inset="0,0,0,0"><div><![endif]-->
         <div>test</div>
       <!--[if mso]></div></v:textbox></v:rect><![endif]-->
     </div>
 
+    <!--[if mso]>
+      <v:image src="image-2.jpg" xmlns:v="urn:schemas-microsoft-com:vml" style="width:600px;height:400px;" />
+    <![endif]-->
+
+    <!--[if mso]>
+      <v:image xmlns:v="urn:schemas-microsoft-com:vml" src="https://example.com/image-3.jpg" style="width:600px;height:400px;" />
+    <![endif]-->
+
     <table>
       <tr>
         <td background="image.png" bgcolor="#7bceeb" width="120" height="92" valign="top">
@@ -81,5 +89,13 @@
         </td>
       </tr>
     </table>
+
+    <!--[if mso]>
+      <v:fill type="tile" src="image.png" color="#7bceeb" />
+    <![endif]-->
+
+    <!--[if mso]>
+      <v:fill type="tile" src="https://example.com/image.png" color="#7bceeb" />
+    <![endif]-->
   </body>
 </html>
diff --git a/test/test-transformers.js b/test/test-transformers.js
index b6cc768..8fa83f3 100644
--- a/test/test-transformers.js
+++ b/test/test-transformers.js
@@ -196,14 +196,14 @@ test('extra attributes (disabled)', async t => {
 })
 
 test('base URL (string)', async t => {
-  const source = await fixture('base-image-url')
+  const source = await fixture('base-url')
   const html = await Maizzle.applyBaseImageUrl(source, 'https://example.com/')
 
-  t.is(html, await expected('base-image-url'))
+  t.is(html, await expected('base-url'))
 })
 
 test('base URL (object)', async t => {
-  const source = await fixture('base-image-url')
+  const source = await fixture('base-url')
   const html = await Maizzle.applyBaseImageUrl(source, {
     url: 'https://example.com/',
     allTags: true,
@@ -211,7 +211,7 @@ test('base URL (object)', async t => {
     inlineCss: true
   })
 
-  t.is(html, await expected('base-image-url'))
+  t.is(html, await expected('base-url'))
 })
 
 test('prettify', async t => {