fix(v2): fix Webpack persistent caching (evict on swizzle/alias/config change) (#5047)

* webpack upgrade

* refactor docusaurus-utils hash fns

* Fix webpack cache eviction problems on config/aliases/swizzle changes

* Move/Rename InitPlugin type

* fix TS typos

* Add tests for webpack aliases

* fix windows tests
This commit is contained in:
Sébastien Lorber 2021-06-24 12:56:56 +02:00 committed by GitHub
parent 9916a0b4a4
commit 99270dbab2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 274 additions and 169 deletions

View file

@ -34,7 +34,7 @@
"stringify-object": "^3.3.0",
"unist-util-visit": "^2.0.2",
"url-loader": "^4.1.1",
"webpack": "^5.37.0"
"webpack": "^5.40.0"
},
"devDependencies": {
"@docusaurus/types": "2.0.0-beta.1",

View file

@ -33,7 +33,7 @@
"reading-time": "^1.3.0",
"remark-admonitions": "^1.2.1",
"tslib": "^2.2.0",
"webpack": "^5.37.0"
"webpack": "^5.40.0"
},
"peerDependencies": {
"react": "^16.8.4 || ^17.0.0",

View file

@ -44,7 +44,7 @@
"shelljs": "^0.8.4",
"tslib": "^2.2.0",
"utility-types": "^3.10.0",
"webpack": "^5.37.0"
"webpack": "^5.40.0"
},
"peerDependencies": {
"react": "^16.8.4 || ^17.0.0",

View file

@ -29,7 +29,7 @@
"remark-admonitions": "^1.2.1",
"slash": "^3.0.0",
"tslib": "^2.1.0",
"webpack": "^5.28.0"
"webpack": "^5.40.0"
},
"peerDependencies": {
"react": "^16.8.4 || ^17.0.0",

View file

@ -28,7 +28,7 @@
"react-waypoint": "^10.1.0",
"sharp": "^0.28.2",
"tslib": "^2.1.0",
"webpack": "^5.28.0"
"webpack": "^5.40.0"
},
"peerDependencies": {
"react": "^16.8.4 || ^17.0.0",

View file

@ -22,8 +22,8 @@
"babel-loader": "^8.2.2",
"clsx": "^1.1.1",
"core-js": "^2.6.5",
"terser-webpack-plugin": "^5.1.1",
"webpack": "^5.28.0",
"terser-webpack-plugin": "^5.1.3",
"webpack": "^5.40.0",
"webpack-merge": "^5.7.3",
"workbox-build": "^6.1.1",
"workbox-precaching": "^6.1.1",

View file

@ -17,7 +17,7 @@
"commander": "^5.1.0",
"joi": "^17.4.0",
"querystring": "0.2.0",
"webpack": "^5.37.0",
"webpack-merge": "^5.7.3"
"webpack": "^5.40.0",
"webpack-merge": "^5.8.0"
}
}

View file

