@aligent/frontend-toolkit

4.1.0 • Public • Published

Frontend Toolkit

A gulp task runner and collection of ES6 RequireJS modules to be used with Magento 2 and Oro themes

Table of Contents

[TOC]

Installation

npm install @aligent/frontend-toolkit

The above example will download version 4

N.B. You may need to install missing npm dependencies as some version of npm don't recursively install dependencies of packages

Updates

Follow guide to update in npm - https://docs.npmjs.com/updating-your-published-package-version-number - $ npm version patch|minor|major

eg npm version patch will change version from 1.0.0 > 1.0.1 in the package.json and also git tag.

Gulp Task Runner

Setup

Copy the following files:

  • gulpfile.sample.js > gulpfile.js
  • toolkitConfig.sample.json > toolkitConfig.json
  • .eslintrc.json
  • babel.config.js
  • postcss.config.js
  • stylelint.config.js
  • kss-config.js

In your package.json add the following:

{
    "scripts": {
        "build": "NODE_ENV=production gulp",
        "lint": "gulp stylelintTask",
        "start": "gulp watchTask --mode=dev",
    },
    "browserslist": [">= 0.25% in AU"]
}

You can then modify your gulpfile to run the tasks you want by updating the default and watch tasks.

TODO

  • [ ] create install process to step through coping config files

Options

All options are defined using the nconf npm pages in the toolkitConfig.sample.json and inside tools/options.js;

The options are ordered by hierarchy from highest to lowest strength.

  1. Node Memory
  2. Node environment variables eg MODE=dev gulp
  3. Arguments eg gulp --mode=dev
  4. toolkitConfig.json in your project root
  5. Defaults

Modes

By default running gulp by itself will run all your tasks in production mode.

Use any of the option methods to change mode

Tasks

The toolkit gulpfile includes the following tasks:

  • SCSS - scss Compiles SCSS to CSS and creates a source map. Will output files to both the web and styleguide directories. CSS properties will automatically be given vendor prefixes based on the browserslist key.

  • Javascript - javascript Uses Babel to transpile latest JS to what is defined in the browserslist key, creates source maps

  • Webpack - jsx|ts|tsx This task is used to build the relevant react components

  • entry point names are used for included css modules too
  • splits common libs into a vendor.js.
  • Styleguide - styleguide This task is used to develop the styleguide using browsersync. Unless the project is just starting out, it's unlikely this task will be used

  • COPY - jpg|gif|png|svg|woff|woff2|ttf This task copies all other assets from source/ to web/

Add non-core tasks

You can add non-core tasks too - just make sure you install the task devDeps into your project.

gulpfile.js:

    const styleguide = require('@aligent/frontend-toolkit/tools/gulpTasks/styleguide');
    exports.styleTask = styleguide;

StoryBook Ready

Add following to your packages.json:

    {
        "scripts": {
            "storybook": "MODE=dev start-storybook --config-dir node_modules/@aligent/frontend-toolkit/tools/storybook -p 3000"
        }
    }

The config looks inside ./app/frontend/design for all files with *.stories.jsx?

Using the task runner in projects

There are some project specific changes that are required before the gulp task runner will work properly.

  • Add npm run script to package.json for project

    "kss": "kss --config kss-config.json"

  • Update .gitignore file in project, adding the following

app/design/frontend/[vendor]/[theme name]/web/css/*
app/design/frontend/[vendor]/[theme name]/web/js/*
styleguide/

ES6 RequireJS modules

Installation

Installing the JS classes into a project is as simple as adding an entry to the requirejs-config.js file.

Here is an example config file, with only the toolkit classes included:

var config = {
    map: {
        '*': {
            // Toolkit Classes
            carousel:          'js/Carousel/Carousel',
            debounce:          'js/Debounce/Debounce',
            restrictHeight:    'js/RestrictHeight/RestrictHeight',
            showHideElement:   'js/ShowHideElement/ShowHideElement',
            toggleTabElements: 'js/ToggleTabElements/ToggleTabElements'
            // Project files
        }
    }
};

Usage

Please refer to the documentation markdown file for each javascript module for usage instructions

SCSS Mixins and Normalize

Installation

To use the mixins and normalize file from the toolkit, you can simply import the toolkit.scss file in your main SCSS file

@import '@aligent/frontend-toolkit/scss/toolkit.scss';
// Rest of the project imports

Usage

Please refer to the documentation markdown file of each mixin for usage instructions

Linting

Stylelint

Stylelint will ensure that all SCSS written is down in a uniform and consistent way.

Installation

Create a new stylelint.config.js file with the following contents

{
  "extends": "@aligent/stylelint-config"
}

Add new script to package.json

"scripts": {
    "stylelint": "gulp stylelintTask"
}

Running the linter

npm run stylelint

Setting up automatic linting within PHPStorm

  • Enable File > Settings > Languages & Frameworks > Stylesheets > Stlyelint

  • Stylelint package use the absolute path to the locally installed version of Stylelint, e.g. {location_of_project_on_hdd}/node_modules/stylelint

ESLint

ESLint will ensure all the Javascript in the toolkit is written in a uniform, consistent way.

Installation

Add a new script to package.json

"scripts": {
  "eslint": "eslint {path/to/source}/js/**/*.js || echo \"Linting complete. Please see errors above\n\""
}

