@docusaurus/core 3.7.0 → 3.8.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.
- package/bin/beforeCli.mjs +3 -3
- package/lib/client/App.js +7 -4
- package/lib/client/serverEntry.js +10 -4
- package/lib/client/serverHelmetUtils.d.ts +11 -0
- package/lib/client/serverHelmetUtils.js +39 -0
- package/lib/client/theme-fallback/ThemeProvider/index.d.ts +9 -0
- package/lib/client/theme-fallback/ThemeProvider/index.js +17 -0
- package/lib/commands/build/buildLocale.js +33 -10
- package/lib/commands/clear.js +4 -3
- package/lib/commands/deploy.js +70 -28
- package/lib/commands/serve.js +2 -2
- package/lib/commands/start/start.js +7 -4
- package/lib/commands/start/utils.js +17 -3
- package/lib/commands/start/webpack.js +1 -1
- package/lib/commands/utils/clearPath.d.ts +10 -0
- package/lib/commands/utils/clearPath.js +21 -0
- package/lib/commands/utils/legacy/evalSourceMapMiddleware.d.ts +2 -0
- package/lib/commands/utils/legacy/evalSourceMapMiddleware.js +57 -0
- package/lib/commands/utils/openBrowser/openBrowser.d.ts +10 -0
- package/lib/commands/utils/openBrowser/openBrowser.js +124 -0
- package/lib/commands/utils/openBrowser/openChrome.applescript +94 -0
- package/lib/server/configValidation.d.ts +3 -1
- package/lib/server/configValidation.js +45 -4
- package/lib/ssg/ssgEnv.d.ts +10 -0
- package/lib/ssg/ssgEnv.js +38 -0
- package/lib/ssg/ssgExecutor.js +121 -8
- package/lib/ssg/ssgGlobalResult.d.ts +12 -0
- package/lib/ssg/ssgGlobalResult.js +74 -0
- package/lib/ssg/ssgParams.d.ts +1 -0
- package/lib/ssg/ssgParams.js +1 -0
- package/lib/ssg/ssgRenderer.d.ts +29 -0
- package/lib/ssg/{ssg.js → ssgRenderer.js} +55 -90
- package/lib/ssg/ssgTemplate.js +6 -7
- package/lib/ssg/ssgUtils.d.ts +0 -1
- package/lib/ssg/ssgUtils.js +0 -9
- package/lib/ssg/ssgWorkerInline.d.ts +12 -0
- package/lib/ssg/ssgWorkerInline.js +17 -0
- package/lib/ssg/ssgWorkerThread.d.ts +13 -0
- package/lib/ssg/ssgWorkerThread.js +36 -0
- package/lib/webpack/base.js +76 -28
- package/lib/webpack/client.js +0 -3
- package/lib/webpack/plugins/BundlerCPUProfilerPlugin.d.ts +12 -0
- package/lib/webpack/plugins/BundlerCPUProfilerPlugin.js +41 -0
- package/package.json +14 -14
- package/lib/ssg/ssg.d.ts +0 -21
- package/lib/webpack/plugins/CleanWebpackPlugin.d.ts +0 -59
- package/lib/webpack/plugins/CleanWebpackPlugin.js +0 -177
package/bin/beforeCli.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import fs from 'fs-extra';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import {createRequire} from 'module';
|
|
13
|
-
import
|
|
13
|
+
import execa from 'execa';
|
|
14
14
|
import {logger} from '@docusaurus/logger';
|
|
15
15
|
import semver from 'semver';
|
|
16
16
|
import updateNotifier from 'update-notifier';
|
|
@@ -111,8 +111,8 @@ export default async function beforeCli() {
|
|
|
111
111
|
return undefined;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
const yarnVersionResult =
|
|
115
|
-
if (yarnVersionResult
|
|
114
|
+
const yarnVersionResult = await execa.command('yarn --version');
|
|
115
|
+
if (yarnVersionResult.exitCode === 0) {
|
|
116
116
|
const majorVersion = parseInt(
|
|
117
117
|
yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
|
|
118
118
|
10,
|
package/lib/client/App.js
CHANGED
|
@@ -10,6 +10,7 @@ import routes from '@generated/routes';
|
|
|
10
10
|
import { useLocation } from '@docusaurus/router';
|
|
11
11
|
import renderRoutes from '@docusaurus/renderRoutes';
|
|
12
12
|
import Root from '@theme/Root';
|
|
13
|
+
import ThemeProvider from '@theme/ThemeProvider';
|
|
13
14
|
import SiteMetadata from '@theme/SiteMetadata';
|
|
14
15
|
import normalizeLocation from './normalizeLocation';
|
|
15
16
|
import { BrowserContextProvider } from './browserContext';
|
|
@@ -34,10 +35,12 @@ export default function App() {
|
|
|
34
35
|
<DocusaurusContextProvider>
|
|
35
36
|
<BrowserContextProvider>
|
|
36
37
|
<Root>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
<ThemeProvider>
|
|
39
|
+
<SiteMetadataDefaults />
|
|
40
|
+
<SiteMetadata />
|
|
41
|
+
<BaseUrlIssueBanner />
|
|
42
|
+
<AppNavigation />
|
|
43
|
+
</ThemeProvider>
|
|
41
44
|
</Root>
|
|
42
45
|
<HasHydratedDataAttribute />
|
|
43
46
|
</BrowserContextProvider>
|
|
@@ -12,7 +12,8 @@ import { renderToHtml } from './renderToHtml';
|
|
|
12
12
|
import preload from './preload';
|
|
13
13
|
import App from './App';
|
|
14
14
|
import { createStatefulBrokenLinks, BrokenLinksProvider, } from './BrokenLinksContext';
|
|
15
|
-
|
|
15
|
+
import { toPageCollectedMetadataInternal } from './serverHelmetUtils';
|
|
16
|
+
const render = async ({ pathname, v4RemoveLegacyPostBuildHeadAttribute, }) => {
|
|
16
17
|
await preload(pathname);
|
|
17
18
|
const modules = new Set();
|
|
18
19
|
const routerContext = {};
|
|
@@ -30,10 +31,15 @@ const render = async ({ pathname }) => {
|
|
|
30
31
|
</HelmetProvider>
|
|
31
32
|
</Loadable.Capture>);
|
|
32
33
|
const html = await renderToHtml(app);
|
|
34
|
+
const { helmet } = helmetContext;
|
|
35
|
+
const metadata = toPageCollectedMetadataInternal({ helmet });
|
|
36
|
+
// TODO Docusaurus v4 remove with deprecated postBuild({head}) API
|
|
37
|
+
// the returned collectedData must be serializable to run in workers
|
|
38
|
+
if (v4RemoveLegacyPostBuildHeadAttribute) {
|
|
39
|
+
metadata.helmet = null;
|
|
40
|
+
}
|
|
33
41
|
const collectedData = {
|
|
34
|
-
|
|
35
|
-
// this makes it impossible to run SSG in a worker thread
|
|
36
|
-
helmet: helmetContext.helmet,
|
|
42
|
+
metadata,
|
|
37
43
|
anchors: statefulBrokenLinks.getCollectedAnchors(),
|
|
38
44
|
links: statefulBrokenLinks.getCollectedLinks(),
|
|
39
45
|
modules: Array.from(modules),
|
|
@@ -0,0 +1,11 @@
|
|
|
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 { PageCollectedMetadataInternal } from '../common';
|
|
8
|
+
import type { HelmetServerState } from 'react-helmet-async';
|
|
9
|
+
export declare function toPageCollectedMetadataInternal({ helmet, }: {
|
|
10
|
+
helmet: HelmetServerState;
|
|
11
|
+
}): PageCollectedMetadataInternal;
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
function getBuildMetaTags(helmet) {
|
|
8
|
+
// @ts-expect-error: see https://github.com/staylor/react-helmet-async/pull/167
|
|
9
|
+
const metaElements = helmet.meta.toComponent() ?? [];
|
|
10
|
+
return metaElements.map((el) => el.props);
|
|
11
|
+
}
|
|
12
|
+
function isNoIndexTag(tag) {
|
|
13
|
+
if (!tag.name || !tag.content) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return (
|
|
17
|
+
// meta name is not case-sensitive
|
|
18
|
+
tag.name.toLowerCase() === 'robots' &&
|
|
19
|
+
// Robots directives are not case-sensitive
|
|
20
|
+
tag.content.toLowerCase().includes('noindex'));
|
|
21
|
+
}
|
|
22
|
+
export function toPageCollectedMetadataInternal({ helmet, }) {
|
|
23
|
+
const tags = getBuildMetaTags(helmet);
|
|
24
|
+
const noIndex = tags.some(isNoIndexTag);
|
|
25
|
+
return {
|
|
26
|
+
helmet, // TODO Docusaurus v4 remove
|
|
27
|
+
public: {
|
|
28
|
+
noIndex,
|
|
29
|
+
},
|
|
30
|
+
internal: {
|
|
31
|
+
htmlAttributes: helmet.htmlAttributes.toString(),
|
|
32
|
+
bodyAttributes: helmet.bodyAttributes.toString(),
|
|
33
|
+
title: helmet.title.toString(),
|
|
34
|
+
meta: helmet.meta.toString(),
|
|
35
|
+
link: helmet.link.toString(),
|
|
36
|
+
script: helmet.script.toString(),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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 ReactNode } from 'react';
|
|
8
|
+
import type { Props } from '@theme/ThemeProvider';
|
|
9
|
+
export default function ThemeProvider({ children }: Props): ReactNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
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 React from 'react';
|
|
8
|
+
// Wrapper component expected to be implemented by a theme
|
|
9
|
+
// Unlike <Layout>, it applies to all sites routes and never unmounts
|
|
10
|
+
//
|
|
11
|
+
// Unlike <Root>, the theme is expected to provide an implementation
|
|
12
|
+
// <Root> is empty and the implementation is expected to be provided by the user
|
|
13
|
+
//
|
|
14
|
+
// Tree order: Root > ThemeProvider > Layout
|
|
15
|
+
export default function ThemeProvider({ children }) {
|
|
16
|
+
return <>{children}</>;
|
|
17
|
+
}
|
|
@@ -19,6 +19,9 @@ const client_1 = require("../../webpack/client");
|
|
|
19
19
|
const server_1 = tslib_1.__importDefault(require("../../webpack/server"));
|
|
20
20
|
const configure_1 = require("../../webpack/configure");
|
|
21
21
|
const ssgExecutor_1 = require("../../ssg/ssgExecutor");
|
|
22
|
+
const clearPath_1 = tslib_1.__importDefault(require("../utils/clearPath"));
|
|
23
|
+
const SkipBundling = process.env.DOCUSAURUS_SKIP_BUNDLING === 'true';
|
|
24
|
+
const ExitAfterBundling = process.env.DOCUSAURUS_EXIT_AFTER_BUNDLING === 'true';
|
|
22
25
|
async function buildLocale({ siteDir, locale, cliOptions, }) {
|
|
23
26
|
// Temporary workaround to unlock the ability to translate the site config
|
|
24
27
|
// We'll remove it if a better official API can be designed
|
|
@@ -47,16 +50,32 @@ async function buildLocale({ siteDir, locale, cliOptions, }) {
|
|
|
47
50
|
props,
|
|
48
51
|
configureWebpackUtils,
|
|
49
52
|
}),
|
|
53
|
+
// We also clear website/build dir
|
|
54
|
+
// returns void, no useful result needed before compilation
|
|
55
|
+
// See also https://github.com/facebook/docusaurus/pull/11037
|
|
56
|
+
SkipBundling ? undefined : (0, clearPath_1.default)(outDir),
|
|
50
57
|
]));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
currentBundler: configureWebpackUtils.currentBundler,
|
|
58
|
+
if (SkipBundling) {
|
|
59
|
+
console.warn(`Skipping the Docusaurus bundling step because DOCUSAURUS_SKIP_BUNDLING='true'`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const cleanupBundlerTracing = await (0, bundler_1.registerBundlerTracing)({
|
|
63
|
+
currentBundler: props.currentBundler,
|
|
58
64
|
});
|
|
59
|
-
|
|
65
|
+
// Run webpack to build JS bundle (client) and static html files (server).
|
|
66
|
+
await logger_1.PerfLogger.async(`Bundling with ${props.currentBundler.name}`, () => {
|
|
67
|
+
return (0, bundler_1.compile)({
|
|
68
|
+
configs:
|
|
69
|
+
// For hash router we don't do SSG and can skip the server bundle
|
|
70
|
+
router === 'hash' ? [clientConfig] : [clientConfig, serverConfig],
|
|
71
|
+
currentBundler: configureWebpackUtils.currentBundler,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
await cleanupBundlerTracing();
|
|
75
|
+
}
|
|
76
|
+
if (ExitAfterBundling) {
|
|
77
|
+
return process.exit(0);
|
|
78
|
+
}
|
|
60
79
|
const { collectedData } = await logger_1.PerfLogger.async('SSG', () => (0, ssgExecutor_1.executeSSG)({
|
|
61
80
|
props,
|
|
62
81
|
serverBundlePath,
|
|
@@ -71,7 +90,10 @@ async function buildLocale({ siteDir, locale, cliOptions, }) {
|
|
|
71
90
|
logger_1.default.success `Generated static files in path=${path_1.default.relative(process.cwd(), outDir)}.`;
|
|
72
91
|
}
|
|
73
92
|
async function executePluginsPostBuild({ plugins, props, collectedData, }) {
|
|
74
|
-
const head =
|
|
93
|
+
const head = props.siteConfig.future.v4.removeLegacyPostBuildHeadAttribute
|
|
94
|
+
? {}
|
|
95
|
+
: lodash_1.default.mapValues(collectedData, (d) => d.metadata.helmet);
|
|
96
|
+
const routesBuildMetadata = lodash_1.default.mapValues(collectedData, (d) => d.metadata.public);
|
|
75
97
|
await Promise.all(plugins.map(async (plugin) => {
|
|
76
98
|
if (!plugin.postBuild) {
|
|
77
99
|
return;
|
|
@@ -79,6 +101,7 @@ async function executePluginsPostBuild({ plugins, props, collectedData, }) {
|
|
|
79
101
|
await plugin.postBuild({
|
|
80
102
|
...props,
|
|
81
103
|
head,
|
|
104
|
+
routesBuildMetadata,
|
|
82
105
|
content: plugin.content,
|
|
83
106
|
});
|
|
84
107
|
}));
|
|
@@ -131,7 +154,7 @@ async function getBuildServerConfig({ props, configureWebpackUtils, }) {
|
|
|
131
154
|
// Remove /build/server server.bundle.js because it is not needed.
|
|
132
155
|
async function cleanupServerBundle(serverBundlePath) {
|
|
133
156
|
if (process.env.DOCUSAURUS_KEEP_SERVER_BUNDLE === 'true') {
|
|
134
|
-
logger_1.default.warn("Will NOT delete server bundle because DOCUSAURUS_KEEP_SERVER_BUNDLE
|
|
157
|
+
logger_1.default.warn("Will NOT delete server bundle because DOCUSAURUS_KEEP_SERVER_BUNDLE='true'");
|
|
135
158
|
}
|
|
136
159
|
else {
|
|
137
160
|
await logger_1.PerfLogger.async('Deleting server bundle', async () => {
|
package/lib/commands/clear.js
CHANGED
|
@@ -12,13 +12,14 @@ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
|
12
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
13
13
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
14
14
|
const utils_1 = require("@docusaurus/utils");
|
|
15
|
+
const clearPath_1 = tslib_1.__importDefault(require("./utils/clearPath"));
|
|
15
16
|
async function removePath(entry) {
|
|
16
17
|
if (!(await fs_extra_1.default.pathExists(entry.path))) {
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
try {
|
|
20
|
-
await
|
|
21
|
-
logger_1.default.success `Removed the ${entry.description} at path=${entry.path}.`;
|
|
21
|
+
await (0, clearPath_1.default)(entry.path);
|
|
22
|
+
logger_1.default.success `Removed the ${entry.description} at path=${path_1.default.relative(process.cwd(), entry.path)}.`;
|
|
22
23
|
}
|
|
23
24
|
catch (err) {
|
|
24
25
|
logger_1.default.error `Could not remove the ${entry.description} at path=${entry.path}.`;
|
|
@@ -38,7 +39,7 @@ async function clear(siteDirParam = '.') {
|
|
|
38
39
|
// In Yarn PnP, cache is stored in `.yarn/.cache` because n_m doesn't exist
|
|
39
40
|
const cacheFolders = ['node_modules', '.yarn'].map((p) => ({
|
|
40
41
|
path: path_1.default.join(siteDir, p, '.cache'),
|
|
41
|
-
description: '
|
|
42
|
+
description: 'bundler persistent cache folder',
|
|
42
43
|
}));
|
|
43
44
|
await Promise.all([generatedFolder, buildFolder, ...cacheFolders].map(removePath));
|
|
44
45
|
}
|
package/lib/commands/deploy.js
CHANGED
|
@@ -12,7 +12,7 @@ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
|
12
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
13
13
|
const os_1 = tslib_1.__importDefault(require("os"));
|
|
14
14
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
15
|
-
const
|
|
15
|
+
const execa_1 = tslib_1.__importDefault(require("execa"));
|
|
16
16
|
const utils_1 = require("@docusaurus/utils");
|
|
17
17
|
const site_1 = require("../server/site");
|
|
18
18
|
const build_1 = require("./build/build");
|
|
@@ -21,19 +21,41 @@ function obfuscateGitPass(str) {
|
|
|
21
21
|
const gitPass = process.env.GIT_PASS;
|
|
22
22
|
return gitPass ? str.replace(gitPass, 'GIT_PASS') : str;
|
|
23
23
|
}
|
|
24
|
+
const debugMode = !!process.env.DOCUSAURUS_DEPLOY_DEBUG;
|
|
24
25
|
// Log executed commands so that user can figure out mistakes on his own
|
|
25
26
|
// for example: https://github.com/facebook/docusaurus/issues/3875
|
|
26
|
-
function
|
|
27
|
+
function exec(cmd, options) {
|
|
28
|
+
const log = options?.log ?? true;
|
|
29
|
+
const failfast = options?.failfast ?? false;
|
|
27
30
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
// TODO migrate to execa(file,[...args]) instead
|
|
32
|
+
// Use async/await everything
|
|
33
|
+
// Avoid execa.command: the args need to be escaped manually
|
|
34
|
+
const result = execa_1.default.commandSync(cmd);
|
|
35
|
+
if (log || debugMode) {
|
|
36
|
+
logger_1.default.info `code=${obfuscateGitPass(cmd)} subdue=${`code: ${result.exitCode}`}`;
|
|
37
|
+
}
|
|
38
|
+
if (debugMode) {
|
|
39
|
+
console.log(result);
|
|
40
|
+
}
|
|
41
|
+
if (failfast && result.exitCode !== 0) {
|
|
42
|
+
throw new Error(`Command returned unexpected exitCode ${result.exitCode}`);
|
|
43
|
+
}
|
|
30
44
|
return result;
|
|
31
45
|
}
|
|
32
46
|
catch (err) {
|
|
33
|
-
logger_1.default.
|
|
34
|
-
|
|
47
|
+
throw new Error(logger_1.default.interpolate `Error while executing command code=${obfuscateGitPass(cmd)}
|
|
48
|
+
In CWD code=${process.cwd()}`, { cause: err });
|
|
35
49
|
}
|
|
36
50
|
}
|
|
51
|
+
// Execa escape args and add necessary quotes automatically
|
|
52
|
+
// When using Execa.command, the args containing spaces must be escaped manually
|
|
53
|
+
function escapeArg(arg) {
|
|
54
|
+
return arg.replaceAll(' ', '\\ ');
|
|
55
|
+
}
|
|
56
|
+
function hasGit() {
|
|
57
|
+
return exec('git --version').exitCode === 0;
|
|
58
|
+
}
|
|
37
59
|
async function deploy(siteDirParam = '.', cliOptions = {}) {
|
|
38
60
|
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
|
|
39
61
|
const { outDir, siteConfig, siteConfigPath } = await (0, site_1.loadContext)({
|
|
@@ -48,16 +70,23 @@ This behavior can have SEO impacts and create relative link issues.
|
|
|
48
70
|
`);
|
|
49
71
|
}
|
|
50
72
|
logger_1.default.info('Deploy command invoked...');
|
|
51
|
-
if (!
|
|
52
|
-
throw new Error('Git not installed or
|
|
73
|
+
if (!hasGit()) {
|
|
74
|
+
throw new Error('Git not installed or not added to PATH!');
|
|
53
75
|
}
|
|
54
76
|
// Source repo is the repo from where the command is invoked
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
const { stdout } = exec('git remote get-url origin', {
|
|
78
|
+
log: false,
|
|
79
|
+
failfast: true,
|
|
80
|
+
});
|
|
81
|
+
const sourceRepoUrl = stdout.trim();
|
|
58
82
|
// The source branch; defaults to the currently checked out branch
|
|
59
83
|
const sourceBranch = process.env.CURRENT_BRANCH ??
|
|
60
|
-
|
|
84
|
+
exec('git rev-parse --abbrev-ref HEAD', {
|
|
85
|
+
log: false,
|
|
86
|
+
failfast: true,
|
|
87
|
+
})
|
|
88
|
+
?.stdout?.toString()
|
|
89
|
+
.trim();
|
|
61
90
|
const gitUser = process.env.GIT_USER;
|
|
62
91
|
let useSSH = process.env.USE_SSH !== undefined &&
|
|
63
92
|
process.env.USE_SSH.toLowerCase() === 'true';
|
|
@@ -87,8 +116,11 @@ This behavior can have SEO impacts and create relative link issues.
|
|
|
87
116
|
// We never deploy on pull request.
|
|
88
117
|
const isPullRequest = process.env.CI_PULL_REQUEST ?? process.env.CIRCLE_PULL_REQUEST;
|
|
89
118
|
if (isPullRequest) {
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
exec('echo "Skipping deploy on a pull request."', {
|
|
120
|
+
log: false,
|
|
121
|
+
failfast: true,
|
|
122
|
+
});
|
|
123
|
+
process.exit(0);
|
|
92
124
|
}
|
|
93
125
|
// github.io indicates organization repos that deploy via default branch. All
|
|
94
126
|
// others use gh-pages (either case can be configured actually, but we can
|
|
@@ -126,21 +158,24 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
|
|
|
126
158
|
}
|
|
127
159
|
// Save the commit hash that triggers publish-gh-pages before checking
|
|
128
160
|
// out to deployment branch.
|
|
129
|
-
const currentCommit =
|
|
161
|
+
const currentCommit = exec('git rev-parse HEAD')?.stdout?.toString().trim();
|
|
130
162
|
const runDeploy = async (outputDirectory) => {
|
|
131
163
|
const targetDirectory = cliOptions.targetDir ?? '.';
|
|
132
164
|
const fromPath = outputDirectory;
|
|
133
165
|
const toPath = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), `${projectName}-${deploymentBranch}`));
|
|
134
|
-
|
|
166
|
+
process.chdir(toPath);
|
|
135
167
|
// Clones the repo into the temp folder and checks out the target branch.
|
|
136
168
|
// If the branch doesn't exist, it creates a new one based on the
|
|
137
169
|
// repository default branch.
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
170
|
+
if (exec(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} ${escapeArg(toPath)}`).exitCode !== 0) {
|
|
171
|
+
exec(`git clone --depth 1 ${deploymentRepoURL} ${escapeArg(toPath)}`);
|
|
172
|
+
exec(`git checkout -b ${deploymentBranch}`);
|
|
141
173
|
}
|
|
142
174
|
// Clear out any existing contents in the target directory
|
|
143
|
-
|
|
175
|
+
exec(`git rm -rf ${escapeArg(targetDirectory)}`, {
|
|
176
|
+
log: false,
|
|
177
|
+
failfast: true,
|
|
178
|
+
});
|
|
144
179
|
const targetPath = path_1.default.join(toPath, targetDirectory);
|
|
145
180
|
try {
|
|
146
181
|
await fs_extra_1.default.copy(fromPath, targetPath);
|
|
@@ -149,22 +184,24 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
|
|
|
149
184
|
logger_1.default.error `Copying build assets from path=${fromPath} to path=${targetPath} failed.`;
|
|
150
185
|
throw err;
|
|
151
186
|
}
|
|
152
|
-
|
|
187
|
+
exec('git add --all', { failfast: true });
|
|
153
188
|
const gitUserName = process.env.GIT_USER_NAME;
|
|
154
189
|
if (gitUserName) {
|
|
155
|
-
|
|
190
|
+
exec(`git config user.name ${escapeArg(gitUserName)}`, { failfast: true });
|
|
156
191
|
}
|
|
157
192
|
const gitUserEmail = process.env.GIT_USER_EMAIL;
|
|
158
193
|
if (gitUserEmail) {
|
|
159
|
-
|
|
194
|
+
exec(`git config user.email ${escapeArg(gitUserEmail)}`, {
|
|
195
|
+
failfast: true,
|
|
196
|
+
});
|
|
160
197
|
}
|
|
161
198
|
const commitMessage = process.env.CUSTOM_COMMIT_MESSAGE ??
|
|
162
199
|
`Deploy website - based on ${currentCommit}`;
|
|
163
|
-
const commitResults =
|
|
164
|
-
if (
|
|
200
|
+
const commitResults = exec(`git commit -m ${escapeArg(commitMessage)} --allow-empty`);
|
|
201
|
+
if (exec(`git push --force origin ${deploymentBranch}`).exitCode !== 0) {
|
|
165
202
|
throw new Error('Running "git push" command failed. Does the GitHub user account you are using have push access to the repository?');
|
|
166
203
|
}
|
|
167
|
-
else if (commitResults.
|
|
204
|
+
else if (commitResults.exitCode === 0) {
|
|
168
205
|
// The commit might return a non-zero value when site is up to date.
|
|
169
206
|
let websiteURL = '';
|
|
170
207
|
if (githubHost === 'github.com') {
|
|
@@ -176,8 +213,13 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
|
|
|
176
213
|
// GitHub enterprise hosting.
|
|
177
214
|
websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
|
|
178
215
|
}
|
|
179
|
-
|
|
180
|
-
|
|
216
|
+
try {
|
|
217
|
+
exec(`echo "Website is live at ${websiteURL}."`, { failfast: true });
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
throw new Error(`Failed to execute command: ${err}`);
|
|
222
|
+
}
|
|
181
223
|
}
|
|
182
224
|
};
|
|
183
225
|
if (!cliOptions.skipBuild) {
|
package/lib/commands/serve.js
CHANGED
|
@@ -14,8 +14,8 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|
|
14
14
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
15
15
|
const utils_1 = require("@docusaurus/utils");
|
|
16
16
|
const serve_handler_1 = tslib_1.__importDefault(require("serve-handler"));
|
|
17
|
-
const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
|
|
18
17
|
const utils_common_1 = require("@docusaurus/utils-common");
|
|
18
|
+
const openBrowser_1 = tslib_1.__importDefault(require("./utils/openBrowser/openBrowser"));
|
|
19
19
|
const config_1 = require("../server/config");
|
|
20
20
|
const build_1 = require("./build/build");
|
|
21
21
|
const getHostPort_1 = require("../server/getHostPort");
|
|
@@ -86,6 +86,6 @@ async function serve(siteDirParam = '.', cliOptions = {}) {
|
|
|
86
86
|
logger_1.default.success `Serving path=${buildDir} directory at: url=${url}`;
|
|
87
87
|
server.listen(port);
|
|
88
88
|
if (cliOptions.open && !process.env.CI) {
|
|
89
|
-
(0, openBrowser_1.default)(url);
|
|
89
|
+
await (0, openBrowser_1.default)(url);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.start = start;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
|
-
const logger_1 = tslib_1.
|
|
12
|
-
const openBrowser_1 = tslib_1.__importDefault(require("
|
|
11
|
+
const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
|
|
12
|
+
const openBrowser_1 = tslib_1.__importDefault(require("../utils/openBrowser/openBrowser"));
|
|
13
13
|
const watcher_1 = require("./watcher");
|
|
14
14
|
const webpack_1 = require("./webpack");
|
|
15
15
|
const utils_1 = require("./utils");
|
|
16
|
-
async function
|
|
16
|
+
async function doStart(siteDirParam = '.', cliOptions = {}) {
|
|
17
17
|
logger_1.default.info('Starting the development server...');
|
|
18
18
|
// Temporary workaround to unlock the ability to translate the site config
|
|
19
19
|
// We'll remove it if a better official API can be designed
|
|
@@ -41,6 +41,9 @@ async function start(siteDirParam = '.', cliOptions = {}) {
|
|
|
41
41
|
});
|
|
42
42
|
await devServer.start();
|
|
43
43
|
if (cliOptions.open) {
|
|
44
|
-
(0, openBrowser_1.default)(reloadableSite.getOpenUrl());
|
|
44
|
+
await (0, openBrowser_1.default)(reloadableSite.getOpenUrl());
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
async function start(siteDirParam = '.', cliOptions = {}) {
|
|
48
|
+
return logger_1.PerfLogger.async('CLI start', () => doStart(siteDirParam, cliOptions));
|
|
49
|
+
}
|
|
@@ -10,13 +10,28 @@ exports.createOpenUrlContext = createOpenUrlContext;
|
|
|
10
10
|
exports.createReloadableSite = createReloadableSite;
|
|
11
11
|
const tslib_1 = require("tslib");
|
|
12
12
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
13
|
+
const url_1 = tslib_1.__importDefault(require("url"));
|
|
13
14
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
14
|
-
const WebpackDevServerUtils_1 = require("react-dev-utils/WebpackDevServerUtils");
|
|
15
15
|
const utils_1 = require("@docusaurus/utils");
|
|
16
16
|
const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
|
|
17
17
|
const getHostPort_1 = require("../../server/getHostPort");
|
|
18
18
|
const site_1 = require("../../server/site");
|
|
19
19
|
const pluginsUtils_1 = require("../../server/plugins/pluginsUtils");
|
|
20
|
+
// This code was historically in CRA/react-dev-utils (deprecated in 2025)
|
|
21
|
+
// We internalized it, refactored and removed useless code paths
|
|
22
|
+
// See https://github.com/facebook/docusaurus/pull/10956
|
|
23
|
+
// See https://github.com/facebook/create-react-app/blob/main/packages/react-dev-utils/WebpackDevServerUtils.js
|
|
24
|
+
function getOpenUrlOrigin(protocol, host, port) {
|
|
25
|
+
const isUnspecifiedHost = host === '0.0.0.0' || host === '::';
|
|
26
|
+
const prettyHost = isUnspecifiedHost ? 'localhost' : host;
|
|
27
|
+
const localUrlForBrowser = url_1.default.format({
|
|
28
|
+
protocol,
|
|
29
|
+
hostname: prettyHost,
|
|
30
|
+
port,
|
|
31
|
+
pathname: '/',
|
|
32
|
+
});
|
|
33
|
+
return localUrlForBrowser;
|
|
34
|
+
}
|
|
20
35
|
async function createOpenUrlContext({ cliOptions, }) {
|
|
21
36
|
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
|
22
37
|
const { host, port } = await (0, getHostPort_1.getHostPort)(cliOptions);
|
|
@@ -24,9 +39,8 @@ async function createOpenUrlContext({ cliOptions, }) {
|
|
|
24
39
|
return process.exit();
|
|
25
40
|
}
|
|
26
41
|
const getOpenUrl = ({ baseUrl, router }) => {
|
|
27
|
-
const urls = (0, WebpackDevServerUtils_1.prepareUrls)(protocol, host, port);
|
|
28
42
|
return (0, utils_1.normalizeUrl)([
|
|
29
|
-
|
|
43
|
+
getOpenUrlOrigin(protocol, host, port),
|
|
30
44
|
router === 'hash' ? '/#/' : '',
|
|
31
45
|
baseUrl,
|
|
32
46
|
]);
|
|
@@ -13,7 +13,7 @@ const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
|
|
|
13
13
|
const bundler_1 = require("@docusaurus/bundler");
|
|
14
14
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
15
15
|
const webpack_dev_server_1 = tslib_1.__importDefault(require("webpack-dev-server"));
|
|
16
|
-
const evalSourceMapMiddleware_1 = tslib_1.__importDefault(require("
|
|
16
|
+
const evalSourceMapMiddleware_1 = tslib_1.__importDefault(require("../utils/legacy/evalSourceMapMiddleware"));
|
|
17
17
|
const watcher_1 = require("./watcher");
|
|
18
18
|
const getHttpsConfig_1 = tslib_1.__importDefault(require("../../webpack/utils/getHttpsConfig"));
|
|
19
19
|
const configure_1 = require("../../webpack/configure");
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* @param pathToClear
|
|
9
|
+
*/
|
|
10
|
+
export default function clearPath(pathToClear: string): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
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.default = clearPath;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
|
+
const promises_1 = require("fs/promises");
|
|
13
|
+
const logger_1 = require("@docusaurus/logger");
|
|
14
|
+
/**
|
|
15
|
+
* @param pathToClear
|
|
16
|
+
*/
|
|
17
|
+
async function clearPath(pathToClear) {
|
|
18
|
+
return logger_1.PerfLogger.async(`clearPath ${path_1.default.relative(process.cwd(), pathToClear)}`, async () => {
|
|
19
|
+
await (0, promises_1.rm)(pathToClear, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
|
|
8
|
+
// TODO Legacy CRA react-dev-utils package code
|
|
9
|
+
// This code was in CRA/react-dev-utils (deprecated in 2025)
|
|
10
|
+
// We just copied the code as-is to remove a fat/useless dependency subtree
|
|
11
|
+
// See https://github.com/facebook/docusaurus/pull/10956
|
|
12
|
+
// See https://github.com/facebook/create-react-app/blob/main/packages/react-dev-utils/evalSourceMapMiddleware.js
|
|
13
|
+
|
|
14
|
+
/* eslint-disable */
|
|
15
|
+
|
|
16
|
+
function base64SourceMap(source) {
|
|
17
|
+
const base64 = Buffer.from(JSON.stringify(source.map()), 'utf8').toString(
|
|
18
|
+
'base64',
|
|
19
|
+
);
|
|
20
|
+
return `data:application/json;charset=utf-8;base64,${base64}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getSourceById(server, id) {
|
|
24
|
+
const module = Array.from(server._stats.compilation.modules).find(
|
|
25
|
+
(m) => server._stats.compilation.chunkGraph.getModuleId(m) == id,
|
|
26
|
+
);
|
|
27
|
+
return module.originalSource();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Middleware responsible for retrieving a generated source
|
|
32
|
+
* Receives a webpack internal url: "webpack-internal:///<module-id>"
|
|
33
|
+
* Returns a generated source: "<source-text><sourceMappingURL><sourceURL>"
|
|
34
|
+
*
|
|
35
|
+
* Based on EvalSourceMapDevToolModuleTemplatePlugin.js
|
|
36
|
+
*
|
|
37
|
+
* @param {import("webpack-dev-server").default} server
|
|
38
|
+
* @returns {import("express").Handler}
|
|
39
|
+
*/
|
|
40
|
+
module.exports = function createEvalSourceMapMiddleware(server) {
|
|
41
|
+
return function handleWebpackInternalMiddleware(req, res, next) {
|
|
42
|
+
if (req.url.startsWith('/__get-internal-source')) {
|
|
43
|
+
const fileName = req.query.fileName;
|
|
44
|
+
const id = fileName.match(/webpack-internal:\/\/\/(.+)/)[1];
|
|
45
|
+
if (!id || !server._stats) {
|
|
46
|
+
next();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const source = getSourceById(server, id);
|
|
50
|
+
const sourceMapURL = `//# sourceMappingURL=${base64SourceMap(source)}`;
|
|
51
|
+
const sourceURL = `//# sourceURL=webpack-internal:///${module.id}`;
|
|
52
|
+
res.end(`${source.source()}\n${sourceMapURL}\n${sourceURL}`);
|
|
53
|
+
} else {
|
|
54
|
+
next();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* Returns true if it opened a browser
|
|
9
|
+
*/
|
|
10
|
+
export default function openBrowser(url: string): Promise<boolean>;
|