@modern-js/plugin-ssg 1.1.2 → 1.2.2

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 CHANGED
@@ -1,5 +1,46 @@
1
1
  # @modern-js/plugin-ssg
2
2
 
3
+ ## 1.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 5a7901d7: fix ssg url
8
+ - Updated dependencies [d9cc5ea9]
9
+ - Updated dependencies [bd819a8d]
10
+ - Updated dependencies [ec4dbffb]
11
+ - Updated dependencies [d099e5c5]
12
+ - Updated dependencies [bada2879]
13
+ - Updated dependencies [24f616ca]
14
+ - Updated dependencies [bd819a8d]
15
+ - @modern-js/core@1.4.0
16
+ - @modern-js/utils@1.3.0
17
+
18
+ ## 1.2.1
19
+
20
+ ### Patch Changes
21
+
22
+ - 83166714: change .npmignore
23
+ - Updated dependencies [83166714]
24
+ - Updated dependencies [c3de9882]
25
+ - Updated dependencies [33ff48af]
26
+ - @modern-js/core@1.3.2
27
+ - @modern-js/utils@1.2.2
28
+
29
+ ## 1.2.0
30
+
31
+ ### Minor Changes
32
+
33
+ - cfe11628: Make Modern.js self bootstraping
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [2da09c69]
38
+ - Updated dependencies [fc71e36f]
39
+ - Updated dependencies [c3d46ee4]
40
+ - Updated dependencies [cfe11628]
41
+ - @modern-js/utils@1.2.0
42
+ - @modern-js/core@1.3.0
43
+
3
44
  ## 1.1.2
4
45
 
5
46
  ### Patch Changes
@@ -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
  }
@@ -52,6 +52,10 @@ process.on('message', async chunk => {
52
52
  modernServer.listen(port, async err => {
53
53
  if (err) {
54
54
  throw err;
55
+ }
56
+
57
+ if (!modernServer) {
58
+ return;
55
59
  } // get server handler, render to ssr
56
60
 
57
61
 
@@ -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
  }
@@ -61,6 +61,10 @@ process.on('message', async chunk => {
61
61
  modernServer.listen(port, async err => {
62
62
  if (err) {
63
63
  throw err;
64
+ }
65
+
66
+ if (!modernServer) {
67
+ return;
64
68
  } // get server handler, render to ssr
65
69
 
66
70
 
@@ -1,4 +1,4 @@
1
- import { ModernRoute } from '@modern-js/server';
1
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
2
2
  import { compile } from '../server/prerender';
3
3
  import { RouteOptions, SsgRoute } from '../types';
4
4
  export declare function makeRender(ssgRoutes: SsgRoute[], render: ReturnType<typeof compile>, port: number): Promise<string>[];
@@ -1,4 +1,4 @@
1
- import { ModernRoute } from '@modern-js/server';
1
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
2
2
  import { SsgRoute } from '../types';
3
3
  export declare function exist(route: ModernRoute, pageRoutes: ModernRoute[]): number;
4
4
  export declare function replaceRoute(ssgRoutes: SsgRoute[], pageRoutes: ModernRoute[]): ModernRoute[];
@@ -1,4 +1,4 @@
1
- import { ModernRoute } from '@modern-js/server';
1
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
2
2
  import { EntryPoint, MultiEntryOptions, SSG, SsgRoute } from '../types';
3
3
  export declare function formatOutput(filename: string): string;
4
4
  export declare function formatPath(str: string): string;
@@ -1,4 +1,4 @@
1
1
  import { NormalizedConfig } from '@modern-js/core';
2
- import { ModernRoute } from '@modern-js/server';
2
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
3
3
  import { SsgRoute } from '../types';
4
4
  export declare const createServer: (ssgRoutes: SsgRoute[], apiRoutes: ModernRoute[], options: NormalizedConfig, appDirectory: string) => Promise<string[]>;
@@ -1,4 +1,4 @@
1
- import { ModernRoute } from '@modern-js/server';
1
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
2
2
  export declare type AgreedRoute = {
3
3
  path: string;
4
4
  component: string;
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ const sharedConfig = require('@scripts/jest-config');
2
+
3
+ /** @type {import('@jest/types').Config.InitialOptions} */
4
+ module.exports = {
5
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
6
+ ...sharedConfig,
7
+ rootDir: __dirname,
8
+ };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "modern",
12
12
  "modern.js"
13
13
  ],
14
- "version": "1.1.2",
14
+ "version": "1.2.2",
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,14 +37,15 @@
35
37
  },
36
38
  "dependencies": {
37
39
  "@babel/runtime": "^7",
38
- "@modern-js/utils": "^1.1.4",
40
+ "@modern-js/utils": "^1.3.0",
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.1.4",
47
+ "@modern-js/server": "^1.4.0",
48
+ "@modern-js/types": "^1.3.0",
46
49
  "@types/jest": "^26",
47
50
  "@types/node": "^14",
48
51
  "@types/react": "^17",
@@ -50,12 +53,13 @@
50
53
  "@types/react-router": "^5.1.16",
51
54
  "@types/react-router-dom": "^5.1.8",
52
55
  "typescript": "^4",
53
- "@modern-js/core": "^1.1.4",
54
- "@modern-js/plugin-testing": "^1.1.1",
55
- "@modern-js/module-tools": "^1.1.1"
56
+ "@modern-js/core": "^1.4.0",
57
+ "@scripts/build": "0.0.0",
58
+ "jest": "^27",
59
+ "@scripts/jest-config": "0.0.0"
56
60
  },
57
61
  "peerDependencies": {
58
- "@modern-js/core": "^1.1.4"
62
+ "@modern-js/core": "^1.4.0"
59
63
  },
60
64
  "sideEffects": false,
61
65
  "modernConfig": {
@@ -65,13 +69,14 @@
65
69
  },
66
70
  "publishConfig": {
67
71
  "registry": "https://registry.npmjs.org/",
68
- "access": "public"
72
+ "access": "public",
73
+ "types": "./dist/types/index.d.ts"
69
74
  },
70
75
  "scripts": {
71
76
  "new": "modern new",
72
77
  "build": "modern build",
73
78
  "dev": "modern build --watch",
74
- "test": "modern test --passWithNoTests"
79
+ "test": "jest --passWithNoTests"
75
80
  },
76
81
  "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"
77
82
  }
@@ -0,0 +1,7 @@
1
+ import plugin from '../src';
2
+
3
+ describe('plugin-ssg', () => {
4
+ it('default', () => {
5
+ expect(plugin).toBeDefined();
6
+ });
7
+ });
package/tests/lib.test.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { ModernRoute } from '@modern-js/server';
4
- import { exist, replaceRoute } from '@/libs/replace';
5
- import { makeRoute } from '@/libs/make';
3
+ import { ServerRoute as ModernRoute } from '@modern-js/types';
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', () => {
@@ -4,9 +4,7 @@
4
4
  "declaration": false,
5
5
  "jsx": "preserve",
6
6
  "baseUrl": "./",
7
- "paths": {
8
- "@/*": ["../src/*"]
9
- },
7
+ "paths": {},
10
8
  "esModuleInterop": true
11
9
  }
12
10
  }
@@ -6,7 +6,7 @@ import {
6
6
  getOutput,
7
7
  replaceWithAlias,
8
8
  standardOptions,
9
- } from '@/libs/util';
9
+ } from '../src/libs/util';
10
10
 
11
11
  describe('test ssg util function', () => {
12
12
  it('should return format path correctly', () => {
package/tsconfig.json CHANGED
@@ -4,9 +4,7 @@
4
4
  "declaration": false,
5
5
  "jsx": "preserve",
6
6
  "baseUrl": "./",
7
- "paths": {
8
- "@/*": ["./src/*"]
9
- },
7
+ "paths": {},
10
8
  "esModuleInterop": true
11
9
  },
12
10
  "include": ["src"]
package/src/global.d.ts DELETED
@@ -1 +0,0 @@
1
- declare module 'normalize-path';
package/src/index.ts DELETED
@@ -1,205 +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];
90
- let entryOptions = intermediateOptions[entryName];
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 - ${ssgRoute.urlPath}, entry - ${ssgRoute.entryName} `,
173
- );
174
- }
175
-
176
- logger.warn(
177
- `new ssg route ${ssgRoute.urlPath} is using ssr now,maybe from parent route ${ssgRoute.entryName},close ssr`,
178
- );
179
- }
180
- ssgRoute.isSSR = false;
181
- ssgRoute.output = formatOutput(ssgRoute.output);
182
- });
183
-
184
- const htmlAry = await createServer(
185
- ssgRoutes,
186
- apiRoutes,
187
- resolvedConfig,
188
- appDirectory,
189
- );
190
-
191
- // write to dist file
192
- writeHtmlFile(htmlAry, ssgRoutes, buildDir);
193
-
194
- // format route info, side effect
195
- replaceRoute(ssgRoutes, pageRoutes);
196
-
197
- // write routes to spec file
198
- writeJSONSpec(buildDir, pageRoutes.concat(apiRoutes));
199
-
200
- logger.info('ssg Compiled successfully');
201
- },
202
- };
203
- }) as any,
204
- { name: '@modern-js/plugin-ssg' },
205
- ) as any;
package/src/libs/make.ts DELETED
@@ -1,45 +0,0 @@
1
- import path from 'path';
2
- import { ModernRoute } from '@modern-js/server';
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
- }
@@ -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
- }
@@ -1,42 +0,0 @@
1
- import normalize from 'normalize-path';
2
- import { ModernRoute } from '@modern-js/server';
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 { ModernRoute } from '@modern-js/server';
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}`;
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
- };
@@ -1 +0,0 @@
1
- export const CLOSE_SIGN = 'modern_close_server';
@@ -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 { ModernRoute } from '@modern-js/server';
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}.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
- });
@@ -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
- });
@@ -1,89 +0,0 @@
1
- import Server, { ModernRoute } from '@modern-js/server';
2
- import portfinder from 'portfinder';
3
- import { NormalizedConfig } from '@modern-js/core';
4
- import { compatRequire } from '@modern-js/utils';
5
- import { makeRender } from '../libs/make';
6
- import { compile as createRender } from './prerender';
7
- import { CLOSE_SIGN } from './consts';
8
- import { SsgRoute } from '@/types';
9
-
10
- type Then<T> = T extends PromiseLike<infer U> ? U : T;
11
-
12
- type ModernServer = Then<ReturnType<typeof Server>>;
13
-
14
- const safetyRequire = (filename: string, base: string) => {
15
- try {
16
- return compatRequire(
17
- require.resolve(`${filename}/server`, { paths: [base] }),
18
- );
19
- } catch (e) {
20
- return compatRequire(require.resolve(filename, { paths: [base] }));
21
- }
22
- };
23
-
24
- process.on('message', async (chunk: string) => {
25
- if (chunk === CLOSE_SIGN) {
26
- // eslint-disable-next-line no-process-exit
27
- process.exit();
28
- }
29
-
30
- const context = JSON.parse(chunk as any);
31
- const {
32
- routes,
33
- options,
34
- appDirectory,
35
- plugins,
36
- }: {
37
- routes: ModernRoute[];
38
- options: NormalizedConfig;
39
- appDirectory: string;
40
- plugins: string[];
41
- } = context;
42
-
43
- const instances = plugins.map(plugin => safetyRequire(plugin, appDirectory));
44
-
45
- let modernServer: ModernServer | null = null;
46
- try {
47
- const { server } = options;
48
-
49
- // start server in default port
50
- const defaultPort = Number(process.env.PORT) || server.port;
51
- portfinder.basePort = defaultPort!;
52
- const port = await portfinder.getPortPromise();
53
-
54
- modernServer = await Server({
55
- pwd: appDirectory,
56
- config: options,
57
- routes,
58
- staticGenerate: true,
59
- plugins: instances,
60
- });
61
-
62
- // listen just for bff request in ssr page
63
- modernServer.listen(port, async (err: Error) => {
64
- if (err) {
65
- throw err;
66
- }
67
-
68
- // get server handler, render to ssr
69
- const render = createRender(modernServer!.getRequestHandler());
70
- const renderPromiseAry = makeRender(
71
- routes.filter(route => !route.isApi) as SsgRoute[],
72
- render,
73
- port,
74
- );
75
-
76
- // eslint-disable-next-line promise/no-promise-in-callback
77
- const htmlAry = await Promise.all(renderPromiseAry);
78
- htmlAry.forEach((html: string) => {
79
- process.send!(html);
80
- process.send!(null);
81
- });
82
-
83
- modernServer!.close();
84
- });
85
- } catch (e) {
86
- modernServer?.close();
87
- throw e;
88
- }
89
- });
package/src/types.ts DELETED
@@ -1,51 +0,0 @@
1
- import { ModernRoute } from '@modern-js/server';
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
- };