@acanto/workflow

Scripts to manage themes in October CMS

Usage no npm install needed!

<script type="module">
  import acantoWorkflow from 'https://cdn.skypack.dev/@acanto/workflow';
</script>

README

Acanto Workflow

npm (scoped)

Frontend code resides by default in the frontend folder placed in the root of the project. Everything gets compiled and assembled in the standard October CMS folder /themes/acanto, (note that the active theme name should always be acanto) whose content is gitignored.

Installation

It is recomended to start a project by cloning the repository October starter with:

git clone git@git.acanto.net:acanto-framework/october-starter.git

Then go into the project root directory cd myproject, then:

npm i @acanto/workflow --save-dev

Setup project

After npm install you can either run acanto start for full development of assets and templates or the lighter acanto tpl to just automatically watch and copy the template files to the destination theme folder. This latter command is thought to be used either by backend developers or to quickly do some layout only changes. The command acanto tpl is limited to watch over template files (twig and php) and build them on change, allowing to develop a skeleton of the website without the whole compilation of the assets. This command is always run when launching the heavier acanto start. Pages can be added at any time running acanto route route-name or acanto route route-one,route-two to add multiple routes, existing routes will not be overwritten, but be careful of using a new unique route id.

Workflow commands

From your root folder run acanto help or npm run help to view the available commands and their description.

acanto-help

CI

When deploying add to the CI script:

# install dependencies
npm install

# build the production code ready for deployment
npm run deploy

Configuration

All gulp configuration is inside a standalone package @acanto/workflow published on npm, the project just uses its package.json file to configure and run the scripts. In this way a project have only one devDependency and the scripts can be updated separately from each project, so that every project can benefit from the updates.

package.json

You might want to add to your project's package.json the following standard shortcuts:

  "scripts": {
    "start": "acanto start",
    "build": "acanto build",
    "tpl": "acanto tpl",
    "route": "acanto route",
    "component": "acanto component",
    "core-sync": "acanto core-sync",
    "test": "acanto test",
    "deploy": "acanto deploy",
    "help": "acanto help"
  }

Each project can override some basic configurations of the scripts from the config section of it package.json. The following configuration is required and needs to change accordingly to your specific project:

  "config": {
    "favicons": {
      "background": "#fefefe"
    }
  }

.env

This file will be autogenerated if not found trying to guess the values (execpt for the db username and password)

APP_URL=http://project.test
APP_API_URL=http://project.test/api
APP_DB_HOST=project.acanto.net
APP_DB_NAME=project_db
# APP_DB_USER=myuser
# APP_DB_PWD=mypassword

Other configuration variables available are commented in the env-example file.

Some useful variables dependent on the environment are automatically made always available to all js and scss files.

Inside all SCSS files you have for instance:

$DEV: true; // or false
$ENV: "dev" | "stage" | "prod" | "preprod"; // as defined in the `APP_ENV` of the current .env file
$SRC_ASSETS: "../assets";
$URL_ASSETS: "https://myproject.com/themes/acanto/assets";
$SRC_FONTS: "../assets/fonts";
$URL_FONTS: "https://myproject.com/themes/acanto/assets/fonts";
$SRC_IMAGES: "../assets/images";
$URL_IMAGES: "https://myproject.com/themes/acanto/assets/images";
$SRC_SVGICONS: "../assets/svgicons";

Inside all JS files you have for instance:

const __DEV__ = true; // or false
const __ENV__ = "dev" | "stage" | "prod" | "preprod"; // as defined in the `APP_ENV` of the current .env file
const __URL__ = "https://myproject.com";
const __API__ = "https://myproject.com/api";
const __ASSETS__ = "https://myproject.com/themes/acanto/assets";
const __FONTS__ = "https://myproject.com/themes/acanto/assets/fonts";
const __IMAGES__ = "https://myproject.com/themes/acanto/assets/images";

All .js files of your project will get automatically IDE support for these variables (at least in VScode): js-constants-autocomplete

Folder structure

