@docusaurus/core 3.1.1 → 3.2.0

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.
Files changed (85) hide show
  1. package/bin/docusaurus.mjs +10 -5
  2. package/lib/client/{serverRenderer.d.ts → renderToHtml.d.ts} +1 -1
  3. package/lib/client/{serverRenderer.js → renderToHtml.js} +1 -1
  4. package/lib/client/serverEntry.d.ts +3 -4
  5. package/lib/client/serverEntry.js +12 -113
  6. package/lib/client/theme-fallback/Error/index.js +22 -8
  7. package/lib/commands/build.d.ts +3 -3
  8. package/lib/commands/build.js +133 -112
  9. package/lib/commands/deploy.d.ts +2 -2
  10. package/lib/commands/deploy.js +3 -3
  11. package/lib/commands/external.js +2 -2
  12. package/lib/commands/serve.d.ts +2 -2
  13. package/lib/commands/serve.js +4 -4
  14. package/lib/commands/{start.d.ts → start/start.d.ts} +3 -3
  15. package/lib/commands/start/start.js +47 -0
  16. package/lib/commands/start/utils.d.ts +31 -0
  17. package/lib/commands/start/utils.js +87 -0
  18. package/lib/commands/start/watcher.d.ts +42 -0
  19. package/lib/commands/start/watcher.js +78 -0
  20. package/lib/commands/start/webpack.d.ts +15 -0
  21. package/lib/commands/start/webpack.js +133 -0
  22. package/lib/commands/swizzle/common.d.ts +1 -0
  23. package/lib/commands/swizzle/common.js +1 -0
  24. package/lib/commands/swizzle/context.js +2 -2
  25. package/lib/commands/swizzle/index.js +32 -3
  26. package/lib/commands/writeHeadingIds.js +2 -2
  27. package/lib/commands/writeTranslations.d.ts +2 -2
  28. package/lib/commands/writeTranslations.js +3 -3
  29. package/lib/index.d.ts +1 -1
  30. package/lib/index.js +1 -1
  31. package/lib/server/brokenLinks.js +4 -4
  32. package/lib/server/clientModules.d.ts +1 -1
  33. package/lib/server/clientModules.js +3 -3
  34. package/lib/server/codegen/codegen.d.ts +20 -0
  35. package/lib/server/codegen/codegen.js +65 -0
  36. package/lib/server/codegen/codegenRoutes.d.ts +49 -0
  37. package/lib/server/codegen/codegenRoutes.js +190 -0
  38. package/lib/server/configValidation.js +6 -4
  39. package/lib/server/i18n.d.ts +2 -2
  40. package/lib/server/i18n.js +20 -2
  41. package/lib/server/plugins/actions.d.ts +19 -0
  42. package/lib/server/plugins/actions.js +62 -0
  43. package/lib/server/plugins/init.js +3 -3
  44. package/lib/server/plugins/plugins.d.ts +21 -0
  45. package/lib/server/plugins/plugins.js +188 -0
  46. package/lib/server/plugins/pluginsUtils.d.ts +16 -0
  47. package/lib/server/plugins/pluginsUtils.js +75 -0
  48. package/lib/server/plugins/routeConfig.d.ts +1 -1
  49. package/lib/server/plugins/routeConfig.js +4 -4
  50. package/lib/server/plugins/synthetic.d.ts +3 -3
  51. package/lib/server/plugins/synthetic.js +0 -2
  52. package/lib/server/routes.d.ts +3 -45
  53. package/lib/server/routes.js +16 -168
  54. package/lib/server/{index.d.ts → site.d.ts} +12 -5
  55. package/lib/server/site.js +164 -0
  56. package/lib/server/siteMetadata.d.ts +5 -4
  57. package/lib/server/siteMetadata.js +14 -10
  58. package/lib/server/translations/translations.d.ts +9 -2
  59. package/lib/server/translations/translations.js +21 -4
  60. package/lib/server/utils.d.ts +0 -2
  61. package/lib/server/utils.js +1 -9
  62. package/lib/ssg.d.ts +31 -0
  63. package/lib/ssg.js +167 -0
  64. package/lib/templates/templates.d.ts +28 -0
  65. package/lib/templates/templates.js +63 -0
  66. package/lib/utils.d.ts +9 -0
  67. package/lib/utils.js +78 -0
  68. package/lib/webpack/base.d.ts +5 -1
  69. package/lib/webpack/base.js +4 -6
  70. package/lib/webpack/client.d.ts +15 -1
  71. package/lib/webpack/client.js +78 -23
  72. package/lib/webpack/minification.d.ts +8 -0
  73. package/lib/webpack/minification.js +97 -0
  74. package/lib/webpack/server.d.ts +5 -3
  75. package/lib/webpack/server.js +41 -50
  76. package/lib/webpack/utils.d.ts +13 -4
  77. package/lib/webpack/utils.js +27 -83
  78. package/package.json +13 -11
  79. package/lib/commands/start.js +0 -212
  80. package/lib/server/index.js +0 -154
  81. package/lib/server/plugins/index.d.ts +0 -18
  82. package/lib/server/plugins/index.js +0 -106
  83. /package/lib/{webpack/templates/index.html.template.ejs → templates/dev.html.template.ejs} +0 -0
  84. /package/lib/{webpack/templates → templates}/ssr.html.template.d.ts +0 -0
  85. /package/lib/{webpack/templates → templates}/ssr.html.template.js +0 -0
