Intro to Contemporary Frontend Tooling

Christopher Bloom, @illepic

Phase2

slides.com/illepic/frontend

Prior deep dives, concepts, hands-on

Congratulations!

🎉🎉🎉

 

We're a JavaScript shop.

 

🎉🎉🎉

Node

A universal JavaScript runtime. Asynchronous and non-blocking, perfect for active servers or just local tooling. Node 10+ runs all our tooling.

 

Drop into a REPL at any time:

node
> 1 + 1
2
>

Run any JavaScript file:

node cats.js
"meow"

Node: Installation*

Manage it with nvm.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

Set a new default node:

nvm install v10 && nvm alias default v10

* Docker projects handle this

See all nodes installed:

nvm ls
       v10.15.0
       v10.15.1
->     v10.16.0

NPM

NPM manages node packages and project structure. It is one of the only globally installed node packages on your machine.

npm init

npm is used to start node projects and create package.json files:

npm installs node modules for projects:

npm install webpack --save-dev

NPM: Installation*

* Docker projects handle this

npm install -g npm@6

Yes, you use npm to update npm. Turtles.

A version of npm will be installed alongside node. Keep it updated.

PHP

For the time being, PHP is still part of Phase2 frontend tooling to compile php-twig. 

 

August 2019: Composer will no longer be required as of Particle 10.4 with our move to patternlab-node.

 

September-October 2019: We'll be experimenting with twig.js and StoryBook, entirely removing the need for PHP within our frontend tooling.

PHP: Installation*

Homebrew is our friend.

brew install php72

* Docker projects handle this

Particle

Config/Tools/Packages

Design System

Apps

npm, webpack, package.json scripts

Components & assets: twig, CSS, JS, icons

Something that eats components & assets:

Drupal, Pattern Lab, etc

Frontend Tech Grid

Syntax Runtime Lint Format Process Optimize Bundle Generate Test
JavaScript ES2016+
TypeScript
Node
Browser
eslint prettier Babel
BrowsersList
Terser Webpack Yeoman Jest
Styles SCSS
CSS (Tailwind)
Browser stylelint prettier SCSS
Tailwind
PostCSS
BrowsersList
PurgeCSS
CSSNano
Webpack Yeoman Backstop
Server markup Twig
 
PHP*
Node*
twiglint prettier Webpack Yeoman pa11y
Cypress
UI: jQuery ES2016+
TypeScript
Browser eslint prettier Babel Terser Webpack Yeoman Jest
Cypress
UI: Vue/React ES2016+
TypeScript
Vue/React
Browser eslint prettier Babel Terser Webpack Yeoman vue-testing-library
react-testing-library
Cypress

JavaScript Pipeline

ES2016+

Webpack

Eslint

Babel

Assets

Tests

CSS Pipeline

SCSS/Tailwind

Webpack

stylelint

PostCSS

Assets

Vue/React Pipeline

ES2016+

Webpack

Eslint

Stylelint

Babel

PostCSS

Assets

Tests

Twig Pipeline

Twig

Webpack

Assets

Sophistication in tooling

=/=

complication

Examples of complication:

  • 20,000 lines of JavaScript with no tests
  • CSS leakage breaking random layout
  • Undefined is not a function
  • Arbitrary code style across a codebase
  • Unoptimized production code
  • A single dangling comma breaking UI for 30% of users for over 2 weeks
  • Managing this abomination by hand:
  1. Every frontend is an application. 
  2. Let robots do the work for you.
  3. We can only code systems.

Phase2 Frontend Tools Philosophy

Dependency Trees

and

Asset Bundles

Two Approaches

Old

 

Run tasks against folders and dump output mixed within source files.

 

Gulp/Grunt

New

 

Entry point files that include assets that include assets, that include assets, that include assets etc. Bundles are generated from this chain of dependencies.

 

Webpack

import $ from 'jquery';
import 'bootstrap/js/dist/carousel';

// Module dependencies
import 'protons';

// Module template
import './_carousel.twig';

// Module styles
import './_carousel.scss';

export const name = 'carousel';

export const defaults = {
  interval: 3000,
};

