@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

    Install

    npm i @aligent/frontend-toolkit

    DownloadsWeekly Downloads

    52

    Version

    4.1.0

    License

    OSL-3.0

    Unpacked Size

    279 kB

    Total Files

    135

    Last publish

    Collaborators

    • aligent-bot
    • aligent-danielvanderploeg
    • luke-denton-aligent
    • jarrod.swift
    • john.smith.aligent