Skip to content

Today I learned

A collection of useful code snippets for a faster development.
Reading time < 5 mins

#19

Add items to an array in Nunjucks

To add items in Nunjucks, use the .push() function.

{% set arr = [1,2] %}
{% set arr = (arr.push(3), arr) %}

Final array:

arr = [1,2,3]

Unfortunately, I did not found any references in the official Nunjucks documentation for this useful function πŸ€·πŸ»β€β™€οΈ

{% set animals = ['cat 🐱', 'dog 🐢', 'lion 🦁'] %}
{% set domesticAnimals = [] %}
{% for animal in animals %}
{% if animal !== 'lion' %}
{% set domesticAnimals = (domesticAnimals.push(animal), domesticAnimals) %}
{% endif %}
{% endfor %}

Final array:

domesticAnimals = ['cat 🐱', 'dog 🐢']

🧨 !important

If you use {% set .... %} inside a for-loop block, pay attention to have defined it outside before entering the loop.
I wrote a post about it: πŸ“’ Nunjucks scoped variable declarations


πŸ“š More info

Docs about Twig 'push' filter. Note that this filter is not present into the official Twig documentation πŸ€·πŸ»β€β™€οΈ

#18

Git flow initialize

To inizialize git flow with default branches configuration, run

git flow init -d

It will create a git flow structure to your project

Initialized empty Git repository in /Users/giulia/Sites/giulia/test/.git/
Using default branch names.
No branches exist yet. Base branches must be created now.
Branch name for production releases: [main]
Branch name for "next release" development: [develop]
.git/hooks/post-commit: line 8: git-stats: command not found

