Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
101 changes: 101 additions & 0 deletions tools/egg-bin/src/commands/bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
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',
default: 'egg',
Comment thread
killagu marked this conversation as resolved.
Outdated
}),
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({ framework: 'egg', 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({ framework: 'egg', baseDir }),
mode: 'development',
tegg: false,
externals: {
force: ['@scope/foo', 'bar'],
inline: ['baz'],
},
});
});
});
1 change: 0 additions & 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
Loading