Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/creadigme/aurelia-docgen

Aurelia + Storybook (can be) ❤
https://github.com/creadigme/aurelia-docgen

aurelia documentation storybook typescript

Last synced: 4 months ago
JSON representation

Aurelia + Storybook (can be) ❤

Awesome Lists containing this project

README

        

[![npm version](https://img.shields.io/npm/v/@creadigme/aurelia-docgen.svg)](https://www.npmjs.com/package/@creadigme/aurelia-docgen)
[![Build Status](https://github.com/creadigme/aurelia-docgen/actions/workflows/ci.yml/badge.svg)](https://github.com/creadigme/aurelia-docgen/actions)
[![CodeQL](https://github.com/creadigme/aurelia-docgen/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/creadigme/aurelia-docgen/actions/workflows/codeql-analysis.yml)
[![codecov](https://codecov.io/gh/creadigme/aurelia-docgen/branch/main/graph/badge.svg?token=BV2ZP1FH6K)](https://codecov.io/gh/creadigme/aurelia-docgen)
[![License Badge](https://img.shields.io/badge/License-AGPL%203%2B-blue.svg)](LICENSE)

# Aurelia Docgen | @creadigme/aurelia-docgen

> Aurelia + Storybook (*can be*) ❤

`Aurelia Docgen` brings the ability to **generate** component1 documentations, **stories**, from *any*2 `Aurelia`3 `TypeScript` project.
Component's stories are written in class comments `@story` or in [YAML](https://yaml.org/) files.

This tool is intended to be used with projects based on [Aurelia framework](https://aurelia.io/)3 + [Storybook](https://storybook.js.org). It could also work with projects using only `Aurelia` **without** `Storybook`.

[![aurelia logo](https://aurelia.io/styles/images/logo.svg "Aurelia")](https://aurelia.io/)

[![storybook logo](https://storybook.js.org/showcase/images/logos/storybookLogo.svg "Storybook")](https://storybook.js.org)

1. `customElement`, `valueConverter`, `customAttribute`, `bindingBehavior` and services.

2. Without any warranty.

3. ⚠️ Aurelia 1 support is not implemented yet.

## 📝 License

Copyright © 2022-2023 [Creadigme](https://www.creadigme.net).

**Disclaimer**

This project has a dual license:
- The **AGPLv3** License - see the [LICENSE file](LICENSE) for details.
- A private license agreement for private or/and commercial use.

See [the FAQ on licensing](https://github.com/creadigme/aurelia-docgen/wiki/License-FAQ#faq).

Do not hesitate to [contact us](https://creadigme.net/contact/).

## 💾 Installation

### Aurelia Docgen

```bash
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](https://storybook.js.org/docs/html/get-started/install)

```bash
# 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](./examples/au2-basic/webpack.config.js)

```diff
/** 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](./examples/au2-basic/.storybook/main.js)

```diff
+ 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`

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

#### `valueConverter`

```typescript
/**
* My converter
*
* @example
* ```html
*
* ${ '1' | doSomething}
* ```
*
* @story My story
* ```html
*
* ${ myValue | doSomething}
* ```
*
* @story My another story
* ```html
*
* ${ myValue | doSomething}
* ```
*/
@valueConverter('doSomething')
export class DoSomethingValueConverter {
public toView(value: string | Record): string {
return /* ?? */ 'ok';
}
}
```

#### `customAttribute`

```typescript
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`

```typescript
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.

```typescript
/**
* 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](https://yaml.org/) next to components like these:

```diff
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:

```yml
# au2-button stories
- title: Toggle
help: |
A button toggle
tags:
- button
- simple
code: |


${state ? '✅' : '☐' }

- title: Another story
help: |
Another story. Look at this sample...
tags:
- button
- simple
- supra
code: |


${state ? '✅' : '☐' }

```

## 🔨 How to use

### **(optional)** Add script in your package.json

```diff
{
"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

```bash
# 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

```bash
# 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`](#aurelia-docgen).
- 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](#webpack-configuration).

**./package.json**
```diff
{
"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**
```typescript
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 {
if (!au) {
au = new Aurelia().register(/** Your configuration */);
// Do your specific stuff here
}
return au;
}

let lastController: ICustomElementController;

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

lastController = await aureliaInst.enhance(config, parentController);
return lastController;
}
```

```bash
npm run build:stories
# all detect components and stories will be written in ./src/stories directory.
```

```bash
# 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 { 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](#cli-parameters) + below:

| Parameter | Description | Sample |
|---|---|---|
| logger | `(msg: string, level: LevelLog) => void` | `console.log(``${level} - ${msg}``)` |

```typescript
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](https://codecov.io/gh/creadigme/aurelia-docgen/branch/main/graph/badge.svg?token=BV2ZP1FH6K)](https://codecov.io/gh/creadigme/aurelia-docgen)

![Coverage sunburst](https://codecov.io/gh/creadigme/aurelia-docgen/branch/main/graphs/sunburst.svg?token=BV2ZP1FH6K)