-
Notifications
You must be signed in to change notification settings - Fork 70
/
build.js
88 lines (81 loc) · 2.85 KB
/
build.js
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
import execa from 'execa';
import fs from 'fs';
import path from 'path';
import semver from 'semver';
import tmp from 'tmp-promise';
import { createTask, transitionTo } from '../lib/tasks';
import buildFailed from '../ui/messages/errors/buildFailed';
import { failed, initial, pending, skipped, success } from '../ui/tasks/build';
export const setSourceDir = async (ctx) => {
if (ctx.options.outputDir) {
ctx.sourceDir = ctx.options.outputDir;
} else if (semver.lt(ctx.storybook.version, '5.0.0')) {
// Storybook v4 doesn't support absolute paths like tmp.dir would yield
ctx.sourceDir = 'storybook-static';
} else {
const tmpDir = await tmp.dir({ unsafeCleanup: true, prefix: `chromatic-` });
ctx.sourceDir = tmpDir.path;
}
};
export const setSpawnParams = (ctx) => {
// Run either:
// npm/yarn run scriptName (depending on npm_execpath)
// node path/to/npm.js run scriptName (if npm run via node)
// Based on https://github.com/mysticatea/npm-run-all/blob/52eaf86242ba408dedd015f53ca7ca368f25a026/lib/run-task.js#L156-L174
const npmExecPath = process.env.npm_execpath;
const isJsPath = typeof npmExecPath === 'string' && /\.m?js/.test(path.extname(npmExecPath));
const isYarn = npmExecPath && path.basename(npmExecPath) === 'yarn.js';
ctx.spawnParams = {
command: (isJsPath ? process.execPath : npmExecPath) || 'npm',
clientArgs: isJsPath ? [npmExecPath, 'run'] : ['run', '--silent'],
scriptArgs: [
ctx.options.buildScriptName,
isYarn ? '' : '--',
'--output-dir',
ctx.sourceDir,
].filter(Boolean),
};
};
const timeoutAfter = (ms) =>
new Promise((resolve, reject) => setTimeout(reject, ms, new Error(`Operation timed out`)));
export const buildStorybook = async (ctx) => {
ctx.buildLogFile = path.resolve('./build-storybook.log');
const logFile = fs.createWriteStream(ctx.buildLogFile);
await new Promise((resolve, reject) => {
logFile.on('open', resolve);
logFile.on('error', reject);
});
try {
const { command, clientArgs, scriptArgs } = ctx.spawnParams;
await Promise.race([
execa(command, [...clientArgs, ...scriptArgs], { stdio: [null, logFile, logFile] }),
timeoutAfter(ctx.env.STORYBOOK_BUILD_TIMEOUT),
]);
} catch (e) {
const buildLog = fs.readFileSync(ctx.buildLogFile, 'utf8');
ctx.log.error(buildFailed(ctx, e, buildLog));
ctx.exitCode = 201;
ctx.userError = true;
throw new Error(failed(ctx).output);
} finally {
logFile.end();
}
};
export default createTask({
title: initial.title,
skip: async (ctx) => {
if (ctx.skip) return true;
if (ctx.options.storybookBuildDir) {
ctx.sourceDir = ctx.options.storybookBuildDir;
return skipped(ctx).output;
}
return false;
},
steps: [
setSourceDir,
setSpawnParams,
transitionTo(pending),
buildStorybook,
transitionTo(success, true),
],
});