-
Notifications
You must be signed in to change notification settings - Fork 88
/
storybook-connection.ts
142 lines (132 loc) · 3.49 KB
/
storybook-connection.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import * as cp from 'child_process';
import waitOn = require('wait-on');
import { StorybookServerTimeoutError, InvalidUrlError } from './errors';
import { Logger } from './logger';
function waitServer(url: string, timeout: number) {
if (!url.startsWith('http')) {
throw new InvalidUrlError(url);
}
const resource = url.startsWith('https') ? url.replace(/^https/, 'https-get') : url.replace(/^http/, 'http-get');
return new Promise<void>((resolve, reject) => {
waitOn({ resources: [resource], timeout }, err => {
if (err) {
if (err.message === 'Timeout') {
return reject(new StorybookServerTimeoutError(timeout));
}
return reject(err);
}
resolve();
});
});
}
export interface StorybookConnectionOptions {
storybookUrl: string;
serverCmd?: string;
serverTimeout?: number;
}
export type StorybookConnectionStatus = 'CONNECTED' | 'CONNECTING' | 'DISCONNECTED';
/**
*
* Represents a connection to Storybook server
*
* @example
*
* ```ts
* const connection = new StorybookConnection({ storybookUrl: 'http://localhost:9009' });
* await connection.connect();
* ```
*
* You can boot Storybook server via `serverCmd`
*
* ```ts
* const connection = new StorybookConnection({
* storybookUrl: 'http://localhost:9009',
* serverCmd: 'start-storybook -p 9009',
* });
* await connection.connect();
* ```
*
**/
export class StorybookConnection {
static spawnCmd(command: string, options?: cp.SpawnOptions) {
const opt: cp.SpawnOptions = {
...(options || {}),
shell: true,
};
const [cmd, ...args] = command.split(/\s+/);
return cp.spawn(cmd, args, opt);
}
private proc?: cp.ChildProcess;
private _status: StorybookConnectionStatus = 'DISCONNECTED';
/**
*
* @param opt Options for construction
* @param logger Logger instance
*
**/
constructor(
private opt: StorybookConnectionOptions,
private logger: Logger = new Logger('silent'),
) {}
/**
*
* @returns URL of Storybook server connecting
*
**/
get url() {
return this.opt.storybookUrl;
}
/**
*
* @returns {@link StorybookConnectionStatus}
*
**/
get status() {
return this._status;
}
/**
*
* Connect Storybook server
*
* @returns Promise of the connection that resolves after connected
*
* @remarks
* If the connection has `serverCmd`, this method boots the server as a child process.
*
**/
async connect() {
this._status = 'CONNECTING';
this.logger.log(`Wait for connecting storybook server ${this.logger.color.green(this.opt.storybookUrl)}.`);
if (this.opt.serverCmd) {
const stdio = this.logger.level === 'verbose' ? [0, 1, 2] : [];
this.proc = StorybookConnection.spawnCmd(this.opt.serverCmd, { stdio });
this.logger.debug('Server process created', this.proc.pid);
}
await waitServer(this.opt.storybookUrl, this.opt.serverTimeout || 10_000);
if (this.opt.serverCmd) {
this.logger.debug('Storybook server started');
} else {
this.logger.debug('Found Storybook server');
}
this._status = 'CONNECTED';
return this;
}
/**
*
* Disconnect to the Storybook server.
*
* @remarks
* If the connection has `serverCmd`, this method shutdowns it.
*
**/
async disconnect() {
if (!this.proc) return;
try {
this.logger.debug('Shutdown storybook server', this.proc.pid);
this.proc.kill('SIGINT');
} catch (e) {
// nothing todo
}
this._status = 'DISCONNECTED';
}
}