@creadigme/aurelia-docgen
TypeScript icon, indicating that this package has built-in type declarations

2.1.0 • Public • Published

npm version Build Status CodeQL codecov License Badge

Aurelia Docgen | @creadigme/aurelia-docgen

Aurelia + Storybook (can be) ❤

Aurelia Docgen brings the ability to generate component1 documentations, stories, from any2 Aurelia3 TypeScript project. Component's stories are written in class comments @story or in YAML files.

This tool is intended to be used with projects based on Aurelia framework3 + Storybook. It could also work with projects using only Aurelia without Storybook.

aurelia logo

storybook logo

1. customElement, valueConverter, customAttribute, bindingBehavior and services.
2. Without any warranty.
3. ⚠️ Aurelia 1 support is not implemented yet.

📝 License

Copyright © 2022-2023 Creadigme.

Disclaimer

This project has a dual license:

  • The AGPLv3 License - see the LICENSE file for details.
  • A private license agreement for private or/and commercial use.

See the FAQ on licensing.

Do not hesitate to contact us.

💾 Installation

Aurelia Docgen

npm i @creadigme/aurelia-docgen@^2 -D
# or
yarn add @creadigme/aurelia-docgen@^2 -D
# or for global use
yarn add @creadigme/aurelia-docgen@^2 -g

Storybook (version 6)

Storybook for HTML (if needed)

Storybook for HTML

# Aurelia 2 project with webpack
npx sb init --type html --builder webpack5

Webpack configuration

./webpack.config.js

Storybook and Aurelia use HMR (Hot Module Replacement), we have to disable Aurelia HMR if Storybook is used.

In your project, edit this file: ./webpack.config.js.

Example: webpack.config.js

/** Your content */