How to name your supporting branch prefixes?
Feature branches? [feature/]
Bugfix branches? [bugfix/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []
Hooks and filters directory? [/Users/giulia/Sites/giulia/test/.git/hooks]

πŸ“š More info

#17

Add multiple classes in pug

Pug, formerly known as Jade, is a template engine thas uses a JavaScript render engine. Sometimes we have to add a class conditionally based on a variable, here I go with some tricky solutions I found.

One condition to check #

The easiest way to check a class in pug is using the ternary operator

- var cond1 = true
.c-component(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE' )

HTML output

<div class="c-component cond1-true"></div>

But there are other two ways to write the same exact condition in pug:

- var cond1 = true

//- (1)
.c-component(class={'cond1-true': cond1 === true})

//- (2)
.c-component(class={cond1True: cond1 === true})

Important #

🧨 !important
(1) kebab-case class cond1-true must be wrapped in quotes
(2) camelCase class cond1True can skip wrapper quotes

HTML output

<div class="c-component cond1-true"></div>
<div class="c-component cond1True"></div>

More than one condition to check #

If we have to check two condintions, we can go also in this case with the ternary operator to choose which classes we want.

We can write the conditionals in three ways:

  1. using two class attributes separated by space
(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE' class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')
  1. using two class attributes separated by comma
(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE', class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')
  1. using two class attributes, one for each parentesis
(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE')(class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')

All three:

pug

- var cond1 = true
- var cond2 = false

.c-component(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE' class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')

.c-component(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE', class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')

.c-component(class=cond1 ? 'cond1-TRUE' : 'cond1-FALSE')(class=cond2 ? 'cond2-TRUE' : 'cond2-FALSE')

HTML output

<div class="c-component cond1-TRUE cond2-FALSE"></div>

<div class="c-component cond1-TRUE cond2-FALSE"></div>

<div class="c-component cond1-TRUE cond2-FALSE"></div>

If we have a ternary option with the second operand empty, we could simplify the pug syntax:

- var cond1 = true
- var cond2 = false

.c-component(class=cond1 && 'cond1-TRUE' class=cond2 && 'cond2-TRUE')
//- or more explicit
.c-component(class=cond1 ? 'cond1-TRUE' : '' class=cond2 ? 'cond2-TRUE' : '')

HTML output

<div class="c-component cond1-TRUE"></div>

πŸ–₯ Codepen example.

πŸ“š More info

#16

Remove duplicates in object arrays

We have an object with multiple arrays inside it, and we need to remove duplicates between these arrays.

const obj = {
arr1: ['a', 'b', 'c'],
arr2: ['a', 'b', 'd', 'e', 'f'],
}
  • First of all, we have to get the two arrays and merge their items with the Array.prototype.concat() method. As the documentation says, we can use this function on arrays only, not on objects!

  • We use this trick: we call an empty array [] and then we apply the concat() method to it

const allItems = [].concat(obj.arr1, obj.arr2)

that will return

console.log(allItems)
// (8) ["a", "b", "c", "a", "b", "d", "e", "f"]

Remove duplicates from an array #

Method 1: filter() #

Let's filter our array with all items inside, just to be without duplicates.

Array.prototype.filter() method use this condition: "for every item I loop through, I will check if the current index (pos) is the same as the indexOf(item), and it this condition is true I will return the item.".

Array.prototype.indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present, so the filter condition is satisfied only for the first time the item pass the loop because loop index and indexOf are the same.

To be more clear let's make a table of the loop:

Item Loop index indexOf* Condition Saved into unique array
'a' 0 0 ok, 0 == 0 so this will return true yes
'b' 1 1 ok, 1 == 1 so this will return true yes
'c' 2 2 ok, 2 == 2 so this will return true yes
'a' 3 0 whoa, 3 != 0 so this will return false nope!
'b' 4 1 whoa, 4 != 1 so this will return false nope!
'd' 5 5 ok, 5 == 5 so this will return true yes
'e' 6 6 ok, 6 == 6 so this will return true yes
'f' 7 7 ok, 7 == 7 so this will return true yes

*indexOf = first position the item is present

const unique = allItems.filter((item, pos) => allItems.indexOf(item) === pos)
console.log(unique)
// (6) ["a", "b", "c", "d", "e", "f"]

Method 2: Set() #

Set() is an object lets you store unique values of any type.

  • create a new Set() to return the unique values
  • spread ... its items inside the Set object
  • wrap all in square brackets to return an array [object]
const unique = [...new Set(allItems)]

or we can use this syntax (I prefer this one! It seems more clear to me that we are manipulating something that will become an array using the Array.from() method)

  • create a new Set() to return the unique values
  • convert the Set object to an array using Array.from()
const unique = Array.from(new Set(allItems))

and the result is exactly the same as above using filter().

console.log(unique)
// (6) ["a", "b", "c", "d", "e", "f"]

To sum up #

/* ==========================================================================
OBJECT ARRAYS, REMOVE DUPLICATES
========================================================================== */


const obj = {
arr1: ['a', 'b', 'c' ],
arr2: ['a','b', 'd', 'e', 'f' ],
}

const allItems = [].concat(obj.arr1, obj.arr2)

// using filter()
const unique_filter = allItems.filter((item, pos) => allItems.indexOf(item) === pos)

// using Set()
const unique_set1 = [...new Set(allItems)]
const unique_set2 = Array.from(new Set(allItems))

console.log(allItems) // (8) ["a", "b", "c", "a", "b", "d", "e", "f"]

console.log(unique_filter) // (6) ["a", "b", "c", "d", "e", "f"]
console.log(unique_set1) // (6) ["a", "b", "c", "d", "e", "f"]
console.log(unique_set2) // (6) ["a", "b", "c", "d", "e", "f"]

πŸ“š More info

How to Remove Array Duplicates in ES6

js
#15

Add HTML classes to 11ty markdown content

11ty comes with some useful plugins for markdown manipulation, one of these is markdown-it-attrs.

This plugin should be used combined with its big brother, the markdown parser markdown-it, which is already added in 11ty basic installation.

markdown-it-attrs uses markdown-it and adds the possibility to add attributes to HTML nodes generated from markdown.

To use it, add this plugin to the .eleventy configuration file:

  • require markdown-it
const markdownIt = require('markdown-it')
  • require markdown-it-attrs
const markdownItAttrs = require('markdown-it-attrs')
  • define basic markdown-it configuration options
const markdownItOptions = {
html: true,
breaks: true,
linkify: true
}
  • set markdown-it-attrs as markdown-it usage options
const markdownLib = markdownIt(markdownItOptions).use(markdownItAttrs)
  • set as eleventy configuration the new markdown configuration
eleventyConfig.setLibrary('md', markdownLib)

To sum up:

// .eleventy.js

const markdownIt = require('markdown-it')
const markdownItAttrs = require('markdown-it-attrs')

const markdownItOptions = {
html: true,
breaks: true,
linkify: true
}

const markdownLib = markdownIt(markdownItOptions).use(markdownItAttrs)
eleventyConfig.setLibrary('md', markdownLib)

Example of usage #

# This is a title {.c-article-section__title}
This is a paragraph with data-state {data-state=important}

Another text with attributes {.c-article-section__disclaimer #articleId attr=value attr2="spaced value"}

![Alt text](image.jpg){.u-shadow}

[Link in a new tab](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a){target="_blank" rel="noopener"}

will output

<h1 class="c-article-section__title">This is a title</h1>
<p data-state=important>This is a paragraph with data-state</p>
<p class="c-article-section__disclaimer" id="articleId" attr=value attr2="spaced value">Another text with attributes</p>

<img class="u-shadow" src="image.jpg" alt="Alt text">
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a" target="_blank" rel="noopener">Link in a new tab</a>

🧨 !important

Note the last line where I added the target="_blank" attribute to the link to open it in a new browser tab. It's ok open a link in a new tab, but for security reasons it has to have also the rel="noopener" attribute.

Side note: unfortunately, I did not find a way to add attributes to markdown tables and blockquotes 😒

πŸ“š More info

#14

Start an npm project

To start an npm project, you have just to run npm init and npm will start asking you a lot of questions about how to call the project, who's the author and so on. If you are like me and have complete trust on your tools and its choices, just add -y at the end. It stands for yes and npm will complete the boring burocrazy for you. 😏

npm init -y
#13

Force include classes in critical CSS

Critical CSS build by Addy Osmani is a useful library that extracts and inlines critical-path CSS in HTML pages.

In the documentation page, there are a lot of configurations available but they are not the only ones! 😏

Critical CSS uses as its engine penthouse which has in turn a lot of configuration options. One of them, is forceInclude.

forceInclude: [...] description from docs πŸ“š:

Array of css selectors to keep in critical css, even if not appearing in critical viewport. Strings or regex (f.e. ['.keepMeEvenIfNotSeenInDom', /^.button/])

For instance, if we want to add a cta class injected via JS and not available in DOM nodes when the critical path is generated, we have to configure our critical CSS options like this:

critical.generate({
base: './',
src: 'template-homepage.html',
css: ['production/css/style-1.css' , 'production/css/style-2.css'],
....
penthouse: {
forceInclude: ['OPTIONAL-CLASS-HERE'],
},
})
#12

Nunjucks scoped variable declarations

We have to pay attention where we set Nunjucks variables because they are scoped

{% set animals = ['🐱', '🐢', '🐺'] %}

{% for item in animals %}
{% set animal = item %}
{% endfor %}

{{ animal }}
{# animal -> ERROR #}
{# animal declared INSIDE the loop is NOT available #}
{% set animals = ['🐱', '🐢', '🐺'] %}

{# note this declaration #}
{% set animal = '' %}

{% for item in animals %}
{% set animal = item %}
{% endfor %}

{{ animal }}
{# animal declared OUTSIDE the loop is available #}
{# animal -> 🐺 (last array item) #}

πŸ“š More info

Twig docs - set variables

#11

Nunjucks advanced loops

Nunjucks is a powerful template engine that allows to loop through arrays and also objects 😏

Loop though an array #

{% set animals = ['🐱', '🐢', '🐺'] %}

{% for item in animals %}
Value: {{ item }}
{% endfor %}
Value: 🐱
Value: 🐢
Value: 🐺

Loop though an object #

{% set animal = {
name: 'cat',
emoji: '🐱'
} %}


{% for key, value in animal %}
{{ key }}: {{ value }}
{% endfor %}

Note that we have to declare the two parameters of the loop key, value.

name: cat
emoji: 🐱

The iterable property #

In Twig exists an intresting property, iterable that checks if a variable can be iterable in a for loop:

Loop through an array:

{% set animals = ['🐱', '🐢', '🐺'] %}

{% if animals is iterable %}
{% for item in animals %}
Value: {{ item }}
{% endfor %}
{% else %}
Not iterable: {{ animal }}
{% endif %}
Value: 🐱
Value: 🐢
Value: 🐺

Loop through an object:

{% set animals = {
name: 'cat',
emoji: '🐱'
} %}


{% if animals is iterable %}
{% for item in animals %}
Value: {{ item }}
{% endfor %}
{% else %}
Not iterable: {{ animal }}
{% endif %}
Value: cat
Value: 🐱

🧨 !important

Please note that iterable is a Twig property and can have unexpected results in Nunjucks template engine.

In Twig a string is not iterable:

{% set animal = 'cat' %}

{% if animal is iterable %}
Iterable!
{% for item in animal %}
Value: {{ item }}
{% endfor %}
{% else %}
Not iterable!
{{ animal }}
{% endif %}

Twig output

Not iterable!
cat

but if we run the same code in Nunjucks, we discover that a string is iterable 🀯

Nunjucks output

Iterable!
Value: c
Value: a
Value: t

Accessing the parent loop #

Nunjucks provides in its loops the loop property.

From the docs the loop.index is

the current iteration of the loop (1 indexed)

But what if we have two nested loops and we want to access to the parent loop?

Workaround: save the loop index as row number! 😏

In this example we have a matrix content: two rows and each row has one ore more cells. If we want to print all cells content and position, we have to:

  • loop (parent loop) through the rows

  • loop (child loop) through the columns

  • get the content inside each cell

    {% set animals = [
    ['🐱', '🐢', '🐺'],
    ['🐍']
    ] %}


    <table>
    {% for row in animals %}
    {# new row #}
    <tr>
    {% set rowloop = loop %}
    {% for cell in row %}
    <td>
    row (rowloop.index):{{ rowloop.index }}
    column (loop.index): {{ loop.index }}
    cell: {{ cell }}
    </td>
    {% endfor %}
    </tr>
    {% endfor %}
    </table>

HTML output


<table>
{# new row #}
<tr>
<td>
row (rowloop.index):1
column (loop.index): 1
cell: 🐱
</td>
<td>
row (rowloop.index):1
column (loop.index): 2
cell: 🐢
</td>
<td>
row (rowloop.index):1
column (loop.index): 3
cell: 🐺
</td>
</tr>
{# new row #}
<tr>
<td>
row (rowloop.index):2
column (loop.index): 1
cell: 🐍
</td>
</tr>
</table>

πŸ“š More info

#10

Shell cheatsheet

Few commands I found very useful during development.

Command Description
man ls show manual for command 'ls'
wc <file> words count
rm <file> remove/delete file
rm -i <file> remove/delete file (interactive, ask confirm)
rmdir <directory> remove/delete directory
rm -R <directory> remove/delete directory and subdirectory
rm -iR <directory> remove/delete directory (interactive)
cp <current location> <destination> copy files
chmod -R 755 <folder> add writing permission to folder
pwd present working directory / print working directory
cd change directory
mkdir make directory
ls list files
ls -l list files (long form)
ls -lah list files (long form, all also hidden, human readable)
touch [filename] create file
chown change owner
cat <file> show file
<cmd> > <file> direct the output of "cmd" into "file"
grep -rl "<text>" <dir> search for all files containing <text> inside <dir>
ln symbolic link
alias show available alias on shell
cd - go to the previous current directory
ctrl + r advanced search (search any word in bash history)