From bf3ab4f6dc1fa466e7e6bd31e2b8b1061c52b5b9 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Mon, 21 Oct 2024 20:09:59 +0200 Subject: [PATCH 01/11] Avoid broken repository state when tsup fails --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bcef52328..ac40c989f 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "isChromatic.d.ts" ], "scripts": { - "build": "clean-package && tsup && clean-package restore", + "build": "clean-package ; tsup ; clean-package restore", "build-storybook": "storybook build --stats-json", "build-test-storybook": "cross-env SMOKE_TEST=true storybook build -o test-storybook --stats-json", "build-subdir": "cd subdir ; yarn build ; cd ..", From 279e36c1c0ee83791bdea4b9da8ca29e0237c926 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Mon, 21 Oct 2024 21:59:35 +0200 Subject: [PATCH 02/11] Add support for logLevel and logPrefix flags, and use timestamp as default prefix --- action-src/main.ts | 4 ++++ action.yml | 6 +++++ node-src/index.ts | 9 +++++--- node-src/lib/log.ts | 55 +++++++++++++++++++++++++++------------------ node-src/types.ts | 2 ++ 5 files changed, 51 insertions(+), 25 deletions(-) diff --git a/action-src/main.ts b/action-src/main.ts index 01babe640..5c188267c 100755 --- a/action-src/main.ts +++ b/action-src/main.ts @@ -116,6 +116,8 @@ async function run() { const forceRebuild = getInput('forceRebuild'); const ignoreLastBuildOnBranch = getInput('ignoreLastBuildOnBranch'); const logFile = getInput('logFile'); + const logLevel = getInput('logLevel'); + const logPrefix = getInput('logPrefix'); const only = getInput('only'); const onlyChanged = getInput('onlyChanged'); const onlyStoryFiles = getMultilineInput('onlyStoryFiles'); @@ -169,6 +171,8 @@ async function run() { ignoreLastBuildOnBranch: maybe(ignoreLastBuildOnBranch), interactive: false, logFile: maybe(logFile), + logLevel: maybe(logLevel), + logPrefix: maybe(logPrefix), only: maybe(only), onlyChanged: maybe(onlyChanged), onlyStoryFiles: maybe(onlyStoryFiles), diff --git a/action.yml b/action.yml index f882aa1c6..629f84809 100755 --- a/action.yml +++ b/action.yml @@ -59,6 +59,12 @@ inputs: logFile: description: 'Write CLI logs to a file' required: false + logLevel: + description: 'One of: silent, error, warn, info, debug (default: info)' + required: false + logPrefix: + description: 'Custom prefix for log messages (default: current timestamp)' + required: false only: description: 'Deprecated, replaced by onlyStoryNames' required: false diff --git a/node-src/index.ts b/node-src/index.ts index 313013560..9b428e290 100644 --- a/node-src/index.ts +++ b/node-src/index.ts @@ -105,10 +105,14 @@ export async function run({ flags?: Flags; options?: Partial; }): Promise> { + const config = { + ...parseArguments(argv), + ...(flags && { flags }), + }; const { sessionId = uuid(), env: environment = getEnvironment(), - log = createLogger(), + log = createLogger(config.flags), } = extraOptions || {}; const packageInfo = await readPkgUp({ cwd: process.cwd() }); @@ -119,8 +123,7 @@ export async function run({ const { path: packagePath, packageJson } = packageInfo; const ctx: InitialContext = { - ...parseArguments(argv), - ...(flags && { flags }), + ...config, ...(extraOptions && { extraOptions }), packagePath, packageJson, diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index 7ffaabedf..d2f7b7ee8 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -5,13 +5,14 @@ import stripAnsi from 'strip-ansi'; import { format } from 'util'; import { errorSerializer } from './logSerializers'; +import { Flags, Options } from '../types'; interface QueueMessage { type: LogType; messages: string[]; } -const { DISABLE_LOGGING, LOG_LEVEL = '' } = process.env; +const { DISABLE_LOGGING, LOG_LEVEL = '', LOG_PREFIX } = process.env; const LOG_LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 }; const DEFAULT_LEVEL = 'info'; @@ -25,26 +26,34 @@ const logInteractive = (args: any[]): string[] => .map((argument) => (argument && argument.message) || argument) .filter((argument) => typeof argument === 'string'); -// Strips ANSI codes from messages and stringifies metadata to JSON +// Stringifies metadata to JSON const logVerbose = (type: string, args: any[]) => { const stringify = type === 'error' ? (err: any) => JSON.stringify(errorSerializer(err)) : JSON.stringify; - return args.map((argument) => - typeof argument === 'string' ? stripAnsi(argument) : stringify(argument) - ); + return args.map((argument) => (typeof argument === 'string' ? argument : stringify(argument))); }; -const withTime = (messages: string[], color = false) => { - if (messages.every((message) => /^\s*$/.test(message))) return messages; - let time = new Date().toISOString().slice(11, 23); - if (color) time = chalk.dim(time); - return [ - time + ' ', - ...messages.map((message) => - typeof message === 'string' ? message.replaceAll('\n', `\n `) : message - ), - ]; -}; +const createPrefixer = + (color = false, prefix?: string) => + (messages: string[]) => { + // Ignore empty log lines + if (messages.every((message) => /^\s*$/.test(message))) return messages; + + // Use a timestamp as default prefix + const pre = prefix ?? chalk.dim(new Date().toISOString().slice(11, 23)); + if (pre === '') return color ? messages : messages.map(stripAnsi); + + // Pad lines with spaces to align with the prefix + const padding = ' '.repeat(stripAnsi(pre).length + 1); + return [ + color ? pre : stripAnsi(pre), + ...messages.map((message) => { + if (typeof message !== 'string') return message; + const string = color ? message : stripAnsi(message); + return string.replaceAll('\n', `\n${padding}`); + }), + ]; + }; type LogType = 'error' | 'warn' | 'info' | 'debug'; type LogFunction = (...args: any[]) => void; @@ -93,12 +102,14 @@ const fileLogger = { }, }; -export const createLogger = () => { - let level = (LOG_LEVEL.toLowerCase() as keyof typeof LOG_LEVELS) || DEFAULT_LEVEL; +export const createLogger = (flags: Flags) => { + let level = flags.logLevel || (LOG_LEVEL.toLowerCase() as Flags['logLevel']) || DEFAULT_LEVEL; if (DISABLE_LOGGING === 'true') level = 'silent'; - const args = new Set(process.argv.slice(2)); - let interactive = !args.has('--debug') && !args.has('--no-interactive'); + let logPrefixer = createPrefixer(true, flags.logPrefix || LOG_PREFIX); + let filePrefixer = createPrefixer(false, flags.logPrefix || LOG_PREFIX); + + let interactive = flags.interactive && !flags.debug; let enqueue = false; const queue: QueueMessage[] = []; @@ -108,10 +119,10 @@ export const createLogger = () => { if (LOG_LEVELS[level] < LOG_LEVELS[type]) return; const logs = logVerbose(type, args); - fileLogger.append(...withTime(logs)); + fileLogger.append(...filePrefixer(logs)); if (logFileOnly) return; - const messages = interactive ? logInteractive(args) : withTime(logs, true); + const messages = interactive ? logInteractive(args) : logPrefixer(logs); if (messages.length === 0) return; // Queue up the logs or print them right away diff --git a/node-src/types.ts b/node-src/types.ts index ce146ba98..9de5c253b 100644 --- a/node-src/types.ts +++ b/node-src/types.ts @@ -51,6 +51,8 @@ export interface Flags { junitReport?: string; list?: boolean; logFile?: string; + logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug'; + logPrefix?: string; storybookLogFile?: string; traceChanged?: string; uploadMetadata?: boolean; From f204f51f20fae9c4b5fa0b88b833a80f5b38d446 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Mon, 21 Oct 2024 22:08:07 +0200 Subject: [PATCH 03/11] Support logLevel and logPrefix through options as well --- node-src/index.ts | 4 ++-- node-src/lib/log.ts | 15 +++++++++------ node-src/types.ts | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/node-src/index.ts b/node-src/index.ts index 9b428e290..142cf6cd5 100644 --- a/node-src/index.ts +++ b/node-src/index.ts @@ -108,11 +108,12 @@ export async function run({ const config = { ...parseArguments(argv), ...(flags && { flags }), + ...(extraOptions && { extraOptions }), }; const { sessionId = uuid(), env: environment = getEnvironment(), - log = createLogger(config.flags), + log = createLogger(config.flags, config.extraOptions), } = extraOptions || {}; const packageInfo = await readPkgUp({ cwd: process.cwd() }); @@ -124,7 +125,6 @@ export async function run({ const { path: packagePath, packageJson } = packageInfo; const ctx: InitialContext = { ...config, - ...(extraOptions && { extraOptions }), packagePath, packageJson, env: environment, diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index d2f7b7ee8..b024c5cf3 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -102,14 +102,17 @@ const fileLogger = { }, }; -export const createLogger = (flags: Flags) => { - let level = flags.logLevel || (LOG_LEVEL.toLowerCase() as Flags['logLevel']) || DEFAULT_LEVEL; +export const createLogger = (flags: Flags, options?: Partial) => { + let level = + options?.logLevel || + flags.logLevel || + (LOG_LEVEL.toLowerCase() as Flags['logLevel']) || + DEFAULT_LEVEL; if (DISABLE_LOGGING === 'true') level = 'silent'; - let logPrefixer = createPrefixer(true, flags.logPrefix || LOG_PREFIX); - let filePrefixer = createPrefixer(false, flags.logPrefix || LOG_PREFIX); - - let interactive = flags.interactive && !flags.debug; + let logPrefixer = createPrefixer(true, options?.logPrefix || flags.logPrefix || LOG_PREFIX); + let filePrefixer = createPrefixer(false, options?.logPrefix || flags.logPrefix || LOG_PREFIX); + let interactive = (options?.interactive || flags.interactive) && !(options?.debug || flags.debug); let enqueue = false; const queue: QueueMessage[] = []; diff --git a/node-src/types.ts b/node-src/types.ts index 9de5c253b..ff6ea80d0 100644 --- a/node-src/types.ts +++ b/node-src/types.ts @@ -71,6 +71,8 @@ export interface Options extends Configuration { configFile?: Flags['configFile']; logFile?: Flags['logFile']; + logLevel?: Flags['logLevel']; + logPrefix?: Flags['logPrefix']; onlyChanged: boolean | string; onlyStoryFiles: Flags['onlyStoryFiles']; onlyStoryNames: Flags['onlyStoryNames']; From a6a6d2d48aff3e112c8af1e77b36774ff6df92bd Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 22 Oct 2024 09:05:17 +0200 Subject: [PATCH 04/11] Handle lint errors --- node-src/lib/log.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index b024c5cf3..31750b24f 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -4,8 +4,8 @@ import { createWriteStream, rm } from 'fs'; import stripAnsi from 'strip-ansi'; import { format } from 'util'; -import { errorSerializer } from './logSerializers'; import { Flags, Options } from '../types'; +import { errorSerializer } from './logSerializers'; interface QueueMessage { type: LogType; @@ -41,7 +41,7 @@ const createPrefixer = // Use a timestamp as default prefix const pre = prefix ?? chalk.dim(new Date().toISOString().slice(11, 23)); - if (pre === '') return color ? messages : messages.map(stripAnsi); + if (pre === '') return color ? messages : messages.map((message) => stripAnsi(message)); // Pad lines with spaces to align with the prefix const padding = ' '.repeat(stripAnsi(pre).length + 1); @@ -102,20 +102,24 @@ const fileLogger = { }, }; +/* eslint-disable-next-line complexity */ export const createLogger = (flags: Flags, options?: Partial) => { let level = options?.logLevel || flags.logLevel || (LOG_LEVEL.toLowerCase() as Flags['logLevel']) || DEFAULT_LEVEL; - if (DISABLE_LOGGING === 'true') level = 'silent'; - let logPrefixer = createPrefixer(true, options?.logPrefix || flags.logPrefix || LOG_PREFIX); - let filePrefixer = createPrefixer(false, options?.logPrefix || flags.logPrefix || LOG_PREFIX); + if (DISABLE_LOGGING === 'true') { + level = 'silent'; + } + let interactive = (options?.interactive || flags.interactive) && !(options?.debug || flags.debug); let enqueue = false; const queue: QueueMessage[] = []; + const logPrefixer = createPrefixer(true, options?.logPrefix || flags.logPrefix || LOG_PREFIX); + const filePrefixer = createPrefixer(false, options?.logPrefix || flags.logPrefix || LOG_PREFIX); const log = (type: LogType, logFileOnly?: boolean) => (...args: any[]) => { From 5501f851efdb14f4b6b0b3aaadef202277f27d26 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 22 Oct 2024 09:27:06 +0200 Subject: [PATCH 05/11] Use local timezone for timestamps --- node-src/lib/log.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index 31750b24f..907cc78b5 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -33,6 +33,16 @@ const logVerbose = (type: string, args: any[]) => { return args.map((argument) => (typeof argument === 'string' ? argument : stringify(argument))); }; +// Generate a timestamp like "14:30:00.123" in local time +const getTimeString = () => + new Date().toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + fractionalSecondDigits: 3, + hour12: false, + }); + const createPrefixer = (color = false, prefix?: string) => (messages: string[]) => { @@ -40,7 +50,7 @@ const createPrefixer = if (messages.every((message) => /^\s*$/.test(message))) return messages; // Use a timestamp as default prefix - const pre = prefix ?? chalk.dim(new Date().toISOString().slice(11, 23)); + const pre = prefix ?? chalk.dim(getTimeString()); if (pre === '') return color ? messages : messages.map((message) => stripAnsi(message)); // Pad lines with spaces to align with the prefix From 9bd481b059541a745036ea380e23e6ebcba70450 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 22 Oct 2024 12:01:37 +0200 Subject: [PATCH 06/11] Add tests and allow logging numbers --- node-src/lib/log.test.ts | 153 +++++++++++++++++++++++++++++++++++++++ node-src/lib/log.ts | 10 ++- 2 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 node-src/lib/log.test.ts diff --git a/node-src/lib/log.test.ts b/node-src/lib/log.test.ts new file mode 100644 index 000000000..3ea58f90c --- /dev/null +++ b/node-src/lib/log.test.ts @@ -0,0 +1,153 @@ +import { afterEach, beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'; + +import { createLogger } from './log'; + +let consoleError: MockInstance; +let consoleWarn: MockInstance; +let consoleInfo: MockInstance; +let consoleDebug: MockInstance; + +beforeEach(() => { + consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined); + consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + consoleInfo = vi.spyOn(console, 'info').mockImplementation(() => undefined); + consoleDebug = vi.spyOn(console, 'debug').mockImplementation(() => undefined); + + vi.useFakeTimers(); + vi.setSystemTime(new Date().setTime(0)); +}); + +afterEach(() => { + delete process.env.DISABLE_LOGGING; + delete process.env.LOG_LEVEL; + delete process.env.LOG_PREFIX; + + consoleError.mockReset(); + consoleWarn.mockReset(); + consoleInfo.mockReset(); + consoleDebug.mockReset(); + + vi.useRealTimers(); +}); + +const timestamp = expect.stringMatching(/\d\d:\d\d:\d\d.\d\d\d/); + +describe('log prefix', () => { + it('should use the log prefix from environment variables', () => { + process.env.LOG_PREFIX = 'env-prefix'; + const logger = createLogger({}); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith('env-prefix', 'message'); + }); + + it('should use the log prefix from flags', () => { + process.env.LOG_PREFIX = 'env-prefix'; + const logger = createLogger({ logPrefix: 'flag-prefix' }); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith('flag-prefix', 'message'); + }); + + it('should prefer the log prefix from options', () => { + process.env.LOG_PREFIX = 'env-prefix'; + const logger = createLogger({ logPrefix: 'flag-prefix' }, { logPrefix: 'option-prefix' }); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith('option-prefix', 'message'); + }); + + it('should use a timestamp as prefix by default', () => { + const logger = createLogger({}); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith(timestamp, 'message'); + }); + + it('should not use a prefix if set to an empty string', () => { + process.env.LOG_PREFIX = ''; + const logger = createLogger({}); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith('message'); + }); + + it('should not use a prefix in interactive mode', () => { + process.env.LOG_PREFIX = 'env-prefix'; + const logger = createLogger({ interactive: true, logPrefix: 'flag-prefix' }); + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith('message'); + }); +}); + +describe('log levels', () => { + it('should ignore debug messages by default', () => { + const logger = createLogger({}); + + logger.error('error', 1, 2); + expect(consoleError).toHaveBeenCalledWith(timestamp, 'error', '1', '2'); + + logger.warn('warning', 1, 2); + expect(consoleWarn).toHaveBeenCalledWith(timestamp, 'warning', '1', '2'); + + logger.info('message', 1, 2); + expect(consoleInfo).toHaveBeenCalledWith(timestamp, 'message', '1', '2'); + + logger.debug('data', 1, 2); + expect(consoleDebug).not.toHaveBeenCalled(); + }); + + it('should use the log level from environment variables', () => { + process.env.LOG_LEVEL = 'debug'; + const logger = createLogger({}); + + logger.error('error'); + expect(consoleError).toHaveBeenCalledWith(timestamp, 'error'); + + logger.warn('warning'); + expect(consoleWarn).toHaveBeenCalledWith(timestamp, 'warning'); + + logger.info('message'); + expect(consoleInfo).toHaveBeenCalledWith(timestamp, 'message'); + + logger.debug('data'); + expect(consoleDebug).toHaveBeenCalledWith(timestamp, 'data'); + }); + + it('should use the log level from flags', () => { + process.env.LOG_LEVEL = 'debug'; + const logger = createLogger({ logLevel: 'warn' }); + + logger.error('error'); + expect(consoleError).toHaveBeenCalledWith(timestamp, 'error'); + + logger.warn('warning'); + expect(consoleWarn).toHaveBeenCalledWith(timestamp, 'warning'); + + logger.info('message'); + expect(consoleInfo).not.toHaveBeenCalled(); + + logger.debug('data'); + expect(consoleDebug).not.toHaveBeenCalled(); + }); + + it('should prefer the log level from options', () => { + process.env.LOG_LEVEL = 'debug'; + const logger = createLogger({ logLevel: 'warn' }, { logLevel: 'error' }); + + logger.error('error'); + expect(consoleError).toHaveBeenCalledWith(timestamp, 'error'); + + logger.warn('warning'); + expect(consoleWarn).not.toHaveBeenCalled(); + + logger.info('message'); + expect(consoleInfo).not.toHaveBeenCalled(); + + logger.debug('data'); + expect(consoleDebug).not.toHaveBeenCalled(); + }); + + it('should respect DISABLE_LOGGING regardless of logLevel flag or option', () => { + process.env.DISABLE_LOGGING = 'true'; + process.env.LOG_LEVEL = 'debug'; + const logger = createLogger({ logLevel: 'warn' }, { logLevel: 'error' }); + logger.error('error'); + expect(consoleError).not.toHaveBeenCalled(); + }); +}); diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index 907cc78b5..ad9152c46 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -12,7 +12,6 @@ interface QueueMessage { messages: string[]; } -const { DISABLE_LOGGING, LOG_LEVEL = '', LOG_PREFIX } = process.env; const LOG_LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 }; const DEFAULT_LEVEL = 'info'; @@ -24,13 +23,18 @@ process.on('unhandledRejection', handleRejection); const logInteractive = (args: any[]): string[] => args .map((argument) => (argument && argument.message) || argument) + // .map((argument) => (typeof argument === 'number' ? String(argument) : argument)) .filter((argument) => typeof argument === 'string'); // Stringifies metadata to JSON const logVerbose = (type: string, args: any[]) => { const stringify = type === 'error' ? (err: any) => JSON.stringify(errorSerializer(err)) : JSON.stringify; - return args.map((argument) => (typeof argument === 'string' ? argument : stringify(argument))); + return args.map((argument) => + typeof argument === 'string' || typeof argument === 'number' + ? String(argument) + : stringify(argument) + ); }; // Generate a timestamp like "14:30:00.123" in local time @@ -114,6 +118,8 @@ const fileLogger = { /* eslint-disable-next-line complexity */ export const createLogger = (flags: Flags, options?: Partial) => { + const { DISABLE_LOGGING, LOG_LEVEL = '', LOG_PREFIX } = process.env; + let level = options?.logLevel || flags.logLevel || From 8e88b0bc34b6a7e71b687c25bd9bcaa3a657f5c0 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 22 Oct 2024 13:10:26 +0200 Subject: [PATCH 07/11] Better stringification --- node-src/lib/log.test.ts | 14 ++++++++++++++ node-src/lib/log.ts | 13 ++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/node-src/lib/log.test.ts b/node-src/lib/log.test.ts index 3ea58f90c..6b01bde8a 100644 --- a/node-src/lib/log.test.ts +++ b/node-src/lib/log.test.ts @@ -151,3 +151,17 @@ describe('log levels', () => { expect(consoleError).not.toHaveBeenCalled(); }); }); + +it('stringifies non-primitive values', () => { + const logger = createLogger({}); + logger.info('message', 1, true, null, undefined, { key: 'value' }); + expect(consoleInfo).toHaveBeenCalledWith( + timestamp, + 'message', + '1', + 'true', + 'null', + undefined, + JSON.stringify({ key: 'value' }) + ); +}); diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index ad9152c46..8e86444b7 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -19,22 +19,21 @@ const DEFAULT_LEVEL = 'info'; const handleRejection = (reason: string) => console.error('Unhandled promise rejection:', reason); process.on('unhandledRejection', handleRejection); +const isPrintable = (value: any, type = typeof value) => + type === 'string' || type === 'number' || type === 'boolean'; + // Omits any JSON metadata, returning only the message string const logInteractive = (args: any[]): string[] => args .map((argument) => (argument && argument.message) || argument) - // .map((argument) => (typeof argument === 'number' ? String(argument) : argument)) - .filter((argument) => typeof argument === 'string'); + .filter((argument) => isPrintable(argument)) + .map(String); // Stringifies metadata to JSON const logVerbose = (type: string, args: any[]) => { const stringify = type === 'error' ? (err: any) => JSON.stringify(errorSerializer(err)) : JSON.stringify; - return args.map((argument) => - typeof argument === 'string' || typeof argument === 'number' - ? String(argument) - : stringify(argument) - ); + return args.map((argument) => (isPrintable(argument) ? String(argument) : stringify(argument))); }; // Generate a timestamp like "14:30:00.123" in local time From f3a71eeef1fdd7f784203929bdf953b83159b2e1 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Wed, 23 Oct 2024 11:13:33 +0200 Subject: [PATCH 08/11] Don't run tsup if clean-package fails --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2c073a86d..15be00f9c 100644 --- a/package.json +++ b/package.json @@ -69,10 +69,10 @@ "isChromatic.d.ts" ], "scripts": { - "build": "clean-package ; tsup ; clean-package restore", + "build": "clean-package && tsup ; clean-package restore", "build-storybook": "storybook build --stats-json", "build-test-storybook": "cross-env SMOKE_TEST=true storybook build -o test-storybook --stats-json", - "build-subdir": "cd subdir ; yarn build ; cd ..", + "build-subdir": "cd subdir && yarn build ; cd ..", "chromatic": "./dist/bin.js", "chromatic-prebuilt": "./dist/bin.js --storybook-build-dir=\"storybook-static\"", "chromatic-staging": "CHROMATIC_INDEX_URL=https://www.staging-chromatic.com ./dist/bin.js", From 7370cf570f317710b18fdd28f6a72120b880c4b4 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Wed, 23 Oct 2024 11:20:52 +0200 Subject: [PATCH 09/11] Add logLevel and logPrefix to parseArguments --- node-src/lib/parseArguments.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node-src/lib/parseArguments.ts b/node-src/lib/parseArguments.ts index 092b4e8da..67289f5bf 100644 --- a/node-src/lib/parseArguments.ts +++ b/node-src/lib/parseArguments.ts @@ -56,6 +56,8 @@ export default function parseArguments(argv: string[]) { --junit-report [filepath] Write build results to a JUnit XML file. {buildNumber} will be replaced with the actual build number. [chromatic-build-{buildNumber}.xml] --list List available stories. This requires running a full build. --log-file [filepath] Write log output to a file. Disable via --no-log-file. [chromatic.log] + --log-level One of "silent", "error", "warn", "info", "debug". Defaults to "info". + --log-prefix Prefix for each log line. Defaults to current timestamp except in interactive mode. Set to "" to disable. --no-file-hashing Disable file hashing. This will cause all files to be uploaded on every build. --no-interactive Don't ask interactive questions about your setup and don't overwrite output. Always true in non-TTY environments. --storybook-log-file [filepath] Write Storybook build output to a file. Disable via --no-storybook-log-file. [storybook-build.log] @@ -113,6 +115,8 @@ export default function parseArguments(argv: string[]) { junitReport: { type: 'string' }, list: { type: 'boolean' }, logFile: { type: 'string' }, + logLevel: { type: 'string', choices: ['silent', 'error', 'warn', 'info', 'debug'] }, + logPrefix: { type: 'string' }, storybookLogFile: { type: 'string' }, traceChanged: { type: 'string' }, uploadMetadata: { type: 'boolean' }, From 2ea671ac66e912b8b17b8b331fdc1a4460ac0fdb Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Wed, 23 Oct 2024 11:23:03 +0200 Subject: [PATCH 10/11] Simplification --- node-src/lib/log.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/node-src/lib/log.ts b/node-src/lib/log.ts index 8e86444b7..ffa999cab 100644 --- a/node-src/lib/log.ts +++ b/node-src/lib/log.ts @@ -19,8 +19,10 @@ const DEFAULT_LEVEL = 'info'; const handleRejection = (reason: string) => console.error('Unhandled promise rejection:', reason); process.on('unhandledRejection', handleRejection); -const isPrintable = (value: any, type = typeof value) => - type === 'string' || type === 'number' || type === 'boolean'; +const isPrintable = (value: any) => { + const type = typeof value; + return type === 'string' || type === 'number' || type === 'boolean'; +}; // Omits any JSON metadata, returning only the message string const logInteractive = (args: any[]): string[] => From 47b32915215ffe70c883fc725ee432f4db4e5a46 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Wed, 23 Oct 2024 16:56:33 +0200 Subject: [PATCH 11/11] Restore original build script behavior --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 15be00f9c..ccd9be094 100644 --- a/package.json +++ b/package.json @@ -69,10 +69,10 @@ "isChromatic.d.ts" ], "scripts": { - "build": "clean-package && tsup ; clean-package restore", + "build": "clean-package && tsup && clean-package restore", "build-storybook": "storybook build --stats-json", "build-test-storybook": "cross-env SMOKE_TEST=true storybook build -o test-storybook --stats-json", - "build-subdir": "cd subdir && yarn build ; cd ..", + "build-subdir": "cd subdir ; yarn build ; cd ..", "chromatic": "./dist/bin.js", "chromatic-prebuilt": "./dist/bin.js --storybook-build-dir=\"storybook-static\"", "chromatic-staging": "CHROMATIC_INDEX_URL=https://www.staging-chromatic.com ./dist/bin.js",