export function enable($context, { carousel = {} }) {
  // Find carousel elements on the page
  const $carousel = $('.carousel', $context);
  // Bail if none exist
  if (!$carousel.length) {
    return;
  }
  // Override defaults with upstream settings
  const settings = { ...defaults, ...carousel };
  // Initialize the carousel with (potentially) overridden settings
  $carousel.carousel({
    interval: settings.interval,
  });
}

export default enable;

apps/drupal.js

source/design-system.js

atoms/...

molecules/...carousel.js

organisms/...

protons/index.js

bootstrap/carousel.js

jquery.js

molecules/carousel.scss

WebPack

Write frontend code that depends on other assets (js, html, css, images, etc) for eventual bundling in the browser. Webpack brings sanity to managing, splitting, loading, optimizing frontend assets.

2015!

WebPack

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};
  <!doctype html>
  <html>
   <head>
     <title>Getting Started</title>
   </head>
   <body>
     <script src="main.js"></script>
   </body>
  </html>
  import _ from 'lodash';
  
  function component() {
    const element = document.createElement('div');

    // Lodash used below!
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    return element;
  }

  document.body.appendChild(component());

webpack.config.js

src/index.js

dist/index.html

WebPack In Particle

./webpack.particle.js
./source/default/webpack.config.js
./apps/drupal/webpack.config.js

Overall, shared config 

Design System-specific config

("Where are 'atoms'?")

App-specifc overrides

("Where is the bundle output?")

("Does Drupal needs its own jQuery?")

WebPack: Loaders

Loaders allow transformation and processing of imported assets.

module.exports = {
  module: {
    rules: [
      { test: /\.ts$/, use: 'ts-loader' },
      {
        test: /\.scss$/,
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
          { loader: 'sass-loader'},
        ]
      }
    ]
  }
};

webpack.config.js

./src/index.ts

import './thingy.scss';

import { IStuff } from '../types';

const doStuff = (stuff: IStuff): string[] =>
  stuff.items.map(item => `Do this: ${item}`);

body {
  background: red;
}

./src/thingy.scss

Loaders In Particle

WebPack: Dev Server