Each path used in the build scripts can be overridden in the specific project package.json file, but the ideal and default folder configuration assumed is the following:

  frontend/ # sources of all the frontend theme that gets compiled
    assets
      images/ # all images (will be compressed and generated webp format too)
      media/ # all video and audio files
      svgicons/ # svg images that will be turned automatically in an icon font
      fonts/ # custom fonts for the specific theme
      favicon.png # required image to automatically generate all favicons
    config/ # configuration files
      placeholders.scss # scss config (always accessible from any other file)
      mixins.scss # scss config (always accessible from any other file)
      variables.scss # scss config (always accessible from any other file)
      index.js # js config (globals, settings, ecc.)
    components/ # decoupled components to use across routes
      Mycomponent/ # component folder uses component name
        index.js # component script
        index.php # component php template part
        index.scss # component style
        index.twig # component twig template part
     core # folder to drop-in core elements from @acanto/frontend library, these are copied automatically to the /themes/acanto/partials folder allowing quick use with e.g. {% partial "element" %} or {% partial "custom-img" %}
      element.twig
    routes # route specific and inlined in the corresponding template
      home
        index.js # route specific and scoped scripts
        index.php # route specific php template part
        index.scss # route specific and scoped styles
        index.twig # route specific twig template part
    layouts # usually just one for project, but more layouts can e created and used independently within the same project with their entrypoint assets separately compiled
      main # usually the standard layout to use in all templates
        index.js # entry point with all common js/scss imports for this layout
        index.php # common php data for this layout
        index.scss # entry point with some simple scss for this layout
        index.twig # base template for this layout
    utils # utils generic components
      a-utility-partial.twig # will be copied to /partials/utils
      animations.scss # utils like styles
      animations.js # utils simple script
    vendor # custom vendor imports
      package-name.js # customised vendor import script
      package-name.scss # customised vendor import styles
  themes/
    acanto/
      theme.yaml # only file that remains on git, needed by October and used as a placeholder to singla the presence of the theme. Everything else here is gitignored
      assets/ # compiled static assets (scripts, styles, images, fonts, etc.) (.gitignored, DO NOT EDIT!)
      layouts/ # twig layout templates (.gitignored, DO NOT EDIT!)
      pages/ # twig page templates (.gitignored, DO NOT EDIT!)
        home.htm # page specific template
      partials/ # twig partials (.gitignored, DO NOT EDIT!)
        data.htm # all core elements/templates get copied in the partials root folder
        automated/ # compiled partials automatically generated by build scripts
        components/ # decoupled components to use across pages
          Mycomponent.htm # component template

Here in details:

frontend/assets

This folders contains all static assets that gets manipulated, optimized and copied over the October's default themes/acanto/assets folder, they are divided as such:

  • frontend/assets/fonts: all fonts files, they will get automatically optimized and content hashed. An .htaccess with long term expiration headers will be placed in this folder on build.
  • frontend/assets/images: all static theme images, they will get automatically optimized, content hashed and a webp additional format will be created for jpg and pngs (with same filename and different extension). An .htaccess with long term expiration headers will be placed in this folder on build.
  • frontend/assets/media: all static theme video, audio and other files, their filenames will not be hashed. No .htaccess will be placed here.
  • frontend/assets/svgicons: all svg icons that will be optimized and inlined in an automated partial placed in themes/acanto/partials/automated/svgicons.htm to be usually included in the layouts/main/index.twig. They will be later used in the templates by using the handy core partial icon, for example with {% partial "icon" glyph='arrow_left' %}
  • frontend/assets/favicon.png: required png image (1024x1024 is the optimal resolution) used to automatically generate all needed favicons. The script build an automated partial themes/acanto/partials/automated/favicons.htm to be usually included in the layouts/main/index.twig

frontend/components

This folder conain components that are quite specific pieces of UI which are usually reused within the same project and can, but not necessarily need, to be reused accross different projects. They should be responsible of specific functionalities and should be configurable from outside enought to allow their reuse in the same project. Usual use cases for components are pieces of UI like the Header, the Footer, Cards, Sliders, ecc.

frontend/config

The scss files here (functions.scss, mixins.scss, variables.scss, placeholders.scss) are always made automatically available to the whole project files. This is also the place where to put JavaScript global configurations such as breakpoints, various forms of data, URLs etc.

frontend/core

This folder contain core elements that are very generic and of common use case. They are usually included repeatedly in the same page and across the same website. Their flexibility should allow to use them even without alteration across different projects. They should be in fact responsible of basic and common elements and UI like forms, images, buttons, typography, ecc. The template files here must be of extension .twig (or .php if necessary but discouraged) and are usually copy-pasted-tweaked from the library @acanto/frontend with the command acanto core-sync.

frontend/routes

This folder contains Routes code that is always scoped and outputted only to its specific template. So by default JS and SCSS code written here cannot interfere with other routes (or pages).

frontend/utils

Here we should put templates mostly js/scss utilities to use and import where needed from either layouts, components or routes. This is also a good place for tiny utils templates.twig if needed, they will be watched and copied over to the partials directory and therefore become available in templates to use with {% partial "utils/my-util" %}

frontend/vendor

This is the folder where to put custom vendor files that cannot for various reasons be added and managed by npm. A common use case is the presence of the premium gsap package that cannot be installed from the public npm repository.

Github workflow

  • On branch master resides the stable development code
  • On branch stage resides the code deployed on the staging enviroment e.g. myproject.acanto.net
  • On branch production is the production code publicly available at e.g. myproject.com
  • Development happens initially on master and then, when more developers come in, specific work is done on specific branches branched from master. It is important to often pull from there with git pull origin master to avoid problematic merge conflicts.

Caveats

Windows

To run the script on windows you might need to go to System Properties and on Advanced -> Add to Environment variables -> Users -> Path append the following variables:

  • C:\Program Files\Git\cmd in order to be able to install npm packages directly from git remotes instead that from published versions git must be in the path source
  • C:\Program Files\nodejs to run node scripts from the current directory

Workflow features

  • Hot Module Replacement
  • JS/SCSS modules bundling with webpack
  • SCSS compilation with sass and postcss
  • SCSS optimization combining media queries with postcss-sort-media-queries
  • ES6 syntax support with babel
  • CSS and JS files automatic splitting and cache busting (content hashed), this allows to long term expire-headers in the .htaccess
  • SCSS and JS sourcemaps
  • Route and Layout specific JS/CSS through October's partials
  • Assets minification
  • Images minification and compression
  • SVG inline icons automatic generation from svg images
  • Favicons auto-generation from single image
  • License banner for compiled css and js files
  • Route and Component generator with .js, .scss, .twig and .php templates

Development of @acanto/workflow

Notes

About assets inlining, CSS is inlined if the file size is minor than 50kb, as the AMP specification suggest. JS inline threshold is set to just 2kb instead, see this article.

About image optimization consider October's CMS plugins offline-responsiveimages and webp-implementation

About optimization evaluate the use of compression-webpack-plugin

About gzip compression see October's not-including-it-by-default reasons here https://github.com/octobercms/october/pull/1896, https://github.com/octobercms/october/pull/1885, https://github.com/octobercms/library/pull/201/commits/db2dec55ed7d42201f92b59e1de74b5c3196841c, https://github.com/octobercms/october/pull/1885/commits/86ca0c7e8d79a4a8f0662203b0b04cf549b6fc63, https://github.com/octobercms/october/issues/1847, https://octobercms.com/forum/post/assets-gzip-compression

About polyfills babel and webpack see: https://github.com/babel/babel-loader#note-transform-runtime--custom-polyfills-eg-promise-library, https://github.com/zloirock/core-js#babel, https://babeljs.io/docs/en/next/babel-preset-env.html#how-does-it-work, https://www.npmjs.com/package/polyfill-library

Bash utilities

  • Convert all .htm files in folder to .php with find . -name "*.htm" -exec bash -c 'mv "$1" "${1%.htm}".php' - '{}' \;
  • Run start with more RAM node --max_old_space_size=8192 node_modules/@acanto/workflow/cli.js start (@see similar issue)

Todo

  • Maybe abstract CI deploy flow/steps into something like this
# deploy-AC-Usa-Ohio:
stage: deploy
only:
  - stage
script:
  - npm install --quiet
  - npm run deploy:stage
  - mkdir -p ~/.ssh
  - echo "$AC_USA_OHIO_SSH_PRIVATE_KEY" >> ~/.ssh/id_dsa
  - chmod 600 ~/.ssh/id_dsa
  - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  - npm run deploy:rsync ubuntu@acusa.acanto.net:/var/www/acusa/