mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 23:57:22 +02:00
fix(content-docs): restore functionality when a category only has index page (#7385)
* fix(content-docs): restore functionality when a category only has index page * use this internally
This commit is contained in:
parent
c3880cc342
commit
6e10a48059
13 changed files with 258 additions and 46 deletions
|
@ -6,4 +6,5 @@ This part is very complicated and hard to navigate. Sidebars are loaded through
|
||||||
2. **Normalization**. The shorthands are expanded. This step is very lenient about the sidebars' shapes. Returns `NormalizedSidebars`.
|
2. **Normalization**. The shorthands are expanded. This step is very lenient about the sidebars' shapes. Returns `NormalizedSidebars`.
|
||||||
3. **Validation**. The normalized sidebars are validated. This step happens after normalization, because the normalized sidebars are easier to validate, and allows us to repeatedly validate & generate in the future.
|
3. **Validation**. The normalized sidebars are validated. This step happens after normalization, because the normalized sidebars are easier to validate, and allows us to repeatedly validate & generate in the future.
|
||||||
4. **Generation**. This step is done through the "processor" (naming is hard). The `autogenerated` items are unwrapped. In the future, steps 3 and 4 may be repeatedly done until all autogenerated items are unwrapped. Returns `ProcessedSidebars`.
|
4. **Generation**. This step is done through the "processor" (naming is hard). The `autogenerated` items are unwrapped. In the future, steps 3 and 4 may be repeatedly done until all autogenerated items are unwrapped. Returns `ProcessedSidebars`.
|
||||||
|
- **Important**: this step should only care about unwrapping autogenerated items, not filtering them, writing additional metadata, applying defaults, etc.—everything will be handled in the post-processor. Important because the generator is exposed to the end-user and we want it to be easy to be reasoned about.
|
||||||
5. **Post-processing**. Defaults are applied (collapsed states), category links are resolved, empty categories are flattened. Returns `Sidebars`.
|
5. **Post-processing**. Defaults are applied (collapsed states), category links are resolved, empty categories are flattened. Returns `Sidebars`.
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"docs": [
|
||||||
|
{
|
||||||
|
"label": "Tutorials",
|
||||||
|
"type": "category",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "autogenerated",
|
||||||
|
"dirName": "tutorials"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "index-only",
|
||||||
|
"type": "category",
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "tutorials/tutorial-basics"
|
||||||
|
},
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"sidebar": [
|
||||||
|
"draft1",
|
||||||
|
{
|
||||||
|
"type": "category",
|
||||||
|
"label": "all drafts",
|
||||||
|
"items": [
|
||||||
|
"draft2",
|
||||||
|
"draft3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "category",
|
||||||
|
"label": "all drafts",
|
||||||
|
"link": {
|
||||||
|
"type": "generated-index"
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
"draft2",
|
||||||
|
"draft3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "category",
|
||||||
|
"label": "all drafts",
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "draft1"
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
"draft2",
|
||||||
|
"draft3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "category",
|
||||||
|
"label": "index not draft",
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "not-draft"
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
"draft2",
|
||||||
|
"draft3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "category",
|
||||||
|
"label": "subitem not draft",
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "draft1"
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
"not-draft",
|
||||||
|
"draft3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,5 +1,56 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`loadSidebars loads sidebars with index-only categories 1`] = `
|
||||||
|
{
|
||||||
|
"docs": [
|
||||||
|
{
|
||||||
|
"collapsed": true,
|
||||||
|
"collapsible": true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "tutorials/tutorial-basics",
|
||||||
|
"label": "tutorial-basics",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Tutorials",
|
||||||
|
"link": undefined,
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tutorials/tutorial-basics",
|
||||||
|
"label": "index-only",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`loadSidebars loads sidebars with interspersed draft items 1`] = `
|
||||||
|
{
|
||||||
|
"sidebar": [
|
||||||
|
{
|
||||||
|
"id": "not-draft",
|
||||||
|
"label": "index not draft",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": true,
|
||||||
|
"collapsible": true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "not-draft",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "subitem not draft",
|
||||||
|
"link": undefined,
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`loadSidebars sidebars link 1`] = `
|
exports[`loadSidebars sidebars link 1`] = `
|
||||||
{
|
{
|
||||||
"docs": [
|
"docs": [
|
||||||
|
|
|
@ -60,14 +60,21 @@ exports[`postProcess corrects collapsed state inconsistencies 3`] = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`postProcess transforms category without subitems 1`] = `
|
exports[`postProcess filters draft items 1`] = `
|
||||||
{
|
{
|
||||||
"sidebar": [
|
"sidebar": [
|
||||||
{
|
{
|
||||||
"href": "version/generated/permalink",
|
"id": "another",
|
||||||
"label": "Category",
|
"label": "Category",
|
||||||
"type": "link",
|
"type": "doc",
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`postProcess transforms category without subitems 1`] = `
|
||||||
|
{
|
||||||
|
"sidebar": [
|
||||||
{
|
{
|
||||||
"id": "doc ID",
|
"id": "doc ID",
|
||||||
"label": "Category 2",
|
"label": "Category 2",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import {jest} from '@jest/globals';
|
import {jest} from '@jest/globals';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import {createSlugger} from '@docusaurus/utils';
|
||||||
import {loadSidebars, DisabledSidebars} from '../index';
|
import {loadSidebars, DisabledSidebars} from '../index';
|
||||||
import type {SidebarProcessorParams} from '../types';
|
import type {SidebarProcessorParams} from '../types';
|
||||||
import {DefaultSidebarItemsGenerator} from '../generator';
|
import {DefaultSidebarItemsGenerator} from '../generator';
|
||||||
|
@ -27,6 +28,7 @@ describe('loadSidebars', () => {
|
||||||
],
|
],
|
||||||
drafts: [],
|
drafts: [],
|
||||||
version: {
|
version: {
|
||||||
|
path: 'version',
|
||||||
contentPath: path.join(fixtureDir, 'docs'),
|
contentPath: path.join(fixtureDir, 'docs'),
|
||||||
contentPathLocalized: path.join(fixtureDir, 'docs'),
|
contentPathLocalized: path.join(fixtureDir, 'docs'),
|
||||||
},
|
},
|
||||||
|
@ -124,6 +126,32 @@ describe('loadSidebars', () => {
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('loads sidebars with index-only categories', async () => {
|
||||||
|
const sidebarPath = path.join(fixtureDir, 'sidebars-category-index.json');
|
||||||
|
const result = await loadSidebars(sidebarPath, {
|
||||||
|
...params,
|
||||||
|
docs: [
|
||||||
|
{
|
||||||
|
id: 'tutorials/tutorial-basics',
|
||||||
|
source: '@site/docs/tutorials/tutorial-basics/index.md',
|
||||||
|
sourceDirName: 'tutorials/tutorial-basics',
|
||||||
|
frontMatter: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(result).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads sidebars with interspersed draft items', async () => {
|
||||||
|
const sidebarPath = path.join(fixtureDir, 'sidebars-drafts.json');
|
||||||
|
const result = await loadSidebars(sidebarPath, {
|
||||||
|
...params,
|
||||||
|
drafts: [{id: 'draft1'}, {id: 'draft2'}, {id: 'draft3'}],
|
||||||
|
categoryLabelSlugger: createSlugger(),
|
||||||
|
});
|
||||||
|
expect(result).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
it('duplicate category metadata files', async () => {
|
it('duplicate category metadata files', async () => {
|
||||||
const sidebarPath = path.join(
|
const sidebarPath = path.join(
|
||||||
fixtureDir,
|
fixtureDir,
|
||||||
|
|
|
@ -35,6 +35,7 @@ describe('postProcess', () => {
|
||||||
{
|
{
|
||||||
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
||||||
version: {path: 'version'},
|
version: {path: 'version'},
|
||||||
|
drafts: [],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ describe('postProcess', () => {
|
||||||
{
|
{
|
||||||
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
||||||
version: {path: 'version'},
|
version: {path: 'version'},
|
||||||
|
drafts: [],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
}).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
@ -79,6 +81,7 @@ describe('postProcess', () => {
|
||||||
{
|
{
|
||||||
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
||||||
version: {path: 'version'},
|
version: {path: 'version'},
|
||||||
|
drafts: [],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
@ -99,6 +102,7 @@ describe('postProcess', () => {
|
||||||
{
|
{
|
||||||
sidebarOptions: {sidebarCollapsed: false, sidebarCollapsible: false},
|
sidebarOptions: {sidebarCollapsed: false, sidebarCollapsible: false},
|
||||||
version: {path: 'version'},
|
version: {path: 'version'},
|
||||||
|
drafts: [],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
@ -118,6 +122,37 @@ describe('postProcess', () => {
|
||||||
{
|
{
|
||||||
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: false},
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: false},
|
||||||
version: {path: 'version'},
|
version: {path: 'version'},
|
||||||
|
drafts: [],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters draft items', () => {
|
||||||
|
expect(
|
||||||
|
postProcessSidebars(
|
||||||
|
{
|
||||||
|
sidebar: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Category',
|
||||||
|
items: [{type: 'doc', id: 'foo'}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Category',
|
||||||
|
link: {
|
||||||
|
type: 'doc',
|
||||||
|
id: 'another',
|
||||||
|
},
|
||||||
|
items: [{type: 'doc', id: 'foo'}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
||||||
|
version: {path: 'version'},
|
||||||
|
drafts: [{id: 'foo', unversionedId: 'foo'}],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
|
|
@ -15,12 +15,20 @@ import type {
|
||||||
ProcessedSidebars,
|
ProcessedSidebars,
|
||||||
SidebarItemCategoryLink,
|
SidebarItemCategoryLink,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import {getDocIds} from '../docs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
type SidebarPostProcessorParams = SidebarProcessorParams & {
|
||||||
|
draftIds: Set<string>;
|
||||||
|
};
|
||||||
|
|
||||||
function normalizeCategoryLink(
|
function normalizeCategoryLink(
|
||||||
category: ProcessedSidebarItemCategory,
|
category: ProcessedSidebarItemCategory,
|
||||||
params: SidebarProcessorParams,
|
params: SidebarPostProcessorParams,
|
||||||
): SidebarItemCategoryLink | undefined {
|
): SidebarItemCategoryLink | undefined {
|
||||||
|
if (category.link?.type === 'doc' && params.draftIds.has(category.link.id)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
if (category.link?.type === 'generated-index') {
|
if (category.link?.type === 'generated-index') {
|
||||||
// Default slug logic can be improved
|
// Default slug logic can be improved
|
||||||
const getDefaultSlug = () =>
|
const getDefaultSlug = () =>
|
||||||
|
@ -38,36 +46,41 @@ function normalizeCategoryLink(
|
||||||
|
|
||||||
function postProcessSidebarItem(
|
function postProcessSidebarItem(
|
||||||
item: ProcessedSidebarItem,
|
item: ProcessedSidebarItem,
|
||||||
params: SidebarProcessorParams,
|
params: SidebarPostProcessorParams,
|
||||||
): SidebarItem {
|
): SidebarItem | null {
|
||||||
if (item.type === 'category') {
|
if (item.type === 'category') {
|
||||||
|
// Fail-fast if there's actually no subitems, no because all subitems are
|
||||||
|
// drafts. This is likely a configuration mistake.
|
||||||
|
if (item.items.length === 0 && !item.link) {
|
||||||
|
throw new Error(
|
||||||
|
`Sidebar category ${item.label} has neither any subitem nor a link. This makes this item not able to link to anything.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const category = {
|
const category = {
|
||||||
...item,
|
...item,
|
||||||
collapsed: item.collapsed ?? params.sidebarOptions.sidebarCollapsed,
|
collapsed: item.collapsed ?? params.sidebarOptions.sidebarCollapsed,
|
||||||
collapsible: item.collapsible ?? params.sidebarOptions.sidebarCollapsible,
|
collapsible: item.collapsible ?? params.sidebarOptions.sidebarCollapsible,
|
||||||
link: normalizeCategoryLink(item, params),
|
link: normalizeCategoryLink(item, params),
|
||||||
items: item.items.map((subItem) =>
|
items: item.items
|
||||||
postProcessSidebarItem(subItem, params),
|
.map((subItem) => postProcessSidebarItem(subItem, params))
|
||||||
),
|
.filter((v): v is SidebarItem => Boolean(v)),
|
||||||
};
|
};
|
||||||
// If the current category doesn't have subitems, we render a normal link
|
// If the current category doesn't have subitems, we render a normal link
|
||||||
// instead.
|
// instead.
|
||||||
if (category.items.length === 0) {
|
if (category.items.length === 0) {
|
||||||
if (!category.link) {
|
// Doesn't make sense to render an empty generated index page, so we
|
||||||
throw new Error(
|
// filter the entire category out as well.
|
||||||
`Sidebar category ${item.label} has neither any subitem nor a link. This makes this item not able to link to anything.`,
|
if (
|
||||||
);
|
!category.link ||
|
||||||
|
category.link.type === 'generated-index' ||
|
||||||
|
params.draftIds.has(category.link.id)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return category.link.type === 'doc'
|
return {
|
||||||
? {
|
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
label: category.label,
|
label: category.label,
|
||||||
id: category.link.id,
|
id: category.link.id,
|
||||||
}
|
|
||||||
: {
|
|
||||||
type: 'link',
|
|
||||||
label: category.label,
|
|
||||||
href: category.link.permalink,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// A non-collapsible category can't be collapsed!
|
// A non-collapsible category can't be collapsed!
|
||||||
|
@ -76,6 +89,12 @@ function postProcessSidebarItem(
|
||||||
}
|
}
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
(item.type === 'doc' || item.type === 'ref') &&
|
||||||
|
params.draftIds.has(item.id)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +102,11 @@ export function postProcessSidebars(
|
||||||
sidebars: ProcessedSidebars,
|
sidebars: ProcessedSidebars,
|
||||||
params: SidebarProcessorParams,
|
params: SidebarProcessorParams,
|
||||||
): Sidebars {
|
): Sidebars {
|
||||||
|
const draftIds = new Set(params.drafts.flatMap(getDocIds));
|
||||||
|
|
||||||
return _.mapValues(sidebars, (sidebar) =>
|
return _.mapValues(sidebars, (sidebar) =>
|
||||||
sidebar.map((item) => postProcessSidebarItem(item, params)),
|
sidebar
|
||||||
|
.map((item) => postProcessSidebarItem(item, {...params, draftIds}))
|
||||||
|
.filter((v): v is SidebarItem => Boolean(v)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {DefaultSidebarItemsGenerator} from './generator';
|
||||||
import {validateSidebars} from './validation';
|
import {validateSidebars} from './validation';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import combinePromises from 'combine-promises';
|
import combinePromises from 'combine-promises';
|
||||||
import {getDocIds, isCategoryIndex} from '../docs';
|
import {isCategoryIndex} from '../docs';
|
||||||
|
|
||||||
function toSidebarItemsGeneratorDoc(
|
function toSidebarItemsGeneratorDoc(
|
||||||
doc: DocMetadataBase,
|
doc: DocMetadataBase,
|
||||||
|
@ -55,8 +55,7 @@ async function processSidebar(
|
||||||
categoriesMetadata: {[filePath: string]: CategoryMetadataFile},
|
categoriesMetadata: {[filePath: string]: CategoryMetadataFile},
|
||||||
params: SidebarProcessorParams,
|
params: SidebarProcessorParams,
|
||||||
): Promise<ProcessedSidebar> {
|
): Promise<ProcessedSidebar> {
|
||||||
const {sidebarItemsGenerator, numberPrefixParser, docs, drafts, version} =
|
const {sidebarItemsGenerator, numberPrefixParser, docs, version} = params;
|
||||||
params;
|
|
||||||
|
|
||||||
// Just a minor lazy transformation optimization
|
// Just a minor lazy transformation optimization
|
||||||
const getSidebarItemsGeneratorDocsAndVersion = _.memoize(() => ({
|
const getSidebarItemsGeneratorDocsAndVersion = _.memoize(() => ({
|
||||||
|
@ -82,19 +81,6 @@ async function processSidebar(
|
||||||
return processItems(generatedItems);
|
return processItems(generatedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
const draftIds = new Set(drafts.flatMap(getDocIds));
|
|
||||||
|
|
||||||
const isDraftItem = (item: NormalizedSidebarItem): boolean => {
|
|
||||||
if (item.type === 'doc' || item.type === 'ref') {
|
|
||||||
return draftIds.has(item.id);
|
|
||||||
}
|
|
||||||
// If a category only contains draft items, it should be filtered entirely.
|
|
||||||
if (item.type === 'category') {
|
|
||||||
return item.items.every(isDraftItem);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
async function processItem(
|
async function processItem(
|
||||||
item: NormalizedSidebarItem,
|
item: NormalizedSidebarItem,
|
||||||
): Promise<ProcessedSidebarItem[]> {
|
): Promise<ProcessedSidebarItem[]> {
|
||||||
|
@ -102,7 +88,7 @@ async function processSidebar(
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
...item,
|
...item,
|
||||||
items: await processItems(item.items),
|
items: (await Promise.all(item.items.map(processItem))).flat(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -115,9 +101,7 @@ async function processSidebar(
|
||||||
async function processItems(
|
async function processItems(
|
||||||
items: NormalizedSidebarItem[],
|
items: NormalizedSidebarItem[],
|
||||||
): Promise<ProcessedSidebarItem[]> {
|
): Promise<ProcessedSidebarItem[]> {
|
||||||
return (
|
return (await Promise.all(items.map(processItem))).flat();
|
||||||
await Promise.all(items.filter((i) => !isDraftItem(i)).map(processItem))
|
|
||||||
).flat();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const processedSidebar = await processItems(unprocessedSidebar);
|
const processedSidebar = await processItems(unprocessedSidebar);
|
||||||
|
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
@ -64,4 +64,4 @@ An embedded expression is optionally preceded by a flag in the form `[a-z]+=` (a
|
||||||
|
|
||||||
If the expression is an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). Each member is formatted by itself and the bullet is not formatted. So you would see the above message printed as:
|
If the expression is an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). Each member is formatted by itself and the bullet is not formatted. So you would see the above message printed as:
|
||||||
|
|
||||||

|

|
Loading…
Add table
Add a link
Reference in a new issue