Skip to content
Draft
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
17 changes: 6 additions & 11 deletions node-src/lib/turbosnap/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,10 @@ describe('traceChangedFiles', () => {
http,
options: {},
sourceDir: '/static/',
fileInfo: { statsPath: '/static/preview-stats.json' },
git: { changedFiles: ['./example.js'] },
turboSnap: {},
} as any;
const onlyStoryFiles = await traceChangedFiles(ctx);
const onlyStoryFiles = await traceChangedFiles(ctx, '/static/preview-stats.json');

expect(onlyStoryFiles).toStrictEqual(deps);
expect(findChangedDependencies).not.toHaveBeenCalled();
Expand All @@ -74,11 +73,10 @@ describe('traceChangedFiles', () => {
http,
options: {},
sourceDir: '/static/',
fileInfo: { statsPath: '/static/preview-stats.json' },
git: { changedFiles: ['./example.js', './package.json'], packageMetadataChanges },
turboSnap: {},
} as any;
await traceChangedFiles(ctx);
await traceChangedFiles(ctx, '/static/preview-stats.json');

expect(ctx.turboSnap.bailReason).toEqual({ changedPackageFiles: ['./package.json'] });
expect(findChangedPackageFiles).toHaveBeenCalledWith(ctx, packageMetadataChanges);
Expand All @@ -96,11 +94,10 @@ describe('traceChangedFiles', () => {
http,
options: {},
sourceDir: '/static/',
fileInfo: { statsPath: '/static/preview-stats.json' },
git: { changedFiles: ['./example.js', './package.json'], packageMetadataChanges },
turboSnap: {},
} as any;
await traceChangedFiles(ctx);
await traceChangedFiles(ctx, '/static/preview-stats.json');

expect(ctx.git.changedDependencyNames).toEqual(['moment']);
});
Expand All @@ -120,11 +117,10 @@ describe('traceChangedFiles', () => {
http,
options: { storybookBaseDir: '/wrong' },
sourceDir: '/static/',
fileInfo: { statsPath: '/static/preview-stats.json' },
git: { changedFiles: ['./example.js'] },
turboSnap: {},
} as any;
await expect(traceChangedFiles(ctx)).rejects.toThrow();
await expect(traceChangedFiles(ctx, '/static/preview-stats.json')).rejects.toThrow();
expect(ctx.exitCode).toBe(exitCodes.INVALID_OPTIONS);
});

Expand All @@ -141,11 +137,10 @@ describe('traceChangedFiles', () => {
http,
options: {},
sourceDir: '/static/',
fileInfo: { statsPath: '/static/preview-stats.json' },
git: { changedFiles: ['./example.js', './package.json'], packageMetadataChanges },
turboSnap: {},
} as any;
const onlyStoryFiles = await traceChangedFiles(ctx);
const onlyStoryFiles = await traceChangedFiles(ctx, '/static/preview-stats.json');

expect(ctx.turboSnap.bailReason).toBeUndefined();
expect(onlyStoryFiles).toStrictEqual(deps);
Expand All @@ -164,7 +159,7 @@ describe('traceChangedFiles', () => {
turboSnap: {},
} as any;

await expect(traceChangedFiles(ctx)).rejects.toThrow();
await expect(traceChangedFiles(ctx, undefined as any)).rejects.toThrow();
expect(ctx.turboSnap.bailReason).toEqual({ missingStatsFile: true });
});
});
12 changes: 7 additions & 5 deletions node-src/lib/turbosnap/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import semver from 'semver';

import { readStatsFile } from '../../tasks/readStatsFile';
import { Context } from '../../types';
import { Context, FileInfo } from '../../types';
import missingStatsFile from '../../ui/messages/errors/missingStatsFile';
import bailFile from '../../ui/messages/warnings/bailFile';
import { checkStorybookBaseDirectory } from '../checkStorybookBaseDirectory';
import { findChangedDependencies } from './findChangedDependencies';
import { findChangedPackageFiles } from './findChangedPackageFiles';
import { getDependentStoryFiles } from './getDependentStoryFiles';

