From 9926faae8860a0824db35a2e434f8334979f7780 Mon Sep 17 00:00:00 2001 From: luhc228 Date: Wed, 5 Jun 2024 10:24:10 +0800 Subject: [PATCH 1/4] feat: add es2021 target (#6899) * feat: add es2021 target * chore: add lint ignore * fix: not define main field in exports field * chore: changeset --- .changeset/rare-shirts-check.md | 5 ++ .eslintignore | 1 + packages/rax-compat/.gitignore | 1 + packages/rax-compat/build.config.mts | 3 + packages/rax-compat/package.json | 84 ++++++++++++++++++++++------ packages/rax-compat/plugin.mjs | 18 ++++++ 6 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 .changeset/rare-shirts-check.md create mode 100644 packages/rax-compat/plugin.mjs diff --git a/.changeset/rare-shirts-check.md b/.changeset/rare-shirts-check.md new file mode 100644 index 0000000000..ae9de5c0ef --- /dev/null +++ b/.changeset/rare-shirts-check.md @@ -0,0 +1,5 @@ +--- +'rax-compat': minor +--- + +feat: export es2021 output diff --git a/.eslintignore b/.eslintignore index 93f43288ef..b95cb05b89 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,6 +14,7 @@ coverage/ /packages/*/lib/ /packages/*/esm/ /packages/*/es2017/ +/packages/*/es2021/ **/tests/libs/*.js # 忽略第三方包 diff --git a/packages/rax-compat/.gitignore b/packages/rax-compat/.gitignore index eb97fce6bd..7f2fa9cfec 100644 --- a/packages/rax-compat/.gitignore +++ b/packages/rax-compat/.gitignore @@ -1,3 +1,4 @@ es2017/ +es2021/ dist/ esm/ diff --git a/packages/rax-compat/build.config.mts b/packages/rax-compat/build.config.mts index 0bc2d21eaf..e0e61e1530 100644 --- a/packages/rax-compat/build.config.mts +++ b/packages/rax-compat/build.config.mts @@ -5,4 +5,7 @@ export default defineConfig({ formats: ['esm', 'es2017'], }, sourceMaps: process.env.NODE_ENV === 'development', + plugins: [ + './plugin.mjs', + ], }); diff --git a/packages/rax-compat/package.json b/packages/rax-compat/package.json index 9c1ae0d9f1..add0027560 100644 --- a/packages/rax-compat/package.json +++ b/packages/rax-compat/package.json @@ -4,28 +4,78 @@ "description": "Rax compatible mode, running rax project on the react runtime.", "files": [ "esm", - "cjs", "es2017", - "dist", - "build" + "es2021" ], "type": "module", "main": "esm/index.js", "module": "esm/index.js", "exports": { - ".": "./esm/index.js", - "./children": "./esm/children.js", - "./clone-element": "./esm/clone-element.js", - "./create-class": "./esm/create-class.js", - "./create-factory": "./esm/create-factory.js", - "./create-portal": "./esm/create-portal.js", - "./find-dom-node": "./esm/find-dom-node.js", - "./is-valid-element": "./esm/is-valid-element.js", - "./unmount-component-at-node": "./esm/unmount-component-at-node.js", - "./runtime": "./esm/runtime/index.js", - "./runtime/jsx-dev-runtime": "./esm/runtime/jsx-dev-runtime.js", - "./runtime/jsx-runtime": "./esm/runtime/jsx-runtime.js", - "./es2017": "./es2017/index.js" + ".": { + "es2021": "./es2021/index.js", + "es2017": "./es2017/index.js", + "default": "./esm/index.js" + }, + "./children": { + "es2021": "./es2021/children.js", + "es2017": "./es2017/children.js", + "default": "./esm/children.js" + }, + "./clone-element": { + "es2021": "./es2021/clone-element.js", + "es2017": "./es2017/clone-element.js", + "default": "./esm/clone-element.js" + }, + "./create-class": { + "es2021": "./es2021/create-class.js", + "es2017": "./es2017/create-class.js", + "default": "./esm/create-class.js" + }, + "./create-factory": { + "es2021": "./es2021/create-factory.js", + "es2017": "./es2017/create-factory.js", + "default": "./esm/create-factory.js" + }, + "./create-portal": { + "es2021": "./es2021/create-portal.js", + "es2017": "./es2017/create-portal.js", + "default": "./esm/create-portal.js" + }, + "./find-dom-node": { + "es2021": "./es2021/find-dom-node.js", + "es2017": "./es2017/find-dom-node.js", + "default": "./esm/find-dom-node.js" + }, + "./is-valid-element": { + "es2021": "./es2021/is-valid-element.js", + "es2017": "./es2017/is-valid-element.js", + "default": "./esm/is-valid-element.js" + }, + "./unmount-component-at-node": { + "es2021": "./es2021/unmount-component-at-node.js", + "es2017": "./es2017/unmount-component-at-node.js", + "default": "./esm/unmount-component-at-node.js" + }, + "./runtime": { + "es2021": "./es2021/runtime/index.js", + "es2017": "./es2017/runtime/index.js", + "default": "./esm/runtime/index.js" + }, + "./runtime/jsx-dev-runtime": { + "es2021": "./es2021/runtime/jsx-dev-runtime.js", + "es2017": "./es2017/runtime/jsx-dev-runtime.js", + "default": "./esm/runtime/jsx-dev-runtime.js" + }, + "./runtime/jsx-runtime": { + "es2021": "./es2021/runtime/jsx-runtime.js", + "es2017": "./es2017/runtime/jsx-runtime.js", + "default": "./esm/runtime/jsx-runtime.js" + }, + "./es2017": { + "es2021": "./es2021/index.js", + "es2017": "./es2017/index.js", + "default": "./esm/index.js" + } }, "sideEffects": [ "dist/*", @@ -77,4 +127,4 @@ "author": "ice-admin@alibaba-inc.com", "license": "MIT", "homepage": "https://github.com/alibaba/ice#readme" -} \ No newline at end of file +} diff --git a/packages/rax-compat/plugin.mjs b/packages/rax-compat/plugin.mjs new file mode 100644 index 0000000000..32fd9379ab --- /dev/null +++ b/packages/rax-compat/plugin.mjs @@ -0,0 +1,18 @@ +/** + * @type {import('@ice/pkg').Plugin} + */ +const plugin = (api) => { + api.registerTask('transform-es2021', { + type: 'transform', + formats: ['es2021'], + outputDir: 'es2021', + modifySwcCompileOptions: (options => { + options.jsc.target = 'es2021'; + return options; + }), + entry: 'src/index', + sourcemap: false, + }); +}; + +export default plugin; From d073ee5adec58e1461ddbef1f5d528d03c3a5e9b Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Thu, 13 Jun 2024 13:36:25 +0800 Subject: [PATCH 2/4] feat: externals plugin for external dependencies mush easier (#6892) * feat: externals plugin for external dependencies mush easier * fix: optimize code * fix: optimize code * fix: example preset react --------- Co-authored-by: ZeroLing --- .changeset/shaggy-cherries-fail.md | 5 ++ examples/app-config/ice.config.mts | 5 +- examples/app-config/package.json | 9 +-- packages/plugin-externals/CHANGELOG.md | 5 ++ packages/plugin-externals/README.md | 45 +++++++++++ packages/plugin-externals/package.json | 36 +++++++++ packages/plugin-externals/src/index.ts | 80 +++++++++++++++++++ .../plugin-externals/src/webpack-plugin.ts | 39 +++++++++ packages/plugin-externals/tsconfig.json | 11 +++ packages/runtime/src/Document.tsx | 9 ++- pnpm-lock.yaml | 43 +++++----- 11 files changed, 258 insertions(+), 29 deletions(-) create mode 100644 .changeset/shaggy-cherries-fail.md create mode 100644 packages/plugin-externals/CHANGELOG.md create mode 100644 packages/plugin-externals/README.md create mode 100644 packages/plugin-externals/package.json create mode 100644 packages/plugin-externals/src/index.ts create mode 100644 packages/plugin-externals/src/webpack-plugin.ts create mode 100644 packages/plugin-externals/tsconfig.json diff --git a/.changeset/shaggy-cherries-fail.md b/.changeset/shaggy-cherries-fail.md new file mode 100644 index 0000000000..bc622bde2c --- /dev/null +++ b/.changeset/shaggy-cherries-fail.md @@ -0,0 +1,5 @@ +--- +'@ice/runtime': patch +--- + +fix: support cdn url in assets manifest diff --git a/examples/app-config/ice.config.mts b/examples/app-config/ice.config.mts index 562d28cc1d..b89aefa7cb 100644 --- a/examples/app-config/ice.config.mts +++ b/examples/app-config/ice.config.mts @@ -1,3 +1,6 @@ import { defineConfig } from '@ice/app'; +import externals from '@ice/plugin-externals'; -export default defineConfig(() => ({})); +export default defineConfig(() => ({ + plugins: [externals({ preset: 'react' })] +})); diff --git a/examples/app-config/package.json b/examples/app-config/package.json index 25618a5f2d..8be59abe78 100644 --- a/examples/app-config/package.json +++ b/examples/app-config/package.json @@ -12,18 +12,13 @@ "license": "MIT", "dependencies": { "@ice/app": "workspace:*", - "@ice/plugin-auth": "workspace:*", - "@ice/plugin-rax-compat": "workspace:*", + "@ice/plugin-externals": "workspace:*", "@ice/runtime": "workspace:*", - "@uni/env": "^1.1.0", - "ahooks": "^3.3.8", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.2", - "speed-measure-webpack-plugin": "^1.5.0", - "webpack": "^5.88.0" + "@types/react-dom": "^18.0.2" } } diff --git a/packages/plugin-externals/CHANGELOG.md b/packages/plugin-externals/CHANGELOG.md new file mode 100644 index 0000000000..20e52887c8 --- /dev/null +++ b/packages/plugin-externals/CHANGELOG.md @@ -0,0 +1,5 @@ +# @ice/plugin-externals + +## 1.0.0 + +- Initial release diff --git a/packages/plugin-externals/README.md b/packages/plugin-externals/README.md new file mode 100644 index 0000000000..7f2e170c85 --- /dev/null +++ b/packages/plugin-externals/README.md @@ -0,0 +1,45 @@ +# @ice/plugin-externals + +`@ice/plugin-externals` is a ice.js plugin. It provides a simple way to add externals support to your application. + +## Install + +```bash +$ npm i @ice/plugin-externals --save-dev +``` + +## Usage + +Set preset `react` to external react in a easy way. + +```js +import { defineConfig } from '@ice/app'; +import externals from '@ice/plugin-externals'; + +export default defineConfig(() => ({ + plugins: [externals({ preset: 'react' })] +})); +``` + +Framework will auto add externals of `react` and `react-dom` to your application, and the cdn url will be inject to the document by default. + +Also, you can custom externals and cdn url by yourself: + +```js +import { defineConfig } from '@ice/app'; +import externals from '@ice/plugin-externals'; + +export default defineConfig(() => ({ + plugins: [externals({ + externals: { + antd: 'Antd', + }, + cdnMap: { + antd: { + development: 'https://unpkg.com/antd/dist/antd.js', + production: 'https://unpkg.com/antd/dist/antd.min.js', + } + } + })] +})); +``` diff --git a/packages/plugin-externals/package.json b/packages/plugin-externals/package.json new file mode 100644 index 0000000000..4f0ca006ac --- /dev/null +++ b/packages/plugin-externals/package.json @@ -0,0 +1,36 @@ +{ + "name": "@ice/plugin-externals", + "version": "1.0.0", + "description": "plugin to make externals much easier in ice.js", + "files": [ + "esm", + "!esm/**/*.map", + "*.d.ts" + ], + "type": "module", + "main": "esm/index.js", + "module": "esm/index.js", + "types": "esm/index.d.ts", + "exports": { + ".": "./esm/index.js" + }, + "sideEffects": false, + "scripts": { + "watch": "tsc -w --sourceMap", + "build": "tsc" + }, + "devDependencies": { + "@ice/app": "^3.3.2", + "@ice/runtime": "^1.2.9", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "webpack": "^5.88.0" + }, + "repository": { + "type": "http", + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-externals" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugin-externals/src/index.ts b/packages/plugin-externals/src/index.ts new file mode 100644 index 0000000000..97c6a65b4d --- /dev/null +++ b/packages/plugin-externals/src/index.ts @@ -0,0 +1,80 @@ +import type { Plugin } from '@ice/app/types'; +import InjectExternalScriptsWebpackPlugin from './webpack-plugin.js'; + +const PLUGIN_NAME = '@ice/plugin-externals'; + +type Preset = 'react'; +interface PluginOptions { + preset?: Preset | Preset[]; + externals?: Record; + cdnMap?: Record; +} + +const plugin: Plugin = (options: PluginOptions) => ({ + name: PLUGIN_NAME, + setup: ({ onGetConfig, context }) => { + const { command } = context; + const reactExternals = { + react: 'React', + 'react-dom': 'ReactDOM', + }; + const reactCDN = { + react: { + development: 'https://g.alicdn.com/code/lib/react/18.3.1/umd/react.development.js', + production: 'https://g.alicdn.com/code/lib/react/18.3.1/umd/react.production.min.js', + }, + 'react-dom': { + development: 'https://g.alicdn.com/code/lib/react-dom/18.3.1/umd/react-dom.development.js', + production: 'https://g.alicdn.com/code/lib/react-dom/18.3.1/umd/react-dom.production.min.js', + }, + }; + onGetConfig((config) => { + config.configureWebpack ??= []; + config.configureWebpack.push((webpackConfig) => { + let externals = options.externals || {}; + let cdnMap = options.cdnMap || {}; + if (options.preset && options.preset === 'react') { + switch (options.preset) { + case 'react': + externals = { + ...reactExternals, + ...externals, + }; + cdnMap = { + ...reactCDN, + ...cdnMap, + }; + break; + } + } + + if (!webpackConfig.externals) { + webpackConfig.externals = externals; + } else if (typeof webpackConfig.externals === 'object') { + webpackConfig.externals = { + ...webpackConfig.externals, + ...externals, + }; + } + const cdnList = []; + Object.keys(cdnMap).forEach((key) => { + const url = command === 'start' ? cdnMap[key].development : cdnMap[key].production; + const urls = Array.isArray(url) ? url : [url]; + cdnList.push(...urls); + }); + if (cdnList.length > 0) { + // @ts-ignore missmatch type becasue of webpack prebundled. + webpackConfig.plugins.push(new InjectExternalScriptsWebpackPlugin({ + externals: cdnList, + })); + } + return webpackConfig; + }); + }); + }, +}); + +export default plugin; diff --git a/packages/plugin-externals/src/webpack-plugin.ts b/packages/plugin-externals/src/webpack-plugin.ts new file mode 100644 index 0000000000..29a0595c61 --- /dev/null +++ b/packages/plugin-externals/src/webpack-plugin.ts @@ -0,0 +1,39 @@ +import webpack from 'webpack'; +import type { Compiler } from 'webpack'; + +const ASSET_MANIFEST_JSON_NAME = 'assets-manifest.json'; + +interface PluginOptions { + externals: string[]; +} + +export default class InjectExternalScriptsWebpackPlugin { + private options: PluginOptions; + + constructor(options: PluginOptions) { + this.options = options; + } + + apply(compiler: Compiler) { + compiler.hooks.make.tap('InjectExternalScriptsWebpackPlugin', (compilation) => { + compilation.hooks.processAssets.tap( + { + name: 'InjectExternalScriptsWebpackPlugin', + stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, + }, + () => { + const assetsManifest = compilation.assets[ASSET_MANIFEST_JSON_NAME]; + if (assetsManifest) { + const json = JSON.parse(assetsManifest.source().toString()); + delete compilation.assets[ASSET_MANIFEST_JSON_NAME]; + json.entries.main.unshift(...this.options.externals); + compilation.emitAsset( + ASSET_MANIFEST_JSON_NAME, + new webpack.sources.RawSource(JSON.stringify(json)), + ); + } + }, + ); + }); + } +} diff --git a/packages/plugin-externals/tsconfig.json b/packages/plugin-externals/tsconfig.json new file mode 100644 index 0000000000..d48d48a70c --- /dev/null +++ b/packages/plugin-externals/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "outDir": "esm", + "module": "NodeNext", + "moduleResolution": "NodeNext" + }, + "include": ["src"] +} diff --git a/packages/runtime/src/Document.tsx b/packages/runtime/src/Document.tsx index 740b36d2a6..53827764d6 100644 --- a/packages/runtime/src/Document.tsx +++ b/packages/runtime/src/Document.tsx @@ -250,5 +250,12 @@ export function getEntryAssets(assetsManifest: AssetsManifest): string[] { result = result.concat(assets); }); - return result.map(filePath => `${publicPath}${filePath}`); + return result.map((filePath: string) => { + const prefixes = ['http:', 'https:', '//']; + if (prefixes.some(prefix => filePath.startsWith(prefix))) { + return filePath; + } else { + return `${publicPath}${filePath}`; + } + }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b768813c2..159a1e27c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,21 +130,12 @@ importers: '@ice/app': specifier: workspace:* version: link:../../packages/ice - '@ice/plugin-auth': + '@ice/plugin-externals': specifier: workspace:* - version: link:../../packages/plugin-auth - '@ice/plugin-rax-compat': - specifier: workspace:* - version: link:../../packages/plugin-rax-compat + version: link:../../packages/plugin-externals '@ice/runtime': specifier: workspace:* version: link:../../packages/runtime - '@uni/env': - specifier: ^1.1.0 - version: 1.1.0 - ahooks: - specifier: ^3.3.8 - version: 3.7.5(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -158,12 +149,6 @@ importers: '@types/react-dom': specifier: ^18.0.2 version: 18.0.11 - speed-measure-webpack-plugin: - specifier: ^1.5.0 - version: 1.5.0(webpack@5.88.2) - webpack: - specifier: ^5.88.0 - version: 5.88.2 examples/basic-project: dependencies: @@ -1974,6 +1959,24 @@ importers: specifier: ^3.3.2 version: link:../ice + packages/plugin-externals: + devDependencies: + '@ice/app': + specifier: ^3.3.2 + version: link:../ice + '@ice/runtime': + specifier: ^1.2.9 + version: link:../runtime + '@types/react': + specifier: ^18.0.0 + version: 18.0.34 + '@types/react-dom': + specifier: ^18.0.0 + version: 18.0.11 + webpack: + specifier: ^5.88.0 + version: 5.88.2 + packages/plugin-fusion: dependencies: '@ice/style-import': @@ -19929,7 +19932,7 @@ packages: dependencies: '@babel/code-frame': 7.18.6 address: 1.2.2 - browserslist: 4.22.1 + browserslist: 4.22.3 chalk: 4.1.2 cross-spawn: 7.0.3 detect-port-alt: 1.1.6 @@ -23572,7 +23575,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.5 acorn: 8.11.2 acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.21.5 + browserslist: 4.22.3 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.2.1 @@ -23611,7 +23614,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.5 acorn: 8.11.2 acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.21.5 + browserslist: 4.22.3 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.2.1 From 15c8200f60634cd1917667f323572e0cedf1db1e Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Mon, 17 Jun 2024 16:24:57 +0800 Subject: [PATCH 3/4] Feat: unnecessary to compile routes config for server render (#6856) * fix: unnecessary to compile routes config for server render * chore: format ejs template * feat: support build fallback entry * chore: add test case for fallback entry * chore: update lock * chore: changeset * chore: lint * chore: lint * chore: remove console log * fix: merge conflict * fix: undefined assign --- .changeset/green-files-watch.md | 7 + examples/basic-project/tsconfig.json | 2 +- examples/with-entry-type/ice.config.mts | 18 -- examples/with-entry-type/src/document.tsx | 42 ---- .../src/pages/Custom/index.tsx | 19 -- examples/with-entry-type/src/pages/about.tsx | 30 --- examples/with-entry-type/src/pages/blog.tsx | 21 -- examples/with-entry-type/src/pages/home.tsx | 20 -- .../src/pages/index.module.css | 25 --- examples/with-entry-type/src/pages/layout.tsx | 22 -- .../.browserslistrc | 0 examples/with-fallback-entry/ice.config.mts | 10 + .../package.json | 9 +- examples/with-fallback-entry/plugin.ts | 12 ++ .../public/favicon.ico | Bin .../src/app.tsx | 0 examples/with-fallback-entry/src/document.tsx | 22 ++ examples/with-fallback-entry/src/global.css | 3 + .../with-fallback-entry/src/pages/index.tsx | 3 + .../src/typings.d.ts | 0 .../tsconfig.json | 0 packages/ice/src/bundler/config/plugins.ts | 19 +- packages/ice/src/bundler/rspack/getConfig.ts | 9 +- .../src/bundler/webpack/getWebpackConfig.ts | 7 +- packages/ice/src/constant.ts | 1 + packages/ice/src/createService.ts | 11 +- .../ice/src/utils/getServerCompilerPlugin.ts | 11 +- .../ice/src/webpack/ServerCompilerPlugin.ts | 1 - .../ice/src/webpack/ServerRunnerPlugin.ts | 1 - .../ice/templates/core/entry.server.ts.ejs | 38 ++-- packages/runtime/server.d.ts | 1 + packages/runtime/src/AppContext.tsx | 2 +- packages/runtime/src/index.server.ts | 1 + packages/runtime/src/renderDocument.tsx | 140 +++++++++++++ packages/runtime/src/runServerApp.tsx | 195 ++---------------- .../runtime/src/server/getDocumentData.ts | 19 ++ packages/runtime/src/server/navigator.ts | 50 ----- packages/runtime/src/server/response.ts | 43 ++++ packages/runtime/src/server/streamRender.tsx | 5 +- packages/runtime/src/types.ts | 38 ++++ packages/runtime/tests/navigator.test.ts | 33 --- packages/shared-config/src/types.ts | 6 + pnpm-lock.yaml | 9 +- tests/integration/basic-project.test.ts | 2 +- tests/integration/with-fallback-entry.test.ts | 26 +++ 45 files changed, 429 insertions(+), 504 deletions(-) create mode 100644 .changeset/green-files-watch.md delete mode 100644 examples/with-entry-type/ice.config.mts delete mode 100644 examples/with-entry-type/src/document.tsx delete mode 100644 examples/with-entry-type/src/pages/Custom/index.tsx delete mode 100644 examples/with-entry-type/src/pages/about.tsx delete mode 100644 examples/with-entry-type/src/pages/blog.tsx delete mode 100644 examples/with-entry-type/src/pages/home.tsx delete mode 100644 examples/with-entry-type/src/pages/index.module.css delete mode 100644 examples/with-entry-type/src/pages/layout.tsx rename examples/{with-entry-type => with-fallback-entry}/.browserslistrc (100%) create mode 100644 examples/with-fallback-entry/ice.config.mts rename examples/{with-entry-type => with-fallback-entry}/package.json (76%) create mode 100644 examples/with-fallback-entry/plugin.ts rename examples/{with-entry-type => with-fallback-entry}/public/favicon.ico (100%) rename examples/{with-entry-type => with-fallback-entry}/src/app.tsx (100%) create mode 100644 examples/with-fallback-entry/src/document.tsx create mode 100644 examples/with-fallback-entry/src/global.css create mode 100644 examples/with-fallback-entry/src/pages/index.tsx rename examples/{with-entry-type => with-fallback-entry}/src/typings.d.ts (100%) rename examples/{with-entry-type => with-fallback-entry}/tsconfig.json (100%) create mode 100644 packages/runtime/server.d.ts create mode 100644 packages/runtime/src/renderDocument.tsx create mode 100644 packages/runtime/src/server/getDocumentData.ts delete mode 100644 packages/runtime/src/server/navigator.ts create mode 100644 packages/runtime/src/server/response.ts delete mode 100644 packages/runtime/tests/navigator.test.ts create mode 100644 tests/integration/with-fallback-entry.test.ts diff --git a/.changeset/green-files-watch.md b/.changeset/green-files-watch.md new file mode 100644 index 0000000000..5125a54b58 --- /dev/null +++ b/.changeset/green-files-watch.md @@ -0,0 +1,7 @@ +--- +'@ice/shared-config': patch +'@ice/runtime': patch +'@ice/app': patch +--- + +feat: support build additional server entry for fallback diff --git a/examples/basic-project/tsconfig.json b/examples/basic-project/tsconfig.json index 6584fa600c..28b2e34aeb 100644 --- a/examples/basic-project/tsconfig.json +++ b/examples/basic-project/tsconfig.json @@ -29,4 +29,4 @@ }, "include": ["src", ".ice", "ice.config.*"], "exclude": ["build", "public"] -} \ No newline at end of file +} diff --git a/examples/with-entry-type/ice.config.mts b/examples/with-entry-type/ice.config.mts deleted file mode 100644 index 080e40e660..0000000000 --- a/examples/with-entry-type/ice.config.mts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from '@ice/app'; - -export default defineConfig({ - plugins: [], - server: { - onDemand: true, - format: 'esm', - }, - output: { - distType: 'javascript' - }, - sourceMap: true, - routes: { - defineRoutes: (route) => { - route('/custom', 'Custom/index.tsx'); - }, - }, -}); diff --git a/examples/with-entry-type/src/document.tsx b/examples/with-entry-type/src/document.tsx deleted file mode 100644 index fd149f08a5..0000000000 --- a/examples/with-entry-type/src/document.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import path from 'path'; -import { fileURLToPath } from 'url'; -import { Meta, Title, Links, Main, Scripts } from 'ice'; -import fse from 'fs-extra'; - -let dirname; -if (typeof __dirname === 'string') { - dirname = __dirname; -} else { - dirname = path.dirname(fileURLToPath(import.meta.url)); -} - -function Document() { - return ( - - - - - - - - <Links /> - </head> - <body> - <Main /> - <Scripts ScriptElement={(props) => { - if (props.src && !props.src.startsWith('http')) { - const filePath = path.join(dirname, `..${props.src}`); - const sourceMapFilePath = path.join(dirname, `..${props.src}.map`); - const res = fse.readFileSync(filePath, 'utf-8'); - return <script data-sourcemap={sourceMapFilePath} dangerouslySetInnerHTML={{ __html: res }} {...props} />; - } else { - return <script {...props} />; - } - }} - /> - </body> - </html> - ); -} - -export default Document; diff --git a/examples/with-entry-type/src/pages/Custom/index.tsx b/examples/with-entry-type/src/pages/Custom/index.tsx deleted file mode 100644 index 164e24ed00..0000000000 --- a/examples/with-entry-type/src/pages/Custom/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Link, useData } from 'ice'; - -export default function Custom() { - const data = useData(); - - return ( - <> - <h2>Custom Page</h2> - <Link to="/home">home</Link> - {data} - </> - ); -} - -export function pageConfig() { - return { - title: 'Custom', - }; -} diff --git a/examples/with-entry-type/src/pages/about.tsx b/examples/with-entry-type/src/pages/about.tsx deleted file mode 100644 index b44b31a76d..0000000000 --- a/examples/with-entry-type/src/pages/about.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Link } from 'ice'; - -export default function About() { - return ( - <> - <h2>About Page</h2> - <Link to="/">home</Link> - <span className="mark">new</span> - </> - ); -} - -export function pageConfig() { - return { - title: 'About', - meta: [ - { - name: 'theme-color', - content: '#eee', - }, - ], - links: [{ - href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', - rel: 'stylesheet', - }], - scripts: [{ - src: 'https://cdn.jsdelivr.net/npm/lodash@2.4.1/dist/lodash.min.js', - }], - }; -} diff --git a/examples/with-entry-type/src/pages/blog.tsx b/examples/with-entry-type/src/pages/blog.tsx deleted file mode 100644 index 4113cb37d1..0000000000 --- a/examples/with-entry-type/src/pages/blog.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Link, useData, useConfig } from 'ice'; - -export default function Blog() { - const data = useData(); - const config = useConfig(); - - console.log('render Blog', 'data', data, 'config', config); - - return ( - <> - <h2>Blog Page</h2> - <Link to="/home">home</Link> - </> - ); -} - -export function pageConfig() { - return { - title: 'Blog', - }; -} \ No newline at end of file diff --git a/examples/with-entry-type/src/pages/home.tsx b/examples/with-entry-type/src/pages/home.tsx deleted file mode 100644 index f5aaf1b72b..0000000000 --- a/examples/with-entry-type/src/pages/home.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { definePageConfig } from 'ice'; - -export default function Home() { - return ( - <> - <h2>Home Page</h2> - </> - ); -} - -export const pageConfig = definePageConfig(() => { - return { - queryParamsPassKeys: [ - 'questionId', - 'source', - 'disableNav', - ], - title: 'Home', - }; -}); diff --git a/examples/with-entry-type/src/pages/index.module.css b/examples/with-entry-type/src/pages/index.module.css deleted file mode 100644 index 679273d44d..0000000000 --- a/examples/with-entry-type/src/pages/index.module.css +++ /dev/null @@ -1,25 +0,0 @@ -.title { - color: red; - margin-left: 10rpx; -} - -.data { - margin-top: 10px; -} - -.homeContainer { - align-items: center; - margin-top: 200rpx; -} - -.homeTitle { - font-size: 45rpx; - font-weight: bold; - margin: 20rpx 0; -} - -.homeInfo { - font-size: 36rpx; - margin: 8rpx 0; - color: #555; -} diff --git a/examples/with-entry-type/src/pages/layout.tsx b/examples/with-entry-type/src/pages/layout.tsx deleted file mode 100644 index 24a21a39b6..0000000000 --- a/examples/with-entry-type/src/pages/layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Outlet } from 'ice'; - -export default () => { - return ( - <div> - <h1>ICE 3.0 Layout</h1> - <Outlet /> - </div> - ); -}; - -export function pageConfig() { - return { - title: 'Layout', - meta: [ - { - name: 'layout-color', - content: '#f00', - }, - ], - }; -} diff --git a/examples/with-entry-type/.browserslistrc b/examples/with-fallback-entry/.browserslistrc similarity index 100% rename from examples/with-entry-type/.browserslistrc rename to examples/with-fallback-entry/.browserslistrc diff --git a/examples/with-fallback-entry/ice.config.mts b/examples/with-fallback-entry/ice.config.mts new file mode 100644 index 0000000000..183825d80b --- /dev/null +++ b/examples/with-fallback-entry/ice.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from '@ice/app'; +import plugin from './plugin'; + +export default defineConfig(() => ({ + plugins: [plugin()], + ssr: true, + server: { + format: 'cjs', + } +})); diff --git a/examples/with-entry-type/package.json b/examples/with-fallback-entry/package.json similarity index 76% rename from examples/with-entry-type/package.json rename to examples/with-fallback-entry/package.json index 76c13d53af..a600d9eb7c 100644 --- a/examples/with-entry-type/package.json +++ b/examples/with-fallback-entry/package.json @@ -1,7 +1,7 @@ { - "name": "@examples/with-entry-type", - "private": true, + "name": "@examples/with-fallback-entry", "version": "1.0.0", + "private": true, "scripts": { "start": "ice start", "build": "ice build" @@ -12,11 +12,10 @@ "dependencies": { "@ice/app": "workspace:*", "@ice/runtime": "workspace:*", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.2.0", + "react-dom": "^18.2.0" }, "devDependencies": { - "fs-extra": "^10.0.0", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.2", "webpack": "^5.88.0" diff --git a/examples/with-fallback-entry/plugin.ts b/examples/with-fallback-entry/plugin.ts new file mode 100644 index 0000000000..8286d268b4 --- /dev/null +++ b/examples/with-fallback-entry/plugin.ts @@ -0,0 +1,12 @@ +export default function createPlugin() { + return { + name: 'custom-plugin', + setup({ onGetConfig }) { + onGetConfig((config) => { + config.server = { + fallbackEntry: true, + }; + }); + }, + }; +} diff --git a/examples/with-entry-type/public/favicon.ico b/examples/with-fallback-entry/public/favicon.ico similarity index 100% rename from examples/with-entry-type/public/favicon.ico rename to examples/with-fallback-entry/public/favicon.ico diff --git a/examples/with-entry-type/src/app.tsx b/examples/with-fallback-entry/src/app.tsx similarity index 100% rename from examples/with-entry-type/src/app.tsx rename to examples/with-fallback-entry/src/app.tsx diff --git a/examples/with-fallback-entry/src/document.tsx b/examples/with-fallback-entry/src/document.tsx new file mode 100644 index 0000000000..1e7b99c49d --- /dev/null +++ b/examples/with-fallback-entry/src/document.tsx @@ -0,0 +1,22 @@ +import { Meta, Title, Links, Main, Scripts } from 'ice'; + +function Document() { + return ( + <html> + <head> + <meta charSet="utf-8" /> + <meta name="description" content="ICE Demo" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <Meta /> + <Title /> + <Links /> + </head> + <body> + <Main /> + <Scripts /> + </body> + </html> + ); +} + +export default Document; diff --git a/examples/with-fallback-entry/src/global.css b/examples/with-fallback-entry/src/global.css new file mode 100644 index 0000000000..604282adc9 --- /dev/null +++ b/examples/with-fallback-entry/src/global.css @@ -0,0 +1,3 @@ +body { + font-size: 14px; +} diff --git a/examples/with-fallback-entry/src/pages/index.tsx b/examples/with-fallback-entry/src/pages/index.tsx new file mode 100644 index 0000000000..bb72815361 --- /dev/null +++ b/examples/with-fallback-entry/src/pages/index.tsx @@ -0,0 +1,3 @@ +export default function Home() { + return <h1>home</h1>; +} diff --git a/examples/with-entry-type/src/typings.d.ts b/examples/with-fallback-entry/src/typings.d.ts similarity index 100% rename from examples/with-entry-type/src/typings.d.ts rename to examples/with-fallback-entry/src/typings.d.ts diff --git a/examples/with-entry-type/tsconfig.json b/examples/with-fallback-entry/tsconfig.json similarity index 100% rename from examples/with-entry-type/tsconfig.json rename to examples/with-fallback-entry/tsconfig.json diff --git a/packages/ice/src/bundler/config/plugins.ts b/packages/ice/src/bundler/config/plugins.ts index a17f5410a3..3d69414195 100644 --- a/packages/ice/src/bundler/config/plugins.ts +++ b/packages/ice/src/bundler/config/plugins.ts @@ -1,5 +1,7 @@ +import path from 'path'; +import type { CommandName } from 'build-scripts'; import ServerRunnerPlugin from '../../webpack/ServerRunnerPlugin.js'; -import { IMPORT_META_RENDERER, IMPORT_META_TARGET, WEB } from '../../constant.js'; +import { IMPORT_META_RENDERER, IMPORT_META_TARGET, WEB, FALLBACK_ENTRY, RUNTIME_TMP_DIR } from '../../constant.js'; import getServerCompilerPlugin from '../../utils/getServerCompilerPlugin.js'; import ReCompilePlugin from '../../webpack/ReCompilePlugin.js'; import getEntryPoints from '../../utils/getEntryPoints.js'; @@ -20,6 +22,18 @@ export const getSpinnerPlugin = (spinner, name?: string) => { }; }; +export const getFallbackEntry = (options: { + rootDir: string; + command: CommandName; + fallbackEntry: boolean; +}): string => { + const { command, fallbackEntry, rootDir } = options; + if (command === 'build' && fallbackEntry) { + return path.join(rootDir, RUNTIME_TMP_DIR, FALLBACK_ENTRY); + } + return ''; +}; + interface ServerPluginOptions { serverRunner?: ServerRunner; serverCompiler?: ServerCompiler; @@ -30,6 +44,7 @@ interface ServerPluginOptions { serverEntry?: string; ensureRoutesConfig: () => Promise<void>; userConfig?: UserConfig; + fallbackEntry?: string; getFlattenRoutes?: () => string[]; command?: string; } @@ -43,6 +58,7 @@ export const getServerPlugin = ({ outputDir, serverCompileTask, userConfig, + fallbackEntry, getFlattenRoutes, command, }: ServerPluginOptions) => { @@ -53,6 +69,7 @@ export const getServerPlugin = ({ return getServerCompilerPlugin(serverCompiler, { rootDir, serverEntry, + fallbackEntry, outputDir, serverCompileTask, userConfig, diff --git a/packages/ice/src/bundler/rspack/getConfig.ts b/packages/ice/src/bundler/rspack/getConfig.ts index 1ca8a848b5..a724b49595 100644 --- a/packages/ice/src/bundler/rspack/getConfig.ts +++ b/packages/ice/src/bundler/rspack/getConfig.ts @@ -11,7 +11,7 @@ import { CSS_MODULES_LOCAL_IDENT_NAME, CSS_MODULES_LOCAL_IDENT_NAME_DEV, } from '../../constant.js'; -import { getReCompilePlugin, getServerPlugin, getSpinnerPlugin } from '../config/plugins.js'; +import { getFallbackEntry, getReCompilePlugin, getServerPlugin, getSpinnerPlugin } from '../config/plugins.js'; import { getExpandedEnvs } from '../../utils/runtimeEnv.js'; import type { BundlerOptions, Context } from '../types.js'; import type { PluginData } from '../../types/plugin.js'; @@ -34,8 +34,8 @@ const getConfig: GetConfig = async (context, options, rspack) => { } = options; const { rootDir, - userConfig, command, + userConfig, extendsPluginAPI: { serverCompileTask, getRoutesFile, @@ -50,6 +50,11 @@ const getConfig: GetConfig = async (context, options, rspack) => { getSpinnerPlugin(spinner), // Add Server runner plugin. getServerPlugin({ + fallbackEntry: getFallbackEntry({ + rootDir, + command, + fallbackEntry: server?.fallbackEntry, + }), serverRunner, ensureRoutesConfig, serverCompiler, diff --git a/packages/ice/src/bundler/webpack/getWebpackConfig.ts b/packages/ice/src/bundler/webpack/getWebpackConfig.ts index c086f9c4b6..4c57414eb3 100644 --- a/packages/ice/src/bundler/webpack/getWebpackConfig.ts +++ b/packages/ice/src/bundler/webpack/getWebpackConfig.ts @@ -7,7 +7,7 @@ import { getRouteExportConfig } from '../../service/config.js'; import { getFileHash } from '../../utils/hash.js'; import DataLoaderPlugin from '../../webpack/DataLoaderPlugin.js'; import { IMPORT_META_RENDERER, IMPORT_META_TARGET, RUNTIME_TMP_DIR, WEB } from '../../constant.js'; -import { getReCompilePlugin, getServerPlugin, getSpinnerPlugin } from '../config/plugins.js'; +import { getFallbackEntry, getReCompilePlugin, getServerPlugin, getSpinnerPlugin } from '../config/plugins.js'; import type RouteManifest from '../../utils/routeManifest.js'; import type ServerRunnerPlugin from '../../webpack/ServerRunnerPlugin.js'; import type ServerCompilerPlugin from '../../webpack/ServerCompilerPlugin.js'; @@ -93,6 +93,11 @@ const getWebpackConfig: GetWebpackConfig = async (context, options) => { serverCompiler, target, rootDir, + fallbackEntry: getFallbackEntry({ + rootDir, + command, + fallbackEntry: server?.fallbackEntry, + }), serverEntry: server?.entry, outputDir, serverCompileTask, diff --git a/packages/ice/src/constant.ts b/packages/ice/src/constant.ts index 16a8f279b0..6f86b923c1 100644 --- a/packages/ice/src/constant.ts +++ b/packages/ice/src/constant.ts @@ -5,6 +5,7 @@ export const DEFAULT_HOST = '0.0.0.0'; export const RUNTIME_TMP_DIR = '.ice'; export const SERVER_ENTRY = path.join(RUNTIME_TMP_DIR, 'entry.server.ts'); +export const FALLBACK_ENTRY = 'entry.document.ts'; export const DATA_LOADER_ENTRY = path.join(RUNTIME_TMP_DIR, 'data-loader.ts'); export const SERVER_OUTPUT_DIR = 'server'; export const IMPORT_META_TARGET = 'import.meta.target'; diff --git a/packages/ice/src/createService.ts b/packages/ice/src/createService.ts index a20bbb27ee..4a97092a1d 100644 --- a/packages/ice/src/createService.ts +++ b/packages/ice/src/createService.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { fileURLToPath } from 'url'; import { createRequire } from 'module'; import { Context } from 'build-scripts'; -import type { CommandArgs, CommandName } from 'build-scripts'; +import type { CommandArgs, CommandName, TaskConfig } from 'build-scripts'; import type { Config } from '@ice/shared-config/types'; import type { AppConfig } from '@ice/runtime/types'; import webpack from '@ice/bundles/compiled/webpack/index.js'; @@ -23,7 +23,7 @@ import { setEnv, updateRuntimeEnv, getCoreEnvKeys } from './utils/runtimeEnv.js' import getRuntimeModules from './utils/getRuntimeModules.js'; import { generateRoutesInfo, getRoutesDefinition } from './routes.js'; import * as config from './config.js'; -import { RUNTIME_TMP_DIR, WEB, RUNTIME_EXPORTS, SERVER_ENTRY } from './constant.js'; +import { RUNTIME_TMP_DIR, WEB, RUNTIME_EXPORTS, SERVER_ENTRY, FALLBACK_ENTRY } from './constant.js'; import createSpinner from './utils/createSpinner.js'; import ServerCompileTask from './utils/ServerCompileTask.js'; import { getAppExportConfig, getRouteExportConfig } from './service/config.js'; @@ -215,7 +215,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt ctx.registerConfig(configType, configData); }); - let taskConfigs = await ctx.setup(); + let taskConfigs: TaskConfig<Config>[] = await ctx.setup(); // get userConfig after setup because of userConfig maybe modified by plugins const { userConfig } = ctx; @@ -296,6 +296,11 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt }, ); + if (platformTaskConfig.config.server?.fallbackEntry) { + // Add fallback entry for server side rendering. + generator.addRenderFile('core/entry.server.ts.ejs', FALLBACK_ENTRY, { hydrate: false }); + } + if (typeof userConfig.dataLoader === 'object' && userConfig.dataLoader.fetcher) { const { packageName, diff --git a/packages/ice/src/utils/getServerCompilerPlugin.ts b/packages/ice/src/utils/getServerCompilerPlugin.ts index a4f3db2fa1..0a5ef5aec5 100644 --- a/packages/ice/src/utils/getServerCompilerPlugin.ts +++ b/packages/ice/src/utils/getServerCompilerPlugin.ts @@ -10,6 +10,7 @@ interface Options { userConfig: UserConfig; outputDir: string; serverEntry: string; + fallbackEntry?: string; serverCompileTask: ExtendsPluginAPI['serverCompileTask']; ensureRoutesConfig: () => Promise<void>; runtimeDefineVars: Record<string, string>; @@ -25,16 +26,24 @@ function getServerCompilerPlugin(serverCompiler: ServerCompiler, options: Option serverCompileTask, ensureRoutesConfig, runtimeDefineVars, + fallbackEntry, entryPoints, } = options; const { ssg, ssr, server: { format } } = userConfig; const isEsm = userConfig?.server?.format === 'esm'; + const defaultEntryPoints = { index: getServerEntry(rootDir, serverEntry) }; + if (fallbackEntry) { + if (entryPoints) { + entryPoints['index.fallback'] = fallbackEntry; + } + defaultEntryPoints['index.fallback'] = fallbackEntry; + } return new ServerCompilerPlugin( serverCompiler, [ { - entryPoints: entryPoints || { index: getServerEntry(rootDir, serverEntry) }, + entryPoints: entryPoints || defaultEntryPoints, outdir: path.join(outputDir, SERVER_OUTPUT_DIR), splitting: isEsm, format, diff --git a/packages/ice/src/webpack/ServerCompilerPlugin.ts b/packages/ice/src/webpack/ServerCompilerPlugin.ts index a4b62f7ce9..09a0569057 100644 --- a/packages/ice/src/webpack/ServerCompilerPlugin.ts +++ b/packages/ice/src/webpack/ServerCompilerPlugin.ts @@ -36,7 +36,6 @@ export default class ServerCompilerPlugin { public compileTask = async (compilation?: Compilation) => { const [buildOptions] = this.serverCompilerOptions; if (!this.isCompiling) { - await this.ensureRoutesConfig(); if (compilation) { // Option of compilationInfo need to be object, while it may changed during multi-time compilation. this.compilerOptions.compilationInfo.assetsManifest = diff --git a/packages/ice/src/webpack/ServerRunnerPlugin.ts b/packages/ice/src/webpack/ServerRunnerPlugin.ts index 8297c32046..4a534e4c0e 100644 --- a/packages/ice/src/webpack/ServerRunnerPlugin.ts +++ b/packages/ice/src/webpack/ServerRunnerPlugin.ts @@ -19,7 +19,6 @@ export default class ServerRunnerPlugin { public compileTask = async (compilation?: Compilation) => { if (!this.isCompiling) { - await this.ensureRoutesConfig(); if (compilation) { // Option of compilationInfo need to be object, while it may changed during multi-time compilation. this.serverRunner.addCompileData({ diff --git a/packages/ice/templates/core/entry.server.ts.ejs b/packages/ice/templates/core/entry.server.ts.ejs index c4ae2cfe13..2fd517eac9 100644 --- a/packages/ice/templates/core/entry.server.ts.ejs +++ b/packages/ice/templates/core/entry.server.ts.ejs @@ -1,6 +1,10 @@ import './env.server'; +<% if (hydrate) {-%> +import { getAppConfig, renderToHTML as renderAppToHTML, renderToResponse as renderAppToResponse } from '@ice/runtime/server'; +<% } else { -%> +import { getAppConfig, getDocumentResponse as renderAppToHTML, renderDocumentToResponse as renderAppToResponse } from '@ice/runtime/server'; +<% }-%> <%- entryServer.imports %> -import * as runtime from '@ice/runtime/server'; <% if (hydrate) {-%> import { commons, statics } from './runtime-modules'; <% }-%> @@ -19,7 +23,6 @@ import createRoutes from '<%- routesFile %>'; <% } else { -%> import routesManifest from './route-manifest.json'; <% } -%> -import routesConfig from './routes-config.bundle.mjs'; <% if (dataLoaderImport.imports) {-%><%-dataLoaderImport.imports%><% } -%> <% if (hydrate) {-%><%- runtimeOptions.imports %><% } -%> @@ -32,10 +35,11 @@ const createRoutes = () => routesManifest; const runtimeModules = { commons, statics }; const getRouterBasename = () => { - const appConfig = runtime.getAppConfig(app); + const appConfig = getAppConfig(app); return appConfig?.router?.basename ?? <%- basename %> ?? ''; } +<% if (hydrate) {-%> const setRuntimeEnv = (renderMode) => { if (renderMode === 'SSG') { process.env.ICE_CORE_SSG = 'true'; @@ -43,6 +47,7 @@ const setRuntimeEnv = (renderMode) => { process.env.ICE_CORE_SSR = 'true'; } } +<% } -%> interface RenderOptions { documentOnly?: boolean; @@ -57,19 +62,21 @@ interface RenderOptions { } export async function renderToHTML(requestContext, options: RenderOptions = {}) { +<% if (hydrate) {-%> const { renderMode = 'SSR' } = options; setRuntimeEnv(renderMode); - +<% }-%> const mergedOptions = mergeOptions(options); - return await runtime.renderToHTML(requestContext, mergedOptions); + return await renderAppToHTML(requestContext, mergedOptions); } export async function renderToResponse(requestContext, options: RenderOptions = {}) { +<% if (hydrate) {-%> const { renderMode = 'SSR' } = options; setRuntimeEnv(renderMode); - +<% }-%> const mergedOptions = mergeOptions(options); - return runtime.renderToResponse(requestContext, mergedOptions); + return renderAppToResponse(requestContext, mergedOptions); } function mergeOptions(options) { @@ -89,16 +96,13 @@ function mergeOptions(options) { Document: Document.default, basename: basename || getRouterBasename(), renderMode, - routesConfig, - <% if (hydrate) {-%> - runtimeOptions: { - <% if (runtimeOptions.exports) { -%> + <% if (hydrate) {-%>runtimeOptions: { +<% if (runtimeOptions.exports) { -%> <%- runtimeOptions.exports %> - <% } -%> - <% if (locals.customRuntimeOptions) { _%> - ...<%- JSON.stringify(customRuntimeOptions) %>, - <% } _%> +<% } -%> +<% if (locals.customRuntimeOptions) { -%> + ...<%- JSON.stringify(customRuntimeOptions) %>, +<% } -%> }, - <% } -%> - }; + <% } -%>}; } diff --git a/packages/runtime/server.d.ts b/packages/runtime/server.d.ts new file mode 100644 index 0000000000..03f36ef707 --- /dev/null +++ b/packages/runtime/server.d.ts @@ -0,0 +1 @@ +export * from './esm/index.server'; diff --git a/packages/runtime/src/AppContext.tsx b/packages/runtime/src/AppContext.tsx index ff05e7fdbf..4aff665a9c 100644 --- a/packages/runtime/src/AppContext.tsx +++ b/packages/runtime/src/AppContext.tsx @@ -10,7 +10,7 @@ function useAppContext() { return value; } -function useAppData() { +function useAppData<T = any>(): T { const value = React.useContext(Context); return value.appData; } diff --git a/packages/runtime/src/index.server.ts b/packages/runtime/src/index.server.ts index 50b818a950..ce055ee921 100644 --- a/packages/runtime/src/index.server.ts +++ b/packages/runtime/src/index.server.ts @@ -1,2 +1,3 @@ export { renderToResponse, renderToHTML } from './runServerApp.js'; +export { renderToResponse as renderDocumentToResponse, getDocumentResponse } from './renderDocument.js'; export * from './index.js'; diff --git a/packages/runtime/src/renderDocument.tsx b/packages/runtime/src/renderDocument.tsx new file mode 100644 index 0000000000..87eb3f8bb8 --- /dev/null +++ b/packages/runtime/src/renderDocument.tsx @@ -0,0 +1,140 @@ +import * as React from 'react'; +import * as ReactDOMServer from 'react-dom/server'; +import getAppConfig from './appConfig.js'; +import { AppContextProvider } from './AppContext.js'; +import { DocumentContextProvider } from './Document.js'; +import addLeadingSlash from './utils/addLeadingSlash.js'; +import getRequestContext from './requestContext.js'; +import matchRoutes from './matchRoutes.js'; +import getDocumentData from './server/getDocumentData.js'; +import getCurrentRoutePath from './utils/getCurrentRoutePath.js'; +import { sendResponse, getLocation } from './server/response.js'; + +import type { + AppContext, + RouteItem, + RouteMatch, + RenderOptions, + Response, + ServerContext, +} from './types.js'; + +interface RenderDocumentOptions { + matches: RouteMatch[]; + renderOptions: RenderOptions; + routes: RouteItem[]; + documentData: any; + routePath?: string; + downgrade?: boolean; +} + +export function renderDocument(options: RenderDocumentOptions): Response { + const { + matches, + renderOptions, + routePath = '', + downgrade, + routes, + documentData, + }: RenderDocumentOptions = options; + + const { + assetsManifest, + app, + Document, + basename, + routesConfig = {}, + serverData, + } = renderOptions; + + const appData = null; + const appConfig = getAppConfig(app); + + const loaderData = {}; + matches.forEach(async (match) => { + const { id } = match.route; + const pageConfig = routesConfig[id]; + + loaderData[id] = { + pageConfig: pageConfig ? pageConfig({}) : {}, + }; + }); + + const appContext: AppContext = { + assetsManifest, + appConfig, + appData, + loaderData, + matches, + routes, + documentOnly: true, + renderMode: 'CSR', + routePath, + basename, + downgrade, + serverData, + documentData, + }; + + const documentContext = { + main: null, + }; + + const htmlStr = ReactDOMServer.renderToString( + <AppContextProvider value={appContext}> + <DocumentContextProvider value={documentContext}> + { + Document && <Document pagePath={routePath} /> + } + </DocumentContextProvider> + </AppContextProvider>, + ); + + return { + value: `<!DOCTYPE html>${htmlStr}`, + headers: { + 'Content-Type': 'text/html; charset=utf-8', + }, + statusCode: 200, + }; +} + +export async function getDocumentResponse( + serverContext: ServerContext, + renderOptions: RenderOptions, +): Promise<Response> { + const { req } = serverContext; + const { + app, + basename, + serverOnlyBasename, + createRoutes, + documentOnly, + renderMode, + } = renderOptions; + const finalBasename = addLeadingSlash(serverOnlyBasename || basename); + const location = getLocation(req.url); + const requestContext = getRequestContext(location, serverContext); + const appConfig = getAppConfig(app); + const routes = createRoutes({ + requestContext, + renderMode, + }); + const documentData = await getDocumentData({ + loaderConfig: renderOptions.documentDataLoader, + requestContext, + documentOnly, + }); + const matches = appConfig?.router?.type === 'hash' ? [] : matchRoutes(routes, location, finalBasename); + const routePath = getCurrentRoutePath(matches); + return renderDocument({ matches, routePath, routes, renderOptions, documentData }); +} + +export async function renderToResponse( + requestContext: ServerContext, + renderOptions: RenderOptions, +) { + const { req, res } = requestContext; + const documentResoponse = await getDocumentResponse(requestContext, renderOptions); + sendResponse(req, res, documentResoponse); +} diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx index 00a8f79439..a61b40c207 100644 --- a/packages/runtime/src/runServerApp.tsx +++ b/packages/runtime/src/runServerApp.tsx @@ -1,22 +1,14 @@ -import type { ServerResponse, IncomingMessage } from 'http'; import * as React from 'react'; -import * as ReactDOMServer from 'react-dom/server'; import type { Location } from 'history'; -import { parsePath } from 'history'; -import { isFunction } from '@ice/shared'; -import type { RenderToPipeableStreamOptions, OnAllReadyParams, NodeWritablePiper } from './server/streamRender.js'; +import type { OnAllReadyParams } from './server/streamRender.js'; import type { - AppContext, RouteItem, ServerContext, - AppExport, - AssetsManifest, + AppContext, + ServerContext, RouteMatch, - PageConfig, - RenderMode, - DocumentComponent, - RuntimeModules, AppData, ServerAppRouterProps, - DocumentDataLoaderConfig, + RenderOptions, + Response, } from './types.js'; import Runtime from './runtime.js'; import { AppContextProvider } from './AppContext.js'; @@ -24,48 +16,15 @@ import { getAppData } from './appData.js'; import getAppConfig from './appConfig.js'; import { DocumentContextProvider } from './Document.js'; import { loadRouteModules } from './routes.js'; -import type { RouteLoaderOptions } from './routes.js'; import { pipeToString, renderToNodeStream } from './server/streamRender.js'; import getRequestContext from './requestContext.js'; import matchRoutes from './matchRoutes.js'; import getCurrentRoutePath from './utils/getCurrentRoutePath.js'; import ServerRouter from './ServerRouter.js'; import addLeadingSlash from './utils/addLeadingSlash.js'; - -export interface RenderOptions { - app: AppExport; - assetsManifest: AssetsManifest; - createRoutes: (options: Pick<RouteLoaderOptions, 'requestContext' | 'renderMode'>) => RouteItem[]; - runtimeModules: RuntimeModules; - documentDataLoader?: DocumentDataLoaderConfig; - Document?: DocumentComponent; - documentOnly?: boolean; - preRender?: boolean; - renderMode?: RenderMode; - // basename is used both for server and client, once set, it will be sync to client. - basename?: string; - // serverOnlyBasename is used when just want to change basename for server. - serverOnlyBasename?: string; - routePath?: string; - disableFallback?: boolean; - routesConfig: { - [key: string]: PageConfig; - }; - runtimeOptions?: Record<string, any>; - serverData?: any; - streamOptions?: RenderToPipeableStreamOptions; -} - -interface Piper { - pipe: NodeWritablePiper; - fallback: Function; -} -interface Response { - statusCode?: number; - statusText?: string; - value?: string | Piper; - headers?: Record<string, string>; -} +import { renderDocument } from './renderDocument.js'; +import { sendResponse, getLocation } from './server/response.js'; +import getDocumentData from './server/getDocumentData.js'; /** * Render and return the result as html string. @@ -161,23 +120,6 @@ export async function renderToResponse(requestContext: ServerContext, renderOpti } } -async function sendResponse( - req: IncomingMessage, - res: ServerResponse, - response: Response, -) { - res.statusCode = response.statusCode; - res.statusMessage = response.statusText; - Object.entries(response.headers || {}).forEach(([name, value]) => { - res.setHeader(name, value); - }); - if (response.value && req.method !== 'HEAD') { - res.end(response.value); - } else { - res.end(); - } -} - function needRevalidate(matchedRoutes: RouteMatch[]) { return matchedRoutes.some(({ route }) => route.exports.includes('dataLoader') && route.exports.includes('staticDataLoader')); } @@ -227,18 +169,13 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio await Promise.all(runtimeModules.statics.map(m => runtime.loadModule(m)).filter(Boolean)); } - // Execute document dataLoader. - let documentData: any; - if (renderOptions.documentDataLoader) { - const { loader } = renderOptions.documentDataLoader; - if (isFunction(loader)) { - documentData = await loader(requestContext, { documentOnly }); - // @TODO: document should have it's own context, not shared with app. - appContext.documentData = documentData; - } else { - console.warn('Document dataLoader only accepts function.'); - } - } + const documentData = await getDocumentData({ + loaderConfig: renderOptions.documentDataLoader, + requestContext, + documentOnly, + }); + // @TODO: document should have it's own context, not shared with app. + appContext.documentData = documentData; // Not to execute [getAppData] when CSR. if (!documentOnly) { @@ -400,105 +337,3 @@ async function renderServerEntry( }, }; } - -interface RenderDocumentOptions { - matches: RouteMatch[]; - renderOptions: RenderOptions; - routes: RouteItem[]; - documentData: any; - routePath?: string; - downgrade?: boolean; -} - -/** - * Render Document for CSR. - */ -function renderDocument(options: RenderDocumentOptions): Response { - const { - matches, - renderOptions, - routePath = '', - downgrade, - routes, - documentData, - }: RenderDocumentOptions = options; - - const { - assetsManifest, - app, - Document, - basename, - routesConfig = {}, - serverData, - } = renderOptions; - - const appData = null; - const appConfig = getAppConfig(app); - - const loaderData = {}; - matches.forEach(async (match) => { - const { id } = match.route; - const pageConfig = routesConfig[id]; - - loaderData[id] = { - pageConfig: pageConfig ? pageConfig({}) : {}, - }; - }); - - const appContext: AppContext = { - assetsManifest, - appConfig, - appData, - loaderData, - matches, - routes, - documentOnly: true, - renderMode: 'CSR', - routePath, - basename, - downgrade, - serverData, - documentData, - }; - - const documentContext = { - main: null, - }; - - const htmlStr = ReactDOMServer.renderToString( - <AppContextProvider value={appContext}> - <DocumentContextProvider value={documentContext}> - { - Document && <Document pagePath={routePath} /> - } - </DocumentContextProvider> - </AppContextProvider>, - ); - - return { - value: `<!DOCTYPE html>${htmlStr}`, - headers: { - 'Content-Type': 'text/html; charset=utf-8', - }, - statusCode: 200, - }; -} - -/** - * ref: https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/server.tsx - */ -const REGEXP_WITH_HOSTNAME = /^https?:\/\/[^/]+/i; -function getLocation(url: string) { - // In case of invalid URL, provide a default base url. - const locationPath = url.replace(REGEXP_WITH_HOSTNAME, '') || '/'; - const locationProps = parsePath(locationPath); - const location: Location = { - pathname: locationProps.pathname || '/', - search: locationProps.search || '', - hash: locationProps.hash || '', - state: null, - key: 'default', - }; - - return location; -} diff --git a/packages/runtime/src/server/getDocumentData.ts b/packages/runtime/src/server/getDocumentData.ts new file mode 100644 index 0000000000..875eceaf39 --- /dev/null +++ b/packages/runtime/src/server/getDocumentData.ts @@ -0,0 +1,19 @@ +import type { DocumentDataLoaderConfig, RequestContext } from '../types.js'; + +interface Options { + loaderConfig: DocumentDataLoaderConfig; + requestContext: RequestContext; + documentOnly: boolean; +} + +export default async function getDocumentData(options: Options) { + const { loaderConfig, requestContext, documentOnly } = options; + if (loaderConfig) { + const { loader } = loaderConfig; + if (typeof loader === 'function') { + return await loader(requestContext, { documentOnly }); + } else { + console.warn('Document dataLoader only accepts function.'); + } + } +} diff --git a/packages/runtime/src/server/navigator.ts b/packages/runtime/src/server/navigator.ts deleted file mode 100644 index 159afe4c53..0000000000 --- a/packages/runtime/src/server/navigator.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { createPath } from 'history'; -import type { To } from 'history'; - -export function createStaticNavigator() { - return { - createHref(to: To) { - return typeof to === 'string' ? to : createPath(to); - }, - push(to: To) { - throw new Error( - 'You cannot use navigator.push() on the server because it is a stateless ' + - 'environment. This error was probably triggered when you did a ' + - `\`navigate(${JSON.stringify(to)})\` somewhere in your app.`, - ); - }, - replace(to: To) { - throw new Error( - 'You cannot use navigator.replace() on the server because it is a stateless ' + - 'environment. This error was probably triggered when you did a ' + - `\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` + - 'in your app.', - ); - }, - go(delta: number) { - throw new Error( - 'You cannot use navigator.go() on the server because it is a stateless ' + - 'environment. This error was probably triggered when you did a ' + - `\`navigate(${delta})\` somewhere in your app.`, - ); - }, - back() { - throw new Error( - 'You cannot use navigator.back() on the server because it is a stateless ' + - 'environment.', - ); - }, - forward() { - throw new Error( - 'You cannot use navigator.forward() on the server because it is a stateless ' + - 'environment.', - ); - }, - block() { - throw new Error( - 'You cannot use navigator.block() on the server because it is a stateless ' + - 'environment.', - ); - }, - }; -} \ No newline at end of file diff --git a/packages/runtime/src/server/response.ts b/packages/runtime/src/server/response.ts new file mode 100644 index 0000000000..cb081fd58d --- /dev/null +++ b/packages/runtime/src/server/response.ts @@ -0,0 +1,43 @@ +import type { ServerResponse, IncomingMessage } from 'http'; +import { parsePath } from 'history'; +import type { Location } from 'history'; +import type { + Response, +} from '../types.js'; + +export async function sendResponse( + req: IncomingMessage, + res: ServerResponse, + response: Response, +) { + res.statusCode = response.statusCode; + res.statusMessage = response.statusText; + Object.entries(response.headers || {}).forEach(([name, value]) => { + res.setHeader(name, value); + }); + if (response.value && req.method !== 'HEAD') { + res.end(response.value); + } else { + res.end(); + } +} + +/** + * ref: https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/server.tsx + */ +const REGEXP_WITH_HOSTNAME = /^https?:\/\/[^/]+/i; +export function getLocation(url: string) { + // In case of invalid URL, provide a default base url. + const locationPath = url.replace(REGEXP_WITH_HOSTNAME, '') || '/'; + const locationProps = parsePath(locationPath); + const location: Location = { + pathname: locationProps.pathname || '/', + search: locationProps.search || '', + hash: locationProps.hash || '', + state: null, + key: 'default', + }; + + return location; +} + diff --git a/packages/runtime/src/server/streamRender.tsx b/packages/runtime/src/server/streamRender.tsx index 34ce4813a6..bff805713f 100644 --- a/packages/runtime/src/server/streamRender.tsx +++ b/packages/runtime/src/server/streamRender.tsx @@ -2,8 +2,7 @@ import * as Stream from 'stream'; import type * as StreamType from 'stream'; import * as ReactDOMServer from 'react-dom/server'; import { getAllAssets } from '../Document.js'; -import type { RenderOptions } from '../runServerApp.js'; -import type { ServerAppRouterProps } from '../types.js'; +import type { ServerAppRouterProps, RenderOptions } from '../types.js'; const { Writable } = Stream; @@ -117,4 +116,4 @@ export function pipeToString(input): Promise<string> { }, }); }); -} \ No newline at end of file +} diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index 1e6c46c280..06c56b01bd 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -3,6 +3,8 @@ import type { InitialEntry, AgnosticRouteObject, Location, History, RouterInit, import type { ComponentType, PropsWithChildren } from 'react'; import type { HydrationOptions, Root } from 'react-dom/client'; import type { Params, RouteObject } from 'react-router-dom'; +import type { RouteLoaderOptions } from './routes.js'; +import type { RenderToPipeableStreamOptions, NodeWritablePiper } from './server/streamRender.js'; type UseConfig = () => RouteConfig<Record<string, any>>; type UseData = () => RouteData; @@ -300,6 +302,42 @@ export interface RouteMatch { export type RenderMode = 'SSR' | 'SSG' | 'CSR'; +interface Piper { + pipe: NodeWritablePiper; + fallback: Function; +} + +export interface Response { + statusCode?: number; + statusText?: string; + value?: string | Piper; + headers?: Record<string, string>; +} + +export interface RenderOptions { + app: AppExport; + assetsManifest: AssetsManifest; + createRoutes: (options: Pick<RouteLoaderOptions, 'requestContext' | 'renderMode'>) => RouteItem[]; + runtimeModules: RuntimeModules; + documentDataLoader?: DocumentDataLoaderConfig; + Document?: DocumentComponent; + documentOnly?: boolean; + preRender?: boolean; + renderMode?: RenderMode; + // basename is used both for server and client, once set, it will be sync to client. + basename?: string; + // serverOnlyBasename is used when just want to change basename for server. + serverOnlyBasename?: string; + routePath?: string; + disableFallback?: boolean; + routesConfig?: { + [key: string]: PageConfig; + }; + runtimeOptions?: Record<string, any>; + serverData?: any; + streamOptions?: RenderToPipeableStreamOptions; +} + declare global { interface ImportMeta { // The build target for ice.js diff --git a/packages/runtime/tests/navigator.test.ts b/packages/runtime/tests/navigator.test.ts deleted file mode 100644 index d7625168c7..0000000000 --- a/packages/runtime/tests/navigator.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { expect, it, describe } from 'vitest'; -import { createStaticNavigator } from '../src/server/navigator'; - -describe('mock server navigator', () => { - const staticNavigator = createStaticNavigator(); - it('createHref', () => { - expect(staticNavigator.createHref('/')).toBe('/'); - }); - - it('push', () => { - expect(() => staticNavigator.push('/')).toThrow(); - }); - - it('replace', () => { - expect(() => staticNavigator.replace('/')).toThrow(); - }); - - it('go', () => { - expect(() => staticNavigator.go(1)).toThrow(); - }); - - it('back', () => { - expect(() => staticNavigator.back()).toThrow(); - }); - - it('forward', () => { - expect(() => staticNavigator.forward()).toThrow(); - }); - - it('block', () => { - expect(() => staticNavigator.block()).toThrow(); - }); -}); \ No newline at end of file diff --git a/packages/shared-config/src/types.ts b/packages/shared-config/src/types.ts index 91babe7ed8..e6a182b7ca 100644 --- a/packages/shared-config/src/types.ts +++ b/packages/shared-config/src/types.ts @@ -192,6 +192,12 @@ export interface Config { memoryRouter?: boolean; server?: { + /** + * Generate sperate bundle for fallback, + * it only outputs document content. + */ + fallbackEntry?: boolean; + entry?: string; buildOptions?: (options: BuildOptions) => BuildOptions; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 159a1e27c8..8dfc36deea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -802,7 +802,7 @@ importers: specifier: ^18.0.6 version: 18.0.11 - examples/with-entry-type: + examples/with-fallback-entry: dependencies: '@ice/app': specifier: workspace:* @@ -811,10 +811,10 @@ importers: specifier: workspace:* version: link:../../packages/runtime react: - specifier: ^18.0.0 + specifier: ^18.2.0 version: 18.2.0 react-dom: - specifier: ^18.0.0 + specifier: ^18.2.0 version: 18.2.0(react@18.2.0) devDependencies: '@types/react': @@ -823,9 +823,6 @@ importers: '@types/react-dom': specifier: ^18.0.2 version: 18.0.11 - fs-extra: - specifier: ^10.0.0 - version: 10.1.0 webpack: specifier: ^5.88.0 version: 5.88.2 diff --git a/tests/integration/basic-project.test.ts b/tests/integration/basic-project.test.ts index 779666f72d..a4b3720ab1 100644 --- a/tests/integration/basic-project.test.ts +++ b/tests/integration/basic-project.test.ts @@ -65,7 +65,7 @@ describe(`build ${example}`, () => { test('render route config when downgrade to CSR.', async () => { await page.push('/downgrade.html'); - expect(await page.$$text('title')).toStrictEqual(['hello']); + expect(await page.$$text('title')).toStrictEqual(['']); expect((await page.$$text('h2')).length).toEqual(0); }); diff --git a/tests/integration/with-fallback-entry.test.ts b/tests/integration/with-fallback-entry.test.ts new file mode 100644 index 0000000000..35373fc2d2 --- /dev/null +++ b/tests/integration/with-fallback-entry.test.ts @@ -0,0 +1,26 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import { fileURLToPath } from 'url'; +import { expect, test, describe } from 'vitest'; +import { buildFixture } from '../utils/build'; +// @ts-ignore +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const example = 'with-fallback-entry'; + +describe(`build ${example}`, () => { + let sizeServer = 0; + let sizeFallback = 0; + + test('build fallback entry', async () => { + await buildFixture(example); + const serverPath = path.join(__dirname, `../../examples/${example}/build/server/index.cjs`); + sizeServer = fs.statSync(serverPath).size; + const fallbackPath = path.join(__dirname, `../../examples/${example}/build/server/index.fallback.cjs`); + sizeFallback = fs.statSync(fallbackPath).size; + + expect(sizeFallback).toBeLessThan(sizeServer); + // The Stat size of fallback entry will reduce more than 50kb. + expect(sizeServer - sizeFallback).toBeGreaterThan(50 * 1024); + }); +}); From 2616c68325d9586ff7d9ae00631b0ced76b680cf Mon Sep 17 00:00:00 2001 From: ClarkXia <xiawenwu41@gmail.com> Date: Wed, 26 Jun 2024 10:28:56 +0800 Subject: [PATCH 4/4] chore: update versions (#6906) --- .changeset/green-files-watch.md | 7 ------- .changeset/rare-shirts-check.md | 5 ----- .changeset/shaggy-cherries-fail.md | 5 ----- packages/ice/CHANGELOG.md | 12 ++++++++++++ packages/ice/package.json | 10 +++++----- packages/plugin-i18n/package.json | 4 ++-- packages/plugin-rax-compat/CHANGELOG.md | 7 +++++++ packages/plugin-rax-compat/package.json | 6 +++--- packages/rax-compat/CHANGELOG.md | 6 ++++++ packages/rax-compat/package.json | 2 +- packages/rspack-config/CHANGELOG.md | 7 +++++++ packages/rspack-config/package.json | 4 ++-- packages/runtime/CHANGELOG.md | 7 +++++++ packages/runtime/package.json | 2 +- packages/shared-config/CHANGELOG.md | 6 ++++++ packages/shared-config/package.json | 2 +- packages/webpack-config/CHANGELOG.md | 7 +++++++ packages/webpack-config/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 19 files changed, 77 insertions(+), 42 deletions(-) delete mode 100644 .changeset/green-files-watch.md delete mode 100644 .changeset/rare-shirts-check.md delete mode 100644 .changeset/shaggy-cherries-fail.md diff --git a/.changeset/green-files-watch.md b/.changeset/green-files-watch.md deleted file mode 100644 index 5125a54b58..0000000000 --- a/.changeset/green-files-watch.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@ice/shared-config': patch -'@ice/runtime': patch -'@ice/app': patch ---- - -feat: support build additional server entry for fallback diff --git a/.changeset/rare-shirts-check.md b/.changeset/rare-shirts-check.md deleted file mode 100644 index ae9de5c0ef..0000000000 --- a/.changeset/rare-shirts-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'rax-compat': minor ---- - -feat: export es2021 output diff --git a/.changeset/shaggy-cherries-fail.md b/.changeset/shaggy-cherries-fail.md deleted file mode 100644 index bc622bde2c..0000000000 --- a/.changeset/shaggy-cherries-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ice/runtime': patch ---- - -fix: support cdn url in assets manifest diff --git a/packages/ice/CHANGELOG.md b/packages/ice/CHANGELOG.md index 10c5cd07ea..944faa3ec3 100644 --- a/packages/ice/CHANGELOG.md +++ b/packages/ice/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 3.4.10 + +### Patch Changes + +- 15c8200f: feat: support build additional server entry for fallback +- Updated dependencies [15c8200f] +- Updated dependencies [d073ee5a] + - @ice/shared-config@1.2.8 + - @ice/runtime@1.4.10 + - @ice/rspack-config@1.1.8 + - @ice/webpack-config@1.1.15 + ## 3.4.9 ### Patch Changes diff --git a/packages/ice/package.json b/packages/ice/package.json index 851ee4292d..eb21ec8827 100644 --- a/packages/ice/package.json +++ b/packages/ice/package.json @@ -1,6 +1,6 @@ { "name": "@ice/app", - "version": "3.4.9", + "version": "3.4.10", "description": "provide scripts and configuration used by web framework ice", "type": "module", "main": "./esm/index.js", @@ -49,10 +49,10 @@ "dependencies": { "@ice/bundles": "0.2.6", "@ice/route-manifest": "1.2.2", - "@ice/runtime": "^1.4.8", - "@ice/shared-config": "1.2.7", - "@ice/webpack-config": "1.1.14", - "@ice/rspack-config": "1.1.7", + "@ice/runtime": "^1.4.10", + "@ice/shared-config": "1.2.8", + "@ice/webpack-config": "1.1.15", + "@ice/rspack-config": "1.1.8", "@swc/helpers": "0.5.1", "@types/express": "^4.17.14", "address": "^1.1.2", diff --git a/packages/plugin-i18n/package.json b/packages/plugin-i18n/package.json index b9a5a7c0f4..48343b3ff8 100644 --- a/packages/plugin-i18n/package.json +++ b/packages/plugin-i18n/package.json @@ -56,8 +56,8 @@ "webpack-dev-server": "4.15.0" }, "peerDependencies": { - "@ice/app": "^3.4.9", - "@ice/runtime": "^1.4.8" + "@ice/app": "^3.4.10", + "@ice/runtime": "^1.4.10" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-rax-compat/CHANGELOG.md b/packages/plugin-rax-compat/CHANGELOG.md index 493bbfd99c..880b297e2a 100644 --- a/packages/plugin-rax-compat/CHANGELOG.md +++ b/packages/plugin-rax-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.3.2 + +### Patch Changes + +- Updated dependencies [9926faae] + - rax-compat@0.3.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/plugin-rax-compat/package.json b/packages/plugin-rax-compat/package.json index b624c28411..21211ba626 100644 --- a/packages/plugin-rax-compat/package.json +++ b/packages/plugin-rax-compat/package.json @@ -1,6 +1,6 @@ { "name": "@ice/plugin-rax-compat", - "version": "0.3.1", + "version": "0.3.2", "description": "Provide rax compat support for ice.js", "license": "MIT", "type": "module", @@ -25,12 +25,12 @@ "consola": "^2.15.3", "css": "^2.2.1", "lodash-es": "^4.17.21", - "rax-compat": "^0.2.10", + "rax-compat": "^0.3.0", "style-unit": "^3.0.5", "stylesheet-loader": "^0.9.1" }, "devDependencies": { - "@ice/app": "^3.4.8", + "@ice/app": "^3.4.10", "@types/lodash-es": "^4.17.7", "webpack": "^5.88.0" }, diff --git a/packages/rax-compat/CHANGELOG.md b/packages/rax-compat/CHANGELOG.md index eeed96e36d..e490edff14 100644 --- a/packages/rax-compat/CHANGELOG.md +++ b/packages/rax-compat/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.3.0 + +### Minor Changes + +- 9926faae: feat: export es2021 output + ## 0.2.12 ### Patch Changes diff --git a/packages/rax-compat/package.json b/packages/rax-compat/package.json index add0027560..d45e7b1853 100644 --- a/packages/rax-compat/package.json +++ b/packages/rax-compat/package.json @@ -1,6 +1,6 @@ { "name": "rax-compat", - "version": "0.2.12", + "version": "0.3.0", "description": "Rax compatible mode, running rax project on the react runtime.", "files": [ "esm", diff --git a/packages/rspack-config/CHANGELOG.md b/packages/rspack-config/CHANGELOG.md index 1a080b497d..507fba5c98 100644 --- a/packages/rspack-config/CHANGELOG.md +++ b/packages/rspack-config/CHANGELOG.md @@ -1,5 +1,12 @@ # @ice/rspack-config +## 1.1.8 + +### Patch Changes + +- Updated dependencies [15c8200f] + - @ice/shared-config@1.2.8 + ## 1.1.7 ### Patch Changes diff --git a/packages/rspack-config/package.json b/packages/rspack-config/package.json index aa27d49a95..7de62a4ae7 100644 --- a/packages/rspack-config/package.json +++ b/packages/rspack-config/package.json @@ -1,6 +1,6 @@ { "name": "@ice/rspack-config", - "version": "1.1.7", + "version": "1.1.8", "repository": "alibaba/ice", "bugs": "https://github.com/alibaba/ice/issues", "homepage": "https://v3.ice.work", @@ -16,7 +16,7 @@ ], "dependencies": { "@ice/bundles": "0.2.6", - "@ice/shared-config": "1.2.7" + "@ice/shared-config": "1.2.8" }, "devDependencies": { "@rspack/core": "0.5.7" diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 96e812545a..a08ab23c26 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -1,5 +1,12 @@ # @ice/runtime +## 1.4.10 + +### Patch Changes + +- 15c8200f: feat: support build additional server entry for fallback +- d073ee5a: fix: support cdn url in assets manifest + ## 1.4.9 Fix: add export of useAsyncValue in single route mode diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 1bd960900d..75486586ca 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@ice/runtime", - "version": "1.4.9", + "version": "1.4.10", "description": "Runtime module for ice.js", "type": "module", "types": "./esm/index.d.ts", diff --git a/packages/shared-config/CHANGELOG.md b/packages/shared-config/CHANGELOG.md index 49f03bfb4b..ac50bdfcca 100644 --- a/packages/shared-config/CHANGELOG.md +++ b/packages/shared-config/CHANGELOG.md @@ -1,5 +1,11 @@ # @ice/shared-config +## 1.2.8 + +### Patch Changes + +- 15c8200f: feat: support build additional server entry for fallback + ## 1.2.7 ### Patch Changes diff --git a/packages/shared-config/package.json b/packages/shared-config/package.json index 0193fd6987..7695a17948 100644 --- a/packages/shared-config/package.json +++ b/packages/shared-config/package.json @@ -1,6 +1,6 @@ { "name": "@ice/shared-config", - "version": "1.2.7", + "version": "1.2.8", "repository": "alibaba/ice", "bugs": "https://github.com/alibaba/ice/issues", "homepage": "https://v3.ice.work", diff --git a/packages/webpack-config/CHANGELOG.md b/packages/webpack-config/CHANGELOG.md index 0e726af0bb..2c1ef90046 100644 --- a/packages/webpack-config/CHANGELOG.md +++ b/packages/webpack-config/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 1.1.15 + +### Patch Changes + +- Updated dependencies [15c8200f] + - @ice/shared-config@1.2.8 + ## 1.1.14 ### Patch Changes diff --git a/packages/webpack-config/package.json b/packages/webpack-config/package.json index 7f62579f6e..497754c645 100644 --- a/packages/webpack-config/package.json +++ b/packages/webpack-config/package.json @@ -1,6 +1,6 @@ { "name": "@ice/webpack-config", - "version": "1.1.14", + "version": "1.1.15", "repository": "alibaba/ice", "bugs": "https://github.com/alibaba/ice/issues", "homepage": "https://v3.ice.work", @@ -15,7 +15,7 @@ "*.d.ts" ], "dependencies": { - "@ice/shared-config": "1.2.7", + "@ice/shared-config": "1.2.8", "@ice/bundles": "0.2.6", "fast-glob": "^3.2.11", "process": "^0.11.10" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 204cbf4a0c..9b179afcbd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1660,16 +1660,16 @@ importers: specifier: 1.2.2 version: link:../route-manifest '@ice/rspack-config': - specifier: 1.1.7 + specifier: 1.1.8 version: link:../rspack-config '@ice/runtime': - specifier: ^1.4.8 + specifier: ^1.4.10 version: link:../runtime '@ice/shared-config': - specifier: 1.2.7 + specifier: 1.2.8 version: link:../shared-config '@ice/webpack-config': - specifier: 1.1.14 + specifier: 1.1.15 version: link:../webpack-config '@swc/helpers': specifier: 0.5.1 @@ -2224,7 +2224,7 @@ importers: specifier: ^4.17.21 version: 4.17.21 rax-compat: - specifier: ^0.2.10 + specifier: ^0.3.0 version: link:../rax-compat style-unit: specifier: ^3.0.5 @@ -2234,7 +2234,7 @@ importers: version: 0.9.1 devDependencies: '@ice/app': - specifier: ^3.4.8 + specifier: ^3.4.10 version: link:../ice '@types/lodash-es': specifier: ^4.17.7 @@ -2382,7 +2382,7 @@ importers: specifier: 0.2.6 version: link:../bundles '@ice/shared-config': - specifier: 1.2.7 + specifier: 1.2.8 version: link:../shared-config devDependencies: '@rspack/core': @@ -2487,7 +2487,7 @@ importers: specifier: 0.2.6 version: link:../bundles '@ice/shared-config': - specifier: 1.2.7 + specifier: 1.2.8 version: link:../shared-config fast-glob: specifier: ^3.2.11