@@ -0,0 +1,164 @@
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.reloadSitePlugin = exports.reloadSite = exports.loadSite = exports.loadContext = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const path_1 = tslib_1.__importDefault(require("path"));
12
+ const utils_1 = require("@docusaurus/utils");
13
+ const combine_promises_1 = tslib_1.__importDefault(require("combine-promises"));
14
+ const config_1 = require("./config");
15
+ const clientModules_1 = require("./clientModules");
16
+ const plugins_1 = require("./plugins/plugins");
17
+ const htmlTags_1 = require("./htmlTags");
18
+ const siteMetadata_1 = require("./siteMetadata");
19
+ const i18n_1 = require("./i18n");
20
+ const translations_1 = require("./translations/translations");
21
+ const utils_2 = require("../utils");
22
+ const codegen_1 = require("./codegen/codegen");
23
+ const routes_1 = require("./routes");
24
+ /**
25
+ * Loading context is the very first step in site building. Its params are
26
+ * directly acquired from CLI options. It mainly loads `siteConfig` and the i18n
27
+ * context (which includes code translations). The `LoadContext` will be passed
28
+ * to plugin constructors.
29
+ */
30
+ async function loadContext(params) {
31
+ const { siteDir, outDir: baseOutDir = utils_1.DEFAULT_BUILD_DIR_NAME, locale, config: customConfigFilePath, } = params;
32
+ const generatedFilesDir = path_1.default.resolve(siteDir, utils_1.GENERATED_FILES_DIR_NAME);
33
+ const { siteVersion, loadSiteConfig: { siteConfig: initialSiteConfig, siteConfigPath }, } = await (0, combine_promises_1.default)({
34
+ siteVersion: (0, siteMetadata_1.loadSiteVersion)(siteDir),
35
+ loadSiteConfig: (0, config_1.loadSiteConfig)({
36
+ siteDir,
37
+ customConfigFilePath,
38
+ }),
39
+ });
40
+ const i18n = await (0, i18n_1.loadI18n)(initialSiteConfig, { locale });
41
+ const baseUrl = (0, utils_1.localizePath)({
42
+ path: initialSiteConfig.baseUrl,
43
+ i18n,
44
+ options: params,
45
+ pathType: 'url',
46
+ });
47
+ const outDir = (0, utils_1.localizePath)({
48
+ path: path_1.default.resolve(siteDir, baseOutDir),
49
+ i18n,
50
+ options: params,
51
+ pathType: 'fs',
52
+ });
53
+ const localizationDir = path_1.default.resolve(siteDir, i18n.path, i18n.localeConfigs[i18n.currentLocale].path);
54
+ const siteConfig = { ...initialSiteConfig, baseUrl };
55
+ const codeTranslations = await (0, translations_1.loadSiteCodeTranslations)({ localizationDir });
56
+ return {
57
+ siteDir,
58
+ siteVersion,
59
+ generatedFilesDir,
60
+ localizationDir,
61
+ siteConfig,
62
+ siteConfigPath,
63
+ outDir,
64
+ baseUrl,
65
+ i18n,
66
+ codeTranslations,
67
+ };
68
+ }
69
+ exports.loadContext = loadContext;
70
+ function createSiteProps(params) {
71
+ const { plugins, routes, context } = params;
72
+ const { generatedFilesDir, siteDir, siteVersion, siteConfig, siteConfigPath, outDir, baseUrl, i18n, localizationDir, codeTranslations: siteCodeTranslations, } = context;
73
+ const { headTags, preBodyTags, postBodyTags } = (0, htmlTags_1.loadHtmlTags)(plugins);
74
+ const siteMetadata = (0, siteMetadata_1.createSiteMetadata)({ plugins, siteVersion });
75
+ const codeTranslations = {
76
+ ...(0, translations_1.getPluginsDefaultCodeTranslations)({ plugins }),
77
+ ...siteCodeTranslations,
78
+ };
79
+ (0, routes_1.handleDuplicateRoutes)(routes, siteConfig.onDuplicateRoutes);
80
+ const routesPaths = (0, routes_1.getRoutesPaths)(routes, baseUrl);
81
+ return {
82
+ siteConfig,
83
+ siteConfigPath,
84
+ siteMetadata,
85
+ siteVersion,
86
+ siteDir,
87
+ outDir,
88
+ baseUrl,
89
+ i18n,
90
+ localizationDir,
91
+ generatedFilesDir,
92
+ routes,
93
+ routesPaths,
94
+ plugins,
95
+ headTags,
96
+ preBodyTags,
97
+ postBodyTags,
98
+ codeTranslations,
99
+ };
100
+ }
101
+ // TODO global data should be part of site props?
102
+ async function createSiteFiles({ site, globalData, }) {
103
+ return utils_2.PerfLogger.async('Create site files', async () => {
104
+ const { props: { plugins, generatedFilesDir, siteConfig, siteMetadata, i18n, codeTranslations, routes, baseUrl, }, } = site;
105
+ const clientModules = (0, clientModules_1.getAllClientModules)(plugins);
106
+ await (0, codegen_1.generateSiteFiles)({
107
+ generatedFilesDir,
108
+ clientModules,
109
+ siteConfig,
110
+ siteMetadata,
111
+ i18n,
112
+ codeTranslations,
113
+ globalData,
114
+ routes,
115
+ baseUrl,
116
+ });
117
+ });
118
+ }
119
+ /**
120
+ * This is the crux of the Docusaurus server-side. It reads everything it needs—
121
+ * code translations, config file, plugin modules... Plugins then use their
122
+ * lifecycles to generate content and other data. It is side-effect-ful because
123
+ * it generates temp files in the `.docusaurus` folder for the bundler.
124
+ */
125
+ async function loadSite(params) {
126
+ const context = await utils_2.PerfLogger.async('Load context', () => loadContext(params));
127
+ const { plugins, routes, globalData } = await (0, plugins_1.loadPlugins)(context);
128
+ const props = await createSiteProps({ plugins, routes, globalData, context });
129
+ const site = { props, params };
130
+ await createSiteFiles({
131
+ site,
132
+ globalData,
133
+ });
134
+ return site;
135
+ }
136
+ exports.loadSite = loadSite;
137
+ async function reloadSite(site) {
138
+ // TODO this can be optimized, for example:
139
+ // - plugins loading same data as before should not recreate routes/bundles
140
+ // - codegen does not need to re-run if nothing changed
141
+ return loadSite(site.params);
142
+ }
143
+ exports.reloadSite = reloadSite;
144
+ async function reloadSitePlugin(site, pluginIdentifier) {
145
+ const { plugins, routes, globalData } = await (0, plugins_1.reloadPlugin)({
146
+ pluginIdentifier,
147
+ plugins: site.props.plugins,
148
+ context: site.props,
149
+ });
150
+ const newProps = await createSiteProps({
151
+ plugins,
152
+ routes,
153
+ globalData,
154
+ context: site.props, // Props extends Context
155
+ });
156
+ const newSite = {
157
+ props: newProps,
158
+ params: site.params,
159
+ };
160
+ // TODO optimize, bypass useless codegen if new site is similar to old site
161
+ await createSiteFiles({ site: newSite, globalData });
162
+ return newSite;
163
+ }
164
+ exports.reloadSitePlugin = reloadSitePlugin;
@@ -5,8 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { LoadedPlugin, PluginVersionInformation, SiteMetadata } from '@docusaurus/types';
8
- export declare function getPluginVersion(pluginPath: string, siteDir: string): Promise<PluginVersionInformation>;
9
- export declare function loadSiteMetadata({ plugins, siteDir, }: {
8
+ export declare function loadSiteVersion(siteDir: string): Promise<string | undefined>;
9
+ export declare function loadPluginVersion(pluginPath: string, siteDir: string): Promise<PluginVersionInformation>;
10
+ export declare function createSiteMetadata({ siteVersion, plugins, }: {
11
+ siteVersion: string | undefined;
10
12
  plugins: LoadedPlugin[];
11
- siteDir: string;
12
- }): Promise<SiteMetadata>;
13
+ }): SiteMetadata;
@@ -6,23 +6,27 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.loadSiteMetadata = exports.getPluginVersion = void 0;
9
+ exports.createSiteMetadata = exports.loadPluginVersion = exports.loadSiteVersion = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
12
  const path_1 = tslib_1.__importDefault(require("path"));
