Malagu 框架提供了一个命令行工具 @malagu/cli ,该工具提供了一些基础的命令,帮助我们初始化、运行、构建和部署项目。该工具只包含最核心的功能,通过命令行插件机制把一些非核心的功能不同平台部署逻辑通过插件的方式提供,命令行插件也是组件的一种形式。通过插件机制,我们可以为命令行工具添加新的命令,或者扩展老的命令。在 Malagu 框架中,很多地方都使用到了命令行插件。

安装


命令行工具在与 Malagu 组件配合使用时,尽量保持与项目中的组件版本一致,避免出现不兼容。框架提供的内置模板默认采用的是 Yarn 工具,当然也可以使用 NPM 类型的模板。模板初始化完成后,也可以改成 NPM 工具。

# 指定淘宝镜像源下载会更快
npm install -g yarn --registry https://registry.npm.taobao.org 
npm install -g @malagu/cli --registry https://registry.npm.taobao.org 


命令行说明


我们可以通过 malagu -h 命令查看命令行帮助信息,malagu -h 命令如下:

Usage: malagu <command> [options]

Malagu CLI 0.0.44

Options:
  -V, --version                     output the version number
  -h, --help                        output usage information

Commands:
  init [options] [name] [template]  init a application
  serve [options] [entry]           serve a applicaton
  build [options] [entry]           build a application
  deploy [options] [entry]          deploy a applicaton
  config                            config faas adapter profile

主要包含四个核心命令:

  1. malagu init:初始化一个模板应用
  2. malagu serve:本地运行应用
  3. malgu build:构建应用,一般情况不使用,部署应用的时候自动使用该命令构建项目
  4. malagu deploy:部署应用,通过 -s, --skip-build 选项跳过构建过程
  5. malagu config:配置 FaaS 适配器相关的个性信息,例如 AKSK,region 等等。该命令是一个上下文命令,只要满足一定上下文要求才会看到该命令,所有需要在项目根目录执行该命令,并且项目依赖了 FaaS 适配器相关的组件,例如 fc-adapter 、 scf-adapter 等等。


初始化


我们可以通过 malagu init 命令使用模板,malagu init 命令如下:

Usage: malagu init [options] [name] [template]

init a application

Options:
  -o, --output-dir [path]  output directory (default: ".")
  -h, --help               output usage information

参数:


选项:


示例:

# 不指定名称,从内置模板列表中选择
$ malagu init

# 指定名称,从内置模板列表中选择
$ malagu init demo

# 指定模板为内置模板名称
$ malagu init demo backend-app

# 指定模板为 GitHub Repository 地址
$ malagu init demo git@github.com:cellbang/cellbang-site.git # 或者 malagu init demo https://github.com/cellbang/cellbang-site.git

内置模板


malagu init

                   ___
 /'\_/`\          /\_ \
/\      \     __  \//\ \      __       __   __  __
\ \ \__\ \  /'__`\  \ \ \   /'__`\   /'_ `\/\ \/\ \
 \ \ \_/\ \/\ \L\.\_ \_\ \_/\ \L\.\_/\ \L\ \ \ \_\ \
  \ \_\\ \_\ \__/.\_\/\____\ \__/.\_\ \____ \ \____/
   \/_/ \/_/\/__/\/_/\/____/\/__/\/_/\/___L\ \/___/
                                       /\____/
                   @malagu/cli@0.0.44  \_/__/

? Select a template to init (Use arrow keys or type to search)
❯ backend-app Official
  sample-app Official
  database-app Official
  admin-app Official
  multi-component Official
  mycli Official
  site Official


本地运行


我们可以通过 malagu serve 命令本地运行项目,推荐使用 npx malagu serve 运行项目,malagu serve 命令如下:

Usage: serve [options] [entry]

serve a applicaton

Options:
  -o, --open [open]        Open browser
  -p, --port [port]        Port used by the server
  -t, --targets [targets]  Specify application targets
  -m, --mode [mode]        Specify application mode
  -h, --help               output usage information

参数:


选项:


示例:

# 指定端口号和打开浏览器
$ malagu serve -p 30001 -o

# 指定应用构建目标产物
$ malagu serve -t frontend

# 指定单个应用运行模式
$ malagu serve -m test

# 指定多个应用运行模式
$ malagu serve -m cdn,database # 或者 malagu serve -m cdn -m database

构建


我们可以通过 malagu build 命令构建项目,推荐使用 npx malagu build 构建项目,malagu build 命令如下:

Usage: build [options] [entry]

build a application

