mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-23 11:38:48 +02:00
feat(docs,blog,pages): add support for "unlisted" front matter - hide md content in production (#8004)
Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
7a023a2c41
commit
683ba3d2a0
131 changed files with 2449 additions and 303 deletions
|
@ -5,7 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {normalizeFrontMatterTags, groupTaggedItems, type Tag} from '../tags';
|
||||
import {
|
||||
normalizeFrontMatterTags,
|
||||
groupTaggedItems,
|
||||
type Tag,
|
||||
getTagVisibility,
|
||||
} from '../tags';
|
||||
|
||||
describe('normalizeFrontMatterTags', () => {
|
||||
it('normalizes simple string tag', () => {
|
||||
|
@ -183,3 +188,52 @@ describe('groupTaggedItems', () => {
|
|||
expect(groupItems(input)).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTagVisibility', () => {
|
||||
type Item = {id: string; unlisted: boolean};
|
||||
|
||||
function isUnlisted(item: Item): boolean {
|
||||
return item.unlisted;
|
||||
}
|
||||
|
||||
const item1: Item = {id: '1', unlisted: false};
|
||||
const item2: Item = {id: '2', unlisted: true};
|
||||
const item3: Item = {id: '3', unlisted: false};
|
||||
const item4: Item = {id: '4', unlisted: true};
|
||||
|
||||
it('works for some unlisted', () => {
|
||||
expect(
|
||||
getTagVisibility({
|
||||
items: [item1, item2, item3, item4],
|
||||
isUnlisted,
|
||||
}),
|
||||
).toEqual({
|
||||
listedItems: [item1, item3],
|
||||
unlisted: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('works for all unlisted', () => {
|
||||
expect(
|
||||
getTagVisibility({
|
||||
items: [item2, item4],
|
||||
isUnlisted,
|
||||
}),
|
||||
).toEqual({
|
||||
listedItems: [item2, item4],
|
||||
unlisted: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('works for all listed', () => {
|
||||
expect(
|
||||
getTagVisibility({
|
||||
items: [item1, item3],
|
||||
isUnlisted,
|
||||
}),
|
||||
).toEqual({
|
||||
listedItems: [item1, item3],
|
||||
unlisted: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
54
packages/docusaurus-utils/src/contentVisibilityUtils.ts
Normal file
54
packages/docusaurus-utils/src/contentVisibilityUtils.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
type Env = 'production' | 'development';
|
||||
|
||||
/**
|
||||
* To easily work on draft/unlisted in dev mode, use this env variable!
|
||||
* SIMULATE_PRODUCTION_VISIBILITY=true yarn start:website
|
||||
*/
|
||||
const simulateProductionVisibility =
|
||||
process.env.SIMULATE_PRODUCTION_VISIBILITY === 'true';
|
||||
|
||||
/**
|
||||
* draft/unlisted is a production-only concept
|
||||
* In dev it is ignored and all content files are included
|
||||
*/
|
||||
function isProduction(env: Env | undefined): boolean {
|
||||
return (
|
||||
simulateProductionVisibility ||
|
||||
(env ?? process.env.NODE_ENV) === 'production'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A draft content will not be included in the production build
|
||||
*/
|
||||
export function isDraft({
|
||||
frontMatter,
|
||||
env,
|
||||
}: {
|
||||
frontMatter: {draft?: boolean};
|
||||
env?: Env;
|
||||
}): boolean {
|
||||
return (isProduction(env) && frontMatter.draft) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An unlisted content will be included in the production build, but hidden.
|
||||
* It is excluded from sitemap, has noIndex, does not appear in lists etc...
|
||||
* Only users having the link can find it.
|
||||
*/
|
||||
export function isUnlisted({
|
||||
frontMatter,
|
||||
env,
|
||||
}: {
|
||||
frontMatter: {unlisted?: boolean};
|
||||
env?: Env;
|
||||
}): boolean {
|
||||
return (isProduction(env) && frontMatter.unlisted) ?? false;
|
||||
}
|
|
@ -62,6 +62,7 @@ export {
|
|||
type FrontMatterTag,
|
||||
normalizeFrontMatterTags,
|
||||
groupTaggedItems,
|
||||
getTagVisibility,
|
||||
} from './tags';
|
||||
export {
|
||||
parseMarkdownHeadingId,
|
||||
|
@ -103,3 +104,4 @@ export {
|
|||
findFolderContainingFile,
|
||||
getFolderContainingFile,
|
||||
} from './dataFileUtils';
|
||||
export {isDraft, isUnlisted} from './contentVisibilityUtils';
|
||||
|
|
|
@ -25,6 +25,8 @@ export type TagsListItem = Tag & {
|
|||
export type TagModule = TagsListItem & {
|
||||
/** The tags list page's permalink. */
|
||||
allTagsPath: string;
|
||||
/** Is this tag unlisted? (when it only contains unlisted items) */
|
||||
unlisted: boolean;
|
||||
};
|
||||
|
||||
export type FrontMatterTag = string | Tag;
|
||||
|
@ -128,3 +130,32 @@ export function groupTaggedItems<Item>(
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permits to get the "tag visibility" (hard to find a better name)
|
||||
* IE, is this tag listed or unlisted
|
||||
* And which items should be listed when this tag is browsed
|
||||
*/
|
||||
export function getTagVisibility<Item>({
|
||||
items,
|
||||
isUnlisted,
|
||||
}: {
|
||||
items: Item[];
|
||||
isUnlisted: (item: Item) => boolean;
|
||||
}): {
|
||||
unlisted: boolean;
|
||||
listedItems: Item[];
|
||||
} {
|
||||
const allItemsUnlisted = items.every(isUnlisted);
|
||||
// When a tag is full of unlisted items, we display all the items
|
||||
// when tag is browsed, but we mark the tag as unlisted
|
||||
if (allItemsUnlisted) {
|
||||
return {unlisted: true, listedItems: items};
|
||||
}
|
||||
// When a tag has some listed items, the tag remains listed
|
||||
// but we filter its unlisted items
|
||||
return {
|
||||
unlisted: false,
|
||||
listedItems: items.filter((item) => !isUnlisted(item)),
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue