From f0c6380b0ca0f6c1edc05b4e86b0ca88faa34ed2 Mon Sep 17 00:00:00 2001 From: XGHeaven Date: Thu, 12 Dec 2024 16:34:48 +0800 Subject: [PATCH 01/13] feat: support htmlGeneratingMode option (#7032) --- .changeset/angry-carrots-occur.md | 5 +++++ examples/basic-project/compatHtml.config.mts | 9 +++++++++ packages/ice/src/bundler/config/output.ts | 6 +++++- packages/ice/src/config.ts | 2 +- packages/ice/src/types/userConfig.ts | 15 +++++++++++++- packages/ice/src/utils/generateEntry.ts | 19 ++++++++++++++---- tests/integration/basic-project.test.ts | 10 ++++++++++ website/docs/guide/basic/config.md | 21 +++++++++++++++++++- 8 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 .changeset/angry-carrots-occur.md create mode 100644 examples/basic-project/compatHtml.config.mts diff --git a/.changeset/angry-carrots-occur.md b/.changeset/angry-carrots-occur.md new file mode 100644 index 0000000000..6790e4df52 --- /dev/null +++ b/.changeset/angry-carrots-occur.md @@ -0,0 +1,5 @@ +--- +'@ice/app': patch +--- + +feat: add htmlGenerating `mode` option diff --git a/examples/basic-project/compatHtml.config.mts b/examples/basic-project/compatHtml.config.mts new file mode 100644 index 0000000000..e039dcaadd --- /dev/null +++ b/examples/basic-project/compatHtml.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from '@ice/app'; +import defaultConfig from './ice.config.mjs'; + +export default defineConfig(() => ({ + ...defaultConfig, + htmlGenerating: { + mode: 'compat' + } +})); diff --git a/packages/ice/src/bundler/config/output.ts b/packages/ice/src/bundler/config/output.ts index 56ecbd9393..a393f82798 100644 --- a/packages/ice/src/bundler/config/output.ts +++ b/packages/ice/src/bundler/config/output.ts @@ -5,6 +5,7 @@ import injectInitialEntry from '../../utils/injectInitialEntry.js'; import { SERVER_OUTPUT_DIR } from '../../constant.js'; import { logger } from '../../utils/logger.js'; import type { BundlerOptions } from '../types.js'; +import type { HtmlGeneratingMode } from '../../types/index.js'; export async function getOutputPaths(options: { rootDir: string; @@ -21,7 +22,8 @@ export async function getOutputPaths(options: { } } if (serverEntry && userConfig.htmlGenerating) { - outputPaths = await buildCustomOutputs(rootDir, outputDir, serverEntry, bundleOptions); + const htmlGeneratingMode = typeof userConfig.htmlGenerating === 'boolean' ? undefined : userConfig.htmlGenerating?.mode; + outputPaths = await buildCustomOutputs(rootDir, outputDir, serverEntry, bundleOptions, htmlGeneratingMode); } return outputPaths; } @@ -37,6 +39,7 @@ async function buildCustomOutputs( outputDir: string, serverEntry: string, bundleOptions: Pick, + generatingMode?: HtmlGeneratingMode, ) { const { userConfig, appConfig, routeManifest } = bundleOptions; const { ssg } = userConfig; @@ -52,6 +55,7 @@ async function buildCustomOutputs( renderMode: ssg ? 'SSG' : undefined, routeType: appConfig?.router?.type, routeManifest, + generatingMode, }); if (routeType === 'memory' && userConfig?.routes?.injectInitialEntry) { injectInitialEntry(routeManifest, outputDir); diff --git a/packages/ice/src/config.ts b/packages/ice/src/config.ts index 0dd14ca72b..c754ce74a5 100644 --- a/packages/ice/src/config.ts +++ b/packages/ice/src/config.ts @@ -508,7 +508,7 @@ const userConfig = [ }, { name: 'htmlGenerating', - validation: 'boolean', + validation: 'boolean|object', defaultValue: true, }, ]; diff --git a/packages/ice/src/types/userConfig.ts b/packages/ice/src/types/userConfig.ts index ae252c00a9..f5fb319391 100644 --- a/packages/ice/src/types/userConfig.ts +++ b/packages/ice/src/types/userConfig.ts @@ -49,6 +49,19 @@ interface Fetcher { method?: string; } +export type HtmlGeneratingMode = 'cleanUrl' | 'compat'; + +export interface HtmlGeneratingConfig { + /** + * Control how file structure to generation html. + * Route: '/' '/foo' '/foo/bar' + * `cleanUrl`: '/index.html' '/foo.html' '/foo/bar.html' + * `compat`: '/index.html' '/foo/index.html' '/foo/bar/index.html' + * @default 'cleanUrl' + */ + mode?: HtmlGeneratingMode; +} + export interface UserConfig { /** * Feature polyfill for legacy browsers, which can not polyfilled by core-js. @@ -178,7 +191,7 @@ export interface UserConfig { * HTML will not be generated when build, If it is false. * @see https://v3.ice.work/docs/guide/basic/config#htmlgenerating */ - htmlGenerating?: boolean; + htmlGenerating?: boolean | HtmlGeneratingConfig; /** * Choose a style of souce mapping to enhance the debugging process. * @see https://v3.ice.work/docs/guide/basic/config#sourcemap diff --git a/packages/ice/src/utils/generateEntry.ts b/packages/ice/src/utils/generateEntry.ts index 198e59d8c1..5e9038fe51 100644 --- a/packages/ice/src/utils/generateEntry.ts +++ b/packages/ice/src/utils/generateEntry.ts @@ -1,6 +1,7 @@ import * as path from 'path'; import fse from 'fs-extra'; import type { ServerContext, RenderMode, AppConfig } from '@ice/runtime'; +import type { HtmlGeneratingMode } from '../types/index.js'; import dynamicImport from './dynamicImport.js'; import { logger } from './logger.js'; import type RouteManifest from './routeManifest.js'; @@ -12,6 +13,7 @@ interface Options { documentOnly: boolean; routeType: AppConfig['router']['type']; renderMode?: RenderMode; + generatingMode?: HtmlGeneratingMode; routeManifest: RouteManifest; } @@ -28,6 +30,7 @@ export default async function generateEntry(options: Options): Promise { expect(bundleContent.includes('__IS_NODE__')).toBe(false); expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/favicon.ico`))).toBe(true); expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/js/data-loader.js`))).toBe(true); + expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/index.html`))).toBe(true); + expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/blog.html`))).toBe(true); const jsonContent = fs.readFileSync(path.join(__dirname, `../../examples/${example}/build/assets-manifest.json`), 'utf-8'); expect(JSON.parse(jsonContent).pages.about.includes('js/framework.js')).toBeFalsy(); const dataLoaderPath = path.join(__dirname, `../../examples/${example}/build/js/data-loader.js`); @@ -69,6 +71,14 @@ describe(`build ${example}`, () => { expect((await page.$$text('h2')).length).toEqual(0); }); + test('using "compat" html generating mode', async () => { + await buildFixture(example, { + config: 'compatHtml.config.mts', + }); + expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/index.html`))).toBeTruthy(); + expect(fs.existsSync(path.join(__dirname, `../../examples/${example}/build/blog/index.html`))).toBeTruthy(); + }); + afterAll(async () => { await browser.close(); }); diff --git a/website/docs/guide/basic/config.md b/website/docs/guide/basic/config.md index 25ad5ed6fa..4bc7366dd7 100644 --- a/website/docs/guide/basic/config.md +++ b/website/docs/guide/basic/config.md @@ -620,11 +620,30 @@ export default defineConfig(() => ({ ### htmlGenerating -- 类型:`boolean` +- 类型:`boolean | object` - 默认值:`true` 如果产物不想生成 html,可以设置为 `false`,在 SSG 开启的情况下,强制关闭 html 生成,将导致 SSG 失效。 +传入 `true` 则与 `{}` 效果一致。 + +#### htmlGenerating.mode + +- 类型: `'cleanUrl' | 'compat'` +- 默认值 `'cleanUrl'` + +配置 HTML 生成文件的规则,避免在某些服务器下出现非首页内容刷新后 404 的情况。目前主要由两种,分别是: + +- `cleanUrl` 生成的文件路径和路由一致。通常用于支持此模式的现代服务器,即自动省略 `.html` 后缀 +- `compat` 生成兼容模式的路径文件,通常用于一些只能省略 `index.html` 的服务器 + +具体区别可以参照下表: + +| Route | `/` | `/foo` | `/foo/bar` | +|------------|---------------|-------------------|-----------------------| +| `cleanUrl` | `/index.html` | `/foo.html` | `/foo/bar.html` | +| `compat` | `/index.html` | `/foo/index.html` | `/foo/bar/index.html` | + ### plugins - 类型:`PluginList` From 7be032109f6d9e525970dde0e9ddada2f464ba07 Mon Sep 17 00:00:00 2001 From: lxzy-yun <924305751@qq.com> Date: Wed, 18 Dec 2024 13:48:34 +0800 Subject: [PATCH 02/13] Update hooks.ts (#7033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去掉默认空数组的赋值,避免 ts 报错 --- packages/plugin-request/src/hooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-request/src/hooks.ts b/packages/plugin-request/src/hooks.ts index 36da793462..100219545b 100644 --- a/packages/plugin-request/src/hooks.ts +++ b/packages/plugin-request/src/hooks.ts @@ -8,7 +8,7 @@ interface RequestResult extends Result { requestAsync: Result['runAsync']; } -export function useRequest( +export function useRequest( service: string | AxiosRequestConfig | Service, options?: Options, plugins?: Plugin[]) { From dcc100457f5f6eb17bfa533c7bf5b752a4465563 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 20 Feb 2025 18:07:41 +0800 Subject: [PATCH 03/13] fix: update es-module-lexer (#7050) * fix: update es-module-lexer * fix: ci --- packages/bundles/package.json | 2 +- pnpm-lock.yaml | 17 ++++++++--------- tests/utils/browser.ts | 6 +++++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/bundles/package.json b/packages/bundles/package.json index 27b3b5a177..88603a59bd 100644 --- a/packages/bundles/package.json +++ b/packages/bundles/package.json @@ -60,7 +60,7 @@ "css-loader": "6.7.1", "css-minimizer-webpack-plugin": "3.4.1", "cssnano": "^5.1.7", - "es-module-lexer": "0.10.5", + "es-module-lexer": "1.6.0", "esbuild-register": "3.4.1", "eslint": "^8.14.0", "eslint-webpack-plugin": "3.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de3814851c..fdfcdbf883 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1478,8 +1478,8 @@ importers: specifier: ^6.0.3 version: 6.0.3 es-module-lexer: - specifier: 0.10.5 - version: 0.10.5 + specifier: 1.6.0 + version: 1.6.0 esbuild-register: specifier: 3.4.1 version: 3.4.1(esbuild@0.17.16) @@ -12691,13 +12691,12 @@ packages: stop-iteration-iterator: 1.0.0 dev: true - /es-module-lexer@0.10.5: - resolution: {integrity: sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==} - dev: true - /es-module-lexer@1.2.1: resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==} + /es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} @@ -23578,7 +23577,7 @@ packages: browserslist: 4.22.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 - es-module-lexer: 1.2.1 + es-module-lexer: 1.6.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -23657,7 +23656,7 @@ packages: browserslist: 4.21.5 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 - es-module-lexer: 1.2.1 + es-module-lexer: 1.6.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -23696,7 +23695,7 @@ packages: browserslist: 4.21.5 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 - es-module-lexer: 1.2.1 + es-module-lexer: 1.6.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 diff --git a/tests/utils/browser.ts b/tests/utils/browser.ts index 5591e9921d..be37c830ce 100644 --- a/tests/utils/browser.ts +++ b/tests/utils/browser.ts @@ -84,7 +84,11 @@ export default class Browser { } async start() { - this.browser = await puppeteer.launch(); + this.browser = await puppeteer.launch( + { + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }, + ); } async close() { From b75f55e903988c1d19f177142fa624243e2ab10a Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Fri, 21 Feb 2025 14:48:12 +0800 Subject: [PATCH 04/13] feat: support suspense event --- examples/with-suspense-ssr/src/document.tsx | 8 +++++++- packages/runtime/src/Suspense.tsx | 14 +++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/examples/with-suspense-ssr/src/document.tsx b/examples/with-suspense-ssr/src/document.tsx index 313c827d5c..3003b2c712 100644 --- a/examples/with-suspense-ssr/src/document.tsx +++ b/examples/with-suspense-ssr/src/document.tsx @@ -13,7 +13,13 @@ function Document() {
-