diff --git a/.changeset/gold-pumas-perform.md b/.changeset/gold-pumas-perform.md
new file mode 100644
index 000000000000..125c850d3f99
--- /dev/null
+++ b/.changeset/gold-pumas-perform.md
@@ -0,0 +1,6 @@
+---
+'@modern-js/app-tools': patch
+---
+
+feat: support deployment for github pages
+feat: 支持使用 github pages 部署
diff --git a/packages/document/main-doc/docs/en/guides/basic-features/deploy.mdx b/packages/document/main-doc/docs/en/guides/basic-features/deploy.mdx
index 3429bfd0089a..1a89062219a1 100644
--- a/packages/document/main-doc/docs/en/guides/basic-features/deploy.mdx
+++ b/packages/document/main-doc/docs/en/guides/basic-features/deploy.mdx
@@ -6,7 +6,7 @@ sidebar_position: 15
Currently, Modern.js offers two deployment way:
- You can host your application in a container that includes a Node.js environment on your own, which provides flexibility for the deployment of the application.
-- You can also deploy your application through a platform. Currently, Modern.js supports the [Netlify](https://www.netlify.com/) and [Vercel](https://vercel.com/).
+- You can also deploy your application through a platform. Currently, Modern.js officially supports deployment on [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), and [Github pages](https://pages.github.com/).
:::info
Currently, Modern.js only supports running in a Node.js environment. Support for more runtime environments will be provided in the future.
@@ -110,6 +110,11 @@ Add the following content to `netlify.toml`:
command = "modern deploy"
```
+:::info
+You can refer to the [deployment project example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr).
+
+:::
+
Now, add a project to the Netlify platform and deploy it!
### Full Stack Project
@@ -129,7 +134,8 @@ Full-stack projects refer to projects that use Custom Web Server, SSR or BFF. Th
```
:::info
-Currently, Modern.js does not support deployment on Netlify Edge Functions. We will support it in future versions.
+1. Currently, Modern.js does not support deployment on Netlify Edge Functions. We will support it in future versions.
+2. You can refer to the [deployment project example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-ssr).
:::
@@ -213,6 +219,11 @@ Commit your project to git, select Framework Preset as `Other` on the Vercel pla
+:::info
+You can refer to the [deployment project examples](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr).
+
+:::
+
### Full Stack Project
Full-stack projects refer to projects that use Custom Web Server, SSR or BFF. These projects need to be deployed on **Vercel Functions**.
@@ -220,14 +231,12 @@ Full-stack projects refer to projects that use Custom Web Server, SSR or BFF. Th
In addition to configuring `vercel.json` in the same way as a [pure front-end project](#pure-front-end-project), there are two points to note for full-stack projects:
1. Currently, Modern.js does not support deploying BFF projects on the Vercel platform. We will support it in future versions.
-2. When deploying on Vercel platform, the default node runtime is `20.x`, it is recommended to choose `18.x` when deploying full-stack projects,
-see [Serverless Function contains invalid runtime error](https://vercel.com/guides/serverless-function-contains-invalid-runtime-error), you can modify `package.json` to specify the version:
-```json title="package.json"
-"engines": {
- "node": "18.x"
-}
-```
+2. The Node.js version for function execution is determined by the project configuration on the Vercel platform.
+:::info
+You can refer to the [deployment project examples](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-ssr).
+
+:::
### Monorepo
@@ -284,6 +293,48 @@ Add the following content to the `packages/app/vercel.json` file:
Just submit your code and deploy it using the Vercel platform.
+## Github Pages
+
+If you're creating a GitHub Pages for a repository without a custom domain, the page URL will follow this format: `http://.github.io/`. Therefore, you need to add the following configuration in `modern.config.ts`:
+
+```
+import { defineConfig } from '@modern-js/app-tools';
+
+export default defineConfig({
+ //...
+ server: {
+ baseUrl: "/"
+ },
+ output: {
+ assetPrefix: "/",
+ }
+});
+```
+
+GitHub Pages supports two deployment ways: branch deployment or GitHub Actions deployment.
+
+For branch deployment, follow these steps:
+
+1. In the GitHub repository, navigate to Settings > Pages > Source > Deploy from a branch
+2. Install the `gh-pages` as devDependency
+3. Add the following script to `package.json`
+```
+"scripts": {
+ //...
+ "deploy:gh-pages": "MODERNJS_DEPLOY=ghPages modern deploy && gh-pages -d .output"
+}
+```
+
+4. Run `npm run deploy:gh-pages`
+
+:::info
+1. Running `MODERNJS_DEPLOY=ghPages modern deploy` will build the production output for GitHub in the .output directory.
+2. You can refer to the [project](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)
+:::
+
+For GitHub Actions deployment, select Settings > Pages > Source > GitHub Actions, and add a workflow file to the project. You can refer to the [example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr).
+
+
## Using Self-Built Node.js Server
Typically, we recommend using the built-in Node.js server of Modern.js to deploy applications. It supports hosting both pure frontend and full-stack projects, ensuring consistent performance in both development and production environments.
diff --git a/packages/document/main-doc/docs/zh/guides/basic-features/deploy.mdx b/packages/document/main-doc/docs/zh/guides/basic-features/deploy.mdx
index fda47d98b1aa..16f91715007f 100644
--- a/packages/document/main-doc/docs/zh/guides/basic-features/deploy.mdx
+++ b/packages/document/main-doc/docs/zh/guides/basic-features/deploy.mdx
@@ -7,7 +7,7 @@ sidebar_position: 15
目前,Modern.js 提供了两种部署方式:
- 你可以将应用自行托管在包含 Node.js 环境的容器中,这为应用提供了部署的灵活性。
-- 你也可以通过平台部署应用,目前 Modern.js 官方支持了 [Netlify](https://www.netlify.com/) 和 [Vercel](https://vercel.com/) 平台。
+- 你也可以通过平台部署应用,目前 Modern.js 官方支持了 [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/) 和 [Github pages](https://pages.github.com/) 平台。
:::info
目前 Modern.js 仅支持在 Node.js 环境中运行,未来将提供更多运行时环境的支持。
@@ -26,6 +26,7 @@ MODERNJS_DEPLOY=netlify npx modern deploy
在 Modern.js 官方支持的部署平台中部署时,无需指定环境变量。
:::
+
## ModernJS 内置 Node.js 服务器
### 单仓库项目
@@ -107,6 +108,11 @@ Netlify 是一个流行的 Web 开发平台,专为构建、发布和维护现
command = "modern deploy"
```
+:::info
+你可参考部署[项目示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)。
+
+:::
+
在 Netlify 平台上添加项目,部署即可。
### 全栈项目
@@ -126,10 +132,14 @@ Netlify 是一个流行的 Web 开发平台,专为构建、发布和维护现
```
:::info
-目前 Modern.js 还不支持在 Netlify Edge Functions 进行部署,我们将在后续的版本中支持。
+1. 目前 Modern.js 还不支持在 Netlify Edge Functions 进行部署,我们将在后续的版本中支持。
+2. 你可参考部署[项目示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-ssr)。
+
:::
+
+
### Monorepo 项目
:::info
@@ -207,6 +217,11 @@ Vercel 是一个面向现代 Web 应用的部署平台,它提供了丰富的
+:::info
+你可参考部署[项目示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)。
+
+:::
+
### 全栈项目
全栈项目是指使用了自定义 Web Server、SSR、BFF 的项目,这些项目需要部署在 **Vercel Functions** 上。
@@ -214,12 +229,13 @@ Vercel 是一个面向现代 Web 应用的部署平台,它提供了丰富的
全栈项目除了按照[纯前端项目](#纯前端项目)的方式配置 `vercel.json` 外,有两点需要注意:
1. 当前,Modern.js 还不支持在 Vercel 平台上部署 BFF 项目,我们将在后续的版本中支持。
-2. Vercel 平台部署时,默认 node 运行时为 `20.x`,部署全栈项目时建议选择 `18.x`,具体原因详见[Serverless Function contains invalid runtime error](https://vercel.com/guides/serverless-function-contains-invalid-runtime-error),你可以修改 `package.json` 指定版本:
-```json title="package.json"
-"engines": {
- "node": "18.x"
-}
-```
+2. 函数运行的 node.js 版本由项目在 Vercel 平台配置决定。
+
+
+:::info
+你可参考部署[项目示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-ssr)。
+
+:::
### Monorepo 项目
@@ -273,6 +289,38 @@ Vercel 是一个面向现代 Web 应用的部署平台,它提供了丰富的
提交你的代码,使用 Vercel 平台部署即可。
+## Github Pages
+
+如果你要为一个仓库常见 Github 页面,并且你没有自定义域名,则该页面的 URL 将会是以下格式:`http://.github.io/`,所以需要在 `modern.config.ts` 中添加
+以下配置:
+```ts
+import { defineConfig } from '@modern-js/app-tools';
+
+export default defineConfig({
+ //...
+ server:{
+ baseUrl: "/"
+ },
+ output: {
+ assetPrefix: "/",
+ }
+});
+```
+
+Github Pages 支持两种部署方式,通过分支部署或通过 Github Actions 部署,如果通过分支部署,可以使用以下步骤:
+1. 在 github 仓库中,选择 `Settings > Pages > Source > Deploy from a branch`。
+2. 安装 `gh-pages` 依赖作为开发依赖。
+3. 在 package.json 的 `scripts` 中添加 `"deploy:gh-pages": "MODERNJS_DEPLOY=ghPages modern deploy && gh-pages -d .output"`。
+4. 执行 `npm run deploy:gh-pages`。
+
+:::info
+1. 执行 `MODERNJS_DEPLOY=ghPages modern deploy`,Modern.js 会把可用于 github 部署的产物构建到 `.output` 目录。
+2. 可以参考项目[示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)。
+
+:::
+
+如果通过 Github Actions 部署,可以选择 Settings > Pages > Source > GitHub Actions,并在项目中添加 workflow 文件,可参考[示例](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)。
+
## 自建 Node.js 服务器
diff --git a/packages/solutions/app-tools/src/plugins/deploy/index.ts b/packages/solutions/app-tools/src/plugins/deploy/index.ts
index 11f9811ac345..1a21a5aa1aaf 100644
--- a/packages/solutions/app-tools/src/plugins/deploy/index.ts
+++ b/packages/solutions/app-tools/src/plugins/deploy/index.ts
@@ -5,15 +5,16 @@ import type {
CliPluginFuture,
} from '../../types';
import type { AppToolsContext } from '../../types/new';
+import { createGhPagesPreset } from './platforms/gh-pages';
import { createNetlifyPreset } from './platforms/netlify';
import { createNodePreset } from './platforms/node';
import { createVercelPreset } from './platforms/vercel';
import { getProjectUsage } from './utils';
-
type DeployPresetCreators = {
node: typeof createNodePreset;
vercel: typeof createVercelPreset;
netlify: typeof createNetlifyPreset;
+ ghPages: typeof createGhPagesPreset;
};
type DeployTarget = keyof DeployPresetCreators;
@@ -22,6 +23,7 @@ const deployPresets: DeployPresetCreators = {
node: createNodePreset,
vercel: createVercelPreset,
netlify: createNetlifyPreset,
+ ghPages: createGhPagesPreset,
};
async function getDeployPreset(
diff --git a/packages/solutions/app-tools/src/plugins/deploy/platforms/gh-pages.ts b/packages/solutions/app-tools/src/plugins/deploy/platforms/gh-pages.ts
new file mode 100644
index 000000000000..dd27a7baa847
--- /dev/null
+++ b/packages/solutions/app-tools/src/plugins/deploy/platforms/gh-pages.ts
@@ -0,0 +1,84 @@
+import path from 'path';
+import type { ServerRoute } from '@modern-js/types';
+import { fs } from '@modern-js/utils';
+import { logger } from '@modern-js/utils';
+import type { CreatePreset } from './platform';
+
+async function reorganizeHtmlFiles(
+ routes: ServerRoute[],
+ baseDir: string,
+ baseUrl = '/',
+) {
+ if (!routes || !Array.isArray(routes)) {
+ logger.error('Invalid server routes');
+ return;
+ }
+
+ await fs.ensureDir(baseDir);
+
+ const baseUrlRegexp = new RegExp(`^${baseUrl}\\/?`);
+
+ const collectedEntryPaths = new Set();
+
+ const copyPromises = routes.map(async route => {
+ const { urlPath, entryPath } = route;
+
+ if (!entryPath) {
+ logger.warn(`Route ${urlPath} does not specify entryPath, skipping`);
+ return;
+ }
+
+ if (collectedEntryPaths.has(entryPath)) {
+ return;
+ }
+
+ collectedEntryPaths.add(entryPath);
+
+ const sourceHtmlPath = path.join(baseDir, entryPath);
+
+ if (!(await fs.pathExists(sourceHtmlPath))) {
+ logger.error(`Source HTML file does not exist: ${sourceHtmlPath}`);
+ return;
+ }
+
+ const targetDir = urlPath.replace(baseUrlRegexp, '');
+
+ await fs.ensureDir(path.dirname(targetDir));
+
+ const targetHtmlPath = path.join(baseDir, targetDir, 'index.html');
+
+ try {
+ await fs.move(sourceHtmlPath, targetHtmlPath);
+ } catch (error: any) {
+ logger.error(`Failed to copy HTML file: ${error.message}`);
+ }
+ });
+
+ await Promise.all(copyPromises);
+}
+
+export const createGhPagesPreset: CreatePreset = (appContext, modernConfig) => {
+ const { serverRoutes, appDirectory, distDirectory } = appContext;
+
+ const {
+ server: { baseUrl },
+ } = modernConfig;
+ const outputDirectory = path.join(appDirectory, '.output');
+
+ return {
+ name: 'gh-pages',
+ async prepare() {
+ await fs.remove(outputDirectory);
+ },
+ async writeOutput() {
+ await fs.copy(distDirectory, outputDirectory);
+ },
+ async end() {
+ await reorganizeHtmlFiles(
+ serverRoutes,
+ outputDirectory,
+ Array.isArray(baseUrl) ? baseUrl[0] : baseUrl,
+ );
+ },
+ };
+};
diff --git a/packages/solutions/app-tools/src/plugins/deploy/platforms/vercel.ts b/packages/solutions/app-tools/src/plugins/deploy/platforms/vercel.ts
index 122c40e1efb3..fe0457b928d0 100644
--- a/packages/solutions/app-tools/src/plugins/deploy/platforms/vercel.ts
+++ b/packages/solutions/app-tools/src/plugins/deploy/platforms/vercel.ts
@@ -101,8 +101,10 @@ export const createVercelPreset: CreatePreset = (
},
});
+ const nodeVersion = process.versions.node.split('.')[0];
+
await fse.writeJSON(path.join(funcsDirectory, '.vc-config.json'), {
- runtime: 'nodejs16.x',
+ runtime: `nodejs${nodeVersion}.x`,
handler: 'index.js',
launcherType: 'Nodejs',
shouldAddHelpers: false,