Skip to content

Commit

Permalink
feat: normalize filepath and ensure path exists for stats writing (#750)
Browse files Browse the repository at this point in the history
* fix: normalize filepath for stats writing

* refactor: move error handling to bundle command itself

* test: setupStatsWriter tests

* fix: ensure path exists

* chore: add changeset
  • Loading branch information
jbroma authored Sep 17, 2024
1 parent 5f2fd65 commit c4a3235
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-falcons-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@callstack/repack': minor
---

Normalize filepath & ensure path exists when writing stats to a file
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import path from 'node:path';
import { fs } from 'memfs';
import { normalizeStatsOptions, writeStats } from '../setupStatsWriter';
import { Logger } from '../../../types';

jest.mock('node:fs', () => jest.requireActual('memfs').fs);

describe('setupStatsWriter', () => {
const logger = { info: jest.fn() } as unknown as Logger;

describe('normalizeStatsOptions', () => {
it('should return options with preset if preset is provided', () => {
const options = {};
const preset = 'detailed';
const result = normalizeStatsOptions(options, preset);
expect(result).toHaveProperty('preset', 'detailed');
});

it('should return options with preset "normal" if options is true', () => {
const options = true;
const result = normalizeStatsOptions(options);
expect(result).toHaveProperty('preset', 'normal');
});

it('should return options with preset "none" if options is false', () => {
const options = false;
const result = normalizeStatsOptions(options);
expect(result).toHaveProperty('preset', 'none');
});

it('should return options as is if no preset is provided', () => {
const options = { custom: 'value' };
const result = normalizeStatsOptions(options);
expect(result).toEqual(options);
});
});

describe('writeStats', () => {
it('should write stats to the specified file', async () => {
const stats = { key: 'value' };
const filepath = 'stats.json';

await writeStats(stats, { filepath, logger, rootDir: '/' });

const absoluteFilepath = path.resolve('/', filepath);
const fileContent = fs.readFileSync(absoluteFilepath, 'utf-8') as string;
expect(JSON.parse(fileContent)).toEqual(stats);
});

it('should ensure path exists', async () => {
const stats = { key: 'value' };
const filepath = './my/custom/dir/stats.json';

await writeStats(stats, { filepath, logger, rootDir: '/' });

const absoluteFilepath = path.resolve('/', filepath);
const fileContent = fs.readFileSync(absoluteFilepath, 'utf-8') as string;
expect(JSON.parse(fileContent)).toEqual(stats);
});
});
});
42 changes: 28 additions & 14 deletions packages/repack/src/commands/common/setupStatsWriter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import fs from 'node:fs';
import path from 'node:path';
import { pipeline } from 'node:stream/promises';
import { stringifyStream } from '@discoveryjs/json-ext';
import { Logger } from '../../types';

function normalizeFilepath(filepath: string, root: string): string {
if (path.isAbsolute(filepath)) {
return filepath;
}
return path.resolve(root, filepath);
}

function ensureFilepathExists(filepath: string) {
fs.mkdirSync(path.dirname(filepath), { recursive: true });
}

export function normalizeStatsOptions<Stats>(
options: Stats,
preset?: string
Expand All @@ -18,23 +30,25 @@ export function normalizeStatsOptions<Stats>(
}
}

interface WriteStatsOptions {
filepath: string;
logger?: Logger;
rootDir: string;
}

export async function writeStats(
stats: any,
filepath: string,
logger: Logger = console
{ filepath, logger = console, rootDir }: WriteStatsOptions
) {
// TODO normalize filepath
const outputPath = normalizeFilepath(filepath, rootDir);
logger.info(`Writing compiler stats`);

try {
// Stats can be fairly big at which point their JSON no longer fits into a single string.
// Approach was copied from `webpack-cli`: https://github.com/webpack/webpack-cli/blob/c03fb03d0aa73d21f16bd9263fd3109efaf0cd28/packages/webpack-cli/src/webpack-cli.ts#L2471-L2482
const statsStream = stringifyStream(stats);
const outputStream = fs.createWriteStream(filepath);
await pipeline(statsStream, outputStream);
logger.info(`Wrote compiler stats to ${filepath}`);
} catch (error) {
logger.error(String(error));
process.exit(2);
}
// Stats can be fairly big at which point their JSON no longer fits into a single string.
// Approach was copied from `webpack-cli`: https://github.com/webpack/webpack-cli/blob/c03fb03d0aa73d21f16bd9263fd3109efaf0cd28/packages/webpack-cli/src/webpack-cli.ts#L2471-L2482
const statsStream = stringifyStream(stats);
ensureFilepathExists(outputPath);
const outputStream = fs.createWriteStream(outputPath);
await pipeline(statsStream, outputStream);

logger.info(`Wrote compiler stats to ${outputPath}`);
}
11 changes: 10 additions & 1 deletion packages/repack/src/commands/rspack/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,16 @@ export async function bundle(
);

const statsJson = stats.toJson(statsOptions);
await writeStats(statsJson, args.json);

try {
await writeStats(statsJson, {
filepath: args.json,
rootDir: compiler.context,
});
} catch (e) {
console.error(String(e));
process.exit(2);
}
}
};

Expand Down
10 changes: 9 additions & 1 deletion packages/repack/src/commands/webpack/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,15 @@ export async function bundle(
);

const statsJson = stats.toJson(statsOptions);
await writeStats(statsJson, args.json);
try {
await writeStats(statsJson, {
filepath: args.json,
rootDir: compiler.context,
});
} catch (e) {
console.error(String(e));
process.exit(2);
}
}
};

Expand Down

0 comments on commit c4a3235

Please sign in to comment.