Skip to content
Merged
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
3 changes: 3 additions & 0 deletions tools/egg-bin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"exports": {
".": "./src/index.ts",
"./baseCommand": "./src/baseCommand.ts",
"./commands/bundle": "./src/commands/bundle.ts",
"./commands/cov": "./src/commands/cov.ts",
"./commands/dev": "./src/commands/dev.ts",
"./commands/manifest": "./src/commands/manifest.ts",
Expand All @@ -41,6 +42,7 @@
"exports": {
".": "./dist/index.js",
"./baseCommand": "./dist/baseCommand.js",
"./commands/bundle": "./dist/commands/bundle.js",
"./commands/cov": "./dist/commands/cov.js",
"./commands/dev": "./dist/commands/dev.js",
"./commands/manifest": "./dist/commands/manifest.js",
Expand All @@ -59,6 +61,7 @@
},
"dependencies": {
"@eggjs/core": "workspace:*",
"@eggjs/egg-bundler": "workspace:*",
"@eggjs/tegg-vitest": "workspace:*",
"@eggjs/utils": "workspace:*",
"@oclif/core": "catalog:",
Expand Down
100 changes: 100 additions & 0 deletions tools/egg-bin/src/commands/bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import path from 'node:path';
import { debuglog } from 'node:util';

import { getFrameworkPath } from '@eggjs/utils';
import { Flags } from '@oclif/core';

import { BaseCommand } from '../baseCommand.ts';

const debug = debuglog('egg/bin/commands/bundle');
const bundleModes = ['production', 'development'] as const;
type BundleMode = (typeof bundleModes)[number];

function getBundleMode(mode: string): BundleMode {
if (mode === 'production' || mode === 'development') {
return mode;
}
throw new Error(`Unsupported bundle mode: ${mode}`);
}

export default class Bundle extends BaseCommand<typeof Bundle> {
static override description = 'Bundle an egg app into a deployable artifact using @eggjs/egg-bundler';

static override examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --output ./dist-bundle',
'<%= config.bin %> <%= command.id %> --mode development',
'<%= config.bin %> <%= command.id %> --framework egg --output ./out',
];

static override flags = {
output: Flags.string({
char: 'o',
description: 'output directory for the bundled artifact',
default: './dist-bundle',
}),
manifest: Flags.string({
description: 'path to manifest.json (defaults to <baseDir>/.egg/manifest.json)',
}),
framework: Flags.string({
char: 'f',
description: 'framework name or absolute path',
}),
mode: Flags.string({
description: 'build mode',
options: [...bundleModes],
default: 'production',
}),
Comment thread
killagu marked this conversation as resolved.
'no-tegg': Flags.boolean({
description: 'disable tegg decoratedFile collection',
default: false,
}),
'force-external': Flags.string({
description: 'package name to always mark as external (repeatable)',
multiple: true,
default: [],
}),
'inline-external': Flags.string({
description: 'package name to force-inline even if auto-detected as external (repeatable)',
multiple: true,
Comment thread
killagu marked this conversation as resolved.
default: [],
}),
};

public async run(): Promise<void> {
const { flags } = this;
const baseDir = flags.base;
const outputDir = path.isAbsolute(flags.output) ? flags.output : path.join(baseDir, flags.output);
const manifestPath = flags.manifest
? path.isAbsolute(flags.manifest)
? flags.manifest
: path.join(baseDir, flags.manifest)
: undefined;

debug(
'bundle: baseDir=%s, outputDir=%s, framework=%s, mode=%s, tegg=%s',
baseDir,
outputDir,
flags.framework,
flags.mode,
!flags['no-tegg'],
);

const { bundle } = await import('@eggjs/egg-bundler');
const result = await bundle({
baseDir,
outputDir,
manifestPath,
framework: getFrameworkPath({ framework: flags.framework, baseDir }),
mode: getBundleMode(flags.mode),
tegg: !flags['no-tegg'],
externals: {
force: flags['force-external'],
inline: flags['inline-external'],
},
Comment thread
killagu marked this conversation as resolved.
});
Comment thread
killagu marked this conversation as resolved.

this.log(`bundled to ${result.outputDir} (${result.files.length} files)`);
this.log(`manifest: ${result.manifestPath}`);
}
}
3 changes: 2 additions & 1 deletion tools/egg-bin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Bundle from './commands/bundle.ts';
import Cov from './commands/cov.ts';
import Dev from './commands/dev.ts';
import Manifest from './commands/manifest.ts';
import Test from './commands/test.ts';

export { Test, Cov, Dev, Manifest };
export { Test, Cov, Dev, Manifest, Bundle };

export * from './baseCommand.ts';
export * from './types.ts';
Expand Down
78 changes: 78 additions & 0 deletions tools/egg-bin/test/commands/bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import path from 'node:path';

import { getFrameworkPath } from '@eggjs/utils';
import { describe, expect, it, vi, beforeEach } from 'vitest';

import Bundle from '../../src/commands/bundle.ts';
import { getFixtures } from '../helper.ts';

const bundleMock = vi.hoisted(() => vi.fn());

vi.mock('@eggjs/egg-bundler', () => ({
bundle: bundleMock,
}));

describe('test/commands/bundle.test.ts', () => {
const baseDir = getFixtures('demo-app');

beforeEach(() => {
bundleMock.mockReset();
bundleMock.mockResolvedValue({
outputDir: path.join(baseDir, 'dist-bundle'),
manifestPath: path.join(baseDir, '.egg/manifest.json'),
files: ['server.js'],
});
});

it('should pass default options to egg-bundler', async () => {
await Bundle.run(['--base', baseDir]);

expect(bundleMock).toHaveBeenCalledTimes(1);
expect(bundleMock).toHaveBeenCalledWith({
baseDir,
outputDir: path.join(baseDir, 'dist-bundle'),
manifestPath: undefined,
framework: getFrameworkPath({ baseDir }),
mode: 'production',
Comment thread
killagu marked this conversation as resolved.
tegg: true,
externals: {
force: [],
inline: [],
},
});
});

it('should pass resolved flag options to egg-bundler', async () => {
await Bundle.run([
'--base',
baseDir,
'--output',
'bundle-output',
'--manifest',
'.egg/custom-manifest.json',
'--mode',
'development',
'--no-tegg',
'--force-external',
'@scope/foo',
'--force-external',
'bar',
'--inline-external',
'baz',
]);

expect(bundleMock).toHaveBeenCalledTimes(1);
expect(bundleMock).toHaveBeenCalledWith({
baseDir,
outputDir: path.join(baseDir, 'bundle-output'),
manifestPath: path.join(baseDir, '.egg/custom-manifest.json'),
framework: getFrameworkPath({ baseDir }),
mode: 'development',
tegg: false,
externals: {
force: ['@scope/foo', 'bar'],
inline: ['baz'],
},
});
});
});
2 changes: 1 addition & 1 deletion tools/egg-bundler/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "@eggjs/egg-bundler",
"version": "0.0.0",
"private": true,
"description": "Bundle an Egg.js application into a deployable artifact using @utoo/pack",
"homepage": "https://github.com/eggjs/egg/tree/next/tools/egg-bundler",
Comment thread
killagu marked this conversation as resolved.
"bugs": {
Expand Down Expand Up @@ -52,6 +51,7 @@
},
"dependencies": {
"@eggjs/core": "workspace:*",
"@eggjs/utils": "workspace:*",
"@utoo/pack": "catalog:",
"tsx": "catalog:"
},
Expand Down
Loading