Criando páginas single para cada categoria (Single templates) no WordPress – 2019

Esse post originalmente era de 2011, dei uma atualizada no conteúdo com alguns novos recursos e correções. Trabalhando com WordPress você tem um arquivo para cada situação específica, por exemplo, o arquivo responsável pela página de categoria é o arquivo category.php, ou page.php para uma página, ou 404.php quando o conteúdo não for encontrado.

Quando tratamos de post o arquivo mais específico é o single.php, por que o mais específico? Caso o single.php não exista o index.php será responsável por exibir o conteúdo da single ou o mais novo arquivo de template singular.php ele foi introduzido na versão 4.3 do WordPress para exibir o conteúdo de um post ou uma página. A imagem abaixo representa a ordem de carregamento:

Mas além dessas opções também podemos criar uma arquivo para a single de cada categoria, mas isso só é possível com trabalhando com três métodos diferentes: trabalhando com filtros , trabalhando com plugin ou trabalhando com condicional tags.

Carregando diferentes templates com add_filter

A primeira forma é adicionando um filtro para isso devemos adicionar-lo dentro do functions.php, o código ficará da seguinte forma:

add_filter('single_template', 'create_single_template');

function create_single_template( $template ) {
	global $post;

	$categories = get_the_category();
	// caso não tenhamos categoria retornamos o template default.
	if ( ! $categories )
			return $template; 

	//resgatando o post type
	$post_type = get_post_type( $post->ID );

	$templates = array();

	foreach ( $categories as $category ) {
			// adicionando templates por id e slug
			$templates[] = "single-{$post_type}-{$category->slug}.php";
			$templates[] = "single-{$post_type}-{$category->term_id}.php";
	}

	// adicionando os templates padrões
	$templates[] = "single-{$post_type}.php";
	$templates[] = 'single.php';
	$templates[] = 'singular.php';
	$templates[] = 'index.php';

	return locate_template( $templates );
}

Explicando código acima, aplicamos um filtro quando o WordPress chama o template para exibir um post, esse código é maior que a versão anterior desse post(2011), por duas razões continuamos retornando uma comportamento padrão e segundo realizamos um tratamento para não sobrescrever a single de um post type, exemplo, caso tenhamos uma categoria filme e um post type filme não termos conflito pois a single da categoria ‘filmes’ será single-post-filmes.php e a single do post type ‘filmes’ será single-filmes.php.

Carregando diferentes templates com Plugin

Alterar functions.php de forma errada pode afetar o funcionamento do todo o seu tema, para evitar problemas caso não domine muito programação a segunda forma é utilizando plugin, ainda sim você precisará de noções de programação para criar o novo template a diferença que qualquer erro afetará somente a página single que você estará criando.

Temos várias opções de plugins os passos serão bem parecidos, vou listar o plugin com atualizações mais recentes o Custom Post Template By Templatic:

  1. Baixe o plugin e instale o plugin em sua aplicação.
  2. Ative o plugin
  3. Crie um novo template para sua página single com um comentário no início do seu arquivo:
<?php
/*
PostType Page Template: [Nome para descrever seu template]
Description: [Essa parte é opcional]
*/
?>

Esse comportamento é bem parecido como template pages, nesse caso o nome do arquivo é irrelevante pode ser qualquer um. Feito isso se tudo ocorrer bem um dropdown irá aparecer na sua página de edição de posts para selecionar o template desejado.

A terceira forma é trabalhando com tags condicionais, elas são funções que funcionam como perguntas lógicas que retornam true ou false, para nossa solução iremos trabalhar com “in_category()”, para o nosso código não ficar extenso podemos dentro de nosso loop utilizar get_template_part como no exemplo abaixo:

// dentro do loop
if ( in_category( 'filmes' ) ) {
	get_template_part( 'template-parts/content', 'filmes' );
} else {
	get_template_part( 'template-parts/content', 'post' );
}

O código acima inserimos dentro de nosso single.php não criamos um template novo para toda nossa single e sim um pequeno bloco que será incluído pela função get_template_part, caso o post seja da categoria “filmes” ele ira buscar dentro da pasta template-parts o arquivo “content-filmes.php” nele você irá realizar a customização que você deseja, caso contrário carregamos o arquivo content-post.php um trecho genérico para outras categorias.

Mais infos sobre conditional tags : http://codex.wordpress.org/Conditional_Tags

Até o próximo post pessoal!

5 maneiras de utilizar o Spread operator

Spread operator parameter foi introduzido no EcmaScript 2015, para um simples recurso no primeiro momento mas utilizados da maneira correta pode se tornar bastante útil, o se você ainda não conhece o spread operator é identificado por “três pontos(…)” com ele podemos desmontar arrays e objetos. Nesse post iremos ver 5 maneiras de utilizar o spread operator.