Running the linter

npm run eslint

Setting up automatic linting within PHPStorm

  • Enable File > Settings > Languages & Frameworks > Javascript > Code Quality Tools > ESLint
  • ESLint Package use the absolute path to the locally installed version of ESLint, e.g. [{location_of_respository_on_hdd}/frontend-toolkit]/node_modules/eslint
  • Configuration File {location_of_project_on_hdd}/node_modules/@aligent/frontend-toolkit/.eslintrc.json

N.B. As the .eslintrc.json file technically doesn't have a filename (it starts with a full stop), you'll likely need to enter the path directly, instead of using the file selector dialog

Webpack Component Override

The webpack configuration has the ability to override any module that webpack is importing. This is done by matching a "base path" and then replacing that base path with a path to a new module. This approach has been used extensively in our PWA projects.

Set Up

  1. Duplicate and rename componentOverrideMapping.sample.js to componentOverrideMapping.js.
  2. Place the componentOverrideMapping.js file in the root of a theme folder. I.e. right next to a theme.xml file
  3. Tell Webpack where to find the componentOverrideMapping.js file. See "Indicate to Webpack where to find componentOverrideMapping.js" section below
  4. Add some overrides to your componentOverrideMaping.js file, and test!

Indicate to Webpack where to find componentOverrideMapping.js

There are 2 ways to tell Webpack where to find the override mapping file:

  1. Inside toolkitConfig.json
  2. Inside gulpfile.js
toolkitConfig.js

This approach would be used when there is only 1 theme in the project. The path to the componentOverrideMapping.js doesn't need any dynamic folder names, therefore allowing it to be static.

To do this, open toolkitConfig.js, find the webpack object within paths, and add/update the componentOverrideMapping property, with the path to the file, relative to the toolkitConfig.json file.

Inside gulpfile.js

This approach would be used when a project has multiple themes. Setting the componentOverrideMapping path here allows us to use a dynamic variable to conditionally point to a different location for componentOverrideMapping.js, based on the theme that is being built.

To do this, copy and paste the following code snippet into the gulpfile.js file within the project. It should go after the @aligent/frontend-toolkit package has been required:

const toolkit = require('@aligent/frontend-toolkit');

// ...

const theme = process.argv
    .find((argv) => argv.includes('--theme'))
    .split('=')[1];

// ...

toolkit.options.set(
    'paths:webpack:componentOverrideMapping',
    `./app/design/frontend/${theme}/componentOverrideMapping.js`
);

N.b. This config is using nconf. The colons between each word is the way of namespacing a property

Next, we need to update the scripts in package.json to specify a theme to build.

"scripts" : {
  "build": "NODE_ENV=production gulp",
  "build:baseTheme": "npm run build -- --theme='[Vendor]/[BaseThemeName]'",
  "build:childTheme": "npm run build -- --theme='[Vendor]/[ChildThemeName]'"
}

To make deployments easier, also add a deploy script, which is essentially just an alias that calls the build scripts for each of the themes:

"scripts": {
  // ...
  "deploy": "npm run build:baseTheme && npm run build:childTheme"
}

Usage

There are 3 ways to enter override paths into the mapping file:

  1. Absolute, root paths
  2. Relative paths, with a leading ./
  3. Relative paths, with a folder/file name as the start

Absolute, root paths

Example: /app/design/frontend/Aligent/base/source/react/Checkout/App.jsx

This approach is most likely going to be used for the "module to override" path, i.e. the key in the mapping object. Using this approach allows us to reference a module that is outside the current theme directory

Relative paths, with a leading ./

Example: ./source/react/Checkout/TabsContainer.jsx