13
13
  const utils_1 = require("@docusaurus/utils");
14
- async function getPackageJsonVersion(packageJsonPath) {
14
+ async function loadPackageJsonVersion(packageJsonPath) {
15
15
  if (await fs_extra_1.default.pathExists(packageJsonPath)) {
16
16
  // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
17
17
  return require(packageJsonPath).version;
18
18
  }
19
19
  return undefined;
20
20
  }
21
- async function getPackageJsonName(packageJsonPath) {
21
+ async function loadPackageJsonName(packageJsonPath) {
22
22
  // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
23
23
  return require(packageJsonPath).name;
24
24
  }
25
- async function getPluginVersion(pluginPath, siteDir) {
25
+ async function loadSiteVersion(siteDir) {
26
+ return loadPackageJsonVersion(path_1.default.join(siteDir, 'package.json'));
27
+ }
28
+ exports.loadSiteVersion = loadSiteVersion;
29
+ async function loadPluginVersion(pluginPath, siteDir) {
26
30
  let potentialPluginPackageJsonDirectory = path_1.default.dirname(pluginPath);
27
31
  while (potentialPluginPackageJsonDirectory !== '/') {
28
32
  const packageJsonPath = path_1.default.join(potentialPluginPackageJsonDirectory, 'package.json');
@@ -35,8 +39,8 @@ async function getPluginVersion(pluginPath, siteDir) {
35
39
  }
36
40
  return {
37
41
  type: 'package',
38
- name: await getPackageJsonName(packageJsonPath),
39
- version: await getPackageJsonVersion(packageJsonPath),
42
+ name: await loadPackageJsonName(packageJsonPath),
43
+ version: await loadPackageJsonVersion(packageJsonPath),
40
44
  };
41
45
  }
42
46
  potentialPluginPackageJsonDirectory = path_1.default.dirname(potentialPluginPackageJsonDirectory);
@@ -46,7 +50,7 @@ async function getPluginVersion(pluginPath, siteDir) {
46
50
  // script in the parent directory of the site.
47
51
  return { type: 'local' };
48
52
  }
49
- exports.getPluginVersion = getPluginVersion;
53
+ exports.loadPluginVersion = loadPluginVersion;
50
54
  /**
51
55
  * We want all `@docusaurus/*` packages to have the exact same version!
52
56
  * @see https://github.com/facebook/docusaurus/issues/3371
@@ -65,10 +69,10 @@ Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?
65
69
  }
66
70
  });
67
71
  }
68
- async function loadSiteMetadata({ plugins, siteDir, }) {
72
+ function createSiteMetadata({ siteVersion, plugins, }) {
69
73
  const siteMetadata = {
70
74
  docusaurusVersion: utils_1.DOCUSAURUS_VERSION,
71
- siteVersion: await getPackageJsonVersion(path_1.default.join(siteDir, 'package.json')),
75
+ siteVersion,
72
76
  pluginVersions: Object.fromEntries(plugins
73
77
  .filter(({ version: { type } }) => type !== 'synthetic')
74
78
  .map(({ name, version }) => [name, version])),
@@ -76,4 +80,4 @@ async function loadSiteMetadata({ plugins, siteDir, }) {
76
80
  checkDocusaurusPackagesVersion(siteMetadata);
77
81
  return siteMetadata;
78
82
  }
79
- exports.loadSiteMetadata = loadSiteMetadata;
83
+ exports.createSiteMetadata = createSiteMetadata;
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import type { TranslationFileContent, TranslationFile, CodeTranslations, InitializedPlugin } from '@docusaurus/types';
7
+ import type { TranslationFileContent, TranslationFile, CodeTranslations, InitializedPlugin, LoadedPlugin } from '@docusaurus/types';
8
8
  export type WriteTranslationsOptions = {
9
9
  override?: boolean;
10
10
  messagePrefix?: string;
@@ -23,9 +23,16 @@ export declare function localizePluginTranslationFile({ localizationDir, plugin,
23
23
  plugin: InitializedPlugin;
24
24
  translationFile: TranslationFile;
25
25
  }): Promise<TranslationFile>;
26
- export declare function getPluginsDefaultCodeTranslationMessages(plugins: InitializedPlugin[]): Promise<CodeTranslations>;
26
+ export declare function mergeCodeTranslations(codeTranslations: CodeTranslations[]): CodeTranslations;
27
+ export declare function loadPluginsDefaultCodeTranslationMessages(plugins: InitializedPlugin[]): Promise<CodeTranslations>;
28
+ export declare function getPluginsDefaultCodeTranslations({ plugins, }: {
29
+ plugins: LoadedPlugin[];
30
+ }): CodeTranslations;
27
31
  export declare function applyDefaultCodeTranslations({ extractedCodeTranslations, defaultCodeMessages, }: {
28
32
  extractedCodeTranslations: TranslationFileContent;
29
33
  defaultCodeMessages: CodeTranslations;
30
34
  }): TranslationFileContent;
35
+ export declare function loadSiteCodeTranslations({ localizationDir, }: {
36
+ localizationDir: string;
37
+ }): Promise<CodeTranslations>;
31
38
  export {};
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.applyDefaultCodeTranslations = exports.getPluginsDefaultCodeTranslationMessages = exports.localizePluginTranslationFile = exports.writePluginTranslations = exports.writeCodeTranslations = exports.readCodeTranslationFileContent = void 0;
9
+ exports.loadSiteCodeTranslations = exports.applyDefaultCodeTranslations = exports.getPluginsDefaultCodeTranslations = exports.loadPluginsDefaultCodeTranslationMessages = exports.mergeCodeTranslations = exports.localizePluginTranslationFile = exports.writePluginTranslations = exports.writeCodeTranslations = exports.readCodeTranslationFileContent = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const path_1 = tslib_1.__importDefault(require("path"));
12
12
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
@@ -145,11 +145,22 @@ async function localizePluginTranslationFile({ localizationDir, plugin, translat
145
145
  return translationFile;
146
146
  }
147
147
  exports.localizePluginTranslationFile = localizePluginTranslationFile;
148
- async function getPluginsDefaultCodeTranslationMessages(plugins) {
148
+ function mergeCodeTranslations(codeTranslations) {
149
+ return codeTranslations.reduce((allCodeTranslations, current) => ({
150
+ ...allCodeTranslations,
151
+ ...current,
152
+ }), {});
153
+ }
154
+ exports.mergeCodeTranslations = mergeCodeTranslations;
155
+ async function loadPluginsDefaultCodeTranslationMessages(plugins) {
149
156
  const pluginsMessages = await Promise.all(plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}));
150
- return pluginsMessages.reduce((allMessages, pluginMessages) => ({ ...allMessages, ...pluginMessages }), {});
157
+ return mergeCodeTranslations(pluginsMessages);
158
+ }
159
+ exports.loadPluginsDefaultCodeTranslationMessages = loadPluginsDefaultCodeTranslationMessages;
160
+ function getPluginsDefaultCodeTranslations({ plugins, }) {
161
+ return mergeCodeTranslations(plugins.map((p) => p.defaultCodeTranslations));
151
162
  }
152
- exports.getPluginsDefaultCodeTranslationMessages = getPluginsDefaultCodeTranslationMessages;
163
+ exports.getPluginsDefaultCodeTranslations = getPluginsDefaultCodeTranslations;
153
164
  function applyDefaultCodeTranslations({ extractedCodeTranslations, defaultCodeMessages, }) {
154
165
  const unusedDefaultCodeMessages = lodash_1.default.difference(Object.keys(defaultCodeMessages), Object.keys(extractedCodeTranslations));
155
166
  if (unusedDefaultCodeMessages.length > 0) {
@@ -162,3 +173,9 @@ Please report this Docusaurus issue. name=${unusedDefaultCodeMessages}`;
162
173
  }));
163
174
  }
164
175
  exports.applyDefaultCodeTranslations = applyDefaultCodeTranslations;
176
+ async function loadSiteCodeTranslations({ localizationDir, }) {
177
+ const codeTranslationFileContent = (await readCodeTranslationFileContent({ localizationDir })) ?? {};
178
+ // We only need key->message for code translations
179
+ return lodash_1.default.mapValues(codeTranslationFileContent, (value) => value.message);
180
+ }
181
+ exports.loadSiteCodeTranslations = loadSiteCodeTranslations;
@@ -5,6 +5,4 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { Globby } from '@docusaurus/utils';
8
- import type { RouteConfig } from '@docusaurus/types';
9
- export declare function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[];
10
8
  export declare function safeGlobby(patterns: string[], options?: Globby.GlobbyOptions): Promise<string[]>;
@@ -6,18 +6,10 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.safeGlobby = exports.getAllFinalRoutes = void 0;
9
+ exports.safeGlobby = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const path_1 = tslib_1.__importDefault(require("path"));
12
12
  const utils_1 = require("@docusaurus/utils");
13
- // Recursively get the final routes (routes with no subroutes)
14
- function getAllFinalRoutes(routeConfig) {
15
- function getFinalRoutes(route) {
16
- return route.routes ? route.routes.flatMap(getFinalRoutes) : [route];
17
- }
18
- return routeConfig.flatMap(getFinalRoutes);
19
- }
20
- exports.getAllFinalRoutes = getAllFinalRoutes;
21
13
  // Globby that fix Windows path patterns
22
14
  // See https://github.com/facebook/docusaurus/pull/4222#issuecomment-795517329
23
15
  async function safeGlobby(patterns, options) {
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,167 @@
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
+ const source = await utils_1.PerfLogger.async(`Load server bundle`, () => fs_extra_1.default.readFile(serverBundlePath));
31
+ utils_1.PerfLogger.log(`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`);
32
+ const filename = path_1.default.basename(serverBundlePath);
33
+ const globals = {
34
+ // When using "new URL('file.js', import.meta.url)", Webpack will emit
35
+ // __filename, and this plugin will throw. not sure the __filename value
36
+ // has any importance for this plugin, just using an empty string to
37
+ // avoid the error. See https://github.com/facebook/docusaurus/issues/4922
38
+ __filename: '',
39
+ // This uses module.createRequire() instead of very old "require-like" lib
40
+ // See also: https://github.com/pierrec/node-eval/issues/33
41
+ require: (0, module_1.createRequire)(serverBundlePath),
42
+ };
43
+ const serverEntry = await utils_1.PerfLogger.async(`Evaluate server bundle`, () => (0, eval_1.default)(source,
44
+ /* filename: */ filename,
45
+ /* scope: */ globals,
46
+ /* includeGlobals: */ true));
47
+ if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
48
+ throw new Error(`Server bundle export from "${filename}" must be a function that renders the Docusaurus React app.`);
49
+ }
50
+ return serverEntry.default;
51
+ }
52
+ exports.loadAppRenderer = loadAppRenderer;
53
+ function pathnameToFilename({ pathname, trailingSlash, }) {
54
+ const outputFileName = pathname.replace(/^[/\\]/, ''); // Remove leading slashes for webpack-dev-server
55
+ // Paths ending with .html are left untouched
56
+ if (/\.html?$/i.test(outputFileName)) {
57
+ return outputFileName;
58
+ }
59
+ // Legacy retro-compatible behavior
60
+ if (typeof trailingSlash === 'undefined') {
61
+ return path_1.default.join(outputFileName, 'index.html');
62
+ }
63
+ // New behavior: we can say if we prefer file/folder output
64
+ // Useful resource: https://github.com/slorber/trailing-slash-guide
65
+ if (pathname === '' || pathname.endsWith('/') || trailingSlash) {
66
+ return path_1.default.join(outputFileName, 'index.html');
67
+ }
68
+ return `${outputFileName}.html`;
69
+ }
70
+ async function generateStaticFiles({ pathnames, renderer, params, }) {
71
+ // Note that we catch all async errors on purpose
72
+ // Docusaurus presents all the SSG errors to the user, not just the first one
73
+ const results = await (0, p_map_1.default)(pathnames, async (pathname) => generateStaticFile({
74
+ pathname,
75
+ renderer,
76
+ params,
77
+ }).then((result) => ({ pathname, result, error: null }), (error) => ({ pathname, result: null, error: error })), { concurrency: Concurrency });
78
+ const [allSSGErrors, allSSGSuccesses] = lodash_1.default.partition(results, (r) => !!r.error);
79
+ if (allSSGErrors.length > 0) {
80
+ const message = `Docusaurus static site generation failed for ${allSSGErrors.length} path${allSSGErrors.length ? 's' : ''}:\n- ${allSSGErrors
81
+ .map((ssgError) => logger_1.default.path(ssgError.pathname))
82
+ .join('\n- ')}`;
83
+ // Note logging this error properly require using inspect(error,{depth})
84
+ // See https://github.com/nodejs/node/issues/51637
85
+ throw new Error(message, {
86
+ cause: new AggregateError(allSSGErrors.map((ssgError) => ssgError.error)),
87
+ });
88
+ }
89
+ const collectedData = lodash_1.default.chain(allSSGSuccesses)
90
+ .keyBy((success) => success.pathname)
91
+ .mapValues((ssgSuccess) => ssgSuccess.result.collectedData)
92
+ .value();
93
+ return { collectedData };
94
+ }
95
+ exports.generateStaticFiles = generateStaticFiles;
96
+ async function generateStaticFile({ pathname, renderer, params, }) {
97
+ try {
98
+ // This only renders the app HTML
99
+ const result = await renderer({
100
+ pathname,
101
+ });
102
+ // This renders the full page HTML, including head tags...
103
+ const fullPageHtml = (0, templates_1.renderSSRTemplate)({
104
+ params,
105
+ result,
106
+ });
107
+ const content = await minifyHtml(fullPageHtml);
108
+ await writeStaticFile({
109
+ pathname,
110
+ content,
111
+ params,
112
+ });
113
+ return result;
114
+ }
115
+ catch (errorUnknown) {
116
+ const error = errorUnknown;
117
+ const tips = getSSGErrorTips(error);
118
+ const message = logger_1.default.interpolate `Can't render static file for pathname path=${pathname}${tips ? `\n\n${tips}` : ''}`;
119
+ throw new Error(message, {
120
+ cause: error,
121
+ });
122
+ }
123
+ }
124
+ function getSSGErrorTips(error) {
125
+ const parts = [];
126
+ const isNotDefinedErrorRegex = /(?:window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i;
127
+ if (isNotDefinedErrorRegex.test(error.message)) {
128
+ parts.push(`It looks like you are using code that should run on the client-side only.
129
+ To get around it, try using one of:
130
+ - ${logger_1.default.code('<BrowserOnly>')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#browseronly')})
131
+ - ${logger_1.default.code('ExecutionEnvironment')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#executionenvironment')}).
132
+ 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).`);
133
+ }
134
+ return parts.join('\n');
135
+ }
136
+ async function writeStaticFile({ content, pathname, params, }) {
137
+ function removeBaseUrl(p, baseUrl) {
138
+ return baseUrl === '/' ? p : p.replace(new RegExp(`^${baseUrl}`), '/');
139
+ }
140
+ const filename = pathnameToFilename({
141
+ pathname: removeBaseUrl(pathname, params.baseUrl),
142
+ trailingSlash: params.trailingSlash,
143
+ });
144
+ const filePath = path_1.default.join(params.outDir, filename);
145
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
146
+ await fs_extra_1.default.writeFile(filePath, content);
147
+ }
148
+ async function minifyHtml(html) {
149
+ try {
150
+ if (process.env.SKIP_HTML_MINIFICATION === 'true') {
151
+ return html;
152
+ }
153
+ // Minify html with https://github.com/DanielRuf/html-minifier-terser
154
+ return await (0, html_minifier_terser_1.minify)(html, {
155
+ removeComments: false,
156
+ removeRedundantAttributes: true,
157
+ removeEmptyAttributes: true,
158
+ removeScriptTypeAttributes: true,
159
+ removeStyleLinkTypeAttributes: true,
160
+ useShortDoctype: true,
161
+ minifyJS: true,
162
+ });
163
+ }
164
+ catch (err) {
165
+ throw new Error('HTML minification failed', { cause: err });
166
+ }
167
+ }
@@ -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,9 @@
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
+ async: <Result>(label: string, asyncFn: () => Result | Promise<Result>) => Promise<Result>;
7
+ };
8
+ export declare const PerfLogger: PerfLoggerAPI;
9
+ export {};