hexo-theme-stellar 1.44.0 → 2.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -5
- package/CONTRIBUTING.md +29 -120
- package/README.md +66 -73
- package/README_EN.md +66 -73
- package/_config.yml +890 -743
- package/_data/icons.yml +43 -171
- package/_data/widgets.yml +1 -1
- package/assets/icon-v2-x512.webp +0 -0
- package/languages/en.yml +59 -2
- package/languages/zh-CN.yml +59 -2
- package/languages/zh-TW.yml +59 -2
- package/layout/404.ejs +7 -4
- package/layout/_partial/comments/artalk/layout.ejs +3 -2
- package/layout/_partial/comments/beaudar/layout.ejs +2 -2
- package/layout/_partial/comments/giscus/layout.ejs +2 -5
- package/layout/_partial/comments/layout.ejs +11 -40
- package/layout/_partial/comments/twikoo/layout.ejs +3 -2
- package/layout/_partial/comments/utterances/layout.ejs +2 -2
- package/layout/_partial/comments/waline/layout.ejs +3 -2
- package/layout/_partial/components/collection-item.ejs +30 -13
- package/layout/_partial/components/collection.ejs +2 -2
- package/layout/_partial/cover/index.ejs +5 -2
- package/layout/_partial/cover/wiki_cover.ejs +39 -28
- package/layout/_partial/dropdown.ejs +26 -14
- package/layout/_partial/head.ejs +96 -69
- package/layout/_partial/main/article/contributors.ejs +12 -9
- package/layout/_partial/main/article/post_footer.ejs +47 -0
- package/layout/_partial/main/article/post_read_next.ejs +22 -0
- package/layout/_partial/main/article/post_related.ejs +14 -0
- package/layout/_partial/main/article/post_tags.ejs +12 -0
- package/layout/_partial/main/article/share.ejs +55 -0
- package/layout/_partial/main/footer.ejs +11 -10
- package/layout/_partial/main/{navbar/nav_tabs_blog.ejs → listing_nav/blog.ejs} +21 -18
- package/layout/_partial/main/listing_nav/wiki.ejs +56 -0
- package/layout/_partial/main/navbar/article_banner.ejs +39 -35
- package/layout/_partial/main/navbar/breadcrumb/blog.ejs +15 -28
- package/layout/_partial/main/navbar/breadcrumb/note.ejs +12 -5
- package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +15 -11
- package/layout/_partial/main/navbar/dateinfo.ejs +28 -12
- package/layout/_partial/main/navbar/ghinfo.ejs +5 -3
- package/layout/_partial/main/notebook/note_card.ejs +12 -17
- package/layout/_partial/main/notebook/note_tags.ejs +3 -4
- package/layout/_partial/main/notebook/notebook_card.ejs +11 -4
- package/layout/_partial/main/pin_slider.ejs +48 -43
- package/layout/_partial/main/post_list/archive_item.ejs +8 -4
- package/layout/_partial/main/post_list/latest_post_card.ejs +10 -4
- package/layout/_partial/main/post_list/post_card.ejs +39 -90
- package/layout/_partial/main/post_list/topic_card.ejs +7 -5
- package/layout/_partial/main/post_list/wiki_card.ejs +16 -13
- package/layout/_partial/menubtn.ejs +3 -3
- package/layout/_partial/primitives/item.ejs +8 -0
- package/layout/_partial/primitives/navigation.ejs +17 -0
- package/layout/_partial/primitives/region.ejs +16 -0
- package/layout/_partial/primitives/section.ejs +8 -0
- package/layout/_partial/primitives/shell.ejs +35 -0
- package/layout/_partial/regions/widgets.ejs +84 -0
- package/layout/_partial/scripts/defines.ejs +46 -76
- package/layout/_partial/scripts/runtime.ejs +55 -0
- package/layout/_partial/scripts.ejs +18 -42
- package/layout/_partial/search/dialog.ejs +33 -0
- package/layout/_partial/search/shortcut.ejs +1 -0
- package/layout/_partial/sidebar/brand.ejs +127 -0
- package/layout/_partial/sidebar/menu.ejs +59 -14
- package/layout/_partial/sidebar/search.ejs +58 -41
- package/layout/_partial/widgets/actions.ejs +25 -0
- package/layout/_partial/widgets/author.ejs +9 -5
- package/layout/_partial/widgets/components/linklist.ejs +1 -2
- package/layout/_partial/widgets/ghissues.ejs +7 -10
- package/layout/_partial/widgets/ghrepo.ejs +23 -16
- package/layout/_partial/widgets/ghuser.ejs +3 -3
- package/layout/_partial/widgets/recent.ejs +25 -21
- package/layout/_partial/widgets/related.ejs +25 -34
- package/layout/_partial/widgets/settings.ejs +17 -0
- package/layout/_partial/widgets/tagtree.ejs +18 -10
- package/layout/_partial/widgets/toc.ejs +17 -9
- package/layout/_partial/widgets/tree.ejs +12 -13
- package/layout/archive.ejs +20 -13
- package/layout/categories.ejs +4 -3
- package/layout/collection-preview.ejs +8 -9
- package/layout/index.ejs +32 -43
- package/layout/index_topic.ejs +6 -13
- package/layout/index_wiki.ejs +17 -34
- package/layout/layout.ejs +195 -65
- package/layout/notebooks.ejs +3 -3
- package/layout/notes.ejs +4 -4
- package/layout/page.ejs +95 -50
- package/layout/settings.ejs +109 -0
- package/layout/tags.ejs +4 -3
- package/{THIRD-PARTY-NOTICES.md → legal/THIRD-PARTY-NOTICES.md} +8 -4
- package/package.json +28 -6
- package/scripts/commands/stellar.js +55 -0
- package/scripts/events/index.js +15 -9
- package/scripts/events/lib/authors.js +9 -7
- package/scripts/events/lib/config-hot-reload.js +65 -0
- package/scripts/events/lib/config-schema.js +20 -0
- package/scripts/events/lib/config.js +22 -63
- package/scripts/events/lib/content-config.js +5 -0
- package/scripts/events/lib/doc_tree.js +99 -9
- package/scripts/events/lib/links.js +6 -3
- package/scripts/events/lib/notebooks.js +200 -8
- package/scripts/events/lib/utils.js +4 -4
- package/scripts/events/lib/version-check.js +57 -3
- package/scripts/filters/index.js +2 -27
- package/scripts/filters/lib/img_lazyload.js +4 -126
- package/scripts/filters/lib/img_onerror.js +14 -17
- package/scripts/filters/lib/page-view-model.js +152 -0
- package/scripts/generators/404.js +13 -8
- package/scripts/generators/author.js +16 -11
- package/scripts/generators/notebooks.js +68 -39
- package/scripts/generators/search.js +62 -67
- package/scripts/generators/settings.js +43 -0
- package/scripts/generators/stellar-icons.js +13 -5
- package/scripts/generators/topic.js +24 -13
- package/scripts/generators/wiki.js +49 -21
- package/scripts/helpers/ai_label.js +6 -12
- package/scripts/helpers/brand.js +17 -0
- package/scripts/helpers/browser_runtime_manifest.js +12 -0
- package/scripts/helpers/caption.js +8 -0
- package/scripts/helpers/category_color.js +13 -13
- package/scripts/helpers/collection.js +18 -0
- package/scripts/helpers/comments_model.js +14 -0
- package/scripts/helpers/extension_assets.js +8 -0
- package/scripts/helpers/json_ld.js +16 -7
- package/scripts/helpers/mdrender.js +3 -9
- package/scripts/helpers/menu.js +9 -0
- package/scripts/helpers/post_view_model.js +23 -0
- package/scripts/helpers/regions.js +37 -0
- package/scripts/helpers/scrollreveal.js +7 -7
- package/scripts/helpers/search_domain.js +34 -0
- package/scripts/helpers/service_provider.js +9 -0
- package/scripts/helpers/settings.js +16 -0
- package/scripts/helpers/share_action.js +8 -0
- package/scripts/helpers/stellar_config.js +12 -0
- package/scripts/helpers/stellar_data.js +8 -0
- package/scripts/helpers/stellar_info.js +21 -16
- package/scripts/helpers/stellar_inject.js +9 -0
- package/scripts/helpers/ui_classes.js +12 -0
- package/scripts/lib/ai_label.js +14 -11
- package/scripts/lib/brand.js +41 -0
- package/scripts/lib/browser-runtime.js +115 -0
- package/scripts/lib/caption.js +21 -0
- package/scripts/lib/collection-pipeline/adapters/notebook.js +62 -0
- package/scripts/lib/collection-pipeline/adapters/post.js +20 -0
- package/scripts/lib/collection-pipeline/adapters/topic.js +82 -0
- package/scripts/lib/collection-pipeline/adapters/wiki.js +20 -0
- package/scripts/lib/collection-pipeline/index.js +169 -0
- package/scripts/lib/collection-pipeline/registry.js +27 -0
- package/scripts/lib/collection-pipeline/shared.js +164 -0
- package/scripts/lib/comments.js +60 -0
- package/scripts/lib/config-inject.js +20 -0
- package/scripts/lib/config-schema.js +1095 -0
- package/scripts/lib/content-config.js +157 -0
- package/scripts/lib/content-defaults.js +56 -0
- package/scripts/lib/content-membership.js +233 -0
- package/scripts/lib/contribution-contract.js +165 -0
- package/scripts/lib/contribution-registry.js +429 -0
- package/scripts/lib/doc_tree.js +46 -44
- package/scripts/lib/doctor.js +296 -0
- package/scripts/lib/front-matter.js +29 -0
- package/scripts/lib/hero-effect-registry.js +262 -0
- package/scripts/lib/html-images.js +128 -0
- package/scripts/lib/internal-constants.js +152 -0
- package/scripts/lib/layout-config.js +62 -0
- package/scripts/lib/mdrender_html.js +36 -23
- package/scripts/lib/menu.js +36 -0
- package/scripts/lib/model-schema.js +90 -0
- package/scripts/lib/models/index.js +1542 -0
- package/scripts/lib/new-note.js +172 -0
- package/scripts/lib/page-view-model-registry.js +103 -0
- package/scripts/lib/regions.js +132 -0
- package/scripts/lib/runtime-data.js +23 -0
- package/scripts/lib/safe-path.js +44 -0
- package/scripts/lib/search-domain.js +59 -0
- package/scripts/lib/seo.js +10 -10
- package/scripts/lib/service-provider.js +12 -0
- package/scripts/lib/settings.js +16 -0
- package/scripts/lib/share-services.js +96 -0
- package/scripts/lib/source-config.js +27 -0
- package/scripts/lib/theme-metadata.js +22 -0
- package/scripts/lib/ui-capabilities.js +32 -0
- package/scripts/lib/widget-registry.js +150 -0
- package/scripts/lib/wiki_readme.js +15 -11
- package/scripts/schema/config-rules.js +290 -0
- package/scripts/schema/config-schema.js +163 -0
- package/scripts/schema/content-config-rules.js +264 -0
- package/scripts/schema/content-config-schema.js +321 -0
- package/scripts/schema/model-schema.js +971 -0
- package/scripts/schema/schema-utils.js +48 -0
- package/scripts/tags/index.js +2 -3
- package/scripts/tags/lib/albums.js +6 -6
- package/scripts/tags/lib/banner.js +10 -4
- package/scripts/tags/lib/box.js +1 -1
- package/scripts/tags/lib/button.js +1 -1
- package/scripts/tags/lib/chat.js +9 -31
- package/scripts/tags/lib/checkbox.js +6 -1
- package/scripts/tags/lib/copy.js +4 -1
- package/scripts/tags/lib/dropdown.js +6 -4
- package/scripts/tags/lib/emoji.js +5 -10
- package/scripts/tags/lib/friends.js +4 -4
- package/scripts/tags/lib/gallery.js +7 -7
- package/scripts/tags/lib/ghcard.js +5 -3
- package/scripts/tags/lib/gist.js +16 -0
- package/scripts/tags/lib/grid.js +4 -2
- package/scripts/tags/lib/hashtag.js +1 -1
- package/scripts/tags/lib/icon.js +1 -1
- package/scripts/tags/lib/image.js +1 -1
- package/scripts/tags/lib/link.js +6 -3
- package/scripts/tags/lib/mark.js +8 -3
- package/scripts/tags/lib/md.js +1 -1
- package/scripts/tags/lib/note.js +1 -1
- package/scripts/tags/lib/okr.js +18 -1
- package/scripts/tags/lib/posters.js +6 -6
- package/scripts/tags/lib/quot.js +15 -5
- package/scripts/tags/lib/rating.js +7 -4
- package/scripts/tags/lib/sites.js +8 -6
- package/scripts/tags/lib/tip.js +6 -6
- package/scripts/tags/lib/toc.js +2 -2
- package/scripts/tags/lib/vote.js +8 -5
- package/source/css/_appearances/_mixins.styl +258 -0
- package/source/css/_appearances/card.styl +30 -0
- package/source/css/_appearances/flat.styl +44 -0
- package/source/css/_appearances/glass.styl +138 -0
- package/source/css/_appearances/minimal.styl +325 -0
- package/source/css/_common/base.styl +5 -9
- package/source/css/_common/blockquote.styl +0 -1
- package/source/css/_common/button.styl +1 -1
- package/source/css/_common/device.styl +24 -42
- package/source/css/_common/dropdown.styl +20 -14
- package/source/css/_common/highlight.styl +5 -12
- package/source/css/_common/icon.styl +38 -0
- package/source/css/_common/input.styl +33 -0
- package/source/css/_common/loading.styl +2 -2
- package/source/css/_common/svg.styl +0 -8
- package/source/css/_components/collection.styl +21 -64
- package/source/css/_components/layout.styl +524 -84
- package/source/css/_components/list.styl +36 -22
- package/source/css/_components/main.styl +16 -19
- package/source/css/_components/md.styl +8 -13
- package/source/css/_components/pages/article-indent.styl +1 -1
- package/source/css/_components/pages/article-story.styl +7 -6
- package/source/css/_components/pages/article-tech.styl +5 -5
- package/source/css/_components/pages/collection-preview.styl +1 -5
- package/source/css/_components/pages/settings.styl +184 -0
- package/source/css/_components/partial/article-banner.styl +2 -2
- package/source/css/_components/partial/article-footer.styl +0 -32
- package/source/css/_components/partial/article-share.styl +48 -0
- package/source/css/_components/partial/article-tags.styl +1 -1
- package/source/css/_components/partial/cover.styl +13 -13
- package/source/css/_components/partial/listing-nav.styl +68 -0
- package/source/css/_components/partial/paginator.styl +1 -1
- package/source/css/_components/pin-slider.styl +3 -3
- package/source/css/_components/sidebar/brand.styl +135 -0
- package/source/css/_components/sidebar/footer.styl +14 -31
- package/source/css/_components/sidebar/nav-area.styl +4 -0
- package/source/css/_components/sidebar/search.styl +176 -26
- package/source/css/_components/sidebar/sidebar.styl +76 -110
- package/source/css/_components/tag-plugins/banner.styl +16 -28
- package/source/css/_components/tag-plugins/checkbox.styl +7 -22
- package/source/css/_components/tag-plugins/copy.styl +3 -5
- package/source/css/_components/tag-plugins/gallery.styl +2 -2
- package/source/css/_components/tag-plugins/grid.styl +3 -0
- package/source/css/_components/tag-plugins/image.styl +2 -1
- package/source/css/_components/tag-plugins/link.styl +0 -1
- package/source/css/_components/tag-plugins/mark.styl +19 -4
- package/source/css/_components/tag-plugins/mdrender.styl +2 -2
- package/source/css/_components/tag-plugins/navbar.styl +23 -17
- package/source/css/_components/tag-plugins/note.styl +2 -2
- package/source/css/_components/tag-plugins/okr.styl +1 -2
- package/source/css/_components/tag-plugins/quot.styl +5 -0
- package/source/css/_components/tag-plugins/rating.styl +3 -2
- package/source/css/_components/tag-plugins/timeline.styl +1 -1
- package/source/css/_components/tag-plugins/tip.styl +1 -1
- package/source/css/_components/tag-plugins/vote.styl +3 -2
- package/source/css/_components/widgets/ghrepo.styl +2 -1
- package/source/css/_components/widgets/ghuser.styl +4 -6
- package/source/css/_components/widgets/settings.styl +49 -0
- package/source/css/_components/widgets/timeline.styl +2 -2
- package/source/css/_components/widgets/toc.styl +12 -33
- package/source/css/_components/widgets/widgets.styl +14 -19
- package/source/css/_components/wiki-card.styl +2 -2
- package/source/css/_custom.styl +26 -32
- package/source/css/_defines/func.styl +47 -37
- package/source/css/_defines/theme_base.styl +8 -10
- package/source/css/_plugins/card-hover.styl +1 -1
- package/source/css/_plugins/copycode.styl +16 -19
- package/source/css/_plugins/index.styl +3 -8
- package/source/css/_plugins/lazyload.styl +1 -1
- package/source/css/main.styl +12 -0
- package/source/js/main.js +302 -59
- package/source/js/plugins/adaptive-text.js +33 -16
- package/source/js/plugins/card-hover.js +2 -24
- package/source/js/plugins/copycode.js +10 -16
- package/source/js/plugins/voice.js +51 -48
- package/source/js/profile.js +316 -0
- package/source/js/runtime/asset-loader.js +61 -0
- package/source/js/runtime/extension-registry.js +149 -0
- package/source/js/runtime/extensions/card-hover.js +5 -0
- package/source/js/runtime/extensions/color-scheme-switch.js +101 -0
- package/source/js/runtime/extensions/comments.js +200 -0
- package/source/js/runtime/extensions/deferred-icons.js +53 -0
- package/source/js/{plugins → runtime/extensions}/dropdown.js +69 -30
- package/source/js/runtime/extensions/feature.js +200 -0
- package/source/js/runtime/extensions/hero-effect.js +179 -0
- package/source/js/runtime/extensions/reveal.js +80 -0
- package/source/js/runtime/extensions/search.js +23 -0
- package/source/js/{services.js → runtime/extensions/services.js} +67 -54
- package/source/js/runtime/extensions/settings.js +235 -0
- package/source/js/runtime/hero-effects/ferrofluid.js +385 -0
- package/source/js/{plugins → runtime/hero-effects}/galaxy.js +53 -177
- package/source/js/runtime/hero-effects/light-rays.js +293 -0
- package/source/js/runtime/index.js +75 -0
- package/source/js/runtime/legacy-request-adapter.js +47 -0
- package/source/js/runtime/request-cache.js +219 -0
- package/source/js/search/algolia-search.js +76 -51
- package/source/js/search/highlight.js +1 -1
- package/source/js/search/local-search.js +96 -57
- package/source/js/search/shortcut.js +31 -20
- package/source/js/services/friends_and_posts.js +6 -1
- package/source/js/services/rating.js +14 -6
- package/source/js/services/siteinfo.js +3 -7
- package/source/js/services/timeline.js +1 -1
- package/source/js/services/vote.js +7 -5
- package/source/js/services/weibo.js +1 -1
- package/source/js/utils.js +19 -444
- package/.claude/skills/stellar-theme-dev/SKILL.md +0 -51
- package/_data/chat_users.yml +0 -0
- package/layout/_partial/comments/artalk/script.ejs +0 -76
- package/layout/_partial/comments/beaudar/script.ejs +0 -33
- package/layout/_partial/comments/giscus/script.ejs +0 -31
- package/layout/_partial/comments/script.ejs +0 -3
- package/layout/_partial/comments/twikoo/script.ejs +0 -25
- package/layout/_partial/comments/utterances/script.ejs +0 -33
- package/layout/_partial/comments/waline/script.ejs +0 -43
- package/layout/_partial/main/article/article_footer.ejs +0 -164
- package/layout/_partial/main/article/article_tags.ejs +0 -16
- package/layout/_partial/main/article/read_next.ejs +0 -63
- package/layout/_partial/main/article/related_posts.ejs +0 -16
- package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +0 -52
- package/layout/_partial/scripts/bootstrap.ejs +0 -37
- package/layout/_partial/scripts/lazyload.ejs +0 -89
- package/layout/_partial/sidebar/grad-def.ejs +0 -26
- package/layout/_partial/sidebar/index_leftbar.ejs +0 -191
- package/layout/_partial/sidebar/index_rightbar.ejs +0 -94
- package/layout/_partial/sidebar/logo.ejs +0 -99
- package/layout/_plugins/adaptive_text.ejs +0 -11
- package/layout/_plugins/card_hover.ejs +0 -32
- package/layout/_plugins/copycode.ejs +0 -17
- package/layout/_plugins/fancybox.ejs +0 -53
- package/layout/_plugins/heti.ejs +0 -21
- package/layout/_plugins/index.ejs +0 -50
- package/layout/_plugins/mathjax.ejs +0 -49
- package/layout/_plugins/mermaid.ejs +0 -33
- package/layout/_plugins/preload.ejs +0 -9
- package/layout/_plugins/scrollreveal.ejs +0 -102
- package/layout/_plugins/search/algolia_search.ejs +0 -12
- package/layout/_plugins/search/local_search.ejs +0 -9
- package/layout/_plugins/swiper.ejs +0 -27
- package/layout/_plugins/tianli_gpt.ejs +0 -14
- package/scripts/commands/new-note.js +0 -49
- package/scripts/events/lib/merge_posts.js +0 -48
- package/scripts/events/lib/topic_tree.js +0 -48
- package/scripts/helpers/related_posts.js +0 -51
- package/scripts/helpers/subtitle.js +0 -8
- package/scripts/lib/notebooks.js +0 -181
- package/scripts/lib/subtitle.js +0 -28
- package/scripts/tags/lib/about.js +0 -48
- package/source/css/_components/page-transition.styl +0 -22
- package/source/css/_components/partial/navbar.styl +0 -51
- package/source/css/_components/sidebar/logo.styl +0 -111
- package/source/css/_components/tag-plugins/about.styl +0 -56
- package/source/css/_components/widgets/tree.styl +0 -0
- package/source/css/_plugins/scrollreveal.styl +0 -6
- package/source/css/_plugins/tianli_gpt.styl +0 -213
- package/source/css/plugins/mermaid.styl +0 -137
- package/source/js/icons.js +0 -60
- package/source/js/services/fcircle.js +0 -35
- package/source/js/theme.js +0 -61
|
@@ -0,0 +1,1542 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { gravatar, stripHTML, truncate } = require("hexo-util");
|
|
4
|
+
const {
|
|
5
|
+
CONTENT_MODEL_FIELDS,
|
|
6
|
+
ContentConfigError,
|
|
7
|
+
isPlainObject
|
|
8
|
+
} = require("../content-config");
|
|
9
|
+
const { assertPageViewModel } = require("../model-schema");
|
|
10
|
+
const {
|
|
11
|
+
ConfigSchemaError,
|
|
12
|
+
isPlainObject: isPlainConfigObject,
|
|
13
|
+
valueType: configValueType
|
|
14
|
+
} = require("../config-schema");
|
|
15
|
+
const { normalize_path: normalizePath } = require("../path_utils");
|
|
16
|
+
const {
|
|
17
|
+
profilePath,
|
|
18
|
+
requireLayoutProfiles,
|
|
19
|
+
toRenderNavigation,
|
|
20
|
+
toRenderRegions
|
|
21
|
+
} = require("../layout-config");
|
|
22
|
+
const { cascadeRegions, resolveRegions } = require("../regions");
|
|
23
|
+
const { normalizeBrand } = require("../brand");
|
|
24
|
+
const { firstContentImage, postDescription, postImages } = require("../seo");
|
|
25
|
+
const { caption } = require("../caption");
|
|
26
|
+
const { wikiReadmeHtml } = require("../wiki_readme");
|
|
27
|
+
const { resolveServiceProvider } = require("../service-provider");
|
|
28
|
+
const { filterShareServices } = require("../share-services");
|
|
29
|
+
const { projectHeroEffect } = require("../hero-effect-registry");
|
|
30
|
+
const { normalizeThemeComments, resolveCommentsModel } = require("../comments");
|
|
31
|
+
const {
|
|
32
|
+
articleFooterDefaults,
|
|
33
|
+
articlePresentationDefaults,
|
|
34
|
+
collectionFooterDefaults,
|
|
35
|
+
requireContentConfig
|
|
36
|
+
} = require("../content-defaults");
|
|
37
|
+
|
|
38
|
+
function cloneValue(value) {
|
|
39
|
+
if (Array.isArray(value)) return value.map(cloneValue);
|
|
40
|
+
if (value == null || typeof value !== "object") return value;
|
|
41
|
+
const result = {};
|
|
42
|
+
for (const [key, child] of Object.entries(value)) {
|
|
43
|
+
result[key] = cloneValue(child);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function heroBackground(value) {
|
|
49
|
+
const background = cloneValue(value || {});
|
|
50
|
+
if (background.effect) background.effect = projectHeroEffect(background.effect);
|
|
51
|
+
return background;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function deepFreeze(value) {
|
|
55
|
+
if (value == null || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
56
|
+
Object.values(value).forEach(deepFreeze);
|
|
57
|
+
return Object.freeze(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function pick(source, fields) {
|
|
61
|
+
const result = {};
|
|
62
|
+
if (!isPlainObject(source)) return result;
|
|
63
|
+
for (const field of fields) {
|
|
64
|
+
if (source[field] != null) result[field] = cloneValue(source[field]);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function mergeConfig(base, override, path = "") {
|
|
70
|
+
const result = cloneValue(isPlainObject(base) ? base : {});
|
|
71
|
+
if (!isPlainObject(override)) return result;
|
|
72
|
+
for (const [key, value] of Object.entries(override)) {
|
|
73
|
+
if (value == null) continue;
|
|
74
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
75
|
+
if (isPlainObject(value) && isPlainObject(result[key])) {
|
|
76
|
+
result[key] = mergeConfig(result[key], value, fieldPath);
|
|
77
|
+
} else {
|
|
78
|
+
result[key] = cloneValue(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function toContentNavigation(config) {
|
|
85
|
+
const navigation = {};
|
|
86
|
+
if (config?.activeMenu != null) navigation.menu = config.activeMenu;
|
|
87
|
+
if (config?.breadcrumb != null) navigation.breadcrumb = config.breadcrumb;
|
|
88
|
+
return navigation;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectionIndexHref(path) {
|
|
92
|
+
if (typeof path !== "string") return null;
|
|
93
|
+
const normalized = profilePath(path);
|
|
94
|
+
return normalized ? `/${normalized}/` : "/";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function collectionBrand(identity, href, options = {}) {
|
|
98
|
+
const normalizedHref = normalizeCollectionPath(href);
|
|
99
|
+
const brand = {
|
|
100
|
+
source: "collection",
|
|
101
|
+
style: "regular",
|
|
102
|
+
backButton: true,
|
|
103
|
+
search: true,
|
|
104
|
+
backHref: collectionIndexHref(options.indexPath),
|
|
105
|
+
backLabel: options.backLabel || "",
|
|
106
|
+
image: { src: identity.icon || null, variant: "icon" },
|
|
107
|
+
name: identity.name,
|
|
108
|
+
tagline: identity.tagline,
|
|
109
|
+
href: normalizedHref ? `/${normalizedHref}/` : "/"
|
|
110
|
+
};
|
|
111
|
+
return brand;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function assertCollectionBrandConfig(config, defaultSource, source) {
|
|
115
|
+
const brand = config?.leftbar?.brand;
|
|
116
|
+
if (!isPlainObject(brand)) return;
|
|
117
|
+
const resolvedSource = brand.source || defaultSource;
|
|
118
|
+
if (resolvedSource === "collection") return;
|
|
119
|
+
const unsupported = ["backButton", "search"].filter(key => brand[key] != null);
|
|
120
|
+
if (unsupported.length === 0) return;
|
|
121
|
+
throw new ContentConfigError(unsupported.map(key => (
|
|
122
|
+
`${source}: leftbar.brand.${key === "backButton" ? "back_button" : key} 仅支持 source: collection`
|
|
123
|
+
)));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function articleIndentEnabled(article) {
|
|
127
|
+
const style = article?.style;
|
|
128
|
+
const mode = article?.paragraphIndent || "auto";
|
|
129
|
+
return mode === "always" || (mode === "auto" && style === "story");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function finalizedRegions(input, collection, item) {
|
|
133
|
+
return resolveRegions({
|
|
134
|
+
profile: collection.profile,
|
|
135
|
+
defaultState: input.stellarConfig.leftbar.defaultState,
|
|
136
|
+
catalog: input.runtimeData?.widgets || {},
|
|
137
|
+
layers: [item.presentation]
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function renderRegionLayout(regionLayout) {
|
|
142
|
+
return {
|
|
143
|
+
topbar: regionLayout.topbar,
|
|
144
|
+
leftbar: regionLayout.leftbar,
|
|
145
|
+
rightbar: regionLayout.rightbar,
|
|
146
|
+
regionWarnings: regionLayout.warnings
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function assertNormalizedConfig(stellarConfig, source, requirements) {
|
|
151
|
+
const issues = [];
|
|
152
|
+
for (const requirement of requirements) {
|
|
153
|
+
const value = requirement.read(stellarConfig);
|
|
154
|
+
if (isPlainConfigObject(value)) continue;
|
|
155
|
+
issues.push(Object.freeze({
|
|
156
|
+
code: "invalid_type",
|
|
157
|
+
source,
|
|
158
|
+
path: requirement.path,
|
|
159
|
+
actualType: configValueType(value),
|
|
160
|
+
expected: requirement.expected,
|
|
161
|
+
migration: requirement.migration
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
if (issues.length > 0) throw new ConfigSchemaError(issues);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function layoutConfigRequirement() {
|
|
168
|
+
return {
|
|
169
|
+
path: "stellarConfig.profiles",
|
|
170
|
+
read: config => config?.profiles,
|
|
171
|
+
expected: "normalized Layout Profile object",
|
|
172
|
+
migration: null
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizeDate(value) {
|
|
177
|
+
if (value == null) return null;
|
|
178
|
+
if (typeof value.format === "function") return value.format();
|
|
179
|
+
if (typeof value.toISOString === "function") return value.toISOString();
|
|
180
|
+
const date = new Date(value);
|
|
181
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeLanguage(...values) {
|
|
185
|
+
for (const value of values) {
|
|
186
|
+
if (Array.isArray(value)) {
|
|
187
|
+
const language = value.find(item => typeof item === "string" && item.length > 0);
|
|
188
|
+
if (language) return language;
|
|
189
|
+
} else if (typeof value === "string" && value.length > 0) {
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return "";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function normalizeTerms(value) {
|
|
197
|
+
let items = value;
|
|
198
|
+
if (items != null && !Array.isArray(items) && typeof items.toArray === "function") {
|
|
199
|
+
items = items.toArray();
|
|
200
|
+
}
|
|
201
|
+
if (!Array.isArray(items)) return [];
|
|
202
|
+
return items.map(item => {
|
|
203
|
+
if (typeof item === "string") return item;
|
|
204
|
+
if (item != null && typeof item.name === "string") return item.name;
|
|
205
|
+
return null;
|
|
206
|
+
}).filter(item => item != null);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizeCollectionPath(value) {
|
|
210
|
+
return normalizePath(value).replace(/^\/+/, "");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function normalizeStringList(value) {
|
|
214
|
+
if (Array.isArray(value)) {
|
|
215
|
+
return value.map(item => String(item)).filter(Boolean);
|
|
216
|
+
}
|
|
217
|
+
if (typeof value === "string" && value.length > 0) {
|
|
218
|
+
return value.split(",").map(item => item.trim()).filter(Boolean);
|
|
219
|
+
}
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function normalizeInject(value) {
|
|
224
|
+
return typeof value === "string" ? value : "";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function normalizeCategoryLinks(value) {
|
|
228
|
+
if (!Array.isArray(value)) return [];
|
|
229
|
+
return value.map(item => ({
|
|
230
|
+
name: typeof item?.name === "string" ? item.name : "",
|
|
231
|
+
path: typeof item?.path === "string" ? normalizeCollectionPath(item.path) : ""
|
|
232
|
+
})).filter(item => item.name.length > 0 && item.path.length > 0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function normalizeLinks(value) {
|
|
236
|
+
if (!Array.isArray(value)) return [];
|
|
237
|
+
return value.map(item => ({
|
|
238
|
+
name: typeof item?.name === "string" ? item.name : "",
|
|
239
|
+
path: typeof item?.path === "string" ? normalizeCollectionPath(item.path) : ""
|
|
240
|
+
})).filter(item => item.name.length > 0 && item.path.length > 0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function normalizePostLink(value) {
|
|
244
|
+
if (value == null || typeof value !== "object") return null;
|
|
245
|
+
const path = typeof value.path === "string" ? normalizeCollectionPath(value.path) : "";
|
|
246
|
+
if (path.length === 0) return null;
|
|
247
|
+
return {
|
|
248
|
+
title: typeof value.title === "string" ? value.title : "",
|
|
249
|
+
path,
|
|
250
|
+
date: normalizeDate(value.date)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function normalizeRelatedItems(value) {
|
|
255
|
+
if (!Array.isArray(value)) return [];
|
|
256
|
+
return value.map(item => ({
|
|
257
|
+
title: typeof item?.title === "string" ? item.title : "",
|
|
258
|
+
path: typeof item?.path === "string" ? item.path : "",
|
|
259
|
+
excerpt: typeof item?.excerpt === "string" ? item.excerpt : ""
|
|
260
|
+
})).filter(item => item.title.length > 0 && item.path.length > 0);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function categoryStyle(category, categoryColors) {
|
|
264
|
+
if (typeof category !== "string" || category.length === 0 || !isPlainObject(categoryColors)) return "";
|
|
265
|
+
const raw = categoryColors[category];
|
|
266
|
+
if (typeof raw !== "string" || raw.length === 0) return "";
|
|
267
|
+
const color = raw.startsWith("#") ? raw : `#${raw}`;
|
|
268
|
+
const background = color.length === 4 ? `${color}2` : color.length === 7 ? `${color}20` : color;
|
|
269
|
+
return `--text-p2:${color};--theme-block:${background}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function resolveLicense(license, item, runtimeData) {
|
|
273
|
+
if (typeof license !== "string" || license.length === 0) return "";
|
|
274
|
+
const authors = isPlainObject(runtimeData.authors) ? runtimeData.authors : null;
|
|
275
|
+
if (!authors) return license;
|
|
276
|
+
const authorId = item.presentation.article?.author;
|
|
277
|
+
const author = typeof authorId === "string" && isPlainObject(authors[authorId])
|
|
278
|
+
? authors[authorId]
|
|
279
|
+
: isPlainObject(runtimeData.defaultAuthor) ? runtimeData.defaultAuthor : null;
|
|
280
|
+
if (!author) return license;
|
|
281
|
+
return license
|
|
282
|
+
.replace("{author.name}", String(author.name || ""))
|
|
283
|
+
.replace("{author.url}", String(author.url || ""));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function buildContributor(item, stellarConfig) {
|
|
287
|
+
const repositories = resolveServiceProvider(stellarConfig.services.contributors)?.repositories;
|
|
288
|
+
if (!Array.isArray(repositories)) return null;
|
|
289
|
+
const source = item.source.file || "";
|
|
290
|
+
const matched = repositories
|
|
291
|
+
.filter(item => typeof item?.sourcePrefix === "string" && source.startsWith(item.sourcePrefix))
|
|
292
|
+
.sort((left, right) => right.sourcePrefix.length - left.sourcePrefix.length)[0];
|
|
293
|
+
if (!matched) return null;
|
|
294
|
+
const relativePath = source.slice(matched.sourcePrefix.length).replace(/^\/+/, "");
|
|
295
|
+
const branch = matched.branch || "main";
|
|
296
|
+
const apiUrl = stellarConfig.services.github.apiUrl.replace(/\/+$/, "");
|
|
297
|
+
return {
|
|
298
|
+
editUrl: `https://github.com/${matched.repository}/blob/${branch}/${relativePath}`,
|
|
299
|
+
commitsUrl: `${apiUrl}/repos/${matched.repository}/commits?path=${encodeURIComponent(relativePath)}`
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function buildPostArticleRender(input, item) {
|
|
304
|
+
const runtimeData = input.runtimeData;
|
|
305
|
+
const articleConfig = requireContentConfig(input.stellarConfig, input.themeSource).article;
|
|
306
|
+
const frontMatter = input.frontMatter;
|
|
307
|
+
const footer = item.presentation.footer || {};
|
|
308
|
+
const extensionConfig = input.stellarConfig;
|
|
309
|
+
const configuredShare = footer.share === true
|
|
310
|
+
? articleConfig.footer.share
|
|
311
|
+
: Array.isArray(footer.share) ? footer.share : [];
|
|
312
|
+
const configuredLicense = footer.license === true
|
|
313
|
+
? articleConfig.footer.license
|
|
314
|
+
: footer.license;
|
|
315
|
+
const shareServices = filterShareServices(configuredShare);
|
|
316
|
+
const summarySource = typeof frontMatter.description === "string" && frontMatter.description.length > 0
|
|
317
|
+
? frontMatter.description
|
|
318
|
+
: item.excerpt || item.content;
|
|
319
|
+
const relatedPostsLimit = articleConfig.relatedPostsLimit;
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
heti: extensionConfig.features.heti.enabled === true,
|
|
323
|
+
tags: footer.showTags === true ? normalizeLinks(input.page.tagLinks) : [],
|
|
324
|
+
footer: {
|
|
325
|
+
references: Array.isArray(footer.references) ? cloneValue(footer.references) : [],
|
|
326
|
+
license: resolveLicense(configuredLicense, item, runtimeData),
|
|
327
|
+
share: shareServices.length > 0 ? {
|
|
328
|
+
services: shareServices,
|
|
329
|
+
permalink: item.route.permalink,
|
|
330
|
+
title: `${item.title} - ${String(input.siteConfig.title || "")}`,
|
|
331
|
+
image: item.cover || "",
|
|
332
|
+
summary: truncate(stripHTML(summarySource), { length: 120 })
|
|
333
|
+
} : null,
|
|
334
|
+
contributor: buildContributor(item, input.stellarConfig)
|
|
335
|
+
},
|
|
336
|
+
previous: normalizePostLink(input.page.previous),
|
|
337
|
+
next: normalizePostLink(input.page.next),
|
|
338
|
+
related: {
|
|
339
|
+
enabled: relatedPostsLimit > 0,
|
|
340
|
+
title: "",
|
|
341
|
+
maxCount: relatedPostsLimit,
|
|
342
|
+
items: normalizeRelatedItems(input.relatedItems)
|
|
343
|
+
},
|
|
344
|
+
comments: buildCommentsRender(input.stellarConfig, item)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function buildPostListingRender(input, collection, item) {
|
|
349
|
+
const articleConfig = requireContentConfig(input.stellarConfig, input.themeSource).article;
|
|
350
|
+
const categories = normalizeCategoryLinks(input.page.categoryLinks);
|
|
351
|
+
const tagLinks = normalizeLinks(input.page.tagLinks);
|
|
352
|
+
const description = typeof input.frontMatter.description === "string" ? input.frontMatter.description : "";
|
|
353
|
+
const excerpt = item.excerpt
|
|
354
|
+
? stripHTML(item.excerpt)
|
|
355
|
+
: description || (collection.listing.excerptLength > 0
|
|
356
|
+
? truncate(stripHTML(item.content), { length: collection.listing.excerptLength })
|
|
357
|
+
: "");
|
|
358
|
+
const lastCategory = categories.length > 0 ? categories[categories.length - 1].name : "";
|
|
359
|
+
return {
|
|
360
|
+
href: typeof input.page.link === "string" && input.page.link.length > 0
|
|
361
|
+
? input.page.link
|
|
362
|
+
: item.route.path,
|
|
363
|
+
title: item.title,
|
|
364
|
+
layout: item.layout,
|
|
365
|
+
date: item.date,
|
|
366
|
+
cover: item.cover || "",
|
|
367
|
+
caption: caption({
|
|
368
|
+
tagline: item.tagline,
|
|
369
|
+
description,
|
|
370
|
+
excerpt: item.excerpt,
|
|
371
|
+
content: item.content
|
|
372
|
+
}),
|
|
373
|
+
excerpt,
|
|
374
|
+
categories: categories.map(category => category.name),
|
|
375
|
+
categoryStyle: categoryStyle(lastCategory, articleConfig.categoryColors),
|
|
376
|
+
tags: articleConfig.listing.showTags === true
|
|
377
|
+
? tagLinks.slice(0, 5).map(tag => tag.name)
|
|
378
|
+
: [],
|
|
379
|
+
authorId: typeof item.presentation.article?.author === "string" && item.presentation.article.author.length > 0
|
|
380
|
+
? item.presentation.article.author
|
|
381
|
+
: String(input.runtimeData.defaultAuthor?.id || ""),
|
|
382
|
+
priority: item.listing.priority,
|
|
383
|
+
listed: item.visibility.listed !== false,
|
|
384
|
+
cardStyle: collection.listing.cardStyle || "classic"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function absoluteSiteAsset(value, siteUrl) {
|
|
389
|
+
if (typeof value !== "string" || value.length === 0) return "";
|
|
390
|
+
if (/^https?:\/\//.test(value)) return value;
|
|
391
|
+
const root = String(siteUrl || "").replace(/\/+$/, "");
|
|
392
|
+
if (root.length === 0) return value;
|
|
393
|
+
return `${root}/${value.replace(/^\/+/, "")}`;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function canonicalUrl(host, path) {
|
|
397
|
+
if (typeof host !== "string" || host.length === 0) return null;
|
|
398
|
+
const normalizedPath = normalizeCollectionPath(path);
|
|
399
|
+
if (normalizedPath === "404" || normalizedPath.startsWith("404/")) return null;
|
|
400
|
+
return `https://${host}/${normalizedPath}${normalizedPath ? "/" : ""}`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function buildPostRenderModel(input, collection, item) {
|
|
404
|
+
const siteConfig = input.siteConfig;
|
|
405
|
+
const seoConfig = input.stellarConfig;
|
|
406
|
+
const appearance = input.stellarConfig.appearance;
|
|
407
|
+
const fallbacks = input.stellarConfig.fallbacks;
|
|
408
|
+
const canonicalConfig = seoConfig.canonical;
|
|
409
|
+
const frontMatter = input.frontMatter;
|
|
410
|
+
const page = input.page;
|
|
411
|
+
const articleStyle = typeof item.presentation.article?.style === "string"
|
|
412
|
+
? item.presentation.article.style
|
|
413
|
+
: null;
|
|
414
|
+
const explicitDescription = typeof frontMatter.description === "string" && frontMatter.description.length > 0
|
|
415
|
+
? frontMatter.description
|
|
416
|
+
: "";
|
|
417
|
+
const descriptionSource = explicitDescription || item.excerpt || item.content;
|
|
418
|
+
const description = truncate(stripHTML(descriptionSource), { length: 150 });
|
|
419
|
+
const openGraphDescription = stripHTML(descriptionSource)
|
|
420
|
+
.substring(0, 200)
|
|
421
|
+
.trim()
|
|
422
|
+
.replace(/\n/g, " ");
|
|
423
|
+
const explicitKeywords = normalizeStringList(frontMatter.keywords);
|
|
424
|
+
const siteKeywords = normalizeStringList(siteConfig.keywords);
|
|
425
|
+
const keywords = explicitKeywords.length > 0
|
|
426
|
+
? explicitKeywords
|
|
427
|
+
: item.tags.length > 0 ? item.tags.slice() : siteKeywords;
|
|
428
|
+
const cardCover = item.cover || "";
|
|
429
|
+
const bannerImage = item.presentation.banner?.image || "";
|
|
430
|
+
const defaultOgImage = siteConfig.avatar || (siteConfig.email ? gravatar(siteConfig.email) : "");
|
|
431
|
+
const openGraphConfig = seoConfig.openGraph;
|
|
432
|
+
let openGraph = null;
|
|
433
|
+
if (openGraphConfig.enabled === true) {
|
|
434
|
+
const pageOpenGraph = isPlainObject(frontMatter.seo?.openGraph) ? frontMatter.seo.openGraph : {};
|
|
435
|
+
const args = {
|
|
436
|
+
type: "article",
|
|
437
|
+
title: item.title,
|
|
438
|
+
url: item.route.permalink,
|
|
439
|
+
site_name: String(siteConfig.title || ""),
|
|
440
|
+
description: openGraphDescription,
|
|
441
|
+
language: normalizeLanguage(page.lang, page.language, frontMatter.lang, frontMatter.language, siteConfig.language),
|
|
442
|
+
author: String(siteConfig.author || ""),
|
|
443
|
+
date: false,
|
|
444
|
+
updated: false
|
|
445
|
+
};
|
|
446
|
+
if (openGraphConfig.twitterId) args.twitter_id = openGraphConfig.twitterId;
|
|
447
|
+
if (cardCover) args.twitter_card = "summary_large_image";
|
|
448
|
+
args.image = cardCover || bannerImage || firstContentImage(item.content) || defaultOgImage || null;
|
|
449
|
+
Object.assign(args, cloneValue(pageOpenGraph));
|
|
450
|
+
openGraph = {
|
|
451
|
+
args,
|
|
452
|
+
title: item.title,
|
|
453
|
+
siteName: String(siteConfig.title || ""),
|
|
454
|
+
twitterTitle: item.title,
|
|
455
|
+
publishedTime: pageOpenGraph.date === false || !item.date ? null : new Date(item.date).toISOString(),
|
|
456
|
+
modifiedTime: pageOpenGraph.updated === false || !item.updated ? null : new Date(item.updated).toISOString(),
|
|
457
|
+
tags: item.tags.slice().sort()
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let authorImage = siteConfig.avatar || (siteConfig.email ? gravatar(siteConfig.email) : "");
|
|
462
|
+
authorImage = absoluteSiteAsset(authorImage, siteConfig.url);
|
|
463
|
+
const author = {
|
|
464
|
+
"@type": "Person",
|
|
465
|
+
name: String(siteConfig.author || ""),
|
|
466
|
+
sameAs: cloneValue(seoConfig.structuredData.sameAs)
|
|
467
|
+
};
|
|
468
|
+
const publisher = { ...author, "@type": "Organization" };
|
|
469
|
+
if (authorImage) {
|
|
470
|
+
author.image = authorImage;
|
|
471
|
+
publisher.image = authorImage;
|
|
472
|
+
publisher.logo = { "@type": "ImageObject", url: authorImage };
|
|
473
|
+
}
|
|
474
|
+
const images = postImages({
|
|
475
|
+
cardCover,
|
|
476
|
+
bannerImage,
|
|
477
|
+
photos: Array.isArray(frontMatter.photos) ? frontMatter.photos : [],
|
|
478
|
+
content: item.content,
|
|
479
|
+
defaultCover: fallbacks.cover
|
|
480
|
+
});
|
|
481
|
+
const jsonLd = {
|
|
482
|
+
"@context": "https://schema.org",
|
|
483
|
+
"@type": "BlogPosting",
|
|
484
|
+
author,
|
|
485
|
+
dateCreated: item.date,
|
|
486
|
+
dateModified: item.updated,
|
|
487
|
+
datePublished: item.date,
|
|
488
|
+
description: postDescription({ excerpt: item.excerpt, content: item.content }),
|
|
489
|
+
headline: item.title,
|
|
490
|
+
mainEntityOfPage: { "@type": "WebPage", "@id": item.route.permalink },
|
|
491
|
+
publisher,
|
|
492
|
+
url: item.route.permalink,
|
|
493
|
+
image: images
|
|
494
|
+
};
|
|
495
|
+
if (item.tags.length > 0) jsonLd.keywords = item.tags.join(", ");
|
|
496
|
+
if (images.length > 0) jsonLd.thumbnailUrl = images[0];
|
|
497
|
+
|
|
498
|
+
const regionLayout = finalizedRegions(input, collection, item);
|
|
499
|
+
return {
|
|
500
|
+
document: {
|
|
501
|
+
language: normalizeLanguage(page.lang, page.language, frontMatter.lang, frontMatter.language, siteConfig.language),
|
|
502
|
+
headBeginInject: normalizeInject(frontMatter.inject?.headBegin),
|
|
503
|
+
headEndInject: normalizeInject(frontMatter.inject?.headEnd),
|
|
504
|
+
bodyBeginInject: normalizeInject(frontMatter.inject?.bodyBegin),
|
|
505
|
+
bodyEndInject: normalizeInject(frontMatter.inject?.bodyEnd),
|
|
506
|
+
preferredTheme: appearance.colorScheme === "auto"
|
|
507
|
+
? "auto"
|
|
508
|
+
: String(appearance.colorScheme || "")
|
|
509
|
+
},
|
|
510
|
+
layout: {
|
|
511
|
+
pageType: "content",
|
|
512
|
+
articleStyle,
|
|
513
|
+
indent: articleIndentEnabled(item.presentation.article),
|
|
514
|
+
siteBackground: Boolean(appearance.backgrounds.page.image),
|
|
515
|
+
blogPath: typeof siteConfig.index_generator?.path === "string" ? normalizeCollectionPath(siteConfig.index_generator.path) : "",
|
|
516
|
+
...renderRegionLayout(regionLayout),
|
|
517
|
+
breadcrumbs: normalizeCategoryLinks(page.categoryLinks)
|
|
518
|
+
},
|
|
519
|
+
seo: {
|
|
520
|
+
title: item.title ? `${item.title} - ${String(siteConfig.title || "")}` : String(siteConfig.title || ""),
|
|
521
|
+
description,
|
|
522
|
+
keywords,
|
|
523
|
+
robots: input.isBackup === true
|
|
524
|
+
? "noindex, nofollow"
|
|
525
|
+
: typeof frontMatter.robots === "string" && frontMatter.robots.length > 0 ? frontMatter.robots : null,
|
|
526
|
+
canonical: canonicalUrl(canonicalConfig.host, item.route.path),
|
|
527
|
+
openGraph,
|
|
528
|
+
jsonLd
|
|
529
|
+
},
|
|
530
|
+
article: buildPostArticleRender(input, item),
|
|
531
|
+
listing: buildPostListingRender(input, collection, item)
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function wikiTitle(itemTitle, collectionName, siteTitle, language) {
|
|
536
|
+
const title = String(itemTitle || "");
|
|
537
|
+
const wiki = String(collectionName || "");
|
|
538
|
+
let stripped = title;
|
|
539
|
+
if (title.toLowerCase() === wiki.toLowerCase()) {
|
|
540
|
+
stripped = "";
|
|
541
|
+
} else if (wiki.length > 0) {
|
|
542
|
+
const flex = wiki.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/[\s-]+/g, "[\\s-]*");
|
|
543
|
+
const matched = title.match(new RegExp(`^${flex}[\\s-]*[::\\-](.*)$`, "i"));
|
|
544
|
+
if (matched?.[1]) stripped = matched[1].trim();
|
|
545
|
+
}
|
|
546
|
+
const colon = String(language || "").toLowerCase().startsWith("zh") ? ":" : ": ";
|
|
547
|
+
const subject = stripped ? `${wiki}${colon}${stripped}` : wiki;
|
|
548
|
+
return subject ? `${subject} - ${String(siteTitle || "")}` : String(siteTitle || "");
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function buildCommentsRender(stellarConfig, item) {
|
|
552
|
+
return resolveCommentsModel(stellarConfig, item.presentation.comments, item.title);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function wikiPageLink(value) {
|
|
556
|
+
if (!value || typeof value.path !== "string" || value.path.length === 0) return null;
|
|
557
|
+
return {
|
|
558
|
+
title: String(value.title || ""),
|
|
559
|
+
path: normalizeCollectionPath(value.path),
|
|
560
|
+
date: null
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function wikiReadNext(collection, item) {
|
|
565
|
+
const pages = collection.navigation.tree.flatMap(section => section.items || [])
|
|
566
|
+
.filter(page => Number.isFinite(page.pageNumber))
|
|
567
|
+
.sort((left, right) => left.pageNumber - right.pageNumber);
|
|
568
|
+
const current = pages.find(page => page.path === item.route.path);
|
|
569
|
+
if (!current) return { previous: null, next: null };
|
|
570
|
+
return {
|
|
571
|
+
previous: wikiPageLink(pages.find(page => page.pageNumber === current.pageNumber - 1)),
|
|
572
|
+
next: wikiPageLink(pages.find(page => page.pageNumber === current.pageNumber + 1))
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function buildWikiRelated(input) {
|
|
577
|
+
if (!Array.isArray(input.relatedCollections)) return [];
|
|
578
|
+
return input.relatedCollections.map(group => ({
|
|
579
|
+
name: String(group?.name || ""),
|
|
580
|
+
items: Array.isArray(group?.items) ? group.items.map(project => {
|
|
581
|
+
const { identity, route } = project;
|
|
582
|
+
return {
|
|
583
|
+
href: normalizeCollectionPath(route.homepage || route.path || ""),
|
|
584
|
+
title: String(identity?.name || project?.id || ""),
|
|
585
|
+
description: String(identity?.description || "")
|
|
586
|
+
};
|
|
587
|
+
}).filter(project => project.href.length > 0 && project.title.length > 0) : []
|
|
588
|
+
})).filter(group => group.name.length > 0 && group.items.length > 0);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function buildWikiListingRender(input, collection) {
|
|
592
|
+
const repository = typeof collection.source.repository === "string" ? collection.source.repository : "";
|
|
593
|
+
const githubApi = input.stellarConfig.services.github.apiUrl;
|
|
594
|
+
return {
|
|
595
|
+
id: collection.id,
|
|
596
|
+
href: collection.route.homepage,
|
|
597
|
+
name: collection.identity.name,
|
|
598
|
+
headline: collection.identity.headline,
|
|
599
|
+
caption: collection.identity.tagline || collection.identity.description,
|
|
600
|
+
description: collection.identity.description,
|
|
601
|
+
tags: normalizeTerms(input.collectionConfig.tags),
|
|
602
|
+
audience: collection.identity.audience,
|
|
603
|
+
icon: collection.identity.icon,
|
|
604
|
+
cover: collection.cover || "",
|
|
605
|
+
repository,
|
|
606
|
+
repositoryApi: repository ? `${githubApi}/repos/${repository}` : "",
|
|
607
|
+
priority: collection.listing.priority,
|
|
608
|
+
order: collection.listing.order ?? 0,
|
|
609
|
+
listed: collection.visibility.listed !== false
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function buildWikiRenderModel(input, collection, item) {
|
|
614
|
+
const siteConfig = input.siteConfig;
|
|
615
|
+
const stellarConfig = input.stellarConfig;
|
|
616
|
+
const frontMatter = input.frontMatter;
|
|
617
|
+
const appearance = stellarConfig.appearance;
|
|
618
|
+
const seoConfig = stellarConfig;
|
|
619
|
+
const language = normalizeLanguage(
|
|
620
|
+
input.page.lang,
|
|
621
|
+
input.page.language,
|
|
622
|
+
frontMatter.lang,
|
|
623
|
+
frontMatter.language,
|
|
624
|
+
siteConfig.language
|
|
625
|
+
);
|
|
626
|
+
const articleStyle = typeof item.presentation.article?.style === "string"
|
|
627
|
+
? item.presentation.article.style
|
|
628
|
+
: null;
|
|
629
|
+
const explicitDescription = typeof frontMatter.description === "string" && frontMatter.description.length > 0
|
|
630
|
+
? frontMatter.description
|
|
631
|
+
: "";
|
|
632
|
+
const descriptionSource = explicitDescription || collection.identity.description || item.excerpt || item.content;
|
|
633
|
+
const description = truncate(stripHTML(descriptionSource), { length: 150 });
|
|
634
|
+
const keywords = normalizeStringList(frontMatter.keywords);
|
|
635
|
+
if (keywords.length === 0) keywords.push(...(item.tags.length > 0 ? item.tags : normalizeStringList(siteConfig.keywords)));
|
|
636
|
+
const cardCover = item.cover || "";
|
|
637
|
+
const bannerImage = item.presentation.banner?.image || "";
|
|
638
|
+
const openGraphConfig = seoConfig.openGraph;
|
|
639
|
+
let openGraph = null;
|
|
640
|
+
if (openGraphConfig.enabled === true) {
|
|
641
|
+
const pageOpenGraph = isPlainObject(frontMatter.seo?.openGraph) ? frontMatter.seo.openGraph : {};
|
|
642
|
+
const args = {
|
|
643
|
+
type: "website",
|
|
644
|
+
title: item.title || collection.identity.headline,
|
|
645
|
+
url: item.route.permalink,
|
|
646
|
+
site_name: String(siteConfig.title || ""),
|
|
647
|
+
description: stripHTML(descriptionSource).substring(0, 200).trim().replace(/\n/g, " "),
|
|
648
|
+
language,
|
|
649
|
+
author: String(siteConfig.author || ""),
|
|
650
|
+
date: false,
|
|
651
|
+
updated: false,
|
|
652
|
+
image: cardCover || bannerImage || firstContentImage(item.content) || siteConfig.avatar || null
|
|
653
|
+
};
|
|
654
|
+
if (openGraphConfig.twitterId) args.twitter_id = openGraphConfig.twitterId;
|
|
655
|
+
if (cardCover || bannerImage) args.twitter_card = "summary_large_image";
|
|
656
|
+
Object.assign(args, cloneValue(pageOpenGraph));
|
|
657
|
+
openGraph = {
|
|
658
|
+
args,
|
|
659
|
+
title: item.title || collection.identity.headline,
|
|
660
|
+
siteName: String(siteConfig.title || ""),
|
|
661
|
+
twitterTitle: item.title || collection.identity.headline,
|
|
662
|
+
publishedTime: null,
|
|
663
|
+
modifiedTime: null,
|
|
664
|
+
tags: []
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const articleDefaults = articleFooterDefaults(requireContentConfig(stellarConfig, input.themeSource));
|
|
669
|
+
const footer = item.presentation.footer || {};
|
|
670
|
+
const resolvedLicense = footer.license === true
|
|
671
|
+
? articleDefaults.license
|
|
672
|
+
: footer.license === false || footer.license == null ? "" : footer.license;
|
|
673
|
+
const configuredShare = footer.share === true
|
|
674
|
+
? articleDefaults.share
|
|
675
|
+
: Array.isArray(footer.share) ? footer.share : [];
|
|
676
|
+
const shareServices = filterShareServices(configuredShare);
|
|
677
|
+
const readNext = wikiReadNext(collection, item);
|
|
678
|
+
const isHomepage = collection.route.homepage === item.route.path;
|
|
679
|
+
const hero = collection.presentation.hero || {};
|
|
680
|
+
const repository = typeof collection.source.repository === "string" ? collection.source.repository : "";
|
|
681
|
+
const githubApi = stellarConfig.services.github.apiUrl;
|
|
682
|
+
const rawUrl = stellarConfig.services.github.rawUrl;
|
|
683
|
+
const banner = mergeConfig({}, item.presentation.banner || {});
|
|
684
|
+
if (banner.headline == null) banner.headline = item.title;
|
|
685
|
+
const readmeHtml = isHomepage ? wikiReadmeHtml(
|
|
686
|
+
{ source: collection.source, homepage: { path: item.route.path } },
|
|
687
|
+
{ path: item.route.path, content: item.content },
|
|
688
|
+
{ rawUrl }
|
|
689
|
+
) : "";
|
|
690
|
+
|
|
691
|
+
const regionLayout = finalizedRegions(input, collection, item);
|
|
692
|
+
return {
|
|
693
|
+
document: {
|
|
694
|
+
language,
|
|
695
|
+
headBeginInject: normalizeInject(frontMatter.inject?.headBegin),
|
|
696
|
+
headEndInject: normalizeInject(frontMatter.inject?.headEnd),
|
|
697
|
+
bodyBeginInject: normalizeInject(frontMatter.inject?.bodyBegin),
|
|
698
|
+
bodyEndInject: normalizeInject(frontMatter.inject?.bodyEnd),
|
|
699
|
+
preferredTheme: appearance.colorScheme === "auto" ? "auto" : String(appearance.colorScheme || "")
|
|
700
|
+
},
|
|
701
|
+
layout: {
|
|
702
|
+
pageType: "content",
|
|
703
|
+
articleStyle,
|
|
704
|
+
indent: articleIndentEnabled(item.presentation.article),
|
|
705
|
+
siteBackground: Boolean(appearance.backgrounds.page.image),
|
|
706
|
+
wikiIndexPath: profilePath(requireLayoutProfiles(stellarConfig).wikiIndex.path),
|
|
707
|
+
algoliaFilterPath: (() => {
|
|
708
|
+
const matched = `${item.route.path}/`.match(/(.*?)\/(.*?)\//i);
|
|
709
|
+
return matched?.[0] || "";
|
|
710
|
+
})(),
|
|
711
|
+
...renderRegionLayout(regionLayout),
|
|
712
|
+
breadcrumbs: [{
|
|
713
|
+
name: collection.identity.name,
|
|
714
|
+
path: collection.route.homepage
|
|
715
|
+
}]
|
|
716
|
+
},
|
|
717
|
+
seo: {
|
|
718
|
+
title: wikiTitle(item.title, collection.identity.name, siteConfig.title, language),
|
|
719
|
+
description,
|
|
720
|
+
keywords,
|
|
721
|
+
robots: input.isBackup === true
|
|
722
|
+
? "noindex, nofollow"
|
|
723
|
+
: typeof frontMatter.robots === "string" && frontMatter.robots.length > 0 ? frontMatter.robots : null,
|
|
724
|
+
canonical: canonicalUrl(seoConfig.canonical.host, item.route.path),
|
|
725
|
+
openGraph,
|
|
726
|
+
jsonLd: {
|
|
727
|
+
"@context": "https://schema.org",
|
|
728
|
+
"@type": "WebPage",
|
|
729
|
+
"@id": item.route.permalink,
|
|
730
|
+
name: item.title || collection.identity.headline,
|
|
731
|
+
description,
|
|
732
|
+
url: item.route.permalink,
|
|
733
|
+
isPartOf: {
|
|
734
|
+
"@type": "WebSite",
|
|
735
|
+
name: String(siteConfig.title || ""),
|
|
736
|
+
url: String(siteConfig.url || "")
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
cover: {
|
|
741
|
+
enabled: isHomepage && hero.enabled === true,
|
|
742
|
+
background: heroBackground(hero.background),
|
|
743
|
+
preview: cloneValue(hero.preview || {}),
|
|
744
|
+
actions: cloneValue(Array.isArray(hero.actions) ? hero.actions : []),
|
|
745
|
+
title: collection.identity.headline || collection.identity.name,
|
|
746
|
+
description: collection.identity.description || explicitDescription,
|
|
747
|
+
repository,
|
|
748
|
+
sourceUrl: repository ? `https://github.com/${repository}` : "",
|
|
749
|
+
releaseApi: repository ? `${githubApi}/repos/${repository}/tags` : "",
|
|
750
|
+
projectName: collection.identity.name || collection.id,
|
|
751
|
+
siteName: String(siteConfig.title || "")
|
|
752
|
+
},
|
|
753
|
+
article: {
|
|
754
|
+
heti: stellarConfig.features.heti.enabled === true,
|
|
755
|
+
banner,
|
|
756
|
+
updated: item.updated,
|
|
757
|
+
readmeHtml,
|
|
758
|
+
footer: {
|
|
759
|
+
references: Array.isArray(footer.references) ? cloneValue(footer.references) : [],
|
|
760
|
+
license: resolveLicense(String(resolvedLicense || ""), item, input.runtimeData),
|
|
761
|
+
share: shareServices.length > 0 ? {
|
|
762
|
+
services: shareServices,
|
|
763
|
+
permalink: item.route.permalink,
|
|
764
|
+
title: `${item.title} - ${String(siteConfig.title || "")}`,
|
|
765
|
+
image: cardCover,
|
|
766
|
+
summary: truncate(stripHTML(descriptionSource), { length: 120 })
|
|
767
|
+
} : null,
|
|
768
|
+
contributor: buildContributor(item, stellarConfig)
|
|
769
|
+
},
|
|
770
|
+
previous: readNext.previous,
|
|
771
|
+
next: readNext.next,
|
|
772
|
+
comments: buildCommentsRender(stellarConfig, item),
|
|
773
|
+
related: Array.isArray(input.related) ? cloneValue(input.related) : buildWikiRelated(input)
|
|
774
|
+
},
|
|
775
|
+
listing: isPlainObject(input.listing)
|
|
776
|
+
? cloneValue(input.listing)
|
|
777
|
+
: buildWikiListingRender(input, collection)
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function buildCollectionModel(stellarConfig) {
|
|
782
|
+
const profiles = requireLayoutProfiles(stellarConfig);
|
|
783
|
+
const postProfile = profiles.post;
|
|
784
|
+
const blogIndex = profiles.blogIndex;
|
|
785
|
+
const content = requireContentConfig(stellarConfig);
|
|
786
|
+
const article = content.article;
|
|
787
|
+
const comments = normalizeThemeComments(stellarConfig.comments);
|
|
788
|
+
const navigation = toRenderNavigation(postProfile);
|
|
789
|
+
const regions = toRenderRegions(stellarConfig, postProfile);
|
|
790
|
+
|
|
791
|
+
return {
|
|
792
|
+
id: "post",
|
|
793
|
+
profile: "post",
|
|
794
|
+
identity: normalizeBrand(stellarConfig.leftbar.brand),
|
|
795
|
+
source: {},
|
|
796
|
+
route: {
|
|
797
|
+
baseDir: profilePath(blogIndex.path)
|
|
798
|
+
},
|
|
799
|
+
navigation,
|
|
800
|
+
listing: {
|
|
801
|
+
pinStyle: article.listing.pinnedLayout,
|
|
802
|
+
cardStyle: article.listing.cardLayout,
|
|
803
|
+
excerptLength: article.listing.excerptLength
|
|
804
|
+
},
|
|
805
|
+
presentation: {
|
|
806
|
+
banner: {},
|
|
807
|
+
...regions,
|
|
808
|
+
article: articlePresentationDefaults(content),
|
|
809
|
+
footer: articleFooterDefaults(content),
|
|
810
|
+
comments
|
|
811
|
+
},
|
|
812
|
+
visibility: {
|
|
813
|
+
listed: true,
|
|
814
|
+
searchable: true
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function buildContentItemModel(page, frontMatter, collection, source, options = {}) {
|
|
820
|
+
const pageNavigation = toContentNavigation(frontMatter);
|
|
821
|
+
const pageRegions = pick(frontMatter, CONTENT_MODEL_FIELDS.regionIds);
|
|
822
|
+
const pageArticle = pick(frontMatter.article, CONTENT_MODEL_FIELDS.article);
|
|
823
|
+
const pageFooter = pick(frontMatter.footer, CONTENT_MODEL_FIELDS.footer);
|
|
824
|
+
const pageComments = pick(frontMatter.comments, CONTENT_MODEL_FIELDS.comments);
|
|
825
|
+
const pageVisibility = pick(frontMatter.visibility, CONTENT_MODEL_FIELDS.visibility);
|
|
826
|
+
return {
|
|
827
|
+
id: String(page._id || page.source || page.path || ""),
|
|
828
|
+
title: String(page.title || frontMatter.title || ""),
|
|
829
|
+
layout: String(page.layout || frontMatter.layout || "post"),
|
|
830
|
+
content: typeof page.content === "string" ? page.content : "",
|
|
831
|
+
excerpt: typeof page.excerpt === "string" ? page.excerpt : "",
|
|
832
|
+
date: normalizeDate(page.date ?? frontMatter.date),
|
|
833
|
+
updated: normalizeDate(page.updated ?? frontMatter.updated ?? page.date ?? frontMatter.date),
|
|
834
|
+
tags: normalizeTerms(page.tags ?? frontMatter.tags),
|
|
835
|
+
categories: normalizeTerms(page.categories ?? frontMatter.categories),
|
|
836
|
+
cover: typeof frontMatter.cover === "string" ? frontMatter.cover : "",
|
|
837
|
+
tagline: typeof frontMatter.tagline === "string" ? frontMatter.tagline : "",
|
|
838
|
+
source: {
|
|
839
|
+
file: String(page.source || source || ""),
|
|
840
|
+
...cloneValue(options.source || collection.source || {}),
|
|
841
|
+
...pick(frontMatter.source, CONTENT_MODEL_FIELDS.source)
|
|
842
|
+
},
|
|
843
|
+
route: {
|
|
844
|
+
path: typeof page.path === "string" ? normalizeCollectionPath(page.path) : "",
|
|
845
|
+
permalink: typeof page.permalink === "string" ? page.permalink : ""
|
|
846
|
+
},
|
|
847
|
+
navigation: mergeConfig(
|
|
848
|
+
pick(collection.navigation, CONTENT_MODEL_FIELDS.navigation),
|
|
849
|
+
pageNavigation
|
|
850
|
+
),
|
|
851
|
+
listing: {
|
|
852
|
+
priority: frontMatter.listing?.priority ?? 0
|
|
853
|
+
},
|
|
854
|
+
presentation: {
|
|
855
|
+
banner: mergeConfig(
|
|
856
|
+
collection.presentation.banner,
|
|
857
|
+
pick(frontMatter.banner, CONTENT_MODEL_FIELDS.banner)
|
|
858
|
+
),
|
|
859
|
+
...cascadeRegions([collection.presentation, pageRegions]),
|
|
860
|
+
article: mergeConfig(collection.presentation.article, pageArticle),
|
|
861
|
+
footer: mergeConfig(collection.presentation.footer, pageFooter),
|
|
862
|
+
comments: mergeConfig(collection.presentation.comments, pageComments)
|
|
863
|
+
},
|
|
864
|
+
visibility: mergeConfig(options.visibility || collection.visibility, pageVisibility)
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function normalizeCollectionIdentity(config) {
|
|
869
|
+
return {
|
|
870
|
+
name: String(config.name || ""),
|
|
871
|
+
headline: String(config.headline ?? config.name ?? ""),
|
|
872
|
+
tagline: String(config.tagline || ""),
|
|
873
|
+
description: String(config.description || ""),
|
|
874
|
+
audience: String(config.audience || ""),
|
|
875
|
+
icon: typeof config.icon === "string" ? config.icon : ""
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function normalizeWikiTree(sections) {
|
|
880
|
+
if (!Array.isArray(sections)) return [];
|
|
881
|
+
return sections.map(section => ({
|
|
882
|
+
title: typeof section?.title === "string" ? section.title : "",
|
|
883
|
+
items: Array.isArray(section?.pages) ? section.pages.map(page => ({
|
|
884
|
+
id: String(page?._id || page?.id || page?.path || ""),
|
|
885
|
+
title: String(page?.title || ""),
|
|
886
|
+
path: typeof page?.path === "string" ? normalizeCollectionPath(page.path) : "",
|
|
887
|
+
pageNumber: Number.isFinite(page?.page_number) ? page.page_number : null,
|
|
888
|
+
isHomepage: page?.is_homepage === true
|
|
889
|
+
})) : []
|
|
890
|
+
}));
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function buildWikiCollectionModel(input, collectionId) {
|
|
894
|
+
const collectionConfig = input.collectionConfig;
|
|
895
|
+
const collectionState = isPlainObject(input.collectionState) ? input.collectionState : {};
|
|
896
|
+
const profiles = requireLayoutProfiles(input.stellarConfig);
|
|
897
|
+
const wikiProfile = profiles.wiki;
|
|
898
|
+
const indexWiki = profiles.wikiIndex;
|
|
899
|
+
const content = requireContentConfig(input.stellarConfig, input.themeSource);
|
|
900
|
+
const collectionRoute = isPlainObject(collectionConfig.route) ? collectionConfig.route : {};
|
|
901
|
+
const collectionListing = isPlainObject(collectionConfig.listing) ? collectionConfig.listing : {};
|
|
902
|
+
const baseDir = collectionRoute.path || `${profilePath(indexWiki.path) || "wiki"}/${collectionId}`;
|
|
903
|
+
const identity = normalizeCollectionIdentity(collectionConfig);
|
|
904
|
+
|
|
905
|
+
const profileNavigation = toRenderNavigation(wikiProfile);
|
|
906
|
+
const collectionNavigation = toContentNavigation(collectionConfig);
|
|
907
|
+
const collectionRegions = pick(collectionConfig, CONTENT_MODEL_FIELDS.regionIds);
|
|
908
|
+
const defaultBrand = collectionBrand(identity, baseDir, {
|
|
909
|
+
indexPath: indexWiki.path,
|
|
910
|
+
backLabel: "btn.all_wiki"
|
|
911
|
+
});
|
|
912
|
+
assertCollectionBrandConfig(collectionConfig, "collection", input.collectionSource || "<collection>");
|
|
913
|
+
const regions = toRenderRegions(input.stellarConfig, wikiProfile, {
|
|
914
|
+
leftbar: { brand: defaultBrand }
|
|
915
|
+
}, {
|
|
916
|
+
brandSources: { collection: defaultBrand },
|
|
917
|
+
layers: [collectionRegions]
|
|
918
|
+
});
|
|
919
|
+
const globalArticle = articlePresentationDefaults(content);
|
|
920
|
+
const globalFooter = collectionFooterDefaults(content);
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
id: collectionId,
|
|
924
|
+
profile: "wiki",
|
|
925
|
+
identity,
|
|
926
|
+
cover: typeof collectionConfig.cover === "string" ? collectionConfig.cover : "",
|
|
927
|
+
source: pick(collectionConfig.source, CONTENT_MODEL_FIELDS.source),
|
|
928
|
+
route: {
|
|
929
|
+
baseDir: normalizeCollectionPath(baseDir),
|
|
930
|
+
homepage: typeof collectionState.homepage?.path === "string"
|
|
931
|
+
? normalizeCollectionPath(collectionState.homepage.path)
|
|
932
|
+
: ""
|
|
933
|
+
},
|
|
934
|
+
navigation: {
|
|
935
|
+
...mergeConfig(profileNavigation, collectionNavigation),
|
|
936
|
+
tree: normalizeWikiTree(collectionState.sections)
|
|
937
|
+
},
|
|
938
|
+
listing: {
|
|
939
|
+
priority: collectionListing.priority ?? 0,
|
|
940
|
+
order: collectionListing.order ?? 0,
|
|
941
|
+
excerptLength: collectionListing.excerptLength ?? null,
|
|
942
|
+
perPage: collectionListing.perPage ?? null
|
|
943
|
+
},
|
|
944
|
+
presentation: {
|
|
945
|
+
hero: cloneValue(collectionConfig.hero || {}),
|
|
946
|
+
banner: pick(collectionConfig.banner, CONTENT_MODEL_FIELDS.banner),
|
|
947
|
+
...regions,
|
|
948
|
+
article: mergeConfig(globalArticle, pick(collectionConfig.article, CONTENT_MODEL_FIELDS.article)),
|
|
949
|
+
footer: mergeConfig(globalFooter, pick(collectionConfig.footer, CONTENT_MODEL_FIELDS.footer)),
|
|
950
|
+
comments: mergeConfig(
|
|
951
|
+
normalizeThemeComments(input.stellarConfig.comments),
|
|
952
|
+
pick(collectionConfig.comments, CONTENT_MODEL_FIELDS.comments)
|
|
953
|
+
)
|
|
954
|
+
},
|
|
955
|
+
visibility: {
|
|
956
|
+
listed: input.collectionListed !== false && collectionConfig.visibility?.listed !== false,
|
|
957
|
+
searchable: collectionConfig.visibility?.searchable !== false
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function buildTopicSeries(collectionId, members, currentId, sort) {
|
|
963
|
+
const items = [];
|
|
964
|
+
for (const [index, member] of (Array.isArray(members) ? members : []).entries()) {
|
|
965
|
+
const config = isPlainObject(member?.frontMatter) ? member.frontMatter : {};
|
|
966
|
+
const page = member?.page || {};
|
|
967
|
+
if (config.collection?.profile !== "topic" || config.collection.id !== collectionId) continue;
|
|
968
|
+
if (config.visibility?.listed === false) continue;
|
|
969
|
+
items.push({
|
|
970
|
+
index,
|
|
971
|
+
id: String(page._id || page.source || page.path || ""),
|
|
972
|
+
title: String(page.title || config.title || ""),
|
|
973
|
+
path: typeof page.path === "string" ? normalizeCollectionPath(page.path) : "",
|
|
974
|
+
date: normalizeDate(page.date ?? config.date),
|
|
975
|
+
current: String(page._id || page.source || page.path || "") === currentId
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const field = sort?.field || "date";
|
|
980
|
+
const direction = sort?.direction === "asc" ? 1 : -1;
|
|
981
|
+
if (field === "date") {
|
|
982
|
+
items.sort((left, right) => {
|
|
983
|
+
const compared = String(left.date || "").localeCompare(String(right.date || "")) * direction;
|
|
984
|
+
return compared || left.index - right.index;
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
return items.map(({ index, ...item }) => item);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function buildTopicCollectionModel(input, collectionId, currentId) {
|
|
991
|
+
const siteConfig = input.siteConfig;
|
|
992
|
+
const collectionConfig = input.collectionConfig;
|
|
993
|
+
const profiles = requireLayoutProfiles(input.stellarConfig);
|
|
994
|
+
const topicProfile = profiles.topic;
|
|
995
|
+
const collectionRoute = isPlainObject(collectionConfig.route) ? collectionConfig.route : {};
|
|
996
|
+
const collectionListing = isPlainObject(collectionConfig.listing) ? collectionConfig.listing : {};
|
|
997
|
+
const content = requireContentConfig(input.stellarConfig, input.themeSource);
|
|
998
|
+
const baseDir = profilePath(topicProfile.path) || "topic";
|
|
999
|
+
const routePath = collectionRoute.path || `${baseDir}/${collectionId}`;
|
|
1000
|
+
const sort = collectionListing.sort ?? { field: "date", direction: "desc" };
|
|
1001
|
+
|
|
1002
|
+
const profileNavigation = toRenderNavigation(topicProfile);
|
|
1003
|
+
const collectionNavigation = toContentNavigation(collectionConfig);
|
|
1004
|
+
const collectionRegions = pick(collectionConfig, CONTENT_MODEL_FIELDS.regionIds);
|
|
1005
|
+
const availableBrand = collectionBrand(normalizeCollectionIdentity(collectionConfig), routePath, {
|
|
1006
|
+
indexPath: topicProfile.path,
|
|
1007
|
+
backLabel: "btn.all_topic"
|
|
1008
|
+
});
|
|
1009
|
+
assertCollectionBrandConfig(collectionConfig, "site", input.collectionSource || "<collection>");
|
|
1010
|
+
const regions = toRenderRegions(input.stellarConfig, topicProfile, null, {
|
|
1011
|
+
brandSources: { collection: availableBrand },
|
|
1012
|
+
layers: [collectionRegions]
|
|
1013
|
+
});
|
|
1014
|
+
const globalArticle = articlePresentationDefaults(content);
|
|
1015
|
+
const globalFooter = collectionFooterDefaults(content);
|
|
1016
|
+
|
|
1017
|
+
return {
|
|
1018
|
+
id: collectionId,
|
|
1019
|
+
profile: "topic",
|
|
1020
|
+
identity: normalizeCollectionIdentity(collectionConfig),
|
|
1021
|
+
cover: typeof collectionConfig.cover === "string" ? collectionConfig.cover : "",
|
|
1022
|
+
source: pick(collectionConfig.source, CONTENT_MODEL_FIELDS.source),
|
|
1023
|
+
route: {
|
|
1024
|
+
baseDir: normalizeCollectionPath(baseDir),
|
|
1025
|
+
path: normalizeCollectionPath(routePath),
|
|
1026
|
+
start: typeof collectionRoute.start === "string"
|
|
1027
|
+
? normalizeCollectionPath(collectionRoute.start)
|
|
1028
|
+
: ""
|
|
1029
|
+
},
|
|
1030
|
+
navigation: {
|
|
1031
|
+
...mergeConfig(profileNavigation, collectionNavigation),
|
|
1032
|
+
series: buildTopicSeries(collectionId, input.members, currentId, sort)
|
|
1033
|
+
},
|
|
1034
|
+
listing: {
|
|
1035
|
+
priority: collectionListing.priority ?? 0,
|
|
1036
|
+
order: collectionListing.order ?? null,
|
|
1037
|
+
cardStyle: content.article.listing.cardLayout,
|
|
1038
|
+
excerptLength: collectionListing.excerptLength ?? null,
|
|
1039
|
+
perPage: collectionListing.perPage ?? null,
|
|
1040
|
+
sort
|
|
1041
|
+
},
|
|
1042
|
+
presentation: {
|
|
1043
|
+
banner: pick(collectionConfig.banner, CONTENT_MODEL_FIELDS.banner),
|
|
1044
|
+
...regions,
|
|
1045
|
+
article: mergeConfig(
|
|
1046
|
+
globalArticle,
|
|
1047
|
+
pick(collectionConfig.article, CONTENT_MODEL_FIELDS.article)
|
|
1048
|
+
),
|
|
1049
|
+
footer: mergeConfig(globalFooter, pick(collectionConfig.footer, CONTENT_MODEL_FIELDS.footer)),
|
|
1050
|
+
comments: mergeConfig(
|
|
1051
|
+
normalizeThemeComments(input.stellarConfig.comments),
|
|
1052
|
+
pick(collectionConfig.comments, CONTENT_MODEL_FIELDS.comments)
|
|
1053
|
+
)
|
|
1054
|
+
},
|
|
1055
|
+
visibility: {
|
|
1056
|
+
listed: input.collectionListed !== false && collectionConfig.visibility?.listed !== false,
|
|
1057
|
+
searchable: collectionConfig.visibility?.searchable !== false
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function notebookBaseDir(collectionId, collectionConfig, stellarConfig) {
|
|
1063
|
+
if (typeof collectionConfig.route?.path === "string" && collectionConfig.route.path.length > 0) {
|
|
1064
|
+
return normalizeCollectionPath(collectionConfig.route.path);
|
|
1065
|
+
}
|
|
1066
|
+
const root = profilePath(requireLayoutProfiles(stellarConfig).notebooks.path);
|
|
1067
|
+
return normalizeCollectionPath([root, collectionId].filter(Boolean).join("/"));
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function buildNotebookTagNavigation(collectionId, baseDir, collectionItems) {
|
|
1071
|
+
const tags = new Map();
|
|
1072
|
+
const items = Array.isArray(collectionItems) ? collectionItems : [];
|
|
1073
|
+
for (const item of items) {
|
|
1074
|
+
if (item?.collection?.profile !== "notebook" || item.collection.id !== collectionId) continue;
|
|
1075
|
+
for (const hierarchy of normalizeTerms(item.tags)) {
|
|
1076
|
+
const parts = hierarchy.split("/").filter(Boolean);
|
|
1077
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
1078
|
+
const name = parts.slice(0, index + 1).join("/");
|
|
1079
|
+
const id = name.toLowerCase();
|
|
1080
|
+
if (tags.has(id)) continue;
|
|
1081
|
+
const parentName = parts.slice(0, index).join("/");
|
|
1082
|
+
tags.set(id, {
|
|
1083
|
+
id,
|
|
1084
|
+
name,
|
|
1085
|
+
label: parts[index],
|
|
1086
|
+
path: normalizeCollectionPath(`${baseDir}/tags/${id}`),
|
|
1087
|
+
parentId: parentName.length > 0 ? parentName.toLowerCase() : null
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
return Array.from(tags.values()).sort((left, right) => left.id.localeCompare(right.id));
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
function buildNotebookCollectionModel(input, collectionId) {
|
|
1096
|
+
const siteConfig = input.siteConfig;
|
|
1097
|
+
const collectionConfig = input.collectionConfig;
|
|
1098
|
+
const profiles = requireLayoutProfiles(input.stellarConfig);
|
|
1099
|
+
const content = requireContentConfig(input.stellarConfig, input.themeSource);
|
|
1100
|
+
const notebookDefaults = content.notebook;
|
|
1101
|
+
const collectionListing = isPlainObject(collectionConfig.listing) ? collectionConfig.listing : {};
|
|
1102
|
+
const defaultListing = notebookDefaults.listing;
|
|
1103
|
+
const baseDir = notebookBaseDir(collectionId, collectionConfig, input.stellarConfig);
|
|
1104
|
+
const identity = normalizeCollectionIdentity(collectionConfig);
|
|
1105
|
+
const profileNavigation = toRenderNavigation(profiles.notebook);
|
|
1106
|
+
const collectionNavigation = toContentNavigation(collectionConfig);
|
|
1107
|
+
const collectionRegions = pick(collectionConfig, CONTENT_MODEL_FIELDS.regionIds);
|
|
1108
|
+
const defaultBrand = collectionBrand(identity, baseDir, {
|
|
1109
|
+
indexPath: profiles.notebooks.path,
|
|
1110
|
+
backLabel: "btn.all_notebook"
|
|
1111
|
+
});
|
|
1112
|
+
assertCollectionBrandConfig(collectionConfig, "collection", input.collectionSource || "<collection>");
|
|
1113
|
+
const regions = toRenderRegions(input.stellarConfig, profiles.note, {
|
|
1114
|
+
leftbar: { brand: defaultBrand }
|
|
1115
|
+
}, {
|
|
1116
|
+
brandSources: { collection: defaultBrand },
|
|
1117
|
+
layers: [collectionRegions]
|
|
1118
|
+
});
|
|
1119
|
+
const globalArticle = articlePresentationDefaults(content);
|
|
1120
|
+
const globalFooter = collectionFooterDefaults(content);
|
|
1121
|
+
|
|
1122
|
+
return {
|
|
1123
|
+
id: collectionId,
|
|
1124
|
+
profile: "notebook",
|
|
1125
|
+
identity,
|
|
1126
|
+
cover: typeof collectionConfig.cover === "string" ? collectionConfig.cover : "",
|
|
1127
|
+
source: pick(collectionConfig.source, CONTENT_MODEL_FIELDS.source),
|
|
1128
|
+
route: { baseDir },
|
|
1129
|
+
navigation: {
|
|
1130
|
+
...mergeConfig(profileNavigation, collectionNavigation),
|
|
1131
|
+
tags: buildNotebookTagNavigation(collectionId, baseDir, input.collectionItems)
|
|
1132
|
+
},
|
|
1133
|
+
listing: {
|
|
1134
|
+
priority: collectionListing.priority ?? 0,
|
|
1135
|
+
order: collectionListing.order ?? 0,
|
|
1136
|
+
excerptLength: collectionListing.excerptLength ?? defaultListing.excerptLength,
|
|
1137
|
+
perPage: collectionListing.perPage ?? defaultListing.perPage ?? siteConfig.per_page ?? 10,
|
|
1138
|
+
sort: collectionListing.sort ?? defaultListing.sort
|
|
1139
|
+
},
|
|
1140
|
+
presentation: {
|
|
1141
|
+
banner: pick(collectionConfig.banner, CONTENT_MODEL_FIELDS.banner),
|
|
1142
|
+
...regions,
|
|
1143
|
+
article: mergeConfig(globalArticle, pick(collectionConfig.article, CONTENT_MODEL_FIELDS.article)),
|
|
1144
|
+
footer: mergeConfig(globalFooter, pick(collectionConfig.footer, CONTENT_MODEL_FIELDS.footer)),
|
|
1145
|
+
comments: mergeConfig(
|
|
1146
|
+
normalizeThemeComments(input.stellarConfig.comments),
|
|
1147
|
+
pick(collectionConfig.comments, CONTENT_MODEL_FIELDS.comments)
|
|
1148
|
+
)
|
|
1149
|
+
},
|
|
1150
|
+
visibility: {
|
|
1151
|
+
listed: collectionConfig.visibility?.listed !== false,
|
|
1152
|
+
searchable: collectionConfig.visibility?.searchable !== false
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function buildNotebookTagTree(collection, inputTags) {
|
|
1158
|
+
const tags = Array.isArray(inputTags) && inputTags.length > 0
|
|
1159
|
+
? inputTags
|
|
1160
|
+
: [{
|
|
1161
|
+
id: "",
|
|
1162
|
+
name: "",
|
|
1163
|
+
label: "",
|
|
1164
|
+
path: collection.route.baseDir,
|
|
1165
|
+
parentId: null
|
|
1166
|
+
}, ...collection.navigation.tags];
|
|
1167
|
+
const children = new Map();
|
|
1168
|
+
for (const tag of tags) {
|
|
1169
|
+
const parentId = typeof tag?.parentId === "string" ? tag.parentId : "";
|
|
1170
|
+
if (!children.has(parentId)) children.set(parentId, []);
|
|
1171
|
+
if (typeof tag?.id === "string" && tag.id.length > 0) children.get(parentId).push(tag.id);
|
|
1172
|
+
}
|
|
1173
|
+
return tags.map(tag => ({
|
|
1174
|
+
id: typeof tag?.id === "string" ? tag.id : "",
|
|
1175
|
+
name: typeof tag?.name === "string" ? tag.name : "",
|
|
1176
|
+
label: typeof tag?.label === "string"
|
|
1177
|
+
? tag.label
|
|
1178
|
+
: typeof tag?.part === "string" ? tag.part : "",
|
|
1179
|
+
path: typeof tag?.path === "string" ? normalizeCollectionPath(tag.path) : collection.route.baseDir,
|
|
1180
|
+
parentId: typeof tag?.parentId === "string"
|
|
1181
|
+
? tag.parentId
|
|
1182
|
+
: typeof tag?.parent === "string" && tag.parent.length > 0 ? tag.parent : null,
|
|
1183
|
+
children: Array.isArray(tag?.children) ? tag.children.slice() : (children.get(tag?.id || "") || []).slice()
|
|
1184
|
+
}));
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
function buildNotebookArticleTags(collection, item) {
|
|
1188
|
+
const navigation = new Map(collection.navigation.tags.map(tag => [tag.id, tag]));
|
|
1189
|
+
return item.tags.map(name => {
|
|
1190
|
+
const id = String(name).toLowerCase();
|
|
1191
|
+
const tag = navigation.get(id);
|
|
1192
|
+
return {
|
|
1193
|
+
name: String(name),
|
|
1194
|
+
path: tag?.path || normalizeCollectionPath(`${collection.route.baseDir}/tags/${id}`)
|
|
1195
|
+
};
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function buildNotebookRenderModel(input, collection, item) {
|
|
1200
|
+
const core = buildPostRenderModel(input, collection, item);
|
|
1201
|
+
const explicitDescription = typeof input.frontMatter.description === "string"
|
|
1202
|
+
? input.frontMatter.description
|
|
1203
|
+
: "";
|
|
1204
|
+
const excerpt = item.excerpt
|
|
1205
|
+
? stripHTML(item.excerpt)
|
|
1206
|
+
: explicitDescription || (collection.listing.excerptLength > 0
|
|
1207
|
+
? truncate(stripHTML(item.content), { length: collection.listing.excerptLength })
|
|
1208
|
+
: "");
|
|
1209
|
+
const collectionTitle = collection.identity.headline || collection.identity.name || collection.id;
|
|
1210
|
+
const openGraph = core.seo.openGraph == null ? null : {
|
|
1211
|
+
...core.seo.openGraph,
|
|
1212
|
+
args: {
|
|
1213
|
+
...core.seo.openGraph.args,
|
|
1214
|
+
type: "website"
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1218
|
+
return {
|
|
1219
|
+
document: core.document,
|
|
1220
|
+
layout: {
|
|
1221
|
+
pageType: core.layout.pageType,
|
|
1222
|
+
articleStyle: core.layout.articleStyle,
|
|
1223
|
+
indent: core.layout.indent,
|
|
1224
|
+
siteBackground: core.layout.siteBackground,
|
|
1225
|
+
notebookIndexPath: profilePath(requireLayoutProfiles(input.stellarConfig).notebooks.path),
|
|
1226
|
+
notebookPath: collection.route.baseDir,
|
|
1227
|
+
algoliaFilterPath: collection.route.baseDir,
|
|
1228
|
+
topbar: core.layout.topbar,
|
|
1229
|
+
leftbar: core.layout.leftbar,
|
|
1230
|
+
rightbar: core.layout.rightbar,
|
|
1231
|
+
regionWarnings: core.layout.regionWarnings,
|
|
1232
|
+
breadcrumbs: [{ name: collectionTitle, path: collection.route.baseDir }],
|
|
1233
|
+
tagTree: buildNotebookTagTree(collection, input.tagTree),
|
|
1234
|
+
recentItems: cloneValue(Array.isArray(input.recentItems) ? input.recentItems : [])
|
|
1235
|
+
},
|
|
1236
|
+
seo: {
|
|
1237
|
+
...core.seo,
|
|
1238
|
+
openGraph,
|
|
1239
|
+
jsonLd: {
|
|
1240
|
+
"@context": "https://schema.org",
|
|
1241
|
+
"@type": "WebPage",
|
|
1242
|
+
"@id": item.route.permalink,
|
|
1243
|
+
name: item.title,
|
|
1244
|
+
description: core.seo.description,
|
|
1245
|
+
url: item.route.permalink,
|
|
1246
|
+
isPartOf: {
|
|
1247
|
+
"@type": "WebSite",
|
|
1248
|
+
name: String(input.siteConfig.title || ""),
|
|
1249
|
+
url: String(input.siteConfig.url || "")
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
},
|
|
1253
|
+
article: {
|
|
1254
|
+
heti: core.article.heti,
|
|
1255
|
+
banner: cloneValue(item.presentation.banner || {}),
|
|
1256
|
+
created: item.date,
|
|
1257
|
+
updated: item.updated,
|
|
1258
|
+
tags: item.presentation.footer?.showTags === true
|
|
1259
|
+
? buildNotebookArticleTags(collection, item)
|
|
1260
|
+
: [],
|
|
1261
|
+
footer: {
|
|
1262
|
+
...core.article.footer
|
|
1263
|
+
},
|
|
1264
|
+
comments: core.article.comments
|
|
1265
|
+
},
|
|
1266
|
+
listing: {
|
|
1267
|
+
id: item.id,
|
|
1268
|
+
collectionId: collection.id,
|
|
1269
|
+
collectionName: collection.identity.name,
|
|
1270
|
+
href: typeof input.page.link === "string" && input.page.link.length > 0
|
|
1271
|
+
? input.page.link
|
|
1272
|
+
: item.route.path,
|
|
1273
|
+
title: item.title,
|
|
1274
|
+
cover: item.cover || "",
|
|
1275
|
+
excerpt,
|
|
1276
|
+
tags: item.tags.slice(),
|
|
1277
|
+
date: item.date,
|
|
1278
|
+
updated: item.updated,
|
|
1279
|
+
priority: item.listing.priority,
|
|
1280
|
+
listed: item.visibility.listed !== false
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function buildPostPageViewModel(input) {
|
|
1286
|
+
const source = input.source || "<page>";
|
|
1287
|
+
const themeSource = input.themeSource || "<theme>";
|
|
1288
|
+
const siteConfig = isPlainObject(input.siteConfig) ? input.siteConfig : {};
|
|
1289
|
+
const runtimeData = isPlainObject(input.runtimeData) ? input.runtimeData : {};
|
|
1290
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1291
|
+
const page = input.page || {};
|
|
1292
|
+
|
|
1293
|
+
assertNormalizedConfig(input.stellarConfig, themeSource, [
|
|
1294
|
+
{
|
|
1295
|
+
path: "stellarConfig.canonical",
|
|
1296
|
+
read: config => config?.canonical,
|
|
1297
|
+
expected: "normalized canonical object",
|
|
1298
|
+
migration: null
|
|
1299
|
+
},
|
|
1300
|
+
layoutConfigRequirement()
|
|
1301
|
+
]);
|
|
1302
|
+
|
|
1303
|
+
const collection = buildCollectionModel(input.stellarConfig);
|
|
1304
|
+
const item = buildContentItemModel(page, frontMatter, collection, source);
|
|
1305
|
+
const render = buildPostRenderModel({ ...input, siteConfig, runtimeData, frontMatter, page }, collection, item);
|
|
1306
|
+
return deepFreeze(assertPageViewModel("post", { collection, item, render }));
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function buildWikiPageViewModelBase(input) {
|
|
1310
|
+
const source = input.source || "<page>";
|
|
1311
|
+
const themeSource = input.themeSource || "<theme>";
|
|
1312
|
+
const collectionSource = input.collectionSource || "<collection>";
|
|
1313
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1314
|
+
const page = input.page || {};
|
|
1315
|
+
|
|
1316
|
+
assertNormalizedConfig(input.stellarConfig, themeSource, [
|
|
1317
|
+
{
|
|
1318
|
+
path: "stellarConfig.canonical",
|
|
1319
|
+
read: config => config?.canonical,
|
|
1320
|
+
expected: "normalized canonical object",
|
|
1321
|
+
migration: null
|
|
1322
|
+
},
|
|
1323
|
+
layoutConfigRequirement()
|
|
1324
|
+
]);
|
|
1325
|
+
|
|
1326
|
+
if (!isPlainObject(input.collectionConfig)) {
|
|
1327
|
+
const collectionId = input.collectionId || frontMatter.collection?.id || "<unknown>";
|
|
1328
|
+
throw new ContentConfigError([
|
|
1329
|
+
`${source}: collection.id ${collectionId} 未找到 Wiki 项目配置 ${collectionSource}`
|
|
1330
|
+
]);
|
|
1331
|
+
}
|
|
1332
|
+
const collectionConfig = input.collectionConfig;
|
|
1333
|
+
|
|
1334
|
+
const collectionId = input.collectionId || frontMatter.collection?.id;
|
|
1335
|
+
if (frontMatter.collection?.profile !== "wiki") {
|
|
1336
|
+
throw new ContentConfigError([`${source}: collection.profile 必须是 wiki`]);
|
|
1337
|
+
}
|
|
1338
|
+
if (collectionId !== frontMatter.collection.id) {
|
|
1339
|
+
throw new ContentConfigError([
|
|
1340
|
+
`${source}: collection.id ${frontMatter.collection.id} 与 Wiki 项目 ${collectionId} 不匹配`
|
|
1341
|
+
]);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const collection = buildWikiCollectionModel({ ...input, collectionConfig }, collectionId);
|
|
1345
|
+
const item = buildContentItemModel(page, frontMatter, collection, source, {
|
|
1346
|
+
source: collection.source,
|
|
1347
|
+
visibility: mergeConfig(
|
|
1348
|
+
{ listed: true, searchable: true },
|
|
1349
|
+
input.collectionConfig?.visibility
|
|
1350
|
+
)
|
|
1351
|
+
});
|
|
1352
|
+
return { collection, item };
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
function completeWikiPageViewModel(input, base) {
|
|
1356
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1357
|
+
const page = input.page || {};
|
|
1358
|
+
const collection = base.collection;
|
|
1359
|
+
const item = base.item;
|
|
1360
|
+
const render = buildWikiRenderModel({
|
|
1361
|
+
...input,
|
|
1362
|
+
siteConfig: isPlainObject(input.siteConfig) ? input.siteConfig : {},
|
|
1363
|
+
runtimeData: isPlainObject(input.runtimeData) ? input.runtimeData : {},
|
|
1364
|
+
frontMatter,
|
|
1365
|
+
page
|
|
1366
|
+
}, collection, item);
|
|
1367
|
+
return deepFreeze(assertPageViewModel("wiki", { collection, item, render }));
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
function buildWikiPageViewModel(input) {
|
|
1371
|
+
return completeWikiPageViewModel(input, buildWikiPageViewModelBase(input));
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function buildTopicPageViewModelBase(input) {
|
|
1375
|
+
const source = input.source || "<page>";
|
|
1376
|
+
const themeSource = input.themeSource || "<theme>";
|
|
1377
|
+
const siteConfig = isPlainObject(input.siteConfig) ? input.siteConfig : {};
|
|
1378
|
+
const collectionConfig = input.collectionConfig;
|
|
1379
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1380
|
+
const page = input.page || {};
|
|
1381
|
+
const collectionId = input.collectionId || frontMatter.collection?.id;
|
|
1382
|
+
|
|
1383
|
+
assertNormalizedConfig(input.stellarConfig, themeSource, [
|
|
1384
|
+
{
|
|
1385
|
+
path: "stellarConfig.canonical",
|
|
1386
|
+
read: config => config?.canonical,
|
|
1387
|
+
expected: "normalized canonical object",
|
|
1388
|
+
migration: null
|
|
1389
|
+
},
|
|
1390
|
+
layoutConfigRequirement()
|
|
1391
|
+
]);
|
|
1392
|
+
|
|
1393
|
+
if (!isPlainObject(collectionConfig)) {
|
|
1394
|
+
throw new ContentConfigError([`${source}: collection.id 无法解析 Topic ${collectionId || "<unknown>"}`]);
|
|
1395
|
+
}
|
|
1396
|
+
if (frontMatter.collection?.profile !== "topic") {
|
|
1397
|
+
throw new ContentConfigError([`${source}: collection.profile 必须是 topic`]);
|
|
1398
|
+
}
|
|
1399
|
+
if (collectionId !== frontMatter.collection.id) {
|
|
1400
|
+
throw new ContentConfigError([
|
|
1401
|
+
`${source}: collection.id ${frontMatter.collection.id} 与 Topic ${collectionId} 不匹配`
|
|
1402
|
+
]);
|
|
1403
|
+
}
|
|
1404
|
+
const currentId = String(page._id || page.source || page.path || "");
|
|
1405
|
+
const collection = buildTopicCollectionModel({
|
|
1406
|
+
...input,
|
|
1407
|
+
siteConfig,
|
|
1408
|
+
collectionConfig
|
|
1409
|
+
}, collectionId, currentId);
|
|
1410
|
+
return deepFreeze({ collection });
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function completeTopicPageViewModel(input, base) {
|
|
1414
|
+
const source = input.source || "<page>";
|
|
1415
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1416
|
+
const collection = base.collection;
|
|
1417
|
+
const item = buildContentItemModel(input.page || {}, frontMatter, collection, source, {
|
|
1418
|
+
source: collection.source,
|
|
1419
|
+
visibility: mergeConfig(
|
|
1420
|
+
{ listed: true, searchable: true },
|
|
1421
|
+
input.collectionConfig?.visibility
|
|
1422
|
+
)
|
|
1423
|
+
});
|
|
1424
|
+
const render = buildPostRenderModel({
|
|
1425
|
+
...input,
|
|
1426
|
+
siteConfig: isPlainObject(input.siteConfig) ? input.siteConfig : {},
|
|
1427
|
+
runtimeData: isPlainObject(input.runtimeData) ? input.runtimeData : {},
|
|
1428
|
+
frontMatter: isPlainObject(input.frontMatter) ? input.frontMatter : {},
|
|
1429
|
+
page: input.page || {}
|
|
1430
|
+
}, collection, item);
|
|
1431
|
+
render.layout.blogPath = collection.route.baseDir;
|
|
1432
|
+
render.layout.breadcrumbs = [{
|
|
1433
|
+
name: collection.identity.headline || collection.identity.name || collection.id,
|
|
1434
|
+
path: collection.navigation.series[0]?.path || collection.route.path
|
|
1435
|
+
}];
|
|
1436
|
+
render.article.banner = cloneValue(item.presentation.banner || {});
|
|
1437
|
+
return deepFreeze(assertPageViewModel("topic", { collection, item, render }));
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function buildTopicPageViewModel(input) {
|
|
1441
|
+
return completeTopicPageViewModel(input, buildTopicPageViewModelBase(input));
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
function buildTopicIndexRender(input) {
|
|
1445
|
+
const collectionId = input.collectionId;
|
|
1446
|
+
const collection = buildTopicCollectionModel(input, collectionId, "");
|
|
1447
|
+
const pages = collection.navigation.series.map(item => ({
|
|
1448
|
+
title: item.title,
|
|
1449
|
+
path: item.path,
|
|
1450
|
+
date: item.date
|
|
1451
|
+
}));
|
|
1452
|
+
const latest = pages[0] || null;
|
|
1453
|
+
const cover = collection.cover || "";
|
|
1454
|
+
return deepFreeze({
|
|
1455
|
+
id: collection.id,
|
|
1456
|
+
name: collection.identity.name,
|
|
1457
|
+
headline: collection.identity.headline,
|
|
1458
|
+
description: collection.identity.description,
|
|
1459
|
+
cover: typeof cover === "string" ? cover : "",
|
|
1460
|
+
href: latest?.path || "/",
|
|
1461
|
+
latest,
|
|
1462
|
+
items: pages.slice(1),
|
|
1463
|
+
sortDate: latest?.date || null,
|
|
1464
|
+
listed: collection.visibility.listed !== false
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
function buildNotebookPageViewModelBase(input) {
|
|
1469
|
+
const source = input.source || "<page>";
|
|
1470
|
+
const themeSource = input.themeSource || "<theme>";
|
|
1471
|
+
const siteConfig = isPlainObject(input.siteConfig) ? input.siteConfig : {};
|
|
1472
|
+
const collectionConfig = input.collectionConfig;
|
|
1473
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1474
|
+
const page = input.page || {};
|
|
1475
|
+
const collectionId = input.collectionId;
|
|
1476
|
+
|
|
1477
|
+
assertNormalizedConfig(input.stellarConfig, themeSource, [layoutConfigRequirement()]);
|
|
1478
|
+
|
|
1479
|
+
if (!isPlainObject(collectionConfig)) {
|
|
1480
|
+
throw new ContentConfigError([`${source}: 未找到 Notebook collection ${collectionId || "<unknown>"}`]);
|
|
1481
|
+
}
|
|
1482
|
+
if (frontMatter.collection?.profile !== "notebook") {
|
|
1483
|
+
throw new ContentConfigError([`${source}: Note 必须显式声明 collection.profile: notebook`]);
|
|
1484
|
+
}
|
|
1485
|
+
if (typeof collectionId !== "string" || collectionId.length === 0) {
|
|
1486
|
+
throw new ContentConfigError([`${source}: Notebook collection id 必须是非空字符串`]);
|
|
1487
|
+
}
|
|
1488
|
+
if (frontMatter.collection.id !== collectionId) {
|
|
1489
|
+
throw new ContentConfigError([
|
|
1490
|
+
`${source}: collection.id ${frontMatter.collection.id} 与 Notebook ${collectionId} 不匹配`
|
|
1491
|
+
]);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
const collection = input.collectionModel || buildNotebookCollectionModel({
|
|
1495
|
+
...input,
|
|
1496
|
+
siteConfig,
|
|
1497
|
+
collectionConfig
|
|
1498
|
+
}, collectionId);
|
|
1499
|
+
return deepFreeze({ collection });
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
function completeNotebookPageViewModel(input, base) {
|
|
1503
|
+
const source = input.source || "<page>";
|
|
1504
|
+
const frontMatter = isPlainObject(input.frontMatter) ? input.frontMatter : {};
|
|
1505
|
+
const collection = base.collection;
|
|
1506
|
+
const item = buildContentItemModel(input.page || {}, frontMatter, collection, source, {
|
|
1507
|
+
source: collection.source,
|
|
1508
|
+
visibility: mergeConfig(
|
|
1509
|
+
{ listed: true, searchable: true },
|
|
1510
|
+
input.collectionConfig?.visibility
|
|
1511
|
+
)
|
|
1512
|
+
});
|
|
1513
|
+
const render = buildNotebookRenderModel({
|
|
1514
|
+
...input,
|
|
1515
|
+
siteConfig: isPlainObject(input.siteConfig) ? input.siteConfig : {},
|
|
1516
|
+
runtimeData: isPlainObject(input.runtimeData) ? input.runtimeData : {},
|
|
1517
|
+
frontMatter,
|
|
1518
|
+
page: input.page || {}
|
|
1519
|
+
}, collection, item);
|
|
1520
|
+
return deepFreeze(assertPageViewModel("notebook", { collection, item, render }));
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function buildNotebookPageViewModel(input) {
|
|
1524
|
+
return completeNotebookPageViewModel(input, buildNotebookPageViewModelBase(input));
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
module.exports = {
|
|
1528
|
+
buildNotebookCollectionModel,
|
|
1529
|
+
buildNotebookPageViewModelBase,
|
|
1530
|
+
buildNotebookPageViewModel,
|
|
1531
|
+
buildPostPageViewModel,
|
|
1532
|
+
buildTopicIndexRender,
|
|
1533
|
+
buildTopicPageViewModel,
|
|
1534
|
+
buildTopicPageViewModelBase,
|
|
1535
|
+
buildWikiListingRender,
|
|
1536
|
+
buildWikiPageViewModel,
|
|
1537
|
+
buildWikiPageViewModelBase,
|
|
1538
|
+
buildWikiRelated,
|
|
1539
|
+
completeNotebookPageViewModel,
|
|
1540
|
+
completeTopicPageViewModel,
|
|
1541
|
+
completeWikiPageViewModel
|
|
1542
|
+
};
|