This approach is most likely going to be used for the "module to replace" path, e.g. the value in the mapping object. Using this approach allows us to easily reference a module that is local to the theme, without having to include the whole path to get to that theme. Essentially it just allows the mapping file to remain a little bit neater

Relative paths, with a folder/file name as the start

Example: source/react/Checkout/TabsContainer.jsx

This approach is identical to "Relative paths, with a leading ./", but just handles if someone forgets to put a specific leading character. Ideally there will always be either a / or a ./ at the start of the paths.

File Extensions in Paths

Almost all module imports in our Webpack built code will omit the extension. This is because Webpack has rules that allow this, it will automatically search for a file that matches .js/.jsx/.ts/.tsx.

The module override plugin is simpling using NodeJS's require function to resolve paths, which means those rules aren't available.

To get around the issue that will arise when trying to overwrite a path that is imported like, import Loading from './Loading', the module override plugin will use a "fallback" of require resolvers, going through the possible extensions. The order that it will try is the same as listed in the paragraph above: .js, .jsx, .ts then finally .tsx. If the plugin doesn't find a module that matches the file name plus one of those extensions, then it will abort overriding.

Settings Overrides (e.g. colours)

Unfortunately, it's not especially easy to get dynamic SCSS variable overwriting working. This means that for the settings overrides to work, they'll need to be defined as CSS Custom Properties. This is something that will need to be confirmed with the client. The fact that the client is keen on using React for parts of their site, they should be open to the change, which means IE11 support has to be dropped from a UI point of view.

In the index.js file of the React application, add the following 2 imports:

import './baseSettings';
import './themeSettings';

Create baseSettings.js and themeSettings.js next to the index.js file.

baseSettings.js

This file acts as an importer for the base settings of the application. The files being imported will be CSS/SCSS. Inside those CSS/SCSS files will likely just be CSS Custom Property definitions.

Example baseSettings.js

I suggest the comment at the top of this example also be copied into your baseSettings.js file.

// This file is intended to import "base settings" like colours/sizes etc.
// There is also a themeSettings file alongside this one, which isn't used in the base, but is used in the themes

import './scss/colours.scss';

themeSettings.js

This file is also an importer of settings, but it's only meant for use in the "theme" folders. It has to exist in the root folder too so that the application will build successfully when there is only 1 theme.

In the theme folders, use componentOverrideMapping.js, replacing the path to themeSettings.js to point to the one in the theme folder. This means that themeSettings.js needs to be created inside the theme too, right next to the componentOverrideMapping.js file that is doing the re-mapping.

Inside the themes' themeSettings.js, there will be imports to CSS/SCSS files that contain CSS Custom Property definitions that will override the CSS Custom Properties in the baseSettings.js imports

Example Project Structure

See an example of a Project Structure that implements all of the above component override patterns

Development

Please refer to CONTRIBUTING.md

Time Tracking

Use the code ALG-119 and project Aligent in Toggl when working on this repository. Ensure the name of this repo, as well as an adequate description of work being undertaken is also included

Examples

ALG-119: FrontendToolkit - Updating package.json dependencies

ALG-119: FrontendToolkit - Issue #2 Fixing eslint rules

The "Issue #2" part of the time entry above refers to Issues in this repository

Examples of what we don't want to see in toggle

ALG-119: FrontendToolkit

ALG-119

Stylelint

Updating the rules

Currently the rules are duplicated. One file, stylelint.config.js is used for linting the toolkit project, while linters/stylelint/index.js is used by projects that have included the toolkit as a dependency. Please be sure to update both files when making changes/updates to rules

Testing

In order to run tests, your installed version of NodeJS needs to support async/await. NodeJS supports this from version 7.6, however it would be best to just update to the latest LTS version

To run all tests, in a terminal, run:

npm test

If you would like to test an individual file, then run the following:

FILE=path/to/test.js npm run singletest

That will set up the HTTP server, run the test, and then stop the server again

Debugging Tests

If tests are failing for whatever reason, you can turn headless mode off, so that the Chrome instance will open, and you can watch exactly what is going on. In order to do that, open test/bootstrap.js and set headless to true.

You will also likely want to increase the value for slowMo, which sets the time between each action the browser takes, in ms

Dependencies (80)

Dev Dependencies (3)

Package Sidebar

Install

npm i @aligent/frontend-toolkit

Weekly Downloads

12

Version

4.1.0

License

OSL-3.0

Unpacked Size

279 kB

Total Files

135

Last publish

Collaborators

  • torbjorn.vanheeswijck
  • aligent-bot
  • aligent-danielvanderploeg
  • luke-denton-aligent
  • jarrod.swift