Requirements

We need an existing Fractal integration with Webpack Encore as a base. This is described in my previous article on integrating Webpack Encore with Fractal.

Includes and their paths

As we want to use our Twig templates in the CMS without modifications, we can not use @component handles, like Fractal does. Instead, we want to use include paths just like in Twig, with absolute filepaths. To be able to do so, we must edit the frctl/@twig adapter in fractal.config.js and set a base.

const twigAdapter = require('@frctl/twig')({
  importContext: true,
+  base: '/',

This will load included templates from the root of the components path set in Fractal.

Sulu functions

Sulu uses some Twig extensions to get path & page information or retrieve content. They are prefixed with sulu_. We don't need to implement them properly, but we need to provide them to our Twig adapter and mimic their behaviour.

In the functions, we can use the context of the template as defined in it's Fractal config. It can be accessed with this.context.

For the navigations, I have defined a nav entry in my header.config.yml.

title: header
status: wip
context:
  nav:
    -
     title: Homepage
     url: /
    -
     title: About
     url: /about
    -
     title: Contact
     url: /contact

This is used by sulu_navigation_root_tree.

const twigAdapter = require('@frctl/twig')({
  …
  functions: {
    asset: function(path) {
        …
    },
+    sulu_navigation_root_tree: function(navigation) {
+      return this.context.nav;
+    },
  },

I am also using sulu_navigation_is_active, which expects a request.resourceLocator to be specified, which it compares with the url of the navigation item.

context:
+  request:
+    resourceLocator: '/about'
const twigAdapter = require('@frctl/twig')({
  …
  functions: {
    asset: function(path) {
        …
    },
+    sulu_navigation_is_active: function(resourceLocator, url) {
+      return (resourceLocator == url);
+    },
  },

Finally, we need to add some methods to mimic path handling.

const twigAdapter = require('@frctl/twig')({
  …
  functions: {
+    sulu_content_root_path: function() {
+      return '/';
+    },
+    sulu_content_path: function(path) {
+      return path;
+    },
  },

For a cleaner approach, we could also write this as an extension to Twigjs and import is as a module.

Integrating into the deploy

While the stylesheets and javascript are deployed with our existing Webpack Encore setup, we also want to deploy the templates from our component library. We should be able to use a copyFiles directive to do so. Actually we are not, because Webpack Encore will always put the copied files into the configured output-path. So we must use copy-webpack-plugin directly. Let's add it to our webpack.config.js.

const Encore = require('@symfony/webpack-encore');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const FractalWebpackPlugin = require('fractal-webpack-plugin');
+ const CopyWebpackPlugin = require('copy-webpack-plugin');

…

+ Encore.addPlugin(new CopyWebpackPlugin({
+ patterns: [
+     {
+     from: './src/components/blocks/**/*.html.twig',
+     to: __dirname + '/templates/blocks/[name].[ext]',
+     },
+ ],
+ }));

Because we can't use context when using globs, we have to set a pattern for each directory. In this case with Sulu it could be something like atoms/, blocks/and includes/ - this depends on your template structure. I have a structure similar to the one from the Sulu demo.

To finish, delete these directories from your templates/ directory and gitignore them for the future - as these are now auto-generated from your component library.

About testing

If you work with fully generated (copied) templates, you must run the frontend build before running any functional tests.