0.226.1 • Published 9 months ago

@eelkeblok/harbor v0.226.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

Harbor

Note: This is not the canonical version of this package. Please refer to @toolbarthomas/harbor.

Harbor is an asset builder that fits within the theme architecture of Drupal 8+ setups. It can create Drupal compatible themes without the need to install the actual CMS. With the help of Storybook it can generate the Twig templates that are used by Drupal themes.

The assets are processed on a very basic level, stylesheets can be compiled with the included compiler and Babel will transform the defined javascript files to enable JS compatibility for older browsers.

It is optional to use the bundled workers but they ensure that they work correctly within Drupal and your styleguide. It is possible to include other frameworks within the environment by simply adding it within a compatible theme library configuration.

Get more information for implementing Harbor in your current Drupal environment.

Setup

You can install Harbor via NPM (Nodejs is required in order to do this.):

$ npm install @eelkeblok/harbor

Then you can start Harbor by simply running:

$ node node_modules/@eelkeblok/harbor/index.js

Harbor will run the default tasks when there are no CLI arguments defined for the initial command. The following CLI arguments can be used in order to customize the build process.

ArgumentDescription
--taskStarts one or more worker tasks to compile the theme assets.
--verboseWrites extended console messages within the command line.
--styleguideStarts the styleguide builder.
--watchObserves for file changes for the initiated tasks.
--minifyMinifies the processed assets.
--testDefines the testing phase for Backstopjs, should be test, reference or approve.

You can also use the harbor command instead if you installed it globally: This will only run the default workers but you can use additional parameters like the local commands. Keep in mind that you need to be in the correct working directory in order to run it correctly.

$ npm install -g @eelkeblok/harbor

Installing Harbor globally will lock you in a specific version so keep in mind it can break your workflow if you installed the newest version without fixing the breaking-changes.

$ harbor
# or
$ harbor --styleguide --watch

Workers

A worker provides the core tasks for Harbor and can be adjusted within the configuration. Workers can be initiated during a Harbor process by calling the defined hook within the command or as CLI argument.

# Starts the workers that have the stylesheets hook from the configuration.
$ node node_modules/@eelkeblok/harbor/index.js --task=stylesheets
$ node node_modules/@eelkeblok/harbor/index.js stylesheets

This example will start the workers that are defined with the stylesheets hook, by default it will process the configured sass entry files (or it will try to use the default configured entries).

The actual hooks are defined within the default configuration of each worker, these hooks can be adjusted within your custom configuration. Workers that share the same hook will be called in parallel order by default. The order of this queue can be adjusted by adding the optional flag :: with the index value to mark the order, this will also run the queue in a sequence. This configuration example will start the Cleaner worker before the FileSync worker during the usage of the prepare task within the CLI.

...
Cleaner: {
  hook: ['clean', 'prepare::0'],
  ...
},
FileSync: {
  hook: ['sync', 'prepare::1']
  ...
}
...

Workers that share the same hook without the double colons will run in a parallel order:

# Should initiate the compile workers in a parallel order:
$ node node_modules/@eelkeblok/harbor/index.js --task=compile

The following workers are configured within the default configuration:

WorkerDescriptionHook(s)
AssetExporterWraps the defined entries as a template literal module.AssetExporter export
CleanerCleans the defined THEME_DIST environment directory.Cleaner clean prepare default
FileSyncSynchronizes the defined entry files to the THEME_DIST environment directory.FilSync sync prepare default
JsCompilerTransforms the defined entry javascript files with Babel.JSCompiler js javascripts compile default
ResolverResolves NPM installed vendor pacakges to the THEME_DIST environment directory.Resolver resolve prepare default
SassCompilerCompiles the defined entry Sass files with Node Sass.SassCompiler sass stylesheets compile default
StyleguideHelperCreates initial storybook entries from the defined Twig templates.StyleguideHelper setup
StyleguideTesterInitiates Snapshot tests for the styleguide with BackstopJS.StyleguideTester test
SVSpriteCompilerCreates one or more inline SVG sprites based from the configured entries.SVGSpriteCompiler svg images compile default

Plugins

