Skip to content

Commit

Permalink
Merge pull request #15061 from storybookjs/angular/builder
Browse files Browse the repository at this point in the history
Angular: Add angular builder to start + build storybook
  • Loading branch information
shilman authored Jun 1, 2021
2 parents c08e40f + ccc4ef1 commit 4e57f61
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 1 deletion.
1 change: 1 addition & 0 deletions app/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"main": "dist/ts3.9/client/index.js",
"module": "dist/ts3.9/client/index.js",
"types": "dist/ts3.9/client/index.d.ts",
"builders": "dist/ts3.9/builders/builders.json",
"typesVersions": {
"<3.8": {
"*": [
Expand Down
48 changes: 48 additions & 0 deletions app/angular/src/builders/build-storybook/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Architect } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
import { schema } from '@angular-devkit/core';
import * as path from 'path';

const buildStandaloneMock = jest.fn().mockImplementation((_options: unknown) => Promise.resolve());

jest.mock('@storybook/angular/standalone', () => buildStandaloneMock);

describe('Build Storybook Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;

beforeEach(async () => {
const registry = new schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);

architectHost = new TestingArchitectHost();
architect = new Architect(architectHost, registry);

// This will either take a Node package name, or a path to the directory
// for the package.json file.
await architectHost.addBuilderFromPackage(path.join(__dirname, '../../..'));
});

it('should work', async () => {
const run = await architect.scheduleBuilder('@storybook/angular:build-storybook', {
browserTarget: 'angular-cli:build-2',
});

const output = await run.result;

await run.stop();

expect(output.success).toBeTruthy();
expect(buildStandaloneMock).toHaveBeenCalledWith({
angularBrowserTarget: 'angular-cli:build-2',
browserTarget: 'angular-cli:build-2',
configDir: '.storybook',
docs: false,
loglevel: undefined,
quiet: false,
outputDir: 'storybook-static',
staticDir: [],
mode: 'static',
});
});
});
40 changes: 40 additions & 0 deletions app/angular/src/builders/build-storybook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { from, Observable, of } from 'rxjs';
import { CLIOptions } from '@storybook/core-common';
import { map, switchMap } from 'rxjs/operators';

// eslint-disable-next-line import/no-extraneous-dependencies
import buildStandalone, { StandaloneOptions } from '@storybook/angular/standalone';

export type StorybookBuilderOptions = JsonObject & {
browserTarget: string;
} & Pick<
// makes sure the option exists
CLIOptions,
'staticDir' | 'outputDir' | 'configDir' | 'loglevel' | 'quiet' | 'docs'
>;

export type StorybookBuilderOutput = JsonObject & BuilderOutput & {};

export default createBuilder(commandBuilder);

function commandBuilder(
options: StorybookBuilderOptions,
_context: BuilderContext
): Observable<StorybookBuilderOutput> {
return of({}).pipe(
map(() => ({
...options,
angularBrowserTarget: options.browserTarget,
})),
switchMap((standaloneOptions) => runInstance({ ...standaloneOptions, mode: 'static' })),
map(() => {
return { success: true };
})
);
}

function runInstance(options: StandaloneOptions) {
return from(buildStandalone(options));
}
47 changes: 47 additions & 0 deletions app/angular/src/builders/build-storybook/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "http://json-schema.org/schema",
"title": "Build Storybook",
"description": "Serve up storybook in development mode.",
"type": "object",
"properties": {
"browserTarget": {
"type": "string",
"description": "Build target to be served in project-name:builder:config format. Should generally target on the builder: '@angular-devkit/build-angular:browser'. Useful for Storybook to use options (styles, assets, ...).",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$"
},
"staticDir": {
"type": "array",
"description": "Directory where to load static files from, array of strings.",
"items": {
"type": "string"
}
},
"outputDir": {
"type": "string",
"description": "Directory where to store built files.",
"default": "storybook-static"
},
"configDir": {
"type": "string",
"description": "Directory where to load Storybook configurations from.",
"default": ".storybook"
},
"loglevel": {
"type": "string",
"description": "Controls level of logging during build. Can be one of: [silly, verbose, info (default), warn, error, silent].",
"pattern": "(silly|verbose|info|warn|silent)"
},
"quiet": {
"type": "boolean",
"description": "Suppress verbose build output.",
"default": false
},
"docs": {
"type": "boolean",
"description": "Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation.",
"default": false
}
},
"additionalProperties": false,
"required": ["browserTarget"]
}
14 changes: 14 additions & 0 deletions app/angular/src/builders/builders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"builders": {
"build-storybook": {
"implementation": "./build-storybook",
"schema": "./build-storybook/schema.json",
"description": "Build storybook"
},
"start-storybook": {
"implementation": "./start-storybook",
"schema": "./start-storybook/schema.json",
"description": "Start storybook"
}
}
}
54 changes: 54 additions & 0 deletions app/angular/src/builders/start-storybook/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Architect } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
import { schema } from '@angular-devkit/core';
import * as path from 'path';

const buildStandaloneMock = jest.fn().mockImplementation((_options: unknown) => Promise.resolve());

jest.mock('@storybook/angular/standalone', () => buildStandaloneMock);

