ssr-plugin-react 6.2.70 → 6.2.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/src/config/base.ts +65 -0
- package/src/config/client.ts +66 -0
- package/src/config/index.ts +2 -0
- package/src/config/server.ts +56 -0
- package/src/entry/client-entry.tsx +57 -0
- package/src/entry/context.tsx +40 -0
- package/src/entry/create-context.ts +19 -0
- package/src/entry/create-router.ts +10 -0
- package/src/entry/server-entry.tsx +106 -0
- package/src/index.ts +49 -0
- package/src/tools/vite.ts +147 -0
- package/src/tools/webpack.ts +20 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
ssr-plugin-react:build: cache hit, replaying output
|
|
1
|
+
ssr-plugin-react:build: cache hit, replaying output 01b00897ba7382a0
|
|
2
2
|
ssr-plugin-react:build:
|
|
3
|
-
ssr-plugin-react:build: > ssr-plugin-react@6.2.
|
|
3
|
+
ssr-plugin-react:build: > ssr-plugin-react@6.2.71 build /home/runner/work/ssr/ssr/packages/plugin-react
|
|
4
4
|
ssr-plugin-react:build: > concurrently "tsc -p ./tsconfig.cjs.json " " tsc -p ./tsconfig.esm.json"
|
|
5
5
|
ssr-plugin-react:build:
|
|
6
|
-
ssr-plugin-react:build: [0] tsc -p ./tsconfig.cjs.json exited with code 0
|
|
7
6
|
ssr-plugin-react:build: [1] tsc -p ./tsconfig.esm.json exited with code 0
|
|
7
|
+
ssr-plugin-react:build: [0] tsc -p ./tsconfig.cjs.json exited with code 0
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [6.2.71](https://github.com/zhangyuang/ssr/compare/plugin-react@6.2.70...plugin-react@6.2.71) (2024-01-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* plugin-react close[#321](https://github.com/zhangyuang/ssr/issues/321) ([a1b69f8](https://github.com/zhangyuang/ssr/commit/a1b69f8ba467b86d3d7fc1fc63d78f97bf3b8afb))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
## [6.2.70](https://github.com/zhangyuang/ssr/compare/plugin-react@6.2.69...plugin-react@6.2.70) (2024-01-24)
|
|
2
11
|
|
|
3
12
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { Mode } from 'ssr-types'
|
|
4
|
+
import { getCwd, loadConfig, setStyle, loadModuleFromFramework, getBuildConfig, addCommonChain } from 'ssr-common-utils'
|
|
5
|
+
import * as WebpackChain from 'webpack-chain'
|
|
6
|
+
import * as webpack from 'ssr-webpack4'
|
|
7
|
+
|
|
8
|
+
const MiniCssExtractPlugin = require(loadModuleFromFramework('ssr-mini-css-extract-plugin'))
|
|
9
|
+
const WebpackBar = require('webpackbar')
|
|
10
|
+
|
|
11
|
+
const getBaseConfig = (chain: WebpackChain, isServer: boolean) => {
|
|
12
|
+
const config = loadConfig()
|
|
13
|
+
const { moduleFileExtensions, chainBaseConfig, alias, define } = config
|
|
14
|
+
const mode = process.env.NODE_ENV as Mode
|
|
15
|
+
|
|
16
|
+
chain.mode(mode)
|
|
17
|
+
chain.module.strictExportPresence(true)
|
|
18
|
+
chain
|
|
19
|
+
.resolve
|
|
20
|
+
.modules
|
|
21
|
+
.add('node_modules')
|
|
22
|
+
.add(join(getCwd(), './node_modules'))
|
|
23
|
+
.end()
|
|
24
|
+
.extensions.merge(moduleFileExtensions)
|
|
25
|
+
.end()
|
|
26
|
+
.alias
|
|
27
|
+
.end()
|
|
28
|
+
|
|
29
|
+
alias && Object.keys(alias).forEach(item => {
|
|
30
|
+
chain.resolve.alias
|
|
31
|
+
.set(item, alias[item])
|
|
32
|
+
})
|
|
33
|
+
addCommonChain(chain, isServer)
|
|
34
|
+
setStyle(chain, /\.css$/, {
|
|
35
|
+
rule: 'css',
|
|
36
|
+
isServer,
|
|
37
|
+
importLoaders: 1
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
setStyle(chain, /\.less$/, {
|
|
41
|
+
rule: 'less',
|
|
42
|
+
loader: 'less-loader',
|
|
43
|
+
isServer,
|
|
44
|
+
importLoaders: 2
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
chain.plugin('minify-css').use(MiniCssExtractPlugin, getBuildConfig().cssBuildConfig)
|
|
48
|
+
|
|
49
|
+
chain.plugin('webpackBar').use(new WebpackBar({
|
|
50
|
+
name: isServer ? 'server' : 'client',
|
|
51
|
+
color: isServer ? '#f173ac' : '#45b97c'
|
|
52
|
+
}))
|
|
53
|
+
chain.plugin('ssrDefine').use(webpack.DefinePlugin, [{
|
|
54
|
+
...process.env,
|
|
55
|
+
__isBrowser__: !isServer,
|
|
56
|
+
...(isServer ? define?.server : define?.client),
|
|
57
|
+
...define?.base
|
|
58
|
+
}])
|
|
59
|
+
chainBaseConfig(chain, isServer)
|
|
60
|
+
return config
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
getBaseConfig
|
|
65
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { loadConfig, getSplitChunksOptions, getOutputPublicPath, loadModuleFromFramework, getBuildConfig, terserConfig, asyncChunkMap } from 'ssr-common-utils'
|
|
2
|
+
import * as WebpackChain from 'webpack-chain'
|
|
3
|
+
import { getBaseConfig } from './base'
|
|
4
|
+
|
|
5
|
+
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
|
|
6
|
+
const safePostCssParser = require('postcss-safe-parser')
|
|
7
|
+
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
|
|
8
|
+
const loadModule = loadModuleFromFramework
|
|
9
|
+
|
|
10
|
+
const getClientWebpack = (chain: WebpackChain) => {
|
|
11
|
+
const { isDev, chunkName, getOutput, cwd, chainClientConfig, host, fePort } = loadConfig()
|
|
12
|
+
const buildConfig = getBuildConfig()
|
|
13
|
+
|
|
14
|
+
const shouldUseSourceMap = isDev || Boolean(process.env.GENERATE_SOURCEMAP)
|
|
15
|
+
const publicPath = getOutputPublicPath()
|
|
16
|
+
getBaseConfig(chain, false)
|
|
17
|
+
chain.devtool(isDev ? 'eval-source-map' : (shouldUseSourceMap ? 'source-map' : false))
|
|
18
|
+
chain.entry(chunkName)
|
|
19
|
+
.add(require.resolve('../entry/client-entry'))
|
|
20
|
+
.end()
|
|
21
|
+
.output
|
|
22
|
+
.path(getOutput().clientOutPut)
|
|
23
|
+
.filename(buildConfig.jsBuldConfig.fileName)
|
|
24
|
+
.chunkFilename(buildConfig.jsBuldConfig.chunkFileName)
|
|
25
|
+
.publicPath(publicPath)
|
|
26
|
+
.end()
|
|
27
|
+
chain.optimization
|
|
28
|
+
.runtimeChunk(true)
|
|
29
|
+
.splitChunks(getSplitChunksOptions(asyncChunkMap))
|
|
30
|
+
.when(!isDev, optimization => {
|
|
31
|
+
optimization.minimizer('terser')
|
|
32
|
+
.use(loadModule('terser-webpack-plugin'), [terserConfig()])
|
|
33
|
+
optimization.minimizer('optimize-css').use(loadModule('optimize-css-assets-webpack-plugin'), [{
|
|
34
|
+
cssProcessorOptions: {
|
|
35
|
+
parser: safePostCssParser,
|
|
36
|
+
map: shouldUseSourceMap ? {
|
|
37
|
+
inline: false,
|
|
38
|
+
annotation: true
|
|
39
|
+
} : false
|
|
40
|
+
}
|
|
41
|
+
}])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
chain.plugin('moduleNotFound').use(ModuleNotFoundPlugin, [cwd])
|
|
45
|
+
|
|
46
|
+
chain.plugin('manifest').use(loadModule('webpack-manifest-plugin'), [{
|
|
47
|
+
fileName: 'asset-manifest.json'
|
|
48
|
+
}])
|
|
49
|
+
|
|
50
|
+
chain.when(isDev, chain => {
|
|
51
|
+
chain.plugin('fast-refresh').use(new ReactRefreshWebpackPlugin({
|
|
52
|
+
overlay: {
|
|
53
|
+
sockHost: host,
|
|
54
|
+
sockPort: fePort
|
|
55
|
+
}
|
|
56
|
+
}))
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
chainClientConfig(chain) // 合并用户自定义配置
|
|
60
|
+
|
|
61
|
+
return chain.toConfig()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
getClientWebpack
|
|
66
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
import { loadConfig, nodeExternals, loadModuleFromFramework } from 'ssr-common-utils'
|
|
3
|
+
import * as WebpackChain from 'webpack-chain'
|
|
4
|
+
import * as webpack from 'ssr-webpack4'
|
|
5
|
+
|
|
6
|
+
import { getBaseConfig } from './base'
|
|
7
|
+
|
|
8
|
+
const getServerWebpack = (chain: WebpackChain) => {
|
|
9
|
+
const config = loadConfig()
|
|
10
|
+
const { isDev, cwd, getOutput, chainServerConfig, whiteList, chunkName } = config
|
|
11
|
+
const shouldUseSourceMap = isDev || Boolean(process.env.GENERATE_SOURCEMAP)
|
|
12
|
+
getBaseConfig(chain, true)
|
|
13
|
+
chain.devtool(isDev ? 'inline-source-map' : false)
|
|
14
|
+
chain.target('node')
|
|
15
|
+
chain.entry(chunkName)
|
|
16
|
+
.add(require.resolve('../entry/server-entry'))
|
|
17
|
+
.end()
|
|
18
|
+
.output
|
|
19
|
+
.path(getOutput().serverOutPut)
|
|
20
|
+
.filename('[name].server.js')
|
|
21
|
+
.libraryTarget('commonjs')
|
|
22
|
+
|
|
23
|
+
const modulesDir = [join(cwd, './node_modules')]
|
|
24
|
+
chain.externals(nodeExternals({
|
|
25
|
+
whitelist: whiteList,
|
|
26
|
+
// externals Dir contains example/xxx/node_modules ssr/node_modules
|
|
27
|
+
modulesDir
|
|
28
|
+
}))
|
|
29
|
+
if (!isDev) {
|
|
30
|
+
chain.optimization.minimizer('terser')
|
|
31
|
+
.use(loadModuleFromFramework('terser-webpack-plugin'), [{
|
|
32
|
+
terserOptions: {
|
|
33
|
+
keep_fnames: true
|
|
34
|
+
},
|
|
35
|
+
extractComments: false,
|
|
36
|
+
parallel: true,
|
|
37
|
+
cache: true,
|
|
38
|
+
sourceMap: shouldUseSourceMap
|
|
39
|
+
}])
|
|
40
|
+
}
|
|
41
|
+
chain.when(isDev, () => {
|
|
42
|
+
chain.watch(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
chain.plugin('serverLimit').use(webpack.optimize.LimitChunkCountPlugin, [{
|
|
46
|
+
maxChunks: 1
|
|
47
|
+
}])
|
|
48
|
+
|
|
49
|
+
chainServerConfig(chain) // 合并用户自定义配置
|
|
50
|
+
|
|
51
|
+
return chain.toConfig()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
getServerWebpack
|
|
56
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createElement } from 'react'
|
|
2
|
+
import * as ReactDOM from 'react-dom'
|
|
3
|
+
import { proxy } from 'valtio'
|
|
4
|
+
import 'react-router' // for vite prebundle list
|
|
5
|
+
import { BrowserRouter, Route, Switch } from 'react-router-dom'
|
|
6
|
+
import { preloadComponent, isMicro, setStoreContext, setStore } from 'ssr-common-utils'
|
|
7
|
+
import { wrapComponent } from 'ssr-hoc-react'
|
|
8
|
+
import { LayoutProps } from 'ssr-types'
|
|
9
|
+
import { STORE_CONTEXT as Context } from '_build/create-context'
|
|
10
|
+
import { Routes } from './create-router'
|
|
11
|
+
import { AppContext } from './context'
|
|
12
|
+
|
|
13
|
+
const { FeRoutes, layoutFetch, App, store } = Routes
|
|
14
|
+
|
|
15
|
+
const clientRender = async (): Promise<void> => {
|
|
16
|
+
const IApp = App ?? function (props: LayoutProps) {
|
|
17
|
+
return props.children!
|
|
18
|
+
}
|
|
19
|
+
setStoreContext(Context)
|
|
20
|
+
for (const key in store) {
|
|
21
|
+
store[key] = proxy(window.__VALTIO_DATA__?.[key])
|
|
22
|
+
}
|
|
23
|
+
setStore(store ?? {})
|
|
24
|
+
const baseName = isMicro() ? window.clientPrefix : window.prefix
|
|
25
|
+
const routes = await preloadComponent(FeRoutes, baseName)
|
|
26
|
+
ReactDOM[window.__USE_SSR__ ? 'hydrate' : 'render'](
|
|
27
|
+
createElement(BrowserRouter, {
|
|
28
|
+
basename: baseName
|
|
29
|
+
}, createElement(AppContext as any, {
|
|
30
|
+
children: createElement(Switch, null,
|
|
31
|
+
createElement(IApp as any, null, createElement(Switch, null,
|
|
32
|
+
routes.map(item => {
|
|
33
|
+
const { fetch, component, path } = item
|
|
34
|
+
component.fetch = fetch
|
|
35
|
+
component.layoutFetch = layoutFetch
|
|
36
|
+
const WrappedComponent = wrapComponent(component)
|
|
37
|
+
return createElement(Route, {
|
|
38
|
+
exact: true,
|
|
39
|
+
key: path,
|
|
40
|
+
path: path,
|
|
41
|
+
render: () => createElement(WrappedComponent, {
|
|
42
|
+
key: location.pathname
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
}))))
|
|
46
|
+
}))
|
|
47
|
+
, document.querySelector(window.ssrDevInfo.rootId ?? '#app'))
|
|
48
|
+
if (!window.__USE_VITE__) {
|
|
49
|
+
(module as any)?.hot?.accept?.()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
clientRender()
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
clientRender
|
|
57
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useReducer, createElement } from 'react'
|
|
2
|
+
import { IProps, Action, IWindow, ReactRoutesType } from 'ssr-types'
|
|
3
|
+
import { STORE_CONTEXT as Context } from '_build/create-context'
|
|
4
|
+
import { Routes } from './create-router'
|
|
5
|
+
|
|
6
|
+
const { reducer, state } = Routes as ReactRoutesType
|
|
7
|
+
|
|
8
|
+
const userState = state ?? {}
|
|
9
|
+
const userReducer = reducer ?? function () {}
|
|
10
|
+
|
|
11
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
12
|
+
|
|
13
|
+
// 客户端的 context 只需要创建一次,在页面整个生命周期内共享
|
|
14
|
+
declare const window: IWindow
|
|
15
|
+
|
|
16
|
+
function defaultReducer (state: any, action: Action) {
|
|
17
|
+
switch (action.type) {
|
|
18
|
+
case 'updateContext':
|
|
19
|
+
if (isDev) {
|
|
20
|
+
console.log('[SSR:updateContext]: dispatch updateContext with action')
|
|
21
|
+
console.log(action)
|
|
22
|
+
}
|
|
23
|
+
return { ...state, ...action.payload }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const initialState = Object.assign({}, userState ?? {}, window.__INITIAL_DATA__)
|
|
28
|
+
|
|
29
|
+
function combineReducer (state: any, action: any) {
|
|
30
|
+
return defaultReducer(state, action) || userReducer(state, action)
|
|
31
|
+
}
|
|
32
|
+
export function AppContext (props: IProps) {
|
|
33
|
+
const [state, dispatch] = useReducer(combineReducer, initialState)
|
|
34
|
+
return createElement(Context.Provider, {
|
|
35
|
+
value: {
|
|
36
|
+
state,
|
|
37
|
+
dispatch
|
|
38
|
+
}
|
|
39
|
+
}, props.children)
|
|
40
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The file is provisional,don't modify it
|
|
2
|
+
|
|
3
|
+
import { Context, createContext } from 'react'
|
|
4
|
+
import { IContext } from 'ssr-types'
|
|
5
|
+
let STORE_CONTEXT: Context<IContext>
|
|
6
|
+
if (__isBrowser__) {
|
|
7
|
+
STORE_CONTEXT = window.STORE_CONTEXT || createContext<IContext>({
|
|
8
|
+
state: {}
|
|
9
|
+
})
|
|
10
|
+
window.STORE_CONTEXT = STORE_CONTEXT
|
|
11
|
+
} else {
|
|
12
|
+
STORE_CONTEXT = createContext<IContext>({
|
|
13
|
+
state: {}
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
STORE_CONTEXT
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { combineRoutes } from 'ssr-common-utils'
|
|
2
|
+
import * as declareRoutes from '_build/ssr-declare-routes'
|
|
3
|
+
import * as ManualRoutes from '_build/ssr-manual-routes'
|
|
4
|
+
import { ReactRoutesType } from 'ssr-types'
|
|
5
|
+
|
|
6
|
+
const Routes = combineRoutes(declareRoutes, ManualRoutes) as ReactRoutesType
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
Routes
|
|
10
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { PassThrough } from 'stream'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { createElement } from 'react'
|
|
4
|
+
import { StaticRouter } from 'react-router-dom'
|
|
5
|
+
import { renderToString, renderToNodeStream } from 'react-dom/server'
|
|
6
|
+
import { findRoute, getManifest, logGreen, normalizePath, getAsyncCssChunk, getAsyncJsChunk, splitPageInfo, reactRefreshFragment, localStorageWrapper, checkRoute, useStore } from 'ssr-common-utils'
|
|
7
|
+
import { ISSRContext, IConfig, ReactESMPreloadFeRouteItem, DynamicFC, StaticFC } from 'ssr-types'
|
|
8
|
+
import { serialize } from 'ssr-serialize-javascript'
|
|
9
|
+
import { STORE_CONTEXT as Context } from '_build/create-context'
|
|
10
|
+
import { Routes } from './create-router'
|
|
11
|
+
|
|
12
|
+
const { FeRoutes, layoutFetch, state, Layout, store } = Routes
|
|
13
|
+
|
|
14
|
+
const serverRender = async (ctx: ISSRContext, config: IConfig) => {
|
|
15
|
+
const { mode, parallelFetch, prefix, isVite, isDev, clientPrefix, stream, rootId, hashRouter } = config
|
|
16
|
+
const rawPath = ctx.request.path ?? ctx.request.url
|
|
17
|
+
const path = normalizePath(rawPath, prefix)
|
|
18
|
+
const routeItem = findRoute<ReactESMPreloadFeRouteItem>(FeRoutes, path)
|
|
19
|
+
checkRoute({ routeItem, path })
|
|
20
|
+
const { fetch, webpackChunkName, component } = routeItem
|
|
21
|
+
|
|
22
|
+
const fn = async () => {
|
|
23
|
+
const dynamicCssOrder = await getAsyncCssChunk(ctx, webpackChunkName, config)
|
|
24
|
+
const dynamicJsOrder = await getAsyncJsChunk(ctx, webpackChunkName, config)
|
|
25
|
+
const manifest = await getManifest(config)
|
|
26
|
+
|
|
27
|
+
const injectCss = ((isVite && isDev) ? [
|
|
28
|
+
<script src="/@vite/client" type="module" key="vite-client" />,
|
|
29
|
+
<script key="vite-react-refresh" type="module" dangerouslySetInnerHTML={{
|
|
30
|
+
__html: reactRefreshFragment
|
|
31
|
+
}} />
|
|
32
|
+
] : dynamicCssOrder.map(css => manifest[css]).filter(Boolean).map(css => <link rel='stylesheet' key={css} href={css} />))
|
|
33
|
+
.concat((isVite && isDev) ? [] : dynamicJsOrder.map(js => manifest[js]).filter(Boolean).map(js =>
|
|
34
|
+
<link href={js} as="script" rel={isVite ? 'modulepreload' : 'preload'} key={js} />
|
|
35
|
+
))
|
|
36
|
+
|
|
37
|
+
const injectScript = [
|
|
38
|
+
...(isVite ? [<script key="viteWindowInit" dangerouslySetInnerHTML={{
|
|
39
|
+
__html: 'window.__USE_VITE__=true'
|
|
40
|
+
}} />] : []),
|
|
41
|
+
...((isVite && isDev) ? [<script type="module" src='/node_modules/ssr-plugin-react/esm/entry/client-entry.js' key="vite-react-entry" />] : []),
|
|
42
|
+
...dynamicJsOrder.map(js => manifest[js]).filter(Boolean).map(item => <script key={item} src={item} type={isVite ? 'module' : 'text/javascript'} />)
|
|
43
|
+
]
|
|
44
|
+
const staticList = {
|
|
45
|
+
injectCss,
|
|
46
|
+
injectScript
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const isCsr = !!(mode === 'csr' || ctx.request.query?.csr)
|
|
50
|
+
const Component = isCsr ? React.Fragment : (component.name === 'dynamicComponent' ? (await (component as DynamicFC)()).default : component as StaticFC)
|
|
51
|
+
|
|
52
|
+
if (isCsr) {
|
|
53
|
+
logGreen(`Current path ${path} use csr render mode`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let [layoutFetchData, fetchData] = [{}, {}]
|
|
57
|
+
|
|
58
|
+
if (!isCsr) {
|
|
59
|
+
const currentFetch = fetch ? (await fetch()).default : null
|
|
60
|
+
const lF = layoutFetch ? layoutFetch({ ctx }) : Promise.resolve({})
|
|
61
|
+
const CF = currentFetch ? currentFetch({ ctx }) : Promise.resolve({});
|
|
62
|
+
[layoutFetchData, fetchData] = parallelFetch ? await Promise.all([lF, CF]) : [await lF, await CF]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const combineData = isCsr ? null : Object.assign(state ?? {}, layoutFetchData ?? {}, fetchData ?? {})
|
|
66
|
+
const ssrDevInfo = { manifest: isDev ? manifest : '', rootId }
|
|
67
|
+
const innerHTML = splitPageInfo({
|
|
68
|
+
'window.__USE_SSR__': !isCsr,
|
|
69
|
+
'window.__INITIAL_DATA__': isCsr ? {} : serialize(combineData),
|
|
70
|
+
'window.__USE_VITE__': isVite,
|
|
71
|
+
'window.prefix': `"${prefix}"`,
|
|
72
|
+
'window.clientPrefix': `"${clientPrefix ?? ''}"`,
|
|
73
|
+
'window.ssrDevInfo': JSON.stringify(ssrDevInfo),
|
|
74
|
+
'window.hashRouter': Boolean(hashRouter),
|
|
75
|
+
'window.__VALTIO_DATA__': isCsr ? {} : serialize(useStore())
|
|
76
|
+
})
|
|
77
|
+
const injectState = <script dangerouslySetInnerHTML={{ __html: innerHTML }} />
|
|
78
|
+
// with jsx type error, use createElement here
|
|
79
|
+
const ele = createElement(StaticRouter, {
|
|
80
|
+
location: ctx.request.url,
|
|
81
|
+
basename: prefix === '/' ? undefined : prefix
|
|
82
|
+
}, createElement(Context.Provider, {
|
|
83
|
+
value: {
|
|
84
|
+
state: combineData
|
|
85
|
+
}
|
|
86
|
+
}, createElement(Layout, {
|
|
87
|
+
ctx: ctx,
|
|
88
|
+
config: config,
|
|
89
|
+
staticList: staticList,
|
|
90
|
+
injectState: injectState
|
|
91
|
+
}, createElement(Component, null))))
|
|
92
|
+
// for ctx.body will loose asynclocalstorage context, consume stream in advance like vue2/3
|
|
93
|
+
return stream ? renderToNodeStream(ele).pipe(new PassThrough()) : renderToString(ele)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return await localStorageWrapper.run({
|
|
97
|
+
context: Context,
|
|
98
|
+
ctx,
|
|
99
|
+
store
|
|
100
|
+
}, fn)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
serverRender,
|
|
105
|
+
Routes
|
|
106
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { loadConfig } from 'ssr-common-utils'
|
|
2
|
+
|
|
3
|
+
const { isVite, optimize } = loadConfig()
|
|
4
|
+
const spinner = require('ora')('Building')
|
|
5
|
+
|
|
6
|
+
export function clientPlugin () {
|
|
7
|
+
return {
|
|
8
|
+
name: 'plugin-react',
|
|
9
|
+
start: async () => {
|
|
10
|
+
if (isVite) {
|
|
11
|
+
const { viteStart } = await import('./tools/vite')
|
|
12
|
+
await viteStart()
|
|
13
|
+
} else {
|
|
14
|
+
if (optimize) {
|
|
15
|
+
spinner.start()
|
|
16
|
+
const { viteBuildClient } = await import('./tools/vite')
|
|
17
|
+
await viteBuildClient()
|
|
18
|
+
process.env.NODE_ENV = 'development'
|
|
19
|
+
spinner.stop()
|
|
20
|
+
const { webpackStart } = await import('./tools/webpack')
|
|
21
|
+
await webpackStart()
|
|
22
|
+
} else {
|
|
23
|
+
const { webpackStart } = await import('./tools/webpack')
|
|
24
|
+
await webpackStart()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
build: async () => {
|
|
29
|
+
if (isVite) {
|
|
30
|
+
const { viteBuild } = await import('./tools/vite')
|
|
31
|
+
await viteBuild()
|
|
32
|
+
} else {
|
|
33
|
+
if (optimize) {
|
|
34
|
+
spinner.start()
|
|
35
|
+
const { viteBuildClient } = await import('./tools/vite')
|
|
36
|
+
await viteBuildClient()
|
|
37
|
+
spinner.stop()
|
|
38
|
+
const { webpackBuild } = await import('./tools/webpack')
|
|
39
|
+
await webpackBuild()
|
|
40
|
+
} else {
|
|
41
|
+
const { webpackBuild } = await import('./tools/webpack')
|
|
42
|
+
await webpackBuild()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export * from './tools/vite'
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { build, UserConfig } from 'vite'
|
|
2
|
+
import {
|
|
3
|
+
loadConfig, chunkNamePlugin, rollupOutputOptions, manifestPlugin, commonConfig,
|
|
4
|
+
asyncOptimizeChunkPlugin, getOutputPublicPath, judgeAntd
|
|
5
|
+
} from 'ssr-common-utils'
|
|
6
|
+
import react from '@vitejs/plugin-react'
|
|
7
|
+
import { createStyleImportPlugin, AndDesignVueResolve, VantResolve, ElementPlusResolve, NutuiResolve, AntdResolve } from 'ssr-vite-plugin-style-import'
|
|
8
|
+
const { getOutput, reactServerEntry, reactClientEntry, viteConfig, supportOptinalChaining, isDev, define, babelOptions, optimize } = loadConfig()
|
|
9
|
+
const { clientOutPut, serverOutPut } = getOutput()
|
|
10
|
+
const isAntd5 = judgeAntd() === 5
|
|
11
|
+
const extraInclude = ([] as string[]).concat(isAntd5 ? ['react-is'] : [])
|
|
12
|
+
|
|
13
|
+
const styleImportConfig = {
|
|
14
|
+
include: ['**/*.vue', '**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx', /chunkName/],
|
|
15
|
+
resolves: [
|
|
16
|
+
AndDesignVueResolve(),
|
|
17
|
+
VantResolve(),
|
|
18
|
+
ElementPlusResolve(),
|
|
19
|
+
NutuiResolve(),
|
|
20
|
+
AntdResolve()
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
const serverPlugins = [
|
|
24
|
+
react({
|
|
25
|
+
...viteConfig?.()?.server?.defaultPluginOptions,
|
|
26
|
+
jsxRuntime: 'automatic',
|
|
27
|
+
babel: {
|
|
28
|
+
...babelOptions,
|
|
29
|
+
plugins: [
|
|
30
|
+
...babelOptions?.plugins ?? [],
|
|
31
|
+
...!supportOptinalChaining ? [
|
|
32
|
+
'@babel/plugin-proposal-optional-chaining',
|
|
33
|
+
'@babel/plugin-proposal-nullish-coalescing-operator'
|
|
34
|
+
] : []
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}),
|
|
38
|
+
viteConfig?.()?.common?.extraPlugin,
|
|
39
|
+
viteConfig?.()?.server?.extraPlugin,
|
|
40
|
+
createStyleImportPlugin(styleImportConfig)
|
|
41
|
+
].filter(Boolean)
|
|
42
|
+
|
|
43
|
+
const serverConfig: UserConfig = {
|
|
44
|
+
...commonConfig(),
|
|
45
|
+
...viteConfig?.().server?.otherConfig,
|
|
46
|
+
plugins: viteConfig?.()?.server?.processPlugin?.(serverPlugins) ?? serverPlugins,
|
|
47
|
+
esbuild: {
|
|
48
|
+
...viteConfig?.().server?.otherConfig?.esbuild,
|
|
49
|
+
keepNames: true,
|
|
50
|
+
logOverride: { 'this-is-undefined-in-esm': 'silent' }
|
|
51
|
+
},
|
|
52
|
+
optimizeDeps: {
|
|
53
|
+
...viteConfig?.().server?.otherConfig?.optimizeDeps,
|
|
54
|
+
include: extraInclude.concat(...viteConfig?.().server?.otherConfig?.optimizeDeps?.include ?? []),
|
|
55
|
+
esbuildOptions: {
|
|
56
|
+
...viteConfig?.().server?.otherConfig?.optimizeDeps?.esbuildOptions,
|
|
57
|
+
// @ts-expect-error
|
|
58
|
+
bundle: isDev
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
build: {
|
|
62
|
+
...viteConfig?.().server?.otherConfig?.build,
|
|
63
|
+
ssr: reactServerEntry,
|
|
64
|
+
outDir: serverOutPut,
|
|
65
|
+
rollupOptions: {
|
|
66
|
+
...viteConfig?.().server?.otherConfig?.build?.rollupOptions,
|
|
67
|
+
input: isDev ? reactClientEntry : reactServerEntry, // setting prebundle list by client-entry in dev
|
|
68
|
+
output: {
|
|
69
|
+
entryFileNames: 'Page.server.js',
|
|
70
|
+
assetFileNames: rollupOutputOptions().assetFileNames
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
define: {
|
|
75
|
+
...viteConfig?.().server?.otherConfig?.define,
|
|
76
|
+
__isBrowser__: false,
|
|
77
|
+
...define?.base,
|
|
78
|
+
...define?.server
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const clientPlugins = [
|
|
82
|
+
react({
|
|
83
|
+
...viteConfig?.()?.client?.defaultPluginOptions,
|
|
84
|
+
jsxRuntime: 'automatic',
|
|
85
|
+
...babelOptions
|
|
86
|
+
}),
|
|
87
|
+
viteConfig?.()?.common?.extraPlugin,
|
|
88
|
+
viteConfig?.()?.client?.extraPlugin,
|
|
89
|
+
createStyleImportPlugin(styleImportConfig)
|
|
90
|
+
].filter(Boolean)
|
|
91
|
+
const clientConfig: UserConfig = {
|
|
92
|
+
...commonConfig(),
|
|
93
|
+
...viteConfig?.().client?.otherConfig,
|
|
94
|
+
base: isDev ? '/' : getOutputPublicPath(),
|
|
95
|
+
esbuild: {
|
|
96
|
+
...viteConfig?.().client?.otherConfig?.esbuild,
|
|
97
|
+
keepNames: true,
|
|
98
|
+
logOverride: { 'this-is-undefined-in-esm': 'silent' }
|
|
99
|
+
},
|
|
100
|
+
optimizeDeps: {
|
|
101
|
+
...viteConfig?.().client?.otherConfig?.optimizeDeps,
|
|
102
|
+
include: ['react-router', ...extraInclude].concat(...viteConfig?.().client?.otherConfig?.optimizeDeps?.include ?? []),
|
|
103
|
+
exclude: ['ssr-hoc-react'].concat(...viteConfig?.().client?.otherConfig?.optimizeDeps?.exclude ?? [])
|
|
104
|
+
},
|
|
105
|
+
plugins: viteConfig?.()?.client?.processPlugin?.(clientPlugins) ?? clientPlugins,
|
|
106
|
+
build: {
|
|
107
|
+
...viteConfig?.().client?.otherConfig?.build,
|
|
108
|
+
...(optimize ? { write: false } : {}),
|
|
109
|
+
ssrManifest: true,
|
|
110
|
+
outDir: clientOutPut,
|
|
111
|
+
rollupOptions: {
|
|
112
|
+
...viteConfig?.().client?.otherConfig?.build?.rollupOptions,
|
|
113
|
+
input: reactClientEntry,
|
|
114
|
+
output: rollupOutputOptions(),
|
|
115
|
+
plugins: [chunkNamePlugin(), asyncOptimizeChunkPlugin(), manifestPlugin()]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
define: {
|
|
119
|
+
...viteConfig?.().client?.otherConfig?.define,
|
|
120
|
+
__isBrowser__: true,
|
|
121
|
+
...define?.base,
|
|
122
|
+
...define?.client
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const viteStart = async () => {
|
|
126
|
+
//
|
|
127
|
+
}
|
|
128
|
+
const viteBuild = async () => {
|
|
129
|
+
await build({ ...clientConfig, mode: 'production' })
|
|
130
|
+
await build({ ...serverConfig, mode: 'production' })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const viteBuildClient = async () => {
|
|
134
|
+
await build({ ...clientConfig, mode: 'production' }).catch(_ => { })
|
|
135
|
+
}
|
|
136
|
+
const viteBuildServer = async () => {
|
|
137
|
+
await build({ ...serverConfig, mode: 'production' })
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
viteBuild,
|
|
142
|
+
viteStart,
|
|
143
|
+
serverConfig,
|
|
144
|
+
clientConfig,
|
|
145
|
+
viteBuildClient,
|
|
146
|
+
viteBuildServer
|
|
147
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as WebpackChain from 'webpack-chain'
|
|
2
|
+
|
|
3
|
+
export const webpackStart = async () => {
|
|
4
|
+
const { startServerBuild } = await import('ssr-webpack/cjs/server')
|
|
5
|
+
const { getServerWebpack } = await import('../config/server')
|
|
6
|
+
const serverConfigChain = new WebpackChain()
|
|
7
|
+
|
|
8
|
+
const { startClientServer } = await import('ssr-webpack')
|
|
9
|
+
const { getClientWebpack } = await import('../config')
|
|
10
|
+
const clientConfigChain = new WebpackChain()
|
|
11
|
+
await Promise.all([startServerBuild(getServerWebpack(serverConfigChain)), startClientServer(getClientWebpack(clientConfigChain))])
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const webpackBuild = async () => {
|
|
15
|
+
const { startServerBuild, startClientBuild } = await import('ssr-webpack')
|
|
16
|
+
const { getClientWebpack, getServerWebpack } = await import('../config')
|
|
17
|
+
const serverConfigChain = new WebpackChain()
|
|
18
|
+
const clientConfigChain = new WebpackChain()
|
|
19
|
+
await Promise.all([startServerBuild(getServerWebpack(serverConfigChain)), startClientBuild(getClientWebpack(clientConfigChain))])
|
|
20
|
+
}
|