Plugins are used to run post-process tasks like starting the storybook development server, optimizing the assets or include a file watcher. These can be defined by adding the given CLI argument hooks within your command:

$ node node_modules/@eelkeblok/harbor/index.js --task=javascripts --minify

More plugins can be included within a single command, the following plugins are available within the default configuration, the result of certain plugins can vary between environments:

PluginEnvironmentDescriptionHook(s)
JSOptimizerproduction onlyMinifies the defined js entries within the THEME_DIST directoryminify
StyleOptimizerproduction onlyMinifies the defined css entries within the THEME_DIST directoryminify
StyleguideCompilerproductionCreates a static storybook styleguide.storybook, styleguide
StyleguideCompilerdevelopmentStarts the storybook development server.storybook, styleguide
Watcherdevelopment onlyWatches the configured instance entries and runs the assigned workers during a file change.watch

This will only generate the actual assets that should be compatible for the Drupal environment. Keep in mind that this command will only run the configured Harbor workers, the actual development tools can be included with extra CLI arguments:

$ node node_modules/@eelkeblok/harbor/index.js --task=javascripts --minify --watch

Environment

An optional Harbor environment can be defined by creating a dotenv file within the root of your theme directory. The following configuration can be adjusted, the default values will be used for any missing environment variable.

Environment variableDefault valueDescription
THEME_SRC./srcDefines the working source directory for all Worker entries.
THEME_DIST./distDefines the build directory for all Worker entries & the styleguide development server.
THEME_PORT8080Defines the server port for the styleguide development server.
THEME_DEBUGfalseIncludes sourcemaps if the defined entries support it.
THEME_ENVIRONMENTproductionEnables environment specific Plugins to be used.
THEME_STATIC_DIRECTORYstorybook-staticDefines the destination path for the static styleguide build. This is used to create multiple static builds within the codebase.
THEME_WEBSOCKET_PORT35729Enables attached library stylesheets to be automatically refreshed within the styleguide, the websocket won't be created if there is no port number defined.
THEME_TEST_PHASEtestDefines the testing method for BackstopJS: test, reference or approve.
THEME_AS_CLIfalseLaunches Storybook in CLI mode that is used by the StyleguideTester.

Default Configuration

The Harbor workers can be configured to point out the location of your assets. A default configuration has been defined within Harbor, a custom configuration can be used by creating harbor.config.js within the working directory.

For example:

  // harbor.config.js

  ...
  workers: {
    SassCompiler: {
      options: ...,
      hook: 'stylesheets',
      plugins: ...,
      entry: {
        main: [...]
      },
    },
  }
  ...

Common Configuration

The following configuration options are available for the default Workers & Plugins. Most options are used before the defined Worker/Plugin is actually running; like resolving the actual entry files or ignoring some source paths for the Worker/Plugin entry:

OptiontypeDescription
entryString/String[]The actual sources that will be processed for the defined Worker or Plugin.
hookStringDefines the commands that should start the given Worker or Plugin
ignoreString/String[]Excludes the defined path(s) from the entry sources for the defined Worker or Plugin.
optionsObjectDefines the optional configuration for the defined Worker/Plugin, the actual options are not common since they are defined for the used NPM libraries.§

Default Worker Configuration

Cleaner configuration

The Cleaner is a default Harbor worker that will delete all files within the defined environment destination directory: THEME_DIST No specific configuration is available for this Worker.

FileSync configuration

The FileSync will synchronize the defined static entries to the configured environment destination directory.

OptiontypeDescription
patternsString[]Copies the given patterns and it's folder structure to the environment destination directory.

JsCompiler configuration

The JsCompiler transforms & lints the defined entries with Babel & Eslint. The result will be written relative to the configured environment destination directory.

OptiontypeDescription
pluginsObjectOptional plugins that will be assigned to the Babel & Eslint instances.
plugins.eslintObjectThe optional Eslint plugin(configuration).
plugins.transformObjectThe optional Babel transform(configuration).

SassCompiler configuration

The SassCompiler renders & prepares the defined entries with Node Sass & Postcss. The result will be written relative to the configured environment destination directory.