describe('Start Storybook Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;

beforeEach(async () => {
const registry = new schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);

architectHost = new TestingArchitectHost();
architect = new Architect(architectHost, registry);

// This will either take a Node package name, or a path to the directory
// for the package.json file.
await architectHost.addBuilderFromPackage(path.join(__dirname, '../../..'));
});

it('should work', async () => {
const run = await architect.scheduleBuilder('@storybook/angular:start-storybook', {
browserTarget: 'angular-cli:build-2',
port: 4400,
});

const output = await run.result;

await run.stop();

expect(output.success).toBeTruthy();
expect(buildStandaloneMock).toHaveBeenCalledWith({
angularBrowserTarget: 'angular-cli:build-2',
browserTarget: 'angular-cli:build-2',
ci: false,
configDir: '.storybook',
docs: false,
host: 'localhost',
https: false,
port: 4400,
quiet: false,
smokeTest: false,
sslCa: undefined,
sslCert: undefined,
sslKey: undefined,
staticDir: [],
});
});
});
51 changes: 51 additions & 0 deletions app/angular/src/builders/start-storybook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { from, Observable, of } from 'rxjs';
import { CLIOptions } from '@storybook/core-common';
import { map, switchMap } from 'rxjs/operators';

// eslint-disable-next-line import/no-extraneous-dependencies
import buildStandalone, { StandaloneOptions } from '@storybook/angular/standalone';

export type StorybookBuilderOptions = JsonObject & {
browserTarget: string;
} & Pick<
// makes sure the option exists
CLIOptions,
| 'port'
| 'host'
| 'staticDir'
| 'configDir'
| 'https'
| 'sslCa'
| 'sslCert'
| 'sslKey'
| 'smokeTest'
| 'ci'
| 'quiet'
| 'docs'
>;

export type StorybookBuilderOutput = JsonObject & BuilderOutput & {};

export default createBuilder(commandBuilder);

function commandBuilder(
options: StorybookBuilderOptions,
_context: BuilderContext
): Observable<StorybookBuilderOutput> {
return of({}).pipe(
map(() => ({
...options,
angularBrowserTarget: options.browserTarget,
})),
switchMap((standaloneOptions) => runInstance(standaloneOptions)),
map(() => {
return { success: true };
})
);
}

function runInstance(options: StandaloneOptions) {
return from(buildStandalone(options));
}
74 changes: 74 additions & 0 deletions app/angular/src/builders/start-storybook/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"$schema": "http://json-schema.org/schema",
"title": "Start Storybook",
"description": "Serve up storybook in development mode.",
"type": "object",
"properties": {
"browserTarget": {
"type": "string",
"description": "Build target to be served in project-name:builder:config format. Should generally target on the builder: '@angular-devkit/build-angular:browser'. Useful for Storybook to use options (styles, assets, ...).",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$"
},
"port": {
"type": "number",
"description": "Port to listen on.",
"default": 9009
},
"host": {
"type": "string",
"description": "Host to listen on.",
"default": "localhost"
},
"staticDir": {
"type": "array",
"description": "Directory where to load static files from, array of strings.",
"items": {
"type": "string"
}
},
"configDir": {
"type": "string",
"description": "Directory where to load Storybook configurations from.",
"default": ".storybook"
},
"https": {
"type": "boolean",
"description": "Serve Storybook over HTTPS. Note: You must provide your own certificate information.",
"default": false
},
"sslCa": {
"type": "string",
"description": "Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)."
},
"sslCert": {
"type": "string",
"description": "Provide an SSL certificate. (Required with --https)."
},
"sslKey": {
"type": "string",
"description": "SSL key to use for serving HTTPS."
},
"smokeTest": {
"type": "boolean",
"description": "Exit after successful start.",
"default": false
},
"ci": {
"type": "boolean",
"description": "CI mode (skip interactive prompts, don't open browser).",
"default": false
},
"quiet": {
"type": "boolean",
"description": "Suppress verbose build output.",
"default": false
},
"docs": {
"type": "boolean",
"description": "Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation.",
"default": false
}
},
"additionalProperties": false,
"required": ["browserTarget"]
}
14 changes: 14 additions & 0 deletions app/angular/standalone.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CLIOptions, LoadOptions, BuilderOptions } from '@storybook/core-common';

export type StandaloneOptions = Partial<
CLIOptions &
LoadOptions &
BuilderOptions & {
mode?: 'static' | 'dev';
angularBrowserTarget: string;
}
>;

declare module '@storybook/angular/standalone' {
export default function buildStandalone(options: StandaloneOptions): Promise<unknown>;
}
3 changes: 2 additions & 1 deletion app/angular/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"types": ["webpack-env", "node"],
"rootDir": "./src",
"resolveJsonModule": true
}
},
"include": ["src/**/*", "src/**/*.json"]
}
15 changes: 15 additions & 0 deletions examples/angular-cli/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@
"scripts": [],
"assets": ["src/favicon.ico", "src/assets"]
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"browserTarget": "angular-cli:build",
"port": 4400,
"staticDir": ["src/assets"]
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"browserTarget": "angular-cli:build",
"staticDir": ["src/assets"]
}
}
}
},
Expand Down

0 comments on commit 4e57f61

Please sign in to comment.