Skip to content

Commit 2a43712

Browse files
authored
Merge pull request #32175 from storybookjs/version-patch-from-9.1.0
Release: Patch 9.1.1
2 parents 073a65a + 8fe5aa6 commit 2a43712

File tree

36 files changed

+554
-178
lines changed

36 files changed

+554
-178
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 9.1.1
2+
3+
- CLI: Fix throwing in readonly environments - [#31785](https://github.com/storybookjs/storybook/pull/31785), thanks @JReinhold!
4+
- Onboarding: Tweak referral wording in survey - [#32185](https://github.com/storybookjs/storybook/pull/32185), thanks @shilman!
5+
- Telemetry: Send index stats on dev exit - [#32168](https://github.com/storybookjs/storybook/pull/32168), thanks @shilman!
6+
17
## 9.1.0
28

39
Storybook 9.1 is packed with new features and improvements to enhance accessibility, streamline testing, and make your development workflow even smoother!

code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export const IntentSurvey = ({
111111
},
112112
},
113113
referrer: {
114-
label: 'How did you learn about Storybook?',
114+
label: 'How did you discover Storybook?',
115115
type: 'select',
116116
required: true,
117117
options: shuffleObject({

code/addons/vitest/src/postinstall-logger.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { isCI } from 'storybook/internal/common';
12
import { colors, logger } from 'storybook/internal/node-logger';
23

3-
const fancy =
4-
process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color';
4+
const fancy = process.platform !== 'win32' || isCI() || process.env.TERM === 'xterm-256color';
55

66
export const step = colors.gray('›');
77
export const info = colors.blue(fancy ? 'ℹ' : 'i');

code/addons/vitest/src/postinstall.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
extractProperFrameworkName,
99
formatFileContent,
1010
getProjectRoot,
11+
isCI,
1112
loadAllPresets,
1213
loadMainConfig,
1314
scanAndTransformFiles,
@@ -79,7 +80,7 @@ export default async function postInstall(options: PostinstallOptions) {
7980

8081
const hasCustomWebpackConfig = !!config.getFieldNode(['webpackFinal']);
8182

82-
const isInteractive = process.stdout.isTTY && !process.env.CI;
83+
const isInteractive = process.stdout.isTTY && !isCI();
8384

8485
if (info.frameworkPackageName === '@storybook/nextjs' && !hasCustomWebpackConfig) {
8586
const out =

code/addons/vitest/src/vitest-plugin/index.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
DEFAULT_FILES_PATTERN,
99
getInterpretedFile,
1010
normalizeStories,
11+
optionalEnvToBoolean,
1112
resolvePathInStorybookCache,
1213
validateConfigurationFiles,
1314
} from 'storybook/internal/common';
@@ -123,14 +124,18 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
123124
},
124125
} as InternalOptions;
125126

126-
if (process.env.DEBUG) {
127+
if (optionalEnvToBoolean(process.env.DEBUG)) {
127128
finalOptions.debug = true;
128129
}
129130

130131
// To be accessed by the global setup file
131132
process.env.__STORYBOOK_URL__ = finalOptions.storybookUrl;
132133
process.env.__STORYBOOK_SCRIPT__ = finalOptions.storybookScript;
133134

135+
// We signal the test runner that we are not running it via Storybook
136+
// We are overriding the environment variable to 'true' if vitest runs via @storybook/addon-vitest's backend
137+
const isVitestStorybook = optionalEnvToBoolean(process.env.VITEST_STORYBOOK);
138+
134139
const directories = {
135140
configDir: finalOptions.configDir,
136141
workingDir: WORKING_DIR,
@@ -212,10 +217,6 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
212217
// plugin.name?.startsWith('vitest:browser')
213218
// )
214219

215-
// We signal the test runner that we are not running it via Storybook
216-
// We are overriding the environment variable to 'true' if vitest runs via @storybook/addon-vitest's backend
217-
const vitestStorybook = process.env.VITEST_STORYBOOK ?? 'false';
218-
219220
const testConfig = nonMutableInputConfig.test;
220221
finalOptions.vitestRoot =
221222
testConfig?.dir || testConfig?.root || nonMutableInputConfig.root || process.cwd();
@@ -260,7 +261,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
260261
// To be accessed by the setup file
261262
__STORYBOOK_URL__: finalOptions.storybookUrl,
262263

263-
VITEST_STORYBOOK: vitestStorybook,
264+
VITEST_STORYBOOK: isVitestStorybook ? 'true' : 'false',
264265
__VITEST_INCLUDE_TAGS__: finalOptions.tags.include.join(','),
265266
__VITEST_EXCLUDE_TAGS__: finalOptions.tags.exclude.join(','),
266267
__VITEST_SKIP_TAGS__: finalOptions.tags.skip.join(','),
@@ -288,9 +289,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
288289
getInitialGlobals: () => {
289290
const envConfig = JSON.parse(process.env.VITEST_STORYBOOK_CONFIG ?? '{}');
290291

291-
const shouldRunA11yTests = process.env.VITEST_STORYBOOK
292-
? (envConfig.a11y ?? false)
293-
: true;
292+
const shouldRunA11yTests = isVitestStorybook ? (envConfig.a11y ?? false) : true;
294293

295294
return {
296295
a11y: {
@@ -373,10 +372,10 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
373372
configureVitest(context) {
374373
context.vitest.config.coverage.exclude.push('storybook-static');
375374

376-
const disableTelemetryVar =
377-
process.env.STORYBOOK_DISABLE_TELEMETRY &&
378-
process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false';
379-
if (!core?.disableTelemetry && !disableTelemetryVar) {
375+
if (
376+
!core?.disableTelemetry &&
377+
!optionalEnvToBoolean(process.env.STORYBOOK_DISABLE_TELEMETRY)
378+
) {
380379
// NOTE: we start telemetry immediately but do not wait on it. Typically it should complete
381380
// before the tests do. If not we may miss the event, we are OK with that.
382381
telemetry(
@@ -410,7 +409,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
410409
}
411410
},
412411
async transform(code, id) {
413-
if (process.env.VITEST !== 'true') {
412+
if (!optionalEnvToBoolean(process.env.VITEST)) {
414413
return code;
415414
}
416415

@@ -434,7 +433,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
434433
// When running tests via the Storybook UI, we need
435434
// to find the right project to run, thus we override
436435
// with a unique identifier using the path to the config dir
437-
if (process.env.VITEST_STORYBOOK) {
436+
if (isVitestStorybook) {
438437
const projectName = `storybook:${normalize(finalOptions.configDir)}`;
439438
plugins.push({
440439
name: 'storybook:workspace-name-override',

code/core/src/cli/bin/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getEnvConfig, parseList } from 'storybook/internal/common';
1+
import { getEnvConfig, optionalEnvToBoolean, parseList } from 'storybook/internal/common';
22
import { logTracker, logger } from 'storybook/internal/node-logger';
33
import { addToGlobalContext } from 'storybook/internal/telemetry';
44

@@ -22,8 +22,7 @@ const command = (name: string) =>
2222
.option(
2323
'--disable-telemetry',
2424
'Disable sending telemetry data',
25-
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
26-
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
25+
optionalEnvToBoolean(process.env.STORYBOOK_DISABLE_TELEMETRY)
2726
)
2827
.option('--debug', 'Get more logs in debug mode', false)
2928
.option('--enable-crash-reports', 'Enable sending crash reports to telemetry data')
@@ -151,7 +150,7 @@ command('build')
151150
await build({
152151
...options,
153152
packageJson: pkg,
154-
test: !!options.test || process.env.SB_TESTBUILD === 'true',
153+
test: !!options.test || optionalEnvToBoolean(process.env.SB_TESTBUILD),
155154
}).catch(() => process.exit(1));
156155
});
157156

code/core/src/cli/globalSettings.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,22 @@ describe('Settings', () => {
8585
);
8686
});
8787

88-
it('throws error if write fails', async () => {
88+
it('logs warning if write fails', async () => {
8989
vi.mocked(fs.writeFile).mockRejectedValue(new Error('Write error'));
9090

91-
await expect(settings.save()).rejects.toThrow('Unable to save global settings');
91+
await expect(settings.save()).resolves.toBeUndefined();
92+
expect(console.warn).toHaveBeenCalledWith(
93+
'Unable to save global settings file to /test/settings.json\nReason: Write error'
94+
);
9295
});
9396

94-
it('throws error if directory creation fails', async () => {
97+
it('logs warning if directory creation fails', async () => {
9598
vi.mocked(fs.mkdir).mockRejectedValue(new Error('Directory creation error'));
9699

97-
await expect(settings.save()).rejects.toThrow('Unable to save global settings');
100+
await expect(settings.save()).resolves.toBeUndefined();
101+
expect(console.warn).toHaveBeenCalledWith(
102+
'Unable to save global settings file to /test/settings.json\nReason: Directory creation error'
103+
);
98104
});
99105
});
100106
});

code/core/src/cli/globalSettings.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import fs from 'node:fs/promises';
22
import { homedir } from 'node:os';
33
import { dirname, join } from 'node:path';
44

5+
import { dedent } from 'ts-dedent';
56
import { z } from 'zod';
67

7-
import { SavingGlobalSettingsFileError } from '../server-errors';
8-
98
const DEFAULT_SETTINGS_PATH = join(homedir(), '.storybook', 'settings.json');
109

1110
const VERSION = 1;
@@ -71,10 +70,9 @@ export class Settings {
7170
await fs.mkdir(dirname(this.filePath), { recursive: true });
7271
await fs.writeFile(this.filePath, JSON.stringify(this.value, null, 2));
7372
} catch (err) {
74-
throw new SavingGlobalSettingsFileError({
75-
filePath: this.filePath,
76-
error: err,
77-
});
73+
console.warn(dedent`
74+
Unable to save global settings file to ${this.filePath}
75+
${err && `Reason: ${(err as Error).message ?? err}`}`);
7876
}
7977
}
8078
}

code/core/src/common/utils/envs.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
// @ts-ignore - Needed for Angular sandbox running without --no-link option. Do NOT convert to @ts-expect-error!
3-
import { getEnvironment } from 'lazy-universal-dotenv';
4-
53
import { nodePathsToArray } from './paths';
64

75
// Load environment variables starts with STORYBOOK_ to the client side.
86

9-
export function loadEnvs(options: { production?: boolean } = {}): {
7+
export async function loadEnvs(options: { production?: boolean } = {}): Promise<{
108
stringified: Record<string, string>;
119
raw: Record<string, string>;
12-
} {
10+
}> {
11+
const { getEnvironment } = await import('lazy-universal-dotenv');
1312
const defaultNodeEnv = options.production ? 'production' : 'development';
1413

1514
const env: Record<string, string | undefined> = {
@@ -67,3 +66,26 @@ export const stringifyProcessEnvs = (raw: Record<string, string>): Record<string
6766
// envs['process.env'] = JSON.stringify(raw);
6867
return envs;
6968
};
69+
70+
export const optionalEnvToBoolean = (input: string | undefined): boolean | undefined => {
71+
if (input === undefined) {
72+
return undefined;
73+
}
74+
if (input.toUpperCase() === 'FALSE' || input === '0') {
75+
return false;
76+
}
77+
if (input.toUpperCase() === 'TRUE' || input === '1') {
78+
return true;
79+
}
80+
return Boolean(input);
81+
};
82+
83+
/**
84+
* Consistently determine if we are in a CI environment
85+
*
86+
* Doing Boolean(process.env.CI) or !process.env.CI is not enough, because users might set CI=false
87+
* or CI=0, which would be truthy, and thus return true in those cases.
88+
*/
89+
export function isCI(): boolean | undefined {
90+
return optionalEnvToBoolean(process.env.CI);
91+
}

code/core/src/core-server/dev-server.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import compression from '@polka/compression';
77
import polka from 'polka';
88
import invariant from 'tiny-invariant';
99

10+
import { telemetry } from '../telemetry';
1011
import type { StoryIndexGenerator } from './utils/StoryIndexGenerator';
1112
import { doTelemetry } from './utils/doTelemetry';
1213
import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders';
@@ -19,6 +20,7 @@ import { openInBrowser } from './utils/open-in-browser';
1920
import { getServerAddresses } from './utils/server-address';
2021
import { getServer } from './utils/server-init';
2122
import { useStatics } from './utils/server-statics';
23+
import { summarizeIndex } from './utils/summarizeIndex';
2224

2325
export async function storybookDevServer(options: Options) {
2426
const [server, core] = await Promise.all([getServer(options), options.presets.apply('core')]);
@@ -130,5 +132,26 @@ export async function storybookDevServer(options: Options) {
130132
// Now the preview has successfully started, we can count this as a 'dev' event.
131133
doTelemetry(app, core, initializedStoryIndexGenerator, options);
132134

135+
async function cancelTelemetry() {
136+
const payload = { eventType: 'dev' };
137+
try {
138+
const generator = await initializedStoryIndexGenerator;
139+
const indexAndStats = await generator?.getIndexAndStats();
140+
// compute stats so we can get more accurate story counts
141+
if (indexAndStats) {
142+
Object.assign(payload, {
143+
storyIndex: summarizeIndex(indexAndStats.storyIndex),
144+
storyStats: indexAndStats.stats,
145+
});
146+
}
147+
} catch (err) {}
148+
await telemetry('canceled', payload, { immediate: true });
149+
process.exit(0);
150+
}
151+
152+
if (!core?.disableTelemetry) {
153+
process.on('SIGINT', cancelTelemetry);
154+
}
155+
133156
return { previewResult, managerResult, address, networkAddress };
134157
}

0 commit comments

Comments
 (0)