1. Combinando arrays

Primeiro exemplo podemos ver como podemos combinar dois arrays ou mais, exemplo:

let semana = ['segunda', 'terça', 'quarta', 'quinta', 'sexta']; 
let semanaCompleta = ['domingo', ... semana, 'sábado']; 

console.log(semanaCompleta)

// output:  ['domingo', 'segunda', 'terça' 'quarta', 'quinta', 'sexta', 'sábado'];

2. Copiando array

Um dos problemas de copiar um array para outra variável, caso passamos a referencia diretamente, não estamos fazendo uma cópia do array e sim uma referência ao primeiro array, como no código abaixo:

let lista1 = [1,2,3]; 
let lista2 = arr;

lista2.push(4);
console.log(lista1);
// output : [1, 2, 3, 4]

Se testarmos o código acima temos como saída do console.log do “lista1” a alteração feita no “lista2”, isso acontece por que temos uma referência para lista1 e não uma copia do array. Para resolver este problema podemos utilizar o spread operator como uma alternativa para copiar a lista como no exemplo abaixo:

let lista1 = [1,2,3]; 
let lista2 = [...arr];

lista2.push(4);
console.log(lista1);
// output : [1, 2, 3]

3. Convertendo um nodeList em um array

Outra utilidade de utilizar o spread operator é converter items que parecem um array mas não são. Por exemplo, quando trabalhamos com querySelectorAll temos como retorno uma lista de items mas essa lista não é do tipo array, o retorno é um elemento nodeList outra função que retorna uma lista é getElementsByClassName mas o retorno dessa função é um HTMLCollection, e o qual é o impacto desses dois tipos?

NodeList and HTMLCollection apenas suportam a função forEach, caso queiramos fazer interações como map, filter, reduce ou sort. Funções referentes ao tipo Array teremos um error como retorno, como no exemplo abaixo.

let buttons = document.querySelectorAll('button');

let newList = buttons.map((button) => (button.innerText = button.innerText + ' Copy!'));

No exemplo acima, levamos em conta que nosso HTML tem uma série de elementos button e queremos realizar uma cópia desta lista de elementos, mas nosso código irá disparar um erro pois, map não é função dentro de um nodeList, para isso precisamos converte-lo em um array para isso utilizamos o spread operator, como no exemplo a seguir:

let buttons = [...document.querySelectorAll('button')];

let newList = buttons.map((button) => (button.innerText = button.innerText + ' Copy!'));

Agora sim, conseguimos executar o nosso código e criar uma cópia dos items selecionados, essa ação poderia ser uma ordenação ou um filtro para alterar elementos específicos. Mas é uma boa base para entender a diferença esses dois items claro que nodeList e HTMLCollection tem suas especificidades, eles estão contidos na browser API enquanto Arrays fazem parte da JavaScript API. Caso queria saber mais sobre esses dois items deixe um comentário.

4. Criando novos objetos

Assim como arrays caso queiramos copiar objetos teremos problemas com referência mas nesse caso podemos ir um pouco além, podemos criar novos objetos a partir de um objeto, por exemplo, em um ecommerce temos um objeto endereço, o usuário pode ter múltiplos endereços, quando ele requisita um entrega um novo objeto é criado com endereço que ele selecionou e o seu nome. Como no exemplo abaixo:

const usuario = { nome: 'fellyph cintra', telefone: '0000-0000' };
const endereco = { cidade : 'caruaru', estado: 'pernambuco' };

const entrega = {
  responsavel: usuario.nome,
  ...endereco
};

console.log(entrega);
//output: {responsavel: 'fellyph cintra', cidade: 'caruaru', estado: 'pernambuco'}

No exemplo acima utilizamos a apenas a propriedade nome do usuário, imagine que o objeto entrega precisa de todos os dados do usuário, nesse caso precisaremos combinar os dois objetos então o código ficará da seguinte forma:

const usuario = { nome: 'fellyph cintra', telefone: '00000-0000' };
const endereco = { cidade : 'caruaru', estado: 'pernambuco' };

const entrega = {
  ...usuario,
  ...endereco
};

console.log(entrega);
//output: {responsavel: 'fellyph cintra', telefone: '00000-0000', cidade: 'caruaru', estado: 'pernambuco'}

5. Excluindo propriedades

Voltando para o exemplo anterior imagine que o objeto usuário contém o email do usuário essa informação não será necessária para o objeto entrega imagine, podemos utilizar o spread operator para remover propriedades também, como no código a seguir:

