Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: load or create svgo.config.js #10211

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ async function loadSidebarsFileUnsafe(
}

// We don't want sidebars to be cached because of hot reloading.
const module = await loadFreshModule(sidebarFilePath);
const module = loadFreshModule(sidebarFilePath);

// TODO unsafe, need to refactor and improve validation
return module as SidebarsConfig;
Expand Down
44 changes: 22 additions & 22 deletions packages/docusaurus-utils/src/__tests__/moduleUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ describe('loadFreshModule', () => {
);

// Should be able to read the initial module graph
await expect(entryFile.load()).resolves.toEqual({
expect(entryFile.load()).toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1',
Expand All @@ -141,11 +141,11 @@ describe('loadFreshModule', () => {
dep2Val: 'dep2 val',
},
});
await expect(dependency1.load()).resolves.toEqual({
expect(dependency1.load()).toEqual({
dep1Export: 'dep1 val1',
dep1Val: 'dep1 val2',
});
await expect(dependency2.load()).resolves.toEqual({
expect(dependency2.load()).toEqual({
dep2Val: 'dep2 val',
});

Expand All @@ -158,7 +158,7 @@ describe('loadFreshModule', () => {
export default {dep1Val: "dep1 val2 updated"}
`,
);
await expect(entryFile.load()).resolves.toEqual({
expect(entryFile.load()).toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1 updated',
Expand All @@ -168,11 +168,11 @@ describe('loadFreshModule', () => {
dep2Val: 'dep2 val',
},
});
await expect(dependency1.load()).resolves.toEqual({
expect(dependency1.load()).toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
expect(dependency2.load()).toEqual({
dep2Val: 'dep2 val',
});

Expand All @@ -183,7 +183,7 @@ describe('loadFreshModule', () => {
export default {dep2Val: "dep2 val updated"} satisfies {dep2Val: string}
`,
);
await expect(entryFile.load()).resolves.toEqual({
expect(entryFile.load()).toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1 updated',
Expand All @@ -193,11 +193,11 @@ describe('loadFreshModule', () => {
dep2Val: 'dep2 val updated',
},
});
await expect(dependency1.load()).resolves.toEqual({
expect(dependency1.load()).toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
expect(dependency2.load()).toEqual({
dep2Val: 'dep2 val updated',
});

Expand All @@ -216,7 +216,7 @@ describe('loadFreshModule', () => {
}
`,
);
await expect(entryFile.load()).resolves.toEqual({
expect(entryFile.load()).toEqual({
someEntryValue: 'entryVal updated',
newAttribute: 'is there',
dependency1: {
Expand All @@ -227,60 +227,60 @@ describe('loadFreshModule', () => {
dep2Val: 'dep2 val updated',
},
});
await expect(dependency1.load()).resolves.toEqual({
expect(dependency1.load()).toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
expect(dependency2.load()).toEqual({
dep2Val: 'dep2 val updated',
});
});
});

describe('invalid module path param', () => {
it('throws if module path does not exist', async () => {
await expect(() => loadFreshModule('/some/unknown/module/path.js'))
.rejects.toThrowErrorMatchingInlineSnapshot(`
expect(() => loadFreshModule('/some/unknown/module/path.js'))
.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "/some/unknown/module/path.js"
Cause: Cannot find module '/some/unknown/module/path.js' from 'packages/docusaurus-utils/src/moduleUtils.ts'"
`);
});

it('throws if module path is undefined', async () => {
await expect(() =>
expect(() =>
// @ts-expect-error: undefined is invalid
loadFreshModule(undefined),
).rejects.toThrowErrorMatchingInlineSnapshot(`
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "undefined"
Cause: Invalid module path of type undefined"
`);
});

it('throws if module path is null', async () => {
await expect(() =>
expect(() =>
// @ts-expect-error: null is invalid
loadFreshModule(null),
).rejects.toThrowErrorMatchingInlineSnapshot(`
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "null"
Cause: Invalid module path of type null"
`);
});

it('throws if module path is number', async () => {
await expect(() =>
expect(() =>
// @ts-expect-error: number is invalid
loadFreshModule(42),
).rejects.toThrowErrorMatchingInlineSnapshot(`
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "42"
Cause: Invalid module path of type 42"
`);
});

it('throws if module path is object', async () => {
await expect(() =>
expect(() =>
// @ts-expect-error: object is invalid
loadFreshModule({}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "[object Object]"
Cause: Invalid module path of type [object Object]"
`);
Expand Down
24 changes: 24 additions & 0 deletions packages/docusaurus-utils/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,27 @@ export const DEFAULT_PLUGIN_ID = 'default';
*/
export const WEBPACK_URL_LOADER_LIMIT =
process.env.WEBPACK_URL_LOADER_LIMIT ?? 10000;

/**
* SVGO config file to tweak settings.
*
* @see https://github.com/svg/svgo?tab=readme-ov-file#configuration
*/
export const SVGO_CONFIG_FILE_NAME = 'svgo.config.js';

/**
* Default SVGO config to use if not configured otherwise.
*/
export const SVGO_DEFAULT_CONFIG = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeTitle: false,
removeViewBox: false,
},
},
},
],
};
2 changes: 2 additions & 0 deletions packages/docusaurus-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export {
DEFAULT_PORT,
DEFAULT_PLUGIN_ID,
WEBPACK_URL_LOADER_LIMIT,
SVGO_CONFIG_FILE_NAME,
SVGO_DEFAULT_CONFIG,
} from './constants';
export {generate, readOutputHTMLFile} from './emitUtils';
export {
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus-utils/src/moduleUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import logger from '@docusaurus/logger';
/*
jiti is able to load ESM, CJS, JSON, TS modules
*/
export async function loadFreshModule(modulePath: string): Promise<unknown> {
export function loadFreshModule(modulePath: string): unknown {
try {
if (typeof modulePath !== 'string') {
throw new Error(
Expand Down
31 changes: 18 additions & 13 deletions packages/docusaurus-utils/src/webpackUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
*/

import path from 'path';
import logger from '@docusaurus/logger';
import {escapePath} from './pathUtils';
import {
GENERATED_FILES_DIR_NAME,
WEBPACK_URL_LOADER_LIMIT,
OUTPUT_STATIC_ASSETS_DIR_NAME,
SVGO_CONFIG_FILE_NAME,
SVGO_DEFAULT_CONFIG,
} from './constants';
import {loadFreshModule} from './moduleUtils';
import type {RuleSetRule, LoaderContext} from 'webpack';

export type WebpackCompilerName = 'server' | 'client';
Expand Down Expand Up @@ -60,6 +65,18 @@ export function getFileLoaderUtils(): FileLoaderUtils {
// the html
const urlLoaderLimit = WEBPACK_URL_LOADER_LIMIT;

const svgoConfigPath = path.resolve(
GENERATED_FILES_DIR_NAME,
SVGO_CONFIG_FILE_NAME,
);
let svgoConfig;
try {
svgoConfig = loadFreshModule(svgoConfigPath);
} catch {
logger.interpolate`Docusaurus could not load SVGO config at path path=${svgoConfigPath}\nUsing default configuration`;
svgoConfig = SVGO_DEFAULT_CONFIG;
}

const fileLoaderFileName = (folder: AssetFolder) =>
path.posix.join(
OUTPUT_STATIC_ASSETS_DIR_NAME,
Expand Down Expand Up @@ -135,19 +152,7 @@ export function getFileLoaderUtils(): FileLoaderUtils {
options: {
prettier: false,
svgo: true,
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeTitle: false,
removeViewBox: false,
},
},
},
],
},
svgoConfig,
titleProp: true,
ref: ![path],
},
Expand Down
22 changes: 22 additions & 0 deletions packages/docusaurus/src/server/codegen/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/

import fs from 'fs-extra';
import {
generate,
escapePath,
DEFAULT_CONFIG_FILE_NAME,
SVGO_CONFIG_FILE_NAME,
SVGO_DEFAULT_CONFIG,
} from '@docusaurus/utils';
import {generateRouteFiles} from './codegenRoutes';
import type {
Expand Down Expand Up @@ -146,6 +149,24 @@ function genSiteStorage({
);
}

async function getSvgoConfig({generatedFilesDir}: {generatedFilesDir: string}) {
let svgoConfig;

if (await fs.pathExists(SVGO_CONFIG_FILE_NAME)) {
svgoConfig = await fs.readFile(SVGO_CONFIG_FILE_NAME, 'utf-8');
} else {
svgoConfig = `export default ${JSON.stringify(
{
SVGO_DEFAULT_CONFIG,
},
null,
2,
)}`;
}

return generate(generatedFilesDir, SVGO_CONFIG_FILE_NAME, svgoConfig);
}

type CodegenParams = {
generatedFilesDir: string;
siteConfig: DocusaurusConfig;
Expand All @@ -170,5 +191,6 @@ export async function generateSiteFiles(params: CodegenParams): Promise<void> {
genSiteStorage(params),
genI18n(params),
genCodeTranslations(params),
getSvgoConfig(params),
]);
}
2 changes: 1 addition & 1 deletion packages/docusaurus/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function loadSiteConfig({
throw new Error(`Config file at "${siteConfigPath}" not found.`);
}

const importedConfig = await loadFreshModule(siteConfigPath);
const importedConfig = loadFreshModule(siteConfigPath);

const loadedConfig: unknown =
typeof importedConfig === 'function'
Expand Down
8 changes: 2 additions & 6 deletions packages/docusaurus/src/server/plugins/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ async function normalizePluginConfig(
if (typeof pluginConfig === 'string') {
const pluginModuleImport = pluginConfig;
const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = (await loadFreshModule(
pluginPath,
)) as ImportedPluginModule;
const pluginModule = loadFreshModule(pluginPath) as ImportedPluginModule;
return {
plugin: pluginModule.default ?? pluginModule,
options: {},
Expand All @@ -90,9 +88,7 @@ async function normalizePluginConfig(
if (typeof pluginConfig[0] === 'string') {
const pluginModuleImport = pluginConfig[0];
const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = (await loadFreshModule(
pluginPath,
)) as ImportedPluginModule;
const pluginModule = loadFreshModule(pluginPath) as ImportedPluginModule;
return {
plugin: pluginModule.default ?? pluginModule,
options: pluginConfig[1],
Expand Down
4 changes: 1 addition & 3 deletions packages/docusaurus/src/server/plugins/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ export async function loadPresets(
);

const presetPath = presetRequire.resolve(presetName);
const presetModule = (await loadFreshModule(
presetPath,
)) as ImportedPresetModule;
const presetModule = loadFreshModule(presetPath) as ImportedPresetModule;

const presetFunction = presetModule.default ?? presetModule;

Expand Down
15 changes: 15 additions & 0 deletions website/_dogfooding/_docs tests/tests/svgs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Logo from './vectors/logo.svg';
import Hacker from './vectors/hacker.svg';
import Integrations from './vectors/integrations.svg';
import OpenSource from './vectors/open-source.svg';
import Mascot from './vectors/mascot.svg';

# Many inline SVGs

Have a bunch of SVGs, they're written to intentionally override each others styled when inlined on the same page.

<Logo height="20rem" />
<Hacker height="20rem" />
<Integrations height="20rem" />
<OpenSource height="20rem" />
<Mascot height="20rem" />
Loading
Loading