Options:
  -t, --targets [targets]  Specify application targets (default: [])
  -m, --mode [mode]        Specify application mode (default: [])
  -p, --prod [prod]        Create a production build
  -h, --help               output usage information

参数:


选项:


示例:

# 指定应用构建目标产物
$ malagu build -t frontend

# 指定单个应用运行模式
$ malagu build -m test

# 指定多个应用运行模式
$ malagu build -m cdn,database # 或者 malagu serve demo -m cdn -m database

# 指定生产环境标识
$ malagu build -p


注意:一般情况,不需要手动执行构建命令,因为在执行部署命令的时候会自动构建,部署命令可以通过选项 -s, --skip-build 跳过构建过程。如果你不打算使用框架提供的部署命令,此时,你需要自己手动执行构建命令。不过,当框架提供的部署命令不满足你的业务需要,更推荐的做法是通过提供一个命令 Deploy 插件,自定义部署逻辑。


部署


我们可以通过 malagu build 命令部署项目,使用部署命令前,需要安装对应平台的适配器组件,比如 @malagu/fc-adapter 、 @malagu/scf-adapter 等等。推荐使用 npx malagu deploy 部署项目,malagu deploy 命令如下:

Usage: deploy [options] [entry]

deploy a applicaton

Options:
  -t, --targets [targets]       Specify application targets (default: [])
  -m, --mode [mode]             Specify application mode (default: [])
  -p, --prod [prod]             Create a production deployment
  -s, --skip-build [skipBuild]  Skip the build process
  -h, --help

参数:


选项:


示例:

# 指定应用构建目标产物
$ malagu deploy -t frontend

# 指定单个应用运行模式
$ malagu deploy -m test

# 指定多个应用运行模式
$ malagu deploy -m cdn,database # 或者 malagu serve demo -m cdn -m database

# 指定生产环境标识
$ malagu deploy -p

# 跳过构建过程
$ malagu deploy -s


插件


命令行工具包含最核心的功能,通过命令行插件机制把一些非核心的功能不同平台部署逻辑通过插件的方式提供,命令行插件也是组件的一种形式。通过插件机制,我们可以为命令行工具添加新的命令,或者扩展老的命令。在 Malagu 框架中,很多地方都使用到了命令行插件。


Cli 插件


执行 malagu 命令的时候,执行该插件。通过 Cli 插件,我们可以为命令行工具添加新的命令,或者扩展老的命令,这是一个自由度非常高的一个插件,比如使用 Cli 插件添加一个新命令:


import { CliContext } from '@malagu/cli';

export default async (context: CliContext) => {
    const { program } = context;
    program
        .command('login')
        .description('Sign in to the app')
        .action(() => {
            console.log('login');
        });
}

插件代码存放到项目目录 src/cli-hook.ts 或者 src/hooks/cli.ts ,框架自动识别加载,也可以通过属性 cliHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Webpack 插件


执行 malagu 命令需要 Webpack 构建的时候,执行该插件。通过 Webpack 插件,我们可以自定义 Webpack 构建行为,我们也可以通过 malagu.webpack 属性配置 Webpack,但是,对于一些自由度比较高的配置,Webpack 插件方式更加适合 。比如使用 Webpack 插件添加一个 Webpack 自己的插件:


import { WebpackContext, FRONTEND_TARGET, getWebpackConfig, ConfigurationContext } from '@malagu/cli';

export default async (context: WebpackContext) => {
    const { dev, cfg, configurations } = context;
    const config = ConfigurationContext.getConfiguration(FRONTEND_TARGET, configurations);
    if (config) {
        const pluginConfig = getWebpackConfig(cfg, FRONTEND_TARGET).workboxWebpackPlugin || {};
        if (!dev || pluginConfig.generateInDevMode) {
            const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
            delete pluginConfig.generateInDevMode;
            config.plugins!.push(
                    new WorkboxWebpackPlugin.GenerateSW({
                        clientsClaim: true,
                        skipWaiting: true,
                        maximumFileSizeToCacheInBytes: dev ? 10 * 1024 * 1024 : undefined,
                        ...pluginConfig
                    })
            );
        }
    }
};

插件代码存放到项目目录 src/webpack-hook.ts 或者 src/hooks/webpack.ts ,框架自动识别加载,也可以通过属性 webpackHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Deploy 插件


执行 malagu deploy 命令的时候,执行该插件。通过 Deploy 插件,我们可以自定义部署行为。比如使用 Deploy 插件实现项目部署到函数计算平台



