Skip to content

Commit 69d477d

Browse files
authored
Merge branch 'main' into sirtimid/kernel-restructure
2 parents f7338d5 + 697008e commit 69d477d

File tree

8 files changed

+309
-83
lines changed

8 files changed

+309
-83
lines changed

packages/cli/src/app.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
import '@endo/init';
2+
3+
import { Logger } from '@ocap/utils';
14
import path from 'node:path';
25
import yargs from 'yargs';
36
import { hideBin } from 'yargs/helpers';
47

5-
import { createBundle } from './commands/bundle.ts';
8+
import { bundleSource } from './commands/bundle.ts';
69
import { getServer } from './commands/serve.ts';
710
import { watchDir } from './commands/watch.ts';
811
import { defaultConfig } from './config.ts';
912
import type { Config } from './config.ts';
1013
import { withTimeout } from './utils.ts';
1114

15+
const logger = new Logger('cli');
16+
1217
await yargs(hideBin(process.argv))
1318
.usage('$0 <command> [options]')
1419
.demandCommand(1)
@@ -26,7 +31,9 @@ await yargs(hideBin(process.argv))
2631
describe: 'The files or directories of files to bundle',
2732
}),
2833
async (args) => {
29-
await Promise.all(args.targets.map(createBundle));
34+
await Promise.all(
35+
args.targets.map(async (target) => bundleSource(target, logger)),
36+
);
3037
},
3138
)
3239
.command(
@@ -55,7 +62,7 @@ await yargs(hideBin(process.argv))
5562
},
5663
dir: resolvedDir,
5764
};
58-
console.info(`starting ${appName} in ${resolvedDir} on ${url}`);
65+
logger.info(`starting ${appName} in ${resolvedDir} on ${url}`);
5966
const server = getServer(config);
6067
await server.listen();
6168
},
@@ -71,19 +78,19 @@ await yargs(hideBin(process.argv))
7178
describe: 'The directory to watch',
7279
}),
7380
(args) => {
74-
const { ready, error } = watchDir(args.dir);
81+
const { ready, error } = watchDir(args.dir, logger);
7582
let handleClose: undefined | (() => Promise<void>);
7683

7784
ready
7885
.then((close) => {
7986
handleClose = close;
80-
console.info(`Watching ${args.dir}...`);
87+
logger.info(`Watching ${args.dir}...`);
8188
return undefined;
8289
})
83-
.catch(console.error);
90+
.catch(logger.error);
8491

8592
error.catch(async (reason) => {
86-
console.error(reason);
93+
logger.error(reason);
8794
// If watching started, close the watcher.
8895
return handleClose ? withTimeout(handleClose(), 400) : undefined;
8996
});
@@ -109,18 +116,21 @@ await yargs(hideBin(process.argv))
109116
const closeHandlers: (() => Promise<void>)[] = [];
110117
const resolvedDir = path.resolve(args.dir);
111118

112-
await createBundle(resolvedDir);
119+
await bundleSource(resolvedDir, logger);
113120

114121
const handleClose = async (): Promise<void> => {
115122
await Promise.all(
116123
closeHandlers.map(async (close) => withTimeout(close(), 400)),
117124
);
118125
};
119126

120-
const { ready: watchReady, error: watchError } = watchDir(resolvedDir);
127+
const { ready: watchReady, error: watchError } = watchDir(
128+
resolvedDir,
129+
logger,
130+
);
121131

122132
watchError.catch(async (reason) => {
123-
console.error(reason);
133+
logger.error(reason);
124134
await handleClose();
125135
});
126136

@@ -136,7 +146,7 @@ await yargs(hideBin(process.argv))
136146
const { close: closeServer, port } = await server.listen();
137147
closeHandlers.push(closeServer);
138148

139-
console.info(`bundling and serving ${resolvedDir} on localhost:${port}`);
149+
logger.info(`bundling and serving ${resolvedDir} on localhost:${port}`);
140150
},
141151
)
142152
.help('help')
Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
1+
import { Logger } from '@ocap/utils';
12
import { readFile, rm } from 'fs/promises';
23
import { basename } from 'path';
34
import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest';
45

5-
import { createBundleFile, createBundleDir } from './bundle.ts';
6+
import { bundleFile, bundleDir, bundleSource } from './bundle.ts';
67
import {
78
makeTestBundleStage,
89
validTestBundleNames,
910
} from '../../test/bundles.ts';
1011
import { fileExists } from '../file.ts';
1112

