Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular: Add compodoc to ng builder #15165

Merged
merged 2 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions app/angular/src/builders/build-storybook/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { Architect } from '@angular-devkit/architect';
import { Architect, createBuilder } 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.doMock('@storybook/angular/standalone', () => buildStandaloneMock);

const cpSpawnMock = {
spawn: jest.fn().mockImplementation(() => ({
stdout: { on: () => {} },
stderr: { on: () => {} },
on: (_event: string, cb: any) => cb(0),
})),
};
jest.doMock('child_process', () => cpSpawnMock);
describe('Build Storybook Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;
Expand All @@ -18,12 +25,64 @@ describe('Build Storybook Builder', () => {
architectHost = new TestingArchitectHost();
architect = new Architect(architectHost, registry);

architectHost.addBuilder(
'@angular-devkit/build-angular:browser',
createBuilder(() => {
return { success: true };
})
);
architectHost.addTarget(
{ project: 'angular-cli', target: 'build-2' },
'@angular-devkit/build-angular:browser',
{
outputPath: 'dist/angular-cli',
index: 'src/index.html',
main: 'src/main.ts',
polyfills: 'src/polyfills.ts',
tsConfig: 'src/tsconfig.app.json',
assets: ['src/favicon.ico', 'src/assets'],
styles: ['src/styles.css'],
scripts: [],
}
);

// 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, '../../..'));
});

afterEach(() => {
jest.clearAllMocks();
});

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

const output = await run.result;

await run.stop();

expect(output.success).toBeTruthy();
expect(cpSpawnMock.spawn).not.toHaveBeenCalledWith();
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',
compodoc: false,
compodocArgs: ['-e', 'json'],
});
});