export default async (context: DeployContext) => {
    const { cfg } = context;

    const deployConfig = getMalaguConfig(cfg, BACKEND_TARGET)['fc-adapter'];

    const profileProvider = new ProfileProvider();
    profile = {
        ...await profileProvider.provide(),
        ...deployConfig.profile

    };

    const regions = deployConfig.regions || [profile.defaultRegion];
    for (region of regions) {
        await doDeploy(context, deployConfig);
    }

};

插件代码存放到项目目录 src/deploy-hook.ts 或者 src/hooks/deploy.ts ,框架自动识别加载,也可以通过属性 deployHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Build 插件


执行 malagu build 命令的时候,执行该插件。通过 Build 插件,我们可以自定义构建行为。比如使用 Build 插件实现项目为部署到函数计算平台 custom 运行时,而生成 bootstrap 启动文件


import { BuildContext, BACKEND_TARGET, getMalaguConfig, getHomePath } from '@malagu/cli';
import { join } from 'path';
import { writeFile } from 'fs-extra';

export default async (context: BuildContext) => {
    const { pkg, cfg } = context;
    const deployConfig = getMalaguConfig(cfg, BACKEND_TARGET)['fc-adapter'];
    if (deployConfig.type === 'custom') {
        const destDir = join(getHomePath(pkg), 'bootstrap');
        const bootstrap = deployConfig.function.bootstrap;
        delete deployConfig.function.bootstrap;

        await writeFile(destDir, `#!/bin/bash\n${bootstrap}`, { mode: 0o755 });
    }

};

插件代码存放到项目目录 src/build-hook.ts 或者 src/hooks/build.ts ,框架自动识别加载,也可以通过属性 buildHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Serve 插件


执行 malagu serve 命令的时候,执行该插件。通过 Serve 插件,我们可以自定义本地运行行为。比如使用 Serve 插件实现基于 Webpack Dev Server 的本地运行


import { ServeContext } from '@malagu/cli';
import * as express from 'express';
export class Deferred<T> {
    resolve: (value?: T) => void;
    reject: (err?: any) => void; // tslint:disable-line

    promise = new Promise<T>((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
    });
}
export default async (context: ServeContext) => {
    const { app, entryContextProvider } = context;
    app.use(express.json());
    app.use(express.raw());
    app.use(express.text());
    app.use(express.urlencoded({ extended: true }));
    let doDispatch: (req: any, res: any) => void;
    const compileDeferred = new Deferred<void>();

    context.compiler.hooks.done.tap('WebServe', () => {
        entryContextProvider().then(async (ctx: any) => {
            const { Dispatcher, Context, HttpContext, ContainerProvider, Application, container } = ctx;
            const c = await container;
            ContainerProvider.set(c);
            await c.get(Application).start();
            const dispatcher = c.get(Dispatcher);
            doDispatch = (req: any, res: any) => {
                const httpContext = new HttpContext(req, res);
                Context.run(() => dispatcher.dispatch(httpContext));
            };
            compileDeferred.resolve();
        });
    });

    app.all('*', async (req: any, res: any) => {
        await compileDeferred.promise;
        doDispatch(req, res);
    });

};

插件代码存放到项目目录 src/serve-hook.ts 或者 src/hooks/serve.ts ,框架自动识别加载,也可以通过属性 serveHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Config 插件


框架在应用配置计算表达式之前,执行该插件。通过 Config 插件,我们可以动态添加一些特殊的应用配置。比如使用 Config 插件为应用配置动态添加阿里云账号 ID


import { ConfigContext } from '@malagu/cli';
import { ProfileProvider, Profile } from './profile-provider';

export default async (context: ConfigContext) => {
    const { config } = context;
    if (config.mode && config.mode.includes('remote')) {
        const p: Profile = config.malagu['fc-adapter'].profile || {};
        if (!p.accountId) {
            const profileProvider = new ProfileProvider();
            const profile = await profileProvider.provide(true);
            config.malagu['fc-adapter'].profile = {
                accountId: profile.accountId,
                defaultRegion: profile.defaultRegion,
            };
        }
    }
};

插件代码存放到项目目录 src/config-hook.ts 或者 src/hooks/config.ts ,框架自动识别加载,也可以通过属性 configHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息:


Init 插件


执行 malagu init 命令的时候,执行该插件。比如:


import { InitContext } from '@malagu/cli';

export default async (context: InitContext) => {
    ...
};

插件代码存放到项目目录 src/init-hook.ts 或者 src/hooks/init.ts ,框架自动识别加载,也可以通过属性 initHooks 自定义其他目录位置。


通过命令行上下文,我们可以取到一下相关信息: