@creadigme/aurelia-docgen
v2.1.0
Published
Aurelia + Storybook (can be) ❤. Aurelia documentation generator.
Downloads
20
Maintainers
Readme
Aurelia Docgen | @creadigme/aurelia-docgen
Aurelia + Storybook (can be) ❤
Aurelia Docgen
brings the ability to generate component1 documentations, stories, from any2 Aurelia
3 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
.
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)
# 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
indevDependencies
. - 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);
}