OptiontypeDescription
optionsObjectOptional configuration for the Node Sass compiler.
options.useLegacyCompilerBooleanFlag that enables the Node Sass compiler instead of the Dart Sass compiler.
pluginsObjectOptional plugins that will be assigned to the Postcss plugin.
plugins.postcssObjectThe optional Postcss plugin(configuration).

StyleguideHelper configuration

The StyleguideHelper creates initial Styleguide entry templates from the existing Twig templates any json or yaml file that is relative to the Twig template will be included within the styleguide entyr.

OptiontypeDescription
optionsObjectOptional configuration for the worker.
options.configurationExtensionsString[]Includes the first configuration entry from the defined extensions, the configuration is found relative within the template context.
options.defaultModuleNameStringDefines the name for the default entry story.
options.destinationDirectoryString/nullWrites the new entries to the defined directory or write it relative to the template by disabling this option.
options.disableAliasBooleanDon't use the included @theme alias and use a relative path instead.
options.extnameStringUse the defined extension when the styleguide entry is written to the FileSystem.
options.filterKeywordsString[]Removes the defined keywords from the generated Module export, File paths won't be adjusted from this settings.
options.ignoreInitialBooleanOverwrites the existing entry files when enabled.
options.prettierBooleanShould implement your project prettier configuration to ensure the styleguide entries are written in the correct syntax.
options.sepStringDefines the structure separator for the entry title.
options.structuredTitleBooleanIncludes the base directory structure for the styleguide entries when enabled.
options.variantsObjectIncludes optional module variants for the entry template.
options.variants[].contextStringShould match with the file that is used for the variant configuration.
options.variants[].queryStringExecutes a regular expression match within the defined context path, scripting files are ignored.
options.variants[].transformFunctionOptional handler that will the matched query values.

Define StyleguideHelper variants

You can define additional module variants for each entry template by defining additional properties that will be used within the template scope:

StyleguideHelper: {
  variants: {
    modifier_class: {
      query: /[^&(){}`a-zA-Z][.][a-zA-Z-]+--[a-zA-Z-]+/g,
      context: 'scss',
      transform: (v) => v.split('.').join(''),
    },
  },
}

The query option should match a regular expression that will match everything within the given source file. This source file is based defined from the initial source file where the from option is used as file extension replacement:

src/example.twig => src/example.scss

A transform handler can be included in order to strip any unwanted character from the matched results within the regular expression.

You can also directly import the variant configuration if the defined context matches a .js, .json, .mjs or .yaml file. This will assign the extra properties to the defined variant:

// Will create a variant as module import:
// import ButtonExampleConfiguration from button.example.json
// within the template context: button.twig.
StyleguideHelper: {
  variants: {
    modifier_class: {
      context: '.example.json',
    },
  },
}

It is also possible to search for configuration files outside the directory by including the includeDirectories option within the variant configuration.

Keep in mind that the directories are resolved from the defined THEME_SRC environment path. It will use the configuration if the file config/{module}.example.json exists:

// Will create a variant as module import:
// import ButtonExampleConfiguration from button.example.json
// outside the template context: button.twig.
StyleguideHelper: {
  variants: {
    modifier_class: {
      context: '.example.json',
      includeDirectories: ['config'],
    },
  },
}

StyleguideTester configuration

The StyleguideTester enables snapshot testing of the generated styleguide. All valid stories will be extracted by Storybook and are tested with BackstopJS.

OptiontypeDescription
optionsObjectOptional configuration for the worker & BackstopJS.
options.backstopJSObjectDefines the base configuration for BackstopJS. More info
options.outputPathStringThe destination for the Styleguide manifest that is used for the Snapshot tester.
options.scenarioDirectoryStringDefines the destination directory for additional scenarios defined as YAML or JSON file.
options.staticDirectoryStringDefines the destination directory for the static styleguide build, to prevent removal of already generated packages.

SvgSpriteCompiler configuration

The SvgSpriteCompiler will compile the defined entries into inline SVG sprites. The result will be written relative to the configured environment destination directory.

OptiontypeDescription
prefixStringThe ID prefix for each icon within the compiled sprite.

Resolver configuration

The Resolver will resolve the defined packages from the node_modules to the environment destination.

OptiontypeDescription
options.cwdStringThe destination directory where the resolved entries will be Written into.

Default Plugin Configuration

AssetExporter configuration

The AssetExporter wraps the defined entry templates as a valid module export template literal. It is possible to include an optional literal function within the actual asset by defining a new includeLiteral Object within the options.

OptiontypeDescription
optionsObjectOptional configuration for the AssetExporter.
options.includeLiteralObjectAssigns Babel module-resolver aliases to the Storybook instance.
options.includeLiteral[].entryStringShould match with the defined entry name, a custom literal will be included when there is a match.
options.includeLiteral[].exportStringThe actual literal that can be prefixed with.
options.includeLiteral[].importStringThe actual import source for the optional module literal.

StyleguideCompiler configuration

The Styleguide Compiler will generate a new Storybook instance for the defined THEME_ENVIRONMENT value.

A Storybook development version can be launched by defining THEME_ENVIRONMENT='development' within your environment configuration.

This will launch a new Storybook development server with the Storybook CLI. More information about the usage of this server can be found on Storybook

You can also create a static version of your Storybook instance by setting THEME_ENVIRONMENT to production. This static styleguide will be written to the defined THEME_DEST destination and will resolve the processed assets within the static package. Keep in mind that some assets cannot be displayed when viewing the static HTML document directly for security reasons.

This can be resolved by viewing the actual result from a (local) webserver.

OptiontypeDescription
optionsObjectOptional configuration for the Storybook compiler.
options.addonsArrayShould contain the Storybook addon configuration.
options.aliasObjectAssigns Babel module-resolver aliases to the Storybook instance.
options.builderDirectoryStringDefines the Twing instance directory .twing that can be used to include custom Twing functionality.
options.configDirectoryStringDefines the Storybook instance directory for your theme: ./.storybook.
options.globalModeBoolean/Stringexperimental This will use a global render context for Twig.
options.optimizationObjectDefines the Webpack optimization configuration.
options.staticDirectoryStringDefines the destination path for the production build of the Storybook styleguide storybook-static.
options.useLegacyCompilerBooleanEnables the usage of older Twing libraries within the styleguide to disable the requirement of async stories.

Watcher configuration

The Watcher can be started by defining the watch parameter to the CLI and will run the defined hooks from the TaskManager. The Watcher will shutdown automatically if no event occured during the defined duration.

OptiontypeDescription
instancesObjectString, ObjectSpawns a Wacther instance for each defined entry.
instances[].eventStringDefines the Event handler and will publish the defined hook with the TaskManager.
instances[].pathString/String[]Watches the given paths for the spawned Watcher.
instances[].workersString[]Will publish the defined Harbor workers in order.
optionsObjectOptional configuration for the Watcher class.
options.delaynumberCreates a timeout before running the connected Workers after a Watch event has occured.
options.durationnumberDefines the lifetime in miliseconds of the spawned Watcher instances.

Example NPM script setup

You can assign the following NPM script entries when using the default hook configuration:

  {
    "production": "node node_modules/@eelkeblok/harbor/index.js --task=prepare,compile --minify",
    "predevelopment": "npm run production",
    "development": "node node_modules/@eelkeblok/harbor/index.js --watch --styleguide",
    "images": "node node_modules/@eelkeblok/harbor/index.js --task=images",
    "javascripts": "node node_modules/@eelkeblok/harbor/index.js --task=javascripts",
    "resolve": "node node_modules/@eelkeblok/harbor/index.js --task=resolve",
    "styleguide": "node node_modules/@eelkeblok/harbor/index.js --styleguide",
    "stylesheets": "node node_modules/@eelkeblok/harbor/index.js --task=stylesheets",
    "test": "node node_modules/@eelkeblok/harbor/index.js --task=test",
  }

Asset management

Assets can be included by using the attach_library Twig function within your templates.

Stylesheets that are injected from the attach_library function will also refresh during a file change.

You need a valid Drupal theme library configuration file within your theme with the defined resources you want to use within the theme:

# example.libraries.yml
base:
  version: 1.x
  css:
    base:
      dist/main/stylesheets/index.css: {}
  js:
    dist/main/javascripts/base.js: {}

The defined assets will be included within the templates that uses the attach_library Twig function:

{{ attach_library('example/base') }}

It also possible to use the defined Storybook preview head & body snippets within your project. Harbor will copy the configuration files from the defined configDirectory option within the StyleguideCompiler plugin ('./storybook').

You can also import the actual assets within each storybook story to enable Hot Module Reload. Keep in mind that you still need to define the required libraries within Drupal if you don't include assets with the attach_library function.

// example.stories.js

import styles from './styles.css';

...

Suggested javascript structure.

Using the functionality of the Drupal.behaviors Object, you can run the defined javascript during the (initial) (re)load within Drupal and Storybook. Storybook calls the attach handler within the Drupal behaviors, this Object is used within Drupal sites and is also available for the styleguide.

The actual javascript can be created like the following and should be compliant with the Drupal javascript structure:

  (function example(Drupal) {
    Drupal.behaviors.example = {
      attach: (context, settings) => {
        ...
      }
    }
  })(Drupal, drupalSettings);

Implementing templates since >=1.0.0

As of version 1.0.0 you need to define your Twing templates within the Storybook loaders configuration. This is required in order to display the actual templates; since they are rendered in asynchronous order:

// example.stories.js

import Template from 'template.twig';

export default {
  title: 'Example template',
  loaders: [
    async ({ args }) => {
      Template: await Template(args); // Keyname can be anything.
    },
  ],
};

// loaded.Templates is defined within the default default export.
export const Default = (args, { loaded }) = > loaded.Template;

// Define the actual arguments
Default.args = {
  title: 'Foo',
};
{{ title }}

Usage of SVG Inline Sprites

Harbor compiles the defined SVG images with the SVGSpriteCompiler and these can be used within the Twig templates. The sprites are available as a Storybook Global that can be accessed within the styleguide.

You can easily include these paths with the add_svg Twig function. This will output the path of an inline SVG sprite that has been created by the SVGSpriteCompiler. This function accepts 3 arguments to output the path of your selection:

{{ add_svg('svg--chevron--down') }}

This will include the path of the SVG sprite based from the first inline svg that has been stored within the THEME_SPRITES storybook global. This would output dist/main/images/svgsprite.svg#svg--chevron--down if the entry key would be defined as svgsprite...

You can use any entry key of the SVGSpriteCompiler configuration to use that specific sprite path instead:

  // harbor.config.js

  workers: {
    ...
      SvgSpriteCompiler: {
        ...
        entry: {
          common: 'main/images/common/**.svg',
          icons: 'main/images/icons/**.svg',
        },
        ...
    ...
  }
{{ add_svg('svg--chevron--down', 'icons') }}

This will output dist/main/images/icons.svg#svg--chevron--down.

It is also possible to output the base SVG element when the second or third argument has been defined as true:

{{ add_svg('svg--logo', true) }}

Would output:

<svg aria-hidden="true" aria-focusable="false">
  <use xlink:href="dist/main/images/common.svg#svg--logo"></use>
</svg>

And:

  {{ add_svg('svg--chevron--down', 'icons', true) }}

Will render:

<svg aria-hidden="true" aria-focusable="false">
  <use xlink:href="dist/main/images/icons.svg#svg--chevron--down"></use>
</svg>

Running Snapshot tests

It is possible to run Snapshot tests with BackstopJS for all created Storybook stories. Storybook first generates a stories manifest in order to define the components to test. A temporary Storybook instance will be created afterwards, which BackstopJS will use for the snapshot tests.

You need to enable reference snapshots first otherwise you will encounter an error, you need to pass the optional test parameter within the command:

$ harbor --task=test --test=reference

This will create reference snapshots within the defined options.backstopJS.bitmaps_reference configuration option. These snapshots will tested with the defined testing snapshots afterwards:

# You don't need to define the `test` parameter since this is the default testing method.
$ harbor --task=test --test=test

An exception will be thrown if there are any mismatches with the references. You can approve these changes automatically by running:

$ harbor --task=test --test=approve