const usuario = { nome: 'fellyph cintra', telefone: '00000-0000', email: 'pindolinha@gmail.com' };
const endereco = { cidade : 'caruaru', estado: 'pernambuco' };

const semEmail = ({ email, ...rest }) => rest;

const entrega = {
  ...semEmail(usuario),
  ...endereco
};

console.log(entrega);
//output: {responsavel: 'fellyph cintra', telefone: '00000-0000', cidade: 'caruaru', estado: 'pernambuco'}

No código acima, criarmos uma função chamada semEmail, nela separamos o email do resto do objeto e passamos como retorno o objeto sem a propriedade email.

Existem outros usos para o spread operator, como propriedades default ou propriedades condicionais, esses são os 5 usos mais básicos para spread operator. Caso tenha um uso particular que você gosta comente abaixo!

Até o próximo post!

Webpack mantendo a qualidade do seu JavaScript

Até o momento vimos dois posts de introdução ao Webpack, nesse post iremos implementar linters em nosso código JavaScript, se você nunca ouviu falar em linting tools, elas são ferramentas de optimização de código, elas nos ajudam a escrever o código organizado e com boas práticas. Mas isso não significa que iremos prevenir bugs, mas a diferença que teremos um código fácil de debugar em em grandes times teremos uma estilo único de desenvolvimento.

Junto ao linters responsáveis pela validação básica podemos integra-los com guias de desenvolvimento, eles utilizam um conjunto padrões de desenvolvimento para realizar uma validação mais restrita, com isso tempos uma melhor manutenção do nosso código. No mercado front-end temos guias específicos para JavaScript e CSS, muitos guias de CSS estão atrelados a guias visuais, mas também podemos encontrar guias para automatização de validação CSS com Webpack e os principais Guias de estilo do mercado são:

Guias JavaScript

Guias CSS/LESS/SASS +HTML

  • Fractal – é uma biblioteca de components com style guide tempos alguns plugins de integração com webpack.
  • Airbnb CSS / SASS Style guide – contém regras de declaração, seletores, formatação, comentários, entre outras regras específicas para SASS
  • Indiomatic CSS Principles – contém regras gerais, whitespace, comentários, formatação e uma série de exemplos práticos.
  • Stylelint Config Standard – essa configuração extends da configuração recomendada e aplica um regra de uma série de guias

Por que utilizar Guia de padrão de desenvolvimento ?

Já abordarmos algumas vantagens mas vale a pena revisar:

  • Ajuda na prevenção de erros
  • Formatação automática em alguns casos
  • Padronização de desenvolvimento
  • Facilidade de manutenção de código
  • Fácil configuração, uma vez definido o estilo você poderá compartilhar a mesma configuração com o seu time
  • Incentivo a boas práticas de desenvolvimento

Ok, Já entendi agora como eu utilizo esses guias com Webpack?

Esse tutorial vamos continuar o trabalho dentro do repositório utilizado nos posts anteriores e implementar a validação do nosso código JavaScript, agora vamos criar uma nova branch chamada step-10 para realizarmos os primeiros passos. Inicialmente vamos instalar utilizando nosso terminal o eslint e eslint-loader em nosso projeto, eles serão os pacotes responsáveis pela validação do nosso código EcmaScript:

npm install eslint-loader eslint --save-dev

Após a instalação dos pacotes precisamos configurar o nosso eslint, podemos criar o nosso arquivo de configuração “.eslintrc” manualmente ou utilizar o terminal para instalação, para facilitar o nosso processo vamos utilizar o terminal, para isso executamos o seguinte comando em nosso terminal:

eslint --init

Quando executamos o comando anterior nosso terminal irá realizar uma série de perguntas que serão elas:

  • Como você gostaria de configurar ESLint?
    R. Usando um style guide popular
  • Qual style guide você gostaria de seguir
    R. Airbnb
  • Você usa React?
    R. No
  • Qual o formato que você quer o seu config file?
    R. JavaScript
  • Você gostaria de instalar as dependências necessárias?
    R. Y(sim)

Feito isso, o arquivo .eslintrc será criado com o seguinte código:

module.exports = {
    "extends": "google"
};

Agora que nos temos o eslint instalado e configurado vamos, inclui-lo em nosso webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          'eslint-loader',
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ],
  },
};

Este é o arquivo de configuração utilizado nos posts anteriores, incluímos o eslint-loader em nossas regras de leitura para arquivos JavaScript ele agora irá trabalhar em conjunto com o babel loader.

Rodando o webpack novamente se tudo ocorrer bem teremos o seguinte retorno:

Nosso lint irá começar a validar o nosso código

