carbon / pipeline
Ultra-fast build stack for Neos CMS based on esbuild and PostCSS
Fund package maintenance!
jonnitto
www.paypal.me/Jonnitto/20eur
Installs: 16 807
Dependents: 1
Suggesters: 0
Security: 0
Stars: 16
Watchers: 4
Forks: 6
Open Issues: 1
Language:JavaScript
Type:neos-build
- dev-main
- 0.16.0
- 0.15.6
- 0.15.5
- 0.15.4
- 0.15.3
- 0.15.2
- 0.15.1
- 0.15.0
- 0.14.0
- 0.13.3
- 0.13.2
- 0.13.1
- 0.13.0
- 0.12.3
- 0.12.2
- 0.12.1
- 0.12.0
- 0.11.5
- 0.11.4
- 0.11.3
- 0.11.2
- 0.11.1
- 0.11.0
- 0.10.15
- 0.10.14
- 0.10.13
- 0.10.12
- 0.10.11
- 0.10.10
- 0.10.9
- 0.10.8
- 0.10.7
- 0.10.6
- 0.10.5
- 0.10.4
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.6
- 0.9.5
- 0.9.4
- 0.9.3
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8.10
- 0.8.9
- 0.8.8
- 0.8.7
- 0.8.6
- 0.8.5
- 0.8.4
- 0.8.3
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.8
- 0.7.7
- 0.7.6
- 0.7.5
- 0.7.4
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.0
- 0.1.0
This package is auto-updated.
Last update: 2024-12-20 16:51:47 UTC
README
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 ✨ feature or found a 🐛 bug, feel free to open an issue.
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 install
to install the required packages defined in package.json
. Feel free to modify and change dependencies before installing 👍
Standalone use in custom projects without Neos
Carbon.Pipeline is also a perfect choice for your non-Neos projects. Consider installing the composer package neos/composer-plugin
beforehand to get Carbon.Pipeline installed in the correct directory under Build/Carbon.Pipeline
.
Manual install
If you want to make significant adjustments to the build stack, you can also download the code as zip file and put it in the Build/Carbon.Pipeline
folder. Go to Carbon.Pipeline/RootFiles/Global
and Carbon.Pipeline/RootFiles/JavaScript
or Carbon.Pipeline/RootFiles/TypeScript
, copy the files to your root folder (Don't forget the hidden files, starting with a dot). After this is done, run the command install
to install the required packages defined in package.json
. Feel free to modify and change dependencies before installing 👍
Choose a package manager
You can choose between different package managers: npm, Yarn and the ultra-fast and disk-space saving pnpm. You can set your favorite package manager by running the command pnpm setPackageManager
, npm run setPackageManager npm
or yarn setPackageManager yarn
. The script behind it don't need any dependencies, so you can run it before the install
command. The default package manager is pnpm
Add files to the build stack
Carbon.Pipeline assumes the following project directory structure for resources:
A configured location under
Resources/Private
: input filesResources/
: output files
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:
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 jsFileExtension: script: .js module: .mjs commonJS: .cjs
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
.
Tasks
As you can choose your favorite package manager, you have to prepend the task name with the corresponding name (pnpm
, yarn
or npm run
)
There are five predefined main tasks:
The tasks are split up, so they can run in parallel mode. But you can also run them separately:
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": "pnpm --dir DistributionPackages/Foo.Editor/Resources/Private/Editor/ build", "watch:editor": "pnpm --dir DistributionPackages/Foo.Editor/Resources/Private/Editor/ watch", "pipeline:editor": "pnpm --dir DistributionPackages/Foo.Editor/Resources/Private/Editor/ install", }
Be aware that you may have different syntax for settings options based on the chosen task manager To set the current work directory, for example you have to set
--cwd
foryarn
,--dir
or-C
forpnpm
and--prefix
fornpm
.
Because the tasks start with build:
, respectively with watch:
or pipeline:
, the tasks will be included in the corresponding root command. In this example, build
, watch
or pipeline
. If you want to go crazy, you can even mix different task managers.
Compression of files
In production mode (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";
Alter the configuration file
In some setups, you may need multiple configurations with different config files. In this edge case, you can set a other config file in your scripts
section in your package.json
file:
{ "build:custom:css": "node Build/Carbon.Pipeline/postcss.mjs --configFile=pipelineCustom.yaml" }
In this example, pipelineCustom.yaml
gets used instead of pipeline.yaml
.
CSS
Sass
If you want to use Sass (.scss
or .sass
files) you have to install sass
and node-sass-tilde-importer
:
For pnpm:
pnpm add -D sass node-sass-tilde-importer
For Yarn:
yarn add --dev sass node-sass-tilde-importer
For npm:
npm add -D sass node-sass-tilde-importer
You have to ways to import files from node_modules
(Example with bootstrap):
@import "node_modules/bootstrap/scss/bootstrap"; @import "~bootstrap/scss/bootstrap";
Pass options to the sass compiler
You can pass options to the sass compiler with sassOptions
.
Example:
To silence warnings from stylesheets loaded through importers and load paths, you can enable quietDeps
:
sassOptions: quietDeps: true
PostCSS
This template comes with a variety of PostCSS Plugins. Feel free to remove some or add your own favorite packages. The configuration is located in .postcssrc.mjs
. The suffix of these files should be .pcss
.
Pass custom options to you PostCSS config file
You can pass custom options to your PostCSS config file with key postcssOptions
. In this example, you would access the key prefix
with ctx.prefix
in your PostCSS config file (.postcssrc.mjs
).
postcssOptions: prefix: true
Use postcss resolve() function
You can use resolve()
in your css/scss files to load resources (eg images) from Resources/Public
of the package. The path will be resolved at compile-time.
.my-class { background-image: resolve('Images/my-image.jpg') }
resolves to
.my-class { background-image: url('/_Resources/Static/Packages/Your.Package/Images/my-image.jpg') }
If you choose to order your Packages in DistributionPackages in subfolders, you can add this setting to ensure the paths are correctly rewritten:
postcssOptions: additionalPackagePathPrefixes: - Sites - Plugins
This ensures that the path that is generated (eg /_Resources/Static/Packages/(Sites|Plugins)/Your.Package/.../
) will be correctly resolved, removing the subfolder from the path.
PostCSS Plugins
Following plugins are included:
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.mjs
. The setup for get the content for the CSS files is also configured. Read more about controlling the file size here. To remove a specific package, you could use this pattern in your pipeline.yaml
:
buildDefaults: content: RemovePacakge: "!DistributionPackages/Package.ToRemove"
By default, following entries are pre-defined:
buildDefaults: content: DistributionPackages: DistributionPackages/**/(Private|NodeTypes)/**/*.{fusion,html,js,jsx,ts,tsx,mjs,mjsx,mts,mtsx,cjs,cjsx,cts,ctsx,svelte,vue} ignoreNodeModules: '!DistributionPackages/**/Private/**/node_modules'
The script put automatically all entries starting with an !
at the end of the list. You can control this setting by calling pnpm showConfig --path=buildDefaults.content
By the way: Alpine.js is excellent in combination with Tailwind CSS.
Javascript
Flow Settings in Javascript
Suppose you use tools like Flownative.Sentry, you perhaps want to pass some of the settings to your Javascript without setting a data
attribute somewhere in the markup. For that, you can enable esbuild.defineFlowSettings
. If set to true
, all settings are passed. It is recommended to put it to a path (e.g. Flownative.Sentry
). This path is added as --path
attribute to the flow configuration:show
command. If you run the command build
, which automatically has the flag --production
, the FLOW_CONTEXT
is set to Production
.
esbuild: defineFlowSettings: Flownative.Sentry
In Javascript, you can access the variables like this:
Sentry.init({ dsn: FLOW.Flownative.Sentry.dsn, release: FLOW.Flownative.Sentry.release, environment: FLOW.Flownative.Sentry.environment, integrations: [new Integrations.BrowserTracing()], });
Make sure your eslint.config.mjs
has the global FLOW
enabled:
import globals from "globals"; import pluginJs from "@eslint/js"; import prettierRecommended from "eslint-plugin-prettier/recommended"; export default [ pluginJs.configs.recommended, prettierRecommended, { ignores: ["Build/", "Packages/", "**/Public/", "**/Resources/Private/Templates/", "*.noLinter.*"], languageOptions: { globals: { ...globals.browser, ...globals.node, FLOW: "readonly", }, }, }, ];
Pass options to esbuild
You can pass options to the esbuild API with esbuild.options
.
Example:
To remove some functions from the production build, you can use the esbuild.options.pure
setting. If you have just
one function, you can pass a string; otherwise, you have to set it to an array:
esbuild: options: pure: - console.log - console.pure
TypeScript
If you want to use TypeScript, just choose the option TypeScript on the composer installation
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
:
For pnpm:
pnpm add -D svelte svelte-preprocess esbuild-svelte @tsconfig/svelte
For Yarn:
yarn add --dev svelte svelte-preprocess esbuild-svelte @tsconfig/svelte
For npm:
npm add -D svelte svelte-preprocess esbuild-svelte @tsconfig/svelte
Enable the plugin in your pipeline.yaml
file:
esbuild: plugins: svelte: enable: true # Name of the esbuild plugin for svelte # plugin: esbuild-svelte # Name of the preprocess plugin # preprocess: svelte-preprocess # Add here your options options: compilerOptions: # external or injected css: external
You can also configure the esbuild plugin and preprocess package, which should be used. Just add a key
plugin
orpreprocess
and the plugin name.
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
:
For pnpm:
pnpm add -D vue esbuild-plugin-vue3
For Yarn:
yarn add --dev vue esbuild-plugin-vue3
For npm:
npm add -D vue esbuild-plugin-vue3
Enable the plugin in your pipeline.yaml
file:
esbuild: plugins: vue: enable: true # Name of the esbuild plugin for Vue # plugin: esbuild-plugin-vue3 # You can pass your needed options here # options:
You can also configure the esbuild plugin, which should be used. Just add a key
plugin
and add the plugin name.
Babel.js / IE 11 support
If you want to use Babel.js, add the following packages to package.json
:
For pnpm:
pnpm add -D @babel/core esbuild-plugin-babel
For Yarn:
yarn add --dev @babel/core esbuild-plugin-babel
For npm:
npm add -D @babel/core esbuild-plugin-babel
as well as additonals babel plugins and/or presets like @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:
As the ENV
variable is set to development
or production
if you run the tasks, you can have different setups (For example remove console
commands with babel-plugin-transform-remove-console
on production
):
{ "env": { "development": { "presets": [ [ "@babel/preset-env", { "modules": false } ] ], "plugins": ["@babel/proposal-class-properties", "@babel/proposal-object-rest-spread"] }, "production": { "presets": [ [ "@babel/preset-env", { "modules": false } ] ], "plugins": ["@babel/proposal-class-properties", "@babel/proposal-object-rest-spread", "transform-remove-console"] } } }
If you are a poor person and have to support Internet Explorer, you must edit your .browserslistrc
.
If a browser starting with ie
is found, the target es5
gets activated.
defaults
ie 11
not dead
Additional esbuild plugins
You can also add additional esbuild plugins, for example esbuild-envfile-plugin
:
esbuild: additionalPlugins: esbuild-envfile-plugin: functionName: setup options: null
As the plugin returns not the function directly (like others), you also have to pass the function's name.
If a plugin returns the function directly, you don't have to set this. If you want to enable such a plugin without any options, you can just pass name-of-the-plugin: true
Live-Reloading
If you want to use live reloading, you can do this with Browsersync.
To install it run pnpm add --global browser-sync
, yarn global add browser-sync
, or npm install -g browser-sync
.
Then you have to create an initial config with browser-sync init
.
After that, you need to adjust the created bs-config.js
file.
You can adjust every parameter, but the two parameter you need to set is files
and proxy
:
module.exports = { files: ["DistributionPackages/**/Public/**/*.css", "DistributionPackages/**/Public/**/*.js"], proxy: "http://your.local.domain", };
If you want to also reload the page if a fusion
or a template file gets changed, you can do so:
module.exports = { files: [ "DistributionPackages/**/Public/**/*.css", "DistributionPackages/**/Public/**/*.js", "DistributionPackages/**/Private/**/*.fusion", "DistributionPackages/**/Private/**/*.html", ], proxy: "http://your.local.domain", };
Make sure you set the correct proxy with the corresponding protocol (https://
or http://
), depending on your setup. To create a better overview of the parameter, you can delete the not changed values from the file.
To start Browsersync you can run browser-sync start --config bs-config.js
. If you want to start it together with watch
, you can add the following line into the scripts
section:
{ "watch:browsersync": "browser-sync start --config bs-config.js", }