carbon/pipeline
composer require carbon/pipeline
0.7.0
Carbon.Pipeline is a delicious blend of esbuild and PostCSS to form a full-featured, ultra-fast modern Javascript and CSS bundler for Flow Framework and Neos CMS.
Getting started
First, thank you that you want to give this build stack a try! If you miss a
Install via composer
Run composer require carbon/pipeline --dev
. Some files (if not already existing) will be copied to your root folder during the installation. After installing the package, run the command yarn install
to install the required packages, defined in package.json
. Feel free to modify and change dependencies before installing
Manual install
If you want to make some significant adjustments to the build stack, you can also download the code as zip file and put it in the folder Build/Carbon.Pipeline
. Go to Carbon.Pipeline/Installer/Distribution/Defaults
and copy the files to your root folder (Don't forget the hidden files, starting with a dot). After this is done, run the command yarn install
to install the required packages, defined in package.json
. Feel free to modify and change dependencies before installing
Add files to the build stack
The whole configuration, including which files to build, is configured in pipeline.yaml
. The default values are set in defaults.yaml
and merged with your configuration. Under the key packages
, you can either add an array with package settings or, if you have just one entry, you can directly add the configuration:
packages:
- package: Vendor.Bar
files:
- Main.pcss
- Main.js
# This is the same as
packages:
package: Vendor.Bar
files:
- Main.pcss
- Main.js
If you have just one file, you can pass this directly without creating an array:
packages:
package: Vendor.Bar
files: Main.js
If you don't set files, all parsable files from the input folder get rendered. Files that start with an underscore (_
) will be ignored.
packages:
package: Vendor.Bar
To change the input and/or the output folder, you can do this with the folder
option:
packages:
package: Vendor.Bar
folder:
input: Assets
output:
inline: Private/Templates
style: Public
script: Public
module: Public
commonJS: Public
Further, you can write the files to another package:
packages:
package: Vendor.Bar
folder:
output:
package: Vendor.Theme
If you want to go crazy with multi-sites in Neos, you can also write the files to multiple packages:
packages:
package: Vendor.Bar
folder:
output:
package:
- Vendor.Theme
- Vendor.Bar
A package entry has the following options:
Key | Type | Description | Example |
---|---|---|---|
package |
string |
The name of the package (required) | Vendor.Foo |
files |
string or array
|
The names of the entry files. If none given, all parsable files in the input folder get rendered | Main.js |
folder.input |
string |
The folder under Resources/Private where to look for the entry files |
Assets |
folder.output.package |
string or array
|
If set, the files will be writen in a different package (one or multiple) | Foo.Bar |
folder.output.inline |
string |
The folder where inline files get rendered | Private/Templates/ |
folder.output.style |
string |
The folder where inline styles rendered | Public/Assets |
folder.output.script |
string |
The folder where inline scripts rendered | Public/Assets |
folder.output.module |
string |
The folder where inline modules rendered | Public/Assets |
folder.output.commonJS |
string |
The folder where inline commonJS files get rendered | Public/Assets |
external |
string or array
|
You can mark a file or a package as external to exclude it from your build. | */Modules/* |
inline |
boolean |
Flag to toggle if the files should be inlined. If set, sourcemaps are disabled | true |
sourcemap |
boolean |
Flag to toggle source map generation | false |
format |
string |
Set the format of the output file. Read more | cjs |
These are the default values for the folders:
folder:
input: Fusion
output:
inline: Private/Templates/InlineAssets
style: Public/Styles
script: Public/Scripts
module: Public/Modules
commonJS: Public/CommonJS
and these for the build options:
external: null
inline: false
sourcemap: true
format: iife
The target folders can be adjusted under the key folder.output
. If you want to change the defaults for all your packages, you can also set this globally in your pipeline.yaml
:
folder:
input: Assets
output:
inline: Private/Templates
style: Public
script: Public
module: Public
commonJS: Public
buildDefaults:
sourcemap: false
format: esm
Please look at the defaults.yaml
file for all the options.
If you set an entry file with the javascript module suffix (.mjs
, .mjsx
, .mts
or .mtsx
) the format of this file will be enforced to esm
. The same with commonJS: If you set an entry file with the javascript commonJS suffix (.cjs
, .cjsx
, .cts
or .ctsx
) the format of this file will be enforced to cjs
. E.g., if you have the following array ["Main.js", "Module.mjs", "CommonJS.cjs"]
, and have no specific setting for the format, Main.js
will have the format iife
, Module.mjs
will have the format esm
and CommonJS.cjs
will have the format cjs
.
Yarn tasks
There are five predefined main tasks:
Command | Description | Command |
---|---|---|
yarn watch |
Start the file watcher | concurrently -r yarn:watch:* |
yarn dev |
Build the files once | concurrently -r yarn:dev:* |
yarn build |
Build the files once for production (with optimzed file size) | concurrently -r yarn:build:* |
yarn pipeline |
Run install, and build the files for production | yarn install --silent --non-interactive;concurrently -r yarn:pipeline:*;yarn build |
yarn showConfig |
Shows the merged configuration from pipeline.yaml and defaults.yaml
|
node Build/Carbon.Pipeline/showConfig.mjs |
The tasks are split up, so they can run in parallel mode. But you can also run them separately:
Command | Description | Command |
---|---|---|
yarn watch:js |
Start the file watcher for JavaScript files | node Build/Carbon.Pipeline/esbuild.mjs --watch |
yarn watch:css |
Start the file watcher for CSS files | node Build/Carbon.Pipeline/postcss.mjs --watch |
yarn dev:js |
Build the files once for JavaScript files | node Build/Carbon.Pipeline/esbuild.mjs |
yarn dev:css |
Build the files once for CSS files | node Build/Carbon.Pipeline/postcss.mjs |
yarn build:js |
Build the JavaScript files once for production | node Build/Carbon.Pipeline/esbuild.mjs --production |
yarn build:css |
Build the CSS files once for production | node Build/Carbon.Pipeline/postcss.mjs --production |
Extendibility
Of course, you can also add your own tasks in the scripts
section of your package.json
file. For example, if you have a Neos UI custom editor and want to start all your tasks in one place, you can add them like this:
"build:editor": "yarn --cwd DistributionPackages/Foo.Editor/Resources/Private/Editor/ build",
"watch:editor": "yarn --cwd DistributionPackages/Foo.Editor/Resources/Private/Editor/ watch",
"pipeline:editor": "yarn --cwd DistributionPackages/Foo.Editor/Resources/Private/Editor/ install --silent --non-interactive",
Because the tasks start with build:
, respectively with watch:
or pipeline:
, the tasks will be included in the corresponding root command. In this example, yarn build
, yarn watch
or yarn pipeline
.
Compression of files
In production mode (yarn build
), the files also get compressed with gzip and brotli. You can edit the compression level under the key buildDefaults.compression
. Per default, the highest compression level is set. To disable compression at all, you can set it to false
:
buildDefaults:
compression: false
Or, if you want to disable just one of them, you can set the entry to false
:
buildDefaults:
compression:
gzip: false
Import files from DistributionPackages and other Packages
By default, two aliases are predefined: DistributionPackages
and Packages
. Like that you can import (CSS and JS) files from other packages like that:
import "DistributionPackages/Vendor.Foo/Resources/Private/Fusion/Main";
import "Packages/Plugins/Jonnitto.PhotoSwipe/Resources/Private/Assets/PhotoSwipe";
@import "DistributionPackages/Vendor.Foo/Resources/Private/Fusion/Main.pcss";
@import "Packages/Carbon/Carbon.Image/Resources/Private/Assets/Tailwind.pcss";
Thanks to a custom made resolve
function, you can also use globbing in CSS imports: @import "Presentation/**/*.pcss";
CSS
Sass
If you want to use Sass (.scss
or .sass
files) you have to install sass
:
yarn add --dev sass
PostCSS
This template comes with a variety of PostCSS Plugins. Feel free to remove some or add your own favorites packages. The configuration is located in .postcssrc.js
. The suffix of these files should be .pcss
.
PostCSS Plugins
Following plugins are included:
Name | Description |
---|---|
postcss-import | Plugin to transform @import rules by inlining content. Thanks to a custom resolve function you can also use glob
|
Tailwind CSS | A utility-first CSS framework for rapidly building custom user interfaces |
postcss-nested | Unwrap nested rules like how Sass does it |
postcss-assets | Plugin to manage assets |
postcss-focus-visible | PostCSS Focus Visible lets you use the :focus-visible pseudo-class in CSS, following the Selectors Level 4 specification. |
postcss-clip-path-polyfill | Add SVG hack for clip-path property to make it work in Firefox. Currently supports only polygon()
|
postcss-sort-media-queries | Combine and sort CSS media queries |
autoprefixer | Parse CSS and add vendor prefixes to CSS rules using values from Can I Use |
cssnano | Modern CSS compression |
postcss-reporter |
console.log() the messages (warnings, etc.) registered by other PostCSS plugins |
Of course, you can add your own or remove not-needed Plugins as you want. This is just meant as a starting point.
Tailwind CSS
This setup comes with Tailwind CSS, a highly customizable, low-level CSS framework. An example configuration is provided in tailwind.config.js
. The setup for purge the CSS files is also configured. Read more about controlling the file size here. Because the CSS bundling is done with the Javascript API from PostCSS, the Just-in-Time Mode from Tailwind CSS works perfectly.
By the way: Alpine.js is excellent in combination with Tailwind CSS.
Javascript
TypeScript
If you want to use TypeScript, add the following packages to package.json
:
yarn add --dev typescript @typescript-eslint/eslint-plugin
Add your tsconfig.json
file; this is just an example:
{
"include": ["DistributionPackages/**/Private/*"],
"exclude": [
"node_modules/*",
"DistributionPackages/**/Public/*",
"DistributionPackages/**/Private/Templates/InlineAssets*",
"Packages"
],
"compilerOptions": {
"baseUrl": "./",
"paths": {
"Packages/*": ["Packages/*"],
"DistributionPackages/*": ["DistributionPackages/*"]
}
}
}
To enable the correct linting, edit .eslintrc
:
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
"eslint:recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"env": {
"es6": true,
"node": true
}
}
React
Using JSX syntax usually requires you to manually import the JSX library you are using. For example, if you are using React, by default, you will need to import React into each JSX file like this:
import * as React from "react";
render(<div />);
Preact
If you're using JSX with a library other than React (such as Preact,), you'll likely need to configure the JSX factory and JSX fragment settings since they default to React.createElement
and React.Fragment
respectively. Add this to your tsconfig.json
or jsconfig.json
:
{
"compilerOptions": {
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
Svelte
If you want to use Svelte, add the following packages to package.json
:
yarn add --dev svelte svelte-preprocess esbuild-svelte @tsconfig/svelte
Enable the plugin in your pipeline.yaml
file:
esbuild:
plugins:
svelte:
enable: true
# Add here your options
options:
compileOptions:
css: true
Your tsconfig.json
may look like this:
{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["DistributionPackages/**/Private/*"],
"exclude": [
"node_modules/*",
"__sapper__/*",
"DistributionPackages/**/Public/*",
"DistributionPackages/**/Private/Templates/InlineAssets*",
"Packages"
],
"compilerOptions": {
"baseUrl": "./",
"paths": {
"Packages/*": ["Packages/*"],
"DistributionPackages/*": ["DistributionPackages/*"]
}
}
}
Vue.js
If you want to use Vue.js, add the following packages to package.json
:
yarn add --dev vue vue-template-compiler esbuild-vue
Enable the plugin in your pipeline.yaml
file:
esbuild:
plugins:
vue:
enable: true
# You can pass your needed options here
# options:
Babel.js / IE 11 support
If you want to use Babel.js, add the following packages to package.json
:
yarn add --dev @babel/core esbuild-plugin-babel
as well additonals babel plugins and/or presets:
yarn add --dev @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
Further, you have to add a file called babel.config.json
, for example:
{
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
],
"plugins": ["@babel/proposal-class-properties", "@babel/proposal-object-rest-spread"]
}
Finally, enable the plugin in your pipeline.yaml
file:
esbuild:
plugins:
babel:
enable: true
# You can pass your needed options here
# options:
If you a poor person and have to support Internet Explorer, you have to edit your .browserslistrc
.
If a browser starting with ie
is found, the target es5
gets activated.
defaults
ie 11
not dead