Lendo os nossos errors e warnings um ponto de atenção para o document. O eslint não reconhece document porque não especificamos qual ambiente estamos desenvolvendo, para isso vamos fazer uma alteração em nosso arquivo .eslintrc:

module.exports = {
    "extends": "airbnb-base",
    "env" : {
        "browser": true,
    }
};

Com essa nova propriedade “env” definimos browser como true, agora o eslint passará considerar que nosso será escrito para browsers. Podemos também especificar que nosso código irá rodar em servidores node.

Rodando nosso código novamente:

Ainda temos alguns erros e warnings podemos resolver de duas maneiras manualmente ou como um autofix, para realizar a correção automática você pode realizar via terminal ou através de configurações no webpack.config.js adicionando opções ao nosso eslint-loader da seguinte forma:

{
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true,
            },
          },
        ],
      },
Depois de rodar webpack com autofix temos os seguintes alertas relacionados ao nosso console.log

Podemos remover o console.log do nosso código essa é uma ação recomendada, imagina você enviar um código em produção cheio de console log, no Webpack podemos definir qual será o modo que iremos trabalhar “development” ou “production” partindo desse princípio podemos. Iremos criar uma condicional em nosso webpack.config quando executarmos o nosso código para produção iremos disparar alertas para remover os console.log de nosso código.

Para essa etapa iremos trabalhar na branch step-11, primeira mudança vamos incluir dois scripts em nosso package.json:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Webpack tutorial",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1",
    "dev" : "webpack --watch --mode=development",
    "build" : "webpack --mode=production"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme",
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "babel-loader": "^8.0.5",
    "css-loader": "^2.1.0",
    "eslint": "^5.15.1",
    "eslint-config-airbnb-base": "^13.1.0",
    "eslint-config-google": "^0.12.0",
    "eslint-loader": "^2.1.2",
    "eslint-plugin-import": "^2.16.0",
    "file-loader": "^3.0.1",
    "node-sass": "^4.11.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1"
  }
}

Agora temos dois novos scripts dev e build, eles incluem o modo que iremos exportar modo de produção irá criar um código minificado sem source map já o código gerado para desenvolvimento terá recursos que irão facilitar o debug de nosso código.

Quando quisermos exportar a versão de desenvolvimento executamos em nosso terminal o comando:

npm run dev

Ele irá executar o comando “webpack –watch –mode=development” assim executaremos o webpack no modo desenvolvimento ainda inclui mais um parâmetro “–watch” para o webpack ficar assistindo todas as mudanças dentro do projeto assim só precisamos rodar o nosso código uma vez. Para exportar nosso script de produção rodamos o comando:

npm run build

Agora com esses dois novos recursos vamos fazer as seguinte alteração em nosso webpack.config:

const path = require('path');

module.exports = (env, options) => ({
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true,
              rules: {
                'no-console': (options.mode === 'development') ? 'off' : 'warn',
              },
            },
          },
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ],
  },
});

Temos várias maneiras de verificar se estamos criando código para produção ou desenvolvimento, no caso acima converti module.exports de um objeto para uma função para resgatar o mode dentro das opções definidas em nosso código executado no terminal.

Dentro das opções eslint-loader adicionamos uma condicional para saber se vamos desligar o alerta para o console.log, este é um exemplo para uma regra especifica mas podemos colocar qualquer regra dentro dessa condicional todas as regras você pode conferir nesse link

Introdução Webpack Parte 2: Loaders

No post anterior vimos uma introdução ao Webpack, criamos uma situação básica carregando múltiplos arquivos JavaScript e vimos que Webpack possui 4 elementos chaves: Entry(arquivo de entrada), output(arquivo de saída), loaders e plugins. Nesse tutorial vamos abordar como trabalhamos como loaders, Webpack em seu core nada mais é que JavaScript carregando JavaScript. Nada mais básico que em uma aplicação web termos uma série de assets com diferentes extensões, jpg, css, js, svg… e como o Webpack se trata com essa situações, a resposta é: Loaders

Loaders

Loaders dão Webpack a possibilidade de processar diferentes tipos de arquivos, define como o Webpack irá ler e exportar os assets do seu projeto. Escritos em Node.JS os loaders precisam regras escritas em expressão regular para definir quais extensões serão carregadas pelo tal.

Temos vários loaders e podemos separa-los nas seguintes categorias:

  • Arquivos
  • JSON
  • Transpiling
  • Templating
  • Styling
  • Linting
  • Frameworks

Para nosso tutorial vamos utilizar os seguintes recursos, babel para fazer o transpiling do nosso ECMAScript2015+ e JSX e Sass para fazer o pre-processamento do nosso estilo. Com esse panorama vamos precisar dos seguintes loaders:

  • Arquivos: url-loader, file-loader
  • Transpiling: babel-loader
  • Styling: style-loader, css-loader, sass-loader