- module.exports = function(env, { analyze }) {
+ module.exports = function(env, { analyze, hmr }) {

/** Your content */

{ test: /\.ts$/i, use: ['ts-loader', 
- '@aurelia/webpack-loader'
+ {
+   loader: '@aurelia/webpack-loader',
+   options: {
+     hmr: hmr === false ? false : undefined,
+   },
+ }
], exclude: /node_modules/ },
{
  test: /[/\\]src[/\\].+\.html$/i,
- use: '@aurelia/webpack-loader',
+ use: {
+   loader: '@aurelia/webpack-loader',
+   options: {
+     hmr: hmr === false ? false : undefined,
+   },
+ },

/** Your content */

./.storybook/main.js

Storybook must use Aurelia configuration. In your project, edit this file: ./.storybook/main.js.

Example: main.js

+ const customWP = require('../webpack.config.js');

module.exports = {
+ webpackFinal: async (config) => {
+   const customConfigs = customWP(config.mode, {
+     hmr: false
+   });
+   return {
+     ...config,
+     module: {
+       ...config.module,
+       rules: (Array.isArray(customConfigs) ? customConfigs[0] : customConfigs).module.rules
+     }
+   };
+ },
+ core: {
+   builder: 'webpack5',
+ },
  "stories": [
-   "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  "framework": "@storybook/html",
  "core": {
    "builder": "@storybook/builder-webpack5"
  }
}

⚠️ Currently we do not support .mdx stories.

📝 Write stories

TLDR; comment your class with @story or/and via YAML files.

Comment your class @story

Just like that:

customElement

/**
 * My component
 *
 * @story My story
 * ```html
 * <au-component value.bind="1"></<au-component>
 * ```
 *
 * @story My another story
 * ```html
 * <au-component value.bind="200"></<au-component>
 * ```
 */
@customElement('au-component')
export class AuComponent implements ICustomElementViewModel {
  /** ... */
  @bindable()
  public value = 1;
}

valueConverter

/**
 * My converter
 *
 * @example
 * ```html
 * <!-- it's the default usage for valueConverter ! -->
 * <span>${ '1' | doSomething}</span>
 * ```
 *
 * @story My story
 * ```html
 * <let my-value.bind="{ a: 1 }">
 * <span>${ myValue | doSomething}</span>
 * ```
 *
 * @story My another story
 * ```html
 * <let my-value.bind="{ a: 1, b: 2 }">
 * <span>${ myValue | doSomething}</span>
 * ```
 */
@valueConverter('doSomething')
export class DoSomethingValueConverter {
  public toView(value: string | Record<string, number>): string {
    return /* ?? */ 'ok';
  }
}

customAttribute

import { customAttribute, INode } from 'aurelia';

/**
 * Red Square
 * From https://docs.aurelia.io/getting-to-know-aurelia/custom-attributes#attribute-aliases
 *
 * @group attributes/red-square
 */
@customAttribute({ name: 'red-square', aliases: ['redify', 'redbox'] }) 
export class RedSquareCustomAttribute {
  constructor(@INode private element: HTMLElement){
      this.element.style.width = this.element.style.height = '100px';
      this.element.style.backgroundColor = 'red';
  }
}

bindingBehavior

import { ILogger, bindingBehavior } from 'aurelia';

/**
 * Log behavior
 *
 * @group binding-behavior/log
 */
@bindingBehavior('log')
export class Log {
  constructor(
    @ILogger readonly logger: ILogger,
  ) {}
  bind(...args) {
    this.logger.debug('bind', ...args);
  }
  unbind(...args) {
    this.logger.debug('unbind', ...args);
  }
}

service

The tag comment @service is the key.

/**
 * My Service
 *
 * @service
 */
export class MyService implements IMyService {
  /**
   * @inheritDoc
   */
  public running: boolean = false;

  /**
   * @inheritDoc
   */
  public start(): void {
    this.running = true;
  }

  /**
   * @inheritDoc
   */
   public stop(): void {
    this.running = false;
  }
}

YAML Way

Stories are written in YAML next to components like these:

 components
 ├── something
 │   ├── au2-button.html
 │   ├── au2-button.ts
+│   ├── au2-button.stories.yml
 │   ├── au2-switch.html
 │   ├── au2-switch.ts
+│   ├── au2-switch.stories.yml
 └── else
 │   ├── supra-ultra-component.html
 │   ├── supra-ultra-component.ts
+│   ├── supra-ultra-component.stories.yml

With this format:

# au2-button stories
- title: Toggle
  help: |
    A button toggle
  tags:
    - button
    - simple
  code: |
    <let state.bind="false"></let>
    <au2-button action.bind="() => state = !state" content="${state ? 'Turn me off' : 'Turn me on'}"></au2-button>
    <div>${state ? '✅' : '☐' }</div>
- title: Another story
  help: |
    Another story. Look at this sample...
  tags:
    - button
    - simple
    - supra
  code: |
    <let state.bind="false"></let>
    <au2-button action.bind="() => state = !state" content="${state ? '✅' : '☐'}"></au2-button>
    <div>${state ? '✅' : '☐' }</div>

🔨 How to use

(optional) Add script in your package.json

{
  "name": "something",
  "scripts": {
+   "build:stories": "aurelia-docgen --out ./src/stories"
!   "build:stories": "aurelia-docgen"
!   "build:stories": "aurelia-docgen --out ./src/stories --auConfigure ./src/configure"
  }
}

Default way - All component stories in one directory

# Go to your project
cd ./my-supra-project

# add a new script in package.json, like `build:stories` with command
# aurelia-docgen --out ./src/stories
npm run build:stories

# all detect components and stories will be written in ./src/stories directory.

DRY way - Component stories next to components

# add a new script in package.json, like `build:stories` with command
# aurelia-docgen
npm run build:stories

# all detected components and the stories will be written next to the detected components.

Real world installation

  • Install @creadigme/aurelia-docgen in devDependencies.
  • Script on package.json as below.
  • All component stories in one directory.
  • Storybook stories can use any register elements.
  • ./.storybook/main.js is edited with webpack configuration.

./package.json

{
  "name": "something",
  "scripts": {
+   "build:stories": "aurelia-docgen --out ./src/stories --auConfigure ./src/configure"
+   "watch:stories": "npm run build:stories -- --watch"
  },
  "devDependencies": {
+   "@creadigme/aurelia-docgen": "^1"
  }
}

./src/configure.ts

import { Aurelia, Registration, type IEnhancementConfig, type IHydratedParentController } from "aurelia";

/** It's just an example */
let au: Aurelia;

/**
 * If specified, this function is called to retrieve the instance of Aurelia
 * @return Aurelia
 */
export async function getOrCreateAurelia(): Promise<Aurelia> {
  if (!au) {
    au = new Aurelia().register(/** Your configuration */);
    // Do your specific stuff here
  }
  return au;
}

let lastController: ICustomElementController<unknown>;

/** Cleanup previous story and enhance current */
export async function enhance(aureliaInst: Aurelia, config: IEnhancementConfig<unknown>, parentController?: IHydratedParentController | null): Promise<ICustomElementController<unknown>> {
  if (lastController) {
    // detaching, unbinding
    await lastController.deactivate(lastController, null);
    // dispose
    await lastController.dispose();
  }

  lastController = await aureliaInst.enhance(config, parentController);
  return lastController;
}
npm run build:stories
# all detect components and stories will be written in ./src/stories directory.
# launch storybook
npm run storybook

CLI parameters

Parameter Description Sample
--projectDir Project directory. Current working directory is used by default. ./
--out Output directory for generated stories. If not specified, stories are written next to the components. ./src/stories/
--mergeOut If out is specified, merges the component stories into a single file ./src/stories/components.stories.ts
--auConfigure Specify the TS file for Aurelia configuration (without extension).

Example ./src/configure file:
export async function getOrCreateAurelia(): Promise<Aurelia> { return Aurelia.register(/** */); }.
If null or empty, only the current component will be register.
--etaTemplate Path of Eta template (https://eta.js.org/). If null, the default template is used
--verbose More logs
--watch Monitor changes

API Parameters

CLI Parameters + below:

Parameter Description Sample
logger (msg: string, level: LevelLog) => void console.log(``${level} - ${msg}``)
import { AureliaDocgen } from '@creadigme/aurelia-docgen';

const au2Docgen = new AureliaDocgen({
  projectDir: './path-of-your-supra-ultra-project',
  out: './src/stories',
});

for (const ceStories of au2Docgen.getStories()) {
  console.dir(ceStories);
}

Coverage

codecov

Coverage sunburst

Package Sidebar

Install

npm i @creadigme/aurelia-docgen

Weekly Downloads

2

Version

2.1.0

License

AGPL-3.0-or-later

Unpacked Size

246 kB

Total Files

8

Last publish

Collaborators

  • agenet