@docusaurus/core 0.0.0-5823 → 0.0.0-5827

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.
@@ -14,7 +14,6 @@ const lodash_1 = tslib_1.__importDefault(require("lodash"));
14
14
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
15
  const utils_1 = require("@docusaurus/utils");
16
16
  const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
17
- const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin"));
18
17
  const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
19
18
  const WebpackDevServerUtils_1 = require("react-dev-utils/WebpackDevServerUtils");
20
19
  const evalSourceMapMiddleware_1 = tslib_1.__importDefault(require("react-dev-utils/evalSourceMapMiddleware"));
@@ -22,9 +21,10 @@ const webpack_1 = tslib_1.__importDefault(require("webpack"));
22
21
  const webpack_dev_server_1 = tslib_1.__importDefault(require("webpack-dev-server"));
23
22
  const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
24
23
  const server_1 = require("../server");
25
- const client_1 = tslib_1.__importDefault(require("../webpack/client"));
24
+ const client_1 = require("../webpack/client");
26
25
  const utils_2 = require("../webpack/utils");
27
26
  const getHostPort_1 = require("../server/getHostPort");
27
+ const utils_3 = require("../utils");
28
28
  async function start(siteDirParam = '.', cliOptions = {}) {
29
29
  // Temporary workaround to unlock the ability to translate the site config
30
30
  // We'll remove it if a better official API can be designed
@@ -32,30 +32,27 @@ async function start(siteDirParam = '.', cliOptions = {}) {
32
32
  process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale;
33
33
  const siteDir = await fs_extra_1.default.realpath(siteDirParam);
34
34
  logger_1.default.info('Starting the development server...');
35
- function loadSite() {
36
- return (0, server_1.load)({
35
+ async function loadSite() {
36
+ utils_3.PerfLogger.start('Loading site');
37
+ const result = await (0, server_1.load)({
37
38
  siteDir,
38
39
  config: cliOptions.config,
39
40
  locale: cliOptions.locale,
40
41
  localizePath: undefined, // Should this be configurable?
41
42
  });
43
+ utils_3.PerfLogger.end('Loading site');
44
+ return result;
42
45
  }
43
46
  // Process all related files as a prop.
44
47
  const props = await loadSite();
45
- const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
46
- const { host, port } = await (0, getHostPort_1.getHostPort)(cliOptions);
47
- if (port === null) {
48
- process.exit();
49
- }
50
- const { baseUrl, headTags, preBodyTags, postBodyTags } = props;
51
- const urls = (0, WebpackDevServerUtils_1.prepareUrls)(protocol, host, port);
52
- const openUrl = (0, utils_1.normalizeUrl)([urls.localUrlForBrowser, baseUrl]);
48
+ const { host, port, getOpenUrl } = await createUrlUtils({ cliOptions });
49
+ const openUrl = getOpenUrl({ baseUrl: props.baseUrl });
53
50
  logger_1.default.success `Docusaurus website is running at: url=${openUrl}`;
54
51
  // Reload files processing.
55
52
  const reload = lodash_1.default.debounce(() => {
56
53
  loadSite()
57
54
  .then(({ baseUrl: newBaseUrl }) => {
58
- const newOpenUrl = (0, utils_1.normalizeUrl)([urls.localUrlForBrowser, newBaseUrl]);
55
+ const newOpenUrl = getOpenUrl({ baseUrl: newBaseUrl });
59
56
  if (newOpenUrl !== openUrl) {
60
57
  logger_1.default.success `Docusaurus website is running at: url=${newOpenUrl}`;
61
58
  }
@@ -64,84 +61,96 @@ async function start(siteDirParam = '.', cliOptions = {}) {
64
61
  logger_1.default.error(err.stack);
65
62
  });
66
63
  }, 500);
67
- const { siteConfig, plugins, localizationDir } = props;
68
- const normalizeToSiteDir = (filepath) => {
69
- if (filepath && path_1.default.isAbsolute(filepath)) {
70
- return (0, utils_1.posixPath)(path_1.default.relative(siteDir, filepath));
64
+ // TODO this is historically not optimized!
65
+ // When any site file changes, we reload absolutely everything :/
66
+ // At least we should try to reload only one plugin individually?
67
+ setupFileWatchers({
68
+ props,
69
+ cliOptions,
70
+ onFileChange: () => {
71
+ reload();
72
+ },
73
+ });
74
+ const config = await getStartClientConfig({
75
+ props,
76
+ minify: cliOptions.minify ?? true,
77
+ poll: cliOptions.poll,
78
+ });
79
+ const compiler = (0, webpack_1.default)(config);
80
+ registerE2ETestHook(compiler);
81
+ const defaultDevServerConfig = await createDevServerConfig({
82
+ cliOptions,
83
+ props,
84
+ host,
85
+ port,
86
+ });
87
+ // Allow plugin authors to customize/override devServer config
88
+ const devServerConfig = (0, webpack_merge_1.default)([defaultDevServerConfig, config.devServer].filter(Boolean));
89
+ const devServer = new webpack_dev_server_1.default(devServerConfig, compiler);
90
+ devServer.startCallback(() => {
91
+ if (cliOptions.open) {
92
+ (0, openBrowser_1.default)(openUrl);
71
93
  }
72
- return (0, utils_1.posixPath)(filepath);
73
- };
74
- const pluginPaths = plugins
75
- .flatMap((plugin) => plugin.getPathsToWatch?.() ?? [])
76
- .filter(Boolean)
77
- .map(normalizeToSiteDir);
78
- const pathsToWatch = [...pluginPaths, props.siteConfigPath, localizationDir];
79
- const pollingOptions = {
94
+ });
95
+ ['SIGINT', 'SIGTERM'].forEach((sig) => {
96
+ process.on(sig, () => {
97
+ devServer.stop();
98
+ process.exit();
99
+ });
100
+ });
101
+ }
102
+ exports.start = start;
103
+ function createPollingOptions({ cliOptions }) {
104
+ return {
80
105
  usePolling: !!cliOptions.poll,
81
106
  interval: Number.isInteger(cliOptions.poll)
82
107
  ? cliOptions.poll
83
108
  : undefined,
84
109
  };
85
- const httpsConfig = await (0, utils_2.getHttpsConfig)();
110
+ }
111
+ function setupFileWatchers({ props, cliOptions, onFileChange, }) {
112
+ const { siteDir } = props;
113
+ const pathsToWatch = getPathsToWatch({ props });
114
+ const pollingOptions = createPollingOptions({ cliOptions });
86
115
  const fsWatcher = chokidar_1.default.watch(pathsToWatch, {
87
116
  cwd: siteDir,
88
117
  ignoreInitial: true,
89
118
  ...{ pollingOptions },
90
119
  });
91
- ['add', 'change', 'unlink', 'addDir', 'unlinkDir'].forEach((event) => fsWatcher.on(event, reload));
92
- let config = (0, webpack_merge_1.default)(await (0, client_1.default)(props, cliOptions.minify, false), {
93
- watchOptions: {
94
- ignored: /node_modules\/(?!@docusaurus)/,
95
- poll: cliOptions.poll,
96
- },
97
- infrastructureLogging: {
98
- // Reduce log verbosity, see https://github.com/facebook/docusaurus/pull/5420#issuecomment-906613105
99
- level: 'warn',
100
- },
101
- plugins: [
102
- // Generates an `index.html` file with the <script> injected.
103
- new html_webpack_plugin_1.default({
104
- template: path_1.default.join(__dirname, '../webpack/templates/index.html.template.ejs'),
105
- // So we can define the position where the scripts are injected.
106
- inject: false,
107
- filename: 'index.html',
108
- title: siteConfig.title,
109
- headTags,
110
- preBodyTags,
111
- postBodyTags,
112
- }),
113
- ],
114
- });
115
- // Plugin Lifecycle - configureWebpack and configurePostCss.
116
- plugins.forEach((plugin) => {
117
- const { configureWebpack, configurePostCss } = plugin;
118
- if (configurePostCss) {
119
- config = (0, utils_2.applyConfigurePostCss)(configurePostCss.bind(plugin), config);
120
- }
121
- if (configureWebpack) {
122
- config = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
123
- config, false, props.siteConfig.webpack?.jsLoader, plugin.content);
124
- }
125
- });
126
- const compiler = (0, webpack_1.default)(config);
127
- compiler.hooks.done.tap('done', (stats) => {
128
- const errorsWarnings = stats.toJson('errors-warnings');
129
- const statsErrorMessage = (0, utils_2.formatStatsErrorMessage)(errorsWarnings);
130
- if (statsErrorMessage) {
131
- console.error(statsErrorMessage);
132
- }
133
- (0, utils_2.printStatsWarnings)(errorsWarnings);
134
- if (process.env.E2E_TEST) {
135
- if (stats.hasErrors()) {
136
- logger_1.default.error('E2E_TEST: Project has compiler errors.');
137
- process.exit(1);
138
- }
139
- logger_1.default.success('E2E_TEST: Project can compile.');
140
- process.exit(0);
120
+ ['add', 'change', 'unlink', 'addDir', 'unlinkDir'].forEach((event) => fsWatcher.on(event, onFileChange));
121
+ }
122
+ function getPathsToWatch({ props }) {
123
+ const { siteDir, siteConfigPath, plugins, localizationDir } = props;
124
+ const normalizeToSiteDir = (filepath) => {
125
+ if (filepath && path_1.default.isAbsolute(filepath)) {
126
+ return (0, utils_1.posixPath)(path_1.default.relative(siteDir, filepath));
141
127
  }
142
- });
128
+ return (0, utils_1.posixPath)(filepath);
129
+ };
130
+ const pluginsPaths = plugins
131
+ .flatMap((plugin) => plugin.getPathsToWatch?.() ?? [])
132
+ .filter(Boolean)
133
+ .map(normalizeToSiteDir);
134
+ return [...pluginsPaths, siteConfigPath, localizationDir];
135
+ }
136
+ async function createUrlUtils({ cliOptions }) {
137
+ const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
138
+ const { host, port } = await (0, getHostPort_1.getHostPort)(cliOptions);
139
+ if (port === null) {
140
+ return process.exit();
141
+ }
142
+ const getOpenUrl = ({ baseUrl }) => {
143
+ const urls = (0, WebpackDevServerUtils_1.prepareUrls)(protocol, host, port);
144
+ return (0, utils_1.normalizeUrl)([urls.localUrlForBrowser, baseUrl]);
145
+ };
146
+ return { host, port, getOpenUrl };
147
+ }
148
+ async function createDevServerConfig({ cliOptions, props, host, port, }) {
149
+ const { baseUrl, siteDir, siteConfig } = props;
150
+ const pollingOptions = createPollingOptions({ cliOptions });
151
+ const httpsConfig = await (0, utils_2.getHttpsConfig)();
143
152
  // https://webpack.js.org/configuration/dev-server
144
- const defaultDevServerConfig = {
153
+ return {
145
154
  hot: cliOptions.hotOnly ? 'only' : true,
146
155
  liveReload: false,
147
156
  client: {
@@ -194,19 +203,40 @@ async function start(siteDirParam = '.', cliOptions = {}) {
194
203
  return middlewares;
195
204
  },
196
205
  };
197
- // Allow plugin authors to customize/override devServer config
198
- const devServerConfig = (0, webpack_merge_1.default)([defaultDevServerConfig, config.devServer].filter(Boolean));
199
- const devServer = new webpack_dev_server_1.default(devServerConfig, compiler);
200
- devServer.startCallback(() => {
201
- if (cliOptions.open) {
202
- (0, openBrowser_1.default)(openUrl);
206
+ }
207
+ // E2E_TEST=true docusaurus start
208
+ // Makes "docusaurus start" exit immediately on success/error, for E2E test
209
+ function registerE2ETestHook(compiler) {
210
+ compiler.hooks.done.tap('done', (stats) => {
211
+ const errorsWarnings = stats.toJson('errors-warnings');
212
+ const statsErrorMessage = (0, utils_2.formatStatsErrorMessage)(errorsWarnings);
213
+ if (statsErrorMessage) {
214
+ console.error(statsErrorMessage);
215
+ }
216
+ (0, utils_2.printStatsWarnings)(errorsWarnings);
217
+ if (process.env.E2E_TEST) {
218
+ if (stats.hasErrors()) {
219
+ logger_1.default.error('E2E_TEST: Project has compiler errors.');
220
+ process.exit(1);
221
+ }
222
+ logger_1.default.success('E2E_TEST: Project can compile.');
223
+ process.exit(0);
203
224
  }
204
225
  });
205
- ['SIGINT', 'SIGTERM'].forEach((sig) => {
206
- process.on(sig, () => {
207
- devServer.stop();
208
- process.exit();
209
- });
226
+ }
227
+ async function getStartClientConfig({ props, minify, poll, }) {
228
+ const { plugins, siteConfig } = props;
229
+ let { clientConfig: config } = await (0, client_1.createStartClientConfig)({
230
+ props,
231
+ minify,
232
+ poll,
233
+ });
234
+ config = (0, utils_2.executePluginsConfigurePostCss)({ plugins, config });
235
+ config = (0, utils_2.executePluginsConfigureWebpack)({
236
+ plugins,
237
+ config,
238
+ isServer: false,
239
+ jsLoader: siteConfig.webpack?.jsLoader,
210
240
  });
241
+ return config;
211
242
  }
212
- exports.start = start;
package/lib/ssg.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { AppRenderer, SiteCollectedData } from './common';
8
+ import type { Manifest } from 'react-loadable-ssr-addon-v5-slorber';
9
+ import type { SSRTemplateCompiled } from './templates/templates';
10
+ export type SSGParams = {
11
+ trailingSlash: boolean | undefined;
12
+ manifest: Manifest;
13
+ headTags: string;
14
+ preBodyTags: string;
15
+ postBodyTags: string;
16
+ outDir: string;
17
+ baseUrl: string;
18
+ noIndex: boolean;
19
+ DOCUSAURUS_VERSION: string;
20
+ ssrTemplate: SSRTemplateCompiled;
21
+ };
22
+ export declare function loadAppRenderer({ serverBundlePath, }: {
23
+ serverBundlePath: string;
24
+ }): Promise<AppRenderer>;
25
+ export declare function generateStaticFiles({ pathnames, renderer, params, }: {
26
+ pathnames: string[];
27
+ renderer: AppRenderer;
28
+ params: SSGParams;
29
+ }): Promise<{
30
+ collectedData: SiteCollectedData;
31
+ }>;
package/lib/ssg.js ADDED
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generateStaticFiles = exports.loadAppRenderer = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
+ const module_1 = require("module");
13
+ const path_1 = tslib_1.__importDefault(require("path"));
14
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
15
+ const eval_1 = tslib_1.__importDefault(require("eval"));
16
+ const p_map_1 = tslib_1.__importDefault(require("p-map"));
17
+ const html_minifier_terser_1 = require("html-minifier-terser");
18
+ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
19
+ const utils_1 = require("./utils");
20
+ const templates_1 = require("./templates/templates");
21
+ // Secret way to set SSR plugin concurrency option
22
+ // Waiting for feedback before documenting this officially?
23
+ const Concurrency = process.env.DOCUSAURUS_SSR_CONCURRENCY
24
+ ? parseInt(process.env.DOCUSAURUS_SSR_CONCURRENCY, 10)
25
+ : // Not easy to define a reasonable option default
26
+ // Will still be better than Infinity
27
+ // See also https://github.com/sindresorhus/p-map/issues/24
28
+ 32;
29
+ async function loadAppRenderer({ serverBundlePath, }) {
30
+ console.log(`SSG - Load server bundle`);
31
+ utils_1.PerfLogger.start(`SSG - Load server bundle`);
32
+ const source = await fs_extra_1.default.readFile(serverBundlePath);
33
+ utils_1.PerfLogger.end(`SSG - Load server bundle`);
34
+ utils_1.PerfLogger.log(`SSG - Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`);
35
+ const filename = path_1.default.basename(serverBundlePath);
36
+ const globals = {
37
+ // When using "new URL('file.js', import.meta.url)", Webpack will emit
38
+ // __filename, and this plugin will throw. not sure the __filename value
39
+ // has any importance for this plugin, just using an empty string to
40
+ // avoid the error. See https://github.com/facebook/docusaurus/issues/4922
41
+ __filename: '',
42
+ // This uses module.createRequire() instead of very old "require-like" lib
43
+ // See also: https://github.com/pierrec/node-eval/issues/33
44
+ require: (0, module_1.createRequire)(serverBundlePath),
45
+ };
46
+ utils_1.PerfLogger.start(`SSG - Evaluate server bundle`);
47
+ const serverEntry = (0, eval_1.default)(source,
48
+ /* filename: */ filename,
49
+ /* scope: */ globals,
50
+ /* includeGlobals: */ true);
51
+ utils_1.PerfLogger.end(`SSG - Evaluate server bundle`);
52
+ if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
53
+ throw new Error(`Server bundle export from "${filename}" must be a function that renders the Docusaurus React app.`);
54
+ }
55
+ return serverEntry.default;
56
+ }
57
+ exports.loadAppRenderer = loadAppRenderer;
58
+ function pathnameToFilename({ pathname, trailingSlash, }) {
59
+ const outputFileName = pathname.replace(/^[/\\]/, ''); // Remove leading slashes for webpack-dev-server
60
+ // Paths ending with .html are left untouched
61
+ if (/\.html?$/i.test(outputFileName)) {
62
+ return outputFileName;
63
+ }
64
+ // Legacy retro-compatible behavior
65
+ if (typeof trailingSlash === 'undefined') {
66
+ return path_1.default.join(outputFileName, 'index.html');
67
+ }
68
+ // New behavior: we can say if we prefer file/folder output
69
+ // Useful resource: https://github.com/slorber/trailing-slash-guide
70
+ if (pathname === '' || pathname.endsWith('/') || trailingSlash) {
71
+ return path_1.default.join(outputFileName, 'index.html');
72
+ }
73
+ return `${outputFileName}.html`;
74
+ }
75
+ async function generateStaticFiles({ pathnames, renderer, params, }) {
76
+ // Note that we catch all async errors on purpose
77
+ // Docusaurus presents all the SSG errors to the user, not just the first one
78
+ const results = await (0, p_map_1.default)(pathnames, async (pathname) => generateStaticFile({
79
+ pathname,
80
+ renderer,
81
+ params,
82
+ }).then((result) => ({ pathname, result, error: null }), (error) => ({ pathname, result: null, error: error })), { concurrency: Concurrency });
83
+ const [allSSGErrors, allSSGSuccesses] = lodash_1.default.partition(results, (r) => !!r.error);
84
+ if (allSSGErrors.length > 0) {
85
+ const message = `Docusaurus static site generation failed for ${allSSGErrors.length} path${allSSGErrors.length ? 's' : ''}:\n- ${allSSGErrors
86
+ .map((ssgError) => logger_1.default.path(ssgError.pathname))
87
+ .join('\n- ')}`;
88
+ // Note logging this error properly require using inspect(error,{depth})
89
+ // See https://github.com/nodejs/node/issues/51637
90
+ throw new Error(message, {
91
+ cause: new AggregateError(allSSGErrors.map((ssgError) => ssgError.error)),
92
+ });
93
+ }
94
+ const collectedData = lodash_1.default.chain(allSSGSuccesses)
95
+ .keyBy((success) => success.pathname)
96
+ .mapValues((ssgSuccess) => ssgSuccess.result.collectedData)
97
+ .value();
98
+ return { collectedData };
99
+ }
100
+ exports.generateStaticFiles = generateStaticFiles;
101
+ async function generateStaticFile({ pathname, renderer, params, }) {
102
+ try {
103
+ // This only renders the app HTML
104
+ const result = await renderer({
105
+ pathname,
106
+ });
107
+ // This renders the full page HTML, including head tags...
108
+ const fullPageHtml = (0, templates_1.renderSSRTemplate)({
109
+ params,
110
+ result,
111
+ });
112
+ const content = await minifyHtml(fullPageHtml);
113
+ await writeStaticFile({
114
+ pathname,
115
+ content,
116
+ params,
117
+ });
118
+ return result;
119
+ }
120
+ catch (errorUnknown) {
121
+ const error = errorUnknown;
122
+ const tips = getSSGErrorTips(error);
123
+ const message = logger_1.default.interpolate `Can't render static file for pathname path=${pathname}${tips ? `\n\n${tips}` : ''}`;
124
+ throw new Error(message, {
125
+ cause: error,
126
+ });
127
+ }
128
+ }
129
+ function getSSGErrorTips(error) {
130
+ const parts = [];
131
+ const isNotDefinedErrorRegex = /(?:window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i;
132
+ if (isNotDefinedErrorRegex.test(error.message)) {
133
+ parts.push(`It looks like you are using code that should run on the client-side only.
134
+ To get around it, try using one of:
135
+ - ${logger_1.default.code('<BrowserOnly>')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#browseronly')})
136
+ - ${logger_1.default.code('ExecutionEnvironment')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#executionenvironment')}).
137
+ It might also require to wrap your client code in ${logger_1.default.code('useEffect')} hook and/or import a third-party library dynamically (if any).`);
138
+ }
139
+ return parts.join('\n');
140
+ }
141
+ async function writeStaticFile({ content, pathname, params, }) {
142
+ function removeBaseUrl(p, baseUrl) {
143
+ return baseUrl === '/' ? p : p.replace(new RegExp(`^${baseUrl}`), '/');
144
+ }
145
+ const filename = pathnameToFilename({
146
+ pathname: removeBaseUrl(pathname, params.baseUrl),
147
+ trailingSlash: params.trailingSlash,
148
+ });
149
+ const filePath = path_1.default.join(params.outDir, filename);
150
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
151
+ await fs_extra_1.default.writeFile(filePath, content);
152
+ }
153
+ async function minifyHtml(html) {
154
+ try {
155
+ if (process.env.SKIP_HTML_MINIFICATION === 'true') {
156
+ return html;
157
+ }
158
+ // Minify html with https://github.com/DanielRuf/html-minifier-terser
159
+ return await (0, html_minifier_terser_1.minify)(html, {
160
+ removeComments: false,
161
+ removeRedundantAttributes: true,
162
+ removeEmptyAttributes: true,
163
+ removeScriptTypeAttributes: true,
164
+ removeStyleLinkTypeAttributes: true,
165
+ useShortDoctype: true,
166
+ minifyJS: true,
167
+ });
168
+ }
169
+ catch (err) {
170
+ throw new Error('HTML minification failed', { cause: err });
171
+ }
172
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { SSGParams } from '../ssg';
8
+ import type { AppRenderResult } from '../common';
9
+ export type SSRTemplateData = {
10
+ appHtml: string;
11
+ baseUrl: string;
12
+ htmlAttributes: string;
13
+ bodyAttributes: string;
14
+ headTags: string;
15
+ preBodyTags: string;
16
+ postBodyTags: string;
17
+ metaAttributes: string[];
18
+ scripts: string[];
19
+ stylesheets: string[];
20
+ noIndex: boolean;
21
+ version: string;
22
+ };
23
+ export type SSRTemplateCompiled = (data: SSRTemplateData) => string;
24
+ export declare function compileSSRTemplate(template: string): Promise<SSRTemplateCompiled>;
25
+ export declare function renderSSRTemplate({ params, result, }: {
26
+ params: SSGParams;
27
+ result: AppRenderResult;
28
+ }): string;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.renderSSRTemplate = exports.compileSSRTemplate = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const eta = tslib_1.__importStar(require("eta"));
12
+ const react_loadable_ssr_addon_v5_slorber_1 = require("react-loadable-ssr-addon-v5-slorber");
13
+ async function compileSSRTemplate(template) {
14
+ const compiledTemplate = eta.compile(template.trim(), {
15
+ rmWhitespace: true,
16
+ });
17
+ return (data) => compiledTemplate(data, eta.defaultConfig);
18
+ }
19
+ exports.compileSSRTemplate = compileSSRTemplate;
20
+ /**
21
+ * Given a list of modules that were SSR an d
22
+ * @param modules
23
+ * @param manifest
24
+ */
25
+ function getScriptsAndStylesheets({ modules, manifest, }) {
26
+ // Get all required assets for this particular page
27
+ // based on client manifest information.
28
+ const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
29
+ const bundles = (0, react_loadable_ssr_addon_v5_slorber_1.getBundles)(manifest, modulesToBeLoaded);
30
+ const stylesheets = (bundles.css ?? []).map((b) => b.file);
31
+ const scripts = (bundles.js ?? []).map((b) => b.file);
32
+ return { scripts, stylesheets };
33
+ }
34
+ function renderSSRTemplate({ params, result, }) {
35
+ const { baseUrl, headTags, preBodyTags, postBodyTags, manifest, noIndex, DOCUSAURUS_VERSION, ssrTemplate, } = params;
36
+ const { html: appHtml, collectedData: { modules, helmet }, } = result;
37
+ const { scripts, stylesheets } = getScriptsAndStylesheets({ manifest, modules });
38
+ const htmlAttributes = helmet.htmlAttributes.toString();
39
+ const bodyAttributes = helmet.bodyAttributes.toString();
40
+ const metaStrings = [
41
+ helmet.title.toString(),
42
+ helmet.meta.toString(),
43
+ helmet.link.toString(),
44
+ helmet.script.toString(),
45
+ ];
46
+ const metaAttributes = metaStrings.filter(Boolean);
47
+ const data = {
48
+ appHtml,
49
+ baseUrl,
50
+ htmlAttributes,
51
+ bodyAttributes,
52
+ headTags,
53
+ preBodyTags,
54
+ postBodyTags,
55
+ metaAttributes,
56
+ scripts,
57
+ stylesheets,
58
+ noIndex,
59
+ version: DOCUSAURUS_VERSION,
60
+ };
61
+ return ssrTemplate(data);
62
+ }
63
+ exports.renderSSRTemplate = renderSSRTemplate;
package/lib/utils.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare const PerfDebuggingEnabled: boolean;
2
+ type PerfLoggerAPI = {
3
+ start: (label: string) => void;
4
+ end: (label: string) => void;
5
+ log: (message: string) => void;
6
+ };
7
+ export declare const PerfLogger: PerfLoggerAPI;
8
+ export {};
package/lib/utils.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PerfLogger = exports.PerfDebuggingEnabled = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Copyright (c) Facebook, Inc. and its affiliates.
7
+ *
8
+ * This source code is licensed under the MIT license found in the
9
+ * LICENSE file in the root directory of this source tree.
10
+ */
11
+ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
12
+ // For now this is a private env variable we use internally
13
+ // But we'll want to expose this feature officially some day
14
+ exports.PerfDebuggingEnabled = !!process.env.DOCUSAURUS_PERF_LOGGER;
15
+ function createPerfLogger() {
16
+ if (!exports.PerfDebuggingEnabled) {
17
+ const noop = () => { };
18
+ return {
19
+ start: noop,
20
+ end: noop,
21
+ log: noop,
22
+ };
23
+ }
24
+ const prefix = logger_1.default.yellow(`[PERF] `);
25
+ return {
26
+ start: (label) => console.time(prefix + label),
27
+ end: (label) => console.timeEnd(prefix + label),
28
+ log: (label) => console.log(prefix + label),
29
+ };
30
+ }
31
+ exports.PerfLogger = createPerfLogger();
@@ -8,4 +8,8 @@ import type { Configuration } from 'webpack';
8
8
  import type { Props } from '@docusaurus/types';
9
9
  export declare const clientDir: string;
10
10
  export declare function excludeJS(modulePath: string): boolean;
11
- export declare function createBaseConfig(props: Props, isServer: boolean, minify?: boolean): Promise<Configuration>;
11
+ export declare function createBaseConfig({ props, isServer, minify, }: {
12
+ props: Props;
13
+ isServer: boolean;
14
+ minify: boolean;
15
+ }): Promise<Configuration>;
@@ -13,6 +13,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
13
13
  const mini_css_extract_plugin_1 = tslib_1.__importDefault(require("mini-css-extract-plugin"));
14
14
  const utils_1 = require("@docusaurus/utils");
15
15
  const utils_2 = require("./utils");
16
+ const minification_1 = require("./minification");
16
17
  const aliases_1 = require("./aliases");
17
18
  const CSS_REGEX = /\.css$/i;
18
19
  const CSS_MODULE_REGEX = /\.module\.css$/i;
@@ -32,12 +33,11 @@ function excludeJS(modulePath) {
32
33
  !LibrariesToTranspileRegex.test(modulePath));
33
34
  }
34
35
  exports.excludeJS = excludeJS;
35
- async function createBaseConfig(props, isServer, minify = true) {
36
+ async function createBaseConfig({ props, isServer, minify, }) {
36
37
  const { outDir, siteDir, siteConfig, siteConfigPath, baseUrl, generatedFilesDir, routesPaths, siteMetadata, plugins, } = props;
37
38
  const totalPages = routesPaths.length;
38
39
  const isProd = process.env.NODE_ENV === 'production';
39
- const minimizeEnabled = minify && isProd && !isServer;
40
- const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
40
+ const minimizeEnabled = minify && isProd;
41
41
  const fileLoaderUtils = (0, utils_1.getFileLoaderUtils)();
42
42
  const name = isServer ? 'server' : 'client';
43
43
  const mode = isProd ? 'production' : 'development';
@@ -124,9 +124,7 @@ async function createBaseConfig(props, isServer, minify = true) {
124
124
  // Only minimize client bundle in production because server bundle is only
125
125
  // used for static site generation
126
126
  minimize: minimizeEnabled,
127
- minimizer: minimizeEnabled
128
- ? (0, utils_2.getMinimizer)(useSimpleCssMinifier)
129
- : undefined,
127
+ minimizer: minimizeEnabled ? (0, minification_1.getMinimizer)() : undefined,
130
128
  splitChunks: isServer
131
129
  ? false
132
130
  : {