Com essa lista vamos seguir um passo-a-passo e falar sobre cada um desses loaders durante a evolução de nosso projeto.

babel-loader

Hoje desenvolvimento com JavaScript moderno é um requisito quase que obrigatório em projetos web, em nossos exemplos anteriores fizemos o uso de EcmaScript 5, versão suportada por todos os browsers do mercado incluindo internet Explorer, mas se a gente quiser fazer o uso de EcmaScript2015+ e não deixar de fora os usuários de browsers mais antigos, reposta será “transpiling”, ele funciona como uma compilação de código, onde temos EcmaScript 2015+ sendo convertido para EcmaScript 5, no mercado temos algumas ferramentas que realizam esse processo atualmente a ferramenta mais usada no mercado é babel.

Webpack é umas da tecnologias compatíveis com babel, assim podemos fazer o processo de transpiling utilizando babel para criar o nosso bundle. Para isso vamos primeiramente instalar babel-loader em nosso projeto, dentro do nosso terminal, na pasta do seu projeto executamos o seguinte comando:

npm install -D babel-loader @babel/core @babel/preset-env

Nesse código temos a instalação do babel-loader, babel core e preset-env, presets são coleções de plugins para o babel suportar uma certa versão, ainda temos presets para cada versão do EcmaScript, react e typescript. Mas a preset-env tem o foco no target environment, ou seja, no ambiente final. Ele suporta as versões mas recentes do ecmaScript, mas o código gerado conseguimos determinar qual será o nível de suporte. Após a instalação dos pacotes nosso package.json ficará da seguinte forma:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Webpack tutorial",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme",
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "babel-loader": "^8.0.5",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1"
  },
  "dependencies": {
    "npm": "^6.7.0"
  }
}

Depois de instalado os pacotes necessários, vamos incluir o loader em nosso webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};

No código acima adicionamos um novo atributo chamado module, ele será responsável por definir as regras de como os nosso módulos serão carregados, passamos um array de regras(rules) contendo os seguintes parâmetros:

  • test: contém uma expressão regular para achar os arquivos que iremos aplicar a regra, na sua maioria utilizamos a extensão que buscamos.
  • exclude: também uma expressão regular, contendo os arquivos ou pastas que iremos ignorar.
  • use: qual o loader iremos utilizar para esse caso.

Por exemplo, em nosso código definimos que todos arquivos que terminam com a extensão .js iremos usar o loader ‘babel-loader‘, dentro do nosso código de entrada adicionamos EcmaScript2015 se conferimos o código gerado temos, por exemplo, a keyword ‘const’ convertida para ‘var’. O exemplo completo você pode conferir na branch step-6.

style-loader e css-loader

Continuando a evolução de nosso webpack.config.js vamos carregar o nosso estilo, para essa tarefa vamos instalar dois loaders css-loader e style-loader, css-loader vai ser responsável por carregar o css junto com webpack e style-loader vai pegar o conteúdo carregado pelo webpack e exportá-lo em uma tag style. Agora em nosso terminal iremos rodar o seguinte código:

npm i -D style-loader css-loader

Completa a instalação vamos adicionar o loader em nosso webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        exclude: /(node_modules|bower_components)/,
        use: ['style-loader', 'css-loader'],
      }
    ]
  }
};

Após adicionarmos a configuração para carregar nosso css o padrão será o mesmo, regex para achar os arquivos css, quais o loaders iremos utilizar com a propriedade use, adicionei um css simples chamado main.css:

:root {
  --c-text:#28262C;
  --c-component-background: #F9F5FF;
  --c-primary: #14248A;
  --c-secondary: #D4C2FC;
  --c-ternary: #998FC7;
  --e-padding: 1em;
}

.navigation,
.main,
.header,
.footer {
  padding: var(--e-padding);
}

.navigation {
  background: var(--c-primary);
}

.main {
  background: var(--c-component-background);
}

.header {
  background: var(--c-component-background);
}

.footer {
  background: var(--c-ternary);
}

Em nosso index.js vamos carregar o css, isso mesmo, faremos o import dentro do nosso arquivo de entrada:

import './css/main.css';

import './js/content';
import './js/header';
import './js/footer';

Quando executarmos o comando webpack em nossa linha de comando teremos a seguinte saída, o arquivo gerado o main.js e os arquivos que serviram de entrada para nosso bundle. O código para esse loader você irá encontrar na branch step-7

Vamos adicionar uma image ao nosso projeto via css, a imagem abaixo será a imagem que iremos trabalhar voce pode salva-la e adicionar na pasta assets/imgs/

