Skip to content

Commit

Permalink
feat: bff support independent project
Browse files Browse the repository at this point in the history
  • Loading branch information
keepview committed Dec 2, 2024
1 parent 30f77f6 commit e778965
Show file tree
Hide file tree
Showing 39 changed files with 986 additions and 159 deletions.
13 changes: 13 additions & 0 deletions .changeset/mean-bobcats-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@modern-js/create-request': patch
'@modern-js/app-tools': patch
'@modern-js/bff-runtime': patch
'@modern-js/bff-core': patch
'@modern-js/plugin-bff': patch
'@modern-js/server': patch
'@modern-js/core': patch
---

feat: bff support independent project

feat: bff 支持跨项目调用
2 changes: 2 additions & 0 deletions packages/cli/core/src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface IAppContext {
serverRoutes: ServerRoute[];
/** Whether to use api only mode */
apiOnly: boolean;
/** prefix for independent bff projects */
indepBffPrefix?: string;
/** The Builder instance */
builder?: UniBuilderInstance | UniBuilderWebpackInstance;
/** Tools type of the current project */
Expand Down
205 changes: 160 additions & 45 deletions packages/cli/plugin-bff/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,133 @@ import type { AppTools, CliPlugin } from '@modern-js/app-tools';
import { ApiRouter } from '@modern-js/bff-core';
import { compile } from '@modern-js/server-utils';
import type { ServerRoute } from '@modern-js/types';
import { fs, API_DIR, SHARED_DIR, normalizeOutputPath } from '@modern-js/utils';
import {
fs,
API_DIR,
SHARED_DIR,
logger,
normalizeOutputPath,
} from '@modern-js/utils';
import clientGenerator from './utils/client-generator';
import pluginGenerator from './utils/plugin-generator';

const DEFAULT_API_PREFIX = '/api';
const TS_CONFIG_FILENAME = 'tsconfig.json';

export const bffPlugin = (): CliPlugin<AppTools> => ({
export interface BffPluginOptions {
projectType: 'web' | 'api';
fetchDomain?: string;
}

export const bffPlugin = (options?: BffPluginOptions): CliPlugin<AppTools> => ({
name: '@modern-js/plugin-bff',
setup: api => {
const isApiProject = options?.projectType === 'api';

const compileApi = async () => {
const {
appDirectory,
distDirectory,
apiDirectory,
sharedDirectory,
moduleType,
} = api.useAppContext();
const modernConfig = api.useResolvedConfigContext();

const distDir = path.resolve(distDirectory);
const apiDir = apiDirectory || path.resolve(appDirectory, API_DIR);
const sharedDir =
sharedDirectory || path.resolve(appDirectory, SHARED_DIR);
const tsconfigPath = path.resolve(appDirectory, TS_CONFIG_FILENAME);

const sourceDirs = [];
if (await fs.pathExists(apiDir)) {
sourceDirs.push(apiDir);
}

if (await fs.pathExists(sharedDir)) {
sourceDirs.push(sharedDir);
}

const { server } = modernConfig;
const { alias } = modernConfig.source;
const { babel } = modernConfig.tools;

if (sourceDirs.length > 0) {
await compile(
appDirectory,
{
server,
alias,
babelConfig: babel,
},
{
sourceDirs,
distDir,
tsconfigPath,
moduleType,
},
);
}
};

const generator = async () => {
const { appDirectory, apiDirectory, lambdaDirectory, port } =
api.useAppContext();

const modernConfig = api.useResolvedConfigContext();
const { bff } = modernConfig || {};
const prefix = bff?.prefix || DEFAULT_API_PREFIX;
const httpMethodDecider = bff?.httpMethodDecider;

const apiRouter = new ApiRouter({
apiDir: apiDirectory,
appDir: appDirectory,
lambdaDir: lambdaDirectory,
prefix,
httpMethodDecider,
isBuild: true,
});

const lambdaDir = apiRouter.getLambdaDir();
const existLambda = apiRouter.isExistLambda();

const ops = {
prefix,
appDir: appDirectory,
apiDir: apiDirectory,
lambdaDir,
existLambda,
port,
requestCreator: (bff as any)?.requestCreator,
target: 'client',
httpMethodDecider,
domain: options?.fetchDomain,
};

await clientGenerator(ops);
await pluginGenerator(ops.prefix);
};

return {
config() {
return {
tools: {
bundlerChain: (chain, { CHAIN_ID, isServer }) => {
const { port, appDirectory, apiDirectory, lambdaDirectory } =
api.useAppContext();
const {
port,
appDirectory,
apiDirectory,
lambdaDirectory,
indepBffPrefix,
} = api.useAppContext() as any;

const modernConfig = api.useResolvedConfigContext();

const { bff } = modernConfig || {};
const prefix = bff?.prefix || DEFAULT_API_PREFIX;
const prefix =
indepBffPrefix || bff?.prefix || DEFAULT_API_PREFIX;

const httpMethodDecider = bff?.httpMethodDecider;

const apiRouter = new ApiRouter({
Expand Down Expand Up @@ -71,13 +180,20 @@ export const bffPlugin = (): CliPlugin<AppTools> => ({
source: {
moduleScopes: [`./${API_DIR}`, /create-request/],
},
// dev: {
// hmr: isApiProject
// ? false
// : api.useResolvedConfigContext()?.dev?.hmr,
// },
};
},
modifyServerRoutes({ routes }) {
const modernConfig = api.useResolvedConfigContext();

const { indepBffPrefix } = api.useAppContext() as any;

const { bff } = modernConfig || {};
const prefix = bff?.prefix || '/api';
const prefix = indepBffPrefix || bff?.prefix || '/api';

const prefixList: string[] = [];

Expand Down Expand Up @@ -117,51 +233,50 @@ export const bffPlugin = (): CliPlugin<AppTools> => ({

return { plugins };
},
async beforeDev() {
if (isApiProject) {
await compileApi();
await generator();
}
},

async afterBuild() {
const {
appDirectory,
distDirectory,
apiDirectory,
sharedDirectory,
moduleType,
} = api.useAppContext();
const modernConfig = api.useResolvedConfigContext();

const distDir = path.resolve(distDirectory);
const apiDir = apiDirectory || path.resolve(appDirectory, API_DIR);
const sharedDir =
sharedDirectory || path.resolve(appDirectory, SHARED_DIR);
const tsconfigPath = path.resolve(appDirectory, TS_CONFIG_FILENAME);

const sourceDirs = [];
if (await fs.pathExists(apiDir)) {
sourceDirs.push(apiDir);
await compileApi();
if (isApiProject) {
await generator();
}
},
async watchFiles() {
const appContext = api.useAppContext();
const config = api.useResolvedConfigContext();
const { generateWatchFiles } = require('@modern-js/app-tools');
const files = await generateWatchFiles(
appContext,
config.source.configDir,
);

if (await fs.pathExists(sharedDir)) {
sourceDirs.push(sharedDir);
if (isApiProject) {
files.push(appContext.apiDirectory);
}

const { server } = modernConfig;
const { alias } = modernConfig.source;
const { babel } = modernConfig.tools;

if (sourceDirs.length > 0) {
await compile(
appDirectory,
{
server,
alias,
babelConfig: babel,
},
{
sourceDirs,
distDir,
tsconfigPath,
moduleType,
},
);
return files;
},

async fileChange(e: {
filename: string;
eventType: string;
isPrivate: boolean;
}) {
const { filename, eventType, isPrivate } = e;

if (
isApiProject &&
!isPrivate &&
(eventType === 'change' || eventType === 'unlink') &&
filename.startsWith('api/')
) {
await compileApi();
await generator();
}
},
};
Expand Down
20 changes: 17 additions & 3 deletions packages/cli/plugin-bff/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,21 @@ export default (): ServerPlugin => ({
return {
async prepare() {
const appContext = api.useAppContext();
const { appDirectory, distDirectory, render } = appContext;
const {
appDirectory,
distDirectory,
render,
indepBffPrefix,
apiDirectory,
} = appContext;
const root = isProd() ? distDirectory : appDirectory;
const apiPath = path.resolve(root || process.cwd(), API_DIR);

let apiPath = path.resolve(root || process.cwd(), API_DIR);

if (indepBffPrefix && apiDirectory) {
// independent bff scenario
apiPath = apiDirectory;
}
apiAppPath = path.resolve(apiPath, API_APP_NAME);

const apiMod = await requireExistModule(apiAppPath);
Expand All @@ -53,7 +65,8 @@ export default (): ServerPlugin => ({

/** bind api server */
const config = api.useConfigContext();
const prefix = config?.bff?.prefix || '/api';
const prefix =
(indepBffPrefix as string) || config?.bff?.prefix || '/api';
const enableHandleWeb = config?.bff?.enableHandleWeb;
const httpMethodDecider = config?.bff?.httpMethodDecider;

Expand Down Expand Up @@ -131,6 +144,7 @@ export default (): ServerPlugin => ({
const apiDir = path.resolve(pwd, API_DIR);
const appContext = api.useAppContext();
const { apiDirectory, lambdaDirectory } = appContext;

apiRouter = new ApiRouter({
appDir: pwd,
apiDir: (apiDirectory as string) || apiDir,
Expand Down
Loading

0 comments on commit e778965

Please sign in to comment.