@modern-js/plugin-ssg 1.1.3-rc.0 → 1.2.3
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/CHANGELOG.md +49 -4
- package/dist/js/modern/index.js +1 -1
- package/dist/js/modern/libs/make.js +2 -2
- package/dist/js/modern/libs/util.js +12 -2
- package/dist/js/modern/server/index.js +6 -13
- package/dist/js/modern/server/process.js +6 -1
- package/dist/js/node/index.js +1 -1
- package/dist/js/node/libs/make.js +2 -2
- package/dist/js/node/libs/util.js +15 -2
- package/dist/js/node/server/index.js +6 -12
- package/dist/js/node/server/process.js +6 -1
- package/dist/types/libs/util.d.ts +11 -1
- package/dist/types/server/index.d.ts +1 -1
- package/jest.config.js +8 -0
- package/package.json +12 -9
- package/tests/index.test.ts +7 -0
- package/tests/lib.test.ts +2 -2
- package/tests/tsconfig.json +1 -3
- package/tests/util.test.ts +55 -42
- package/tsconfig.json +1 -3
- package/src/global.d.ts +0 -1
- package/src/index.ts +0 -209
- package/src/libs/make.ts +0 -45
- package/src/libs/output.ts +0 -19
- package/src/libs/replace.ts +0 -42
- package/src/libs/util.ts +0 -127
- package/src/server/consts.ts +0 -1
- package/src/server/index.ts +0 -85
- package/src/server/prerender.ts +0 -49
- package/src/server/process.ts +0 -90
- package/src/types.ts +0 -51
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,57 @@
|
|
|
1
1
|
# @modern-js/plugin-ssg
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.2.3
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- 1e17ef11: fix: fix ssg with new plugin route logic
|
|
8
|
+
- Updated dependencies [53aca274]
|
|
9
|
+
- Updated dependencies [78279953]
|
|
10
|
+
- Updated dependencies [e116ace5]
|
|
11
|
+
- Updated dependencies [4d72edea]
|
|
12
|
+
- @modern-js/core@1.4.1
|
|
13
|
+
- @modern-js/utils@1.3.1
|
|
14
|
+
|
|
15
|
+
## 1.2.2
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 5a7901d7: fix ssg url
|
|
20
|
+
- Updated dependencies [d9cc5ea9]
|
|
21
|
+
- Updated dependencies [bd819a8d]
|
|
22
|
+
- Updated dependencies [ec4dbffb]
|
|
23
|
+
- Updated dependencies [d099e5c5]
|
|
24
|
+
- Updated dependencies [bada2879]
|
|
25
|
+
- Updated dependencies [24f616ca]
|
|
26
|
+
- Updated dependencies [bd819a8d]
|
|
27
|
+
- @modern-js/core@1.4.0
|
|
28
|
+
- @modern-js/utils@1.3.0
|
|
29
|
+
|
|
30
|
+
## 1.2.1
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- 83166714: change .npmignore
|
|
35
|
+
- Updated dependencies [83166714]
|
|
36
|
+
- Updated dependencies [c3de9882]
|
|
37
|
+
- Updated dependencies [33ff48af]
|
|
38
|
+
- @modern-js/core@1.3.2
|
|
39
|
+
- @modern-js/utils@1.2.2
|
|
40
|
+
|
|
41
|
+
## 1.2.0
|
|
42
|
+
|
|
43
|
+
### Minor Changes
|
|
44
|
+
|
|
45
|
+
- cfe11628: Make Modern.js self bootstraping
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- Updated dependencies [2da09c69]
|
|
50
|
+
- Updated dependencies [fc71e36f]
|
|
51
|
+
- Updated dependencies [c3d46ee4]
|
|
52
|
+
- Updated dependencies [cfe11628]
|
|
53
|
+
- @modern-js/utils@1.2.0
|
|
54
|
+
- @modern-js/core@1.3.0
|
|
10
55
|
|
|
11
56
|
## 1.1.2
|
|
12
57
|
|
package/dist/js/modern/index.js
CHANGED
|
@@ -172,7 +172,7 @@ export default createPlugin(() => {
|
|
|
172
172
|
ssgRoute.isSSR = false;
|
|
173
173
|
ssgRoute.output = formatOutput(ssgRoute.output);
|
|
174
174
|
});
|
|
175
|
-
const htmlAry = await createServer(ssgRoutes, apiRoutes, resolvedConfig, appDirectory); // write to dist file
|
|
175
|
+
const htmlAry = await createServer(ssgRoutes, pageRoutes, apiRoutes, resolvedConfig, appDirectory); // write to dist file
|
|
176
176
|
|
|
177
177
|
writeHtmlFile(htmlAry, ssgRoutes, buildDir); // format route info, side effect
|
|
178
178
|
|
|
@@ -25,13 +25,13 @@ export function makeRoute(baseRoute, route, headers = {}) {
|
|
|
25
25
|
return _objectSpread(_objectSpread({}, baseRoute), {}, {
|
|
26
26
|
urlPath: normalize(`${urlPath}${route}`) || '/',
|
|
27
27
|
headers,
|
|
28
|
-
output: path.join(entryPath, `..${route}`)
|
|
28
|
+
output: path.join(entryPath, `..${route === '/' ? '' : route}`)
|
|
29
29
|
});
|
|
30
30
|
} else {
|
|
31
31
|
return _objectSpread(_objectSpread({}, baseRoute), {}, {
|
|
32
32
|
urlPath: normalize(`${urlPath}${route.url}`) || '/',
|
|
33
33
|
headers: _objectSpread(_objectSpread({}, headers), route.headers),
|
|
34
|
-
output: route.output ? path.normalize(route.output) : path.join(entryPath, `..${route.url}`)
|
|
34
|
+
output: route.output ? path.normalize(route.output) : path.join(entryPath, `..${route.url === '/' ? '' : route.url}`)
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
|
|
3
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
+
|
|
5
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
|
|
1
7
|
import path from 'path';
|
|
2
|
-
import { ROUTE_SPEC_FILE, fs, isSingleEntry } from '@modern-js/utils';
|
|
8
|
+
import { ROUTE_SPEC_FILE, fs, isSingleEntry, SERVER_BUNDLE_DIRECTORY } from '@modern-js/utils';
|
|
3
9
|
export function formatOutput(filename) {
|
|
4
10
|
const outputPath = path.extname(filename) ? filename : `${filename}/index.html`;
|
|
5
11
|
return outputPath;
|
|
@@ -124,4 +130,8 @@ export const standardOptions = (ssgOptions, entrypoints) => {
|
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
return false;
|
|
127
|
-
};
|
|
133
|
+
};
|
|
134
|
+
export const openRouteSSR = routes => routes.map(ssgRoute => _objectSpread(_objectSpread({}, ssgRoute), {}, {
|
|
135
|
+
isSSR: true,
|
|
136
|
+
bundle: `${SERVER_BUNDLE_DIRECTORY}/${ssgRoute.entryName}.js`
|
|
137
|
+
}));
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
-
|
|
3
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
-
|
|
5
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
|
|
7
1
|
import childProcess from 'child_process';
|
|
8
2
|
import path from 'path';
|
|
9
|
-
import { logger
|
|
3
|
+
import { logger } from '@modern-js/utils';
|
|
10
4
|
import { useAppContext } from '@modern-js/core';
|
|
5
|
+
import { openRouteSSR } from "../libs/util";
|
|
11
6
|
import { CLOSE_SIGN } from "./consts";
|
|
12
|
-
export const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new Promise((resolve, reject) => {
|
|
7
|
+
export const createServer = (ssgRoutes, pageRoutes, apiRoutes, options, appDirectory) => new Promise((resolve, reject) => {
|
|
13
8
|
// this side of the shallow copy of a route for subsequent render processing, to prevent the modification of the current field
|
|
14
9
|
// manually enable the server-side rendering configuration for all routes that require SSG
|
|
15
|
-
const backup =
|
|
16
|
-
isSSR: true,
|
|
17
|
-
bundle: `${SERVER_BUNDLE_DIRECTORY}/${ssgRoute.entryName}.js`
|
|
18
|
-
}));
|
|
10
|
+
const backup = openRouteSSR(pageRoutes);
|
|
19
11
|
const total = backup.concat(apiRoutes);
|
|
20
12
|
const cp = childProcess.fork(path.join(__dirname, 'process'), {
|
|
21
13
|
cwd: appDirectory,
|
|
@@ -26,6 +18,7 @@ export const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new
|
|
|
26
18
|
const plugins = serverPlugins.map(p => p.name);
|
|
27
19
|
cp.send(JSON.stringify({
|
|
28
20
|
options,
|
|
21
|
+
renderRoutes: ssgRoutes,
|
|
29
22
|
routes: total,
|
|
30
23
|
appDirectory,
|
|
31
24
|
plugins
|
|
@@ -41,7 +34,7 @@ export const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new
|
|
|
41
34
|
htmlChunks.length = 0;
|
|
42
35
|
}
|
|
43
36
|
|
|
44
|
-
if (htmlAry.length ===
|
|
37
|
+
if (htmlAry.length === ssgRoutes.length) {
|
|
45
38
|
cp.send(CLOSE_SIGN);
|
|
46
39
|
resolve(htmlAry);
|
|
47
40
|
}
|
|
@@ -26,6 +26,7 @@ process.on('message', async chunk => {
|
|
|
26
26
|
const context = JSON.parse(chunk);
|
|
27
27
|
const {
|
|
28
28
|
routes,
|
|
29
|
+
renderRoutes,
|
|
29
30
|
options,
|
|
30
31
|
appDirectory,
|
|
31
32
|
plugins
|
|
@@ -52,11 +53,15 @@ process.on('message', async chunk => {
|
|
|
52
53
|
modernServer.listen(port, async err => {
|
|
53
54
|
if (err) {
|
|
54
55
|
throw err;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!modernServer) {
|
|
59
|
+
return;
|
|
55
60
|
} // get server handler, render to ssr
|
|
56
61
|
|
|
57
62
|
|
|
58
63
|
const render = createRender(modernServer.getRequestHandler());
|
|
59
|
-
const renderPromiseAry = makeRender(
|
|
64
|
+
const renderPromiseAry = makeRender(renderRoutes, render, port); // eslint-disable-next-line promise/no-promise-in-callback
|
|
60
65
|
|
|
61
66
|
const htmlAry = await Promise.all(renderPromiseAry);
|
|
62
67
|
htmlAry.forEach(html => {
|
package/dist/js/node/index.js
CHANGED
|
@@ -191,7 +191,7 @@ var _default = (0, _core.createPlugin)(() => {
|
|
|
191
191
|
ssgRoute.isSSR = false;
|
|
192
192
|
ssgRoute.output = (0, _util.formatOutput)(ssgRoute.output);
|
|
193
193
|
});
|
|
194
|
-
const htmlAry = await (0, _server.createServer)(ssgRoutes, apiRoutes, resolvedConfig, appDirectory); // write to dist file
|
|
194
|
+
const htmlAry = await (0, _server.createServer)(ssgRoutes, pageRoutes, apiRoutes, resolvedConfig, appDirectory); // write to dist file
|
|
195
195
|
|
|
196
196
|
(0, _output.writeHtmlFile)(htmlAry, ssgRoutes, buildDir); // format route info, side effect
|
|
197
197
|
|
|
@@ -38,13 +38,13 @@ function makeRoute(baseRoute, route, headers = {}) {
|
|
|
38
38
|
return _objectSpread(_objectSpread({}, baseRoute), {}, {
|
|
39
39
|
urlPath: (0, _normalizePath.default)(`${urlPath}${route}`) || '/',
|
|
40
40
|
headers,
|
|
41
|
-
output: _path.default.join(entryPath, `..${route}`)
|
|
41
|
+
output: _path.default.join(entryPath, `..${route === '/' ? '' : route}`)
|
|
42
42
|
});
|
|
43
43
|
} else {
|
|
44
44
|
return _objectSpread(_objectSpread({}, baseRoute), {}, {
|
|
45
45
|
urlPath: (0, _normalizePath.default)(`${urlPath}${route.url}`) || '/',
|
|
46
46
|
headers: _objectSpread(_objectSpread({}, headers), route.headers),
|
|
47
|
-
output: route.output ? _path.default.normalize(route.output) : _path.default.join(entryPath, `..${route.url}`)
|
|
47
|
+
output: route.output ? _path.default.normalize(route.output) : _path.default.join(entryPath, `..${route.url === '/' ? '' : route.url}`)
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -8,7 +8,7 @@ exports.formatPath = formatPath;
|
|
|
8
8
|
exports.getOutput = getOutput;
|
|
9
9
|
exports.getUrlPrefix = getUrlPrefix;
|
|
10
10
|
exports.isDynamicUrl = isDynamicUrl;
|
|
11
|
-
exports.writeJSONSpec = exports.standardOptions = exports.replaceWithAlias = exports.readJSONSpec = void 0;
|
|
11
|
+
exports.writeJSONSpec = exports.standardOptions = exports.replaceWithAlias = exports.readJSONSpec = exports.openRouteSSR = void 0;
|
|
12
12
|
|
|
13
13
|
var _path = _interopRequireDefault(require("path"));
|
|
14
14
|
|
|
@@ -16,6 +16,12 @@ var _utils = require("@modern-js/utils");
|
|
|
16
16
|
|
|
17
17
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
18
|
|
|
19
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
20
|
+
|
|
21
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
22
|
+
|
|
23
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
24
|
+
|
|
19
25
|
function formatOutput(filename) {
|
|
20
26
|
const outputPath = _path.default.extname(filename) ? filename : `${filename}/index.html`;
|
|
21
27
|
return outputPath;
|
|
@@ -157,4 +163,11 @@ const standardOptions = (ssgOptions, entrypoints) => {
|
|
|
157
163
|
return false;
|
|
158
164
|
};
|
|
159
165
|
|
|
160
|
-
exports.standardOptions = standardOptions;
|
|
166
|
+
exports.standardOptions = standardOptions;
|
|
167
|
+
|
|
168
|
+
const openRouteSSR = routes => routes.map(ssgRoute => _objectSpread(_objectSpread({}, ssgRoute), {}, {
|
|
169
|
+
isSSR: true,
|
|
170
|
+
bundle: `${_utils.SERVER_BUNDLE_DIRECTORY}/${ssgRoute.entryName}.js`
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
exports.openRouteSSR = openRouteSSR;
|
|
@@ -13,23 +13,16 @@ var _utils = require("@modern-js/utils");
|
|
|
13
13
|
|
|
14
14
|
var _core = require("@modern-js/core");
|
|
15
15
|
|
|
16
|
+
var _util = require("../libs/util");
|
|
17
|
+
|
|
16
18
|
var _consts = require("./consts");
|
|
17
19
|
|
|
18
20
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
23
|
-
|
|
24
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
25
|
-
|
|
26
|
-
const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new Promise((resolve, reject) => {
|
|
22
|
+
const createServer = (ssgRoutes, pageRoutes, apiRoutes, options, appDirectory) => new Promise((resolve, reject) => {
|
|
27
23
|
// this side of the shallow copy of a route for subsequent render processing, to prevent the modification of the current field
|
|
28
24
|
// manually enable the server-side rendering configuration for all routes that require SSG
|
|
29
|
-
const backup =
|
|
30
|
-
isSSR: true,
|
|
31
|
-
bundle: `${_utils.SERVER_BUNDLE_DIRECTORY}/${ssgRoute.entryName}.js`
|
|
32
|
-
}));
|
|
25
|
+
const backup = (0, _util.openRouteSSR)(pageRoutes);
|
|
33
26
|
const total = backup.concat(apiRoutes);
|
|
34
27
|
|
|
35
28
|
const cp = _child_process.default.fork(_path.default.join(__dirname, 'process'), {
|
|
@@ -42,6 +35,7 @@ const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new Promis
|
|
|
42
35
|
const plugins = serverPlugins.map(p => p.name);
|
|
43
36
|
cp.send(JSON.stringify({
|
|
44
37
|
options,
|
|
38
|
+
renderRoutes: ssgRoutes,
|
|
45
39
|
routes: total,
|
|
46
40
|
appDirectory,
|
|
47
41
|
plugins
|
|
@@ -57,7 +51,7 @@ const createServer = (ssgRoutes, apiRoutes, options, appDirectory) => new Promis
|
|
|
57
51
|
htmlChunks.length = 0;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
if (htmlAry.length ===
|
|
54
|
+
if (htmlAry.length === ssgRoutes.length) {
|
|
61
55
|
cp.send(_consts.CLOSE_SIGN);
|
|
62
56
|
resolve(htmlAry);
|
|
63
57
|
}
|
|
@@ -35,6 +35,7 @@ process.on('message', async chunk => {
|
|
|
35
35
|
const context = JSON.parse(chunk);
|
|
36
36
|
const {
|
|
37
37
|
routes,
|
|
38
|
+
renderRoutes,
|
|
38
39
|
options,
|
|
39
40
|
appDirectory,
|
|
40
41
|
plugins
|
|
@@ -61,11 +62,15 @@ process.on('message', async chunk => {
|
|
|
61
62
|
modernServer.listen(port, async err => {
|
|
62
63
|
if (err) {
|
|
63
64
|
throw err;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!modernServer) {
|
|
68
|
+
return;
|
|
64
69
|
} // get server handler, render to ssr
|
|
65
70
|
|
|
66
71
|
|
|
67
72
|
const render = (0, _prerender.compile)(modernServer.getRequestHandler());
|
|
68
|
-
const renderPromiseAry = (0, _make.makeRender)(
|
|
73
|
+
const renderPromiseAry = (0, _make.makeRender)(renderRoutes, render, port); // eslint-disable-next-line promise/no-promise-in-callback
|
|
69
74
|
|
|
70
75
|
const htmlAry = await Promise.all(renderPromiseAry);
|
|
71
76
|
htmlAry.forEach(html => {
|
|
@@ -8,4 +8,14 @@ export declare function getOutput(route: SsgRoute, base: string, agreed?: boolea
|
|
|
8
8
|
export declare const readJSONSpec: (dir: string) => ModernRoute[];
|
|
9
9
|
export declare const writeJSONSpec: (dir: string, routes: ModernRoute[]) => void;
|
|
10
10
|
export declare const replaceWithAlias: (base: string, filePath: string, alias: string) => string;
|
|
11
|
-
export declare const standardOptions: (ssgOptions: SSG, entrypoints: EntryPoint[]) => false | MultiEntryOptions;
|
|
11
|
+
export declare const standardOptions: (ssgOptions: SSG, entrypoints: EntryPoint[]) => false | MultiEntryOptions;
|
|
12
|
+
export declare const openRouteSSR: (routes: ModernRoute[]) => {
|
|
13
|
+
isSSR: boolean;
|
|
14
|
+
bundle: string;
|
|
15
|
+
entryName?: string | undefined;
|
|
16
|
+
urlPath: string;
|
|
17
|
+
entryPath: string;
|
|
18
|
+
isSPA?: boolean | undefined;
|
|
19
|
+
isApi?: boolean | undefined;
|
|
20
|
+
enableModernMode?: boolean | undefined;
|
|
21
|
+
}[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { NormalizedConfig } from '@modern-js/core';
|
|
2
2
|
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
3
3
|
import { SsgRoute } from '../types';
|
|
4
|
-
export declare const createServer: (ssgRoutes: SsgRoute[], apiRoutes: ModernRoute[], options: NormalizedConfig, appDirectory: string) => Promise<string[]>;
|
|
4
|
+
export declare const createServer: (ssgRoutes: SsgRoute[], pageRoutes: ModernRoute[], apiRoutes: ModernRoute[], options: NormalizedConfig, appDirectory: string) => Promise<string[]>;
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"modern",
|
|
12
12
|
"modern.js"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.2.3",
|
|
15
15
|
"jsnext:source": "./src/index.ts",
|
|
16
16
|
"types": "./dist/types/index.d.ts",
|
|
17
17
|
"main": "./dist/js/node/index.js",
|
|
@@ -20,12 +20,14 @@
|
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
22
22
|
"node": {
|
|
23
|
+
"jsnext:source": "./src/index.ts",
|
|
23
24
|
"import": "./dist/js/modern/index.js",
|
|
24
25
|
"require": "./dist/js/node/index.js"
|
|
25
26
|
},
|
|
26
27
|
"default": "./dist/js/treeshaking/index.js"
|
|
27
28
|
},
|
|
28
29
|
"./cli": {
|
|
30
|
+
"jsnext:source": "./src/index.ts",
|
|
29
31
|
"node": {
|
|
30
32
|
"import": "./dist/js/modern/index.js",
|
|
31
33
|
"require": "./dist/js/node/index.js"
|
|
@@ -35,15 +37,15 @@
|
|
|
35
37
|
},
|
|
36
38
|
"dependencies": {
|
|
37
39
|
"@babel/runtime": "^7",
|
|
38
|
-
"@modern-js/utils": "^1.1
|
|
40
|
+
"@modern-js/utils": "^1.3.1",
|
|
39
41
|
"node-mocks-http": "^1.10.1",
|
|
40
42
|
"normalize-path": "^3.0.0",
|
|
41
43
|
"portfinder": "^1.0.28",
|
|
42
44
|
"react-router-dom": "^5.2.1"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
|
-
"@modern-js/server": "^1.
|
|
46
|
-
"@modern-js/types": "^1.1
|
|
47
|
+
"@modern-js/server": "^1.4.1",
|
|
48
|
+
"@modern-js/types": "^1.3.1",
|
|
47
49
|
"@types/jest": "^26",
|
|
48
50
|
"@types/node": "^14",
|
|
49
51
|
"@types/react": "^17",
|
|
@@ -51,12 +53,13 @@
|
|
|
51
53
|
"@types/react-router": "^5.1.16",
|
|
52
54
|
"@types/react-router-dom": "^5.1.8",
|
|
53
55
|
"typescript": "^4",
|
|
54
|
-
"@modern-js/core": "^1.
|
|
55
|
-
"@
|
|
56
|
-
"
|
|
56
|
+
"@modern-js/core": "^1.4.1",
|
|
57
|
+
"@scripts/build": "0.0.0",
|
|
58
|
+
"jest": "^27",
|
|
59
|
+
"@scripts/jest-config": "0.0.0"
|
|
57
60
|
},
|
|
58
61
|
"peerDependencies": {
|
|
59
|
-
"@modern-js/core": "^1.
|
|
62
|
+
"@modern-js/core": "^1.4.1"
|
|
60
63
|
},
|
|
61
64
|
"sideEffects": false,
|
|
62
65
|
"modernConfig": {
|
|
@@ -72,7 +75,7 @@
|
|
|
72
75
|
"new": "modern new",
|
|
73
76
|
"build": "modern build",
|
|
74
77
|
"dev": "modern build --watch",
|
|
75
|
-
"test": "
|
|
78
|
+
"test": "jest --passWithNoTests"
|
|
76
79
|
},
|
|
77
80
|
"readme": "\n<p align=\"center\">\n <a href=\"https://modernjs.dev\" target=\"blank\"><img src=\"https://lf3-static.bytednsdoc.com/obj/eden-cn/ylaelkeh7nuhfnuhf/modernjs-cover.png\" width=\"300\" alt=\"Modern.js Logo\" /></a>\n</p>\n<p align=\"center\">\n现代 Web 工程体系\n <br/>\n <a href=\"https://modernjs.dev\" target=\"blank\">\n modernjs.dev\n </a>\n</p>\n<p align=\"center\">\n The meta-framework suite designed from scratch for frontend-focused modern web development\n</p>\n\n# Introduction\n\n> The doc site ([modernjs.dev](https://modernjs.dev)) and articles are only available in Chinese for now, we are planning to add English versions soon.\n\n- [Modern.js: Hello, World!](https://zhuanlan.zhihu.com/p/426707646)\n\n## Getting Started\n\n- [Quick Start](https://modernjs.dev/docs/start)\n- [Guides](https://modernjs.dev/docs/guides)\n- [API References](https://modernjs.dev/docs/apis)\n\n## Contributing\n\n- [Contributing Guide](https://github.com/modern-js-dev/modern.js/blob/main/CONTRIBUTING.md)\n"
|
|
78
81
|
}
|
package/tests/lib.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
4
|
-
import { exist, replaceRoute } from '
|
|
5
|
-
import { makeRoute } from '
|
|
4
|
+
import { exist, replaceRoute } from '../src/libs/replace';
|
|
5
|
+
import { makeRoute } from '../src/libs/make';
|
|
6
6
|
|
|
7
7
|
describe('test functional function', () => {
|
|
8
8
|
it('should check route exist correctly', () => {
|
package/tests/tsconfig.json
CHANGED
package/tests/util.test.ts
CHANGED
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
getOutput,
|
|
7
7
|
replaceWithAlias,
|
|
8
8
|
standardOptions,
|
|
9
|
-
|
|
9
|
+
openRouteSSR,
|
|
10
|
+
} from '../src/libs/util';
|
|
10
11
|
|
|
11
12
|
describe('test ssg util function', () => {
|
|
12
13
|
it('should return format path correctly', () => {
|
|
@@ -73,6 +74,7 @@ describe('test ssg util function', () => {
|
|
|
73
74
|
expect(replaceWithAlias('/src', '/src/app.js', '@src')).toBe('@src/app.js');
|
|
74
75
|
});
|
|
75
76
|
|
|
77
|
+
// eslint-disable-next-line max-statements
|
|
76
78
|
it('should starndar user config correctly', () => {
|
|
77
79
|
const opt0 = standardOptions(false, []);
|
|
78
80
|
expect(opt0).toBeFalsy();
|
|
@@ -98,47 +100,58 @@ describe('test ssg util function', () => {
|
|
|
98
100
|
};
|
|
99
101
|
const opt4 = standardOptions(ssg1, [{ entryName: 'main', entry: '' }]);
|
|
100
102
|
expect(opt4).toEqual({ main: ssg1 });
|
|
103
|
+
|
|
104
|
+
// error usage, just test
|
|
105
|
+
const ssg2 = {
|
|
106
|
+
routes: ['/foo', { url: '/baz' }],
|
|
107
|
+
};
|
|
108
|
+
const opt5 = standardOptions(ssg2, [
|
|
109
|
+
{ entryName: 'main', entry: '' },
|
|
110
|
+
{ entryName: 'home', entry: '' },
|
|
111
|
+
]);
|
|
112
|
+
expect(opt5).toEqual(ssg2);
|
|
113
|
+
|
|
114
|
+
const ssg3 = {
|
|
115
|
+
main: { routes: ['/foo', { url: '/baz' }] },
|
|
116
|
+
home: false,
|
|
117
|
+
};
|
|
118
|
+
const opt6 = standardOptions(ssg3, [
|
|
119
|
+
{ entryName: 'main', entry: '' },
|
|
120
|
+
{ entryName: 'home', entry: '' },
|
|
121
|
+
]);
|
|
122
|
+
expect(opt6).toEqual(ssg3);
|
|
123
|
+
|
|
124
|
+
const ssg4 = () => true;
|
|
125
|
+
const opt7 = standardOptions(ssg4, [
|
|
126
|
+
{ entryName: 'main', entry: '' },
|
|
127
|
+
{ entryName: 'home', entry: '' },
|
|
128
|
+
]);
|
|
129
|
+
expect(opt7).toEqual({ main: true, home: true });
|
|
130
|
+
|
|
131
|
+
const ssg5 = (entryName: string) => {
|
|
132
|
+
if (entryName === 'main') {
|
|
133
|
+
return true;
|
|
134
|
+
} else {
|
|
135
|
+
return {
|
|
136
|
+
routes: ['/foo'],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const opt8 = standardOptions(ssg5, [
|
|
141
|
+
{ entryName: 'main', entry: '' },
|
|
142
|
+
{ entryName: 'home', entry: '' },
|
|
143
|
+
]);
|
|
144
|
+
expect(opt8).toEqual({ main: true, home: { routes: ['/foo'] } });
|
|
101
145
|
});
|
|
102
146
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const ssg3 = {
|
|
114
|
-
main: { routes: ['/foo', { url: '/baz' }] },
|
|
115
|
-
home: false,
|
|
116
|
-
};
|
|
117
|
-
const opt6 = standardOptions(ssg3, [
|
|
118
|
-
{ entryName: 'main', entry: '' },
|
|
119
|
-
{ entryName: 'home', entry: '' },
|
|
120
|
-
]);
|
|
121
|
-
expect(opt6).toEqual(ssg3);
|
|
122
|
-
|
|
123
|
-
const ssg4 = () => true;
|
|
124
|
-
const opt7 = standardOptions(ssg4, [
|
|
125
|
-
{ entryName: 'main', entry: '' },
|
|
126
|
-
{ entryName: 'home', entry: '' },
|
|
127
|
-
]);
|
|
128
|
-
expect(opt7).toEqual({ main: true, home: true });
|
|
129
|
-
|
|
130
|
-
const ssg5 = (entryName: string) => {
|
|
131
|
-
if (entryName === 'main') {
|
|
132
|
-
return true;
|
|
133
|
-
} else {
|
|
134
|
-
return {
|
|
135
|
-
routes: ['/foo'],
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
const opt8 = standardOptions(ssg5, [
|
|
140
|
-
{ entryName: 'main', entry: '' },
|
|
141
|
-
{ entryName: 'home', entry: '' },
|
|
142
|
-
]);
|
|
143
|
-
expect(opt8).toEqual({ main: true, home: { routes: ['/foo'] } });
|
|
147
|
+
it('should get ssr route correctly', () => {
|
|
148
|
+
const ssrRoutes = openRouteSSR([
|
|
149
|
+
{
|
|
150
|
+
isSSR: false,
|
|
151
|
+
},
|
|
152
|
+
] as any);
|
|
153
|
+
|
|
154
|
+
expect(ssrRoutes[0].isSSR).toBeTruthy();
|
|
155
|
+
expect(ssrRoutes[0].bundle).toBeDefined();
|
|
156
|
+
});
|
|
144
157
|
});
|
package/tsconfig.json
CHANGED
package/src/global.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare module 'normalize-path';
|
package/src/index.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { logger, PLUGIN_SCHEMAS } from '@modern-js/utils';
|
|
3
|
-
import {
|
|
4
|
-
createPlugin,
|
|
5
|
-
useAppContext,
|
|
6
|
-
useResolvedConfigContext,
|
|
7
|
-
} from '@modern-js/core';
|
|
8
|
-
import { generatePath } from 'react-router-dom';
|
|
9
|
-
import {
|
|
10
|
-
AgreedRoute,
|
|
11
|
-
AgreedRouteMap,
|
|
12
|
-
EntryPoint,
|
|
13
|
-
ExtendOutputConfig,
|
|
14
|
-
SSG,
|
|
15
|
-
SsgRoute,
|
|
16
|
-
} from './types';
|
|
17
|
-
import {
|
|
18
|
-
formatOutput,
|
|
19
|
-
isDynamicUrl,
|
|
20
|
-
readJSONSpec,
|
|
21
|
-
standardOptions,
|
|
22
|
-
writeJSONSpec,
|
|
23
|
-
} from './libs/util';
|
|
24
|
-
import { createServer } from './server';
|
|
25
|
-
import { writeHtmlFile } from './libs/output';
|
|
26
|
-
import { replaceRoute } from './libs/replace';
|
|
27
|
-
import { makeRoute } from './libs/make';
|
|
28
|
-
|
|
29
|
-
export default createPlugin(
|
|
30
|
-
(() => {
|
|
31
|
-
const agreedRouteMap: AgreedRouteMap = {};
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
validateSchema() {
|
|
35
|
-
return PLUGIN_SCHEMAS['@modern-js/plugin-ssg'];
|
|
36
|
-
},
|
|
37
|
-
modifyFileSystemRoutes({
|
|
38
|
-
entrypoint,
|
|
39
|
-
routes,
|
|
40
|
-
}: {
|
|
41
|
-
entrypoint: EntryPoint;
|
|
42
|
-
routes: AgreedRoute[];
|
|
43
|
-
}) {
|
|
44
|
-
const { entryName } = entrypoint;
|
|
45
|
-
agreedRouteMap[entryName] = routes;
|
|
46
|
-
|
|
47
|
-
return { entrypoint, routes };
|
|
48
|
-
},
|
|
49
|
-
// eslint-disable-next-line max-statements
|
|
50
|
-
async afterBuild() {
|
|
51
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
52
|
-
const resolvedConfig = useResolvedConfigContext();
|
|
53
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
54
|
-
const appContext = useAppContext();
|
|
55
|
-
|
|
56
|
-
const { appDirectory, entrypoints } = appContext;
|
|
57
|
-
const { output } = resolvedConfig;
|
|
58
|
-
const { ssg, path: outputPath } = output as typeof output &
|
|
59
|
-
ExtendOutputConfig;
|
|
60
|
-
|
|
61
|
-
const ssgOptions: SSG = Array.isArray(ssg) ? ssg.pop() : ssg;
|
|
62
|
-
// no ssg configuration, skip ssg render.
|
|
63
|
-
if (!ssgOptions) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const buildDir = path.join(appDirectory, outputPath as string);
|
|
68
|
-
const routes = readJSONSpec(buildDir);
|
|
69
|
-
|
|
70
|
-
// filter all routes not web
|
|
71
|
-
const pageRoutes = routes.filter(route => !route.isApi);
|
|
72
|
-
const apiRoutes = routes.filter(route => route.isApi);
|
|
73
|
-
|
|
74
|
-
// if no web page route, skip ssg render
|
|
75
|
-
if (pageRoutes.length === 0) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const intermediateOptions = standardOptions(ssgOptions, entrypoints);
|
|
80
|
-
|
|
81
|
-
if (!intermediateOptions) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const ssgRoutes: SsgRoute[] = [];
|
|
86
|
-
// each route will try to match the configuration
|
|
87
|
-
pageRoutes.forEach(pageRoute => {
|
|
88
|
-
const { entryName, entryPath } = pageRoute;
|
|
89
|
-
const agreedRoutes = agreedRouteMap[entryName as string];
|
|
90
|
-
let entryOptions = intermediateOptions[entryName as string];
|
|
91
|
-
|
|
92
|
-
if (!agreedRoutes) {
|
|
93
|
-
// default behavior for non-agreed route
|
|
94
|
-
if (!entryOptions) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// only add entry route if entryOptions is true
|
|
99
|
-
if (entryOptions === true) {
|
|
100
|
-
ssgRoutes.push({ ...pageRoute, output: entryPath });
|
|
101
|
-
} else if (entryOptions.routes?.length > 0) {
|
|
102
|
-
// if entryOptions is object and has routes options
|
|
103
|
-
// add every route in options
|
|
104
|
-
const { routes: enrtyRoutes, headers } = entryOptions;
|
|
105
|
-
enrtyRoutes.forEach(route => {
|
|
106
|
-
ssgRoutes.push(makeRoute(pageRoute, route, headers));
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
// Unless entryOptions is set to false
|
|
111
|
-
// the default behavior is to add all file-based routes
|
|
112
|
-
if (entryOptions === false) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!entryOptions || entryOptions === true) {
|
|
117
|
-
entryOptions = { preventDefault: [], routes: [], headers: {} };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const {
|
|
121
|
-
preventDefault = [],
|
|
122
|
-
routes: userRoutes = [],
|
|
123
|
-
headers,
|
|
124
|
-
} = entryOptions;
|
|
125
|
-
// if the user sets the routes, then only add them
|
|
126
|
-
if (userRoutes.length > 0) {
|
|
127
|
-
userRoutes.forEach(route => {
|
|
128
|
-
if (typeof route === 'string') {
|
|
129
|
-
ssgRoutes.push(makeRoute(pageRoute, route, headers));
|
|
130
|
-
} else if (Array.isArray(route.params)) {
|
|
131
|
-
route.params.forEach(param => {
|
|
132
|
-
ssgRoutes.push(
|
|
133
|
-
makeRoute(
|
|
134
|
-
pageRoute,
|
|
135
|
-
{ ...route, url: generatePath(route.url, param) },
|
|
136
|
-
headers,
|
|
137
|
-
),
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
} else {
|
|
141
|
-
ssgRoutes.push(makeRoute(pageRoute, route, headers));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
} else {
|
|
145
|
-
// otherwith add all except dynamic routes
|
|
146
|
-
agreedRoutes
|
|
147
|
-
.filter(route => !preventDefault.includes(route.path))
|
|
148
|
-
.forEach(route => {
|
|
149
|
-
if (!isDynamicUrl(route.path)) {
|
|
150
|
-
ssgRoutes.push(makeRoute(pageRoute, route.path, headers));
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
if (ssgRoutes.length === 0) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// currently SSG and SSR cannot be turned on at the same time、same route
|
|
162
|
-
ssgRoutes.forEach((ssgRoute: SsgRoute) => {
|
|
163
|
-
if (ssgRoute.isSSR) {
|
|
164
|
-
const isOriginRoute = pageRoutes.some(
|
|
165
|
-
pageRoute =>
|
|
166
|
-
pageRoute.urlPath === ssgRoute.urlPath &&
|
|
167
|
-
pageRoute.entryName === ssgRoute.entryName,
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
if (isOriginRoute) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`ssg can not using with ssr,url - ${
|
|
173
|
-
ssgRoute.urlPath
|
|
174
|
-
}, entry - ${ssgRoute.entryName!} `,
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
logger.warn(
|
|
179
|
-
`new ssg route ${
|
|
180
|
-
ssgRoute.urlPath
|
|
181
|
-
} is using ssr now,maybe from parent route ${ssgRoute.entryName!},close ssr`,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
ssgRoute.isSSR = false;
|
|
185
|
-
ssgRoute.output = formatOutput(ssgRoute.output);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const htmlAry = await createServer(
|
|
189
|
-
ssgRoutes,
|
|
190
|
-
apiRoutes,
|
|
191
|
-
resolvedConfig,
|
|
192
|
-
appDirectory,
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// write to dist file
|
|
196
|
-
writeHtmlFile(htmlAry, ssgRoutes, buildDir);
|
|
197
|
-
|
|
198
|
-
// format route info, side effect
|
|
199
|
-
replaceRoute(ssgRoutes, pageRoutes);
|
|
200
|
-
|
|
201
|
-
// write routes to spec file
|
|
202
|
-
writeJSONSpec(buildDir, pageRoutes.concat(apiRoutes));
|
|
203
|
-
|
|
204
|
-
logger.info('ssg Compiled successfully');
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
}) as any,
|
|
208
|
-
{ name: '@modern-js/plugin-ssg' },
|
|
209
|
-
) as any;
|
package/src/libs/make.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
3
|
-
import normalize from 'normalize-path';
|
|
4
|
-
import { compile } from '../server/prerender';
|
|
5
|
-
import { RouteOptions, SsgRoute } from '../types';
|
|
6
|
-
|
|
7
|
-
export function makeRender(
|
|
8
|
-
ssgRoutes: SsgRoute[],
|
|
9
|
-
render: ReturnType<typeof compile>,
|
|
10
|
-
port: number,
|
|
11
|
-
): Promise<string>[] {
|
|
12
|
-
return ssgRoutes.map((ssgRoute: SsgRoute) =>
|
|
13
|
-
render({
|
|
14
|
-
url: ssgRoute.urlPath,
|
|
15
|
-
headers: { host: `localhost:${port}`, ...ssgRoute.headers },
|
|
16
|
-
connection: {},
|
|
17
|
-
}),
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function makeRoute(
|
|
22
|
-
baseRoute: ModernRoute,
|
|
23
|
-
route: string | RouteOptions,
|
|
24
|
-
headers: Record<string, any> = {},
|
|
25
|
-
): SsgRoute {
|
|
26
|
-
const { urlPath, entryPath } = baseRoute;
|
|
27
|
-
|
|
28
|
-
if (typeof route === 'string') {
|
|
29
|
-
return {
|
|
30
|
-
...baseRoute,
|
|
31
|
-
urlPath: normalize(`${urlPath}${route}`) || '/',
|
|
32
|
-
headers,
|
|
33
|
-
output: path.join(entryPath, `..${route}`),
|
|
34
|
-
};
|
|
35
|
-
} else {
|
|
36
|
-
return {
|
|
37
|
-
...baseRoute,
|
|
38
|
-
urlPath: normalize(`${urlPath}${route.url}`) || '/',
|
|
39
|
-
headers: { ...headers, ...route.headers },
|
|
40
|
-
output: route.output
|
|
41
|
-
? path.normalize(route.output)
|
|
42
|
-
: path.join(entryPath, `..${route.url}`),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
package/src/libs/output.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { fs } from '@modern-js/utils';
|
|
3
|
-
import { SsgRoute } from '../types';
|
|
4
|
-
|
|
5
|
-
export function writeHtmlFile(
|
|
6
|
-
htmlAry: string[],
|
|
7
|
-
ssgRoutes: SsgRoute[],
|
|
8
|
-
baseDir: string,
|
|
9
|
-
) {
|
|
10
|
-
htmlAry.forEach((html: any, index: number) => {
|
|
11
|
-
const ssgRoute = ssgRoutes[index];
|
|
12
|
-
const filepath = path.join(baseDir, ssgRoute.output);
|
|
13
|
-
if (!fs.existsSync(path.dirname(filepath))) {
|
|
14
|
-
fs.ensureDirSync(path.dirname(filepath));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fs.writeFileSync(filepath, html);
|
|
18
|
-
});
|
|
19
|
-
}
|
package/src/libs/replace.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import normalize from 'normalize-path';
|
|
2
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
3
|
-
import { SsgRoute } from '../types';
|
|
4
|
-
|
|
5
|
-
export function exist(route: ModernRoute, pageRoutes: ModernRoute[]): number {
|
|
6
|
-
return pageRoutes.slice().findIndex(pageRoute => {
|
|
7
|
-
const urlEqual = normalize(pageRoute.urlPath) === normalize(route.urlPath);
|
|
8
|
-
const entryEqual = pageRoute.entryName === route.entryName;
|
|
9
|
-
if (urlEqual && entryEqual) {
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
return false;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function replaceRoute(ssgRoutes: SsgRoute[], pageRoutes: ModernRoute[]) {
|
|
17
|
-
// remove redundant fields and replace rendered entryPath
|
|
18
|
-
const cleanSsgRoutes = ssgRoutes.map(ssgRoute => {
|
|
19
|
-
const { output, headers, ...cleanSsgRoute } = ssgRoute;
|
|
20
|
-
return Object.assign(
|
|
21
|
-
cleanSsgRoute,
|
|
22
|
-
output ? { entryPath: output } : {},
|
|
23
|
-
) as ModernRoute;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// all routes that need to be added and replaced
|
|
27
|
-
const freshRoutes: ModernRoute[] = [];
|
|
28
|
-
cleanSsgRoutes.forEach(ssgRoute => {
|
|
29
|
-
const index = exist(ssgRoute, pageRoutes);
|
|
30
|
-
|
|
31
|
-
if (index < 0) {
|
|
32
|
-
// new route
|
|
33
|
-
freshRoutes.push({ ...ssgRoute });
|
|
34
|
-
} else {
|
|
35
|
-
// overwrite original entry
|
|
36
|
-
pageRoutes[index].entryPath = ssgRoute.entryPath;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
pageRoutes.push(...freshRoutes);
|
|
41
|
-
return pageRoutes;
|
|
42
|
-
}
|
package/src/libs/util.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { ROUTE_SPEC_FILE, fs, isSingleEntry } from '@modern-js/utils';
|
|
3
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
4
|
-
import { EntryPoint, MultiEntryOptions, SSG, SsgRoute } from '../types';
|
|
5
|
-
|
|
6
|
-
export function formatOutput(filename: string) {
|
|
7
|
-
const outputPath = path.extname(filename)
|
|
8
|
-
? filename
|
|
9
|
-
: `${filename}/index.html`;
|
|
10
|
-
return outputPath;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function formatPath(str: string) {
|
|
14
|
-
let addr = str;
|
|
15
|
-
if (!addr || typeof addr !== 'string') {
|
|
16
|
-
return addr;
|
|
17
|
-
}
|
|
18
|
-
if (addr.startsWith('.')) {
|
|
19
|
-
addr = addr.slice(1);
|
|
20
|
-
}
|
|
21
|
-
if (!addr.startsWith('/')) {
|
|
22
|
-
addr = `/${addr}`;
|
|
23
|
-
}
|
|
24
|
-
if (addr.endsWith('/') && addr !== '/') {
|
|
25
|
-
addr = addr.slice(0, addr.length - 1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return addr;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isDynamicUrl(url: string): boolean {
|
|
32
|
-
return url.includes(':');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getUrlPrefix(route: SsgRoute, baseUrl: string | string[]) {
|
|
36
|
-
let base = '';
|
|
37
|
-
if (Array.isArray(baseUrl)) {
|
|
38
|
-
const filters = baseUrl.filter(url => route.urlPath.includes(url));
|
|
39
|
-
if (filters.length > 1) {
|
|
40
|
-
const matched = filters.sort((a, b) => a.length - b.length)[0];
|
|
41
|
-
|
|
42
|
-
// this should never happend
|
|
43
|
-
if (!matched) {
|
|
44
|
-
throw new Error('');
|
|
45
|
-
}
|
|
46
|
-
base = matched;
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
base = baseUrl;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
base = base === '/' ? '' : base;
|
|
53
|
-
const entryName = route.entryName === 'main' ? '' : route.entryName;
|
|
54
|
-
const prefix = `${base}/${entryName as string}`;
|
|
55
|
-
return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// if no output, return default path for aggred-route(relative),
|
|
59
|
-
// or thorw error for control-route
|
|
60
|
-
export function getOutput(route: SsgRoute, base: string, agreed?: boolean) {
|
|
61
|
-
const { output } = route;
|
|
62
|
-
if (output) {
|
|
63
|
-
return output;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (agreed) {
|
|
67
|
-
const urlWithoutBase = route.urlPath.replace(base, '');
|
|
68
|
-
return urlWithoutBase.startsWith('/')
|
|
69
|
-
? urlWithoutBase.slice(1)
|
|
70
|
-
: urlWithoutBase;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
throw new Error(
|
|
74
|
-
`routing must provide output when calling createPage(), check ${route.urlPath}`,
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export const readJSONSpec = (dir: string) => {
|
|
79
|
-
const routeJSONPath = path.join(dir, ROUTE_SPEC_FILE);
|
|
80
|
-
const routeJSON: {
|
|
81
|
-
routes: ModernRoute[];
|
|
82
|
-
} = require(routeJSONPath);
|
|
83
|
-
const { routes } = routeJSON;
|
|
84
|
-
return routes;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export const writeJSONSpec = (dir: string, routes: ModernRoute[]) => {
|
|
88
|
-
const routeJSONPath = path.join(dir, ROUTE_SPEC_FILE);
|
|
89
|
-
fs.writeJSONSync(routeJSONPath, { routes }, { spaces: 2 });
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export const replaceWithAlias = (
|
|
93
|
-
base: string,
|
|
94
|
-
filePath: string,
|
|
95
|
-
alias: string,
|
|
96
|
-
) => path.posix.join(alias, path.posix.relative(base, filePath));
|
|
97
|
-
|
|
98
|
-
export const standardOptions = (ssgOptions: SSG, entrypoints: EntryPoint[]) => {
|
|
99
|
-
if (ssgOptions === false) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (ssgOptions === true) {
|
|
104
|
-
return entrypoints.reduce((opt, entry) => {
|
|
105
|
-
opt[entry.entryName] = ssgOptions;
|
|
106
|
-
return opt;
|
|
107
|
-
}, {} as MultiEntryOptions);
|
|
108
|
-
} else if (typeof ssgOptions === 'object') {
|
|
109
|
-
const isSingle = isSingleEntry(entrypoints);
|
|
110
|
-
|
|
111
|
-
if (isSingle && typeof (ssgOptions as any).main === 'undefined') {
|
|
112
|
-
return { main: ssgOptions } as MultiEntryOptions;
|
|
113
|
-
} else {
|
|
114
|
-
return ssgOptions as MultiEntryOptions;
|
|
115
|
-
}
|
|
116
|
-
} else if (typeof ssgOptions === 'function') {
|
|
117
|
-
const intermediateOptions: MultiEntryOptions = {};
|
|
118
|
-
for (const entrypoint of entrypoints) {
|
|
119
|
-
const { entryName } = entrypoint;
|
|
120
|
-
// Todo may be async function
|
|
121
|
-
intermediateOptions[entryName] = ssgOptions(entryName);
|
|
122
|
-
}
|
|
123
|
-
return intermediateOptions;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return false;
|
|
127
|
-
};
|
package/src/server/consts.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const CLOSE_SIGN = 'modern_close_server';
|
package/src/server/index.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import childProcess from 'child_process';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { logger, SERVER_BUNDLE_DIRECTORY } from '@modern-js/utils';
|
|
4
|
-
import { NormalizedConfig, useAppContext } from '@modern-js/core';
|
|
5
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
6
|
-
import { SsgRoute } from '../types';
|
|
7
|
-
import { CLOSE_SIGN } from './consts';
|
|
8
|
-
|
|
9
|
-
export const createServer = (
|
|
10
|
-
ssgRoutes: SsgRoute[],
|
|
11
|
-
apiRoutes: ModernRoute[],
|
|
12
|
-
options: NormalizedConfig,
|
|
13
|
-
appDirectory: string,
|
|
14
|
-
): Promise<string[]> =>
|
|
15
|
-
new Promise((resolve, reject) => {
|
|
16
|
-
// this side of the shallow copy of a route for subsequent render processing, to prevent the modification of the current field
|
|
17
|
-
// manually enable the server-side rendering configuration for all routes that require SSG
|
|
18
|
-
const backup: ModernRoute[] = ssgRoutes.map(ssgRoute => ({
|
|
19
|
-
...ssgRoute,
|
|
20
|
-
isSSR: true,
|
|
21
|
-
bundle: `${SERVER_BUNDLE_DIRECTORY}/${ssgRoute.entryName as string}.js`,
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
const total = backup.concat(apiRoutes);
|
|
25
|
-
|
|
26
|
-
const cp = childProcess.fork(path.join(__dirname, 'process'), {
|
|
27
|
-
cwd: appDirectory,
|
|
28
|
-
silent: true,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const appContext = useAppContext();
|
|
32
|
-
const serverPlugins = appContext.plugins
|
|
33
|
-
.filter((p: any) => p.server)
|
|
34
|
-
.map((p: any) => p.server);
|
|
35
|
-
const plugins = serverPlugins.map((p: any) => p.name);
|
|
36
|
-
|
|
37
|
-
cp.send(
|
|
38
|
-
JSON.stringify({
|
|
39
|
-
options,
|
|
40
|
-
routes: total,
|
|
41
|
-
appDirectory,
|
|
42
|
-
plugins,
|
|
43
|
-
}),
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const htmlChunks: string[] = [];
|
|
47
|
-
const htmlAry: string[] = [];
|
|
48
|
-
|
|
49
|
-
cp.on('message', (chunk: string) => {
|
|
50
|
-
if (chunk !== null) {
|
|
51
|
-
htmlChunks.push(chunk);
|
|
52
|
-
} else {
|
|
53
|
-
const html = htmlChunks.join('');
|
|
54
|
-
htmlAry.push(html);
|
|
55
|
-
htmlChunks.length = 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (htmlAry.length === backup.length) {
|
|
59
|
-
cp.send(CLOSE_SIGN);
|
|
60
|
-
resolve(htmlAry);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
cp.stderr!.on('data', chunk => {
|
|
65
|
-
const str = chunk.toString();
|
|
66
|
-
if (str.includes('Error')) {
|
|
67
|
-
logger.error(str);
|
|
68
|
-
reject(new Error('ssg render failed'));
|
|
69
|
-
cp.kill('SIGKILL');
|
|
70
|
-
} else {
|
|
71
|
-
logger.info(str.replace(/[^\S\n]+/g, ' '));
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
cp.stdout!.on('data', chunk => {
|
|
76
|
-
const str = chunk.toString();
|
|
77
|
-
if (str.includes('Error')) {
|
|
78
|
-
logger.error(str);
|
|
79
|
-
reject(new Error('ssg render failed'));
|
|
80
|
-
cp.kill('SIGKILL');
|
|
81
|
-
} else {
|
|
82
|
-
logger.info(str.replace(/[^\S\n]+/g, ' '));
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
});
|
package/src/server/prerender.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import EventEmitter from 'events';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { Readable } from 'stream';
|
|
4
|
-
import httpMocks from 'node-mocks-http';
|
|
5
|
-
|
|
6
|
-
export type Options = {
|
|
7
|
-
url: string;
|
|
8
|
-
headers: {
|
|
9
|
-
host: string;
|
|
10
|
-
[key: string]: string;
|
|
11
|
-
};
|
|
12
|
-
[propName: string]: any;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const compile =
|
|
16
|
-
(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) =>
|
|
17
|
-
(options: Options, extend = {}): Promise<string> =>
|
|
18
|
-
new Promise((resolve, reject) => {
|
|
19
|
-
const req = httpMocks.createRequest({
|
|
20
|
-
...options,
|
|
21
|
-
eventEmitter: Readable,
|
|
22
|
-
});
|
|
23
|
-
const res = httpMocks.createResponse({ eventEmitter: EventEmitter });
|
|
24
|
-
|
|
25
|
-
Object.assign(req, extend);
|
|
26
|
-
const proxyRes = new Proxy(res, {
|
|
27
|
-
get(obj: any, prop: any) {
|
|
28
|
-
if (typeof prop === 'symbol' && !obj[prop]) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
return obj[prop];
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
res.on('finish', () => {
|
|
36
|
-
if (res.statusCode !== 200) {
|
|
37
|
-
reject(new Error(res.statusMessage));
|
|
38
|
-
} else {
|
|
39
|
-
resolve(res._getData());
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
res.on('error', (e: Error) => reject(e));
|
|
44
|
-
try {
|
|
45
|
-
requestHandler(req, proxyRes);
|
|
46
|
-
} catch (e) {
|
|
47
|
-
reject(e);
|
|
48
|
-
}
|
|
49
|
-
});
|
package/src/server/process.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import Server from '@modern-js/server';
|
|
2
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
3
|
-
import portfinder from 'portfinder';
|
|
4
|
-
import { NormalizedConfig } from '@modern-js/core';
|
|
5
|
-
import { compatRequire } from '@modern-js/utils';
|
|
6
|
-
import { makeRender } from '../libs/make';
|
|
7
|
-
import { compile as createRender } from './prerender';
|
|
8
|
-
import { CLOSE_SIGN } from './consts';
|
|
9
|
-
import { SsgRoute } from '@/types';
|
|
10
|
-
|
|
11
|
-
type Then<T> = T extends PromiseLike<infer U> ? U : T;
|
|
12
|
-
|
|
13
|
-
type ModernServer = Then<ReturnType<typeof Server>>;
|
|
14
|
-
|
|
15
|
-
const safetyRequire = (filename: string, base: string) => {
|
|
16
|
-
try {
|
|
17
|
-
return compatRequire(
|
|
18
|
-
require.resolve(`${filename}/server`, { paths: [base] }),
|
|
19
|
-
);
|
|
20
|
-
} catch (e) {
|
|
21
|
-
return compatRequire(require.resolve(filename, { paths: [base] }));
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
process.on('message', async (chunk: string) => {
|
|
26
|
-
if (chunk === CLOSE_SIGN) {
|
|
27
|
-
// eslint-disable-next-line no-process-exit
|
|
28
|
-
process.exit();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const context = JSON.parse(chunk as any);
|
|
32
|
-
const {
|
|
33
|
-
routes,
|
|
34
|
-
options,
|
|
35
|
-
appDirectory,
|
|
36
|
-
plugins,
|
|
37
|
-
}: {
|
|
38
|
-
routes: ModernRoute[];
|
|
39
|
-
options: NormalizedConfig;
|
|
40
|
-
appDirectory: string;
|
|
41
|
-
plugins: string[];
|
|
42
|
-
} = context;
|
|
43
|
-
|
|
44
|
-
const instances = plugins.map(plugin => safetyRequire(plugin, appDirectory));
|
|
45
|
-
|
|
46
|
-
let modernServer: ModernServer | null = null;
|
|
47
|
-
try {
|
|
48
|
-
const { server } = options;
|
|
49
|
-
|
|
50
|
-
// start server in default port
|
|
51
|
-
const defaultPort = Number(process.env.PORT) || server.port;
|
|
52
|
-
portfinder.basePort = defaultPort!;
|
|
53
|
-
const port = await portfinder.getPortPromise();
|
|
54
|
-
|
|
55
|
-
modernServer = await Server({
|
|
56
|
-
pwd: appDirectory,
|
|
57
|
-
config: options,
|
|
58
|
-
routes,
|
|
59
|
-
staticGenerate: true,
|
|
60
|
-
plugins: instances,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// listen just for bff request in ssr page
|
|
64
|
-
modernServer.listen(port, async (err: Error) => {
|
|
65
|
-
if (err) {
|
|
66
|
-
throw err;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// get server handler, render to ssr
|
|
70
|
-
const render = createRender(modernServer!.getRequestHandler());
|
|
71
|
-
const renderPromiseAry = makeRender(
|
|
72
|
-
routes.filter(route => !route.isApi) as SsgRoute[],
|
|
73
|
-
render,
|
|
74
|
-
port,
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
// eslint-disable-next-line promise/no-promise-in-callback
|
|
78
|
-
const htmlAry = await Promise.all(renderPromiseAry);
|
|
79
|
-
htmlAry.forEach((html: string) => {
|
|
80
|
-
process.send!(html);
|
|
81
|
-
process.send!(null);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
modernServer!.close();
|
|
85
|
-
});
|
|
86
|
-
} catch (e) {
|
|
87
|
-
modernServer?.close();
|
|
88
|
-
throw e;
|
|
89
|
-
}
|
|
90
|
-
});
|
package/src/types.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { ServerRoute as ModernRoute } from '@modern-js/types';
|
|
2
|
-
|
|
3
|
-
export type AgreedRoute = {
|
|
4
|
-
path: string;
|
|
5
|
-
component: string;
|
|
6
|
-
_component: string;
|
|
7
|
-
exact: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type EntryPoint = {
|
|
11
|
-
entryName: string;
|
|
12
|
-
entry: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type AgreedRouteMap = {
|
|
16
|
-
[propNames: string]: AgreedRoute[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type SsgRoute = ModernRoute & {
|
|
20
|
-
output: string;
|
|
21
|
-
headers?: Record<string, string>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type RouteOptions =
|
|
25
|
-
| string
|
|
26
|
-
| {
|
|
27
|
-
url: string;
|
|
28
|
-
output?: string;
|
|
29
|
-
params?: Record<string, any>[];
|
|
30
|
-
headers?: Record<string, any>;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type SingleEntryOptions =
|
|
34
|
-
| boolean
|
|
35
|
-
| {
|
|
36
|
-
preventDefault?: string[];
|
|
37
|
-
headers?: Record<string, any>;
|
|
38
|
-
routes: RouteOptions[];
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type MultiEntryOptions = Record<string, SingleEntryOptions>;
|
|
42
|
-
|
|
43
|
-
export type SSG =
|
|
44
|
-
| boolean
|
|
45
|
-
| SingleEntryOptions
|
|
46
|
-
| MultiEntryOptions
|
|
47
|
-
| ((entryName: string) => SingleEntryOptions);
|
|
48
|
-
|
|
49
|
-
export type ExtendOutputConfig = {
|
|
50
|
-
ssg: SSG;
|
|
51
|
-
};
|