Giter VIP home page Giter VIP logo

framework's Introduction

Maizzle Starter

Quickly build HTML emails with Tailwind CSS

Version Build Downloads License

Getting Started

Run this command and follow the prompts:

npx create-maizzle

Documentation

Maizzle documentation is available at https://maizzle.com

Issues

Please open all issues in the framework repository.

License

The Maizzle framework is open-sourced software licensed under the MIT license.

framework's People

Contributors

alexdilley avatar alextes90 avatar cossssmin avatar dependabot-preview[bot] avatar dependabot[bot] avatar depfu[bot] avatar dhritzkiv avatar digitalmaster avatar gaurishhs avatar genu avatar jank1310 avatar louislva avatar rabberbock avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

framework's Issues

Using with another template engine

Hi!
Maizzle its awesome framework with full control of templates.
But can we use it with other template engines?
For example if I want to use Laravel and Blade and need to include some dynamic content with specific directives like @foreach or include some variable. Is it possible somehow?

Parsing value from variable inside 'class' tag doesn't work, but it works with i.e. 'for' and 'id'

  • Maizzle Version: ^1.0.0
  • Node.js Version: #.#.#

What worked

<fetch url="">
   <each loop="product in response.products">
      <label 
         for="product{{ product.id }}"
         on="tap:fakeCheck{{ product.id }}.toggleClass(class='bg-red-50')"
         role="button"
         tabindex="{{ product.id }}"
         class="no-outline font-bold font-base no-border">
      {{ product.title }} / &euro; {{ product.price }}
      </label>
   </each>
</fetch>

What didn't work

<fetch url="">
   <each loop="product in response.products">
      <label 
         for="product{{ product.id }}"
         on="tap:fakeCheck{{ product.id }}.toggleClass(class='bg-red-50')"
         role="button"
         tabindex="{{ product.id }}"
         class="no-outline font-bold font-base no-border bg-deals-{{ product.type }}">
      {{ product.title }} / &euro; {{ product.price }}
      </label>
   </each>
</fetch>

Product return a lowercase string that matched the term weekend, deal--{{ product.type }} should then parse deal--weekend which it didn't. For some reason though this method worked for the id and for field.

external engine variables break urlParameters

  • Maizzle Version: 1.1.1
  • Node.js Version: 13.8.0

I'm trying to generate production emails containing jinja2 template conditionals and variables that I can later populate in a transactional scenario (order, welcome, password reset, etc emails).

With this example:

---
urlParameters:
  utm_source: transactional email
  utm_campaign: order confirmed
---
...
Click here to <a href="{{{ page.SHOP_SITE_URL }}}/@{{ order.id }}">view current order status</a>

it generates the following:

Click here to <a href="https://store.example.ca/{{ order.id }}" style="color: #5850ec;">view current order status</a>

But it should generate this:

Click here to <a href="https://store.example.ca/{{ order.id }}?utm_campaign=order confirmed&utm_medium=email&utm_source=transactional email" style="color: #5850ec;">view current order status</a>

config.js:

  ...
  urlParameters: {
    _options: {
      qs: {
        encode: false
      },
    },
    utm_medium: 'email'
  },

  SHOP_SITE_URL: 'https://store.example.ca',

To be clear, other links in the same email work fine, as long as they don't have @{{ variable_name }} syntax embedded in the href.

Is it possible to address this as a maizzle fix - or is there a standard way to deal with this in my templates?

Parsing value from variable inside 'class' tag doesn't work, but it works with i.e. 'for' and 'id'

  • Maizzle Version: ^1.0.0
  • Node.js Version: #.#.#

What worked

<fetch url="">
   <each loop="product in response.products">
      <label 
         for="product{{ product.id }}"
         on="tap:fakeCheck{{ product.id }}.toggleClass(class='bg-red-50')"
         role="button"
         tabindex="{{ product.id }}"
         class="no-outline font-bold font-base no-border">
      {{ product.title }} / &euro; {{ product.price }}
      </label>
   </each>
</fetch>

What didn't work

<fetch url="">
   <each loop="product in response.products">
      <label 
         for="product{{ product.id }}"
         on="tap:fakeCheck{{ product.id }}.toggleClass(class='bg-red-50')"
         role="button"
         tabindex="{{ product.id }}"
         class="no-outline font-bold font-base no-border bg-deals-{{ product.type }}">
      {{ product.title }} / &euro; {{ product.price }}
      </label>
   </each>
</fetch>

Product return a lowercase string that matched the term weekend, deal--{{ product.type }} should then parse deal--weekend which it didn't. For some reason though this method worked for the id and for field.

Tailwind changes not being picked up when watching files

Right now, when using maizzle serve, even if the browser refreshes when you make a change in tailwind.config.js, that change is not being picked up - you need to stop the restart the process.

This is happening because we basically require('tailwind.config.js') so that we can read a bunch of config keys (this was needed starting with Tailwind 1.4).

The fix for this requires that we invalidate the require() cache, so everytime we call Tailwind.fromFile() a fresh user's Tailwind config is loaded.

Feature request: Components for Common HTML

  • Maizzle Version: 0.1.1
  • Node.js Version: 11.11.0

Description:

Hi there! Please excuse me if this is in the wrong format.

I want to say thank you for all your hard work in putting this together. I love the speed & ease I can pump out email templates using Maizzle + Tailwind.

I wanted to suggest giving users the option to use prebuilt Nunjucks components to avoid wewriting out painfully tedious HTML table markup (and similar elements).

I've created a some basic prop-based components while using the {{ cotnent }} slot for reusability (as per the docs) but also to allow for nesting components.

Table Component Examples

Single <tr> and <td>

