diff --git a/packages/cli/test/fixtures/patternlab-config.json b/packages/cli/test/fixtures/patternlab-config.json index 4ecc87893..615be9273 100644 --- a/packages/cli/test/fixtures/patternlab-config.json +++ b/packages/cli/test/fixtures/patternlab-config.json @@ -83,5 +83,15 @@ "color": "dark", "density": "compact", "layout": "horizontal" - } + }, + "engines": { + "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], + "extend": "helpers/*.js" + } + } } diff --git a/packages/core/patternlab-config.json b/packages/core/patternlab-config.json index 2571bb52f..d3022f24b 100644 --- a/packages/core/patternlab-config.json +++ b/packages/core/patternlab-config.json @@ -88,6 +88,16 @@ "density": "compact", "layout": "horizontal" }, + "engines": { + "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], + "extend": "helpers/*.js" + } + }, "uikits": [ { "name": "uikit-workshop", diff --git a/packages/core/src/lib/pattern_engines.js b/packages/core/src/lib/pattern_engines.js index a744f4d7d..a719d5f22 100644 --- a/packages/core/src/lib/pattern_engines.js +++ b/packages/core/src/lib/pattern_engines.js @@ -62,6 +62,19 @@ function findEngineModulesInDirectory(dir) { return foundEngines; } +function findEnginesInConfig(config) { + if ('engines' in config) { + return config.engines; + } + logger.warning( + "Scanning the 'node_modules' folder for pattern engines is deprecated and will be removed in v6." + ); + logger.warning( + 'To configure your engines in patternlab-config.json, see https://patternlab.io/docs/editing-the-configuration-options/#heading-engines' + ); + return null; +} + // // PatternEngines: the main export of this module // @@ -86,52 +99,106 @@ const PatternEngines = Object.create({ loadAllEngines: function (patternLabConfig) { const self = this; - // Try to load engines! We scan for engines at each path specified above. This - // function is kind of a big deal. - enginesDirectories.forEach(function (engineDirectory) { - const enginesInThisDir = findEngineModulesInDirectory( - engineDirectory.path - ); + // Try to load engines! We load the engines configured in patternlab-config.json + const enginesInConfig = findEnginesInConfig(patternLabConfig); - logger.debug( - `Loading engines from ${engineDirectory.displayName}: ${engineDirectory.path} ...` - ); + if (enginesInConfig) { + // Quick fix until we've removed @pattern-lab/engine-mustache, starting with https://github.com/pattern-lab/patternlab-node/issues/1239 & https://github.com/pattern-lab/patternlab-node/pull/1455 + // @TODO: Remove after removing @pattern-lab/engine-mustache dependency + enginesInConfig.mustache = enginesInConfig.mustache || {}; + enginesInConfig.mustache.package = + enginesInConfig.mustache.package || '@pattern-lab/engine-mustache'; + enginesInConfig.mustache.extensions = + enginesInConfig.mustache.extensions || 'mustache'; - // find all engine-named things in this directory and try to load them, - // unless it's already been loaded. - enginesInThisDir.forEach(function (engineDiscovery) { + // Try loading each of the configured pattern engines + // eslint-disable-next-line guard-for-in + for (const name in enginesInConfig) { + const engineConfig = enginesInConfig[name]; let errorMessage; const successMessage = 'good to go'; try { // Give it a try! load 'er up. But not if we already have, - // of course. Also pass the pattern lab config object into + // of course. Also pass the Pattern Lab config object into // the engine's closure scope so it can know things about // things. - if (self[engineDiscovery.name]) { + if (self[name]) { throw new Error('already loaded, skipping.'); } - self[engineDiscovery.name] = require(engineDiscovery.modulePath); - if ( - typeof self[engineDiscovery.name].usePatternLabConfig === 'function' - ) { - self[engineDiscovery.name].usePatternLabConfig(patternLabConfig); - } - if (typeof self[engineDiscovery.name].spawnMeta === 'function') { - self[engineDiscovery.name].spawnMeta(patternLabConfig); + if ('package' in engineConfig) { + self[name] = require(engineConfig.package); + if (typeof self[name].usePatternLabConfig === 'function') { + self[name].usePatternLabConfig(patternLabConfig); + } + if (typeof self[name].spawnMeta === 'function') { + self[name].spawnMeta(patternLabConfig); + } + } else { + logger.warning( + `Engine ${name} not configured correctly. Please configure your engines in patternlab-config.json as documented in https://patternlab.io/docs/editing-the-configuration-options/#heading-engines` + ); } } catch (err) { errorMessage = err.message; } finally { // report on the status of the engine, one way or another! logger.info( - `Pattern Engine ${engineDiscovery.name}: ${ + `Pattern Engine ${name} / package ${engineConfig.package}: ${ errorMessage ? errorMessage : successMessage }` ); } + } + } else { + // Try to load engines! We scan for engines at each path specified above. This + // function is kind of a big deal. + enginesDirectories.forEach(function (engineDirectory) { + const enginesInThisDir = findEngineModulesInDirectory( + engineDirectory.path + ); + + `Loading engines from ${engineDirectory.displayName}: ${engineDirectory.path} ...`; + + // find all engine-named things in this directory and try to load them, + // unless it's already been loaded. + enginesInThisDir.forEach(function (engineDiscovery) { + let errorMessage; + const successMessage = 'good to go'; + + try { + // Give it a try! load 'er up. But not if we already have, + // of course. Also pass the Pattern Lab config object into + // the engine's closure scope so it can know things about + // things. + if (self[engineDiscovery.name]) { + throw new Error('already loaded, skipping.'); + } + self[engineDiscovery.name] = require(engineDiscovery.modulePath); + if ( + typeof self[engineDiscovery.name].usePatternLabConfig === + 'function' + ) { + self[engineDiscovery.name].usePatternLabConfig(patternLabConfig); + } + if (typeof self[engineDiscovery.name].spawnMeta === 'function') { + self[engineDiscovery.name].spawnMeta(patternLabConfig); + } + } catch (err) { + errorMessage = err.message; + } finally { + // report on the status of the engine, one way or another! + logger.info( + `Pattern Engine ${ + engineDiscovery.name + } by discovery (deprecated): ${ + errorMessage ? errorMessage : successMessage + }` + ); + } + }); }); - }); + } // Complain if for some reason we haven't loaded any engines. if (Object.keys(self).length === 0) { diff --git a/packages/core/test/util/patternlab-config.json b/packages/core/test/util/patternlab-config.json index 6095b5288..e101b2ad4 100644 --- a/packages/core/test/util/patternlab-config.json +++ b/packages/core/test/util/patternlab-config.json @@ -73,6 +73,16 @@ "density": "compact", "layout": "horizontal" }, + "engines": { + "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], + "extend": "helpers/*.js" + } + }, "uikits": [ { "name": "uikit-workshop", diff --git a/packages/development-edition-engine-handlebars/patternlab-config.json b/packages/development-edition-engine-handlebars/patternlab-config.json index 1ec665048..a8873d362 100644 --- a/packages/development-edition-engine-handlebars/patternlab-config.json +++ b/packages/development-edition-engine-handlebars/patternlab-config.json @@ -108,6 +108,11 @@ ], "engines": { "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], "extend": "helpers/*.js" } }, diff --git a/packages/development-edition-engine-react/patternlab-config.json b/packages/development-edition-engine-react/patternlab-config.json index 8e42b1247..23f8673e8 100644 --- a/packages/development-edition-engine-react/patternlab-config.json +++ b/packages/development-edition-engine-react/patternlab-config.json @@ -87,5 +87,13 @@ "color": "dark", "density": "compact", "layout": "horizontal" + }, + "engines": { + "react": { + "package": "@pattern-lab/engine-react", + "fileExtensions": [ + "jsx" + ] + } } } diff --git a/packages/development-edition-engine-twig/patternlab-config.json b/packages/development-edition-engine-twig/patternlab-config.json index 38527b3e6..8fba803bd 100644 --- a/packages/development-edition-engine-twig/patternlab-config.json +++ b/packages/development-edition-engine-twig/patternlab-config.json @@ -104,6 +104,10 @@ ], "engines": { "twig": { + "package": "@pattern-lab/engine-twig", + "fileExtensions": [ + "twig" + ], "namespaces": { "atoms": "source/_patterns/atoms/", "molecules": "source/_patterns/molecules/", diff --git a/packages/docs/src/docs/advanced-config-options.md b/packages/docs/src/docs/advanced-config-options.md index 7f2b5384c..4665c35ca 100644 --- a/packages/docs/src/docs/advanced-config-options.md +++ b/packages/docs/src/docs/advanced-config-options.md @@ -180,6 +180,37 @@ Sets the panel name and language for the code tab on the styleguide. Since this **default**: `mustache` +## engines + +An engine is a wrapper around a templating library like Handlebars, Twig or others. An [engine package](docs/template-language-and-patternengines/) +is the bridge between Pattern Lab and the standalone NPM package supporting the templating language. + +`engines` accepts an map of Engine objects. The mandatory properties for each Pattern Lab engine are: + +- `package`: the NodeJS package name. Add the package of the engine as a dependency in `package.json` before you configure it here. +- `fileExtensions`: list of pattern file extensions which will be handled by this pattern engine. + +Other engine specific configuration options can be added and will be passed to the pattern engine at loading time. See the NPM package documentation for the properties each pattern engine supports. + +**default**: + +```javascript + "engines": { + "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "extensions": [ + "handlebars", + "hbs" + ], + "extend": "helpers/*.js" + ... + } + } +``` + +Configuring the engines in the config file was introduced in v5.14. The fallback lookup mode by scanning the +`node_modules` folder is **deprecated** and will be removed in Pattern Lab v7. + ## patternStateCascade See the [Pattern State Documentation](/docs/using-pattern-states/) @@ -402,7 +433,7 @@ Important details: - the [default `paths.source` object paths](https://github.com/pattern-lab/patternlab-node/pull/840/commits/a4961bd5d696a05fb516cdd951163b0f918d5e19) within `patternlab-config.json` are now relative to the current UIKit. See the [structure of uikit-workshop](https://github.com/pattern-lab/patternlab-node/tree/master/packages/uikit-workshop) for more info - the [default `paths.public` object paths](https://github.com/pattern-lab/patternlab-node/pull/840/commits/812bab3659f504043e8b61b1dc1cdac71f248449) within `patternlab-config.json` are now relative to the current UIKit's `outputDir`. Absolute paths will no longer work. Someone could test putting an absolute path in a UIKit `outputDir` property and see what happens I suppose. - `dependencyGraph.json` has moved to the project root rather than `public/` as we should only retain one -- The lookup of the uikit by `name` is deprecated and the user will be notified of it. If the `package` property isn't defined, there is a default fallback lookup strategy where the value of `name` is tried as: +- The lookup of the uikit by `name` is **deprecated** and will be removed in v7. The user will be notified of it. If the `package` property isn't defined, there is a default fallback lookup strategy where the value of `name` is tried as: - `` - `uikit-` - `@pattern-lab/` diff --git a/packages/edition-node-gulp/patternlab-config.json b/packages/edition-node-gulp/patternlab-config.json index e63fec918..7a6e89dd8 100644 --- a/packages/edition-node-gulp/patternlab-config.json +++ b/packages/edition-node-gulp/patternlab-config.json @@ -87,6 +87,16 @@ "density": "compact", "layout": "horizontal" }, + "engines": { + "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], + "extend": "helpers/*.js" + } + }, "uikits": [ { "name": "uikit-workshop", diff --git a/packages/edition-node/patternlab-config.json b/packages/edition-node/patternlab-config.json index 4afb4d1d5..1296ff103 100644 --- a/packages/edition-node/patternlab-config.json +++ b/packages/edition-node/patternlab-config.json @@ -99,6 +99,11 @@ ], "engines": { "handlebars": { + "package": "@pattern-lab/engine-handlebars", + "fileExtensions": [ + "handlebars", + "hbs" + ], "extend": "helpers/*.js" } } diff --git a/packages/edition-twig/patternlab-config.json b/packages/edition-twig/patternlab-config.json index e9fd3c456..3907ebb47 100644 --- a/packages/edition-twig/patternlab-config.json +++ b/packages/edition-twig/patternlab-config.json @@ -1,6 +1,10 @@ { "engines": { "twig": { + "package": "@pattern-lab/engine-twig-php", + "fileExtensions": [ + "twig" + ], "namespaces": [ { "id": "uikit", diff --git a/packages/starterkit-twig-demo/patternlab-config.json b/packages/starterkit-twig-demo/patternlab-config.json index e713b77d0..c25133362 100644 --- a/packages/starterkit-twig-demo/patternlab-config.json +++ b/packages/starterkit-twig-demo/patternlab-config.json @@ -1,6 +1,10 @@ { "engines": { "twig": { + "package": "@pattern-lab/engine-twig-php", + "fileExtensions": [ + "twig" + ], "namespaces": [ { "id": "atoms",