@ -283,6 +283,13 @@ export interface Plugin<Content> {
}): ThemeConfig;
}
export type InitializedPlugin = Plugin<unknown> & {
readonly options: PluginOptions;
readonly version: DocusaurusPluginVersionInformation;
};
export type LoadedPlugin = InitializedPlugin & {readonly content: unknown};
export type PluginModule = {
<T, X>(context: LoadContext, options: T): Plugin<X>;
validateOptions?<T>(data: OptionValidationContext<T>): T;
@ -337,7 +344,8 @@ export interface RouteConfig {
priority?: number;
}
export interface ThemeAlias {
// Aliases used for Webpack resolution (when using docusaurus swizzle)
export interface ThemeAliases {
[alias: string]: string;
}

View file

@ -5,7 +5,28 @@
* LICENSE file in the root directory of this source tree.
*/
import {docuHash} from '../docuHash';
import {simpleHash, docuHash} from '../hashUtils';
describe('hashUtils', () => {
test('simpleHash', () => {
const asserts: Record<string, string> = {
'': 'd41',
'/foo-bar': '096',
'/foo/bar': '1df',
'/endi/lie': '9fa',
'/endi-lie': 'fd3',
'/yangshun/tay': '48d',
'/yangshun-tay': 'f3b',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar':
'd46',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/test1-test2':
'787',
};
Object.keys(asserts).forEach((str) => {
expect(simpleHash(str, 3)).toBe(asserts[str]);
});
});
});
describe('docuHash', () => {
test('docuHash works', () => {

View file

@ -5,28 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/
import {simpleHash, isNameTooLong, shortName} from '../pathUtils';
import {isNameTooLong, shortName} from '../pathUtils';
describe('pathUtils', () => {
test('simpleHash', () => {
const asserts: Record<string, string> = {
'': 'd41',
'/foo-bar': '096',
'/foo/bar': '1df',
'/endi/lie': '9fa',
'/endi-lie': 'fd3',
'/yangshun/tay': '48d',
'/yangshun-tay': 'f3b',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar':
'd46',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/test1-test2':
'787',
};
Object.keys(asserts).forEach((file) => {
expect(simpleHash(file, 3)).toBe(asserts[file]);
});
});
test('isNameTooLong', () => {
const asserts: Record<string, boolean> = {
'': false,

View file

@ -5,10 +5,19 @@
* LICENSE file in the root directory of this source tree.
*/
import {createHash} from 'crypto';
import {kebabCase} from 'lodash';
import {shortName, isNameTooLong} from './pathUtils';
import {shortName, isNameTooLong, simpleHash} from './pathUtils';
export function md5Hash(str: string): string {
return createHash('md5').update(str).digest('hex');
}
export function simpleHash(str: string, length: number): string {
return md5Hash(str).substr(0, length);
}
// Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
/**
* Given an input string, convert to kebab-case and append a hash.
* Avoid str collision.

View file

@ -22,8 +22,7 @@ import {
import resolvePathnameUnsafe from 'resolve-pathname';
import {posixPath as posixPathImport} from './posixPath';
import {simpleHash} from './pathUtils';
import {docuHash} from './docuHash';
import {simpleHash, docuHash} from './hashUtils';
export const posixPath = posixPathImport;
@ -32,8 +31,7 @@ export * from './codeTranslationsUtils';
export * from './markdownParser';
export * from './markdownLinks';
export * from './escapePath';
export * from './docuHash';
export {simpleHash} from './pathUtils';
export {md5Hash, simpleHash, docuHash} from './hashUtils';
const fileHash = new Map();
export async function generate(

View file

@ -7,8 +7,6 @@
// Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
import {createHash} from 'crypto';
// MacOS (APFS) and Windows (NTFS) filename length limit = 255 chars, Others = 255 bytes
const MAX_PATH_SEGMENT_CHARS = 255;
const MAX_PATH_SEGMENT_BYTES = 255;
@ -42,7 +40,3 @@ export const shortName = (str: string): string => {
)
.toString();
};
export function simpleHash(str: string, length: number): string {
return createHash('md5').update(str).digest('hex').substr(0, length);
}

View file

@ -63,10 +63,10 @@
"chokidar": "^3.5.1",
"clean-css": "^5.1.2",
"commander": "^5.1.0",
"copy-webpack-plugin": "^8.1.1",
"copy-webpack-plugin": "^9.0.0",
"core-js": "^3.9.1",
"css-loader": "^5.1.1",
"css-minimizer-webpack-plugin": "^2.0.0",
"css-minimizer-webpack-plugin": "^3.0.1",
"cssnano": "^5.0.4",
"del": "^6.0.0",
"detect-port": "^1.3.0",
@ -79,7 +79,7 @@
"globby": "^11.0.2",
"html-minifier-terser": "^5.1.1",
"html-tags": "^3.1.0",
"html-webpack-plugin": "^5.3.1",
"html-webpack-plugin": "^5.3.2",
"import-fresh": "^3.3.0",
"is-root": "^2.1.0",
"leven": "^3.1.0",
@ -105,15 +105,15 @@
"shelljs": "^0.8.4",
"std-env": "^2.2.1",
"strip-ansi": "^6.0.0",
"terser-webpack-plugin": "^5.1.2",
"terser-webpack-plugin": "^5.1.3",
"tslib": "^2.2.0",
"update-notifier": "^5.1.0",
"url-loader": "^4.1.1",
"wait-on": "^5.3.0",
"webpack": "^5.37.0",
"webpack": "^5.40.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.7.3",
"webpack-merge": "^5.8.0",
"webpackbar": "^5.0.0-3"
},
"peerDependencies": {

View file

@ -4,9 +4,9 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {ConfigOptions} from '@docusaurus/types';
import {ConfigOptions, InitializedPlugin} from '@docusaurus/types';
import {loadContext, loadPluginConfigs} from '../server';
import initPlugins, {InitPlugin} from '../server/plugins/init';
import initPlugins from '../server/plugins/init';
import {
writePluginTranslations,
@ -25,7 +25,7 @@ async function writePluginTranslationFiles({
options,
}: {
siteDir: string;
plugin: InitPlugin;
plugin: InitializedPlugin;
locale: string;
options: WriteTranslationsOptions;
}) {

View file

@ -6,8 +6,12 @@
*/
import htmlTagObjectToString from './htmlTags';
import {InjectedHtmlTags, HtmlTagObject, HtmlTags} from '@docusaurus/types';
import {LoadedPlugin} from '../plugins';
import {
InjectedHtmlTags,
HtmlTagObject,
HtmlTags,
LoadedPlugin,
} from '@docusaurus/types';
function toString(val: string | HtmlTagObject): string {
return typeof val === 'string' ? val : htmlTagObjectToString(val);

View file

@ -13,14 +13,12 @@ import {
DEFAULT_BUILD_DIR_NAME,
DEFAULT_CONFIG_FILE_NAME,
GENERATED_FILES_DIR_NAME,
THEME_PATH,
} from '../constants';
import loadClientModules from './client-modules';
import loadConfig from './config';
import {loadPlugins} from './plugins';
import loadPresets from './presets';
import loadRoutes from './routes';
import loadThemeAlias from './themes';
import {
DocusaurusConfig,
DocusaurusSiteMetadata,
@ -178,14 +176,6 @@ export async function load(
`export default ${JSON.stringify(siteConfig, null, 2)};`,
);
// Themes.
const fallbackTheme = path.resolve(__dirname, '../client/theme-fallback');
const pluginThemes: string[] = plugins
.map((plugin) => plugin.getThemePath && plugin.getThemePath())
.filter((x): x is string => Boolean(x));
const userTheme = path.resolve(siteDir, THEME_PATH);
const alias = loadThemeAlias([fallbackTheme, ...pluginThemes], [userTheme]);
// Make a fake plugin to:
// - Resolve aliased theme components
// - Inject scripts/stylesheets
@ -202,11 +192,6 @@ export async function load(
getClientModules() {
return siteConfigClientModules;
},
configureWebpack: () => ({
resolve: {
alias,
},
}),
injectHtmlTags: () => {
const stylesheetsTags = stylesheets.map((source) =>
typeof source === 'string'

View file

@ -5,11 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
import {InitializedPlugin} from '@docusaurus/types';
import {ensureUniquePluginInstanceIds} from '../pluginIds';
import {InitPlugin} from '../init';
function createTestPlugin(name: string, id?: string): InitPlugin {
function createTestPlugin(name: string, id?: string): InitializedPlugin {
// @ts-expect-error: good enough for tests
return {
name,

View file

@ -16,8 +16,10 @@ import {
AllContent,
TranslationFiles,
ThemeConfig,
LoadedPlugin,
InitializedPlugin,
} from '@docusaurus/types';
import initPlugins, {InitPlugin} from './init';
import initPlugins from './init';
import chalk from 'chalk';
import {DEFAULT_PLUGIN_ID} from '../../constants';
import {chain} from 'lodash';
@ -53,8 +55,6 @@ export function sortConfig(routeConfigs: RouteConfig[]): void {
});
}
export type LoadedPlugin = InitPlugin & {content: unknown};
export async function loadPlugins({
pluginConfigs,
context,
@ -68,7 +68,7 @@ export async function loadPlugins({
themeConfigTranslated: ThemeConfig;
}> {
// 1. Plugin Lifecycle - Initialization/Constructor.
const plugins: InitPlugin[] = initPlugins({
const plugins: InitializedPlugin[] = initPlugins({
pluginConfigs,
context,
});

View file

@ -12,9 +12,9 @@ import {
ImportedPluginModule,
LoadContext,
PluginModule,
Plugin,
PluginConfig,
PluginOptions,
InitializedPlugin,
} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '../../constants';
import {getPluginVersion} from '../versions';
@ -124,18 +124,13 @@ function getThemeValidationFunction(
}
}
export type InitPlugin = Plugin<unknown> & {
readonly options: PluginOptions;
readonly version: DocusaurusPluginVersionInformation;
};
export default function initPlugins({
pluginConfigs,
context,
}: {
pluginConfigs: PluginConfig[];
context: LoadContext;
}): InitPlugin[] {
}): InitializedPlugin[] {
// We need to resolve plugins from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these plugins.
// We need to fallback to createRequireFromPath since createRequire is only available in node v12.
@ -192,7 +187,7 @@ export default function initPlugins({
}
}
const plugins: InitPlugin[] = pluginConfigs
const plugins: InitializedPlugin[] = pluginConfigs
.map((pluginConfig) => {
if (!pluginConfig) {
return null;

View file

@ -6,12 +6,14 @@
*/
import {groupBy} from 'lodash';
import {InitPlugin} from './init';
import {DEFAULT_PLUGIN_ID} from '../../constants';
import {InitializedPlugin} from '@docusaurus/types';
// It is forbidden to have 2 plugins of the same name sharing the same id
// this is required to support multi-instance plugins without conflict
export function ensureUniquePluginInstanceIds(plugins: InitPlugin[]): void {
export function ensureUniquePluginInstanceIds(
plugins: InitializedPlugin[],
): void {
const pluginsByName = groupBy(plugins, (p) => p.name);
Object.entries(pluginsByName).forEach(([pluginName, pluginInstances]) => {
const pluginInstancesById = groupBy(

View file

@ -6,15 +6,15 @@
*/
import path from 'path';
import loadThemeAlias from '../index';
import {loadThemeAliases} from '../index';
describe('loadThemeAlias', () => {
describe('loadThemeAliases', () => {
test('next alias can override the previous alias', () => {
const fixtures = path.join(__dirname, '__fixtures__');
const theme1Path = path.join(fixtures, 'theme-1');
const theme2Path = path.join(fixtures, 'theme-2');
const alias = loadThemeAlias([theme1Path, theme2Path]);
const alias = loadThemeAliases([theme1Path, theme2Path]);
expect(alias).toEqual({
'@theme-init/Layout': path.join(theme1Path, 'Layout.js'), // TODO: Write separate test case for this?
'@theme/Footer': path.join(theme1Path, 'Footer/index.js'),

View file

@ -9,13 +9,14 @@ import globby from 'globby';
import fs from 'fs-extra';
import path from 'path';
import {fileToPath, posixPath, normalizeUrl} from '@docusaurus/utils';
import {ThemeAlias} from '@docusaurus/types';
import {ThemeAliases} from '@docusaurus/types';
import {sortBy} from 'lodash';
// TODO make async
export default function themeAlias(
themePath: string,
addOriginalAlias: boolean,
): ThemeAlias {
): ThemeAliases {
if (!fs.pathExistsSync(themePath)) {
return {};
}
@ -30,7 +31,7 @@ export default function themeAlias(
file.endsWith('/index.js'),
);
const aliases: ThemeAlias = {};
const aliases: ThemeAliases = {};
sortedThemeComponentFiles.forEach((relativeSource) => {
const filePath = path.join(themePath, relativeSource);

View file

@ -5,13 +5,17 @@
* LICENSE file in the root directory of this source tree.
*/
import {ThemeAlias} from '@docusaurus/types';
import {ThemeAliases, LoadedPlugin} from '@docusaurus/types';
import path from 'path';
import {THEME_PATH} from '../../constants';
import themeAlias from './alias';
const ThemeFallbackDir = path.resolve(__dirname, '../../client/theme-fallback');
function buildThemeAliases(
themeAliases: ThemeAlias,
aliases: ThemeAlias = {},
): ThemeAlias {
themeAliases: ThemeAliases,
aliases: ThemeAliases = {},
): ThemeAliases {
Object.keys(themeAliases).forEach((aliasKey) => {
if (aliasKey in aliases) {
const componentName = aliasKey.substring(aliasKey.indexOf('/') + 1);
@ -24,11 +28,11 @@ function buildThemeAliases(
return aliases;
}
export default function loadThemeAlias(
export function loadThemeAliases(
themePaths: string[],
userThemePaths: string[] = [],
): ThemeAlias {
let aliases = {};
): ThemeAliases {
let aliases = {}; // TODO refactor, inelegant side-effect
themePaths.forEach((themePath) => {
const themeAliases = themeAlias(themePath, true);
@ -42,3 +46,17 @@ export default function loadThemeAlias(
return aliases;
}
export function loadPluginsThemeAliases({
siteDir,
plugins,
}: {
siteDir: string;
plugins: LoadedPlugin[];
}): ThemeAliases {
const pluginThemes: string[] = plugins
.map((plugin) => (plugin.getThemePath ? plugin.getThemePath() : undefined))
.filter((x): x is string => Boolean(x));
const userTheme = path.resolve(siteDir, THEME_PATH);
return loadThemeAliases([ThemeFallbackDir, ...pluginThemes], [userTheme]);
}

View file

@ -17,9 +17,12 @@ import {
} from '../translations';
import fs from 'fs-extra';
import tmp from 'tmp-promise';
import {TranslationFile, TranslationFileContent} from '@docusaurus/types';
import {
InitializedPlugin,
TranslationFile,
TranslationFileContent,
} from '@docusaurus/types';
import path from 'path';
import {InitPlugin} from '../../plugins/init';
async function createTmpSiteDir() {
const {path: siteDirPath} = await tmp.dir({
@ -467,27 +470,27 @@ describe('localizePluginTranslationFile', () => {
describe('getPluginsDefaultCodeTranslationMessages', () => {
function createTestPlugin(
fn: InitPlugin['getDefaultCodeTranslationMessages'],
): InitPlugin {
return {getDefaultCodeTranslationMessages: fn} as InitPlugin;
fn: InitializedPlugin['getDefaultCodeTranslationMessages'],
): InitializedPlugin {
return {getDefaultCodeTranslationMessages: fn} as InitializedPlugin;
}
test('for empty plugins', async () => {
const plugins: InitPlugin[] = [];
const plugins: InitializedPlugin[] = [];
await expect(
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({});
});
test('for 1 plugin without lifecycle', async () => {
const plugins: InitPlugin[] = [createTestPlugin(undefined)];
const plugins: InitializedPlugin[] = [createTestPlugin(undefined)];
await expect(
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({});
});
test('for 1 plugin with lifecycle', async () => {
const plugins: InitPlugin[] = [
const plugins: InitializedPlugin[] = [
createTestPlugin(async () => ({
a: '1',
b: '2',
@ -502,7 +505,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
});
test('for 2 plugins with lifecycles', async () => {
const plugins: InitPlugin[] = [
const plugins: InitializedPlugin[] = [
createTestPlugin(async () => ({
a: '1',
b: '2',
@ -523,7 +526,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
});
test('for realistic use-case', async () => {
const plugins: InitPlugin[] = [
const plugins: InitializedPlugin[] = [
createTestPlugin(undefined),
createTestPlugin(async () => ({
a: '1',

View file

@ -13,7 +13,7 @@ import {
} from '../translationsExtractor';
import {getBabelOptions} from '../../../webpack/utils';
import path from 'path';
import {InitPlugin} from '../../plugins/init';
import {InitializedPlugin} from '@docusaurus/types';
import {SRC_DIR_NAME} from '../../../constants';
const TestBabelOptions = getBabelOptions({
@ -258,7 +258,7 @@ export default function MySiteComponent1() {
`,
);
function createTestPlugin(pluginDir: string): InitPlugin {
function createTestPlugin(pluginDir: string): InitializedPlugin {
// @ts-expect-error: good enough for this test
return {
name: 'abc',

View file

@ -6,12 +6,12 @@
*/
import path from 'path';
import fs from 'fs-extra';
import {InitPlugin} from '../plugins/init';
import {mapValues, difference} from 'lodash';
import {
TranslationFileContent,
TranslationFile,
TranslationMessage,
InitializedPlugin,
} from '@docusaurus/types';
import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils';
import {Joi} from '@docusaurus/utils-validation';
@ -193,7 +193,7 @@ function getPluginTranslationFilePath({
locale,
translationFilePath,
}: TranslationContext & {
plugin: InitPlugin;
plugin: InitializedPlugin;
translationFilePath: string;
}): string {
const dirPath = getPluginI18nPath({
@ -213,7 +213,7 @@ export async function writePluginTranslations({
translationFile,
options,
}: TranslationContext & {
plugin: InitPlugin;
plugin: InitializedPlugin;
translationFile: TranslationFile;
options?: WriteTranslationsOptions;
}): Promise<void> {
@ -236,7 +236,7 @@ export async function localizePluginTranslationFile({
locale,
translationFile,
}: TranslationContext & {
plugin: InitPlugin;
plugin: InitializedPlugin;
translationFile: TranslationFile;
}): Promise<TranslationFile> {
const filePath = getPluginTranslationFilePath({
@ -263,7 +263,7 @@ export async function localizePluginTranslationFile({
}
export async function getPluginsDefaultCodeTranslationMessages(
plugins: InitPlugin[],
plugins: InitializedPlugin[],
): Promise<Record<string, string>> {
const pluginsMessages = await Promise.all(
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),

View file

@ -10,9 +10,12 @@ import generate from '@babel/generator';
import chalk from 'chalk';
import {parse, types as t, NodePath, TransformOptions} from '@babel/core';
import {flatten} from 'lodash';
import {TranslationFileContent, TranslationMessage} from '@docusaurus/types';
import {
InitializedPlugin,
TranslationFileContent,
TranslationMessage,
} from '@docusaurus/types';
import nodePath from 'path';
import {InitPlugin} from '../plugins/init';
import {SRC_DIR_NAME} from '../../constants';
import {safeGlobby} from '../utils';
@ -35,7 +38,7 @@ function getSiteSourceCodeFilePaths(siteDir: string): string[] {
return [nodePath.join(siteDir, SRC_DIR_NAME)];
}
function getPluginSourceCodeFilePaths(plugin: InitPlugin): string[] {
function getPluginSourceCodeFilePaths(plugin: InitializedPlugin): string[] {
// The getPathsToWatch() generally returns the js/jsx/ts/tsx/md/mdx file paths
// We can use this method as well to know which folders we should try to extract translations from
// Hacky/implicit, but do we want to introduce a new lifecycle method just for that???
@ -59,7 +62,7 @@ export async function globSourceCodeFilePaths(
async function getSourceCodeFilePaths(
siteDir: string,
plugins: InitPlugin[],
plugins: InitializedPlugin[],
): Promise<string[]> {
const sitePaths = getSiteSourceCodeFilePaths(siteDir);
@ -75,7 +78,7 @@ async function getSourceCodeFilePaths(
export async function extractSiteSourceCodeTranslations(
siteDir: string,
plugins: InitPlugin[],
plugins: InitializedPlugin[],
babelOptions: TransformOptions,
): Promise<TranslationFileContent> {
// Should we warn here if the same translation "key" is found in multiple source code files?

View file

@ -1,5 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`base webpack config should create webpack aliases 1`] = `
Object {
"@docusaurus/BrowserOnly": "../../../../client/exports/BrowserOnly.tsx",
"@docusaurus/ComponentCreator": "../../../../client/exports/ComponentCreator.tsx",
"@docusaurus/ExecutionEnvironment": "../../../../client/exports/ExecutionEnvironment.ts",
"@docusaurus/Head": "../../../../client/exports/Head.tsx",
"@docusaurus/Interpolate": "../../../../client/exports/Interpolate.tsx",
"@docusaurus/Link": "../../../../client/exports/Link.tsx",
"@docusaurus/Noop": "../../../../client/exports/Noop.ts",
"@docusaurus/Translate": "../../../../client/exports/Translate.tsx",
"@docusaurus/constants": "../../../../client/exports/constants.ts",
"@docusaurus/context": "../../../../client/exports/context.ts",
"@docusaurus/isInternalUrl": "../../../../client/exports/isInternalUrl.ts",
"@docusaurus/renderRoutes": "../../../../client/exports/renderRoutes.ts",
"@docusaurus/router": "../../../../client/exports/router.ts",
"@docusaurus/useBaseUrl": "../../../../client/exports/useBaseUrl.ts",
"@docusaurus/useDocusaurusContext": "../../../../client/exports/useDocusaurusContext.ts",
"@docusaurus/useGlobalData": "../../../../client/exports/useGlobalData.ts",
"@generated": "../../../../../../..",
"@site": "",
"@theme-init/PluginThemeComponentOverridden": "pluginThemeFolder/PluginThemeComponentOverridden.js",
"@theme-original/Layout": "../../../../client/theme-fallback/Layout/index.js",
"@theme-original/Loading": "../../../../client/theme-fallback/Loading/index.js",
"@theme-original/NotFound": "../../../../client/theme-fallback/NotFound/index.js",
"@theme-original/PluginThemeComponent1": "pluginThemeFolder/PluginThemeComponent1.js",
"@theme-original/PluginThemeComponentOverridden": "pluginThemeFolder/PluginThemeComponentOverridden.js",
"@theme-original/Root": "../../../../client/theme-fallback/Root/index.js",
"@theme-original/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js",
"@theme/Layout": "../../../../client/theme-fallback/Layout/index.js",
"@theme/Loading": "../../../../client/theme-fallback/Loading/index.js",
"@theme/NotFound": "../../../../client/theme-fallback/NotFound/index.js",
"@theme/PluginThemeComponent1": "pluginThemeFolder/PluginThemeComponent1.js",
"@theme/PluginThemeComponentOverridden": "src/theme/PluginThemeComponentOverridden.js",
"@theme/Root": "../../../../client/theme-fallback/Root/index.js",
"@theme/UserThemeComponent1": "src/theme/UserThemeComponent1.js",
"@theme/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js",
"@theme/subfolder/UserThemeComponent2": "src/theme/subfolder/UserThemeComponent2.js",
"react-loadable": "../../../../../../../@docusaurus/react-loadable",
}
`;
exports[`getDocusaurusAliases() return appropriate webpack aliases 1`] = `
Object {
"@docusaurus/BrowserOnly": "../../client/exports/BrowserOnly.tsx",

View file

@ -16,6 +16,7 @@ import {
import * as utils from '../utils';
import {mapValues} from 'lodash';
import {posixPath} from '@docusaurus/utils';
import {Props, ThemeAliases} from '@docusaurus/types';
describe('babel transpilation exclude logic', () => {
test('always transpile client dir files', () => {
@ -78,9 +79,9 @@ describe('getDocusaurusAliases()', () => {
});
describe('base webpack config', () => {
const props = {
const props: Props = {
outDir: '',
siteDir: '',
siteDir: path.resolve(__dirname, '__fixtures__', 'base_test_site'),
siteConfig: {},
baseUrl: '',
generatedFilesDir: '',
@ -91,12 +92,35 @@ describe('base webpack config', () => {
siteMetadata: {
docusaurusVersion: '2.0.0-alpha.70',
},
plugins: [
{
getThemePath() {
return path.resolve(
__dirname,
'__fixtures__',
'base_test_site',
'pluginThemeFolder',
);
},
},
],
};
afterEach(() => {
jest.restoreAllMocks();
});
test('should create webpack aliases', () => {
// @ts-expect-error: Docusaurus webpack alias is always an object
const aliases: ThemeAliases =
createBaseConfig(props, true).resolve?.alias ?? {};
// Make aliases relative so that test work on all computers
const relativeAliases = mapValues(aliases, (a) =>
posixPath(path.relative(props.siteDir, a)),
);
expect(relativeAliases).toMatchSnapshot();
});
test('should use svg rule', () => {
const fileLoaderUtils = utils.getFileLoaderUtils();
const mockSvg = jest.spyOn(fileLoaderUtils.rules, 'svg');

View file

@ -19,6 +19,8 @@ import {
} from './utils';
import {STATIC_DIR_NAME} from '../constants';
import SharedModuleAliases from './sharedModuleAliases';
import {loadPluginsThemeAliases} from '../server/themes';
import {md5Hash} from '@docusaurus/utils';
const CSS_REGEX = /\.css$/;
const CSS_MODULE_REGEX = /\.module\.css$/;
@ -74,10 +76,12 @@ export function createBaseConfig(
outDir,
siteDir,
siteConfig,
siteConfigPath,
baseUrl,
generatedFilesDir,
routesPaths,
siteMetadata,
plugins,
} = props;
const totalPages = routesPaths.length;
const isProd = process.env.NODE_ENV === 'production';
@ -89,25 +93,32 @@ export function createBaseConfig(
const name = isServer ? 'server' : 'client';
const mode = isProd ? 'production' : 'development';
const themeAliases = loadPluginsThemeAliases({siteDir, plugins});
return {
mode,
name,
cache: {
// TODO temporary env variable to reduce risk of Webpack 5 release
// maybe expose an official api, once this is solved? https://github.com/webpack/webpack/issues/13034
type:
(process.env.DOCUSAURUS_WEBPACK_CACHE_TYPE as 'filesystem') ||
'filesystem',
type: 'filesystem',
// Can we share the same cache across locales?
// Exploring that question at https://github.com/webpack/webpack/issues/13034
// name: `${name}-${mode}`,
name: `${name}-${mode}-${props.i18n.currentLocale}`,
version: siteMetadata.docusaurusVersion,
// When version string changes, cache is evicted
version: [
siteMetadata.docusaurusVersion,
// Webpack does not evict the cache correctly on alias/swizzle change, so we force eviction.
// See https://github.com/webpack/webpack/issues/13627
md5Hash(JSON.stringify(themeAliases)),
].join('-'),
// When one of those modules/dependencies change (including transitive deps), cache is invalidated
buildDependencies: {
// When one of dependencies change, cache is invalidated
config: [
__filename,
path.join(__dirname, isServer ? 'server.js' : 'client.js'),
// Docusaurus config changes can affect MDX/JSX compilation, so we'd rather evict the cache.
// See https://github.com/questdb/questdb.io/issues/493
siteConfigPath,
],
},
},
@ -147,6 +158,7 @@ export function createBaseConfig(
// so we use fine-grained aliases instead
// '@docusaurus': path.resolve(__dirname, '../client/exports'),
...getDocusaurusAliases(),
...themeAliases,
},
// This allows you to set a fallback for where Webpack should look for modules.
// We want `@docusaurus/core` own dependencies/`node_modules` to "win" if there is conflict

View file

@ -6716,13 +6716,13 @@ copy-text-to-clipboard@^3.0.1:
resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c"
integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==
copy-webpack-plugin@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz#3f697e162764925c2f0d235f380676125508fd26"
integrity sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==
copy-webpack-plugin@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.0.0.tgz#2bf592785d2fcdde9342dfed3676490fe0aa7ce8"
integrity sha512-k8UB2jLIb1Jip2nZbCz83T/XfhfjX6mB1yLJNYKrpYi7FQimfOoFv/0//iT6HV1K8FwUB5yUbCcnpLebJXJTug==
dependencies:
fast-glob "^3.2.5"
glob-parent "^5.1.1"
glob-parent "^6.0.0"
globby "^11.0.3"
normalize-path "^3.0.0"
p-limit "^3.1.0"
@ -6941,13 +6941,13 @@ css-loader@^5.1.1:
schema-utils "^3.0.0"
semver "^7.3.5"
css-minimizer-webpack-plugin@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-2.0.0.tgz#3c42f6624ed4cf4780dd963e23ee649e5a25c1a8"
integrity sha512-cG/uc94727tx5pBNtb1Sd7gvUPzwmcQi1lkpfqTpdkuNq75hJCw7bIVsCNijLm4dhDcr1atvuysl2rZqOG8Txw==
css-minimizer-webpack-plugin@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.1.tgz#2f866079411d42309a485512642c0cb08b5468ae"
integrity sha512-RGFIv6iZWUPO2T1vE5+5pNCSs2H2xtHYRdfZPiiNH8Of6QOn9BeFnZSoHiQMkmsxOO/JkPe4BpKfs7slFIWcTA==
dependencies:
cssnano "^5.0.0"
jest-worker "^26.3.0"
jest-worker "^27.0.2"
p-limit "^3.0.2"
postcss "^8.2.9"
schema-utils "^3.0.0"
@ -8008,10 +8008,10 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
es-module-lexer@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e"
integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==
es-module-lexer@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.6.0.tgz#e72ab05b7412e62b9be37c37a09bdb6000d706f0"
integrity sha512-f8kcHX1ArhllUtb/wVSyvygoKCznIjnxhLxy7TCvIiMdT7fL4ZDTIKaadMe6eLvOXg6Wk02UeoFgUoZ2EKZZUA==
es-to-primitive@^1.2.1:
version "1.2.1"
@ -9480,13 +9480,20 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0:
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-parent@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.0.tgz#f851b59b388e788f3a44d63fab50382b2859c33c"
integrity sha512-Hdd4287VEJcZXUwv1l8a+vXC1GjOQqXe+VS30w/ypihpcnu9M1n3xeYeJu5CBpeEQj2nAab2xxz28GuA3vp4Ww==
dependencies:
is-glob "^4.0.1"
glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@ -10107,15 +10114,15 @@ html-void-elements@^1.0.0:
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483"
integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==
html-webpack-plugin@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz#8797327548e3de438e3494e0c6d06f181a7f20d1"
integrity sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==
html-webpack-plugin@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.3.2.tgz#7b04bf80b1f6fe84a6d3f66c8b79d64739321b08"
integrity sha512-HvB33boVNCz2lTyBsSiMffsJ+m0YLIQ+pskblXgN9fnjS1BgEcuAfdInfXfGrkdXV406k9FiDi86eVCDBgJOyQ==
dependencies:
"@types/html-minifier-terser" "^5.0.0"
html-minifier-terser "^5.0.1"
lodash "^4.17.20"
pretty-error "^2.1.1"
lodash "^4.17.21"
pretty-error "^3.0.4"
tapable "^2.0.0"
htmlparser2@^3.10.0, htmlparser2@^3.9.1:
@ -11568,7 +11575,7 @@ jest-watcher@^26.6.2:
jest-util "^26.6.2"
string-length "^4.0.1"
jest-worker@^26.2.1, jest-worker@^26.3.0, jest-worker@^26.6.2:
jest-worker@^26.2.1, jest-worker@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
@ -15156,13 +15163,13 @@ pretty-bytes@^5.3.0:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
pretty-error@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"
integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==
pretty-error@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-3.0.4.tgz#94b1d54f76c1ed95b9c604b9de2194838e5b574e"
integrity sha512-ytLFLfv1So4AO1UkoBF6GXQgJRaKbiSiGFICaOPNwQ3CMvBvXpLRubeQWyPGnsbV/t9ml9qto6IeCsho0aEvwQ==
dependencies:
lodash "^4.17.20"
renderkid "^2.0.4"
renderkid "^2.0.6"
pretty-format@^24.9.0:
version "24.9.0"
@ -16276,16 +16283,16 @@ remove-trailing-separator@^1.0.1:
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
renderkid@^2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.6.tgz#aaf875a67f2d1705821a22b64515db6d9e025fd2"
integrity sha512-GIis2GBr/ho0pFNf57D4XM4+PgnQuTii0WCPjEZmZfKivzUfGuRdjN2aQYtYMiNggHmNyBve+thFnNR1iBRcKg==
renderkid@^2.0.6:
version "2.0.7"
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609"
integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==
dependencies:
css-select "^4.1.3"
dom-converter "^0.2.0"
htmlparser2 "^6.1.0"
lodash "^4.17.21"
strip-ansi "^6.0.0"
strip-ansi "^3.0.1"
repeat-element@^1.1.2:
version "1.1.4"
@ -17982,7 +17989,7 @@ terminal-link@^2.0.0:
ansi-escapes "^4.2.1"
supports-hyperlinks "^2.0.0"
terser-webpack-plugin@^5.1.1, terser-webpack-plugin@^5.1.2:
terser-webpack-plugin@^5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.3.tgz#30033e955ca28b55664f1e4b30a1347e61aa23af"
integrity sha512-cxGbMqr6+A2hrIB5ehFIF+F/iST5ZOxvOmy9zih9ySbP1C2oEWQSOUS+2SNBTjzx5xLKO4xnod9eywdfq1Nb9A==
@ -19218,7 +19225,7 @@ webpack-log@^2.0.0:
ansi-colors "^3.0.0"
uuid "^3.3.2"
webpack-merge@^5.7.3:
webpack-merge@^5.7.3, webpack-merge@^5.8.0:
version "5.8.0"
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
@ -19242,10 +19249,10 @@ webpack-sources@^2.2.0, webpack-sources@^2.3.0:
source-list-map "^2.0.1"
source-map "^0.6.1"
webpack@^5.28.0, webpack@^5.37.0:
version "5.38.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.38.1.tgz#5224c7f24c18e729268d3e3bc97240d6e880258e"
integrity sha512-OqRmYD1OJbHZph6RUMD93GcCZy4Z4wC0ele4FXyYF0J6AxO1vOSuIlU1hkS/lDlR9CDYBz64MZRmdbdnFFoT2g==
webpack@^5.40.0:
version "5.40.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.40.0.tgz#3182cfd324759d715252cf541901a226e57b5061"
integrity sha512-c7f5e/WWrxXWUzQqTBg54vBs5RgcAgpvKE4F4VegVgfo4x660ZxYUF2/hpMkZUnLjgytVTitjeXaN4IPlXCGIw==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.47"
@ -19256,7 +19263,7 @@ webpack@^5.28.0, webpack@^5.37.0:
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.8.0"
es-module-lexer "^0.4.0"
es-module-lexer "^0.6.0"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
@ -19267,7 +19274,7 @@ webpack@^5.28.0, webpack@^5.37.0:
neo-async "^2.6.2"
schema-utils "^3.0.0"
tapable "^2.1.1"
terser-webpack-plugin "^5.1.1"
terser-webpack-plugin "^5.1.3"
watchpack "^2.2.0"
webpack-sources "^2.3.0"