<!-- src/components/table-singletd.njk -->

<table class="{{ data.classTb }}">
  <tr class="{{ data.classTr }}">
    <td class="{{ data.classTd }}">
      {{ content }}
    </td>
  </tr>
</table>

Multiple nested <tr> inside of {{ content }}

<!-- src/components/table-multitr.njk -->

<table class="{{ data.classTb }}">
  {{ content }}
</table>

Single <tr> with multiple nested <td> inside of {{ content }}

<!-- src/components/table-multitd.njk -->

<table class="{{ data.classTb }}">
  <tr class="{{ data.classTr }}">
    {{ content }}
  </tr>
</table>

Other Exmples

Simple anchor tag with conditional check to see if href is empty.

<!-- src/components/anchor.njk -->

<a {% if data.href %} href="{{data.href}}"{% endif %}>
  {{ content }}
</a>

Using Components in Templates

In our Templates we can now use the component as we like using the instructions to the Maizzle docs.

However I chose to set each component to a variable for sake of not repeating the path multiple times (should the components folder be moved, etc).

This markup...

<!-- src/templates/my-template.njk -->

{% set multiTd = "src/components/table-multitd.njk" %}
{% set a = "src/components/anchor.njk" %}

{% component multiTd, { classTb: 'w-full' }% }
  <td class="w-1-2">
    { %component a, { class: 'btn-link', href: 'http://www.maizzle.com'} %}
      Maizzle Home
    {%endcomponent%}
  </td>
  <td class="w-1-2">
    {% component a, { class: 'btn-link', href: '#'} %}
      Product
    {% endcomponent %}
  </td>
{% endcomponent %}

Will output this when compiled...

<table class="w-full bg-gray-900" cellpadding="0" cellspacing="0" role="presentation">
  <tbody>
    <tr>
      <td class="w-1-2">
        <a class="text-orange-100" href="http://www.maizzle.com">
          Maizzle Home
        </a>
      </td>
      <td class="w-1-2">
        <a class="text-orange-100">
          Other Anchor (No href prop specified)
        </a>
      </td>
    </tr>
  </tbody>
</table>

I figure this could be explanded into much more such as a columned artciles, hero areas, reusable buttons, social media buttons, etc, etc.

Let me know if this is something you'd worth considering into the framework and I will happily make a PR with a few different reusable components.

Thanks!

No 'config.undefined.js' file found

  • Maizzle/CLI Version: 0.2.1
  • Maizzle/framework Version: 0.3.0
  • Node.js Version: 10.16.1

Description:

The maizzle/framework v 0.3.0 has the following issues when running maizzle serve:

(node:17474) UnhandledPromiseRejectionWarning: Error: No 'config.undefined.js' file found
(node:17474) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:17474) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code

Steps To Reproduce:

Update/install maizzle/framework v.0.3.0
Create new maizzle project and cd
run maizzle serve

Workaround

in your terminal run touch config.undefined.js
then maizzle serve

baseImageURL not replacing URLs in production build

  • Maizzle Version: 1.0.8
  • Node.js Version: 14.1.0

I used https://github.com/maizzle/starter-netlify-identity as a starter for a new Maizzle project and simply added baseImageURL to config.production.js.

What I expected

My production output HTML to have image URLs prefixed with baseImageURL.

What I saw

Image URLs are output as-is, without the prefix.

I also tried adding baseImageURL to my Front Matter YAML on a specific template, but that didn't help either.

Add default for opts.tailwind.css in render()

Right now, you need to explicitly set tailwind.css in the options object when using the render() method - otherwise you get an error that "Tailwind CSS was compiled to empty string".

Maizzle should provide at least @tailwind utilities; by default, so if you don't want anything else generated when compiling Tailwind, you don't have to explicitly set that key.

Basically this:

Maizzle.render(
  str,
  {
    tailwind: {
      config: require('./tailwind.config'),
      css: '@tailwind utilities;',
    },
    maizzle: {
      config: require('./config'),
    }
  }
).then(html => console.log(html))

would be the same as this:

Maizzle.render(
  str,
  {
    tailwind: {
      config: require('./tailwind.config'),
    },
    maizzle: {
      config: require('./config'),
    }
  }
).then(html => console.log(html))

@import after @tailwind in main.css does not get added to compiled CSS

  • Maizzle Version: 0.1.1
  • Node.js Version: 9.11.2

Description:

In main.css, any @imports that appear after the @tailwind imports seem to get ignored.

For example:

This produces a stylesheet that has the reset, components and tailwind utilities, but we are missing the custom utilities (table-header-group etc)

/* does not work */
@import "custom/reset";
@tailwind components;
@tailwind utilities;
@import "custom/utilities";

If you reorder @import "custom/utilities"; so it's above the @tailwind it does get compiled

/* works */
@import "custom/reset";
@import "custom/utilities";
@tailwind components;
@tailwind utilities;

The stylesheet get compiled in both versions, but in the first example it's missing anything in the custom/utilities file.

Safe class renaming conflict

84b6dbd introduced a bug where two different utility classes would be renamed using the same name.

If you have this in the tailwind.config.js:

module.exports = {
  theme: {
    width: {
      '2/5': '40%',
      '2.5': '0.625rem',
    }
  }
}

Then you get:

.w-2-5 {
  width: 40% !important;
}

.w-2-5 {
  width: 0.625rem !important;
}

This can be fixed by replacing . with a _ instead of -, so you'd get this:

.w-2-5 {
  width: 40% !important;
}

.w-2_5 {
  width: 0.625rem !important;
}

<raw> tag error

  • Maizzle Version: 1.0.9
  • Node.js Version: 13.10.1

I'm having an issue with raw tags maizzle. I believe this was fixed in this version but I can't seem to get it to work. I'm not sure if I'm using them incorrectly?

Here is an example of a view as webpage link I'm using

<a href="<raw>{{system.viewAsWebpageLink}}</raw>" target="_blank">View Online</a>

I get this error

✖ Failed to compile build_local/module-template.html (node:23887) UnhandledPromiseRejectionWarning: ReferenceError: system is not defined (node:23887) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) (node:23887) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

If I remove the <raw> tags and link it builds correctly.

TypeError: Cannot read property 'tags' of undefined

  • Maizzle Version: 0.6.0
  • Node.js Version: 6.13.0

Description:

When attempting Maizzle Serve on a project I get the following error.

✖ Build failed
TypeError: Cannot read property 'tags' of undefined
at Object.module.exports.init (/node_modules/@maizzle/framework/src/nunjucks/index.js:7:23)
at module.exports (/node_modules/@maizzle/framework/src/generators/output/toString.js:62:48)
at asyncForEach (/node_modules/@maizzle/framework/src/generators/output/toDisk.js:49:18)

Steps To Reproduce:

Originally I started getting this error today on a project I had been working on for a while, but I tried creating a new project and continue to get the error. I tried using different node versions but I still get the same error. I also tried on two different Mac computers and get the same error.

afterBuild event

We could have an afterBuild event that would run after everything is compiled and templates have been output to the build.destination directory. This would return a list of output file paths.

Would be useful for things like:

  • generating a build path files list
  • running additional post-processing tasks
  • programmatically CRUDing other files based on these paths

This event would only be available for the CLI (when running maizzle build), so you would need to register it in your environment config like so:

events: {
  afterBuild(files) {
    console.log(files)
  },
},

With the maizzle/maizzle project, that would output:

[
  'build_staging/amp-carousel.html',
  'build_staging/images/maizzle.png',
  'build_staging/newsletter.html',
  'build_staging/transactional.html'
]

This was inspired by maizzle/maizzle-php#36 and Jigsaw's afterBuild event listener.

Dynamic class name/value missing in production when set to string 'outer'

  • Maizzle Version:
    Framework v0.9.1
    CLI v0.5.0

Description:

My production builds are losing width values when using dynamic class names.

I'm using a Nunjucks variable to post create a dynamic class name in a layout. The class is .w-col-outer or .w-col-inner. The values are defined in tailwind.config.js as "660px" and "550px" respectively.

In my layout I'm using a wrapper table and several cells opened like so: <td class="w-col-{{page.classes.columnWidth}}">. In the template file I define columnWidth: outer | inner in the frontmatter.

When I run a production build, templates with columnWidth: outer generate wrapper cells without inline widths, width attributes. or the original class-name. They do however have inlined values from reset.css.

Templates with columnWidth: inner work fine. The class name is removed and replaced with inline width and width attribute with the expected value.

It also works fine if I change the layout to <td class="{{page.classes.columnWidth}}"> and use columnWidth: w-col-outer in the template. It's an easy workaround, so this isn't a horrible issue in this case.

I've checked my config files. The only thing I that seemed a potential issue was applySizeAttribute: {width: ["TABLE", "TD"]} but changing that seems to have no effect.

Reverse Stack & PurgeCSS

  • Maizzle Version: 0.7.0
  • Node.js Version: 12.10.0

Description:

I've tried to use the https://maizzle.com/docs/reverse-stack/#reverse-2-col but when I run maizzle build staging or maizzle build production the sm-inline-block w-1-2 classes get striped away.

It seems like that PurgeCSS can't find the classes inside the template and because its not inside the css, the classes get strieped away. I think there is a bug inside the PurgeCSS extractor.

It works fine during maizzle serve but not in staging/production.

Steps To Reproduce:

  1. Create a new Maizzle project maizzle new my-project
  2. Fix this small Bug maizzle/maizzle#13
  3. Replace the content inside {% block template %} ... {% endblock %} in src/templates/newsletter.njk with the example:
<table class="w-full">
  <tr>
    <td class="sm-inline-block w-1-2 sm-w-full px-8">
      <p class="text-2xl font-hairline font-sans text-black">Left text</p>
      <p class="text-grey-dark">Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore aspernatur.</p>
    </td>
    <td class="sm-inline-block w-1-2 sm-w-full px-8">
      <img src="https://picsum.photos/600/600">
    </td>
  </tr>
</table>
  1. Run maizzle build staging and check the build_staging/newsletter.html It should look like this:
<body style="margin: 0; padding: 0; width: 100%; word-break: break-word; -webkit-font-smoothing: antialiased;">
  <table role="presentation" width="100%" cellspacing="0" cellpadding="0">
    <tbody><tr>
      <td class="sm-w-full" style="padding-left: 8px; padding-right: 8px;">
        <p style="font-family: -apple-system, 'Segoe UI', sans-serif; font-weight: 100; color: #000000; font-size: 24px;">Left text</p>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore aspernatur.</p>
      </td>
      <td class="sm-w-full" style="padding-left: 8px; padding-right: 8px;">
        <img src="https://picsum.photos/600/600" style="border: 0; line-height: 100%; vertical-align: middle;" alt="">
      </td>
    </tr>
  </tbody></table>

</body>

With only <td class="sm-w-full" style="padding-left: 8px; padding-right: 8px;">

Strangely enough if I just add the example right after {% block template %} and leave the newsletter example below, the classes get not purged and it works fine...

Using <outlook> after <if> has no output

  • Maizzle Version: 1.0.7
  • Node.js Version: 10.20.1

Input