Com a image no local definido vamos fazer a seguinte alteracao em nosso CSS:

:root {
  --c-text:#28262C;
  --c-component-background: #F9F5FF;
  --c-primary: #14248A;
  --c-secondary: #D4C2FC;
  --c-ternary: #998FC7;
  --e-padding: 1em;
  --header-img: url(../../assets/imgs/webpack-logo.png);
}

.navigation,
.main,
.header,
.footer {
  padding: var(--e-padding);
}

.navigation {
  background: var(--c-primary);
}

.main {
  background: var(--c-component-background);
}

.header {
  background: var(--header-img);
  background-repeat: no-repeat;
}

.footer {
  background: var(--c-ternary);
}

Vamos adicionar a nossa imagem como background do nosso header e tentar compilar o nosso código:

Temos um erro de compilacao do nosso codigo

Isso acontece porque não temos o loader apropriado para a extensão .png, para esse caso podemos trabalhar com loaders:

  • file-loader: irá carregar o arquivo e criar uma copia do arquivo em nossa pasta de output(dist)
  • url-loader: irá carregar o arquivo especificado e irá criar uma copia no formato base64.

Para corrigir o nosso projeto iremos instalar dos dois loaders:

npm i -D file-loader url-loader

Com os loaders instalados vamos combinar os dois loaders em uma solução, arquivos pequenos convertemos em base64 e arquivos pesado criamos uma copia em nossa pasta dist, como podemos ver em nosso webpack.config.js (step-8):

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        exclude: /(node_modules|bower_components)/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ]
  }
};

Agora temos loaders para nossos arquivos .js .css e images, caso queiramos trabalhar com pre-processadores, por exemplo, SASS ou LESS, parra o nosso tutorial iremos trabalhar com SASS mas o processo é similar para o LESS. Primeiro em nosso terminal iremos instalar os pacotes necessários:

npm install sass-loader node-sass --save-dev

Com tudo pronto vamos alterar a regra de leitura no nosso css para também aceitar arquivos SASS.

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ]
  }
};

A principal diferença em nosso webpack.config.js é regex que anteriomente procurava arquivos com a extensão .css agora com a regra ‘(sa|sc|c)ss’ procura arquivos .sass .scss ou .css e por fim adicionamento o sass-loader, se trocarmos a extensão do nosso arquivo .css por .scss ele irá carregar normalmente. Exemplo:

_variables.scss

$c-text:#28262C;
$c-component-background: #F9F5FF;
$c-primary: #14248A;
$c-secondary: #D4C2FC;
$c-ternary: #998FC7;
$e-padding: 1em;
$header-img: url(../../assets/imgs/webpack-logo.png);

main.scss

@import 'variables';

.navigation,
.main,
.header,
.footer {
  padding: $e-padding;
}

.navigation {
  background: $c-primary;
}

.main {
  background: $c-component-background;
}

.header {
  background: $header-img;
  background-repeat: no-repeat;
}

.footer {
  background: $c-ternary;
}

O código completo desse tutorial você irá encontrar na branch step-9 E até o próximo tutorial.

Work smart with Gutenberg

Hi folks, last weekend I’ve done my first talk in 2019, at WordCamp Prague it was a pleasure to be again in Prague, one of my favourite city in Europe and my presentation it was about Gutenberg, I have created a small project with a few examples using static blocks, editable and dynamic blocks, follow the slides:

The repository with the examples from my presentation you can find here: https://github.com/fellyph/digital-agency-block-kit

Introdução a Webpack

Webpack é um static module bundler ou numa tradução genérica um “empacotador de módulos”, mas o que isso significa? Webpack vai gerenciar a leitura de arquivos de seu projeto empacotando esse conteúdo da forma que você desejar. Ele irá funcionar de forma recursiva, um arquivo funciona como ponto de partida, a partir desse ponto inicial ele irá varrer todos os arquivos que estão conectados em sua aplicação e para cada caso teremos um tratamento específico.

Qual a vantagem disso tudo? Ainda é comum aplicações web carregando dezenas JavaScript com diferentes script tags, se você não fizer isso da maneira correta irá causa uma série de problemas de performance em sua aplicação. E o gerenciamento de módulos ataca alguns problemas de performance, por padrão Webpack trabalha apenas com o carregamento de Javascript, mas podemos instalar recursos necessários para trabalhar com CSS, imagens, arquivos SASS, LESS e TypeScript. Esses “recursos” são chamados de loaders que veremos em breve.

