@aligent/frontend-toolkit
v4.1.0
Published
A set of tools/classes to help with Magento 2 and Oro theme development
Downloads
146
Readme
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.
- Node Memory
- Node environment variables eg
MODE=dev gulp
- Arguments eg
gulp --mode=dev
- toolkitConfig.json in your project root
- 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 mapsWebpack - 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
- Duplicate and rename
componentOverrideMapping.sample.js
tocomponentOverrideMapping.js
. - Place the
componentOverrideMapping.js
file in the root of a theme folder. I.e. right next to atheme.xml
file - Tell Webpack where to find the
componentOverrideMapping.js
file. See "Indicate to Webpack where to findcomponentOverrideMapping.js
" section below - 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:
- Inside
toolkitConfig.json
- 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:
- Absolute, root paths
- Relative paths, with a leading
./
- 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