Local host for static files. "Hot reloads" assets from Webpack without the need to write assets to disk. Shows and refreshes Pattern Lab HTML output while we work.

  • Config
    • ./apps/node-pl/webpack.config.js
  • Custom scripts
    • ./tools/webpack/*

Babel

Transforms any version of JavaScript into another version of JavaScript. Babel lets us use up to Stage 3 JavaScript proposals today while ensuring compiled JavaScript is supported by our target browsers.

  • Config
    • ./babel.config.env
  • Loader
    • ./webpack.particle.js

SCSS/Tailwind

Writing raw CSS sucks. Two flavors of styling:

SCSS

  • Variables, mixins, loops
  • Warning: Requires node binary
  • Loader
    • ./webpack.particle.js
    • ./source/default/webpack.config.js

 

Tailwind

  • Utility-first approach
  • Vars, mixins
  • No binaries, only JS
  • Config/Loader: see HRW

PostCSS

Transform CSS with JavaScript.

 

  • Adds browser-specific prefixes
  • Optimizes CSS
  • postcss.parts
  • Config:
    • ./webpack.particle.js

BrowsersList

Declare browsers you support, lets Babel and PostCSS figure out how your bundle must be transformed.

last 2 versions
not dead
> 1% in US
ie 11
./.browserslistrc

Ensure all CSS and JS are compiled for support by:

  • the last two versions of Chrome, Safari, and Firefox
  • as long as they're not dead
  • and they have >1% usage in the US*
  • and IE11 because we can't have nice things

Eslint

Enforce JavaScript code style rules.

 

  • Use predefined rulesets that can be overridden
  • We use AirBnB overridden by Prettier
  • Prevents webpack compile at configurable error level
  • Integration to all IDEs
  • Config:
    • ./.eslintrc.js
    • ./.eslintignore
  • Loader:
    • ./webpack.particle.js

Stylelint

Enforce SCSS/CSS code style rules.

  • No !important
  • No nesting > 3 levels
  • No styling on IDs
  • Colon and semicolon requirements
  • Config:
    • ./.stylelintrc
    • ./.stylelintignore
    • ./webpack.particle.js

Prettier

Transforms code to code style rules. Also guarantees code to fit within a column count limit.

 

If adhering to the opinionated rules in the prior two slides sounds like a lot of work, you're in luck: this reformats every file the project.

npm run fmt

To reformat on save within an IDE, see JetBrains and VSCode plugins.

Vue/React

UI interactive libraries of choice for Particle.

Q: "When should I reach for Vue/React instead of jQuery?"

A: When the pain of writing HTML in PHP to store application state in DOM outweighs the discomfort of adding a JS library.

 

  • Use the respective CLIs: vue-cli or create-react-app
  • Integrate:
    • Drupal libraries to import these bundles
    • Pattern Lab uses single webpack bundle

Jest

Simple JavaScript testing framework.

 

  • All yeoman-generated Particle components come w/an example Jest test
  • All JavaScript should be coded such that it is testable
  • Encourages moving logic to functions outside of DOM manipulation
  • Yes, even "Drupal" jQuery
npm run test

editorconfig

Share IDE settings across teams via a config file. 

 

  • Install editorconfig plugins for JetBrains/VSCode
  • File will be read automatically and IDE preferences set
  • Config:
    • ./.editorconfig
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

[composer.{json,lock}]
indent_size = 4

Yeoman

Automate the creation of new source files easily.

 

  • Generate new design system components with one command
  • Add your own Yeoman generators for repetitive creation
  • Guarantee proper process through generators
npm run new

package.json

The core of any node project. Holds all packages, scripts, and meta about a project.

  • dependencies: libraries used in the running app
  • devDependencies: libraries only used for testing/building/development
  • scripts:
    • All Particle commands are npm scripts
    • Run any named script by `npm run script-name`
    • node binaries (node_modules/.bin) are available to scripts:
scripts: {
 "lint": "eslint --ext .js ./"
}

Apps: Pattern Lab

Do frontend work according to Atomic Design principles in a fast and iterative way. Augmented by Webpack Dev Server for rapid reloads and recompiling.

 

  • Just a way to see HTML output from Twig easily
  • Encourages decoupling design system from app
  • Active community (node version)
  • Refer to the 908,234,298 prior presentations on this subjct

Apps: Drupal

A Drupal theme pre-wired to consume:

  • Static assets
  • Twig components files by @namespace
# Define Particle theme's library CSS and JS assets along
core:
  css:
    theme:
      ../../dist/app-drupal/assets/app.styles.css:
        preprocess: true
  js:
    ../../dist/app-drupal/assets/app.js:
      preprocess: true
  # see all in Drupal's `core/core.libraries.yml`
  dependencies:
    - particle/jquery
    - core/drupal
    - core/drupalSettings

# Create custom jQuery libraries
# that'll override the Drupal core jQuery libraries
# See `libraries-override` in particle.info.yml
jquery:
  js:
    ../../dist/app-drupal/assets/drupal-jquery.js:
      { minified: true, weight: -20 }

./apps/drupal/particle.libraries.yml

# ...
component-libraries:
  protons:
    paths: []
  atoms:
    paths:
      - ../../dist/app-drupal/assets/atomic/_patterns/01-atoms
  molecules:
    paths:
      - ../../dist/app-drupal/assets/atomic/_patterns/02-molecules
  organisms:
    paths:
      - ../../dist/app-drupal/assets/atomic/_patterns/03-organisms
  templates:
    paths: []
  pages:
    paths: []

./apps/drupal/particle.info.yml

Apps: Drupal

Integrate Twig to Drupal

Make a "presenter" file at a hook that uses Drupal as the "model" to provide data to pure twig components

{% set article_card = {
  card_border: 'none',
  card_title: label,
  card_text: content.body,
  button: {
    button_element: 'a',
    button_color: 'secondary',
    button_text: 'View details »'|t,
    button_link: url,
  }
} %}

{% Feel free to use Drupal attributes() here %}
{% include '@molecules/card/_card.twig' with article_card %}
./apps/drupal/templates/content/node--article--teaser.html.twig

That's a lot!

Made with Slides.com