// eslint-disable-next-line complexity
export const traceChangedFiles = async (ctx: Context) => {
export const traceChangedFiles = async (
ctx: Omit<Context, 'fileInfo'>,
statsPath: FileInfo['statsPath']
// eslint-disable-next-line complexity
) => {
if (!ctx.turboSnap || ctx.turboSnap.unavailable) return;
if (!ctx.git.changedFiles) return;
if (!ctx.fileInfo?.statsPath) {
if (!statsPath) {
// If we don't know the SB version, we should assume we don't support `--stats-json`
const nonLegacyStatsSupported =
ctx.storybook?.version &&
Expand All @@ -23,7 +26,6 @@ export const traceChangedFiles = async (ctx: Context) => {
throw new Error(missingStatsFile({ legacy: !nonLegacyStatsSupported }));
}

const { statsPath } = ctx.fileInfo;
const { changedFiles, packageMetadataChanges } = ctx.git;

// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
Expand Down
41 changes: 22 additions & 19 deletions node-src/tasks/prepare/calculateFileHashes.test.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
import { afterEach, describe, expect, it, vi } from 'vitest';

import TestLogger from '../../lib/testLogger';
import { Deps, FileInfo } from '../../types';
import { calculateFileHashes } from './calculateFileHashes';

vi.mock('../../lib/getFileHashes', () => ({
getFileHashes: (files: string[]) =>
Promise.resolve(Object.fromEntries(files.map((f) => [f, 'hash']))),
Promise.resolve(Object.fromEntries(files.map((f) => [f, `hash-${f}`]))),
}));

const environment = { CHROMATIC_RETRIES: 2, CHROMATIC_OUTPUT_INTERVAL: 0 };
const log = new TestLogger();
const http = { fetch: vi.fn() };

afterEach(() => {
vi.restoreAllMocks();
vi.resetAllMocks();
});

describe('calculateFileHashes', () => {
it('sets hashes on context.fileInfo', async () => {
const fileInfo = {
it('returns hashes', async () => {
const fileInfo: FileInfo = {
statsPath: '',
lengths: [
{ knownAs: 'iframe.html', contentLength: 42 },
{ knownAs: 'index.html', contentLength: 42 },
{
knownAs: 'iframe.html',
contentLength: 42,
pathname: '',
},
{
knownAs: 'index.html',
contentLength: 42,
pathname: '',
},
],
paths: ['iframe.html', 'index.html'],
total: 84,
};
const ctx = {
env: environment,
const deps = {
env: {} as Deps['env'],
log,
http,
sourceDir: '/static/',
options: { fileHashing: true },
fileInfo,
announcedBuild: { id: '1' },
} as any;
};

await calculateFileHashes(ctx, {} as any);
const hashes = await calculateFileHashes(deps, { fileInfo, sourceDir: '/static/' });

expect(ctx.fileInfo.hashes).toMatchObject({
'iframe.html': 'hash',
'index.html': 'hash',
expect(hashes).toMatchObject({
'iframe.html': 'hash-iframe.html',
'index.html': 'hash-index.html',
});
});
});
52 changes: 26 additions & 26 deletions node-src/tasks/prepare/calculateFileHashes.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import { getFileHashes } from '../../lib/getFileHashes';
import { transitionTo } from '../../lib/tasks';
import { Context, Task } from '../../types';
import { hashing, invalid } from '../../ui/tasks/prepare';
import { getFileHashes } from '@cli/getFileHashes';

import type { Deps, FileInfo } from '../../types';

type CalculateFileHashesDeps = Pick<Deps, 'log' | 'env'>;

export interface CalculateFileHashesInput {
fileInfo: FileInfo;
sourceDir: string;
}

/**
* Calculates file hashes for all files to be uploaded.
* File hashes are used for deduplication and integrity checking during upload.
* Skips calculation if file hashing is disabled or the task is being skipped.
*
* @param ctx - The CLI context containing file info and options
* @param task - The current Listr task for UI updates
* @param deps - The CLI dependencies containing logging and environment access
* @param input - The input containing file information for hashing
*
* @returns A mapping of file paths to their calculated hashes.
*/
export async function calculateFileHashes(ctx: Context, task: Task) {
if (ctx.skip || !ctx.options.fileHashing) return;
transitionTo(hashing)(ctx, task);

try {
if (!ctx.fileInfo) {
throw new Error(invalid(ctx).output);
}

const start = Date.now();
ctx.fileInfo.hashes = await getFileHashes(
ctx.fileInfo.paths,
ctx.sourceDir,
ctx.env.CHROMATIC_HASH_CONCURRENCY
);
ctx.log.debug(`Calculated file hashes in ${Date.now() - start}ms`);
} catch (err) {
ctx.log.warn('Failed to calculate file hashes');
ctx.log.debug(err);
}
export async function calculateFileHashes(
deps: CalculateFileHashesDeps,
input: CalculateFileHashesInput
): Promise<Record<string, string>> {
const start = Date.now();
const hashes = await getFileHashes(
input.fileInfo.paths,
input.sourceDir,
deps.env.CHROMATIC_HASH_CONCURRENCY
);
deps.log.debug(`Calculated file hashes in ${Date.now() - start}ms`);
return hashes;
}
Loading
Loading