it('should run compodoc', async () => {
const run = await architect.scheduleBuilder('@storybook/angular:build-storybook', {
browserTarget: 'angular-cli:build-2',
});
Expand All @@ -33,6 +92,14 @@ describe('Build Storybook Builder', () => {
await run.stop();

expect(output.success).toBeTruthy();
expect(cpSpawnMock.spawn).toHaveBeenCalledWith('compodoc', [
'-p',
'src/tsconfig.app.json',
'-d',
'',
'-e',
'json',
]);
expect(buildStandaloneMock).toHaveBeenCalledWith({
angularBrowserTarget: 'angular-cli:build-2',
browserTarget: 'angular-cli:build-2',
Expand All @@ -43,6 +110,8 @@ describe('Build Storybook Builder', () => {
outputDir: 'storybook-static',
staticDir: [],
mode: 'static',
compodoc: true,
compodocArgs: ['-e', 'json'],
});
});
});
36 changes: 33 additions & 3 deletions app/angular/src/builders/build-storybook/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import {
BuilderContext,
BuilderOutput,
createBuilder,
targetFromTargetString,
} 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';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { runCompodoc } from '../utils/run-compodoc';

export type StorybookBuilderOptions = JsonObject & {
browserTarget: string;
compodoc: boolean;
compodocArgs: string[];
} & Pick<
// makes sure the option exists
CLIOptions,
Expand All @@ -21,9 +30,17 @@ export default createBuilder(commandBuilder);

function commandBuilder(
options: StorybookBuilderOptions,
_context: BuilderContext
context: BuilderContext
): Observable<StorybookBuilderOutput> {
return of({}).pipe(
return from(setup(options, context)).pipe(
switchMap(({ browserOptions }) =>
options.compodoc
? runCompodoc(
{ compodocArgs: options.compodocArgs, tsconfig: browserOptions.tsConfig },
context
)
: of({})
),
map(() => ({
...options,
angularBrowserTarget: options.browserTarget,
Expand All @@ -35,6 +52,19 @@ function commandBuilder(
);
}

async function setup(options: StorybookBuilderOptions, context: BuilderContext) {
const browserTarget = targetFromTargetString(options.browserTarget);
const browserOptions = await context.validateOptions<JsonObject & BrowserBuilderOptions>(
await context.getTargetOptions(browserTarget),
await context.getBuilderNameForTarget(browserTarget)
);

return {
browserOptions,
browserTarget,
};
}

function runInstance(options: StandaloneOptions) {
return from(buildStandalone(options));
}
13 changes: 13 additions & 0 deletions app/angular/src/builders/build-storybook/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@
"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
},
"compodoc": {
"type": "boolean",
"description": "Execute compodoc before.",
"default": true
},
"compodocArgs": {
"type": "array",
"description": "Compodoc options : https://compodoc.app/guides/options.html. Options `-p` with tsconfig path and `-d` with workspace root is always given.",
"default": ["-e", "json"],
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
Expand Down
78 changes: 76 additions & 2 deletions app/angular/src/builders/start-storybook/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Architect } from '@angular-devkit/architect';
import { Architect, createBuilder } 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.doMock('@storybook/angular/standalone', () => buildStandaloneMock);

const cpSpawnMock = {
spawn: jest.fn().mockImplementation(() => ({
stdout: { on: () => {} },
stderr: { on: () => {} },
on: (_event: string, cb: any) => cb(0),
})),
};
jest.doMock('child_process', () => cpSpawnMock);

describe('Start Storybook Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;
Expand All @@ -18,22 +26,48 @@ describe('Start Storybook Builder', () => {
architectHost = new TestingArchitectHost();
architect = new Architect(architectHost, registry);

architectHost.addBuilder(
'@angular-devkit/build-angular:browser',
createBuilder(() => {
return { success: true };
})
);
architectHost.addTarget(
{ project: 'angular-cli', target: 'build-2' },
'@angular-devkit/build-angular:browser',
{
outputPath: 'dist/angular-cli',
index: 'src/index.html',
main: 'src/main.ts',
polyfills: 'src/polyfills.ts',
tsConfig: 'src/tsconfig.app.json',
assets: ['src/favicon.ico', 'src/assets'],
styles: ['src/styles.css'],
scripts: [],
}
);
// 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, '../../..'));
});

afterEach(() => {
jest.clearAllMocks();
});

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

const output = await run.result;

await run.stop();

expect(output.success).toBeTruthy();
expect(cpSpawnMock.spawn).not.toHaveBeenCalledWith();
expect(buildStandaloneMock).toHaveBeenCalledWith({
angularBrowserTarget: 'angular-cli:build-2',
browserTarget: 'angular-cli:build-2',
Expand All @@ -49,6 +83,46 @@ describe('Start Storybook Builder', () => {
sslCert: undefined,
sslKey: undefined,
staticDir: [],
compodoc: false,
compodocArgs: ['-e', 'json'],
});
});

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

const output = await run.result;

await run.stop();

expect(output.success).toBeTruthy();
expect(cpSpawnMock.spawn).toHaveBeenCalledWith('compodoc', [
'-p',
'src/tsconfig.app.json',
'-d',
'',
'-e',
'json',
]);
expect(buildStandaloneMock).toHaveBeenCalledWith({
angularBrowserTarget: 'angular-cli:build-2',
browserTarget: 'angular-cli:build-2',
ci: false,
configDir: '.storybook',
docs: false,
host: 'localhost',
https: false,
port: 9009,
quiet: false,
smokeTest: false,
sslCa: undefined,
sslCert: undefined,
sslKey: undefined,
staticDir: [],
compodoc: true,
compodocArgs: ['-e', 'json'],
});
});
});
36 changes: 33 additions & 3 deletions app/angular/src/builders/start-storybook/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import {
BuilderContext,
BuilderOutput,
createBuilder,
targetFromTargetString,
} from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
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';
import { runCompodoc } from '../utils/run-compodoc';

export type StorybookBuilderOptions = JsonObject & {
browserTarget: string;
compodoc: boolean;
compodocArgs: string[];
} & Pick<
// makes sure the option exists
CLIOptions,
Expand All @@ -32,9 +41,17 @@ export default createBuilder(commandBuilder);

function commandBuilder(
options: StorybookBuilderOptions,
_context: BuilderContext
context: BuilderContext
): Observable<StorybookBuilderOutput> {
return of({}).pipe(
return from(setup(options, context)).pipe(
switchMap(({ browserOptions }) =>
options.compodoc
? runCompodoc(
{ compodocArgs: options.compodocArgs, tsconfig: browserOptions.tsConfig },
context
)
: of({})
),
map(() => ({
...options,
angularBrowserTarget: options.browserTarget,
Expand All @@ -46,6 +63,19 @@ function commandBuilder(
);
}

async function setup(options: StorybookBuilderOptions, context: BuilderContext) {
const browserTarget = targetFromTargetString(options.browserTarget);
const browserOptions = await context.validateOptions<JsonObject & BrowserBuilderOptions>(
await context.getTargetOptions(browserTarget),
await context.getBuilderNameForTarget(browserTarget)
);

return {
browserOptions,
browserTarget,
};
}

function runInstance(options: StandaloneOptions) {
return from(buildStandalone(options));
}
13 changes: 13 additions & 0 deletions app/angular/src/builders/start-storybook/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@
"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
},
"compodoc": {
"type": "boolean",
"description": "Execute compodoc before.",
"default": true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because is added by default with sb init 🤷‍♂️
but if the user does not have compodoc it is necessary to manually change to false
I don't know if this is a good situation 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThibaudAV#1582 does compodoc get installed by default? if so, then we should enable by default. if not, then we should disable by default. open to your opinions, but maybe we will move away from compodoc in the future as requested by some users. but for now i think we do what's consistent with the CLI

},
"compodocArgs": {
"type": "array",
"description": "Compodoc options : https://compodoc.app/guides/options.html. Options `-p` with tsconfig path and `-d` with workspace root is always given.",
"default": ["-e", "json"],
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
Expand Down
Loading