12-
const mocks = vi.hoisted(() => ({
13-
bundleSource: vi.fn(),
14-
}));
13+
const mocks = vi.hoisted(() => {
14+
return {
15+
endoBundleSource: vi.fn(),
16+
Logger: vi.fn(() => ({
17+
info: vi.fn(),
18+
error: vi.fn(),
19+
subLogger: vi.fn(),
20+
})),
21+
isDirectory: vi.fn(),
22+
};
23+
});
1524

1625
vi.mock('@endo/bundle-source', () => ({
17-
default: mocks.bundleSource,
26+
default: mocks.endoBundleSource,
1827
}));
1928

2029
vi.mock('@endo/init', () => ({}));
2130

31+
vi.mock('@ocap/utils', () => ({
32+
Logger: mocks.Logger,
33+
}));
34+
35+
vi.mock('../file.ts', async (importOriginal) => ({
36+
...(await importOriginal()),
37+
isDirectory: mocks.isDirectory,
38+
}));
39+
2240
describe('bundle', async () => {
41+
let logger: Logger;
42+
2343
const { testBundleRoot, getTestBundleSpecs, globBundles, resolveBundlePath } =
2444
await makeTestBundleStage();
2545
const testBundleSpecs = getTestBundleSpecs(validTestBundleNames);
@@ -32,20 +52,22 @@ describe('bundle', async () => {
3252
afterAll(deleteTestBundles);
3353

3454
beforeEach(async () => {
35-
vi.resetModules();
3655
await deleteTestBundles();
56+
vi.resetModules();
57+
logger = new Logger();
58+
vi.resetAllMocks();
3759
});
3860

39-
describe('createBundleFile', () => {
61+
describe('bundleFile', () => {
4062
it.each(testBundleSpecs)(
4163
'bundles a single file: $name',
4264
async ({ source, bundle }) => {
4365
expect(await fileExists(bundle)).toBe(false);
4466

4567
const testContent = { source: 'test-content' };
46-
mocks.bundleSource.mockImplementationOnce(() => testContent);
68+
mocks.endoBundleSource.mockImplementationOnce(() => testContent);
4769

48-
await createBundleFile(source);
70+
await bundleFile(source, { logger });
4971

5072
expect(await fileExists(bundle)).toBe(true);
5173

@@ -57,27 +79,27 @@ describe('bundle', async () => {
5779
},
5880
);
5981

60-
it('calls console.error if bundling fails', async () => {
61-
const consoleErrorSpy = vi.spyOn(console, 'error');
82+
it('calls logger.error if bundling fails', async () => {
83+
const loggerErrorSpy = vi.spyOn(logger, 'error');
6284
const badBundle = resolveBundlePath('bad-vat.fails');
63-
await createBundleFile(badBundle);
64-
expect(consoleErrorSpy).toHaveBeenCalledOnce();
85+
await bundleFile(badBundle, { logger });
86+
expect(loggerErrorSpy).toHaveBeenCalledOnce();
6587
});
6688
});
6789

68-
describe('createBundleDir', () => {
90+
describe('bundleDir', () => {
6991
it('bundles a directory', async () => {
7092
expect(await globBundles()).toStrictEqual([]);
7193

7294
// mocked bundleSource fails iff the target filename has '.fails.'
73-
mocks.bundleSource.mockImplementation((bundlePath) => {
95+
mocks.endoBundleSource.mockImplementation((bundlePath) => {
7496
if (bundlePath.includes('.fails.')) {
7597
throw new Error(`Failed to bundle ${bundlePath}`);
7698
}
7799
return 'test content';
78100
});
79101

80-
await createBundleDir(testBundleRoot);
102+
await bundleDir(testBundleRoot, { logger });
81103

82104
const bundledOutputs = (await globBundles()).map((bundlePath) =>
83105
basename(bundlePath, '.bundle'),
@@ -88,4 +110,18 @@ describe('bundle', async () => {
88110
expect(bundledOutputs).toStrictEqual(validTestBundleNames);
89111
});
90112
});
113+
114+
describe('bundleSource', () => {
115+
it('calls logger.error if bundling fails', async () => {
116+
mocks.isDirectory.mockImplementationOnce(() => {
117+
throw new Error('test error');
118+
});
119+
const loggerErrorSpy = vi.spyOn(logger, 'error');
120+
await bundleSource(resolveBundlePath('test'), logger);
121+
expect(loggerErrorSpy).toHaveBeenCalledWith(
122+
expect.stringContaining('error bundling target'),
123+
expect.any(Error),
124+
);
125+
});
126+
});
91127
});
Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,82 @@
11
import '@endo/init';
2-
import bundleSource from '@endo/bundle-source';
2+
import endoBundleSource from '@endo/bundle-source';
3+
import { Logger } from '@ocap/utils';
34
import { glob } from 'glob';
45
import { writeFile } from 'node:fs/promises';
56
import { resolve, join } from 'node:path';
67

78
import { isDirectory } from '../file.ts';
89
import { resolveBundlePath } from '../path.ts';
910

11+
type BundleFileOptions = {
12+
logger: Logger;
13+
targetPath?: string;
14+
};
15+
1016
/**
1117
* Create a bundle given path to an entry point.
1218
*
1319
* @param sourcePath - Path to the source file that is the root of the bundle.
14-
* @param destinationPath - Optional path to which to write the bundle.
20+
* @param options - Options for bundling the file.
21+
* @param options.logger - The logger to use for logging (required).
22+
* @param options.targetPath - Optional path to which to write the bundle.
1523
* If not provided, defaults to sourcePath with `.bundle` extension.
1624
* @returns A promise that resolves when the bundle has been written.
1725
*/
18-
export async function createBundleFile(
26+
export async function bundleFile(
1927
sourcePath: string,
20-
destinationPath?: string,
28+
options: BundleFileOptions,
2129
): Promise<void> {
30+
const { logger, targetPath } = options;
2231
const sourceFullPath = resolve(sourcePath);
23-
const bundlePath = destinationPath ?? resolveBundlePath(sourceFullPath);
32+
const bundlePath = targetPath ?? resolveBundlePath(sourceFullPath);
2433
try {
25-
const bundle = await bundleSource(sourceFullPath);
26-
const bundleString = JSON.stringify(bundle);
27-
await writeFile(bundlePath, bundleString);
28-
console.log(`wrote ${bundlePath}: ${new Blob([bundleString]).size} bytes`);
34+
const bundle = await endoBundleSource(sourceFullPath);
35+
const bundleContent = JSON.stringify(bundle);
36+
await writeFile(bundlePath, bundleContent);
37+
logger.info(`wrote ${bundlePath}: ${new Blob([bundleContent]).size} bytes`);
2938
} catch (problem) {
30-
console.error(problem);
39+
logger.error(`error bundling file ${sourceFullPath}`, problem);
3140
}
3241
}
3342

3443
/**
3544
* Create a bundle given path to an entry point.
3645
*
3746
* @param sourceDir - Path to a directory of source files to bundle.
47+
* @param options - Options for bundling the directory.
48+
* @param options.logger - The logger to use for logging (required).
3849
* @returns A promise that resolves when the bundles have been written.
3950
*/
40-
export async function createBundleDir(sourceDir: string): Promise<void> {
41-
console.log('bundling dir', sourceDir);
51+
export async function bundleDir(
52+
sourceDir: string,
53+
options: { logger: Logger },
54+
): Promise<void> {
55+
const { logger } = options;
56+
logger.info('bundling dir', sourceDir);
4257
await Promise.all(
4358
(await glob(join(sourceDir, '*.js'))).map(
44-
async (source) => await createBundleFile(source),
59+
async (source) => await bundleFile(source, { logger }),
4560
),
4661
);
4762
}
4863

4964
/**
5065
* Bundle a target file or every file in the target directory.
5166
*
52-
* @param target The file or directory to apply the bundler to.
67+
* @param target - The file or directory to apply the bundler to.
68+
* @param logger - The logger to use for logging.
69+
*
5370
* @returns A promise that resolves when bundling is done.
5471
*/
55-
export async function createBundle(target: string): Promise<void> {
56-
await ((await isDirectory(target)) ? createBundleDir : createBundleFile)(
57-
target,
58-
);
72+
export async function bundleSource(
73+
target: string,
74+
logger: Logger,
75+
): Promise<void> {
76+
try {
77+
const targetIsDirectory = await isDirectory(target);
78+
await (targetIsDirectory ? bundleDir : bundleFile)(target, { logger });
79+
} catch (problem) {
80+
logger.error(`error bundling target ${target}`, problem);
81+
}
5982
}

0 commit comments

Comments
 (0)