Atualmente na versão 4 Webpack esté entre as principais ferramentas de configuração de ambiente, anteriormente ocupadas pelo Gulp e o Grunt. O que impulsionou sua popularidade foi a adoção por grandes CLI’s do mercado, com angular-cli e create-react-app conseguimos abstrair toda a complexidade de realizar a configuração inicial de projetos com Angular ou React e Webpack é um grande responsável por toda essa mágica por trás dos CLIs.

Vale salientar que funcionamento do Webpack é um pouco diferente do Gulp e Grunt, eles são gerenciadores de tarefas enquanto o Webpack é um gerenciador de módulos, o seu foco é como os arquivos serão carregados separados por extensão, mas claro que podemos trabalhar uma série de recursos que o gulp/grunt realizam, por exemplo, toda parte de optimização, minificação, tratamento de arquivos e concatenação.

O processo de leitura com Webpack é dividido em 4 conceitos chaves:

  • Entry: qual será o nosso arquivo de entrada, o nosso ponto inicial
  • Output: nosso arquivo final gerado
  • Loaders: para cada tipo de arquivo o Webpack possui um loader devemos gerenciar este ponto no nosso arquivo de configuração
  • Plugins: Webpack disponibiliza uma série de plugins, para diferentes tipos de situações

Nas versões anteriores nos iniciávamos um projeto com o arquivo webpack.config.js nele especificamos como o Webpack ira se comportar. Na versão nova do Webpack podemos iniciar um projeto sem o arquivo de configuração, mas voce quiser realizar optimizações extra com o o webpack.config.js

Preparativos

Para esse tutorial vamos criar um projeto simples com dois arquivos, a estrutura será a seguinte, cada passo desse tutorial irei criar uma branch para voce acompanha a evolução dos arquivos, por exemplo, step-1… step-2… step-3… O nosso ponto de partida será o seguinte:

Temos um aquivo index.html e dentro da pasta SRC temos uma index.js

Antes de iniciar a instalação do Webpack, voce irá precisar de uma versão atualizada do nodeJS/NPM, podemos verificar a versão do nosso NPM com o comando em nosso terminal:

npm -v

Na raiz no nosso projeto vamos inicializar o nosso pacote NPM(step-2), nele vamos salvar todas as dependências para o nosso projeto, assim em nosso terminal executamos o comando:

npm init

Na imagem acima temos o final do passo-a-passo realizar pelo NPM init, ele ira verificar se ja temos um arquivo package.json, caso não, ele irar nos ajudar a criar um, fazendo perguntas sobre os campos necessários e ao final ele gerar uma arquivo package.json:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Tutorial about webpack",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme"
}

Com o nosso package.json configurado agora vamos instalar o webpack e webpack-cli em nosso projeto(step-3), voltando para o terminal vamos executar os seguintes comandos:

npm install webpack webpack-cli --save-dev

Se tudo correr bem teremos a confirmação em nosso terminal:

Caso a versão do seu node não suporte a última versão do Webpack, voce receberá um alerta para atualizar o seu nodeJS and NPM. Agora vamos criar a determinada situação para fazer uma comparação após usarmos Webpack. Inicialmente vamos adicionar três novos scripts(content.js, header.js e footer.js) ao nosso projeto e carrega-los em nossa index.html(isso voce poderá acompanhar na branch step 4).

Simulando uma conexão lenta o resultado será o seguinte. Temos 4 requisições durando um total de 4.29 segundos.

Uma ação de melhorar a performance de um site é reduzir o numero de requisições, em browsers modernos e conexões HTTP2 isso não tao visível. Mas quando não temos esse recursos temos uma limitação de requisições em paralelo isso causa um efeito cascata aumentando o tempo de carregamento. Uma solução para isso será enviar um único bundle contendo todos os arquivos, agora vamos verificar como fazer isso com webpack primeiro vamos definir o nosso webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  }
};

No arquivo acima tempos temos instruções qual é o arquivo de entrada neste caso o index.js e qual arquivo será gerado dentro da pasta ‘dist’ o arquivo main.js. E nosso index.js importa os três arquivos, content.js, header.js e footer.js

var myContent = require('./js/content');
var myHeader = require('./js/header');
var myFooter = require('./js/footer');

O Webpack irá ler o arquivo index.js e varrer todos os arquivos relacionados e criar um arquivo único simulando uma conexão lenta o resultado será o seguinte:

Agora temos duas requisições 2 requisições em 4.24 segundos

Até o momento não fizemos nenhuma otimização esse post serviu como uma introdução, nos próximos posts podemos evoluir o nosso projeto e aumentar sua complexidade. O código final você encontra aqui: https://github.com/fellyph/webpack-tutorial/tree/step-5

Internacionalização com blocos Gutenberg

