From 4a145bc459ca4e4582ca3cfd311851845f15f8f4 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 13:03:53 +0200 Subject: [PATCH 01/25] upgrade webpackbar to v6 --- packages/docusaurus-plugin-pwa/src/deps.d.ts | 15 ------- packages/docusaurus/src/deps.d.ts | 9 ---- yarn.lock | 47 ++++++++++++-------- 3 files changed, 29 insertions(+), 42 deletions(-) delete mode 100644 packages/docusaurus-plugin-pwa/src/deps.d.ts diff --git a/packages/docusaurus-plugin-pwa/src/deps.d.ts b/packages/docusaurus-plugin-pwa/src/deps.d.ts deleted file mode 100644 index 104a45712162..000000000000 --- a/packages/docusaurus-plugin-pwa/src/deps.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// TODO incompatible declaration file: https://github.com/unjs/webpackbar/pull/108 -declare module 'webpackbar' { - import webpack from 'webpack'; - - export default class WebpackBarPlugin extends webpack.ProgressPlugin { - constructor(options: {name: string; color?: string}); - } -} diff --git a/packages/docusaurus/src/deps.d.ts b/packages/docusaurus/src/deps.d.ts index 4cf9a6df98b9..e14603a3de88 100644 --- a/packages/docusaurus/src/deps.d.ts +++ b/packages/docusaurus/src/deps.d.ts @@ -38,15 +38,6 @@ declare module 'webpack/lib/HotModuleReplacementPlugin' { export default HotModuleReplacementPlugin; } -// TODO incompatible declaration file: https://github.com/unjs/webpackbar/pull/108 -declare module 'webpackbar' { - import webpack from 'webpack'; - - export default class WebpackBarPlugin extends webpack.ProgressPlugin { - constructor(options: {name: string; color?: string}); - } -} - // TODO incompatible declaration file declare module 'eta' { export const defaultConfig: object; diff --git a/yarn.lock b/yarn.lock index b3dbc59bea61..ea49e96a6f0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4337,7 +4337,7 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -5695,10 +5695,10 @@ connect@3.7.0: parseurl "~1.3.3" utils-merge "1.0.1" -consola@^2.15.3: - version "2.15.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" - integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +consola@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" + integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== console-control-strings@^1.1.0: version "1.1.0" @@ -7900,7 +7900,7 @@ feed@^4.2.2: dependencies: xml-js "^1.6.11" -figures@3.2.0, figures@^3.0.0: +figures@3.2.0, figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -11043,6 +11043,13 @@ markdown-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== +markdown-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" + integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== + dependencies: + repeat-string "^1.0.0" + markdown-table@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" @@ -14734,7 +14741,7 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" -repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== @@ -15666,10 +15673,10 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -std-env@^3.0.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe" - integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== +std-env@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== streamx@^2.15.0: version "2.15.0" @@ -17203,15 +17210,19 @@ webpack@^5, webpack@^5.88.1: watchpack "^2.4.1" webpack-sources "^3.2.3" -webpackbar@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" - integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== +webpackbar@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-6.0.1.tgz#5ef57d3bf7ced8b19025477bc7496ea9d502076b" + integrity sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q== dependencies: - chalk "^4.1.0" - consola "^2.15.3" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + consola "^3.2.3" + figures "^3.2.0" + markdown-table "^2.0.0" pretty-time "^1.1.0" - std-env "^3.0.1" + std-env "^3.7.0" + wrap-ansi "^7.0.0" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" From b4e71c1fcb96fb24b42312f2c236000d23976b05 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 13:23:06 +0200 Subject: [PATCH 02/25] improve dynamic bundler architecture --- packages/docusaurus-plugin-pwa/package.json | 2 +- packages/docusaurus-plugin-pwa/src/index.ts | 15 ++++++++---- packages/docusaurus-types/src/context.d.ts | 6 +++++ packages/docusaurus/package.json | 2 +- packages/docusaurus/src/commands/build.ts | 20 +++++++++------- packages/docusaurus/src/server/site.ts | 8 +++++++ .../src/webpack/__tests__/base.test.ts | 2 ++ packages/docusaurus/src/webpack/base.ts | 2 +- packages/docusaurus/src/webpack/client.ts | 15 ++++++------ .../docusaurus/src/webpack/currentBundler.ts | 23 +++++++++++++++++++ .../plugins/StaticDirectoriesCopyPlugin.ts | 6 ++--- packages/docusaurus/src/webpack/server.ts | 9 +++++--- packages/docusaurus/src/webpack/utils.ts | 13 ++++++++--- 13 files changed, 90 insertions(+), 33 deletions(-) diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index 39f2c9296ef9..a5d020d4a337 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -36,7 +36,7 @@ "tslib": "^2.6.0", "webpack": "^5.88.1", "webpack-merge": "^5.9.0", - "webpackbar": "^5.0.2", + "webpackbar": "^6.0.1", "workbox-build": "^7.0.0", "workbox-precaching": "^7.0.0", "workbox-window": "^7.0.0" diff --git a/packages/docusaurus-plugin-pwa/src/index.ts b/packages/docusaurus-plugin-pwa/src/index.ts index 0fcc59f0a184..5a89f67a2514 100644 --- a/packages/docusaurus-plugin-pwa/src/index.ts +++ b/packages/docusaurus-plugin-pwa/src/index.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import webpack, {type Configuration} from 'webpack'; +import {type Configuration} from 'webpack'; import WebpackBar from 'webpackbar'; import Terser from 'terser-webpack-plugin'; import {injectManifest} from 'workbox-build'; @@ -89,10 +89,10 @@ export default function pluginPWA( }); }, - configureWebpack(config) { + configureWebpack(config, isServer, {currentBundler}) { return { plugins: [ - new webpack.EnvironmentPlugin( + new currentBundler.instance.EnvironmentPlugin( // See https://github.com/facebook/docusaurus/pull/10455#issuecomment-2317593528 // See https://github.com/webpack/webpack/commit/adf2a6b7c6077fd806ea0e378c1450cccecc9ed0#r145989788 // @ts-expect-error: bad Webpack type? @@ -162,10 +162,12 @@ export default function pluginPWA( ], }, plugins: [ - new webpack.EnvironmentPlugin({ + new props.currentBundler.instance.EnvironmentPlugin({ // Fallback value required with Webpack 5 PWA_SW_CUSTOM: swCustom ?? '', }), + + // TODO fix progress bar plugin new WebpackBar({ name: 'Service Worker', color: 'red', @@ -182,7 +184,10 @@ export default function pluginPWA( }, }; - await compile([swWebpackConfig]); + await compile({ + configs: [swWebpackConfig], + currentBundler: props.currentBundler, + }); const swDest = path.resolve(props.outDir, 'sw.js'); diff --git a/packages/docusaurus-types/src/context.d.ts b/packages/docusaurus-types/src/context.d.ts index e399c5342191..98c08a4656ae 100644 --- a/packages/docusaurus-types/src/context.d.ts +++ b/packages/docusaurus-types/src/context.d.ts @@ -8,6 +8,7 @@ import type {DocusaurusConfig} from './config'; import type {CodeTranslations, I18n} from './i18n'; import type {LoadedPlugin, PluginVersionInformation} from './plugin'; import type {PluginRouteConfig} from './routing'; +import type {CurrentBundler} from './bundler'; export type DocusaurusContext = { siteConfig: DocusaurusConfig; @@ -74,6 +75,11 @@ export type LoadContext = { * Defines the default browser storage behavior for a site */ siteStorage: SiteStorage; + + /** + * The bundler used to build the site (Webpack or Rspack) + */ + currentBundler: CurrentBundler; }; export type Props = LoadContext & { diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 72bdb1e9376b..e031aabaa241 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -101,7 +101,7 @@ "webpack-bundle-analyzer": "^4.9.0", "webpack-dev-server": "^4.15.1", "webpack-merge": "^5.9.0", - "webpackbar": "^5.0.2" + "webpackbar": "^6.0.1" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.5.2", diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index f06eba8c0c0a..27a1a4ae2fb8 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -174,7 +174,7 @@ async function buildLocale({ // We can build the 2 configs in parallel const [{clientConfig, clientManifestPath}, {serverConfig, serverBundlePath}] = - await PerfLogger.async('Creating webpack configs', () => + await PerfLogger.async('Creating bundler configs', () => Promise.all([ getBuildClientConfig({ props, @@ -189,13 +189,17 @@ async function buildLocale({ ); // Run webpack to build JS bundle (client) and static html files (server). - await PerfLogger.async('Bundling with Webpack', () => { - if (router === 'hash') { - return compile([clientConfig]); - } else { - return compile([clientConfig, serverConfig]); - } - }); + await PerfLogger.async( + `Bundling with ${configureWebpackUtils.currentBundler.name}`, + () => { + return compile({ + configs: + // For hash router we don't do SSG and can skip the server bundle + router === 'hash' ? [clientConfig] : [clientConfig, serverConfig], + currentBundler: configureWebpackUtils.currentBundler, + }); + }, + ); const {collectedData} = await PerfLogger.async('SSG', () => executeSSG({ diff --git a/packages/docusaurus/src/server/site.ts b/packages/docusaurus/src/server/site.ts index 5e6cb99856d2..046755dfc616 100644 --- a/packages/docusaurus/src/server/site.ts +++ b/packages/docusaurus/src/server/site.ts @@ -26,6 +26,7 @@ import { import {generateSiteFiles} from './codegen/codegen'; import {getRoutesPaths, handleDuplicateRoutes} from './routes'; import {createSiteStorage} from './storage'; +import {getCurrentBundler} from '../webpack/currentBundler'; import type {LoadPluginsResult} from './plugins/plugins'; import type { DocusaurusConfig, @@ -88,6 +89,10 @@ export async function loadContext( }), }); + const currentBundler = await getCurrentBundler({ + siteConfig: initialSiteConfig, + }); + const i18n = await loadI18n(initialSiteConfig, {locale}); const baseUrl = localizePath({ @@ -126,6 +131,7 @@ export async function loadContext( baseUrl, i18n, codeTranslations, + currentBundler, }; } @@ -145,6 +151,7 @@ function createSiteProps( i18n, localizationDir, codeTranslations: siteCodeTranslations, + currentBundler, } = context; const {headTags, preBodyTags, postBodyTags} = loadHtmlTags({ @@ -181,6 +188,7 @@ function createSiteProps( preBodyTags, postBodyTags, codeTranslations, + currentBundler, }; } diff --git a/packages/docusaurus/src/webpack/__tests__/base.test.ts b/packages/docusaurus/src/webpack/__tests__/base.test.ts index aa8ec0fb6814..61302a0cf02d 100644 --- a/packages/docusaurus/src/webpack/__tests__/base.test.ts +++ b/packages/docusaurus/src/webpack/__tests__/base.test.ts @@ -8,6 +8,7 @@ import {jest} from '@jest/globals'; import path from 'path'; import _ from 'lodash'; +import webpack from 'webpack'; import * as utils from '@docusaurus/utils/lib/webpackUtils'; import {posixPath} from '@docusaurus/utils'; import {excludeJS, clientDir, createBaseConfig} from '../base'; @@ -87,6 +88,7 @@ describe('base webpack config', () => { siteMetadata: { docusaurusVersion: '2.0.0-alpha.70', }, + currentBundler: {name: 'webpack', instance: webpack}, plugins: [ { getThemePath() { diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 14f053cdf086..645c5e48c7dd 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -91,7 +91,7 @@ export async function createBaseConfig({ const createJsLoader = await createJsLoaderFactory({siteConfig}); const CSSExtractPlugin = await getCSSExtractPlugin({ - currentBundler: configureWebpackUtils.currentBundler, + currentBundler: props.currentBundler, }); return { diff --git a/packages/docusaurus/src/webpack/client.ts b/packages/docusaurus/src/webpack/client.ts index dca201849d0a..16e106258e49 100644 --- a/packages/docusaurus/src/webpack/client.ts +++ b/packages/docusaurus/src/webpack/client.ts @@ -7,8 +7,6 @@ import path from 'path'; import merge from 'webpack-merge'; -import WebpackBar from 'webpackbar'; -import webpack from 'webpack'; import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; import ReactLoadableSSRAddon from 'react-loadable-ssr-addon-v5-slorber'; import HtmlWebpackPlugin from 'html-webpack-plugin'; @@ -17,6 +15,7 @@ import ChunkAssetPlugin from './plugins/ChunkAssetPlugin'; import CleanWebpackPlugin from './plugins/CleanWebpackPlugin'; import ForceTerminatePlugin from './plugins/ForceTerminatePlugin'; import {createStaticDirectoriesCopyPlugin} from './plugins/StaticDirectoriesCopyPlugin'; +import {getProgressBarPlugin} from './currentBundler'; import type { ConfigureWebpackUtils, FasterConfig, @@ -45,6 +44,10 @@ async function createBaseClientConfig({ configureWebpackUtils, }); + const ProgressBarPlugin = await getProgressBarPlugin({ + currentBundler: configureWebpackUtils.currentBundler, + }); + return merge(baseConfig, { // Useless, disabled on purpose (errors on existing sites with no // browserslist config) @@ -56,17 +59,15 @@ async function createBaseClientConfig({ runtimeChunk: true, }, plugins: [ - new webpack.DefinePlugin({ + new props.currentBundler.instance.DefinePlugin({ 'process.env.HYDRATE_CLIENT_ENTRY': JSON.stringify(hydrate), }), new ChunkAssetPlugin(), - // Show compilation progress bar and build time. - new WebpackBar({ + new ProgressBarPlugin({ name: 'Client', }), await createStaticDirectoriesCopyPlugin({ props, - currentBundler: configureWebpackUtils.currentBundler, }), ].filter(Boolean), }); @@ -88,7 +89,7 @@ export async function createStartClientConfig({ }): Promise<{clientConfig: Configuration}> { const {siteConfig, headTags, preBodyTags, postBodyTags} = props; - const clientConfig: webpack.Configuration = merge( + const clientConfig = merge( await createBaseClientConfig({ props, minify, diff --git a/packages/docusaurus/src/webpack/currentBundler.ts b/packages/docusaurus/src/webpack/currentBundler.ts index d0089aeb05a0..2005dea60e67 100644 --- a/packages/docusaurus/src/webpack/currentBundler.ts +++ b/packages/docusaurus/src/webpack/currentBundler.ts @@ -6,6 +6,7 @@ */ import webpack from 'webpack'; +import WebpackBar from 'webpackbar'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import CopyWebpackPlugin from 'copy-webpack-plugin'; import logger from '@docusaurus/logger'; @@ -64,3 +65,25 @@ export async function getCopyPlugin({ // https://github.com/webpack-contrib/copy-webpack-plugin return CopyWebpackPlugin; } + +export async function getProgressBarPlugin({ + currentBundler, +}: { + currentBundler: CurrentBundler; +}): Promise { + if (currentBundler.name === 'rspack') { + class CustomRspackProgressPlugin extends currentBundler.instance + .ProgressPlugin { + constructor({name}: {name: string}) { + // TODO add support for color + // Unfortunately the rspack.ProgressPlugin does not have a name option + // See https://rspack.dev/plugins/webpack/progress-plugin + // @ts-expect-error: adapt Rspack ProgressPlugin constructor + super({prefix: name}); + } + } + return CustomRspackProgressPlugin as typeof WebpackBar; + } + + return WebpackBar; +} diff --git a/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts b/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts index 297d1bc99190..641d9fae8f73 100644 --- a/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts +++ b/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts @@ -8,18 +8,16 @@ import path from 'path'; import fs from 'fs-extra'; import {getCopyPlugin} from '../currentBundler'; -import type {CurrentBundler, Props} from '@docusaurus/types'; +import type {Props} from '@docusaurus/types'; import type {WebpackPluginInstance} from 'webpack'; export async function createStaticDirectoriesCopyPlugin({ props, - currentBundler, }: { props: Props; - currentBundler: CurrentBundler; }): Promise { const CopyPlugin = await getCopyPlugin({ - currentBundler, + currentBundler: props.currentBundler, }); const { diff --git a/packages/docusaurus/src/webpack/server.ts b/packages/docusaurus/src/webpack/server.ts index e8df2b485c23..1bfda5f2fab7 100644 --- a/packages/docusaurus/src/webpack/server.ts +++ b/packages/docusaurus/src/webpack/server.ts @@ -8,7 +8,7 @@ import path from 'path'; import merge from 'webpack-merge'; import {NODE_MAJOR_VERSION, NODE_MINOR_VERSION} from '@docusaurus/utils'; -import WebpackBar from 'webpackbar'; +import {getProgressBarPlugin} from './currentBundler'; import {createBaseConfig} from './base'; import type {ConfigureWebpackUtils, Props} from '@docusaurus/types'; import type {Configuration} from 'webpack'; @@ -28,6 +28,10 @@ export default async function createServerConfig({ configureWebpackUtils, }); + const ProgressBarPlugin = await getProgressBarPlugin({ + currentBundler: props.currentBundler, + }); + const outputFilename = 'server.bundle.js'; const outputDir = path.join(props.outDir, '__server'); const serverBundlePath = path.join(outputDir, outputFilename); @@ -43,8 +47,7 @@ export default async function createServerConfig({ libraryTarget: 'commonjs2', }, plugins: [ - // Show compilation progress bar. - new WebpackBar({ + new ProgressBarPlugin({ name: 'Server', color: 'yellow', }), diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 8711be52c544..5b72a5d04c73 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -10,10 +10,11 @@ import path from 'path'; import crypto from 'crypto'; import logger from '@docusaurus/logger'; import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; -import webpack, {type Configuration} from 'webpack'; +import {type Configuration} from 'webpack'; import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; import {importSwcJsLoaderFactory} from '../faster'; import {getCSSExtractPlugin} from './currentBundler'; +import type webpack from 'webpack'; import type { ConfigureWebpackUtils, CurrentBundler, @@ -202,9 +203,15 @@ declare global { } } -export function compile(config: Configuration[]): Promise { +export function compile({ + configs, + currentBundler, +}: { + configs: Configuration[]; + currentBundler: CurrentBundler; +}): Promise { return new Promise((resolve, reject) => { - const compiler = webpack(config); + const compiler = currentBundler.instance(configs); compiler.run((err, stats) => { if (err) { logger.error(err.stack ?? err); From bd00c5def2c7a531ea8c7527bf4a090d905cb5b4 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 13:25:18 +0200 Subject: [PATCH 03/25] cleanup ContextReplacementPlugin import mess --- packages/docusaurus-theme-classic/src/index.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/index.ts b/packages/docusaurus-theme-classic/src/index.ts index 9ffb4e3e87db..568fd070c6de 100644 --- a/packages/docusaurus-theme-classic/src/index.ts +++ b/packages/docusaurus-theme-classic/src/index.ts @@ -6,7 +6,6 @@ */ import path from 'path'; -import {createRequire} from 'module'; import rtlcss from 'rtlcss'; import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations'; import {getTranslationFiles, translateThemeConfig} from './translations'; @@ -19,14 +18,6 @@ import type {LoadContext, Plugin} from '@docusaurus/types'; import type {ThemeConfig} from '@docusaurus/theme-common'; import type {Plugin as PostCssPlugin} from 'postcss'; import type {PluginOptions} from '@docusaurus/theme-classic'; -import type webpack from 'webpack'; - -const requireFromDocusaurusCore = createRequire( - require.resolve('@docusaurus/core/package.json'), -); -const ContextReplacementPlugin = requireFromDocusaurusCore( - 'webpack/lib/ContextReplacementPlugin', -) as typeof webpack.ContextReplacementPlugin; function getInfimaCSSFile(direction: string) { return `infima/dist/css/default/default${ @@ -89,7 +80,7 @@ export default function themeClassic( return modules; }, - configureWebpack() { + configureWebpack(__config, __isServer, {currentBundler}) { const prismLanguages = additionalLanguages .map((lang) => `prism-${lang}`) .join('|'); @@ -99,7 +90,7 @@ export default function themeClassic( // This allows better optimization by only bundling those components // that the user actually needs, because the modules are dynamically // required and can't be known during compile time. - new ContextReplacementPlugin( + new currentBundler.instance.ContextReplacementPlugin( /prismjs[\\/]components$/, new RegExp(`^./(${prismLanguages})$`), ), From 45c112668f7895d9c2931f874f1536956ec31e38 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 13:44:37 +0200 Subject: [PATCH 04/25] extract @docusaurus/bundler package --- packages/docusaurus-bundler/.npmignore | 3 + packages/docusaurus-bundler/README.md | 3 + packages/docusaurus-bundler/package.json | 31 ++++++++ .../src/index.ts} | 0 packages/docusaurus-bundler/tsconfig.json | 10 +++ packages/docusaurus-plugin-pwa/package.json | 2 +- packages/docusaurus-plugin-pwa/src/index.ts | 10 ++- packages/docusaurus/package.json | 6 +- .../__tests__/__snapshots__/site.test.ts.snap | 4 + packages/docusaurus/src/server/site.ts | 2 +- packages/docusaurus/src/webpack/base.ts | 2 +- packages/docusaurus/src/webpack/client.ts | 2 +- packages/docusaurus/src/webpack/configure.ts | 2 +- .../plugins/StaticDirectoriesCopyPlugin.ts | 2 +- packages/docusaurus/src/webpack/server.ts | 2 +- packages/docusaurus/src/webpack/utils.ts | 2 +- yarn.lock | 79 +++++++++++-------- 17 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 packages/docusaurus-bundler/.npmignore create mode 100644 packages/docusaurus-bundler/README.md create mode 100644 packages/docusaurus-bundler/package.json rename packages/{docusaurus/src/webpack/currentBundler.ts => docusaurus-bundler/src/index.ts} (100%) create mode 100644 packages/docusaurus-bundler/tsconfig.json diff --git a/packages/docusaurus-bundler/.npmignore b/packages/docusaurus-bundler/.npmignore new file mode 100644 index 000000000000..03c9ae1e1b54 --- /dev/null +++ b/packages/docusaurus-bundler/.npmignore @@ -0,0 +1,3 @@ +.tsbuildinfo* +tsconfig* +__tests__ diff --git a/packages/docusaurus-bundler/README.md b/packages/docusaurus-bundler/README.md new file mode 100644 index 000000000000..c63782702b61 --- /dev/null +++ b/packages/docusaurus-bundler/README.md @@ -0,0 +1,3 @@ +# `@docusaurus/bundler` + +Docusaurus util package to abstract the current bundler. diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json new file mode 100644 index 000000000000..47b59ba580ab --- /dev/null +++ b/packages/docusaurus-bundler/package.json @@ -0,0 +1,31 @@ +{ + "name": "@docusaurus/bundler", + "version": "3.5.2", + "description": "Docusaurus util package to abstract the current bundler.", + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "scripts": { + "build": "tsc", + "watch": "tsc --watch" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/facebook/docusaurus.git", + "directory": "packages/docusaurus-utils" + }, + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "3.5.2", + "@docusaurus/types": "3.5.2", + "copy-webpack-plugin": "^12.0.2", + "mini-css-extract-plugin": "^2.9.1", + "webpack": "^5.88.1", + "webpackbar": "^6.0.1" + }, + "engines": { + "node": ">=18.0" + } +} diff --git a/packages/docusaurus/src/webpack/currentBundler.ts b/packages/docusaurus-bundler/src/index.ts similarity index 100% rename from packages/docusaurus/src/webpack/currentBundler.ts rename to packages/docusaurus-bundler/src/index.ts diff --git a/packages/docusaurus-bundler/tsconfig.json b/packages/docusaurus-bundler/tsconfig.json new file mode 100644 index 000000000000..74731e2257e1 --- /dev/null +++ b/packages/docusaurus-bundler/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": false, + "sourceMap": true, + "declarationMap": true + }, + "include": ["src"], + "exclude": ["**/__tests__/**"] +} diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index a5d020d4a337..67e3152316fc 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -22,6 +22,7 @@ "dependencies": { "@babel/core": "^7.23.3", "@babel/preset-env": "^7.23.3", + "@docusaurus/bundler": "3.5.2", "@docusaurus/core": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/theme-common": "3.5.2", @@ -36,7 +37,6 @@ "tslib": "^2.6.0", "webpack": "^5.88.1", "webpack-merge": "^5.9.0", - "webpackbar": "^6.0.1", "workbox-build": "^7.0.0", "workbox-precaching": "^7.0.0", "workbox-window": "^7.0.0" diff --git a/packages/docusaurus-plugin-pwa/src/index.ts b/packages/docusaurus-plugin-pwa/src/index.ts index 5a89f67a2514..bb2114fab0d6 100644 --- a/packages/docusaurus-plugin-pwa/src/index.ts +++ b/packages/docusaurus-plugin-pwa/src/index.ts @@ -7,7 +7,7 @@ import path from 'path'; import {type Configuration} from 'webpack'; -import WebpackBar from 'webpackbar'; +import {getProgressBarPlugin} from '@docusaurus/bundler'; import Terser from 'terser-webpack-plugin'; import {injectManifest} from 'workbox-build'; import {normalizeUrl} from '@docusaurus/utils'; @@ -139,6 +139,10 @@ export default function pluginPWA( async postBuild(props) { const swSourceFileTest = /\.m?js$/; + const ProgressBarPlugin = await getProgressBarPlugin({ + currentBundler: props.currentBundler, + }); + const swWebpackConfig: Configuration = { entry: require.resolve('./sw.js'), output: { @@ -166,9 +170,7 @@ export default function pluginPWA( // Fallback value required with Webpack 5 PWA_SW_CUSTOM: swCustom ?? '', }), - - // TODO fix progress bar plugin - new WebpackBar({ + new ProgressBarPlugin({ name: 'Service Worker', color: 'red', }), diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index e031aabaa241..73bb03497c66 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -43,6 +43,7 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", + "@docusaurus/bundler": "3.5.2", "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/mdx-loader": "3.5.2", @@ -59,7 +60,6 @@ "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", - "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.1", @@ -76,7 +76,6 @@ "html-webpack-plugin": "^5.5.3", "leven": "^3.1.0", "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.7.6", "null-loader": "^4.0.1", "p-map": "^4.0.0", "postcss": "^8.4.26", @@ -100,8 +99,7 @@ "webpack": "^5.88.1", "webpack-bundle-analyzer": "^4.9.0", "webpack-dev-server": "^4.15.1", - "webpack-merge": "^5.9.0", - "webpackbar": "^6.0.1" + "webpack-merge": "^5.9.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.5.2", diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap index 06fa00daab7d..2c61404bd204 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/site.test.ts.snap @@ -4,6 +4,10 @@ exports[`load loads props for site with custom i18n path 1`] = ` { "baseUrl": "/", "codeTranslations": {}, + "currentBundler": { + "instance": [Function], + "name": "webpack", + }, "generatedFilesDir": "/packages/docusaurus/src/server/__tests__/__fixtures__/custom-i18n-site/.docusaurus", "headTags": "", "i18n": { diff --git a/packages/docusaurus/src/server/site.ts b/packages/docusaurus/src/server/site.ts index 046755dfc616..fdb5610a6d3b 100644 --- a/packages/docusaurus/src/server/site.ts +++ b/packages/docusaurus/src/server/site.ts @@ -13,6 +13,7 @@ import { } from '@docusaurus/utils'; import {PerfLogger} from '@docusaurus/logger'; import combinePromises from 'combine-promises'; +import {getCurrentBundler} from '@docusaurus/bundler'; import {loadSiteConfig} from './config'; import {getAllClientModules} from './clientModules'; import {loadPlugins, reloadPlugin} from './plugins/plugins'; @@ -26,7 +27,6 @@ import { import {generateSiteFiles} from './codegen/codegen'; import {getRoutesPaths, handleDuplicateRoutes} from './routes'; import {createSiteStorage} from './storage'; -import {getCurrentBundler} from '../webpack/currentBundler'; import type {LoadPluginsResult} from './plugins/plugins'; import type { DocusaurusConfig, diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 645c5e48c7dd..7888590abfba 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -7,11 +7,11 @@ import fs from 'fs-extra'; import path from 'path'; +import {getCSSExtractPlugin} from '@docusaurus/bundler'; import {md5Hash, getFileLoaderUtils} from '@docusaurus/utils'; import {createJsLoaderFactory, getCustomBabelConfigFilePath} from './utils'; import {getMinimizers} from './minification'; import {loadThemeAliases, loadDocusaurusAliases} from './aliases'; -import {getCSSExtractPlugin} from './currentBundler'; import type {Configuration} from 'webpack'; import type { ConfigureWebpackUtils, diff --git a/packages/docusaurus/src/webpack/client.ts b/packages/docusaurus/src/webpack/client.ts index 16e106258e49..6ac4c5de2cb3 100644 --- a/packages/docusaurus/src/webpack/client.ts +++ b/packages/docusaurus/src/webpack/client.ts @@ -10,12 +10,12 @@ import merge from 'webpack-merge'; import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; import ReactLoadableSSRAddon from 'react-loadable-ssr-addon-v5-slorber'; import HtmlWebpackPlugin from 'html-webpack-plugin'; +import {getProgressBarPlugin} from '@docusaurus/bundler'; import {createBaseConfig} from './base'; import ChunkAssetPlugin from './plugins/ChunkAssetPlugin'; import CleanWebpackPlugin from './plugins/CleanWebpackPlugin'; import ForceTerminatePlugin from './plugins/ForceTerminatePlugin'; import {createStaticDirectoriesCopyPlugin} from './plugins/StaticDirectoriesCopyPlugin'; -import {getProgressBarPlugin} from './currentBundler'; import type { ConfigureWebpackUtils, FasterConfig, diff --git a/packages/docusaurus/src/webpack/configure.ts b/packages/docusaurus/src/webpack/configure.ts index 2f50bfbe05ce..b95ae42c9453 100644 --- a/packages/docusaurus/src/webpack/configure.ts +++ b/packages/docusaurus/src/webpack/configure.ts @@ -10,8 +10,8 @@ import { customizeArray, customizeObject, } from 'webpack-merge'; +import {getCurrentBundler} from '@docusaurus/bundler'; import {createJsLoaderFactory, createStyleLoadersFactory} from './utils'; -import {getCurrentBundler} from './currentBundler'; import type {Configuration, RuleSetRule} from 'webpack'; import type { Plugin, diff --git a/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts b/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts index 641d9fae8f73..3e5ec8ddb78a 100644 --- a/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts +++ b/packages/docusaurus/src/webpack/plugins/StaticDirectoriesCopyPlugin.ts @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs-extra'; -import {getCopyPlugin} from '../currentBundler'; +import {getCopyPlugin} from '@docusaurus/bundler'; import type {Props} from '@docusaurus/types'; import type {WebpackPluginInstance} from 'webpack'; diff --git a/packages/docusaurus/src/webpack/server.ts b/packages/docusaurus/src/webpack/server.ts index 1bfda5f2fab7..205d157a1323 100644 --- a/packages/docusaurus/src/webpack/server.ts +++ b/packages/docusaurus/src/webpack/server.ts @@ -8,7 +8,7 @@ import path from 'path'; import merge from 'webpack-merge'; import {NODE_MAJOR_VERSION, NODE_MINOR_VERSION} from '@docusaurus/utils'; -import {getProgressBarPlugin} from './currentBundler'; +import {getProgressBarPlugin} from '@docusaurus/bundler'; import {createBaseConfig} from './base'; import type {ConfigureWebpackUtils, Props} from '@docusaurus/types'; import type {Configuration} from 'webpack'; diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 5b72a5d04c73..f7f8f7626cfd 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -8,12 +8,12 @@ import fs from 'fs-extra'; import path from 'path'; import crypto from 'crypto'; +import {getCSSExtractPlugin} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; import {type Configuration} from 'webpack'; import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; import {importSwcJsLoaderFactory} from '../faster'; -import {getCSSExtractPlugin} from './currentBundler'; import type webpack from 'webpack'; import type { ConfigureWebpackUtils, diff --git a/yarn.lock b/yarn.lock index ea49e96a6f0e..8f4465fa4993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2823,6 +2823,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.5.0.tgz#70af51f38ef3b624eb08eb02a2ed70f117fa19d7" integrity sha512-3rO1QIz6mL0MvFVTOxqhDJRVsLfG/vK2VSlKKPghALA6FhJqU7L+RUHnFvH5BP5HhkWiMQqq514i9ZFTcqoGCQ== +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + "@sinonjs/commons@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" @@ -5847,17 +5852,17 @@ copy-text-to-clipboard@^3.2.0: resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz#0202b2d9bdae30a49a53f898626dcc3b49ad960b" integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== -copy-webpack-plugin@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" - integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== +copy-webpack-plugin@^12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz#935e57b8e6183c82f95bd937df658a59f6a2da28" + integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== dependencies: - fast-glob "^3.2.11" + fast-glob "^3.3.2" glob-parent "^6.0.1" - globby "^13.1.1" + globby "^14.0.0" normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" core-js-compat@^3.31.0, core-js-compat@^3.33.1, core-js-compat@^3.36.1: version "3.37.0" @@ -7832,7 +7837,7 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: +fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.1, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -8526,16 +8531,17 @@ globby@11.1.0, globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.1: - version "13.2.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" - integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== +globby@^14.0.0: + version "14.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.2.tgz#06554a54ccfe9264e5a9ff8eded46aa1e306482f" + integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== dependencies: - dir-glob "^3.0.1" - fast-glob "^3.3.0" + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" ignore "^5.2.4" - merge2 "^1.4.1" - slash "^4.0.0" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" globjoin@^0.1.4: version "0.1.4" @@ -12081,12 +12087,13 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-css-extract-plugin@^2.7.6: - version "2.7.6" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d" - integrity sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw== +mini-css-extract-plugin@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz#4d184f12ce90582e983ccef0f6f9db637b4be758" + integrity sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ== dependencies: schema-utils "^4.0.0" + tapable "^2.2.1" minimalistic-assert@^1.0.0: version "1.0.1" @@ -13388,6 +13395,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -15029,7 +15041,7 @@ schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0, schema-utils@^4.0.1: +schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== @@ -15154,10 +15166,10 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -15394,10 +15406,10 @@ slash@3.0.0, slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== slice-ansi@^3.0.0: version "3.0.0" @@ -16068,7 +16080,7 @@ table@^6.8.1: string-width "^4.2.3" strip-ansi "^6.0.1" -tapable@2.2.1, tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: +tapable@2.2.1, tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== @@ -16639,6 +16651,11 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + unified@^11.0.0, unified@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.3.tgz#e141be0fe466a2d28b2160f62712bc9cbc08fdd4" From db5d4cedee1f73a6f8cf6ed07787609b541c5d22 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 15:51:56 +0200 Subject: [PATCH 05/25] revert copy-webpack-plugin version --- packages/docusaurus-bundler/package.json | 2 +- yarn.lock | 62 +++++++++--------------- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 47b59ba580ab..96d330c0b7b2 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -20,7 +20,7 @@ "dependencies": { "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", - "copy-webpack-plugin": "^12.0.2", + "copy-webpack-plugin": "^11.0.0", "mini-css-extract-plugin": "^2.9.1", "webpack": "^5.88.1", "webpackbar": "^6.0.1" diff --git a/yarn.lock b/yarn.lock index 8f4465fa4993..ee0b0d70694e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2823,11 +2823,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.5.0.tgz#70af51f38ef3b624eb08eb02a2ed70f117fa19d7" integrity sha512-3rO1QIz6mL0MvFVTOxqhDJRVsLfG/vK2VSlKKPghALA6FhJqU7L+RUHnFvH5BP5HhkWiMQqq514i9ZFTcqoGCQ== -"@sindresorhus/merge-streams@^2.1.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" - integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== - "@sinonjs/commons@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" @@ -5852,17 +5847,17 @@ copy-text-to-clipboard@^3.2.0: resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz#0202b2d9bdae30a49a53f898626dcc3b49ad960b" integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== -copy-webpack-plugin@^12.0.2: - version "12.0.2" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz#935e57b8e6183c82f95bd937df658a59f6a2da28" - integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== dependencies: - fast-glob "^3.3.2" + fast-glob "^3.2.11" glob-parent "^6.0.1" - globby "^14.0.0" + globby "^13.1.1" normalize-path "^3.0.0" - schema-utils "^4.2.0" - serialize-javascript "^6.0.2" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" core-js-compat@^3.31.0, core-js-compat@^3.33.1, core-js-compat@^3.36.1: version "3.37.0" @@ -7837,7 +7832,7 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.1, fast-glob@^3.3.2: +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -8531,17 +8526,16 @@ globby@11.1.0, globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^14.0.0: - version "14.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.2.tgz#06554a54ccfe9264e5a9ff8eded46aa1e306482f" - integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== +globby@^13.1.1: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== dependencies: - "@sindresorhus/merge-streams" "^2.1.0" - fast-glob "^3.3.2" + dir-glob "^3.0.1" + fast-glob "^3.3.0" ignore "^5.2.4" - path-type "^5.0.0" - slash "^5.1.0" - unicorn-magic "^0.1.0" + merge2 "^1.4.1" + slash "^4.0.0" globjoin@^0.1.4: version "0.1.4" @@ -13395,11 +13389,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path-type@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" - integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -15041,7 +15030,7 @@ schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.2.0: +schema-utils@^4.0.0, schema-utils@^4.0.1: version "4.2.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== @@ -15166,7 +15155,7 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== @@ -15406,10 +15395,10 @@ slash@3.0.0, slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" - integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== slice-ansi@^3.0.0: version "3.0.0" @@ -16651,11 +16640,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -unicorn-magic@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" - integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== - unified@^11.0.0, unified@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.3.tgz#e141be0fe466a2d28b2160f62712bc9cbc08fdd4" From b7584cc7112ce72f2b4365eaacd534be54d88f52 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 16:07:02 +0200 Subject: [PATCH 06/25] package.json typo --- packages/docusaurus-bundler/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 96d330c0b7b2..6284576b8d2a 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -14,7 +14,7 @@ "repository": { "type": "git", "url": "https://github.com/facebook/docusaurus.git", - "directory": "packages/docusaurus-utils" + "directory": "packages/docusaurus-bundler" }, "license": "MIT", "dependencies": { From bb48dd896ff5842dcd081d7bd813cf7bb1004444 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 16:09:18 +0200 Subject: [PATCH 07/25] add tslib --- packages/docusaurus-bundler/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 6284576b8d2a..f637d12c5ab3 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -22,6 +22,7 @@ "@docusaurus/types": "3.5.2", "copy-webpack-plugin": "^11.0.0", "mini-css-extract-plugin": "^2.9.1", + "tslib": "^2.6.0", "webpack": "^5.88.1", "webpackbar": "^6.0.1" }, From 8dac80f0a7fbc95b1ae2132d08735da12016885a Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 16:41:51 +0200 Subject: [PATCH 08/25] move minimizers to bundler package --- packages/docusaurus-bundler/package.json | 3 + .../docusaurus-bundler/src/currentBundler.ts | 89 +++++++++++++++++++ .../src/faster.ts | 0 packages/docusaurus-bundler/src/index.ts | 88 ++---------------- .../src}/minification.ts | 22 ++++- packages/docusaurus-plugin-pwa/package.json | 1 - packages/docusaurus-plugin-pwa/src/index.ts | 12 ++- packages/docusaurus/package.json | 1 - packages/docusaurus/src/webpack/base.ts | 7 +- packages/docusaurus/src/webpack/utils.ts | 6 +- 10 files changed, 132 insertions(+), 97 deletions(-) create mode 100644 packages/docusaurus-bundler/src/currentBundler.ts rename packages/{docusaurus => docusaurus-bundler}/src/faster.ts (100%) rename packages/{docusaurus/src/webpack => docusaurus-bundler/src}/minification.ts (84%) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index f637d12c5ab3..74d2aa38ce89 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -18,10 +18,13 @@ }, "license": "MIT", "dependencies": { + "@docusaurus/faster": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", "copy-webpack-plugin": "^11.0.0", + "css-minimizer-webpack-plugin": "^5.0.1", "mini-css-extract-plugin": "^2.9.1", + "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "webpack": "^5.88.1", "webpackbar": "^6.0.1" diff --git a/packages/docusaurus-bundler/src/currentBundler.ts b/packages/docusaurus-bundler/src/currentBundler.ts new file mode 100644 index 000000000000..2005dea60e67 --- /dev/null +++ b/packages/docusaurus-bundler/src/currentBundler.ts @@ -0,0 +1,89 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import webpack from 'webpack'; +import WebpackBar from 'webpackbar'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import logger from '@docusaurus/logger'; +import type {CurrentBundler, DocusaurusConfig} from '@docusaurus/types'; + +// We inject a site config slice because the Rspack flag might change place +type SiteConfigSlice = { + future: { + experimental_faster: Pick< + DocusaurusConfig['future']['experimental_faster'], + 'rspackBundler' + >; + }; +}; + +function isRspack(siteConfig: SiteConfigSlice): boolean { + return siteConfig.future.experimental_faster.rspackBundler; +} + +export async function getCurrentBundler({ + siteConfig, +}: { + siteConfig: SiteConfigSlice; +}): Promise { + if (isRspack(siteConfig)) { + // TODO add support for Rspack + logger.error( + 'Rspack bundler is not supported yet, will use Webpack instead', + ); + } + return { + name: 'webpack', + instance: webpack, + }; +} + +export async function getCSSExtractPlugin({ + currentBundler, +}: { + currentBundler: CurrentBundler; +}): Promise { + if (currentBundler.name === 'rspack') { + throw new Error('Rspack bundler is not supported yet'); + } + return MiniCssExtractPlugin; +} + +export async function getCopyPlugin({ + currentBundler, +}: { + currentBundler: CurrentBundler; +}): Promise { + if (currentBundler.name === 'rspack') { + throw new Error('Rspack bundler is not supported yet'); + } + // https://github.com/webpack-contrib/copy-webpack-plugin + return CopyWebpackPlugin; +} + +export async function getProgressBarPlugin({ + currentBundler, +}: { + currentBundler: CurrentBundler; +}): Promise { + if (currentBundler.name === 'rspack') { + class CustomRspackProgressPlugin extends currentBundler.instance + .ProgressPlugin { + constructor({name}: {name: string}) { + // TODO add support for color + // Unfortunately the rspack.ProgressPlugin does not have a name option + // See https://rspack.dev/plugins/webpack/progress-plugin + // @ts-expect-error: adapt Rspack ProgressPlugin constructor + super({prefix: name}); + } + } + return CustomRspackProgressPlugin as typeof WebpackBar; + } + + return WebpackBar; +} diff --git a/packages/docusaurus/src/faster.ts b/packages/docusaurus-bundler/src/faster.ts similarity index 100% rename from packages/docusaurus/src/faster.ts rename to packages/docusaurus-bundler/src/faster.ts diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index 2005dea60e67..cf1a31ce61c1 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -5,85 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import webpack from 'webpack'; -import WebpackBar from 'webpackbar'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import CopyWebpackPlugin from 'copy-webpack-plugin'; -import logger from '@docusaurus/logger'; -import type {CurrentBundler, DocusaurusConfig} from '@docusaurus/types'; +export {importSwcJsMinifierOptions, importSwcJsLoaderFactory} from './faster'; -// We inject a site config slice because the Rspack flag might change place -type SiteConfigSlice = { - future: { - experimental_faster: Pick< - DocusaurusConfig['future']['experimental_faster'], - 'rspackBundler' - >; - }; -}; +export { + getCurrentBundler, + getCSSExtractPlugin, + getCopyPlugin, + getProgressBarPlugin, +} from './currentBundler'; -function isRspack(siteConfig: SiteConfigSlice): boolean { - return siteConfig.future.experimental_faster.rspackBundler; -} - -export async function getCurrentBundler({ - siteConfig, -}: { - siteConfig: SiteConfigSlice; -}): Promise { - if (isRspack(siteConfig)) { - // TODO add support for Rspack - logger.error( - 'Rspack bundler is not supported yet, will use Webpack instead', - ); - } - return { - name: 'webpack', - instance: webpack, - }; -} - -export async function getCSSExtractPlugin({ - currentBundler, -}: { - currentBundler: CurrentBundler; -}): Promise { - if (currentBundler.name === 'rspack') { - throw new Error('Rspack bundler is not supported yet'); - } - return MiniCssExtractPlugin; -} - -export async function getCopyPlugin({ - currentBundler, -}: { - currentBundler: CurrentBundler; -}): Promise { - if (currentBundler.name === 'rspack') { - throw new Error('Rspack bundler is not supported yet'); - } - // https://github.com/webpack-contrib/copy-webpack-plugin - return CopyWebpackPlugin; -} - -export async function getProgressBarPlugin({ - currentBundler, -}: { - currentBundler: CurrentBundler; -}): Promise { - if (currentBundler.name === 'rspack') { - class CustomRspackProgressPlugin extends currentBundler.instance - .ProgressPlugin { - constructor({name}: {name: string}) { - // TODO add support for color - // Unfortunately the rspack.ProgressPlugin does not have a name option - // See https://rspack.dev/plugins/webpack/progress-plugin - // @ts-expect-error: adapt Rspack ProgressPlugin constructor - super({prefix: name}); - } - } - return CustomRspackProgressPlugin as typeof WebpackBar; - } - - return WebpackBar; -} +export {getMinimizers} from './minification'; diff --git a/packages/docusaurus/src/webpack/minification.ts b/packages/docusaurus-bundler/src/minification.ts similarity index 84% rename from packages/docusaurus/src/webpack/minification.ts rename to packages/docusaurus-bundler/src/minification.ts index d3ec2a1951a9..bc42a5d53460 100644 --- a/packages/docusaurus/src/webpack/minification.ts +++ b/packages/docusaurus-bundler/src/minification.ts @@ -7,13 +7,14 @@ import TerserPlugin from 'terser-webpack-plugin'; import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; -import {importSwcJsMinifierOptions} from '../faster'; +import {importSwcJsMinifierOptions} from './faster'; import type {CustomOptions, CssNanoOptions} from 'css-minimizer-webpack-plugin'; import type {WebpackPluginInstance} from 'webpack'; -import type {FasterConfig} from '@docusaurus/types'; +import type {CurrentBundler, FasterConfig} from '@docusaurus/types'; export type MinimizersConfig = { faster: Pick; + currentBundler: CurrentBundler; }; // See https://github.com/webpack-contrib/terser-webpack-plugin#parallel @@ -111,8 +112,23 @@ function getCssMinimizer(): WebpackPluginInstance { } } -export async function getMinimizers( +async function getWebpackMinimizers( params: MinimizersConfig, ): Promise { return Promise.all([getJsMinimizer(params), getCssMinimizer()]); } + +async function getRspackMinimizers({ + currentBundler, +}: MinimizersConfig): Promise { + console.log('currentBundler', currentBundler.name); + throw new Error('TODO Rspack minimizers not implemented yet'); +} + +export async function getMinimizers( + params: MinimizersConfig, +): Promise { + return params.currentBundler.name === 'rspack' + ? getRspackMinimizers(params) + : getWebpackMinimizers(params); +} diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index 67e3152316fc..84c8853a8b4a 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -33,7 +33,6 @@ "babel-loader": "^9.1.3", "clsx": "^2.0.0", "core-js": "^3.31.1", - "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "webpack": "^5.88.1", "webpack-merge": "^5.9.0", diff --git a/packages/docusaurus-plugin-pwa/src/index.ts b/packages/docusaurus-plugin-pwa/src/index.ts index bb2114fab0d6..b9ad0b0b04bb 100644 --- a/packages/docusaurus-plugin-pwa/src/index.ts +++ b/packages/docusaurus-plugin-pwa/src/index.ts @@ -7,8 +7,7 @@ import path from 'path'; import {type Configuration} from 'webpack'; -import {getProgressBarPlugin} from '@docusaurus/bundler'; -import Terser from 'terser-webpack-plugin'; +import {getProgressBarPlugin, getMinimizers} from '@docusaurus/bundler'; import {injectManifest} from 'workbox-build'; import {normalizeUrl} from '@docusaurus/utils'; import logger from '@docusaurus/logger'; @@ -159,11 +158,10 @@ export default function pluginPWA( // See https://developers.google.com/web/tools/workbox/guides/using-bundlers#webpack minimizer: debug ? [] - : [ - new Terser({ - test: swSourceFileTest, - }), - ], + : await getMinimizers({ + faster: props.siteConfig.future.experimental_faster, + currentBundler: props.currentBundler, + }), }, plugins: [ new props.currentBundler.instance.EnvironmentPlugin({ diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 73bb03497c66..ea22f2ea7f7f 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -92,7 +92,6 @@ "semver": "^7.5.4", "serve-handler": "npm:@docusaurus/serve-handler@6.2.0", "shelljs": "^0.8.5", - "terser-webpack-plugin": "^5.3.10", "tslib": "^2.6.0", "update-notifier": "^6.0.2", "url-loader": "^4.1.1", diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 7888590abfba..ffd426f2318d 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -7,10 +7,9 @@ import fs from 'fs-extra'; import path from 'path'; -import {getCSSExtractPlugin} from '@docusaurus/bundler'; +import {getCSSExtractPlugin, getMinimizers} from '@docusaurus/bundler'; import {md5Hash, getFileLoaderUtils} from '@docusaurus/utils'; import {createJsLoaderFactory, getCustomBabelConfigFilePath} from './utils'; -import {getMinimizers} from './minification'; import {loadThemeAliases, loadDocusaurusAliases} from './aliases'; import type {Configuration} from 'webpack'; import type { @@ -180,7 +179,9 @@ export async function createBaseConfig({ // Only minimize client bundle in production because server bundle is only // used for static site generation minimize: minimizeEnabled, - minimizer: minimizeEnabled ? await getMinimizers({faster}) : undefined, + minimizer: minimizeEnabled + ? await getMinimizers({faster, currentBundler: props.currentBundler}) + : undefined, splitChunks: isServer ? false : { diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index f7f8f7626cfd..0472d000d734 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -8,12 +8,14 @@ import fs from 'fs-extra'; import path from 'path'; import crypto from 'crypto'; -import {getCSSExtractPlugin} from '@docusaurus/bundler'; +import { + getCSSExtractPlugin, + importSwcJsLoaderFactory, +} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; import {type Configuration} from 'webpack'; import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; -import {importSwcJsLoaderFactory} from '../faster'; import type webpack from 'webpack'; import type { ConfigureWebpackUtils, From a3a33d99196e6c1c8a1cb3b2c83f90b55b7022e8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 19 Sep 2024 16:53:43 +0200 Subject: [PATCH 09/25] move compiler to bundler package --- packages/docusaurus-bundler/package.json | 2 + packages/docusaurus-bundler/src/compiler.ts | 87 +++++++++++++++++++ packages/docusaurus-bundler/src/index.ts | 2 + packages/docusaurus-plugin-pwa/src/index.ts | 7 +- packages/docusaurus/package.json | 1 - packages/docusaurus/src/commands/build.ts | 2 +- .../docusaurus/src/commands/start/webpack.ts | 7 +- .../webpack/plugins/ForceTerminatePlugin.ts | 2 +- packages/docusaurus/src/webpack/utils.ts | 78 ----------------- 9 files changed, 100 insertions(+), 88 deletions(-) create mode 100644 packages/docusaurus-bundler/src/compiler.ts diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 74d2aa38ce89..fb9255c5c032 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -18,12 +18,14 @@ }, "license": "MIT", "dependencies": { + "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/faster": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", "copy-webpack-plugin": "^11.0.0", "css-minimizer-webpack-plugin": "^5.0.1", "mini-css-extract-plugin": "^2.9.1", + "react-dev-utils": "^12.0.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "webpack": "^5.88.1", diff --git a/packages/docusaurus-bundler/src/compiler.ts b/packages/docusaurus-bundler/src/compiler.ts new file mode 100644 index 000000000000..384d704e48fb --- /dev/null +++ b/packages/docusaurus-bundler/src/compiler.ts @@ -0,0 +1,87 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {type Configuration} from 'webpack'; +import logger from '@docusaurus/logger'; +import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; +import type webpack from 'webpack'; +import type {CurrentBundler} from '@docusaurus/types'; + +export function formatStatsErrorMessage( + statsJson: ReturnType | undefined, +): string | undefined { + if (statsJson?.errors?.length) { + // TODO formatWebpackMessages does not print stack-traces + // Also the error causal chain is lost here + // We log the stacktrace inside serverEntry.tsx for now (not ideal) + const {errors} = formatWebpackMessages(statsJson); + return errors + .map((str) => logger.red(str)) + .join(`\n\n${logger.yellow('--------------------------')}\n\n`); + } + return undefined; +} + +export function printStatsWarnings( + statsJson: ReturnType | undefined, +): void { + if (statsJson?.warnings?.length) { + statsJson.warnings?.forEach((warning) => { + logger.warn(warning); + }); + } +} + +declare global { + interface Error { + /** @see https://webpack.js.org/api/node/#error-handling */ + details?: unknown; + } +} + +export function compile({ + configs, + currentBundler, +}: { + configs: Configuration[]; + currentBundler: CurrentBundler; +}): Promise { + return new Promise((resolve, reject) => { + const compiler = currentBundler.instance(configs); + compiler.run((err, stats) => { + if (err) { + logger.error(err.stack ?? err); + if (err.details) { + logger.error(err.details); + } + reject(err); + } + // Let plugins consume all the stats + const errorsWarnings = stats?.toJson('errors-warnings'); + if (stats?.hasErrors()) { + const statsErrorMessage = formatStatsErrorMessage(errorsWarnings); + reject( + new Error( + `Failed to compile due to Webpack errors.\n${statsErrorMessage}`, + ), + ); + } + printStatsWarnings(errorsWarnings); + + // Webpack 5 requires calling close() so that persistent caching works + // See https://github.com/webpack/webpack.js.org/pull/4775 + compiler.close((errClose) => { + if (errClose) { + logger.error(`Error while closing Webpack compiler: ${errClose}`); + reject(errClose); + } else { + resolve(stats!); + } + }); + }); + }); +} diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index cf1a31ce61c1..5c6fa9d3786c 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +export {printStatsWarnings, formatStatsErrorMessage, compile} from './compiler'; + export {importSwcJsMinifierOptions, importSwcJsLoaderFactory} from './faster'; export { diff --git a/packages/docusaurus-plugin-pwa/src/index.ts b/packages/docusaurus-plugin-pwa/src/index.ts index b9ad0b0b04bb..85dbebc861d4 100644 --- a/packages/docusaurus-plugin-pwa/src/index.ts +++ b/packages/docusaurus-plugin-pwa/src/index.ts @@ -7,11 +7,14 @@ import path from 'path'; import {type Configuration} from 'webpack'; -import {getProgressBarPlugin, getMinimizers} from '@docusaurus/bundler'; +import { + compile, + getProgressBarPlugin, + getMinimizers, +} from '@docusaurus/bundler'; import {injectManifest} from 'workbox-build'; import {normalizeUrl} from '@docusaurus/utils'; import logger from '@docusaurus/logger'; -import {compile} from '@docusaurus/core/lib/webpack/utils'; import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations'; import type {HtmlTags, LoadContext, Plugin} from '@docusaurus/types'; import type {PluginOptions} from '@docusaurus/plugin-pwa'; diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index ea22f2ea7f7f..9469a7e377d6 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -44,7 +44,6 @@ "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", "@docusaurus/bundler": "3.5.2", - "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/mdx-loader": "3.5.2", "@docusaurus/utils": "3.5.2", diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 27a1a4ae2fb8..6cc6e73b751f 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -8,6 +8,7 @@ import fs from 'fs-extra'; import path from 'path'; import _ from 'lodash'; +import {compile} from '@docusaurus/bundler'; import logger, {PerfLogger} from '@docusaurus/logger'; import {DOCUSAURUS_VERSION, mapAsyncSequential} from '@docusaurus/utils'; import {loadSite, loadContext, type LoadContextParams} from '../server/site'; @@ -18,7 +19,6 @@ import { createConfigureWebpackUtils, executePluginsConfigureWebpack, } from '../webpack/configure'; -import {compile} from '../webpack/utils'; import {loadI18n} from '../server/i18n'; import { diff --git a/packages/docusaurus/src/commands/start/webpack.ts b/packages/docusaurus/src/commands/start/webpack.ts index b2ddd0853c15..7f164893e53a 100644 --- a/packages/docusaurus/src/commands/start/webpack.ts +++ b/packages/docusaurus/src/commands/start/webpack.ts @@ -8,15 +8,12 @@ import path from 'path'; import merge from 'webpack-merge'; import webpack from 'webpack'; +import {formatStatsErrorMessage, printStatsWarnings} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; import WebpackDevServer from 'webpack-dev-server'; import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware'; import {createPollingOptions} from './watcher'; -import { - formatStatsErrorMessage, - getHttpsConfig, - printStatsWarnings, -} from '../../webpack/utils'; +import {getHttpsConfig} from '../../webpack/utils'; import { createConfigureWebpackUtils, executePluginsConfigureWebpack, diff --git a/packages/docusaurus/src/webpack/plugins/ForceTerminatePlugin.ts b/packages/docusaurus/src/webpack/plugins/ForceTerminatePlugin.ts index 15a41a2127f1..18a1c4104eb0 100644 --- a/packages/docusaurus/src/webpack/plugins/ForceTerminatePlugin.ts +++ b/packages/docusaurus/src/webpack/plugins/ForceTerminatePlugin.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +import {formatStatsErrorMessage} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; -import {formatStatsErrorMessage} from '../utils'; import type webpack from 'webpack'; // When building, include the plugin to force terminate building if errors diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 0472d000d734..85c8d0810d0d 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -14,9 +14,6 @@ import { } from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; -import {type Configuration} from 'webpack'; -import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; -import type webpack from 'webpack'; import type { ConfigureWebpackUtils, CurrentBundler, @@ -24,31 +21,6 @@ import type { } from '@docusaurus/types'; import type {TransformOptions} from '@babel/core'; -export function formatStatsErrorMessage( - statsJson: ReturnType | undefined, -): string | undefined { - if (statsJson?.errors?.length) { - // TODO formatWebpackMessages does not print stack-traces - // Also the error causal chain is lost here - // We log the stacktrace inside serverEntry.tsx for now (not ideal) - const {errors} = formatWebpackMessages(statsJson); - return errors - .map((str) => logger.red(str)) - .join(`\n\n${logger.yellow('--------------------------')}\n\n`); - } - return undefined; -} - -export function printStatsWarnings( - statsJson: ReturnType | undefined, -): void { - if (statsJson?.warnings?.length) { - statsJson.warnings?.forEach((warning) => { - logger.warn(warning); - }); - } -} - export async function createStyleLoadersFactory({ currentBundler, }: { @@ -198,56 +170,6 @@ export async function createJsLoaderFactory({ throw new Error(`Docusaurus bug: unexpected jsLoader value${jsLoader}`); } -declare global { - interface Error { - /** @see https://webpack.js.org/api/node/#error-handling */ - details: unknown; - } -} - -export function compile({ - configs, - currentBundler, -}: { - configs: Configuration[]; - currentBundler: CurrentBundler; -}): Promise { - return new Promise((resolve, reject) => { - const compiler = currentBundler.instance(configs); - compiler.run((err, stats) => { - if (err) { - logger.error(err.stack ?? err); - if (err.details) { - logger.error(err.details); - } - reject(err); - } - // Let plugins consume all the stats - const errorsWarnings = stats?.toJson('errors-warnings'); - if (stats?.hasErrors()) { - const statsErrorMessage = formatStatsErrorMessage(errorsWarnings); - reject( - new Error( - `Failed to compile due to Webpack errors.\n${statsErrorMessage}`, - ), - ); - } - printStatsWarnings(errorsWarnings); - - // Webpack 5 requires calling close() so that persistent caching works - // See https://github.com/webpack/webpack.js.org/pull/4775 - compiler.close((errClose) => { - if (errClose) { - logger.error(`Error while closing Webpack compiler: ${errClose}`); - reject(errClose); - } else { - resolve(stats!); - } - }); - }); - }); -} - // Ensure the certificate and key provided are valid and if not // throw an easy to debug error function validateKeyAndCerts({ From 5d8339b9d5336e207b5c91cb292f062b09816165 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 11:14:41 +0200 Subject: [PATCH 10/25] move clean-css deps --- packages/docusaurus-bundler/package.json | 1 + packages/docusaurus/package.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index fb9255c5c032..a28f2a659501 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -22,6 +22,7 @@ "@docusaurus/faster": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", + "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", "css-minimizer-webpack-plugin": "^5.0.1", "mini-css-extract-plugin": "^2.9.1", diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 9469a7e377d6..fa25f60d00f9 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -55,7 +55,6 @@ "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", - "clean-css": "^5.3.2", "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", From a386846bedecd5d6f627da9a0806dab59ed64834 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 11:46:54 +0200 Subject: [PATCH 11/25] move js loader to bundler pkg --- packages/docusaurus-bundler/package.json | 12 +++ .../src/{faster.ts => importFaster.ts} | 0 packages/docusaurus-bundler/src/index.ts | 8 +- .../src/loaders/babelPreset.ts} | 0 .../src/loaders/jsLoader.ts | 91 +++++++++++++++++++ .../docusaurus-bundler/src/minification.ts | 2 +- packages/docusaurus/package.json | 9 -- .../src/commands/writeTranslations.ts | 5 +- packages/docusaurus/src/webpack/base.ts | 9 +- packages/docusaurus/src/webpack/configure.ts | 4 +- packages/docusaurus/src/webpack/utils.ts | 91 +------------------ 11 files changed, 125 insertions(+), 106 deletions(-) rename packages/docusaurus-bundler/src/{faster.ts => importFaster.ts} (100%) rename packages/{docusaurus/src/babel/preset.ts => docusaurus-bundler/src/loaders/babelPreset.ts} (100%) create mode 100644 packages/docusaurus-bundler/src/loaders/jsLoader.ts diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index a28f2a659501..316f911abf7f 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -18,13 +18,25 @@ }, "license": "MIT", "dependencies": { + "@babel/core": "^7.23.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@babel/runtime-corejs3": "^7.22.6", "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/faster": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "babel-loader": "^9.1.3", + "babel-plugin-dynamic-import-node": "^2.3.3", "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", "css-minimizer-webpack-plugin": "^5.0.1", + "fs-extra": "^11.1.1", "mini-css-extract-plugin": "^2.9.1", "react-dev-utils": "^12.0.1", "terser-webpack-plugin": "^5.3.9", diff --git a/packages/docusaurus-bundler/src/faster.ts b/packages/docusaurus-bundler/src/importFaster.ts similarity index 100% rename from packages/docusaurus-bundler/src/faster.ts rename to packages/docusaurus-bundler/src/importFaster.ts diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index 5c6fa9d3786c..151245ccc792 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -7,8 +7,6 @@ export {printStatsWarnings, formatStatsErrorMessage, compile} from './compiler'; -export {importSwcJsMinifierOptions, importSwcJsLoaderFactory} from './faster'; - export { getCurrentBundler, getCSSExtractPlugin, @@ -17,3 +15,9 @@ export { } from './currentBundler'; export {getMinimizers} from './minification'; + +export { + getBabelOptions, + getCustomBabelConfigFilePath, + createJsLoaderFactory, +} from './loaders/jsLoader'; diff --git a/packages/docusaurus/src/babel/preset.ts b/packages/docusaurus-bundler/src/loaders/babelPreset.ts similarity index 100% rename from packages/docusaurus/src/babel/preset.ts rename to packages/docusaurus-bundler/src/loaders/babelPreset.ts diff --git a/packages/docusaurus-bundler/src/loaders/jsLoader.ts b/packages/docusaurus-bundler/src/loaders/jsLoader.ts new file mode 100644 index 000000000000..6cd0d65ab4aa --- /dev/null +++ b/packages/docusaurus-bundler/src/loaders/jsLoader.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import fs from 'fs-extra'; +import path from 'path'; +import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; +import {importSwcJsLoaderFactory} from '../importFaster'; +import type {ConfigureWebpackUtils, DocusaurusConfig} from '@docusaurus/types'; +import type {TransformOptions} from '@babel/core'; + +export async function getCustomBabelConfigFilePath( + siteDir: string, +): Promise { + const customBabelConfigurationPath = path.join( + siteDir, + BABEL_CONFIG_FILE_NAME, + ); + return (await fs.pathExists(customBabelConfigurationPath)) + ? customBabelConfigurationPath + : undefined; +} + +export function getBabelOptions({ + isServer, + babelOptions, +}: { + isServer?: boolean; + babelOptions?: TransformOptions | string; +} = {}): TransformOptions { + if (typeof babelOptions === 'string') { + return { + babelrc: false, + configFile: babelOptions, + caller: {name: isServer ? 'server' : 'client'}, + }; + } + return { + ...(babelOptions ?? {presets: [require.resolve('./babelPreset')]}), + babelrc: false, + configFile: false, + caller: {name: isServer ? 'server' : 'client'}, + }; +} + +const BabelJsLoaderFactory: ConfigureWebpackUtils['getJSLoader'] = ({ + isServer, + babelOptions, +}) => { + return { + loader: require.resolve('babel-loader'), + options: getBabelOptions({isServer, babelOptions}), + }; +}; + +// Confusing: function that creates a function that creates actual js loaders +// This is done on purpose because the js loader factory is a public API +// It is injected in configureWebpack plugin lifecycle for plugin authors +export async function createJsLoaderFactory({ + siteConfig, +}: { + siteConfig: { + webpack?: DocusaurusConfig['webpack']; + future?: { + experimental_faster: DocusaurusConfig['future']['experimental_faster']; + }; + }; +}): Promise { + const jsLoader = siteConfig.webpack?.jsLoader ?? 'babel'; + if ( + jsLoader instanceof Function && + siteConfig.future?.experimental_faster.swcJsLoader + ) { + throw new Error( + "You can't use a custom webpack.jsLoader and experimental_faster.swcJsLoader at the same time", + ); + } + if (jsLoader instanceof Function) { + return ({isServer}) => jsLoader(isServer); + } + if (siteConfig.future?.experimental_faster.swcJsLoader) { + return importSwcJsLoaderFactory(); + } + if (jsLoader === 'babel') { + return BabelJsLoaderFactory; + } + throw new Error(`Docusaurus bug: unexpected jsLoader value${jsLoader}`); +} diff --git a/packages/docusaurus-bundler/src/minification.ts b/packages/docusaurus-bundler/src/minification.ts index bc42a5d53460..49305b7c6d4d 100644 --- a/packages/docusaurus-bundler/src/minification.ts +++ b/packages/docusaurus-bundler/src/minification.ts @@ -7,7 +7,7 @@ import TerserPlugin from 'terser-webpack-plugin'; import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; -import {importSwcJsMinifierOptions} from './faster'; +import {importSwcJsMinifierOptions} from './importFaster'; import type {CustomOptions, CssNanoOptions} from 'css-minimizer-webpack-plugin'; import type {WebpackPluginInstance} from 'webpack'; import type {CurrentBundler, FasterConfig} from '@docusaurus/types'; diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index fa25f60d00f9..f02195fc9fc0 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -35,13 +35,6 @@ "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", "@docusaurus/bundler": "3.5.2", "@docusaurus/logger": "3.5.2", @@ -50,8 +43,6 @@ "@docusaurus/utils-common": "3.5.2", "@docusaurus/utils-validation": "3.5.2", "autoprefixer": "^10.4.14", - "babel-loader": "^9.1.3", - "babel-plugin-dynamic-import-node": "^2.3.3", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", diff --git a/packages/docusaurus/src/commands/writeTranslations.ts b/packages/docusaurus/src/commands/writeTranslations.ts index 9460622c7bef..5eb2b151a151 100644 --- a/packages/docusaurus/src/commands/writeTranslations.ts +++ b/packages/docusaurus/src/commands/writeTranslations.ts @@ -7,6 +7,10 @@ import fs from 'fs-extra'; import path from 'path'; +import { + getCustomBabelConfigFilePath, + getBabelOptions, +} from '@docusaurus/bundler'; import {loadContext, type LoadContextParams} from '../server/site'; import {initPlugins} from '../server/plugins/init'; import { @@ -20,7 +24,6 @@ import { extractSiteSourceCodeTranslations, globSourceCodeFilePaths, } from '../server/translations/translationsExtractor'; -import {getCustomBabelConfigFilePath, getBabelOptions} from '../webpack/utils'; import type {InitializedPlugin} from '@docusaurus/types'; export type WriteTranslationsCLIOptions = Pick< diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index ffd426f2318d..bba2d9cc3e0b 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -7,9 +7,14 @@ import fs from 'fs-extra'; import path from 'path'; -import {getCSSExtractPlugin, getMinimizers} from '@docusaurus/bundler'; +import { + getCSSExtractPlugin, + getMinimizers, + getCustomBabelConfigFilePath, + createJsLoaderFactory, +} from '@docusaurus/bundler'; + import {md5Hash, getFileLoaderUtils} from '@docusaurus/utils'; -import {createJsLoaderFactory, getCustomBabelConfigFilePath} from './utils'; import {loadThemeAliases, loadDocusaurusAliases} from './aliases'; import type {Configuration} from 'webpack'; import type { diff --git a/packages/docusaurus/src/webpack/configure.ts b/packages/docusaurus/src/webpack/configure.ts index b95ae42c9453..eebc714754a8 100644 --- a/packages/docusaurus/src/webpack/configure.ts +++ b/packages/docusaurus/src/webpack/configure.ts @@ -10,8 +10,8 @@ import { customizeArray, customizeObject, } from 'webpack-merge'; -import {getCurrentBundler} from '@docusaurus/bundler'; -import {createJsLoaderFactory, createStyleLoadersFactory} from './utils'; +import {getCurrentBundler, createJsLoaderFactory} from '@docusaurus/bundler'; +import {createStyleLoadersFactory} from './utils'; import type {Configuration, RuleSetRule} from 'webpack'; import type { Plugin, diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 85c8d0810d0d..273b88e0c3b0 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -8,18 +8,9 @@ import fs from 'fs-extra'; import path from 'path'; import crypto from 'crypto'; -import { - getCSSExtractPlugin, - importSwcJsLoaderFactory, -} from '@docusaurus/bundler'; +import {getCSSExtractPlugin} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; -import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; -import type { - ConfigureWebpackUtils, - CurrentBundler, - DocusaurusConfig, -} from '@docusaurus/types'; -import type {TransformOptions} from '@babel/core'; +import type {ConfigureWebpackUtils, CurrentBundler} from '@docusaurus/types'; export async function createStyleLoadersFactory({ currentBundler, @@ -92,84 +83,6 @@ export async function createStyleLoadersFactory({ }; } -export async function getCustomBabelConfigFilePath( - siteDir: string, -): Promise { - const customBabelConfigurationPath = path.join( - siteDir, - BABEL_CONFIG_FILE_NAME, - ); - return (await fs.pathExists(customBabelConfigurationPath)) - ? customBabelConfigurationPath - : undefined; -} - -export function getBabelOptions({ - isServer, - babelOptions, -}: { - isServer?: boolean; - babelOptions?: TransformOptions | string; -} = {}): TransformOptions { - if (typeof babelOptions === 'string') { - return { - babelrc: false, - configFile: babelOptions, - caller: {name: isServer ? 'server' : 'client'}, - }; - } - return { - ...(babelOptions ?? {presets: [require.resolve('../babel/preset')]}), - babelrc: false, - configFile: false, - caller: {name: isServer ? 'server' : 'client'}, - }; -} - -const BabelJsLoaderFactory: ConfigureWebpackUtils['getJSLoader'] = ({ - isServer, - babelOptions, -}) => { - return { - loader: require.resolve('babel-loader'), - options: getBabelOptions({isServer, babelOptions}), - }; -}; - -// Confusing: function that creates a function that creates actual js loaders -// This is done on purpose because the js loader factory is a public API -// It is injected in configureWebpack plugin lifecycle for plugin authors -export async function createJsLoaderFactory({ - siteConfig, -}: { - siteConfig: { - webpack?: DocusaurusConfig['webpack']; - future?: { - experimental_faster: DocusaurusConfig['future']['experimental_faster']; - }; - }; -}): Promise { - const jsLoader = siteConfig.webpack?.jsLoader ?? 'babel'; - if ( - jsLoader instanceof Function && - siteConfig.future?.experimental_faster.swcJsLoader - ) { - throw new Error( - "You can't use a custom webpack.jsLoader and experimental_faster.swcJsLoader at the same time", - ); - } - if (jsLoader instanceof Function) { - return ({isServer}) => jsLoader(isServer); - } - if (siteConfig.future?.experimental_faster.swcJsLoader) { - return importSwcJsLoaderFactory(); - } - if (jsLoader === 'babel') { - return BabelJsLoaderFactory; - } - throw new Error(`Docusaurus bug: unexpected jsLoader value${jsLoader}`); -} - // Ensure the certificate and key provided are valid and if not // throw an easy to debug error function validateKeyAndCerts({ From 818d835376d473225b7b4354957b1a770b46375a Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 12:08:43 +0200 Subject: [PATCH 12/25] move style loader and dependencies to bundler pkg --- packages/docusaurus-bundler/package.json | 8 ++ packages/docusaurus-bundler/src/index.ts | 4 + .../src/loaders/styleLoader.ts | 80 +++++++++++++++++++ packages/docusaurus/package.json | 9 --- packages/docusaurus/src/babel/preset.ts | 9 +++ packages/docusaurus/src/webpack/configure.ts | 7 +- packages/docusaurus/src/webpack/utils.ts | 73 ----------------- 7 files changed, 106 insertions(+), 84 deletions(-) create mode 100644 packages/docusaurus-bundler/src/loaders/styleLoader.ts create mode 100644 packages/docusaurus/src/babel/preset.ts diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index 316f911abf7f..af61c63edb13 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -31,16 +31,24 @@ "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", "@docusaurus/utils": "3.5.2", + "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "postcss": "^8.4.26", + "postcss-loader": "^7.3.3", + "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "mini-css-extract-plugin": "^2.9.1", + "null-loader": "^4.0.1", "react-dev-utils": "^12.0.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", + "url-loader": "^4.1.1", "webpack": "^5.88.1", "webpackbar": "^6.0.1" }, diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index 151245ccc792..ef6d995387e6 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -21,3 +21,7 @@ export { getCustomBabelConfigFilePath, createJsLoaderFactory, } from './loaders/jsLoader'; + +export {createStyleLoadersFactory} from './loaders/styleLoader'; + +export {default as babelPreset} from './loaders/babelPreset'; diff --git a/packages/docusaurus-bundler/src/loaders/styleLoader.ts b/packages/docusaurus-bundler/src/loaders/styleLoader.ts new file mode 100644 index 000000000000..3aea678d7714 --- /dev/null +++ b/packages/docusaurus-bundler/src/loaders/styleLoader.ts @@ -0,0 +1,80 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {getCSSExtractPlugin} from '../currentBundler'; +import type {ConfigureWebpackUtils, CurrentBundler} from '@docusaurus/types'; + +export async function createStyleLoadersFactory({ + currentBundler, +}: { + currentBundler: CurrentBundler; +}): Promise { + const CssExtractPlugin = await getCSSExtractPlugin({currentBundler}); + + return function getStyleLoaders( + isServer: boolean, + cssOptionsArg: { + [key: string]: unknown; + } = {}, + ) { + const cssOptions: {[key: string]: unknown} = { + // TODO turn esModule on later, see https://github.com/facebook/docusaurus/pull/6424 + esModule: false, + ...cssOptionsArg, + }; + + // On the server we don't really need to extract/emit CSS + // We only need to transform CSS module imports to a styles object + if (isServer) { + return cssOptions.modules + ? [ + { + loader: require.resolve('css-loader'), + options: cssOptions, + }, + ] + : // Ignore regular CSS files + [{loader: require.resolve('null-loader')}]; + } + + return [ + { + loader: CssExtractPlugin.loader, + options: { + esModule: true, + }, + }, + { + loader: require.resolve('css-loader'), + options: cssOptions, + }, + + // TODO apart for configurePostCss(), do we really need this loader? + // Note: using postcss here looks inefficient/duplicate + // But in practice, it's not a big deal because css-loader also uses postcss + // and is able to reuse the parsed AST from postcss-loader + // See https://github.com/webpack-contrib/css-loader/blob/master/src/index.js#L159 + { + // Options for PostCSS as we reference these options twice + // Adds vendor prefixing based on your specified browser support in + // package.json + loader: require.resolve('postcss-loader'), + options: { + postcssOptions: { + // Necessary for external CSS imports to work + // https://github.com/facebook/create-react-app/issues/2677 + ident: 'postcss', + plugins: [ + // eslint-disable-next-line global-require + require('autoprefixer'), + ], + }, + }, + }, + ]; + }; +} diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index f02195fc9fc0..b76b164389a8 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -42,7 +42,6 @@ "@docusaurus/utils": "3.5.2", "@docusaurus/utils-common": "3.5.2", "@docusaurus/utils-validation": "3.5.2", - "autoprefixer": "^10.4.14", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -50,25 +49,18 @@ "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", - "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", - "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "html-minifier-terser": "^7.2.0", "html-tags": "^3.3.1", "html-webpack-plugin": "^5.5.3", "leven": "^3.1.0", "lodash": "^4.17.21", - "null-loader": "^4.0.1", "p-map": "^4.0.0", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", @@ -83,7 +75,6 @@ "shelljs": "^0.8.5", "tslib": "^2.6.0", "update-notifier": "^6.0.2", - "url-loader": "^4.1.1", "webpack": "^5.88.1", "webpack-bundle-analyzer": "^4.9.0", "webpack-dev-server": "^4.15.1", diff --git a/packages/docusaurus/src/babel/preset.ts b/packages/docusaurus/src/babel/preset.ts new file mode 100644 index 000000000000..a59ca5c8c965 --- /dev/null +++ b/packages/docusaurus/src/babel/preset.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// TODO Docusaurus v4, do breaking change and expose babel preset cleanly +export {babelPreset as default} from '@docusaurus/bundler'; diff --git a/packages/docusaurus/src/webpack/configure.ts b/packages/docusaurus/src/webpack/configure.ts index eebc714754a8..86803108b497 100644 --- a/packages/docusaurus/src/webpack/configure.ts +++ b/packages/docusaurus/src/webpack/configure.ts @@ -10,8 +10,11 @@ import { customizeArray, customizeObject, } from 'webpack-merge'; -import {getCurrentBundler, createJsLoaderFactory} from '@docusaurus/bundler'; -import {createStyleLoadersFactory} from './utils'; +import { + getCurrentBundler, + createJsLoaderFactory, + createStyleLoadersFactory, +} from '@docusaurus/bundler'; import type {Configuration, RuleSetRule} from 'webpack'; import type { Plugin, diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils.ts index 273b88e0c3b0..3772e9d07dfe 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils.ts @@ -8,80 +8,7 @@ import fs from 'fs-extra'; import path from 'path'; import crypto from 'crypto'; -import {getCSSExtractPlugin} from '@docusaurus/bundler'; import logger from '@docusaurus/logger'; -import type {ConfigureWebpackUtils, CurrentBundler} from '@docusaurus/types'; - -export async function createStyleLoadersFactory({ - currentBundler, -}: { - currentBundler: CurrentBundler; -}): Promise { - const CssExtractPlugin = await getCSSExtractPlugin({currentBundler}); - - return function getStyleLoaders( - isServer: boolean, - cssOptionsArg: { - [key: string]: unknown; - } = {}, - ) { - const cssOptions: {[key: string]: unknown} = { - // TODO turn esModule on later, see https://github.com/facebook/docusaurus/pull/6424 - esModule: false, - ...cssOptionsArg, - }; - - // On the server we don't really need to extract/emit CSS - // We only need to transform CSS module imports to a styles object - if (isServer) { - return cssOptions.modules - ? [ - { - loader: require.resolve('css-loader'), - options: cssOptions, - }, - ] - : // Ignore regular CSS files - [{loader: require.resolve('null-loader')}]; - } - - return [ - { - loader: CssExtractPlugin.loader, - options: { - esModule: true, - }, - }, - { - loader: require.resolve('css-loader'), - options: cssOptions, - }, - - // TODO apart for configurePostCss(), do we really need this loader? - // Note: using postcss here looks inefficient/duplicate - // But in practice, it's not a big deal because css-loader also uses postcss - // and is able to reuse the parsed AST from postcss-loader - // See https://github.com/webpack-contrib/css-loader/blob/master/src/index.js#L159 - { - // Options for PostCSS as we reference these options twice - // Adds vendor prefixing based on your specified browser support in - // package.json - loader: require.resolve('postcss-loader'), - options: { - postcssOptions: { - // Necessary for external CSS imports to work - // https://github.com/facebook/create-react-app/issues/2677 - ident: 'postcss', - plugins: [ - // eslint-disable-next-line global-require - require('autoprefixer'), - ], - }, - }, - }, - ]; - }; -} // Ensure the certificate and key provided are valid and if not // throw an easy to debug error From 7fc4c7ca9558edadc1bffd6089a5bda195ceb663 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 12:16:51 +0200 Subject: [PATCH 13/25] preset comment --- packages/docusaurus/src/babel/preset.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/docusaurus/src/babel/preset.ts b/packages/docusaurus/src/babel/preset.ts index a59ca5c8c965..10210b1428d5 100644 --- a/packages/docusaurus/src/babel/preset.ts +++ b/packages/docusaurus/src/babel/preset.ts @@ -6,4 +6,11 @@ */ // TODO Docusaurus v4, do breaking change and expose babel preset cleanly +/* +this just ensure retro-compatibility with our former init template .babelrc.js: + +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; + */ export {babelPreset as default} from '@docusaurus/bundler'; From 49400344092e11f41903a23f382b0d7049db92cb Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 12:35:41 +0200 Subject: [PATCH 14/25] fix tests --- packages/docusaurus-bundler/package.json | 3 + .../docusaurus/src/commands/start/webpack.ts | 2 +- .../__tests__/translationsExtractor.test.ts | 2 +- .../__fixtures__/getHttpsConfig}/host.crt | 0 .../__fixtures__/getHttpsConfig}/host.key | 0 .../__fixtures__/getHttpsConfig}/invalid.crt | 0 .../__fixtures__/getHttpsConfig}/invalid.key | 0 .../utils/__tests__/getHttpsConfig.test.ts | 68 +++++++++++++++++++ .../{utils.ts => utils/getHttpsConfig.ts} | 2 +- 9 files changed, 74 insertions(+), 3 deletions(-) rename packages/docusaurus/src/webpack/{__tests__/__fixtures__ => utils/__tests__/__fixtures__/getHttpsConfig}/host.crt (100%) rename packages/docusaurus/src/webpack/{__tests__/__fixtures__ => utils/__tests__/__fixtures__/getHttpsConfig}/host.key (100%) rename packages/docusaurus/src/webpack/{__tests__/__fixtures__ => utils/__tests__/__fixtures__/getHttpsConfig}/invalid.crt (100%) rename packages/docusaurus/src/webpack/{__tests__/__fixtures__ => utils/__tests__/__fixtures__/getHttpsConfig}/invalid.key (100%) create mode 100644 packages/docusaurus/src/webpack/utils/__tests__/getHttpsConfig.test.ts rename packages/docusaurus/src/webpack/{utils.ts => utils/getHttpsConfig.ts} (97%) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index af61c63edb13..eaecd5715c84 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -52,6 +52,9 @@ "webpack": "^5.88.1", "webpackbar": "^6.0.1" }, + "devDependencies": { + "@total-typescript/shoehorn": "^0.1.2" + }, "engines": { "node": ">=18.0" } diff --git a/packages/docusaurus/src/commands/start/webpack.ts b/packages/docusaurus/src/commands/start/webpack.ts index 7f164893e53a..9c462cd7d974 100644 --- a/packages/docusaurus/src/commands/start/webpack.ts +++ b/packages/docusaurus/src/commands/start/webpack.ts @@ -13,7 +13,7 @@ import logger from '@docusaurus/logger'; import WebpackDevServer from 'webpack-dev-server'; import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware'; import {createPollingOptions} from './watcher'; -import {getHttpsConfig} from '../../webpack/utils'; +import getHttpsConfig from '../../webpack/utils/getHttpsConfig'; import { createConfigureWebpackUtils, executePluginsConfigureWebpack, diff --git a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts index 09ea7786323a..abdd43e092b0 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts @@ -9,12 +9,12 @@ import {jest} from '@jest/globals'; import path from 'path'; import fs from 'fs-extra'; import tmp from 'tmp-promise'; +import {getBabelOptions} from '@docusaurus/bundler'; import {SRC_DIR_NAME} from '@docusaurus/utils'; import { extractSourceCodeFileTranslations, extractSiteSourceCodeTranslations, } from '../translationsExtractor'; -import {getBabelOptions} from '../../../webpack/utils'; import type {InitializedPlugin, LoadedPlugin} from '@docusaurus/types'; const TestBabelOptions = getBabelOptions({ diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/host.crt b/packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/host.crt similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/host.crt rename to packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/host.crt diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/host.key b/packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/host.key similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/host.key rename to packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/host.key diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.crt b/packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/invalid.crt similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.crt rename to packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/invalid.crt diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.key b/packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/invalid.key similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.key rename to packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/invalid.key diff --git a/packages/docusaurus/src/webpack/utils/__tests__/getHttpsConfig.test.ts b/packages/docusaurus/src/webpack/utils/__tests__/getHttpsConfig.test.ts new file mode 100644 index 000000000000..2f395cd4bcd6 --- /dev/null +++ b/packages/docusaurus/src/webpack/utils/__tests__/getHttpsConfig.test.ts @@ -0,0 +1,68 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import getHttpsConfig from '../getHttpsConfig'; + +describe('getHttpsConfig', () => { + const originalEnv = process.env; + + function getFixture(name: string) { + return path.join(__dirname, '__fixtures__/getHttpsConfig', name); + } + + beforeEach(() => { + jest.resetModules(); + process.env = {...originalEnv}; + }); + + afterAll(() => { + process.env = originalEnv; + }); + + it('returns true for HTTPS not env', async () => { + await expect(getHttpsConfig()).resolves.toBe(false); + }); + + it('returns true for HTTPS in env', async () => { + process.env.HTTPS = 'true'; + await expect(getHttpsConfig()).resolves.toBe(true); + }); + + it('returns custom certs if they are in env', async () => { + process.env.HTTPS = 'true'; + process.env.SSL_CRT_FILE = getFixture('host.crt'); + process.env.SSL_KEY_FILE = getFixture('host.key'); + await expect(getHttpsConfig()).resolves.toEqual({ + key: expect.any(Buffer), + cert: expect.any(Buffer), + }); + }); + + it("throws if file doesn't exist", async () => { + process.env.HTTPS = 'true'; + process.env.SSL_CRT_FILE = getFixture('nonexistent.crt'); + process.env.SSL_KEY_FILE = getFixture('host.key'); + await expect(getHttpsConfig()).rejects.toThrowErrorMatchingInlineSnapshot( + `"You specified SSL_CRT_FILE in your env, but the file "/packages/docusaurus/src/webpack/utils/__tests__/__fixtures__/getHttpsConfig/nonexistent.crt" can't be found."`, + ); + }); + + it('throws for invalid key', async () => { + process.env.HTTPS = 'true'; + process.env.SSL_CRT_FILE = getFixture('host.crt'); + process.env.SSL_KEY_FILE = getFixture('invalid.key'); + await expect(getHttpsConfig()).rejects.toThrow(); + }); + + it('throws for invalid cert', async () => { + process.env.HTTPS = 'true'; + process.env.SSL_CRT_FILE = getFixture('invalid.crt'); + process.env.SSL_KEY_FILE = getFixture('host.key'); + await expect(getHttpsConfig()).rejects.toThrow(); + }); +}); diff --git a/packages/docusaurus/src/webpack/utils.ts b/packages/docusaurus/src/webpack/utils/getHttpsConfig.ts similarity index 97% rename from packages/docusaurus/src/webpack/utils.ts rename to packages/docusaurus/src/webpack/utils/getHttpsConfig.ts index 3772e9d07dfe..083614ceb889 100644 --- a/packages/docusaurus/src/webpack/utils.ts +++ b/packages/docusaurus/src/webpack/utils/getHttpsConfig.ts @@ -53,7 +53,7 @@ async function readEnvFile(file: string, type: string) { // Get the https config // Return cert files if provided in env, otherwise just true or false -export async function getHttpsConfig(): Promise< +export default async function getHttpsConfig(): Promise< boolean | {cert: Buffer; key: Buffer} > { const appDirectory = await fs.realpath(process.cwd()); From 92ef99da7d447d866dc23f992f11245f3e051aed Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 12:37:43 +0200 Subject: [PATCH 15/25] fix tests --- .../src/loaders/__tests__/jsLoader.test.ts | 77 ++++++++++ .../src/webpack/__tests__/utils.test.ts | 138 ------------------ 2 files changed, 77 insertions(+), 138 deletions(-) create mode 100644 packages/docusaurus-bundler/src/loaders/__tests__/jsLoader.test.ts delete mode 100644 packages/docusaurus/src/webpack/__tests__/utils.test.ts diff --git a/packages/docusaurus-bundler/src/loaders/__tests__/jsLoader.test.ts b/packages/docusaurus-bundler/src/loaders/__tests__/jsLoader.test.ts new file mode 100644 index 000000000000..2ac34d425f40 --- /dev/null +++ b/packages/docusaurus-bundler/src/loaders/__tests__/jsLoader.test.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {fromPartial} from '@total-typescript/shoehorn'; +import {createJsLoaderFactory} from '../jsLoader'; + +import type {RuleSetRule} from 'webpack'; + +describe('createJsLoaderFactory', () => { + function testJsLoaderFactory( + siteConfig?: Parameters[0]['siteConfig'], + ) { + return createJsLoaderFactory({ + siteConfig: { + ...siteConfig, + webpack: { + jsLoader: 'babel', + ...siteConfig?.webpack, + }, + future: fromPartial(siteConfig?.future), + }, + }); + } + + it('createJsLoaderFactory defaults to babel loader', async () => { + const createJsLoader = await testJsLoaderFactory(); + expect(createJsLoader({isServer: true}).loader).toBe( + require.resolve('babel-loader'), + ); + expect(createJsLoader({isServer: false}).loader).toBe( + require.resolve('babel-loader'), + ); + }); + + it('createJsLoaderFactory accepts loaders with preset', async () => { + const createJsLoader = await testJsLoaderFactory({ + webpack: {jsLoader: 'babel'}, + }); + + expect( + createJsLoader({ + isServer: true, + }).loader, + ).toBe(require.resolve('babel-loader')); + expect( + createJsLoader({ + isServer: false, + }).loader, + ).toBe(require.resolve('babel-loader')); + }); + + it('createJsLoaderFactory allows customization', async () => { + const customJSLoader = (isServer: boolean): RuleSetRule => ({ + loader: 'my-fast-js-loader', + options: String(isServer), + }); + + const createJsLoader = await testJsLoaderFactory({ + webpack: {jsLoader: customJSLoader}, + }); + + expect( + createJsLoader({ + isServer: true, + }), + ).toEqual(customJSLoader(true)); + expect( + createJsLoader({ + isServer: false, + }), + ).toEqual(customJSLoader(false)); + }); +}); diff --git a/packages/docusaurus/src/webpack/__tests__/utils.test.ts b/packages/docusaurus/src/webpack/__tests__/utils.test.ts deleted file mode 100644 index 3493556f9a78..000000000000 --- a/packages/docusaurus/src/webpack/__tests__/utils.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path'; -import {createJsLoaderFactory, getHttpsConfig} from '../utils'; -import {DEFAULT_FUTURE_CONFIG} from '../../server/configValidation'; -import type {RuleSetRule} from 'webpack'; - -describe('customize JS loader', () => { - function testJsLoaderFactory( - siteConfig?: Parameters[0]['siteConfig'], - ) { - return createJsLoaderFactory({ - siteConfig: { - ...siteConfig, - webpack: { - jsLoader: 'babel', - ...siteConfig?.webpack, - }, - future: { - ...DEFAULT_FUTURE_CONFIG, - ...siteConfig?.future, - }, - }, - }); - } - - it('createJsLoaderFactory defaults to babel loader', async () => { - const createJsLoader = await testJsLoaderFactory(); - expect(createJsLoader({isServer: true}).loader).toBe( - require.resolve('babel-loader'), - ); - expect(createJsLoader({isServer: false}).loader).toBe( - require.resolve('babel-loader'), - ); - }); - - it('createJsLoaderFactory accepts loaders with preset', async () => { - const createJsLoader = await testJsLoaderFactory({ - webpack: {jsLoader: 'babel'}, - }); - - expect( - createJsLoader({ - isServer: true, - }).loader, - ).toBe(require.resolve('babel-loader')); - expect( - createJsLoader({ - isServer: false, - }).loader, - ).toBe(require.resolve('babel-loader')); - }); - - it('createJsLoaderFactory allows customization', async () => { - const customJSLoader = (isServer: boolean): RuleSetRule => ({ - loader: 'my-fast-js-loader', - options: String(isServer), - }); - - const createJsLoader = await testJsLoaderFactory({ - webpack: {jsLoader: customJSLoader}, - }); - - expect( - createJsLoader({ - isServer: true, - }), - ).toEqual(customJSLoader(true)); - expect( - createJsLoader({ - isServer: false, - }), - ).toEqual(customJSLoader(false)); - }); -}); - -describe('getHttpsConfig', () => { - const originalEnv = process.env; - - beforeEach(() => { - jest.resetModules(); - process.env = {...originalEnv}; - }); - - afterAll(() => { - process.env = originalEnv; - }); - - it('returns true for HTTPS not env', async () => { - await expect(getHttpsConfig()).resolves.toBe(false); - }); - - it('returns true for HTTPS in env', async () => { - process.env.HTTPS = 'true'; - await expect(getHttpsConfig()).resolves.toBe(true); - }); - - it('returns custom certs if they are in env', async () => { - process.env.HTTPS = 'true'; - process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/host.crt'); - process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key'); - await expect(getHttpsConfig()).resolves.toEqual({ - key: expect.any(Buffer), - cert: expect.any(Buffer), - }); - }); - - it("throws if file doesn't exist", async () => { - process.env.HTTPS = 'true'; - process.env.SSL_CRT_FILE = path.join( - __dirname, - '__fixtures__/nonexistent.crt', - ); - process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key'); - await expect(getHttpsConfig()).rejects.toThrowErrorMatchingInlineSnapshot( - `"You specified SSL_CRT_FILE in your env, but the file "/packages/docusaurus/src/webpack/__tests__/__fixtures__/nonexistent.crt" can't be found."`, - ); - }); - - it('throws for invalid key', async () => { - process.env.HTTPS = 'true'; - process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/host.crt'); - process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/invalid.key'); - await expect(getHttpsConfig()).rejects.toThrow(); - }); - - it('throws for invalid cert', async () => { - process.env.HTTPS = 'true'; - process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/invalid.crt'); - process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key'); - await expect(getHttpsConfig()).rejects.toThrow(); - }); -}); From 03a564918abd84379d8fadebdac71d58d1a626c9 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 13:28:43 +0200 Subject: [PATCH 16/25] create docusaurus-babel package --- packages/docusaurus-babel/.npmignore | 3 ++ packages/docusaurus-babel/README.md | 3 ++ packages/docusaurus-babel/package.json | 45 +++++++++++++++++++ packages/docusaurus-babel/src/index.ts | 8 ++++ .../src/preset.ts} | 0 packages/docusaurus-babel/tsconfig.json | 10 +++++ packages/docusaurus-bundler/package.json | 11 +---- packages/docusaurus-bundler/src/index.ts | 2 - .../src/loaders/jsLoader.ts | 4 +- packages/docusaurus/src/babel/preset.ts | 4 +- website/babel.config.js | 2 +- 11 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 packages/docusaurus-babel/.npmignore create mode 100644 packages/docusaurus-babel/README.md create mode 100644 packages/docusaurus-babel/package.json create mode 100644 packages/docusaurus-babel/src/index.ts rename packages/{docusaurus-bundler/src/loaders/babelPreset.ts => docusaurus-babel/src/preset.ts} (100%) create mode 100644 packages/docusaurus-babel/tsconfig.json diff --git a/packages/docusaurus-babel/.npmignore b/packages/docusaurus-babel/.npmignore new file mode 100644 index 000000000000..03c9ae1e1b54 --- /dev/null +++ b/packages/docusaurus-babel/.npmignore @@ -0,0 +1,3 @@ +.tsbuildinfo* +tsconfig* +__tests__ diff --git a/packages/docusaurus-babel/README.md b/packages/docusaurus-babel/README.md new file mode 100644 index 000000000000..1ee7d3f1543e --- /dev/null +++ b/packages/docusaurus-babel/README.md @@ -0,0 +1,3 @@ +# `@docusaurus/babel` + +Docusaurus package for Babel-related utils. diff --git a/packages/docusaurus-babel/package.json b/packages/docusaurus-babel/package.json new file mode 100644 index 000000000000..0568be489597 --- /dev/null +++ b/packages/docusaurus-babel/package.json @@ -0,0 +1,45 @@ +{ + "name": "@docusaurus/babel", + "version": "3.5.2", + "description": "Docusaurus package for Babel-related utils.", + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "exports": { + "./preset": { + "types": "./lib/preset.d.ts", + "default": "./lib/preset.js" + }, + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + } + }, + "scripts": { + "build": "tsc", + "watch": "tsc --watch" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/facebook/docusaurus.git", + "directory": "packages/docusaurus-babel" + }, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@babel/runtime-corejs3": "^7.22.6", + "babel-loader": "^9.1.3", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=18.0" + } +} diff --git a/packages/docusaurus-babel/src/index.ts b/packages/docusaurus-babel/src/index.ts new file mode 100644 index 000000000000..6dad5b96b146 --- /dev/null +++ b/packages/docusaurus-babel/src/index.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export const x = 42; diff --git a/packages/docusaurus-bundler/src/loaders/babelPreset.ts b/packages/docusaurus-babel/src/preset.ts similarity index 100% rename from packages/docusaurus-bundler/src/loaders/babelPreset.ts rename to packages/docusaurus-babel/src/preset.ts diff --git a/packages/docusaurus-babel/tsconfig.json b/packages/docusaurus-babel/tsconfig.json new file mode 100644 index 000000000000..74731e2257e1 --- /dev/null +++ b/packages/docusaurus-babel/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": false, + "sourceMap": true, + "declarationMap": true + }, + "include": ["src"], + "exclude": ["**/__tests__/**"] +} diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index eaecd5715c84..f683ef264cdf 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -18,22 +18,13 @@ }, "license": "MIT", "dependencies": { - "@babel/core": "^7.23.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", + "@docusaurus/babel": "3.5.2", "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/faster": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/types": "3.5.2", "@docusaurus/utils": "3.5.2", "autoprefixer": "^10.4.14", - "babel-loader": "^9.1.3", - "babel-plugin-dynamic-import-node": "^2.3.3", "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index ef6d995387e6..c1974dac9fb4 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -23,5 +23,3 @@ export { } from './loaders/jsLoader'; export {createStyleLoadersFactory} from './loaders/styleLoader'; - -export {default as babelPreset} from './loaders/babelPreset'; diff --git a/packages/docusaurus-bundler/src/loaders/jsLoader.ts b/packages/docusaurus-bundler/src/loaders/jsLoader.ts index 6cd0d65ab4aa..d4a371260af4 100644 --- a/packages/docusaurus-bundler/src/loaders/jsLoader.ts +++ b/packages/docusaurus-bundler/src/loaders/jsLoader.ts @@ -39,7 +39,9 @@ export function getBabelOptions({ }; } return { - ...(babelOptions ?? {presets: [require.resolve('./babelPreset')]}), + ...(babelOptions ?? { + presets: [require.resolve('@docusaurus/babel/preset')], + }), babelrc: false, configFile: false, caller: {name: isServer ? 'server' : 'client'}, diff --git a/packages/docusaurus/src/babel/preset.ts b/packages/docusaurus/src/babel/preset.ts index 10210b1428d5..eb74e4085a84 100644 --- a/packages/docusaurus/src/babel/preset.ts +++ b/packages/docusaurus/src/babel/preset.ts @@ -13,4 +13,6 @@ module.exports = { presets: [require.resolve('@docusaurus/core/lib/babel/preset')], }; */ -export {babelPreset as default} from '@docusaurus/bundler'; +import BabelPreset from '@docusaurus/babel/preset'; + +export default BabelPreset; diff --git a/website/babel.config.js b/website/babel.config.js index cd005dd9cccc..25875e982f12 100644 --- a/website/babel.config.js +++ b/website/babel.config.js @@ -6,5 +6,5 @@ */ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: ['@docusaurus/babel/preset'], }; From 1589f9d50c6d21422a7ed90b353b40d166eeb938 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 14:45:54 +0200 Subject: [PATCH 17/25] move utils to docusaurus-babel --- packages/docusaurus-babel/package.json | 4 +- packages/docusaurus-babel/src/index.ts | 2 +- packages/docusaurus-babel/src/utils.ts | 48 ++++++++ packages/docusaurus-bundler/package.json | 1 - packages/docusaurus-bundler/src/index.ts | 8 +- .../src/loaders/jsLoader.ts | 41 +------ packages/docusaurus-mdx-loader/README.md | 1 - packages/docusaurus/package.json | 1 + .../src/commands/writeTranslations.ts | 5 +- .../__tests__/translationsExtractor.test.ts | 2 +- packages/docusaurus/src/webpack/base.ts | 2 +- yarn.lock | 106 +++++++++--------- 12 files changed, 111 insertions(+), 110 deletions(-) create mode 100644 packages/docusaurus-babel/src/utils.ts diff --git a/packages/docusaurus-babel/package.json b/packages/docusaurus-babel/package.json index 0568be489597..b6520bea7423 100644 --- a/packages/docusaurus-babel/package.json +++ b/packages/docusaurus-babel/package.json @@ -36,8 +36,10 @@ "@babel/preset-typescript": "^7.22.5", "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", + "@docusaurus/utils": "3.5.2", "babel-loader": "^9.1.3", - "babel-plugin-dynamic-import-node": "^2.3.3" + "babel-plugin-dynamic-import-node": "^2.3.3", + "fs-extra": "^11.1.1" }, "engines": { "node": ">=18.0" diff --git a/packages/docusaurus-babel/src/index.ts b/packages/docusaurus-babel/src/index.ts index 6dad5b96b146..40c8ed0278f9 100644 --- a/packages/docusaurus-babel/src/index.ts +++ b/packages/docusaurus-babel/src/index.ts @@ -5,4 +5,4 @@ * LICENSE file in the root directory of this source tree. */ -export const x = 42; +export {getCustomBabelConfigFilePath, getBabelOptions} from './utils'; diff --git a/packages/docusaurus-babel/src/utils.ts b/packages/docusaurus-babel/src/utils.ts new file mode 100644 index 000000000000..364d42ce8cdb --- /dev/null +++ b/packages/docusaurus-babel/src/utils.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import fs from 'fs-extra'; +import path from 'path'; +import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; +import type {TransformOptions} from '@babel/core'; + +export async function getCustomBabelConfigFilePath( + siteDir: string, +): Promise { + const customBabelConfigurationPath = path.join( + siteDir, + BABEL_CONFIG_FILE_NAME, + ); + return (await fs.pathExists(customBabelConfigurationPath)) + ? customBabelConfigurationPath + : undefined; +} + +export function getBabelOptions({ + isServer, + babelOptions, +}: { + isServer?: boolean; + babelOptions?: TransformOptions | string; +} = {}): TransformOptions { + const caller = {name: isServer ? 'server' : 'client'}; + if (typeof babelOptions === 'string') { + return { + babelrc: false, + configFile: babelOptions, + caller, + }; + } + return { + ...(babelOptions ?? { + presets: [require.resolve('@docusaurus/babel/preset')], + }), + babelrc: false, + configFile: false, + caller, + }; +} diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index f683ef264cdf..a0ac7fc99a97 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -33,7 +33,6 @@ "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", "mini-css-extract-plugin": "^2.9.1", "null-loader": "^4.0.1", "react-dev-utils": "^12.0.1", diff --git a/packages/docusaurus-bundler/src/index.ts b/packages/docusaurus-bundler/src/index.ts index c1974dac9fb4..457cdd9d2516 100644 --- a/packages/docusaurus-bundler/src/index.ts +++ b/packages/docusaurus-bundler/src/index.ts @@ -15,11 +15,5 @@ export { } from './currentBundler'; export {getMinimizers} from './minification'; - -export { - getBabelOptions, - getCustomBabelConfigFilePath, - createJsLoaderFactory, -} from './loaders/jsLoader'; - +export {createJsLoaderFactory} from './loaders/jsLoader'; export {createStyleLoadersFactory} from './loaders/styleLoader'; diff --git a/packages/docusaurus-bundler/src/loaders/jsLoader.ts b/packages/docusaurus-bundler/src/loaders/jsLoader.ts index d4a371260af4..64c2c9ced976 100644 --- a/packages/docusaurus-bundler/src/loaders/jsLoader.ts +++ b/packages/docusaurus-bundler/src/loaders/jsLoader.ts @@ -5,48 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import fs from 'fs-extra'; -import path from 'path'; -import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils'; +import {getBabelOptions} from '@docusaurus/babel'; import {importSwcJsLoaderFactory} from '../importFaster'; import type {ConfigureWebpackUtils, DocusaurusConfig} from '@docusaurus/types'; -import type {TransformOptions} from '@babel/core'; - -export async function getCustomBabelConfigFilePath( - siteDir: string, -): Promise { - const customBabelConfigurationPath = path.join( - siteDir, - BABEL_CONFIG_FILE_NAME, - ); - return (await fs.pathExists(customBabelConfigurationPath)) - ? customBabelConfigurationPath - : undefined; -} - -export function getBabelOptions({ - isServer, - babelOptions, -}: { - isServer?: boolean; - babelOptions?: TransformOptions | string; -} = {}): TransformOptions { - if (typeof babelOptions === 'string') { - return { - babelrc: false, - configFile: babelOptions, - caller: {name: isServer ? 'server' : 'client'}, - }; - } - return { - ...(babelOptions ?? { - presets: [require.resolve('@docusaurus/babel/preset')], - }), - babelrc: false, - configFile: false, - caller: {name: isServer ? 'server' : 'client'}, - }; -} const BabelJsLoaderFactory: ConfigureWebpackUtils['getJSLoader'] = ({ isServer, diff --git a/packages/docusaurus-mdx-loader/README.md b/packages/docusaurus-mdx-loader/README.md index b3a60699a6b5..51895ab92740 100644 --- a/packages/docusaurus-mdx-loader/README.md +++ b/packages/docusaurus-mdx-loader/README.md @@ -18,7 +18,6 @@ module: { { test: /\.mdx?$/, use: [ - 'babel-loader', { loader: '@docusaurus/mdx-loader', options: { diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index b76b164389a8..e858b9959c60 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -36,6 +36,7 @@ "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", "@babel/traverse": "^7.22.8", + "@docusaurus/babel": "3.5.2", "@docusaurus/bundler": "3.5.2", "@docusaurus/logger": "3.5.2", "@docusaurus/mdx-loader": "3.5.2", diff --git a/packages/docusaurus/src/commands/writeTranslations.ts b/packages/docusaurus/src/commands/writeTranslations.ts index 5eb2b151a151..22405fa8df33 100644 --- a/packages/docusaurus/src/commands/writeTranslations.ts +++ b/packages/docusaurus/src/commands/writeTranslations.ts @@ -7,10 +7,7 @@ import fs from 'fs-extra'; import path from 'path'; -import { - getCustomBabelConfigFilePath, - getBabelOptions, -} from '@docusaurus/bundler'; +import {getCustomBabelConfigFilePath, getBabelOptions} from '@docusaurus/babel'; import {loadContext, type LoadContextParams} from '../server/site'; import {initPlugins} from '../server/plugins/init'; import { diff --git a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts index abdd43e092b0..5a5cf1d64731 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts @@ -9,7 +9,7 @@ import {jest} from '@jest/globals'; import path from 'path'; import fs from 'fs-extra'; import tmp from 'tmp-promise'; -import {getBabelOptions} from '@docusaurus/bundler'; +import {getBabelOptions} from '@docusaurus/babel'; import {SRC_DIR_NAME} from '@docusaurus/utils'; import { extractSourceCodeFileTranslations, diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index bba2d9cc3e0b..a9d4adc05c96 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -7,10 +7,10 @@ import fs from 'fs-extra'; import path from 'path'; +import {getCustomBabelConfigFilePath} from '@docusaurus/babel'; import { getCSSExtractPlugin, getMinimizers, - getCustomBabelConfigFilePath, createJsLoaderFactory, } from '@docusaurus/bundler'; diff --git a/yarn.lock b/yarn.lock index ee0b0d70694e..9aeb40606209 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4574,15 +4574,15 @@ at-least-node@^1.0.0: integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== autoprefixer@^10.4.14, autoprefixer@^10.4.19: - version "10.4.19" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" - integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== dependencies: - browserslist "^4.23.0" - caniuse-lite "^1.0.30001599" + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" fraction.js "^4.3.7" normalize-range "^0.1.2" - picocolors "^1.0.0" + picocolors "^1.0.1" postcss-value-parser "^4.2.0" available-typed-arrays@^1.0.5: @@ -4913,7 +4913,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.23.0, browserslist@^4.23.1: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -5140,7 +5140,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001599, caniuse-lite@^1.0.30001646: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646: version "1.0.30001651" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138" integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== @@ -5936,7 +5936,7 @@ cosmiconfig@^7.1.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@^8.1.3, cosmiconfig@^8.2.0: +cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== @@ -6129,18 +6129,18 @@ css-functions-list@^3.1.0: integrity sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg== css-loader@^6.8.1: - version "6.8.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" - integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== + version "6.11.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" + integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== dependencies: icss-utils "^5.1.0" - postcss "^8.4.21" - postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.3" - postcss-modules-scope "^3.0.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.8" + semver "^7.5.4" css-minimizer-webpack-plugin@^5.0.1: version "5.0.1" @@ -10251,7 +10251,7 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -jiti@^1.18.2, jiti@^1.20.0: +jiti@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42" integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA== @@ -13403,10 +13403,10 @@ periscopic@^3.0.0: estree-walker "^3.0.0" is-reference "^3.0.0" -picocolors@^1.0.0, picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -13537,13 +13537,13 @@ postcss-discard-unused@^6.0.5: postcss-selector-parser "^6.0.16" postcss-loader@^7.3.3: - version "7.3.3" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.3.tgz#6da03e71a918ef49df1bb4be4c80401df8e249dd" - integrity sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA== + version "7.3.4" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" + integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== dependencies: - cosmiconfig "^8.2.0" - jiti "^1.18.2" - semver "^7.3.8" + cosmiconfig "^8.3.5" + jiti "^1.20.0" + semver "^7.5.4" postcss-media-query-parser@^0.2.3: version "0.2.3" @@ -13608,24 +13608,24 @@ postcss-minify-selectors@^6.0.4: dependencies: postcss-selector-parser "^6.0.16" -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== -postcss-modules-local-by-default@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" - integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== dependencies: postcss-selector-parser "^6.0.4" @@ -13739,9 +13739,9 @@ postcss-safe-parser@^6.0.0: integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.16" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz#3b88b9f5c5abd989ef4e2fc9ec8eedd34b20fb04" - integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -13778,14 +13778,14 @@ postcss-zindex@^6.0.2: resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-6.0.2.tgz#e498304b83a8b165755f53db40e2ea65a99b56e1" integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg== -postcss@^8.2.x, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.38: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== +postcss@^8.2.x, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: + version "8.4.47" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" + integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== dependencies: nanoid "^3.3.7" - picocolors "^1.0.0" - source-map-js "^1.2.0" + picocolors "^1.1.0" + source-map-js "^1.2.1" prebuild-install@^7.1.1: version "7.1.1" @@ -15515,10 +15515,10 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -source-map-js@^1.0.1, source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.0.1, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@0.5.13: version "0.5.13" From 785f67684cbf44b78c710c7f87f6ed78e00a12be Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 14:55:05 +0200 Subject: [PATCH 18/25] cleanup signature of extractSiteSourceCodeTranslations --- packages/docusaurus-babel/src/utils.ts | 2 ++ .../src/commands/writeTranslations.ts | 12 +++-------- .../__tests__/translationsExtractor.test.ts | 5 ++--- .../translations/translationsExtractor.ts | 21 +++++++++++++------ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/docusaurus-babel/src/utils.ts b/packages/docusaurus-babel/src/utils.ts index 364d42ce8cdb..cb058081c6c1 100644 --- a/packages/docusaurus-babel/src/utils.ts +++ b/packages/docusaurus-babel/src/utils.ts @@ -27,6 +27,8 @@ export function getBabelOptions({ babelOptions, }: { isServer?: boolean; + // TODO Docusaurus v4 fix this + // weird to have getBabelOptions take a babelOptions param babelOptions?: TransformOptions | string; } = {}): TransformOptions { const caller = {name: isServer ? 'server' : 'client'}; diff --git a/packages/docusaurus/src/commands/writeTranslations.ts b/packages/docusaurus/src/commands/writeTranslations.ts index 22405fa8df33..046a549b4f4d 100644 --- a/packages/docusaurus/src/commands/writeTranslations.ts +++ b/packages/docusaurus/src/commands/writeTranslations.ts @@ -7,7 +7,6 @@ import fs from 'fs-extra'; import path from 'path'; -import {getCustomBabelConfigFilePath, getBabelOptions} from '@docusaurus/babel'; import {loadContext, type LoadContextParams} from '../server/site'; import {initPlugins} from '../server/plugins/init'; import { @@ -103,16 +102,11 @@ Available locales are: ${context.i18n.locales.join(',')}.`, ); } - const babelOptions = getBabelOptions({ - isServer: true, - babelOptions: await getCustomBabelConfigFilePath(siteDir), - }); - const extractedCodeTranslations = await extractSiteSourceCodeTranslations( + const extractedCodeTranslations = await extractSiteSourceCodeTranslations({ siteDir, plugins, - babelOptions, - await getExtraSourceCodeFilePaths(), - ); + extraSourceCodeFilePaths: await getExtraSourceCodeFilePaths(), + }); const defaultCodeMessages = await loadPluginsDefaultCodeTranslationMessages( plugins, diff --git a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts index 5a5cf1d64731..de983479d1ce 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts @@ -694,11 +694,10 @@ export default function MyComponent(props: Props) { plugin2, {name: 'dummy', options: {}, version: {type: 'synthetic'}} as const, ] as LoadedPlugin[]; - const translations = await extractSiteSourceCodeTranslations( + const translations = await extractSiteSourceCodeTranslations({ siteDir, plugins, - TestBabelOptions, - ); + }); expect(translations).toEqual({ siteComponentFileId1: { description: 'site component 1 desc', diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index fc7767b384c4..05e326bee401 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -17,6 +17,7 @@ import { type TransformOptions, } from '@babel/core'; import {SRC_DIR_NAME} from '@docusaurus/utils'; +import {getBabelOptions, getCustomBabelConfigFilePath} from '@docusaurus/babel'; import {safeGlobby} from '../utils'; import type { InitializedPlugin, @@ -82,12 +83,20 @@ async function getSourceCodeFilePaths( return globSourceCodeFilePaths(allPaths); } -export async function extractSiteSourceCodeTranslations( - siteDir: string, - plugins: InitializedPlugin[], - babelOptions: TransformOptions, - extraSourceCodeFilePaths: string[] = [], -): Promise { +export async function extractSiteSourceCodeTranslations({ + siteDir, + plugins, + extraSourceCodeFilePaths = [], +}: { + siteDir: string; + plugins: InitializedPlugin[]; + extraSourceCodeFilePaths?: string[]; +}): Promise { + const babelOptions = getBabelOptions({ + isServer: true, + babelOptions: await getCustomBabelConfigFilePath(siteDir), + }); + // Should we warn here if the same translation "key" is found in multiple // source code files? function toTranslationFileContent( From e2fe8ddbcc620180ab66bf7f465f184e765c9b7e Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 15:34:31 +0200 Subject: [PATCH 19/25] migrate translation extraction code to babel package --- packages/docusaurus-babel/package.json | 6 +- .../babelTranslationsExtractor.test.ts | 537 ++++++++++++++++++ .../src/babelTranslationsExtractor.ts | 266 +++++++++ packages/docusaurus-babel/src/index.ts | 2 + .../package.json | 1 + .../src/utils.ts | 8 +- packages/docusaurus-utils/src/globUtils.ts | 20 +- packages/docusaurus-utils/src/index.ts | 1 + packages/docusaurus/package.json | 3 - .../src/commands/writeHeadingIds.ts | 2 +- .../__tests__/translationsExtractor.test.ts | 531 +---------------- .../translations/translationsExtractor.ts | 258 +-------- packages/docusaurus/src/server/utils.ts | 24 - .../__tests__/__snapshots__/base.test.ts.snap | 51 ++ .../src/webpack/__tests__/base.test.ts | 17 +- 15 files changed, 899 insertions(+), 828 deletions(-) create mode 100644 packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts create mode 100644 packages/docusaurus-babel/src/babelTranslationsExtractor.ts delete mode 100644 packages/docusaurus/src/server/utils.ts diff --git a/packages/docusaurus-babel/package.json b/packages/docusaurus-babel/package.json index b6520bea7423..e7e18cddd74f 100644 --- a/packages/docusaurus-babel/package.json +++ b/packages/docusaurus-babel/package.json @@ -29,6 +29,8 @@ "license": "MIT", "dependencies": { "@babel/core": "^7.23.3", + "@babel/generator": "^7.23.3", + "@babel/traverse": "^7.22.8", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.22.9", "@babel/preset-env": "^7.22.9", @@ -36,10 +38,12 @@ "@babel/preset-typescript": "^7.22.5", "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", + "@docusaurus/logger": "3.5.2", "@docusaurus/utils": "3.5.2", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", - "fs-extra": "^11.1.1" + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" }, "engines": { "node": ">=18.0" diff --git a/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts b/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts new file mode 100644 index 000000000000..f221bd235019 --- /dev/null +++ b/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts @@ -0,0 +1,537 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {jest} from '@jest/globals'; +import fs from 'fs-extra'; +import tmp from 'tmp-promise'; +import {getBabelOptions} from '../utils'; +import {extractSourceCodeFileTranslations} from '../babelTranslationsExtractor'; + +const TestBabelOptions = getBabelOptions({ + isServer: true, +}); + +async function createTmpSourceCodeFile({ + extension, + content, +}: { + extension: string; + content: string; +}) { + const file = await tmp.file({ + prefix: 'jest-createTmpSourceCodeFile', + postfix: `.${extension}`, + }); + + await fs.writeFile(file.path, content); + + return { + sourceCodeFilePath: file.path, + }; +} + +describe('extractSourceCodeFileTranslations', () => { + it('throws for bad source code', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +const default => { + +} +`, + }); + + const errorMock = jest.spyOn(console, 'error').mockImplementation(() => {}); + + await expect( + extractSourceCodeFileTranslations(sourceCodeFilePath, TestBabelOptions), + ).rejects.toThrow(); + + expect(errorMock).toHaveBeenCalledWith( + expect.stringMatching( + /Error while attempting to extract Docusaurus translations from source code file at/, + ), + ); + }); + + it('extracts nothing from untranslated source code', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +const unrelated = 42; +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [], + }); + }); + + it('extracts from a translate() functions calls', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import {translate} from '@docusaurus/Translate'; + +export default function MyComponent() { + return ( +
+ + + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + codeId: {message: 'code message', description: 'code description'}, + codeId1: {message: 'codeId1'}, + }, + warnings: [], + }); + }); + + it('extracts from a components', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Translate from '@docusaurus/Translate'; + +export default function MyComponent() { + return ( +
+ + code message + + + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + codeId: {message: 'code message', description: 'code description'}, + codeId1: {message: 'codeId1', description: 'description 2'}, + }, + warnings: [], + }); + }); + + it('extracts statically evaluable content', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Translate, {translate} from '@docusaurus/Translate'; + +const prefix = "prefix "; + +export default function MyComponent() { + return ( +
+ + + {prefix + "code message"} + + + + { + + prefix + \`Static template literal with unusual formatting!\` + } + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + 'prefix codeId comp': { + message: 'prefix code message', + description: 'prefix code description', + }, + 'prefix codeId fn': { + message: 'prefix code message', + description: 'prefix code description', + }, + 'prefix Static template literal with unusual formatting!': { + message: 'prefix Static template literal with unusual formatting!', + }, + }, + warnings: [], + }); + }); + + it('extracts from TypeScript file', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'tsx', + content: ` +import {translate} from '@docusaurus/Translate'; + +type ComponentProps = {toto: string} + +export default function MyComponent(props: ComponentProps) { + return ( +
+ + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + codeId: {message: 'code message', description: 'code description'}, + 'code message 2': { + message: 'code message 2', + description: 'code description 2', + }, + }, + warnings: [], + }); + }); + + it('does not extract from functions that is not docusaurus provided', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import translate from 'a-lib'; + +export default function somethingElse() { + const a = translate('foo'); + return bar +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [], + }); + }); + + it('does not extract from functions that is internal', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +function translate() { + return 'foo' +} + +export default function somethingElse() { + const a = translate('foo'); + return a; +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [], + }); + }); + + it('recognizes aliased imports', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Foo, {translate as bar} from '@docusaurus/Translate'; + +export function MyComponent() { + return ( +
+ + code message + + + +
+ ); +} + +export default function () { + return ( +
+ + + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + codeId: { + description: 'code description', + message: 'code message', + }, + codeId1: { + message: 'codeId1', + }, + }, + warnings: [], + }); + }); + + it('recognizes aliased imports as string literal', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import {'translate' as bar} from '@docusaurus/Translate'; + +export default function () { + return ( +
+ + + +
+ ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + codeId1: { + message: 'codeId1', + }, + }, + warnings: [], + }); + }); + + it('warns about id if no children', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Translate from '@docusaurus/Translate'; + +export default function () { + return ( + + ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [ + ` without children must have id prop. +Example: +File: ${sourceCodeFilePath} at line 6 +Full code: `, + ], + }); + }); + + it('warns about dynamic id', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Translate from '@docusaurus/Translate'; + +export default function () { + return ( + foo + ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: { + foo: { + message: 'foo', + }, + }, + warnings: [ + ` prop=id should be a statically evaluable object. +Example: Message +Dynamically constructed values are not allowed, because they prevent translations to be extracted. +File: ${sourceCodeFilePath} at line 6 +Full code: foo`, + ], + }); + }); + + it('warns about dynamic children', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import Translate from '@docusaurus/Translate'; + +export default function () { + return ( + hhh + ); +} +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [ + `Translate content could not be extracted. It has to be a static string and use optional but static props, like text. +File: ${sourceCodeFilePath} at line 6 +Full code: hhh`, + ], + }); + }); + + it('warns about dynamic translate argument', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import {translate} from '@docusaurus/Translate'; + +translate(foo); +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [ + `translate() first arg should be a statically evaluable object. +Example: translate({message: "text",id: "optional.id",description: "optional description"} +Dynamically constructed values are not allowed, because they prevent translations to be extracted. +File: ${sourceCodeFilePath} at line 4 +Full code: translate(foo)`, + ], + }); + }); + + it('warns about too many arguments', async () => { + const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + extension: 'js', + content: ` +import {translate} from '@docusaurus/Translate'; + +translate({message: 'a'}, {a: 1}, 2); +`, + }); + + const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( + sourceCodeFilePath, + TestBabelOptions, + ); + + expect(sourceCodeFileTranslations).toEqual({ + sourceCodeFilePath, + translations: {}, + warnings: [ + `translate() function only takes 1 or 2 args +File: ${sourceCodeFilePath} at line 4 +Full code: translate({ + message: 'a' +}, { + a: 1 +}, 2)`, + ], + }); + }); +}); diff --git a/packages/docusaurus-babel/src/babelTranslationsExtractor.ts b/packages/docusaurus-babel/src/babelTranslationsExtractor.ts new file mode 100644 index 000000000000..744f1aaa2161 --- /dev/null +++ b/packages/docusaurus-babel/src/babelTranslationsExtractor.ts @@ -0,0 +1,266 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import fs from 'fs-extra'; +import logger from '@docusaurus/logger'; +import traverse, {type Node} from '@babel/traverse'; +import generate from '@babel/generator'; +import { + parse, + type types as t, + type NodePath, + type TransformOptions, +} from '@babel/core'; +import type {TranslationFileContent} from '@docusaurus/types'; + +export type SourceCodeFileTranslations = { + sourceCodeFilePath: string; + translations: TranslationFileContent; + warnings: string[]; +}; + +export async function extractAllSourceCodeFileTranslations( + sourceCodeFilePaths: string[], + babelOptions: TransformOptions, +): Promise { + return Promise.all( + sourceCodeFilePaths.flatMap((sourceFilePath) => + extractSourceCodeFileTranslations(sourceFilePath, babelOptions), + ), + ); +} + +export async function extractSourceCodeFileTranslations( + sourceCodeFilePath: string, + babelOptions: TransformOptions, +): Promise { + try { + const code = await fs.readFile(sourceCodeFilePath, 'utf8'); + + const ast = parse(code, { + ...babelOptions, + ast: true, + // filename is important, because babel does not process the same files + // according to their js/ts extensions. + // See https://x.com/NicoloRibaudo/status/1321130735605002243 + filename: sourceCodeFilePath, + }) as Node; + + const translations = extractSourceCodeAstTranslations( + ast, + sourceCodeFilePath, + ); + return translations; + } catch (err) { + logger.error`Error while attempting to extract Docusaurus translations from source code file at path=${sourceCodeFilePath}.`; + throw err; + } +} + +/* +Need help understanding this? + +Useful resources: +https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md +https://github.com/formatjs/formatjs/blob/main/packages/babel-plugin-formatjs/index.ts +https://github.com/pugjs/babel-walk + */ +function extractSourceCodeAstTranslations( + ast: Node, + sourceCodeFilePath: string, +): SourceCodeFileTranslations { + function sourceWarningPart(node: Node) { + return `File: ${sourceCodeFilePath} at line ${node.loc?.start.line ?? '?'} +Full code: ${generate(node).code}`; + } + + const translations: TranslationFileContent = {}; + const warnings: string[] = []; + let translateComponentName: string | undefined; + let translateFunctionName: string | undefined; + + // First pass: find import declarations of Translate / translate. + // If not found, don't process the rest to avoid false positives + traverse(ast, { + ImportDeclaration(path) { + if ( + path.node.importKind === 'type' || + path.get('source').node.value !== '@docusaurus/Translate' + ) { + return; + } + const importSpecifiers = path.get('specifiers'); + const defaultImport = importSpecifiers.find( + (specifier): specifier is NodePath => + specifier.node.type === 'ImportDefaultSpecifier', + ); + const callbackImport = importSpecifiers.find( + (specifier): specifier is NodePath => + specifier.node.type === 'ImportSpecifier' && + (( + (specifier as NodePath).get('imported') + .node as t.Identifier + ).name === 'translate' || + ( + (specifier as NodePath).get('imported') + .node as t.StringLiteral + ).value === 'translate'), + ); + + translateComponentName = defaultImport?.get('local').node.name; + translateFunctionName = callbackImport?.get('local').node.name; + }, + }); + + traverse(ast, { + ...(translateComponentName && { + JSXElement(path) { + if ( + !path + .get('openingElement') + .get('name') + .isJSXIdentifier({name: translateComponentName}) + ) { + return; + } + function evaluateJSXProp(propName: string): string | undefined { + const attributePath = path + .get('openingElement.attributes') + .find( + (attr) => + attr.isJSXAttribute() && + attr.get('name').isJSXIdentifier({name: propName}), + ); + + if (attributePath) { + const attributeValue = attributePath.get('value') as NodePath; + + const attributeValueEvaluated = + attributeValue.isJSXExpressionContainer() + ? (attributeValue.get('expression') as NodePath).evaluate() + : attributeValue.evaluate(); + + if ( + attributeValueEvaluated.confident && + typeof attributeValueEvaluated.value === 'string' + ) { + return attributeValueEvaluated.value; + } + warnings.push( + ` prop=${propName} should be a statically evaluable object. +Example: Message +Dynamically constructed values are not allowed, because they prevent translations to be extracted. +${sourceWarningPart(path.node)}`, + ); + } + + return undefined; + } + + const id = evaluateJSXProp('id'); + const description = evaluateJSXProp('description'); + let message: string; + const childrenPath = path.get('children'); + + // Handle empty content + if (!childrenPath.length) { + if (!id) { + warnings.push(` without children must have id prop. +Example: +${sourceWarningPart(path.node)}`); + } else { + translations[id] = { + message: id, + ...(description && {description}), + }; + } + + return; + } + + // Handle single non-empty content + const singleChildren = childrenPath + // Remove empty/useless text nodes that might be around our + // translation! Makes the translation system more reliable to JSX + // formatting issues + .filter( + (children) => + !( + children.isJSXText() && + children.node.value.replace('\n', '').trim() === '' + ), + ) + .pop(); + const isJSXText = singleChildren?.isJSXText(); + const isJSXExpressionContainer = + singleChildren?.isJSXExpressionContainer() && + (singleChildren.get('expression') as NodePath).evaluate().confident; + + if (isJSXText || isJSXExpressionContainer) { + message = isJSXText + ? singleChildren.node.value.trim().replace(/\s+/g, ' ') + : String( + (singleChildren.get('expression') as NodePath).evaluate().value, + ); + + translations[id ?? message] = { + message, + ...(description && {description}), + }; + } else { + warnings.push( + `Translate content could not be extracted. It has to be a static string and use optional but static props, like text. +${sourceWarningPart(path.node)}`, + ); + } + }, + }), + + ...(translateFunctionName && { + CallExpression(path) { + if (!path.get('callee').isIdentifier({name: translateFunctionName})) { + return; + } + + const args = path.get('arguments'); + if (args.length === 1 || args.length === 2) { + const firstArgPath = args[0]!; + + // translate("x" + "y"); => translate("xy"); + const firstArgEvaluated = firstArgPath.evaluate(); + + if ( + firstArgEvaluated.confident && + typeof firstArgEvaluated.value === 'object' + ) { + const {message, id, description} = firstArgEvaluated.value as { + [propName: string]: unknown; + }; + translations[String(id ?? message)] = { + message: String(message ?? id), + ...(Boolean(description) && {description: String(description)}), + }; + } else { + warnings.push( + `translate() first arg should be a statically evaluable object. +Example: translate({message: "text",id: "optional.id",description: "optional description"} +Dynamically constructed values are not allowed, because they prevent translations to be extracted. +${sourceWarningPart(path.node)}`, + ); + } + } else { + warnings.push( + `translate() function only takes 1 or 2 args +${sourceWarningPart(path.node)}`, + ); + } + }, + }), + }); + + return {sourceCodeFilePath, translations, warnings}; +} diff --git a/packages/docusaurus-babel/src/index.ts b/packages/docusaurus-babel/src/index.ts index 40c8ed0278f9..643e05734b3d 100644 --- a/packages/docusaurus-babel/src/index.ts +++ b/packages/docusaurus-babel/src/index.ts @@ -6,3 +6,5 @@ */ export {getCustomBabelConfigFilePath, getBabelOptions} from './utils'; + +export {extractAllSourceCodeFileTranslations} from './babelTranslationsExtractor'; diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index 9ccec80c4e73..f771daeeb3ee 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -23,6 +23,7 @@ "tslib": "^2.6.0" }, "devDependencies": { + "@docusaurus/babel": "3.5.2", "@docusaurus/core": "3.5.2", "@docusaurus/logger": "3.5.2", "lodash": "^4.17.21" diff --git a/packages/docusaurus-theme-translations/src/utils.ts b/packages/docusaurus-theme-translations/src/utils.ts index 95cf7f4ec712..2b93c1af0058 100644 --- a/packages/docusaurus-theme-translations/src/utils.ts +++ b/packages/docusaurus-theme-translations/src/utils.ts @@ -14,10 +14,8 @@ import path from 'path'; import fs from 'fs-extra'; // Unsafe import, should we create a package for the translationsExtractor ?; -import { - globSourceCodeFilePaths, - extractAllSourceCodeFileTranslations, -} from '@docusaurus/core/lib/server/translations/translationsExtractor'; +import {globSourceCodeFilePaths} from '@docusaurus/core/lib/server/translations/translationsExtractor'; +import {extractAllSourceCodeFileTranslations} from '@docusaurus/babel'; import type {TranslationFileContent} from '@docusaurus/types'; async function getPackageCodePath(packageName: string) { @@ -69,7 +67,7 @@ export async function extractThemeCodeMessages( const filesExtractedTranslations = await extractAllSourceCodeFileTranslations( filePaths, { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: ['@docusaurus/babel/preset'], }, ); diff --git a/packages/docusaurus-utils/src/globUtils.ts b/packages/docusaurus-utils/src/globUtils.ts index 72b65d75d7f9..9f207df99ea2 100644 --- a/packages/docusaurus-utils/src/globUtils.ts +++ b/packages/docusaurus-utils/src/globUtils.ts @@ -10,8 +10,11 @@ import path from 'path'; import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby import {addSuffix} from '@docusaurus/utils-common'; +import Globby from 'Globby'; +import {posixPath} from './pathUtils'; + /** A re-export of the globby instance. */ -export {default as Globby} from 'globby'; +export {Globby}; /** * The default glob patterns we ignore when sourcing content. @@ -85,3 +88,18 @@ export function createAbsoluteFilePathMatcher( return (absoluteFilePath: string) => matcher(getRelativeFilePath(absoluteFilePath)); } + +// Globby that fix Windows path patterns +// See https://github.com/facebook/docusaurus/pull/4222#issuecomment-795517329 +export async function safeGlobby( + patterns: string[], + options?: Globby.GlobbyOptions, +): Promise { + // Required for Windows support, as paths using \ should not be used by globby + // (also using the windows hard drive prefix like c: is not a good idea) + const globPaths = patterns.map((dirPath) => + posixPath(path.relative(process.cwd(), dirPath)), + ); + + return Globby(globPaths, options); +} diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index d9a24a408655..06bab64e9b71 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -97,6 +97,7 @@ export {md5Hash, simpleHash, docuHash} from './hashUtils'; export { Globby, GlobExcludeDefault, + safeGlobby, createMatcher, createAbsoluteFilePathMatcher, } from './globUtils'; diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index e858b9959c60..45236bcc2c3c 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -33,9 +33,6 @@ "url": "https://github.com/facebook/docusaurus/issues" }, "dependencies": { - "@babel/core": "^7.23.3", - "@babel/generator": "^7.23.3", - "@babel/traverse": "^7.22.8", "@docusaurus/babel": "3.5.2", "@docusaurus/bundler": "3.5.2", "@docusaurus/logger": "3.5.2", diff --git a/packages/docusaurus/src/commands/writeHeadingIds.ts b/packages/docusaurus/src/commands/writeHeadingIds.ts index 909e3136b55f..3963270dc582 100644 --- a/packages/docusaurus/src/commands/writeHeadingIds.ts +++ b/packages/docusaurus/src/commands/writeHeadingIds.ts @@ -8,12 +8,12 @@ import fs from 'fs-extra'; import logger from '@docusaurus/logger'; import { + safeGlobby, writeMarkdownHeadingId, type WriteHeadingIDOptions, } from '@docusaurus/utils'; import {loadContext} from '../server/site'; import {initPlugins} from '../server/plugins/init'; -import {safeGlobby} from '../server/utils'; async function transformMarkdownFile( filepath: string, diff --git a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts index de983479d1ce..69c9ec46831f 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts @@ -9,18 +9,10 @@ import {jest} from '@jest/globals'; import path from 'path'; import fs from 'fs-extra'; import tmp from 'tmp-promise'; -import {getBabelOptions} from '@docusaurus/babel'; import {SRC_DIR_NAME} from '@docusaurus/utils'; -import { - extractSourceCodeFileTranslations, - extractSiteSourceCodeTranslations, -} from '../translationsExtractor'; +import {extractSiteSourceCodeTranslations} from '../translationsExtractor'; import type {InitializedPlugin, LoadedPlugin} from '@docusaurus/types'; -const TestBabelOptions = getBabelOptions({ - isServer: true, -}); - async function createTmpDir() { const {path: siteDirPath} = await tmp.dir({ prefix: 'jest-createTmpSiteDir', @@ -28,527 +20,6 @@ async function createTmpDir() { return siteDirPath; } -async function createTmpSourceCodeFile({ - extension, - content, -}: { - extension: string; - content: string; -}) { - const file = await tmp.file({ - prefix: 'jest-createTmpSourceCodeFile', - postfix: `.${extension}`, - }); - - await fs.writeFile(file.path, content); - - return { - sourceCodeFilePath: file.path, - }; -} - -describe('extractSourceCodeFileTranslations', () => { - it('throws for bad source code', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -const default => { - -} -`, - }); - - const errorMock = jest.spyOn(console, 'error').mockImplementation(() => {}); - - await expect( - extractSourceCodeFileTranslations(sourceCodeFilePath, TestBabelOptions), - ).rejects.toThrow(); - - expect(errorMock).toHaveBeenCalledWith( - expect.stringMatching( - /Error while attempting to extract Docusaurus translations from source code file at/, - ), - ); - }); - - it('extracts nothing from untranslated source code', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -const unrelated = 42; -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [], - }); - }); - - it('extracts from a translate() functions calls', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import {translate} from '@docusaurus/Translate'; - -export default function MyComponent() { - return ( -
- - - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - codeId: {message: 'code message', description: 'code description'}, - codeId1: {message: 'codeId1'}, - }, - warnings: [], - }); - }); - - it('extracts from a components', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Translate from '@docusaurus/Translate'; - -export default function MyComponent() { - return ( -
- - code message - - - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - codeId: {message: 'code message', description: 'code description'}, - codeId1: {message: 'codeId1', description: 'description 2'}, - }, - warnings: [], - }); - }); - - it('extracts statically evaluable content', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Translate, {translate} from '@docusaurus/Translate'; - -const prefix = "prefix "; - -export default function MyComponent() { - return ( -
- - - {prefix + "code message"} - - - - { - - prefix + \`Static template literal with unusual formatting!\` - } - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - 'prefix codeId comp': { - message: 'prefix code message', - description: 'prefix code description', - }, - 'prefix codeId fn': { - message: 'prefix code message', - description: 'prefix code description', - }, - 'prefix Static template literal with unusual formatting!': { - message: 'prefix Static template literal with unusual formatting!', - }, - }, - warnings: [], - }); - }); - - it('extracts from TypeScript file', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'tsx', - content: ` -import {translate} from '@docusaurus/Translate'; - -type ComponentProps = {toto: string} - -export default function MyComponent(props: ComponentProps) { - return ( -
- - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - codeId: {message: 'code message', description: 'code description'}, - 'code message 2': { - message: 'code message 2', - description: 'code description 2', - }, - }, - warnings: [], - }); - }); - - it('does not extract from functions that is not docusaurus provided', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import translate from 'a-lib'; - -export default function somethingElse() { - const a = translate('foo'); - return bar -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [], - }); - }); - - it('does not extract from functions that is internal', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -function translate() { - return 'foo' -} - -export default function somethingElse() { - const a = translate('foo'); - return a; -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [], - }); - }); - - it('recognizes aliased imports', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Foo, {translate as bar} from '@docusaurus/Translate'; - -export function MyComponent() { - return ( -
- - code message - - - -
- ); -} - -export default function () { - return ( -
- - - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - codeId: { - description: 'code description', - message: 'code message', - }, - codeId1: { - message: 'codeId1', - }, - }, - warnings: [], - }); - }); - - it('recognizes aliased imports as string literal', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import {'translate' as bar} from '@docusaurus/Translate'; - -export default function () { - return ( -
- - - -
- ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - codeId1: { - message: 'codeId1', - }, - }, - warnings: [], - }); - }); - - it('warns about id if no children', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Translate from '@docusaurus/Translate'; - -export default function () { - return ( - - ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [ - ` without children must have id prop. -Example: -File: ${sourceCodeFilePath} at line 6 -Full code: `, - ], - }); - }); - - it('warns about dynamic id', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Translate from '@docusaurus/Translate'; - -export default function () { - return ( - foo - ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: { - foo: { - message: 'foo', - }, - }, - warnings: [ - ` prop=id should be a statically evaluable object. -Example: Message -Dynamically constructed values are not allowed, because they prevent translations to be extracted. -File: ${sourceCodeFilePath} at line 6 -Full code: foo`, - ], - }); - }); - - it('warns about dynamic children', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import Translate from '@docusaurus/Translate'; - -export default function () { - return ( - hhh - ); -} -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [ - `Translate content could not be extracted. It has to be a static string and use optional but static props, like text. -File: ${sourceCodeFilePath} at line 6 -Full code: hhh`, - ], - }); - }); - - it('warns about dynamic translate argument', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import {translate} from '@docusaurus/Translate'; - -translate(foo); -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [ - `translate() first arg should be a statically evaluable object. -Example: translate({message: "text",id: "optional.id",description: "optional description"} -Dynamically constructed values are not allowed, because they prevent translations to be extracted. -File: ${sourceCodeFilePath} at line 4 -Full code: translate(foo)`, - ], - }); - }); - - it('warns about too many arguments', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ - extension: 'js', - content: ` -import {translate} from '@docusaurus/Translate'; - -translate({message: 'a'}, {a: 1}, 2); -`, - }); - - const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, - TestBabelOptions, - ); - - expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, - translations: {}, - warnings: [ - `translate() function only takes 1 or 2 args -File: ${sourceCodeFilePath} at line 4 -Full code: translate({ - message: 'a' -}, { - a: 1 -}, 2)`, - ], - }); - }); -}); - describe('extractSiteSourceCodeTranslations', () => { it('extracts translation from all plugins source code', async () => { const siteDir = await createTmpDir(); diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index 05e326bee401..c5fd758b85f6 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -6,19 +6,13 @@ */ import nodePath from 'path'; -import fs from 'fs-extra'; import logger from '@docusaurus/logger'; -import traverse, {type Node} from '@babel/traverse'; -import generate from '@babel/generator'; +import {safeGlobby, SRC_DIR_NAME} from '@docusaurus/utils'; import { - parse, - type types as t, - type NodePath, - type TransformOptions, -} from '@babel/core'; -import {SRC_DIR_NAME} from '@docusaurus/utils'; -import {getBabelOptions, getCustomBabelConfigFilePath} from '@docusaurus/babel'; -import {safeGlobby} from '../utils'; + getBabelOptions, + getCustomBabelConfigFilePath, + extractAllSourceCodeFileTranslations, +} from '@docusaurus/babel'; import type { InitializedPlugin, TranslationFileContent, @@ -141,245 +135,3 @@ type SourceCodeFileTranslations = { translations: TranslationFileContent; warnings: string[]; }; - -export async function extractAllSourceCodeFileTranslations( - sourceCodeFilePaths: string[], - babelOptions: TransformOptions, -): Promise { - return Promise.all( - sourceCodeFilePaths.flatMap((sourceFilePath) => - extractSourceCodeFileTranslations(sourceFilePath, babelOptions), - ), - ); -} - -export async function extractSourceCodeFileTranslations( - sourceCodeFilePath: string, - babelOptions: TransformOptions, -): Promise { - try { - const code = await fs.readFile(sourceCodeFilePath, 'utf8'); - - const ast = parse(code, { - ...babelOptions, - ast: true, - // filename is important, because babel does not process the same files - // according to their js/ts extensions. - // See https://x.com/NicoloRibaudo/status/1321130735605002243 - filename: sourceCodeFilePath, - }) as Node; - - const translations = extractSourceCodeAstTranslations( - ast, - sourceCodeFilePath, - ); - return translations; - } catch (err) { - logger.error`Error while attempting to extract Docusaurus translations from source code file at path=${sourceCodeFilePath}.`; - throw err; - } -} - -/* -Need help understanding this? - -Useful resources: -https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md -https://github.com/formatjs/formatjs/blob/main/packages/babel-plugin-formatjs/index.ts -https://github.com/pugjs/babel-walk - */ -function extractSourceCodeAstTranslations( - ast: Node, - sourceCodeFilePath: string, -): SourceCodeFileTranslations { - function sourceWarningPart(node: Node) { - return `File: ${sourceCodeFilePath} at line ${node.loc?.start.line ?? '?'} -Full code: ${generate(node).code}`; - } - - const translations: TranslationFileContent = {}; - const warnings: string[] = []; - let translateComponentName: string | undefined; - let translateFunctionName: string | undefined; - - // First pass: find import declarations of Translate / translate. - // If not found, don't process the rest to avoid false positives - traverse(ast, { - ImportDeclaration(path) { - if ( - path.node.importKind === 'type' || - path.get('source').node.value !== '@docusaurus/Translate' - ) { - return; - } - const importSpecifiers = path.get('specifiers'); - const defaultImport = importSpecifiers.find( - (specifier): specifier is NodePath => - specifier.node.type === 'ImportDefaultSpecifier', - ); - const callbackImport = importSpecifiers.find( - (specifier): specifier is NodePath => - specifier.node.type === 'ImportSpecifier' && - (( - (specifier as NodePath).get('imported') - .node as t.Identifier - ).name === 'translate' || - ( - (specifier as NodePath).get('imported') - .node as t.StringLiteral - ).value === 'translate'), - ); - - translateComponentName = defaultImport?.get('local').node.name; - translateFunctionName = callbackImport?.get('local').node.name; - }, - }); - - traverse(ast, { - ...(translateComponentName && { - JSXElement(path) { - if ( - !path - .get('openingElement') - .get('name') - .isJSXIdentifier({name: translateComponentName}) - ) { - return; - } - function evaluateJSXProp(propName: string): string | undefined { - const attributePath = path - .get('openingElement.attributes') - .find( - (attr) => - attr.isJSXAttribute() && - attr.get('name').isJSXIdentifier({name: propName}), - ); - - if (attributePath) { - const attributeValue = attributePath.get('value') as NodePath; - - const attributeValueEvaluated = - attributeValue.isJSXExpressionContainer() - ? (attributeValue.get('expression') as NodePath).evaluate() - : attributeValue.evaluate(); - - if ( - attributeValueEvaluated.confident && - typeof attributeValueEvaluated.value === 'string' - ) { - return attributeValueEvaluated.value; - } - warnings.push( - ` prop=${propName} should be a statically evaluable object. -Example: Message -Dynamically constructed values are not allowed, because they prevent translations to be extracted. -${sourceWarningPart(path.node)}`, - ); - } - - return undefined; - } - - const id = evaluateJSXProp('id'); - const description = evaluateJSXProp('description'); - let message: string; - const childrenPath = path.get('children'); - - // Handle empty content - if (!childrenPath.length) { - if (!id) { - warnings.push(` without children must have id prop. -Example: -${sourceWarningPart(path.node)}`); - } else { - translations[id] = { - message: id, - ...(description && {description}), - }; - } - - return; - } - - // Handle single non-empty content - const singleChildren = childrenPath - // Remove empty/useless text nodes that might be around our - // translation! Makes the translation system more reliable to JSX - // formatting issues - .filter( - (children) => - !( - children.isJSXText() && - children.node.value.replace('\n', '').trim() === '' - ), - ) - .pop(); - const isJSXText = singleChildren?.isJSXText(); - const isJSXExpressionContainer = - singleChildren?.isJSXExpressionContainer() && - (singleChildren.get('expression') as NodePath).evaluate().confident; - - if (isJSXText || isJSXExpressionContainer) { - message = isJSXText - ? singleChildren.node.value.trim().replace(/\s+/g, ' ') - : String( - (singleChildren.get('expression') as NodePath).evaluate().value, - ); - - translations[id ?? message] = { - message, - ...(description && {description}), - }; - } else { - warnings.push( - `Translate content could not be extracted. It has to be a static string and use optional but static props, like text. -${sourceWarningPart(path.node)}`, - ); - } - }, - }), - - ...(translateFunctionName && { - CallExpression(path) { - if (!path.get('callee').isIdentifier({name: translateFunctionName})) { - return; - } - - const args = path.get('arguments'); - if (args.length === 1 || args.length === 2) { - const firstArgPath = args[0]!; - - // translate("x" + "y"); => translate("xy"); - const firstArgEvaluated = firstArgPath.evaluate(); - - if ( - firstArgEvaluated.confident && - typeof firstArgEvaluated.value === 'object' - ) { - const {message, id, description} = firstArgEvaluated.value as { - [propName: string]: unknown; - }; - translations[String(id ?? message)] = { - message: String(message ?? id), - ...(Boolean(description) && {description: String(description)}), - }; - } else { - warnings.push( - `translate() first arg should be a statically evaluable object. -Example: translate({message: "text",id: "optional.id",description: "optional description"} -Dynamically constructed values are not allowed, because they prevent translations to be extracted. -${sourceWarningPart(path.node)}`, - ); - } - } else { - warnings.push( - `translate() function only takes 1 or 2 args -${sourceWarningPart(path.node)}`, - ); - } - }, - }), - }); - - return {sourceCodeFilePath, translations, warnings}; -} diff --git a/packages/docusaurus/src/server/utils.ts b/packages/docusaurus/src/server/utils.ts deleted file mode 100644 index d6c09dc468e1..000000000000 --- a/packages/docusaurus/src/server/utils.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path'; -import {posixPath, Globby} from '@docusaurus/utils'; - -// Globby that fix Windows path patterns -// See https://github.com/facebook/docusaurus/pull/4222#issuecomment-795517329 -export async function safeGlobby( - patterns: string[], - options?: Globby.GlobbyOptions, -): Promise { - // Required for Windows support, as paths using \ should not be used by globby - // (also using the windows hard drive prefix like c: is not a good idea) - const globPaths = patterns.map((dirPath) => - posixPath(path.relative(process.cwd(), dirPath)), - ); - - return Globby(globPaths, options); -} diff --git a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap index 88a08cdf5997..8f321b063446 100644 --- a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap +++ b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap @@ -52,3 +52,54 @@ exports[`base webpack config creates webpack aliases 1`] = ` "react-dom": "../../../../../../../node_modules/react-dom", } `; + +exports[`base webpack config uses svg rule 1`] = ` +{ + "oneOf": [ + { + "issuer": { + "and": [ + /\\\\\\.\\(\\?:tsx\\?\\|jsx\\?\\|mdx\\?\\)\\$/i, + ], + }, + "use": [ + { + "loader": "/node_modules/@svgr/webpack/dist/index.js", + "options": { + "prettier": false, + "svgo": true, + "svgoConfig": { + "plugins": [ + { + "name": "preset-default", + "params": { + "overrides": { + "removeTitle": false, + "removeViewBox": false, + }, + }, + }, + ], + }, + "titleProp": true, + }, + }, + ], + }, + { + "use": [ + { + "loader": "/node_modules/url-loader/dist/cjs.js", + "options": { + "emitFile": true, + "fallback": "/node_modules/file-loader/dist/cjs.js", + "limit": 10000, + "name": "assets/images/[name]-[contenthash].[ext]", + }, + }, + ], + }, + ], + "test": /\\\\\\.svg\\$/i, +} +`; diff --git a/packages/docusaurus/src/webpack/__tests__/base.test.ts b/packages/docusaurus/src/webpack/__tests__/base.test.ts index 61302a0cf02d..b4c29b514e17 100644 --- a/packages/docusaurus/src/webpack/__tests__/base.test.ts +++ b/packages/docusaurus/src/webpack/__tests__/base.test.ts @@ -9,7 +9,6 @@ import {jest} from '@jest/globals'; import path from 'path'; import _ from 'lodash'; import webpack from 'webpack'; -import * as utils from '@docusaurus/utils/lib/webpackUtils'; import {posixPath} from '@docusaurus/utils'; import {excludeJS, clientDir, createBaseConfig} from '../base'; import { @@ -135,20 +134,18 @@ describe('base webpack config', () => { }); it('uses svg rule', async () => { - const isServer = true; - const fileLoaderUtils = utils.getFileLoaderUtils(isServer); - const mockSvg = jest.spyOn(fileLoaderUtils.rules, 'svg'); - jest - .spyOn(utils, 'getFileLoaderUtils') - .mockImplementation(() => fileLoaderUtils); - - await createBaseConfig({ + const config = await createBaseConfig({ props, isServer: false, minify: false, faster: DEFAULT_FASTER_CONFIG, configureWebpackUtils: await createTestConfigureWebpackUtils(), }); - expect(mockSvg).toHaveBeenCalled(); + + const svgRule = (config.module?.rules ?? []).find((rule) => { + return rule && (rule as any).test.toString().includes('.svg'); + }); + expect(svgRule).toBeDefined(); + expect(svgRule).toMatchSnapshot(); }); }); From f11d38b128264287373d755e657017fb9a8a9658 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 15:37:50 +0200 Subject: [PATCH 20/25] fix import typo --- packages/docusaurus-utils/src/globUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-utils/src/globUtils.ts b/packages/docusaurus-utils/src/globUtils.ts index 9f207df99ea2..9cb1c49e9d76 100644 --- a/packages/docusaurus-utils/src/globUtils.ts +++ b/packages/docusaurus-utils/src/globUtils.ts @@ -10,7 +10,7 @@ import path from 'path'; import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby import {addSuffix} from '@docusaurus/utils-common'; -import Globby from 'Globby'; +import Globby from 'globby'; import {posixPath} from './pathUtils'; /** A re-export of the globby instance. */ From 4e4dcaa0e1aa41334927d8f5dbd53c90d546cf19 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 16:44:06 +0200 Subject: [PATCH 21/25] move babel-loader dependency for pnp --- packages/docusaurus-babel/package.json | 1 - packages/docusaurus-bundler/package.json | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-babel/package.json b/packages/docusaurus-babel/package.json index e7e18cddd74f..38349bb57cdf 100644 --- a/packages/docusaurus-babel/package.json +++ b/packages/docusaurus-babel/package.json @@ -40,7 +40,6 @@ "@babel/runtime-corejs3": "^7.22.6", "@docusaurus/logger": "3.5.2", "@docusaurus/utils": "3.5.2", - "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index a0ac7fc99a97..dd269b6a57fb 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -25,6 +25,7 @@ "@docusaurus/types": "3.5.2", "@docusaurus/utils": "3.5.2", "autoprefixer": "^10.4.14", + "babel-loader": "^9.1.3", "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", From c10a955eb9f42fab1af9822c1544cd517bd83840 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 16:49:09 +0200 Subject: [PATCH 22/25] re-add babel-core deps --- packages/docusaurus-bundler/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docusaurus-bundler/package.json b/packages/docusaurus-bundler/package.json index dd269b6a57fb..2c6b2235096b 100644 --- a/packages/docusaurus-bundler/package.json +++ b/packages/docusaurus-bundler/package.json @@ -18,6 +18,7 @@ }, "license": "MIT", "dependencies": { + "@babel/core": "^7.23.3", "@docusaurus/babel": "3.5.2", "@docusaurus/cssnano-preset": "3.5.2", "@docusaurus/faster": "3.5.2", From 715b70032d69ba56dbb3d87ed7b9ed2e36f027f3 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 17:10:06 +0200 Subject: [PATCH 23/25] extract globTranslatableSourceFiles util --- .../package.json | 1 + .../src/utils.ts | 5 ++-- packages/docusaurus-utils/src/globUtils.ts | 22 ++++++++++++++++ packages/docusaurus-utils/src/index.ts | 1 + .../src/commands/writeTranslations.ts | 8 +++--- .../translations/translationsExtractor.ts | 26 ++----------------- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index f771daeeb3ee..07a82ee4be11 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -26,6 +26,7 @@ "@docusaurus/babel": "3.5.2", "@docusaurus/core": "3.5.2", "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", "lodash": "^4.17.21" }, "engines": { diff --git a/packages/docusaurus-theme-translations/src/utils.ts b/packages/docusaurus-theme-translations/src/utils.ts index 2b93c1af0058..481d2116df62 100644 --- a/packages/docusaurus-theme-translations/src/utils.ts +++ b/packages/docusaurus-theme-translations/src/utils.ts @@ -13,8 +13,7 @@ import path from 'path'; import fs from 'fs-extra'; -// Unsafe import, should we create a package for the translationsExtractor ?; -import {globSourceCodeFilePaths} from '@docusaurus/core/lib/server/translations/translationsExtractor'; +import {globTranslatableSourceFiles} from '@docusaurus/utils'; import {extractAllSourceCodeFileTranslations} from '@docusaurus/babel'; import type {TranslationFileContent} from '@docusaurus/types'; @@ -60,7 +59,7 @@ export async function extractThemeCodeMessages( // eslint-disable-next-line no-param-reassign targetDirs ??= (await getThemes()).flatMap((theme) => theme.src); - const filePaths = (await globSourceCodeFilePaths(targetDirs)).filter( + const filePaths = (await globTranslatableSourceFiles(targetDirs)).filter( (filePath) => ['.js', '.jsx'].includes(path.extname(filePath)), ); diff --git a/packages/docusaurus-utils/src/globUtils.ts b/packages/docusaurus-utils/src/globUtils.ts index 9cb1c49e9d76..c7e2cab13421 100644 --- a/packages/docusaurus-utils/src/globUtils.ts +++ b/packages/docusaurus-utils/src/globUtils.ts @@ -103,3 +103,25 @@ export async function safeGlobby( return Globby(globPaths, options); } + +// A bit weird to put this here, but it's used by core + theme-translations +export async function globTranslatableSourceFiles( + patterns: string[], +): Promise { + // We only support extracting source code translations from these kind of files + const extensionsAllowed = new Set([ + '.js', + '.jsx', + '.ts', + '.tsx', + // TODO support md/mdx too? (may be overkill) + // need to compile the MDX to JSX first and remove front matter + // '.md', + // '.mdx', + ]); + + const filePaths = await safeGlobby(patterns); + return filePaths.filter((filePath) => + extensionsAllowed.has(path.extname(filePath)), + ); +} diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 06bab64e9b71..5a35bb672df2 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -98,6 +98,7 @@ export { Globby, GlobExcludeDefault, safeGlobby, + globTranslatableSourceFiles, createMatcher, createAbsoluteFilePathMatcher, } from './globUtils'; diff --git a/packages/docusaurus/src/commands/writeTranslations.ts b/packages/docusaurus/src/commands/writeTranslations.ts index 046a549b4f4d..ab577806faa3 100644 --- a/packages/docusaurus/src/commands/writeTranslations.ts +++ b/packages/docusaurus/src/commands/writeTranslations.ts @@ -7,6 +7,7 @@ import fs from 'fs-extra'; import path from 'path'; +import {globTranslatableSourceFiles} from '@docusaurus/utils'; import {loadContext, type LoadContextParams} from '../server/site'; import {initPlugins} from '../server/plugins/init'; import { @@ -16,10 +17,7 @@ import { loadPluginsDefaultCodeTranslationMessages, applyDefaultCodeTranslations, } from '../server/translations/translations'; -import { - extractSiteSourceCodeTranslations, - globSourceCodeFilePaths, -} from '../server/translations/translationsExtractor'; +import {extractSiteSourceCodeTranslations} from '../server/translations/translationsExtractor'; import type {InitializedPlugin} from '@docusaurus/types'; export type WriteTranslationsCLIOptions = Pick< @@ -48,7 +46,7 @@ async function getExtraSourceCodeFilePaths(): Promise { if (!themeCommonLibDir) { return []; // User may not use a Docusaurus official theme? Quite unlikely... } - return globSourceCodeFilePaths([themeCommonLibDir]); + return globTranslatableSourceFiles([themeCommonLibDir]); } async function writePluginTranslationFiles({ diff --git a/packages/docusaurus/src/server/translations/translationsExtractor.ts b/packages/docusaurus/src/server/translations/translationsExtractor.ts index c5fd758b85f6..f3a778286e13 100644 --- a/packages/docusaurus/src/server/translations/translationsExtractor.ts +++ b/packages/docusaurus/src/server/translations/translationsExtractor.ts @@ -7,7 +7,7 @@ import nodePath from 'path'; import logger from '@docusaurus/logger'; -import {safeGlobby, SRC_DIR_NAME} from '@docusaurus/utils'; +import {globTranslatableSourceFiles, SRC_DIR_NAME} from '@docusaurus/utils'; import { getBabelOptions, getCustomBabelConfigFilePath, @@ -18,21 +18,6 @@ import type { TranslationFileContent, } from '@docusaurus/types'; -// We only support extracting source code translations from these kind of files -const TranslatableSourceCodeExtension = new Set([ - '.js', - '.jsx', - '.ts', - '.tsx', - // TODO support md/mdx too? (may be overkill) - // need to compile the MDX to JSX first and remove front matter - // '.md', - // '.mdx', -]); -function isTranslatableSourceCodePath(filePath: string): boolean { - return TranslatableSourceCodeExtension.has(nodePath.extname(filePath)); -} - function getSiteSourceCodeFilePaths(siteDir: string): string[] { return [nodePath.join(siteDir, SRC_DIR_NAME)]; } @@ -53,13 +38,6 @@ function getPluginSourceCodeFilePaths(plugin: InitializedPlugin): string[] { return codePaths.map((p) => nodePath.resolve(plugin.path, p)); } -export async function globSourceCodeFilePaths( - dirPaths: string[], -): Promise { - const filePaths = await safeGlobby(dirPaths); - return filePaths.filter(isTranslatableSourceCodePath); -} - async function getSourceCodeFilePaths( siteDir: string, plugins: InitializedPlugin[], @@ -74,7 +52,7 @@ async function getSourceCodeFilePaths( const allPaths = [...sitePaths, ...pluginsPaths]; - return globSourceCodeFilePaths(allPaths); + return globTranslatableSourceFiles(allPaths); } export async function extractSiteSourceCodeTranslations({ From 766176b7d43f57d9fde66bfffad58437b9cf7c3c Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 17:10:28 +0200 Subject: [PATCH 24/25] Use @docusaurus/babel/preset for newly init sites + docs --- packages/create-docusaurus/templates/shared/babel.config.js | 2 +- website/docs/configuration.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-docusaurus/templates/shared/babel.config.js b/packages/create-docusaurus/templates/shared/babel.config.js index e00595dae7d6..ca4e55cbf11f 100644 --- a/packages/create-docusaurus/templates/shared/babel.config.js +++ b/packages/create-docusaurus/templates/shared/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: ['@docusaurus/babel/preset'], }; diff --git a/website/docs/configuration.mdx b/website/docs/configuration.mdx index 239ced56edee..8278b5cc3fe9 100644 --- a/website/docs/configuration.mdx +++ b/website/docs/configuration.mdx @@ -279,7 +279,7 @@ For new Docusaurus projects, we automatically generated a `babel.config.js` in t ```js title="babel.config.js" export default { - presets: ['@docusaurus/core/lib/babel/preset'], + presets: ['@docusaurus/babel/preset'], }; ``` From 3ca28bebf8b074e35e14fafcbb334852aa44fcc3 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 20 Sep 2024 17:25:19 +0200 Subject: [PATCH 25/25] add babel pkg to init templates --- .../create-docusaurus/templates/classic-typescript/package.json | 1 + packages/create-docusaurus/templates/classic/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/create-docusaurus/templates/classic-typescript/package.json b/packages/create-docusaurus/templates/classic-typescript/package.json index 56bba3947c42..88f1117814f7 100644 --- a/packages/create-docusaurus/templates/classic-typescript/package.json +++ b/packages/create-docusaurus/templates/classic-typescript/package.json @@ -15,6 +15,7 @@ "typecheck": "tsc" }, "dependencies": { + "@docusaurus/babel": "3.5.2", "@docusaurus/core": "3.5.2", "@docusaurus/preset-classic": "3.5.2", "@mdx-js/react": "^3.0.0", diff --git a/packages/create-docusaurus/templates/classic/package.json b/packages/create-docusaurus/templates/classic/package.json index 2d2535698e05..c0dad98a022c 100644 --- a/packages/create-docusaurus/templates/classic/package.json +++ b/packages/create-docusaurus/templates/classic/package.json @@ -14,6 +14,7 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { + "@docusaurus/babel": "3.5.2", "@docusaurus/core": "3.5.2", "@docusaurus/preset-classic": "3.5.2", "@mdx-js/react": "^3.0.0",