Compare commits
7 Commits
main
...
slorber/fi
| Author | SHA1 | Date |
|---|---|---|
|
|
b7c6d5eb53 | |
|
|
c37f5b5d54 | |
|
|
257e0b0aae | |
|
|
1434051211 | |
|
|
1d3d9a1654 | |
|
|
bb2aa5a714 | |
|
|
6b78ffd913 |
|
|
@ -6,11 +6,15 @@
|
|||
*/
|
||||
|
||||
declare module '@generated/client-modules' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const clientModules: readonly any[];
|
||||
const clientModules: readonly unknown[];
|
||||
export default clientModules;
|
||||
}
|
||||
|
||||
declare module '@generated/css-client-modules' {
|
||||
const cssClientModules: readonly unknown[];
|
||||
export default cssClientModules;
|
||||
}
|
||||
|
||||
declare module '@generated/docusaurus.config' {
|
||||
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,6 @@ function testValidateThemeConfig(partialThemeConfig) {
|
|||
});
|
||||
}
|
||||
|
||||
function testOk(partialThemeConfig) {
|
||||
expect(
|
||||
testValidateThemeConfig({...DEFAULT_CONFIG, ...partialThemeConfig}),
|
||||
).toEqual({
|
||||
...DEFAULT_CONFIG,
|
||||
...partialThemeConfig,
|
||||
});
|
||||
}
|
||||
|
||||
describe('themeConfig', () => {
|
||||
test('should accept valid theme config', () => {
|
||||
const userConfig = {
|
||||
|
|
@ -477,31 +468,13 @@ describe('themeConfig', () => {
|
|||
});
|
||||
|
||||
describe('customCss config', () => {
|
||||
test('should accept customCss undefined', () => {
|
||||
testOk({
|
||||
customCss: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept customCss string', () => {
|
||||
testOk({
|
||||
customCss: './path/to/cssFile.css',
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept customCss string array', () => {
|
||||
testOk({
|
||||
customCss: ['./path/to/cssFile.css', './path/to/cssFile2.css'],
|
||||
});
|
||||
});
|
||||
|
||||
test('should reject customCss number', () => {
|
||||
test('should reject customCss string', () => {
|
||||
expect(() =>
|
||||
testValidateThemeConfig({
|
||||
customCss: 42,
|
||||
customCss: './path/to/cssFile.css',
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"customCss\\" must be one of [array, string]"`,
|
||||
`"themeConfig.customCss is invalid. Custom css used to be provided as themeOptions.customCss, but it is deprecated now."`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -234,3 +234,4 @@ export function getSwizzleComponentList(): string[] {
|
|||
}
|
||||
|
||||
export {validateThemeConfig} from './validateThemeConfig';
|
||||
export {validateOptions} from './options';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* 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 {Joi} from '@docusaurus/utils-validation';
|
||||
import type {
|
||||
ValidationResult,
|
||||
OptionValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
import type {Options} from '@docusaurus/theme-classic';
|
||||
|
||||
const DEFAULT_OPTIONS: Options = {
|
||||
customCss: null,
|
||||
};
|
||||
|
||||
export const Schema = Joi.object({
|
||||
customCss: Joi.alternatives()
|
||||
.try(Joi.array().items(Joi.string().required()), Joi.string().required())
|
||||
.optional()
|
||||
.default(DEFAULT_OPTIONS.customCss)
|
||||
.warning('deprecate.error', {
|
||||
msg: `theme.customCss option is deprecated.
|
||||
Please use siteConfig.styling.css instead.
|
||||
|
||||
Note that this also changes the CSS insertion order!
|
||||
This enables your site CSS to override default theme CSS more easily, without using !important
|
||||
|
||||
Before: custom site CSS was inserted before Infima CSS and default theme CSS.
|
||||
After: custom site CSS will be inserted after Infima CSS and default theme CSS.
|
||||
|
||||
See also https://github.com/facebook/docusaurus/pull/6227
|
||||
`,
|
||||
})
|
||||
.messages({
|
||||
'deprecate.error': '{#msg}',
|
||||
}),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options>): ValidationResult<Options> {
|
||||
return validate(Schema, options);
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
declare module '@docusaurus/theme-classic' {
|
||||
export type Options = {
|
||||
customCss?: string | string[];
|
||||
customCss?: string | string[] | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -244,10 +244,6 @@ const FooterLinkItemSchema = Joi.object({
|
|||
// (users may need additional attributes like target, aria-role, data-customAttribute...)
|
||||
.unknown();
|
||||
|
||||
const CustomCssSchema = Joi.alternatives()
|
||||
.try(Joi.array().items(Joi.string().required()), Joi.string().required())
|
||||
.optional();
|
||||
|
||||
const ThemeConfigSchema = Joi.object({
|
||||
// TODO temporary (@alpha-58)
|
||||
disableDarkMode: Joi.any().forbidden().messages({
|
||||
|
|
@ -259,7 +255,13 @@ const ThemeConfigSchema = Joi.object({
|
|||
'any.unknown':
|
||||
'defaultDarkMode theme config is deprecated. Please use the new colorMode attribute. You likely want: config.themeConfig.colorMode.defaultMode = "dark"',
|
||||
}),
|
||||
customCss: CustomCssSchema,
|
||||
|
||||
// TODO temporary
|
||||
customCss: Joi.any().forbidden().messages({
|
||||
'any.unknown':
|
||||
'themeConfig.customCss is invalid. Custom css used to be provided as themeOptions.customCss, but it is deprecated now.',
|
||||
}),
|
||||
|
||||
colorMode: ColorModeSchema,
|
||||
image: Joi.string(),
|
||||
docs: DocsSchema,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ export type ThemeConfig = {
|
|||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type StylingConfig = {
|
||||
css: string[];
|
||||
};
|
||||
|
||||
// Docusaurus config, after validation/normalization
|
||||
export interface DocusaurusConfig {
|
||||
baseUrl: string;
|
||||
|
|
@ -33,6 +37,7 @@ export interface DocusaurusConfig {
|
|||
// trailingSlash undefined = legacy retrocompatible behavior => /file => /file/index.html
|
||||
trailingSlash: boolean | undefined;
|
||||
i18n: I18nConfig;
|
||||
styling: StylingConfig;
|
||||
onBrokenLinks: ReportingSeverity;
|
||||
onBrokenMarkdownLinks: ReportingSeverity;
|
||||
onDuplicateRoutes: ReportingSeverity;
|
||||
|
|
@ -82,6 +87,9 @@ export type Config = Overwrite<
|
|||
url: Required<DocusaurusConfig['url']>;
|
||||
baseUrl: Required<DocusaurusConfig['baseUrl']>;
|
||||
i18n?: DeepPartial<DocusaurusConfig['i18n']>;
|
||||
styling?: Omit<StylingConfig, 'css'> & {
|
||||
css?: string | string[];
|
||||
};
|
||||
}
|
||||
>;
|
||||
|
||||
|
|
@ -419,6 +427,7 @@ interface HtmlTagObject {
|
|||
innerHTML?: string;
|
||||
}
|
||||
|
||||
// TODO weird useless type, refactor
|
||||
export type ValidationResult<T> = T;
|
||||
|
||||
export type ValidationSchema<T> = Joi.ObjectSchema<T>;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// import order impacts CSS insertion ordering
|
||||
// See https://github.com/facebook/docusaurus/pull/6227
|
||||
import '@generated/client-modules';
|
||||
import '@generated/registry';
|
||||
import '@generated/css-client-modules';
|
||||
|
||||
import React from 'react';
|
||||
import {hydrate, render} from 'react-dom';
|
||||
import {BrowserRouter} from 'react-router-dom';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import logger from '@docusaurus/logger';
|
||||
import {DocusaurusConfig, I18nConfig} from '@docusaurus/types';
|
||||
import {DocusaurusConfig, I18nConfig, StylingConfig} from '@docusaurus/types';
|
||||
import {DEFAULT_CONFIG_FILE_NAME, STATIC_DIR_NAME} from '@docusaurus/utils';
|
||||
import {
|
||||
Joi,
|
||||
|
|
@ -24,9 +24,14 @@ export const DEFAULT_I18N_CONFIG: I18nConfig = {
|
|||
localeConfigs: {},
|
||||
};
|
||||
|
||||
export const DEFAULT_STYLING_CONFIG: StylingConfig = {
|
||||
css: [],
|
||||
};
|
||||
|
||||
export const DEFAULT_CONFIG: Pick<
|
||||
DocusaurusConfig,
|
||||
| 'i18n'
|
||||
| 'styling'
|
||||
| 'onBrokenLinks'
|
||||
| 'onBrokenMarkdownLinks'
|
||||
| 'onDuplicateRoutes'
|
||||
|
|
@ -41,6 +46,7 @@ export const DEFAULT_CONFIG: Pick<
|
|||
| 'staticDirectories'
|
||||
> = {
|
||||
i18n: DEFAULT_I18N_CONFIG,
|
||||
styling: DEFAULT_STYLING_CONFIG,
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
onDuplicateRoutes: 'warn',
|
||||
|
|
@ -101,7 +107,7 @@ const LocaleConfigSchema = Joi.object({
|
|||
direction: Joi.string().equal('ltr', 'rtl').default('ltr'),
|
||||
});
|
||||
|
||||
const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
||||
const I18nConfigSchema = Joi.object<I18nConfig>({
|
||||
defaultLocale: Joi.string().required(),
|
||||
locales: Joi.array().items().min(1).items(Joi.string().required()).required(),
|
||||
localeConfigs: Joi.object()
|
||||
|
|
@ -123,6 +129,15 @@ const SiteUrlSchema = URISchema.required().custom((value, helpers) => {
|
|||
return value;
|
||||
}, 'siteUrlCustomValidation');
|
||||
|
||||
const StylingSchema = Joi.object({
|
||||
css: Joi.alternatives()
|
||||
.try(
|
||||
Joi.array().items(Joi.string().required()).required(),
|
||||
Joi.string().custom((val) => [val]), // normalize: string -> string[]
|
||||
)
|
||||
.default(DEFAULT_STYLING_CONFIG.css),
|
||||
}).default(DEFAULT_STYLING_CONFIG);
|
||||
|
||||
// TODO move to @docusaurus/utils-validation
|
||||
export const ConfigSchema = Joi.object({
|
||||
baseUrl: Joi.string()
|
||||
|
|
@ -134,7 +149,8 @@ export const ConfigSchema = Joi.object({
|
|||
title: Joi.string().required(),
|
||||
url: SiteUrlSchema,
|
||||
trailingSlash: Joi.boolean(), // No default value! undefined = retrocompatible legacy behavior!
|
||||
i18n: I18N_CONFIG_SCHEMA,
|
||||
i18n: I18nConfigSchema,
|
||||
styling: StylingSchema,
|
||||
onBrokenLinks: Joi.string()
|
||||
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
||||
.default(DEFAULT_CONFIG.onBrokenLinks),
|
||||
|
|
|
|||
|
|
@ -316,6 +316,15 @@ export async function load(
|
|||
plugins.push(createBootstrapPlugin({siteConfig}));
|
||||
plugins.push(createMDXFallbackPlugin({siteDir, siteConfig}));
|
||||
|
||||
// Load CSS client modules
|
||||
const genCSSClientModules = generate(
|
||||
generatedFilesDir,
|
||||
'css-client-modules.js',
|
||||
`export default [\n${siteConfig.styling.css
|
||||
.map((module) => `import('${escapePath(module)}'),`)
|
||||
.join('\n')}\n];\n`,
|
||||
);
|
||||
|
||||
// Load client modules.
|
||||
const clientModules = loadClientModules(plugins);
|
||||
const genClientModules = generate(
|
||||
|
|
@ -402,6 +411,7 @@ ${Object.keys(registry)
|
|||
);
|
||||
|
||||
await Promise.all([
|
||||
genCSSClientModules,
|
||||
genClientModules,
|
||||
genSiteConfig,
|
||||
genRegistry,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ const config = {
|
|||
// - force trailing slashes for deploy previews
|
||||
// - avoid trailing slashes in prod
|
||||
trailingSlash: isDeployPreview,
|
||||
styling: {
|
||||
css: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
stylesheets: [
|
||||
{
|
||||
href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
|
||||
|
|
@ -304,7 +307,7 @@ const config = {
|
|||
remarkPlugins: [npm2yarn],
|
||||
},
|
||||
theme: {
|
||||
customCss: [require.resolve('./src/css/custom.css')],
|
||||
customCss: [require.resolve('./src/css/customLegacy.css')],
|
||||
},
|
||||
gtag: !isDeployPreview
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
/* Used to test CSS insertion order */
|
||||
.test-marker-theme-custom-css-legacy {
|
||||
content: "theme-custom-css-legacy";
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ import React from 'react';
|
|||
import type {Props} from '@theme/CodeBlock';
|
||||
import CodeBlock from '@theme-original/CodeBlock';
|
||||
|
||||
import './styles.module.css';
|
||||
|
||||
// This component does nothing on purpose
|
||||
// Dogfood: wrapping a theme component already enhanced by another theme
|
||||
// See https://github.com/facebook/docusaurus/pull/5983
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Used to test CSS insertion order */
|
||||
.test-marker-theme-code-block {
|
||||
content: "theme-code-block";
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* 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 React from 'react';
|
||||
import type {Props} from '@theme/DocItem';
|
||||
import DocItem from '@theme-original/DocItem';
|
||||
|
||||
// This component is only used to test for CSS insertion order
|
||||
import './styles.module.css';
|
||||
|
||||
export default function DocItemWrapper(props: Props): JSX.Element {
|
||||
return <DocItem {...props} />;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Used to test CSS insertion order */
|
||||
.test-marker-theme-doc-item {
|
||||
content: "theme-doc-item";
|
||||
}
|
||||
|
||||
|
|
@ -41,14 +41,17 @@ const EXPECTED_CSS_MARKERS = [
|
|||
'.pills__item',
|
||||
'.tabs__item',
|
||||
|
||||
// Test markers
|
||||
'.test-marker-site-custom-css-unique-rule',
|
||||
// Markers, using Webpack require()
|
||||
'.test-marker-theme-custom-css-legacy', // TODO should be removed later
|
||||
'.test-marker-site-client-module',
|
||||
'.test-marker-theme-layout',
|
||||
'.test-marker-site-index-page',
|
||||
|
||||
// lazy loaded lib
|
||||
// Markers, using Webpack dynamic import() (routes use dynamic imports)
|
||||
'.test-marker-site-index-page',
|
||||
'.test-marker-site-custom-css-unique-rule',
|
||||
'.DocSearch-Modal',
|
||||
'.test-marker-theme-code-block',
|
||||
'.test-marker-theme-doc-item',
|
||||
];
|
||||
|
||||
const cssDirName = path.join(__dirname, 'build', 'assets', 'css');
|
||||
|
|
|
|||
Loading…
Reference in New Issue