Se você está acessando o blog pela primeira vez esse post faz parte de uma série de posts relacionados na criação de blocos para o novo editor do WordPress Gutenberg. Caso queira conferir os posts anteriores:

Internacionalização dentro da construção de temas é o fator fundamental, nele damos a possibilidade do tema se adaptar a diversos idiomas. Essa regra também se aplica aos nossos blocos, caso queiramos criar um produto que atenda diversos idiomas, precisaremos permitir que toda saída de texto do nosso bloco se a adapte ao idioma do WordPress.

Antes do Gutenberg a internacionalização de texto era feita no back-end através da função wp_localize_script() . Agora com o Gutenberg é possível fazer a internacionalização no front-end, para isso vamos trabalhar como uma biblioteca javascript chamada wp-i18n. Se você já trabalhou com a internacionalização de temas o processo será o mesmo, precisamos especificar uma domínio para nossas String, nada mais é que um id que é adicionado no cabeçalho do nosso tema ou plugin. Após definido nosso domínio (text domain) fazemos a chama de texto da seguinte forma:

__( 'Hello, dear user!', 'my-text-domain' );

Essa função que envolve o texto será responsável por conferir se o tema ou plugin possui tradução para essa string, que será armazenado em um arquivo .pot. Ele pode ser gerado automaticamente por algumas ferramentas. No caso de blocos Gutenberg podemos utilizar o WordPress i18n babel plugin para gerar nosso arquivo .pot.

Para nosso tutorial vamos utilizar os blocos criados nos exercícios anteriores e o resultado será o seguinte:

/**
 * 1 - Criando primeiro bloco Gutenberg com ES5: parte 04
 * 	-	 internacionalizacao de blocos
 */
( function( blocks, element, editor, i18n ) {
	var el = element.createElement,
			RichText = editor.RichText;

	//registrando nosso bloco
	blocks.registerBlockType( 'fellyph/tutorial-03', {
		title: i18n.__('Bloco com cadastro de atributo', 'fellyph'),
		icon: 'smiley',
		category: 'layout',
		keywords: [
			i18n.__('Tutorial', 'fellyph'),
			i18n.__('Cadastro', 'fellyph'),
			i18n.__('Dinâmico', 'fellyph')
		],
		
		//registrando o tipo de bloco que iremos trabalhar
		attributes: {
			content: {
				type: 'string',
				source: 'html',
				selector: 'p',
			}
		},
		
		// função responsável por exibir nosso bloco no editor
		edit: function(props) {
			var content = props.attributes.content;

			// função responsável por acompanhar as mudanças do nosso atributo
			function onChangeContent( newContent ) {
				props.setAttributes( { content: newContent } );
			}
			
			// agora retornamos um elemento RichText ao invés de um parâgrafo
			return el(
				RichText,
				{
					tagName: 'p',
					className: props.className,
					onChange: onChangeContent,
					placeholder: i18n.__('Insira texto aqui', 'fellyph'),
					value: content,
				}
			);
		},

		save: function(props) {
			return el(
				RichText.Content , {
					tagName: 'p',
					className: props.className,
					value: props.attributes.content,
				}
			);
		},
	});
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.editor,
	window.wp.i18n
) );

Na primeira linha adicionamos uma nova dependência o script i18n, dentro do nosso script trocamos todas as chamadas de strings que serão exibidas para o usuário, no front-end e no editor de nossa aplicação pela função i18n.__() , nela vamos passar o texto original e o text domain para tradução como parâmetros.

Para aumentar a quantidade de texto eu adicionei uma nova propriedade em nosso bloco keywords, ela ajudar na busca de nossos componentes, no caso adicionamos 3 keywords em um array: Tutorial, Cadastro e Dinâmico com isso quando realizamos uma consulta as keywords podem ser um critério para achar o nosso bloco como podemos ver na imagem abaixo:

A keyword ajuda a achar o nosso bloco

O resultado do nosso bloco será praticamente o mesmo, com uma diferença que o nosso texto agora está localizado:

Podemos ver nos detalhes do nosso bloco que estamos carregando o título que cadastramos e no bloco podemos ver o nosso placeholder.

O último passo será criar o nosso arquivo de tradução o .poc, temos duas opções, a primeira seria gerando com software especifico o poedit, caso você queira evitar tooling com Babel e Webpack:

A seguida opção com o pacote nodeJS @wordpress/babel-plugin-makepot, para isso precisamos figuração de nosso projeto com babel e Webpack isso vai demandar um post específico dai entramos na criação de blocos com JSX.

O código completo você irá encontrar em meu github: https://github.com/fellyph/Gutenberg-tutorials/tree/tutoriais/gutenberg-04

Até o próximo post!