<outlook>
  <xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml>
  <style>
    td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
  </style>
</outlook>

Expected output

<!--[if mso|ie]>
  <xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml>
  <style>
    td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
  </style>
<![endif]-->

Actual output
Nothing


For now I'll just use the regular tags, but using the default Maizzle config the <outlook> tag doesn't seem to do anything. I haven't tried any of the other custom tags yet.

Tailwind config path can only be inside project root

  • Maizzle Version: 1.0.5

Right now, you can't use a tailwind.config.js file located outside your project root:

tailwind: {
  css: 'src/assets/css/main.css',
  config: 'F:/DEV/foo/bar/tailwind.config.js',
},

Maizzle currently can't get to that config file, so it uses the default Tailwind settings (as in modern CSS, for web not for email).

Autoprefixer

Hi, I'm searching for config option for autoprefixer.

I'm using Maizzle to generate html templates that get included into pages, not for actual emails. So I want to include autoprefixer and I saw that this is already in the Maizzle package so I guess this is interally used already and availlable in the build process? If not, would there be a workaround for me?

Thanks in advance!

<raw> tag not working inside <if condition>

  • Maizzle Version: 1.0.3
  • Node.js Version: 12.16.3

Hi there.

I love the project, thanks a lot for all your work and effort!

I want to achive the following:

In development:
Hi, Jolly Roger.
In Production:
Hi, {{username}}

I thought I'll be clever and tried:

<if condition="page.env === 'production'">
 <raw>
     {{ username }}
</raw>
</if>
 <else>
     Jolly Roger
 </else>

This worked for development, but in production the output was:
undefined

-> So I guess the <raw> tag does not work in a <condition>

Thx

Stop serve break when there is a compilation error

Description:

Everytime I am developing and if I have a syntax error, for example, while importing components amongst the mid tipping and the auto saving the compiler throws an error and breaks my development flow.
Would it be possible to just silenntly catch these exceptions, throw them in the consnole but not really halt the server? Just use the previously working compiled version. Its better than always having to do maizzle serve everytime

Steps To Reproduce:

Pass data to components inside other components

I would like to know if its possible to pass data values to other components like so:

// on component a.njk
{% component "src/components/common/spacer.njk", {
  spacing: "{{ data.spacing }}"
} %} {% endcomponent %}
// on component that uses a.nnjk
{% component "src/a.njk", {
 spacing: "pt-16"
} %} {% endcomponent %}

This doesnt work however.

Is there a way to do it?

Multilevel template inheritance not working properly

  • Maizzle Version: 0.7.3
  • Node.js Version: 10.18.1

Description:

Hi. I want to thank you for first for having done for all of us maizzle framework. It's an awesome tool for writing email templates.

However, there is a thing i would like to report to you. I saw that this argument was already discussed in an other issue some months ago #69, and you've found already a solution, but this seems not to work properly.

The problem occurs when i try to extend three or more templates. Take this example:
i have the default layout, that is extended by a first template (first.njk)

---
layout: 'src/layouts/default.njk'
level: 1
---

{% block template %}
  <table class="w-600 sm:w-full">
    <tr>
      <td class="p-48 sm:py-32 sm:px-24 text-center">
        {% block level %}
          {{ page.level }}
        {% endblock %}

        {% block content %}
            first level content
          {% endblock %}
      </td>
    </tr>
  </table>
{% endblock %}

Then i have a second template, called "second.njk", that extends the first

---
layout: 'src/templates/first.njk'
level: 2
---

{% block content %}

  second level content that overwrites the  first one {{ page.level }}

{% endblock %}

Here everything is still working, but when you try to add a third template (third.njk), that extends the second, there are some obvious problems

---
layout: 'src/templates/second.njk'
level: 3
---

{% block content %}

  third level content that overwrites the second one {{ page.level }}

  {% block other_block %}
    new
  {% endblock %}

{% endblock %}

The first problem is that i want to extend the template and define everytime just the "content" block. I don't want to define everytime the "template" block, because, in this case, i have there just some layout (markup and css) and i don't want to rewrite it everytime. Rewriting the "content" block in "second.njk" works, and also the layout (markup and css) from "first.njk" is there. But when i overwrite the "content" block in "third.njk", the layout is no more there.

The second problem is that i can't use {{ super() }} in the blocks: let's say i want to take everything from "content" block of "first.njk" also in "second.njk", define something else there and then take all this content in "third.njk" with another {{ super }}, defining even here something else but keeping everything was already done in other templates. This doesn't work.

The reason of why this is not working is in "toString.js":

    html = layout ? `${blockStart} extends "${layout}" ${blockEnd}\n${html}` : html
    html = nunjucks.renderString(html, { page: config, env: options.env, css: compiledCSS })

    while (fm(html).attributes.layout) {
      const front = fm(html)
      html = `${blockStart} extends "${front.attributes.layout}" ${blockEnd}\n${blockStart} block template ${blockEnd}${front.body}${blockStart} endblock ${blockEnd}`
      html = nunjucks.renderString(html, { page: config, env: options.env, css: compiledCSS })
    }

Maizzle here is doing something that nunjucks already comes with, in another way, that causes the problems that i mentioned above.

The solution would be to remove the while loop, and to use the {% extends "template" %} syntax, right after the definition of the front matter. This works, inheritance works very well, and i can call {{ super() }} in every inheritance level, in the same block. I could also define just the blocks that i want, in every level, without having to define the "template" block everytime. Just this would be enough to solve

    html = layout ? `${blockStart} extends "${layout}" ${blockEnd}\n${html}` : html
    html = nunjucks.renderString(html, { page: config, env: options.env, css: compiledCSS })

In this case, who is using just one level of inheritance (template that extends the layout), could omit the {% extends "template" %} syntax, and who's doing multiple levels of inheritance should use it for all the levels.

In my opinion nunjucks is a very, very powerful tool, and maizzle should use all the best features that nunjucks offers. The multilevel inheritance gives me the possibility to set the "skeleton" of my email template just once, and change then the content of the emails that i have to send.

urlParameters preserve @ as unescaped

I am trying to use DotMailer merge tags in Front Matter urlParameters. I am out of my depth as usual. I know @ is a reserved character and needs the right combination of '' and "" to preserve. I understand double quotes encodes, whereas single could preserve.


urlParameters:
utm_source: '@Date@'

Url outputs.

utm_source=%40date%40

If anyone knows the answer, that would be great. I've been looking at cheerio and htmlparser2 - generally looking through the dependencies hoping to get lucky with some parameters I can override.

Note- research so far "decodeURIComponent('%40')",
, { decodeEntities: true }

Unable to `Maizzle.render()` files that have includes

  • Maizzle Version: 0.3.2
  • Node.js Version: 11.4.0

Description:

First off, you've done a great job with Maizzle. Exactly what I was looking for since I'm already a huge fan of tailwind. I want to use it server side inside my express app and here's the issue I'm running into.

Unable to Maizzle.render() the example file

Steps To Reproduce:

Code

const str = fs.readFileSync(
  path.join(__dirname, 'src/templates/transactional.njk'),
  'utf8',
);

Maizzle.render(str, {
  tailwind: {
    config: require('./tailwind.config'),
    css:
      '@tailwind utilities; .myborder { @apply border border-solid border-gray-300; }',
  },
  maizzle: {
    config: require('./config.production'),
  },
})
  .then(console.log)
  .catch(console.error);

Receive the following error

{ Template render error: (unknown path)
  Error: template not found: src/partials/footer-links.njk

maizzle new [project] installs v0.8, not v0.9

Framework v0.8.0
CLI v0.5.0

What's the process for updating/installing a new project to the latest version of Maizzle?

I'm trying to use the new markdown features, but my <markdown> tags are being ignored (literal tag is displayed in the html).

It seems my Framework is stuck on 0.8.0, shouldn't maizzle new [project] install version 0.9.1?

I deleted my package.lock.json and increased the version in package.json and did a new npm install to get the latest version.

Some kind of caching issue?

  • Maizzle Version: 0.4.3
  • Node.js Version: 10.16.0

I'm using a system with a lot of includes. I've not quite figured out how to replicate, but I will update my ticket when I have a moment. I'm experiencing this on two different instances/projects.

After I run a build task (staging or production), I often experience two issues:

Changes in the HTML have not been picked up. I often have to add random text and rerun the build. For example I commented out a chunk of text and replaced it with an include, when I ran the build the uncommented text appeared (as if it was serving a cached version of that include) and the new include appeared.
Tailwind classes get removed and are not inlined. If the tailwind class has been used before, it get's inlined. If it's the first time I've used the class it's ignored and removed. If I add the class to the base template it's then parsed correctly in the includes.

The issues seem to be happening in the included templates.

Tailwind base import

  • Maizzle Version: 1.0.9
  • Node.js Version: 12.16.2

After hours of searching I noticed that if I include base styles from tailwind:

// main.css

/**
 * This injects Tailwind's base styles and any base styles registered by
 * plugins.
 */
@import 'tailwindcss/base';

that the build command will crash with:

$ maizzle build production
✖ Failed to compile build_production/accordion.html
(node:7733) UnhandledPromiseRejectionWarning: CssSyntaxError: <css input>:273:1: Unclosed comment
    at Input.error (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/input.js:130:16)
    at unclosed (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/tokenize.js:50:17)
    at Object.nextToken (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/tokenize.js:240:15)
    at Parser.parse (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/parser.js:49:30)
    at parse (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/parse.js:17:12)
    at new LazyResult (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/lazy-result.js:60:16)
    at Processor.<anonymous> (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/processor.js:138:12)
    at Processor.process (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/postcss/lib/processor.js:117:23)
    at process (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/posthtml-safe-class-names/lib/index.js:29:86)
    at traverse (/Users/macpro/Code/newschrott/starter-amp4email/node_modules/posthtml/lib/api.js:105:26)
(node:7733) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:7733) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

This was just working fine back in version 0.9.1 and it actually also works with maizzle watch but the production build will always fail, no matter what.

As you do not include base styles in any of your starters/examples I guess you already know about this? Is there any way around this or how to fix this?

PS: I actually tested a few versions, I think it's all from 1.0 and up as it seems to be a posthtml/postcss problem?!

render() does not use full config

Right now, the render() method uses an environment config based only on what you give it: an object based on the merger between the config you pass in options, and any Front Matter attributes.

This can lead to unexpected results, because some option that you rely on might be missing.

Ideally, the generated config should be a merger between:

  1. the base config.js
  2. a config object that you pass in options
  3. any Front Matter attributes

[Question] How do I pass custom Tokens when Building email

Hi, I am trying to create some email templates for Marketo. Marketo uses tokens in this format {{ Token }}. For example, the unsubscribe token is <a href="{{system.unsubscribeLink}}" target="_blank" class="mktNoTrack">. When I run Maizzle build the href is left blank <a href target="_blank" class="mktNoTrack">

How do I pass the token through? Looking through the docs I see how to do it for CSS tags, but I wasn't sure about tokens used in HTML.

Default layout not loaded

  • Maizzle Version: Framework v0.5.0 / CLI v0.3.0
  • Node.js Version: v12.13.1

Description:

If the layout var is not declared inside template, the default layout is not exported.

Steps To Reproduce:

Default installation

Solution:

$ @maizzle/framework/src/generators/output/toString.js

//html = hasLayout ? `{% extends "${hasLayout}" %}\n${html}` : html
html = layoutPath ? `{% extends "${layoutPath}" %}\n${html}` : html

Nunjucks Multilevel Template Inheritance is broken

  • Maizzle Version: 0.4.4
  • Node.js Version: 12.13.1

Description:

Inheritance between templates doesn't work.

You can find the feature's documentation here:
Nunjucks (https://mozilla.github.io/nunjucks/templating.html#template-inheritance)
and Jinja (https://jinja.palletsprojects.com/en/2.10.x/templates/#template-inheritance)

This is caused by the code who extends the layout for all templates.

#node_modules/@maizzle/framework/src/generators/output/toDisk.js:60

html = `{% extends "${layout}" %}\n${frontMatter.body}`

Some solutions could be to skip layout inclusion if the template contains {% extends, "layout" setting is explicitly set to false or a setting doNotExtendsLayout is set.

Steps To Reproduce:

Extends a file or a template with another template and build.

<style postcss> using default tailwind config

Currently, if you add this inside a template:

<block name="head">
  <style postcss>
    @screen sm {
      .foo {
        color: red;
      }
    }
  </style>
</block>

... the compiled CSS will use Tailwind's default config instead of the one that our project uses (as defined in the environment config that we ran the build command based on - i.e. in build.tailwind.config).

This is because fromFile() expects a Tailwind config object:

fromString: async (css, html, tailwindConfig = {}, maizzleConfig = {}) => {

... but we're passing it false instead:

replacements.postcss = css => Tailwind.fromString(css, html, false, config)

This could be solved by modifying the transformer to attempt to read the tailwind.config.js file set in the environment config, but only if non-object or an empty object is provided.

We could finally fallback to an empty object, that will use the default Tailwind config.

Tailwind classes in conditional comments

  • Maizzle Version:
    Framework v0.9.1
    CLI v0.5.0

Description:

Tailwind classes applied on elements inside HTML conditional comments aren't inlined. PurgeCSS and email-comb still apply and the entire class attribute is stripped off leaving no styles on the conditionals.

Typing out the inline styles works but severely limits the use of components. With manually entered inline values I can't use variables to apply classes that would change (e.g. bg-color, text-color, border-width, etc.).

Steps To Reproduce:

A production build with:
<!--[if mso]><td class="border-white border-3 border-solid text-center w-260 h-49 mso-padding-0"><![endif]-->

Results in:
<!--[if mso]><td><![endif]-->

Expected:
<!--[if mso]><td align="center" width="260" height="49" style="mso-padding-alt: 0; border: 3 solid #ffffff;"><![endif]-->

Default config.production.js is missing minify

  • Maizzle Version: 0.7.3

Description:

In config.js comments reads "Enable and configure minifier options. Enable for production builds." but minify is missing from the default config.production.js. Would it be good to have good defaults and best practices encouraged in the default configuration or is there some reason minify is omitted?

I can do a PR for adding at least the very basic minify configuration (enabled: true) or maybe a better default configuration if someone more experienced could say what it would be.

Gradients and key:value theme colors

Currently, tailwindcss-gradients cannot correctly generate gradients based on the default key:value pair from Tailwind's default color scheme.

So this:

linearGradients: theme => ({
  colors: theme('colors'),
    directions: {
      't': 'to top',
    },
}),

Would generate a class like:

.bg-gradient-t-gray {
  background-image: linear-gradient(to top, transparent, [object Object]) !important;
}

Solution

Need to create a dedicated gradients key in tailwind.config.js and define colors as in the docs.

deleting id="whatever" - when compiling?

  • Maizzle Version: 1.0.3
  • Node.js Version: 14.3.0
    deleting id="whatever" - when compiling?

When you compile id="menu" is deleted - is maizzle suppose to do that?

Great framework really appreciate it.

Cannot escape curly braces in attribute values

  • Maizzle Version: 1.0.0
  • Node.js Version: 14.1.0

Escaping curly braces with @ in content works as expected. But escaping it between < and > does not work at all. If I try it inside an attribute value (like <a href="@{{ config('app.url') }}">) it keeps @ as literal symbol and tries to evaluate the rest. This fails and is unexpected for me, and I could not figure out any other way to print {{ anything }} inside of an HTML element.

Trailings slashes in URLs get Replaced by -

  • Maizzle Version: 0.5.2
  • Node.js Version: v12.13.1

Description:

Trailings slashes in URLs get replaced by - if preceded by another - in the same URL
For ex: https://test.test/my-docs/help
become
https://test.test/my-docs-help

The issue is caused by new feature "Escaped class names"
2e4395d

Feature request: Merge tags

When I build templates that needs to be localized, I put all texts and URLs into variables at the ESP (Email service provider).

Example:

<h1>{l10n.introduction.header}</h2>
{l10n.introduction.text}

I could add the tags directly into the template, but it's problematic to have these during development and layout testing. Ideally I want to use front matter to populate the variables with test data and let it use that for development and stage builds.

Example:

---
l10n.introduction.header: Header 1 test
l10n.introduction.text: "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Itaque hic ipse iam pridem est reiectus; Huius ego nunc auctoritatem sequens idem faciam. Id enim volumus, id contendimus, ut officii fructus sit ipsum officium. Nondum autem explanatum satis, erat, quid maxime natura vellet.</p><p>Duo Reges: constructio interrete. Et certamen honestum et disputatio splendida! omnis est enim de virtutis dignitate contentio.</p>"
---
{% block template %}
<h1>{% __(l10n.introduction.header) %}</h2>
{% __(l10n.introduction.text) }
{% endblock %}

It would be nice to also include to exclude ESP specific code. Like:

{% mergetags %}
    {if (profile.data.vip = 'true')}
{% endmergetags%}

{# HTML code for VIP customers   #}

{% mergetags %}
    {/if}
{% endmergetags %}

Let me know if you have any question for my suggestion.

Borders

Not sure whether it's me or a bug, but I can't find a solution in the docs... So posting it here. Let me know if I should move this issue.


Somehow I can't get the borders to show when using class="border" or anything border-related. I'm using the default provided (tailwind) configs.

When I run this snippet using Tailwind instead of Maizzle all works as expected though:

https://codepen.io/sandervanhooft/pen/pBzWQY

Any suggestion how I can solve this?

Integration with existing framework, existing TailwindCSS setup

Hi,

First-off, this looks like the dream setup for me, as someone who doesn't know much about e-mail design and just loves working with Tailwind CSS. Thanks for your effort!

I have a few questions/concerns that some may be able to help with here: I want to integrate Maizzle in my Django web-app. This comes with two concerns for me:

  1. Django uses Jinja, which Nunjucks is a superset of. Somewhat like we do with Nunjucks, in Django we pass context to the email template (an .html file), and this context is used to populate variables with values. But in build production Maizzle does the same to {{ variable}}s. What's my best option here? Right now I'm thinking of this workflow which seems dirty yet simple:

    1. Use {% raw %} tags to escape variables that should be populated by Django.
    2. When happy with the result, copy the production file contents to my Django mail template.

The problem with this is I'm never able to truly test the final outcome (not that this is possible with my current setup). For variables like username this isn't so bad, but what if I want to pass the src value of an img, or an array of srcs like such?

  1. The project already integrates Tailwind CSS. As I understand, Maizzle uses a "custom flavor" of Tailwind, so the two config files would be incompatible in my case. I guess this can be solved by simply renaming the config file and have Maizzle look for the renamed config? or is this not supported?

Any pointers are much appreciated!

Default variable value not working inside the component, if that variable is not passed into the "locals" object

  • Maizzle Version: 1.0.8
  • Node.js Version: 14.2.0

Hi Cosmin,

I was trying the new features that you've included in the last 2 weeks in the maizzle framework. I have to say that i love the new postHtml syntax compared to the nunkucks one. However, i would like to report some small things to you.

Until the "0.9.1" maizzle version, using nunjucks, i defined some components that i used everytime in almost all emails, and i passed to each of them an object with all the configurations i wanted everytime i instantiated that component in a template. Inside of the component file, to each of those values contained in the object i gave a default value, with "old" nunjucks syntax (ex: {{ data.textColor or '#ffffff' }}).

Here is an example of a simple cta component:

<table class="w-full text-center">
    <tr>
        <th>
            <table align="center" class="border-solid rounded mx-auto w-full" style="border-width: {{ data.borderWidth or '0' }}; border-color: {{ data.borderColor or '#ffffff' }}; background-color: {{ data.backgroundColor or '#ffa500' }};">
                <tr>
                    <th class="text-center uppercase" style="color: {{ data.textColor or '#000000' }}; mso-padding-alt: {{ data.paddingY or '15px' }} {{ data.paddingX or '30px' }};">
                        <a href="{{ data.ctaLink or '#' }}" class="no-underline font-bold" style="color: {{ data.textColor or '#ffffff' }}">
                            <span class="block" style="padding-left: {{ data.paddingX or '30px' }}; padding-right: {{ data.paddingX or '30px' }}; padding-top: {{ data.paddingY or '15px' }}; padding-bottom: {{ data.paddingY or '15px' }}; ">
                                {{ data.ctaText }}
                            </span>
                        </a>
                    </th>
                </tr>
            </table>
        </th>
    </tr>
</table>

Every time that i instantiated this component, i was able to pass just one parameter (the only one without the default value - ctaText, and other parameters if they changed, like colors and other things).

I saw you did the same thing with the new postHtml components. In your example (vmlbg.html component) you do the same thing, with the new || or syntax.

<!--[if mso]>
<v:image src="{{ src || 'https://via.placeholder.com/600x400' }}" xmlns:v="urn:schemas-microsoft-com:vml" style="width:{{ width || 600 }}px;height:{{ height || 400 }}px;" />
<v:rect fill="false" stroke="false" style="position:absolute;width:{{ width || 600 }}px;height:{{ height || 400 }}px;">
<v:textbox inset="0,0,0,0"><div><![endif]-->
<content></content>
<!--[if mso]></div></v:textbox></v:rect><![endif]-->

Well, the problem is that, if you don't pass in the "locals" object even just one of the variables used inside the component file, mmaizzle will not compile anything, throwing this error:

✖ Failed to compile build_local/promotional.html
(node:6264) UnhandledPromiseRejectionWarning: ReferenceError: width is not defined
(Use `node --trace-warnings ...` to show where the warning was created)
(node:6264) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:6264) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I think this is a must feature to have. If you have a big component, using 20 or 30 variables inside and just 3 or 5 or 10 changing everytime, you will not pass to that component 30 variables every time.

Another thing i wanted to ask: while using the postHtml syntax {{ ... }} into the html files, vscode reports this syntax as errors, not everywhere, but just in the <style> tag and in the style="" attribute (i noticed just those 2 for the moment). Is there an extension for vscode that solves this? How do you handle this?

Using Blank Template does not inline the css

  • Maizzle Version: 0.2.0
  • Node.js Version: 6.9.0

Description:

I'm using maizzle to build blade files in my laravel project, in an attempt to generate partial files that will be included at run time.
To do so I created a blank template that just has a block template in it:

// /layouts/blank.njk
{% block template %}{% endblock %}

and for my partial:

---
layout: src/layouts/blank.njk
---
{% block template %}
		<tr>
			<td class="px-4">
				<p class="font-bold">{% raw %}{{ $title }}{% endraw %}</p>
			</td>
		</tr>
{% endblock %}

What I hoped would happen

I would expect Maizzle to process the file, convert classes to inline the styles etc

What happens

The output file still is minimized but no processing of the css (inlining is done)

<tr><td class="px-4"><p class="font-bold">{{ $title }}</p></td></tr>

Steps To Reproduce:

Described above

Any help with this would be greatly appreciated and thanks for a great tool!

Order of breakpoints

  • Maizzle Version: 0.1.2
  • Node.js Version: 10.16.0

Description:

By default breakpoints use max-width setting (desktop first) methodology, however the CSS order is set to min-width order/methodology. When using several breakpoints on an element, the CSS order means only the largest breakpoint is used (excluding full).

Steps To Reproduce:

<div class="xs-w-128 sm-w-256 w-full bg-black text-white">
text
</div>

In the above example the DIV will never be 128, as the 256 rule will always override it.

Possible solution:

Changing the breakpoint order fixed the issue for me:
BEFORE

screens: {
      all: {'raw': 'screen'},
      xs: {'max': '480px'},
      sm: {'max': '600px'}
 },

AFTER

screens: {
      all: {'raw': 'screen'},
      sm: {'max': '600px'},
      xs: {'max': '480px'}
 },

Getting undefined when using <raw> or @{{

I'm trying to not escape my vars in {{}} but they keep turning into undefined. I wrapped it in <raw> which didn't fix it, I also added @{{ but that didn't work for the href's.
Unsure what I'm doing wrong.

  • Maizzle Version: 1.0.7
  • Node.js Version: 13.0.1

Template:

---
title: You have been invited
bodyClass: bg-white h-full font-sans p-4 text-gray-900
htmlClass: bg-white h-full font-sans p-4 text-gray-900
---

<extends src="src/layouts/master.html">
  <block name="template">
    <raw>
      <div>
        <h1 class="mb-4 text-2xl font-bold leading-tight text-black">
          {{ givenName }} invited you to {{ storyName }}
        </h1>
        <p class="mb-12 text-base font-medium leading-tight">
          {{ fullName }} has invited you to edit
          <a href="@{{inviteLink}}" class="underline text-tella"
            >{{ storyName }}</a
          >
          on X.
        </p>
        <a
          href="{{ inviteLink }}"
          class="inline-flex items-center px-6 py-3 text-lg font-medium text-white no-underline border border-transparent rounded h-9 transition ease-in-out duration-150 focus:outline-none hover:shadow bg-tella focus:border-tella focus:shadow-outline-purple active:text-gray-400"
          >Open in X</a
        >
        <component src="src/components/footer.html"></component>
      </div>
    </raw>
  </block>
</extends>

Generated:

<div>
--
  | <h1 class="mb-4 text-2xl font-bold leading-tight text-black">
  | undefined invited you to undefined
  | </h1>
  | <p class="mb-12 text-base font-medium leading-tight">
  | undefined has invited you to edit
  | <a href="@undefined" class="underline text-tella">undefined</a>
  | on Tella.
  | </p>
  | <a href="undefined" class="inline-flex items-center px-6 py-3 text-lg font-medium text-white no-underline border border-transparent rounded h-9 transition ease-in-out duration-150 focus:outline-none hover:shadow bg-tella focus:border-tella focus:shadow-outline-purple active:text-gray-400">Open in Tella</a>

Feature Request: Nunjucks configuration

Would you accept a PR that gives more control over Nunjucks config?

For example, Nunjucks has some builtin filters but it also allows to add custom filters. So, if I have an email template that is for an invoice, I'd like to add a currency filter, which is implemented like:

var env = new nunjucks.Environment();

env.addFilter('currency', function(str, count) {
    // return currency formatted currency
});

This would require maizzle to allow configuring the nunjucks.Environment() instance, which currently it does not.

One option, that is, perhaps, the least intrusive is to add a layer of lifecycle hooks to the Maizzle.render() function.

So it would be something like:

Maizzle.render( str, {
   beforeRender: (config, nunjucksEnv) => {
       // gets called before nunjucks renders the template
   },
})

Any thoughts?

Usage in Node

The section on using on server side isn't very clear. I wasn't actually able to get it to work and there doesn't seem to be any other examples.

I think it needs to be more elaborate, or at least provide a full example so that someone can actually write up a good guide in the docs on how to set it up.

Possible to have multiple target build directories?

I am just trying maizzle to see if it will save me time (moving over from mjml templates, I'm also a fan of tailwindcss).

One question I have is, is it possible to have multiple target build directories?

starter-litmus has templates for ceej and slate, and upon build, both get saved to different subfolders within the destination folder (build_local, for example). But can you specify different destination folders that are not within the same parent build_local?

Here's my use-case:

  1. project_1 folder contains: unique header and footer, other unique email templates
  2. project_2 folder contains: unique header and footer, other unique email templates
  3. common folder contains: common components (to both projects)

I have all files in

  • template files in maizzle/src/templates/project_1 and maizzle/src/templates/project_2
  • component files (header and footer) in maizzle/src/components/project_1, maizzle/src/components/project_2 and maizzle/src/components/common

I would like my compiled / built email templates to go into ~/home/project_1/emails and ~/home/project_2/emails

Is this possible with the config?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.