From 1c86d21f6437132bdb5b6a256b9a10ff96a1f19b Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Mon, 13 Jul 2026 11:58:46 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .circleci/config.yml | 115 + .dockerignore | 9 + .gemini/config.yaml | 13 + .gitattributes | 8 + .github/ISSUE_TEMPLATE/bug_report.md | 23 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.md | 11 + .github/SUPPORT.md | 3 + .github/dependabot.yml | 7 + .github/workflows/image.yml | 49 + .github/workflows/stale.yml | 52 + .github/workflows/test.yml | 140 + .gitignore | 8 + .gitmodules | 0 .mailmap | 3 + .prettierignore | 1 + AGENTS.md | 22 + CLAUDE.md | 1 + CONTRIBUTING.md | 212 + Dockerfile | 101 + LICENSE | 201 + README.md | 310 + README.wehub.md | 7 + SECURITY.md | 11 + bufferpool/bufpool.go | 38 + bufferpool/bufpool_test.go | 31 + cache/docs.go | 2 + cache/dynacache/dynacache.go | 647 ++ cache/dynacache/dynacache_test.go | 230 + cache/filecache/filecache.go | 570 + cache/filecache/filecache_config.go | 317 + cache/filecache/filecache_config_test.go | 171 + cache/filecache/filecache_integration_test.go | 105 + cache/filecache/filecache_pruner.go | 172 + cache/filecache/filecache_pruner_test.go | 115 + cache/filecache/filecache_test.go | 285 + cache/httpcache/httpcache.go | 240 + cache/httpcache/httpcache_integration_test.go | 95 + cache/httpcache/httpcache_test.go | 73 + check.sh | 82 + check_gofmt.sh | 2 + codegen/methods.go | 538 + codegen/methods2_test.go | 20 + codegen/methods_test.go | 96 + commands/commandeer.go | 711 ++ commands/commands.go | 73 + commands/config.go | 240 + commands/convert.go | 315 + commands/deploy.go | 51 + commands/deploy_flags.go | 33 + commands/deploy_off.go | 50 + commands/env.go | 70 + commands/gen.go | 395 + commands/helpers.go | 121 + commands/hugo_windows.go | 33 + commands/hugobuilder.go | 1197 ++ commands/import.go | 625 ++ commands/list.go | 213 + commands/mod.go | 354 + commands/new.go | 230 + commands/release.go | 55 + commands/server.go | 1261 +++ common/collections/append.go | 140 + common/collections/append_test.go | 149 + common/collections/collections.go | 21 + common/collections/order.go | 20 + common/collections/slice.go | 95 + common/collections/slice_test.go | 172 + common/collections/stack.go | 135 + common/collections/stack_test.go | 77 + common/constants/constants.go | 44 + common/docs.go | 2 + common/hashing/hashing.go | 225 + common/hashing/hashing_test.go | 152 + common/hdebug/debug.go | 61 + common/herrors/error_locator.go | 170 + common/herrors/error_locator_test.go | 152 + common/herrors/errors.go | 180 + common/herrors/errors_test.go | 45 + common/herrors/file_error.go | 449 + common/herrors/file_error_test.go | 80 + common/herrors/line_number_extractors.go | 73 + common/herrors/line_number_extractors_test.go | 31 + common/hexec/esmloader.go | 30 + common/hexec/esmloader.mjs | 61 + common/hexec/exec.go | 557 + common/hexec/exec_integration_test.go | 74 + common/hexec/exec_test.go | 474 + common/himage/image.go | 84 + common/hiter/iter.go | 66 + common/hmaps/cache.go | 218 + common/hmaps/cache_test.go | 57 + common/hmaps/maps.go | 238 + common/hmaps/maps_test.go | 201 + common/hmaps/ordered.go | 144 + common/hmaps/ordered_test.go | 99 + common/hmaps/orderedintset.go | 154 + common/hmaps/orderedintset_test.go | 117 + common/hmaps/params.go | 481 + common/hmaps/params_test.go | 201 + common/hreflect/convert.go | 222 + common/hreflect/convert_test.go | 284 + common/hreflect/helpers.go | 377 + common/hreflect/helpers_test.go | 260 + common/hstore/scratch.go | 160 + common/hstore/scratch_test.go | 217 + common/hstrings/strings.go | 179 + common/hstrings/strings_test.go | 144 + common/hsync/oncemore.go | 162 + common/hsync/oncemore_test.go | 80 + common/htime/htime_integration_test.go | 78 + common/htime/time.go | 177 + common/htime/time_test.go | 145 + common/hugio/copy.go | 93 + common/hugio/hasBytesWriter.go | 103 + common/hugio/hasBytesWriter_test.go | 125 + common/hugio/readers.go | 147 + common/hugio/writers.go | 113 + common/hugo/hugo.go | 417 + common/hugo/hugo_integration_test.go | 113 + common/hugo/hugo_test.go | 61 + common/hugo/vars_extended.go | 18 + common/hugo/vars_regular.go | 18 + common/hugo/vars_withdeploy.go | 18 + common/hugo/vars_withdeploy_off.go | 18 + common/hugo/version_current.go | 25 + common/loggers/handlerdefault.go | 106 + common/loggers/handlersmisc.go | 145 + common/loggers/handlerterminal.go | 100 + common/loggers/handlerterminal_test.go | 40 + common/loggers/logger.go | 398 + common/loggers/logger_test.go | 154 + common/loggers/loggerglobal.go | 62 + common/math/math.go | 133 + common/math/math_test.go | 111 + common/para/para.go | 73 + common/para/para_test.go | 97 + common/paths/path.go | 348 + common/paths/path_test.go | 271 + common/paths/pathparser.go | 1093 ++ common/paths/pathparser_test.go | 917 ++ common/paths/paths_integration_test.go | 169 + .../FuzzParsePathContent/62fadd5d4f836c18 | 2 + common/paths/type_string.go | 32 + common/paths/url.go | 215 + common/paths/url_test.go | 66 + common/predicate/predicate.go | 301 + common/predicate/predicate_test.go | 282 + common/predicate/rangeop_test.go | 52 + common/rungroup/rungroup.go | 93 + common/rungroup/rungroup_test.go | 44 + common/tasks/tasks.go | 150 + common/terminal/colors.go | 99 + common/text/position.go | 100 + common/text/position_test.go | 32 + common/text/transform.go | 78 + common/text/transform_test.go | 72 + common/types/closer.go | 54 + common/types/convert.go | 129 + common/types/convert_test.go | 48 + common/types/css/csstypes.go | 20 + common/types/evictingqueue.go | 114 + common/types/evictingqueue_test.go | 72 + common/types/hstring/stringtypes.go | 36 + common/types/hstring/stringtypes_test.go | 30 + common/types/types.go | 158 + common/types/types_test.go | 51 + common/urls/baseURL.go | 112 + common/urls/baseURL_test.go | 81 + common/urls/ref.go | 22 + common/version/version.go | 289 + common/version/version_test.go | 103 + compare/compare.go | 67 + compare/compare_strings.go | 113 + compare/compare_strings_test.go | 83 + compare/compare_test.go | 14 + config/allconfig/allconfig.go | 1281 +++ .../allconfig/allconfig_integration_test.go | 406 + config/allconfig/alldecoders.go | 513 + config/allconfig/configlanguage.go | 311 + config/allconfig/docshelper.go | 48 + config/allconfig/load.go | 575 + config/allconfig/load_test.go | 71 + config/commonConfig.go | 511 + config/commonConfig_test.go | 197 + config/configLoader.go | 254 + config/configLoader_test.go | 45 + config/configProvider.go | 123 + config/configProvider_test.go | 35 + config/defaultConfigProvider.go | 374 + config/defaultConfigProvider_test.go | 483 + config/env.go | 93 + config/env_test.go | 32 + config/namespace.go | 75 + config/namespace_test.go | 60 + config/privacy/privacyConfig.go | 120 + config/privacy/privacyConfig_test.go | 98 + config/security/securityConfig.go | 394 + config/security/securityConfig_test.go | 424 + config/security/whitelist.go | 140 + config/security/whitelist_test.go | 67 + config/services/servicesConfig.go | 110 + config/services/servicesConfig_test.go | 69 + config/testconfig/testconfig.go | 95 + create/content.go | 402 + create/content_test.go | 379 + create/skeletons/project/assets/.gitkeep | 0 create/skeletons/project/content/.gitkeep | 0 create/skeletons/project/data/.gitkeep | 0 create/skeletons/project/i18n/.gitkeep | 0 create/skeletons/project/layouts/.gitkeep | 0 create/skeletons/project/static/.gitkeep | 0 create/skeletons/project/themes/.gitkeep | 0 create/skeletons/skeletons.go | 182 + .../theme/assets/css/components/footer.css | 4 + .../theme/assets/css/components/header.css | 4 + create/skeletons/theme/assets/css/main.css | 15 + create/skeletons/theme/assets/js/main.js | 1 + create/skeletons/theme/content/_index.md | 9 + .../skeletons/theme/content/posts/_index.md | 7 + .../skeletons/theme/content/posts/post-1.md | 10 + .../skeletons/theme/content/posts/post-2.md | 10 + .../content/posts/post-3/bryce-canyon.jpg | Bin 0 -> 19224 bytes .../theme/content/posts/post-3/index.md | 12 + create/skeletons/theme/data/.gitkeep | 0 create/skeletons/theme/i18n/.gitkeep | 0 .../theme/layouts/_partials/footer.html | 1 + .../theme/layouts/_partials/head.html | 5 + .../theme/layouts/_partials/head/css.html | 15 + .../theme/layouts/_partials/head/js.html | 15 + .../theme/layouts/_partials/header.html | 2 + .../theme/layouts/_partials/menu.html | 51 + .../theme/layouts/_partials/terms.html | 23 + create/skeletons/theme/layouts/baseof.html | 17 + create/skeletons/theme/layouts/home.html | 9 + create/skeletons/theme/layouts/page.html | 10 + create/skeletons/theme/layouts/section.html | 10 + create/skeletons/theme/layouts/taxonomy.html | 7 + create/skeletons/theme/layouts/term.html | 7 + create/skeletons/theme/static/favicon.ico | Bin 0 -> 15406 bytes deploy/cloudfront.go | 72 + deploy/deploy.go | 763 ++ deploy/deploy_azure.go | 21 + deploy/deploy_test.go | 1102 ++ deploy/deployconfig/deployConfig.go | 179 + deploy/deployconfig/deployConfig_test.go | 198 + deploy/google.go | 39 + deps/deps.go | 542 + deps/deps_test.go | 31 + docs/.codespellrc | 13 + docs/.cspell.json | 204 + docs/.editorconfig | 20 + docs/.github/ISSUE_TEMPLATE/config.yml | 1 + docs/.github/ISSUE_TEMPLATE/default.md | 6 + docs/.github/SUPPORT.md | 3 + docs/.github/workflows/codeql-analysis.yml | 26 + docs/.github/workflows/lint.yml | 61 + docs/.github/workflows/stale.yml | 32 + docs/.gitignore | 13 + docs/.markdownlint-cli2.yaml | 94 + docs/.prettierignore | 3 + docs/.prettierrc | 12 + docs/AGENTS.md | 25 + docs/CLAUDE.md | 1 + docs/LICENSE.md | 3 + docs/README.md | 25 + docs/archetypes/default.md | 6 + docs/archetypes/functions.md | 11 + docs/archetypes/glossary.md | 13 + docs/archetypes/methods.md | 10 + docs/archetypes/news.md | 7 + docs/assets/css/components/all.css | 8 + docs/assets/css/components/chroma.css | 85 + docs/assets/css/components/chroma_dark.css | 85 + docs/assets/css/components/content.css | 49 + docs/assets/css/components/fonts.css | 15 + docs/assets/css/components/helpers.css | 19 + docs/assets/css/components/highlight.css | 11 + docs/assets/css/components/shortcodes.css | 4 + .../assets/css/components/tableofcontents.css | 14 + docs/assets/css/components/todo-lists.css | 15 + .../css/components/view-transitions.css | 24 + docs/assets/css/styles.css | 143 + .../examples/landscape-exif-orientation-5.jpg | Bin 0 -> 38639 bytes docs/assets/images/examples/mask.png | Bin 0 -> 137283 bytes .../images/examples/zion-national-park.jpg | Bin 0 -> 44553 bytes docs/assets/images/hugo-github-screenshot.png | Bin 0 -> 243609 bytes docs/assets/images/logos/logo-128x128.png | Bin 0 -> 4759 bytes docs/assets/images/logos/logo-256x256.png | Bin 0 -> 8822 bytes docs/assets/images/logos/logo-512x512.png | Bin 0 -> 73651 bytes docs/assets/images/logos/logo-64x64.png | Bin 0 -> 2533 bytes docs/assets/images/logos/logo-96x96.png | Bin 0 -> 3644 bytes .../sponsors/Route4MeLogoBlueOnWhite.svg | 15 + .../assets/images/sponsors/bep-consulting.svg | 4 + docs/assets/images/sponsors/butter-dark.svg | 1 + docs/assets/images/sponsors/butter-light.svg | 1 + .../images/sponsors/cloudcannon-cms-logo.svg | 13 + docs/assets/images/sponsors/esolia-logo.svg | 67 + docs/assets/images/sponsors/goland.svg | 20 + .../assets/images/sponsors/graitykit-dark.svg | 3 + docs/assets/images/sponsors/linode-logo.svg | 1 + .../linode-logo_standard_light_medium.png | Bin 0 -> 11972 bytes docs/assets/images/sponsors/logo-pinme.svg | 100 + .../images/sponsors/your-company-dark.svg | 4 + docs/assets/images/sponsors/your-company.svg | 4 + docs/assets/js/alpinejs/data/index.js | 3 + docs/assets/js/alpinejs/data/navbar.js | 28 + docs/assets/js/alpinejs/data/search.js | 124 + docs/assets/js/alpinejs/data/toc.js | 71 + docs/assets/js/alpinejs/magics/helpers.js | 36 + docs/assets/js/alpinejs/magics/index.js | 1 + docs/assets/js/alpinejs/stores/index.js | 1 + docs/assets/js/alpinejs/stores/nav.js | 104 + docs/assets/js/head-early.js | 20 + docs/assets/js/helpers/helpers.js | 17 + docs/assets/js/helpers/index.js | 2 + docs/assets/js/helpers/lrucache.js | 19 + docs/assets/js/main.js | 57 + docs/assets/jsconfig.json | 9 + .../assets/opengraph/gohugoio-card-base-1.png | Bin 0 -> 242906 bytes docs/assets/opengraph/mulish-black.ttf | Bin 0 -> 105920 bytes docs/content/LICENSE.md | 201 + docs/content/en/_common/_index.md | 13 + .../en/_common/configuration/locale.md | 22 + .../en/_common/configuration/page-matcher.md | 22 + .../en/_common/content-format-table.md | 13 + .../en/_common/embedded-get-page-images.md | 7 + docs/content/en/_common/filter-sort-group.md | 8 + .../en/_common/functions/fmt/format-string.md | 7 + .../functions/go-html-template-package.md | 14 + .../functions/go-template/text-template.md | 7 + .../functions/hugo/sites-collection.md | 9 + .../functions/images/apply-image-filter.md | 26 + .../en/_common/functions/js/options.md | 107 + docs/content/en/_common/functions/locales.md | 9 + .../reflect/image-reflection-functions.md | 49 + .../_common/functions/regular-expressions.md | 12 + .../en/_common/functions/truthy-falsy.md | 7 + .../functions/urls/anchorize-vs-urlize.md | 35 + docs/content/en/_common/gitignore-public.md | 8 + docs/content/en/_common/glob-patterns.md | 23 + docs/content/en/_common/gomodules-info.md | 16 + .../en/_common/installation/01-editions.md | 23 + .../_common/installation/02-prerequisites.md | 43 + .../installation/03-prebuilt-binaries.md | 19 + .../installation/04-build-from-source.md | 49 + .../en/_common/installation/homebrew.md | 13 + .../en/_common/menu-entries/pre-and-post.md | 39 + .../en/_common/menu-entry-properties.md | 31 + .../methods/media-type/core-methods.md | 18 + .../output-formats/to-use-this-method.md | 9 + .../en/_common/methods/page/next-and-prev.md | 59 + .../page/nextinsection-and-previnsection.md | 77 + .../_common/methods/pages/group-sort-order.md | 5 + .../en/_common/methods/pages/next-and-prev.md | 71 + .../resource/global-page-remote-resources.md | 6 + .../methods/resource/processing-spec.md | 67 + .../methods/taxonomy/get-a-taxonomy-object.md | 66 + .../ordered-taxonomy-element-methods.md | 24 + .../en/_common/parsable-date-time-strings.md | 14 + docs/content/en/_common/permalink-tokens.md | 70 + .../_common/ref-and-relref-error-handling.md | 10 + .../en/_common/ref-and-relref-options.md | 12 + .../en/_common/render-hooks/pageinner.md | 45 + docs/content/en/_common/scratch-pad-scope.md | 21 + docs/content/en/_common/store-methods.md | 81 + docs/content/en/_common/store-scope.md | 21 + .../en/_common/syntax-highlighting-options.md | 57 + docs/content/en/_common/time-layout-string.md | 46 + docs/content/en/_index.md | 4 + docs/content/en/about/_index.md | 9 + docs/content/en/about/features.md | 140 + docs/content/en/about/introduction.md | 34 + docs/content/en/about/license.md | 75 + docs/content/en/about/security.md | 63 + docs/content/en/commands/_index.md | 8 + docs/content/en/commands/hugo.md | 83 + docs/content/en/commands/hugo_build.md | 71 + docs/content/en/commands/hugo_completion.md | 45 + .../en/commands/hugo_completion_bash.md | 64 + .../en/commands/hugo_completion_fish.md | 55 + .../en/commands/hugo_completion_powershell.md | 52 + .../en/commands/hugo_completion_zsh.md | 66 + docs/content/en/commands/hugo_config.md | 52 + .../content/en/commands/hugo_config_mounts.md | 44 + docs/content/en/commands/hugo_convert.md | 46 + .../en/commands/hugo_convert_toJSON.md | 46 + .../en/commands/hugo_convert_toTOML.md | 46 + .../en/commands/hugo_convert_toYAML.md | 46 + docs/content/en/commands/hugo_deploy.md | 54 + docs/content/en/commands/hugo_env.md | 43 + docs/content/en/commands/hugo_gen.md | 42 + .../en/commands/hugo_gen_chromastyles.md | 51 + docs/content/en/commands/hugo_gen_doc.md | 49 + docs/content/en/commands/hugo_gen_man.md | 46 + docs/content/en/commands/hugo_import.md | 42 + .../content/en/commands/hugo_import_jekyll.md | 46 + docs/content/en/commands/hugo_list.md | 46 + docs/content/en/commands/hugo_list_all.md | 43 + docs/content/en/commands/hugo_list_drafts.md | 43 + docs/content/en/commands/hugo_list_expired.md | 43 + docs/content/en/commands/hugo_list_future.md | 43 + .../en/commands/hugo_list_published.md | 43 + docs/content/en/commands/hugo_mod.md | 58 + docs/content/en/commands/hugo_mod_clean.md | 50 + docs/content/en/commands/hugo_mod_get.md | 74 + docs/content/en/commands/hugo_mod_graph.md | 51 + docs/content/en/commands/hugo_mod_init.md | 55 + docs/content/en/commands/hugo_mod_npm.md | 44 + docs/content/en/commands/hugo_mod_npm_pack.md | 56 + docs/content/en/commands/hugo_mod_tidy.md | 44 + docs/content/en/commands/hugo_mod_vendor.md | 50 + docs/content/en/commands/hugo_mod_verify.md | 49 + docs/content/en/commands/hugo_new.md | 49 + docs/content/en/commands/hugo_new_content.md | 58 + docs/content/en/commands/hugo_new_project.md | 45 + docs/content/en/commands/hugo_new_theme.md | 45 + docs/content/en/commands/hugo_server.md | 97 + docs/content/en/commands/hugo_server_trust.md | 40 + docs/content/en/commands/hugo_version.md | 43 + docs/content/en/configuration/_index.md | 7 + docs/content/en/configuration/all.md | 416 + docs/content/en/configuration/build.md | 85 + docs/content/en/configuration/caches.md | 63 + docs/content/en/configuration/cascade.md | 62 + .../content/en/configuration/content-types.md | 63 + docs/content/en/configuration/deployment.md | 159 + docs/content/en/configuration/front-matter.md | 104 + docs/content/en/configuration/http-cache.md | 129 + docs/content/en/configuration/imaging.md | 155 + docs/content/en/configuration/introduction.md | 315 + docs/content/en/configuration/languages.md | 198 + docs/content/en/configuration/markup.md | 365 + docs/content/en/configuration/media-types.md | 82 + docs/content/en/configuration/menus.md | 137 + docs/content/en/configuration/minify.md | 18 + docs/content/en/configuration/module.md | 208 + .../en/configuration/output-formats.md | 206 + docs/content/en/configuration/outputs.md | 49 + docs/content/en/configuration/page.md | 42 + docs/content/en/configuration/pagination.md | 45 + docs/content/en/configuration/params.md | 100 + docs/content/en/configuration/permalinks.md | 105 + docs/content/en/configuration/privacy.md | 50 + .../en/configuration/related-content.md | 111 + docs/content/en/configuration/roles.md | 35 + docs/content/en/configuration/security.md | 94 + docs/content/en/configuration/segments.md | 196 + docs/content/en/configuration/server.md | 126 + docs/content/en/configuration/services.md | 46 + docs/content/en/configuration/sitemap.md | 26 + docs/content/en/configuration/taxonomies.md | 68 + docs/content/en/configuration/ugly-urls.md | 37 + docs/content/en/configuration/versions.md | 37 + docs/content/en/content-management/_index.md | 8 + .../en/content-management/archetypes.md | 188 + .../en/content-management/build-options.md | 297 + .../content/en/content-management/comments.md | 86 + .../en/content-management/content-adapters.md | 348 + .../en/content-management/data-sources.md | 114 + .../content/en/content-management/diagrams.md | 260 + docs/content/en/content-management/formats.md | 128 + .../en/content-management/front-matter.md | 341 + .../image-processing/index.md | 170 + .../image-processing/sunset.jpg | Bin 0 -> 34584 bytes .../content-management/markdown-attributes.md | 119 + .../en/content-management/mathematics.md | 223 + docs/content/en/content-management/menus.md | 100 + .../en/content-management/multilingual.md | 399 + .../content-management/organization/index.md | 155 + .../en/content-management/page-bundles.md | 145 + .../en/content-management/page-resources.md | 273 + .../en/content-management/related-content.md | 104 + .../content/en/content-management/sections.md | 139 + .../en/content-management/shortcodes.md | 232 + .../en/content-management/summaries.md | 153 + .../content-management/syntax-highlighting.md | 101 + .../en/content-management/taxonomies.md | 179 + docs/content/en/content-management/urls.md | 259 + docs/content/en/contribute/_index.md | 9 + docs/content/en/contribute/development.md | 148 + .../en/contribute/documentation/index.md | 593 + docs/content/en/contribute/themes.md | 25 + docs/content/en/documentation.md | 22 + docs/content/en/featured.png | Bin 0 -> 73881 bytes docs/content/en/functions/_index.md | 8 + docs/content/en/functions/cast/ToFloat.md | 46 + docs/content/en/functions/cast/ToInt.md | 50 + docs/content/en/functions/cast/ToString.md | 49 + docs/content/en/functions/cast/_index.md | 7 + .../content/en/functions/collections/After.md | 68 + .../en/functions/collections/Append.md | 100 + .../content/en/functions/collections/Apply.md | 26 + .../en/functions/collections/Complement.md | 73 + docs/content/en/functions/collections/D.md | 82 + .../en/functions/collections/Delimit.md | 31 + .../en/functions/collections/Dictionary.md | 51 + .../content/en/functions/collections/First.md | 56 + .../content/en/functions/collections/Group.md | 34 + docs/content/en/functions/collections/In.md | 22 + .../en/functions/collections/IndexFunction.md | 50 + .../en/functions/collections/Intersect.md | 26 + .../content/en/functions/collections/IsSet.md | 42 + .../en/functions/collections/KeyVals.md | 43 + docs/content/en/functions/collections/Last.md | 56 + .../content/en/functions/collections/Merge.md | 69 + .../en/functions/collections/NewScratch.md | 113 + .../en/functions/collections/Querify.md | 48 + .../en/functions/collections/Reverse.md | 16 + docs/content/en/functions/collections/Seq.md | 35 + .../en/functions/collections/Shuffle.md | 35 + .../content/en/functions/collections/Slice.md | 23 + docs/content/en/functions/collections/Sort.md | 150 + .../en/functions/collections/SymDiff.md | 20 + .../content/en/functions/collections/Union.md | 37 + docs/content/en/functions/collections/Uniq.md | 16 + .../content/en/functions/collections/Where.md | 418 + .../en/functions/collections/_index.md | 7 + .../en/functions/compare/Conditional.md | 31 + docs/content/en/functions/compare/Default.md | 47 + docs/content/en/functions/compare/Eq.md | 24 + docs/content/en/functions/compare/Ge.md | 35 + docs/content/en/functions/compare/Gt.md | 35 + docs/content/en/functions/compare/Le.md | 35 + docs/content/en/functions/compare/Lt.md | 35 + docs/content/en/functions/compare/Ne.md | 24 + docs/content/en/functions/compare/_index.md | 7 + docs/content/en/functions/crypto/HMAC.md | 27 + docs/content/en/functions/crypto/Hash.md | 36 + docs/content/en/functions/crypto/MD5.md | 24 + docs/content/en/functions/crypto/SHA1.md | 16 + docs/content/en/functions/crypto/SHA256.md | 16 + docs/content/en/functions/crypto/_index.md | 7 + docs/content/en/functions/css/Build.md | 381 + docs/content/en/functions/css/PostCSS.md | 136 + docs/content/en/functions/css/Quoted.md | 61 + docs/content/en/functions/css/Sass.md | 352 + docs/content/en/functions/css/TailwindCSS.md | 144 + docs/content/en/functions/css/Unquoted.md | 49 + docs/content/en/functions/css/_index.md | 7 + docs/content/en/functions/debug/Dump.md | 33 + docs/content/en/functions/debug/Timer.md | 35 + .../en/functions/debug/VisualizeSpaces.md | 15 + docs/content/en/functions/debug/_index.md | 7 + docs/content/en/functions/diagrams/Goat.md | 115 + docs/content/en/functions/diagrams/_index.md | 7 + .../en/functions/encoding/Base64Decode.md | 39 + .../en/functions/encoding/Base64Encode.md | 16 + .../en/functions/encoding/HexDecode.md | 15 + .../en/functions/encoding/HexEncode.md | 15 + docs/content/en/functions/encoding/Jsonify.md | 36 + docs/content/en/functions/encoding/_index.md | 7 + docs/content/en/functions/fmt/Errorf.md | 24 + docs/content/en/functions/fmt/Erroridf.md | 38 + docs/content/en/functions/fmt/Print.md | 18 + docs/content/en/functions/fmt/Printf.md | 39 + docs/content/en/functions/fmt/Println.md | 17 + docs/content/en/functions/fmt/Warnf.md | 33 + docs/content/en/functions/fmt/Warnidf.md | 38 + docs/content/en/functions/fmt/_index.md | 7 + docs/content/en/functions/global/_index.md | 6 + docs/content/en/functions/global/page.md | 100 + docs/content/en/functions/global/site.md | 30 + .../en/functions/go-template/_index.md | 7 + docs/content/en/functions/go-template/and.md | 28 + .../content/en/functions/go-template/block.md | 54 + .../content/en/functions/go-template/break.md | 31 + .../en/functions/go-template/continue.md | 32 + .../en/functions/go-template/define.md | 50 + docs/content/en/functions/go-template/else.md | 65 + docs/content/en/functions/go-template/end.md | 60 + docs/content/en/functions/go-template/if.md | 50 + docs/content/en/functions/go-template/len.md | 47 + docs/content/en/functions/go-template/not.md | 33 + docs/content/en/functions/go-template/or.md | 28 + .../content/en/functions/go-template/range.md | 220 + .../en/functions/go-template/return.md | 93 + .../en/functions/go-template/template.md | 42 + docs/content/en/functions/go-template/try.md | 110 + .../en/functions/go-template/urlquery.md | 27 + docs/content/en/functions/go-template/with.md | 95 + docs/content/en/functions/hash/FNV32a.md | 16 + docs/content/en/functions/hash/XxHash.md | 20 + docs/content/en/functions/hash/_index.md | 7 + docs/content/en/functions/hugo/BuildDate.md | 19 + docs/content/en/functions/hugo/CommitHash.md | 15 + docs/content/en/functions/hugo/Data.md | 105 + docs/content/en/functions/hugo/Deps.md | 72 + docs/content/en/functions/hugo/Environment.md | 26 + docs/content/en/functions/hugo/Generator.md | 15 + docs/content/en/functions/hugo/GoVersion.md | 15 + .../en/functions/hugo/IsDevelopment.md | 15 + docs/content/en/functions/hugo/IsExtended.md | 15 + docs/content/en/functions/hugo/IsMultihost.md | 37 + .../en/functions/hugo/IsMultilingual.md | 35 + .../content/en/functions/hugo/IsProduction.md | 15 + docs/content/en/functions/hugo/IsServer.md | 15 + docs/content/en/functions/hugo/Sites.md | 82 + docs/content/en/functions/hugo/Store.md | 112 + docs/content/en/functions/hugo/Version.md | 15 + docs/content/en/functions/hugo/WorkingDir.md | 15 + docs/content/en/functions/hugo/_index.md | 7 + .../content/en/functions/images/AutoOrient.md | 46 + .../content/en/functions/images/Brightness.md | 33 + .../en/functions/images/ColorBalance.md | 33 + docs/content/en/functions/images/Colorize.md | 37 + docs/content/en/functions/images/Config.md | 28 + docs/content/en/functions/images/Contrast.md | 33 + docs/content/en/functions/images/Dither.md | 155 + docs/content/en/functions/images/Filter.md | 75 + docs/content/en/functions/images/Gamma.md | 33 + .../en/functions/images/GaussianBlur.md | 33 + docs/content/en/functions/images/Grayscale.md | 31 + docs/content/en/functions/images/Hue.md | 33 + docs/content/en/functions/images/Invert.md | 31 + docs/content/en/functions/images/Mask.md | 75 + docs/content/en/functions/images/Opacity.md | 47 + docs/content/en/functions/images/Overlay.md | 45 + docs/content/en/functions/images/Padding.md | 69 + docs/content/en/functions/images/Pixelate.md | 31 + docs/content/en/functions/images/Process.md | 49 + docs/content/en/functions/images/QR.md | 140 + .../content/en/functions/images/Saturation.md | 33 + docs/content/en/functions/images/Sepia.md | 33 + docs/content/en/functions/images/Sigmoid.md | 37 + docs/content/en/functions/images/Text.md | 124 + .../en/functions/images/UnsharpMask.md | 37 + docs/content/en/functions/images/_index.md | 7 + docs/content/en/functions/inflect/Humanize.md | 24 + .../content/en/functions/inflect/Pluralize.md | 16 + .../en/functions/inflect/Singularize.md | 16 + docs/content/en/functions/inflect/_index.md | 7 + docs/content/en/functions/js/Babel.md | 119 + docs/content/en/functions/js/Batch.md | 310 + docs/content/en/functions/js/Build.md | 130 + docs/content/en/functions/js/_index.md | 7 + .../en/functions/lang/FormatAccounting.md | 17 + .../en/functions/lang/FormatCurrency.md | 17 + .../content/en/functions/lang/FormatNumber.md | 17 + .../en/functions/lang/FormatNumberCustom.md | 30 + .../en/functions/lang/FormatPercent.md | 17 + docs/content/en/functions/lang/Merge.md | 29 + docs/content/en/functions/lang/Translate.md | 251 + docs/content/en/functions/lang/_index.md | 7 + docs/content/en/functions/math/Abs.md | 15 + docs/content/en/functions/math/Acos.md | 15 + docs/content/en/functions/math/Add.md | 23 + docs/content/en/functions/math/Asin.md | 15 + docs/content/en/functions/math/Atan.md | 15 + docs/content/en/functions/math/Atan2.md | 15 + docs/content/en/functions/math/Ceil.md | 15 + docs/content/en/functions/math/Cos.md | 15 + docs/content/en/functions/math/Counter.md | 33 + docs/content/en/functions/math/Div.md | 17 + docs/content/en/functions/math/Floor.md | 15 + docs/content/en/functions/math/Log.md | 15 + docs/content/en/functions/math/Max.md | 15 + docs/content/en/functions/math/MaxInt64.md | 27 + docs/content/en/functions/math/Min.md | 15 + docs/content/en/functions/math/Mod.md | 15 + docs/content/en/functions/math/ModBool.md | 15 + docs/content/en/functions/math/Mul.md | 17 + docs/content/en/functions/math/Pi.md | 15 + docs/content/en/functions/math/Pow.md | 15 + docs/content/en/functions/math/Product.md | 15 + docs/content/en/functions/math/Rand.md | 41 + docs/content/en/functions/math/Round.md | 15 + docs/content/en/functions/math/Sin.md | 15 + docs/content/en/functions/math/Sqrt.md | 15 + docs/content/en/functions/math/Sub.md | 17 + docs/content/en/functions/math/Sum.md | 14 + docs/content/en/functions/math/Tan.md | 15 + docs/content/en/functions/math/ToDegrees.md | 15 + docs/content/en/functions/math/ToRadians.md | 15 + docs/content/en/functions/math/_index.md | 6 + .../en/functions/openapi3/Unmarshal.md | 121 + docs/content/en/functions/openapi3/_index.md | 7 + docs/content/en/functions/os/FileExists.md | 39 + docs/content/en/functions/os/Getenv.md | 56 + docs/content/en/functions/os/ReadDir.md | 47 + docs/content/en/functions/os/ReadFile.md | 36 + docs/content/en/functions/os/Stat.md | 30 + docs/content/en/functions/os/_index.md | 7 + docs/content/en/functions/partials/Include.md | 78 + .../en/functions/partials/IncludeCached.md | 55 + docs/content/en/functions/partials/_index.md | 7 + docs/content/en/functions/path/Base.md | 20 + docs/content/en/functions/path/BaseName.md | 20 + docs/content/en/functions/path/Clean.md | 27 + docs/content/en/functions/path/Dir.md | 21 + docs/content/en/functions/path/Ext.md | 18 + docs/content/en/functions/path/Join.md | 28 + docs/content/en/functions/path/Split.md | 28 + docs/content/en/functions/path/_index.md | 7 + .../en/functions/reflect/IsImageResource.md | 29 + .../reflect/IsImageResourceProcessable.md | 31 + .../reflect/IsImageResourceWithMeta.md | 30 + docs/content/en/functions/reflect/IsMap.md | 17 + docs/content/en/functions/reflect/IsPage.md | 23 + .../en/functions/reflect/IsResource.md | 65 + docs/content/en/functions/reflect/IsSite.md | 23 + docs/content/en/functions/reflect/IsSlice.md | 17 + docs/content/en/functions/reflect/_index.md | 7 + docs/content/en/functions/resources/ByType.md | 27 + docs/content/en/functions/resources/Concat.md | 25 + docs/content/en/functions/resources/Copy.md | 23 + .../functions/resources/ExecuteAsTemplate.md | 61 + .../en/functions/resources/Fingerprint.md | 36 + .../en/functions/resources/FromString.md | 75 + docs/content/en/functions/resources/Get.md | 24 + .../en/functions/resources/GetMatch.md | 28 + .../en/functions/resources/GetRemote.md | 239 + docs/content/en/functions/resources/Match.md | 29 + docs/content/en/functions/resources/Minify.md | 18 + .../en/functions/resources/PostProcess.md | 143 + docs/content/en/functions/resources/_index.md | 7 + docs/content/en/functions/safe/CSS.md | 62 + docs/content/en/functions/safe/HTML.md | 54 + docs/content/en/functions/safe/HTMLAttr.md | 60 + docs/content/en/functions/safe/JS.md | 59 + docs/content/en/functions/safe/JSStr.md | 62 + docs/content/en/functions/safe/URL.md | 61 + docs/content/en/functions/safe/_index.md | 7 + docs/content/en/functions/strings/Chomp.md | 22 + docs/content/en/functions/strings/Contains.md | 22 + .../en/functions/strings/ContainsAny.md | 22 + .../en/functions/strings/ContainsNonSpace.md | 22 + docs/content/en/functions/strings/Count.md | 21 + .../en/functions/strings/CountRunes.md | 20 + .../en/functions/strings/CountWords.md | 16 + .../strings/Diff/diff-screen-capture.png | Bin 0 -> 7290 bytes .../en/functions/strings/Diff/index.md | 31 + .../en/functions/strings/FindRESubmatch.md | 88 + docs/content/en/functions/strings/FindRe.md | 34 + .../en/functions/strings/FirstUpper.md | 16 + .../content/en/functions/strings/HasPrefix.md | 16 + .../content/en/functions/strings/HasSuffix.md | 16 + docs/content/en/functions/strings/Repeat.md | 16 + docs/content/en/functions/strings/Replace.md | 23 + .../en/functions/strings/ReplacePairs.md | 111 + .../content/en/functions/strings/ReplaceRE.md | 38 + .../content/en/functions/strings/RuneCount.md | 20 + .../en/functions/strings/SliceString.md | 24 + docs/content/en/functions/strings/Split.md | 24 + docs/content/en/functions/strings/Substr.md | 36 + docs/content/en/functions/strings/Title.md | 29 + docs/content/en/functions/strings/ToLower.md | 16 + docs/content/en/functions/strings/ToUpper.md | 16 + docs/content/en/functions/strings/Trim.md | 16 + docs/content/en/functions/strings/TrimLeft.md | 23 + .../en/functions/strings/TrimPrefix.md | 18 + .../content/en/functions/strings/TrimRight.md | 23 + .../content/en/functions/strings/TrimSpace.md | 20 + .../en/functions/strings/TrimSuffix.md | 18 + docs/content/en/functions/strings/Truncate.md | 23 + docs/content/en/functions/strings/_index.md | 7 + .../content/en/functions/templates/Current.md | 157 + docs/content/en/functions/templates/Defer.md | 99 + docs/content/en/functions/templates/Exists.md | 27 + docs/content/en/functions/templates/Inner.md | 71 + docs/content/en/functions/templates/_index.md | 7 + docs/content/en/functions/time/AsTime.md | 48 + docs/content/en/functions/time/Duration.md | 41 + docs/content/en/functions/time/Format.md | 80 + docs/content/en/functions/time/In.md | 30 + docs/content/en/functions/time/Now.md | 42 + .../en/functions/time/ParseDuration.md | 32 + docs/content/en/functions/time/_index.md | 7 + .../en/functions/transform/CanHighlight.md | 16 + .../content/en/functions/transform/Emojify.md | 27 + .../en/functions/transform/HTMLEscape.md | 29 + .../en/functions/transform/HTMLUnescape.md | 29 + .../en/functions/transform/HTMLtoMarkdown.md | 37 + .../en/functions/transform/Highlight.md | 63 + .../functions/transform/HighlightCodeBlock.md | 74 + .../en/functions/transform/Markdownify.md | 27 + .../en/functions/transform/Plainify.md | 16 + .../en/functions/transform/PortableText.md | 222 + .../en/functions/transform/Remarshal.md | 89 + docs/content/en/functions/transform/ToMath.md | 182 + .../en/functions/transform/Unmarshal.md | 373 + .../en/functions/transform/XMLEscape.md | 38 + docs/content/en/functions/transform/_index.md | 7 + docs/content/en/functions/urls/AbsLangURL.md | 72 + docs/content/en/functions/urls/AbsURL.md | 62 + docs/content/en/functions/urls/Anchorize.md | 18 + docs/content/en/functions/urls/JoinPath.md | 27 + docs/content/en/functions/urls/Parse.md | 36 + docs/content/en/functions/urls/PathEscape.md | 22 + .../content/en/functions/urls/PathUnescape.md | 22 + docs/content/en/functions/urls/Ref.md | 46 + docs/content/en/functions/urls/RelLangURL.md | 82 + docs/content/en/functions/urls/RelRef.md | 46 + docs/content/en/functions/urls/RelURL.md | 72 + docs/content/en/functions/urls/URLize.md | 61 + docs/content/en/functions/urls/_index.md | 7 + docs/content/en/getting-started/_index.md | 8 + .../en/getting-started/directory-structure.md | 208 + .../build-websites-with-hugo.png | Bin 0 -> 18058 bytes .../hugo-in-action.png | Bin 0 -> 22891 bytes .../external-learning-resources/index.md | 120 + .../content/en/getting-started/quick-start.md | 213 + docs/content/en/getting-started/usage.md | 150 + docs/content/en/host-and-deploy/_index.md | 8 + .../deploy-with-hugo-deploy.md | 102 + .../en/host-and-deploy/deploy-with-rclone.md | 49 + .../en/host-and-deploy/deploy-with-rsync.md | 135 + .../host-on-aws-amplify/amplify-01.png | Bin 0 -> 21326 bytes .../host-on-aws-amplify/amplify-02.png | Bin 0 -> 48097 bytes .../host-on-aws-amplify/amplify-03.png | Bin 0 -> 19282 bytes .../host-on-aws-amplify/amplify-04.png | Bin 0 -> 81416 bytes .../host-on-aws-amplify/amplify-05.png | Bin 0 -> 22619 bytes .../host-on-aws-amplify/amplify-06.png | Bin 0 -> 61021 bytes .../host-on-aws-amplify/index.md | 196 + .../host-on-azure-static-web-apps.md | 13 + .../host-on-cloudflare/cloudflare-01.png | Bin 0 -> 49820 bytes .../host-on-cloudflare/cloudflare-02.png | Bin 0 -> 95146 bytes .../host-on-cloudflare/cloudflare-03.png | Bin 0 -> 30347 bytes .../host-on-cloudflare/cloudflare-04.png | Bin 0 -> 8258 bytes .../host-on-cloudflare/cloudflare-05.png | Bin 0 -> 27869 bytes .../host-on-cloudflare/cloudflare-06.png | Bin 0 -> 25167 bytes .../host-on-cloudflare/cloudflare-07.png | Bin 0 -> 4901 bytes .../host-on-cloudflare/index.md | 277 + .../en/host-and-deploy/host-on-firebase.md | 107 + .../host-on-github-pages/gh-pages-01.png | Bin 0 -> 3677 bytes .../host-on-github-pages/gh-pages-02.png | Bin 0 -> 4014 bytes .../host-on-github-pages/gh-pages-03.png | Bin 0 -> 6390 bytes .../host-on-github-pages/gh-pages-04.png | Bin 0 -> 6258 bytes .../host-on-github-pages/gh-pages-05.png | Bin 0 -> 4793 bytes .../host-on-github-pages/index.md | 233 + .../host-and-deploy/host-on-gitlab-pages.md | 202 + .../host-and-deploy/host-on-netlify/index.md | 134 + .../host-on-netlify/netlify-01.png | Bin 0 -> 5469 bytes .../host-on-netlify/netlify-02.png | Bin 0 -> 10930 bytes .../host-on-netlify/netlify-03.png | Bin 0 -> 16856 bytes .../host-on-netlify/netlify-04.png | Bin 0 -> 17918 bytes .../host-on-netlify/netlify-05.png | Bin 0 -> 6264 bytes .../host-on-netlify/netlify-06.png | Bin 0 -> 28898 bytes .../host-on-netlify/netlify-07.png | Bin 0 -> 12854 bytes .../host-on-netlify/netlify-08.png | Bin 0 -> 5066 bytes .../host-on-netlify/netlify-09.png | Bin 0 -> 1908 bytes .../host-on-netlify/netlify-10.png | Bin 0 -> 5381 bytes .../host-and-deploy/host-on-render/index.md | 211 + .../host-on-render/render-01.png | Bin 0 -> 6828 bytes .../host-on-render/render-02.png | Bin 0 -> 6810 bytes .../host-on-render/render-03.png | Bin 0 -> 16902 bytes .../host-on-render/render-04.png | Bin 0 -> 5850 bytes .../host-on-render/render-05.png | Bin 0 -> 31955 bytes .../host-on-render/render-06.png | Bin 0 -> 5415 bytes .../host-on-render/render-07.png | Bin 0 -> 23683 bytes .../host-on-render/render-08.png | Bin 0 -> 5987 bytes .../host-on-render/render-09.png | Bin 0 -> 8057 bytes .../host-on-render/render-10.png | Bin 0 -> 7484 bytes .../host-on-sourcehut-pages.md | 113 + .../host-and-deploy/host-on-vercel/index.md | 223 + .../host-on-vercel/vercel-01.png | Bin 0 -> 3601 bytes .../host-on-vercel/vercel-02.png | Bin 0 -> 7228 bytes .../host-on-vercel/vercel-03.png | Bin 0 -> 16364 bytes .../host-on-vercel/vercel-04.png | Bin 0 -> 4583 bytes .../host-on-vercel/vercel-05.png | Bin 0 -> 5822 bytes .../host-on-vercel/vercel-06.png | Bin 0 -> 30267 bytes .../host-on-vercel/vercel-07.png | Bin 0 -> 8068 bytes .../host-on-vercel/vercel-08.png | Bin 0 -> 16094 bytes .../host-on-vercel/vercel-09.png | Bin 0 -> 11250 bytes .../host-on-vercel/vercel-10.png | Bin 0 -> 9097 bytes docs/content/en/hugo-modules/_index.md | 8 + docs/content/en/hugo-modules/introduction.md | 19 + .../en/hugo-modules/nodejs-dependencies.md | 61 + .../en/hugo-modules/theme-components.md | 35 + docs/content/en/hugo-modules/use-modules.md | 200 + docs/content/en/hugo-pipes/_index.md | 7 + docs/content/en/hugo-pipes/bundling.md | 9 + docs/content/en/hugo-pipes/fingerprint.md | 9 + docs/content/en/hugo-pipes/introduction.md | 79 + docs/content/en/hugo-pipes/js.md | 11 + docs/content/en/hugo-pipes/minification.md | 9 + docs/content/en/hugo-pipes/postcss.md | 8 + docs/content/en/hugo-pipes/postprocess.md | 8 + .../en/hugo-pipes/resource-from-string.md | 9 + .../en/hugo-pipes/resource-from-template.md | 9 + .../en/hugo-pipes/transpile-sass-to-css.md | 10 + docs/content/en/installation/_index.md | 8 + docs/content/en/installation/bsd.md | 67 + docs/content/en/installation/linux.md | 215 + docs/content/en/installation/macos.md | 42 + docs/content/en/installation/windows.md | 153 + docs/content/en/methods/_index.md | 8 + docs/content/en/methods/duration/Abs.md | 15 + docs/content/en/methods/duration/Hours.md | 15 + .../en/methods/duration/Microseconds.md | 15 + .../en/methods/duration/Milliseconds.md | 15 + docs/content/en/methods/duration/Minutes.md | 15 + .../en/methods/duration/Nanoseconds.md | 15 + docs/content/en/methods/duration/Round.md | 18 + docs/content/en/methods/duration/Seconds.md | 15 + docs/content/en/methods/duration/Truncate.md | 18 + docs/content/en/methods/duration/_index.md | 7 + .../content/en/methods/menu-entry/Children.md | 66 + .../en/methods/menu-entry/HasChildren.md | 66 + .../en/methods/menu-entry/Identifier.md | 41 + docs/content/en/methods/menu-entry/KeyName.md | 39 + docs/content/en/methods/menu-entry/Menu.md | 22 + docs/content/en/methods/menu-entry/Name.md | 28 + docs/content/en/methods/menu-entry/Page.md | 53 + docs/content/en/methods/menu-entry/PageRef.md | 109 + docs/content/en/methods/menu-entry/Params.md | 61 + docs/content/en/methods/menu-entry/Parent.md | 50 + docs/content/en/methods/menu-entry/Post.md | 12 + docs/content/en/methods/menu-entry/Pre.md | 12 + docs/content/en/methods/menu-entry/Title.md | 22 + docs/content/en/methods/menu-entry/URL.md | 22 + docs/content/en/methods/menu-entry/Weight.md | 31 + docs/content/en/methods/menu-entry/_index.md | 7 + docs/content/en/methods/menu/ByName.md | 65 + docs/content/en/methods/menu/ByWeight.md | 71 + docs/content/en/methods/menu/Limit.md | 50 + docs/content/en/methods/menu/Reverse.md | 51 + docs/content/en/methods/menu/_index.md | 7 + .../en/methods/output-format/MediaType.md | 32 + docs/content/en/methods/output-format/Name.md | 18 + .../en/methods/output-format/Permalink.md | 18 + docs/content/en/methods/output-format/Rel.md | 18 + .../en/methods/output-format/RelPermalink.md | 18 + .../en/methods/output-format/_index.md | 7 + docs/content/en/methods/page/Aliases.md | 138 + .../en/methods/page/AllTranslations.md | 88 + .../methods/page/AlternativeOutputFormats.md | 31 + docs/content/en/methods/page/Ancestors.md | 76 + docs/content/en/methods/page/BundleType.md | 35 + docs/content/en/methods/page/CodeOwners.md | 64 + docs/content/en/methods/page/Content.md | 16 + .../en/methods/page/ContentWithoutSummary.md | 22 + .../content/en/methods/page/CurrentSection.md | 48 + docs/content/en/methods/page/Data.md | 101 + docs/content/en/methods/page/Date.md | 33 + docs/content/en/methods/page/Description.md | 27 + docs/content/en/methods/page/Draft.md | 21 + docs/content/en/methods/page/Eq.md | 21 + docs/content/en/methods/page/ExpiryDate.md | 32 + docs/content/en/methods/page/File.md | 183 + docs/content/en/methods/page/FirstSection.md | 48 + docs/content/en/methods/page/Fragments.md | 108 + .../content/en/methods/page/FuzzyWordCount.md | 18 + docs/content/en/methods/page/GetPage.md | 64 + docs/content/en/methods/page/GetTerms.md | 41 + docs/content/en/methods/page/GitInfo.md | 194 + .../content/en/methods/page/HasMenuCurrent.md | 33 + docs/content/en/methods/page/HasShortcode.md | 50 + .../en/methods/page/HeadingsFiltered.md | 16 + docs/content/en/methods/page/InSection.md | 91 + docs/content/en/methods/page/IsAncestor.md | 87 + docs/content/en/methods/page/IsBranch.md | 32 + docs/content/en/methods/page/IsDescendant.md | 87 + docs/content/en/methods/page/IsHome.md | 26 + docs/content/en/methods/page/IsMenuCurrent.md | 31 + docs/content/en/methods/page/IsNode.md | 15 + docs/content/en/methods/page/IsPage.md | 26 + docs/content/en/methods/page/IsSection.md | 26 + docs/content/en/methods/page/IsTranslated.md | 56 + docs/content/en/methods/page/Keywords.md | 44 + docs/content/en/methods/page/Kind.md | 32 + docs/content/en/methods/page/Language.md | 117 + docs/content/en/methods/page/Lastmod.md | 36 + docs/content/en/methods/page/Layout.md | 40 + docs/content/en/methods/page/Len.md | 14 + docs/content/en/methods/page/LinkTitle.md | 29 + docs/content/en/methods/page/Next.md | 12 + docs/content/en/methods/page/NextInSection.md | 12 + docs/content/en/methods/page/OutputFormats.md | 73 + docs/content/en/methods/page/Page.md | 35 + docs/content/en/methods/page/Pages.md | 82 + docs/content/en/methods/page/Paginate.md | 47 + docs/content/en/methods/page/Paginator.md | 40 + docs/content/en/methods/page/Param.md | 48 + docs/content/en/methods/page/Params.md | 42 + docs/content/en/methods/page/Parent.md | 52 + docs/content/en/methods/page/Path.md | 131 + docs/content/en/methods/page/Permalink.md | 24 + docs/content/en/methods/page/Plain.md | 23 + docs/content/en/methods/page/PlainWords.md | 31 + docs/content/en/methods/page/Prev.md | 12 + docs/content/en/methods/page/PrevInSection.md | 12 + docs/content/en/methods/page/PublishDate.md | 32 + docs/content/en/methods/page/RawContent.md | 23 + docs/content/en/methods/page/ReadingTime.md | 47 + docs/content/en/methods/page/Ref.md | 37 + docs/content/en/methods/page/RegularPages.md | 79 + .../en/methods/page/RegularPagesRecursive.md | 83 + docs/content/en/methods/page/RelPermalink.md | 24 + docs/content/en/methods/page/RelRef.md | 37 + docs/content/en/methods/page/Render.md | 71 + .../en/methods/page/RenderShortcodes.md | 87 + docs/content/en/methods/page/RenderString.md | 49 + docs/content/en/methods/page/Resources.md | 88 + docs/content/en/methods/page/Rotate.md | 55 + docs/content/en/methods/page/Scratch.md | 19 + docs/content/en/methods/page/Section.md | 54 + docs/content/en/methods/page/Sections.md | 62 + docs/content/en/methods/page/Site.md | 18 + docs/content/en/methods/page/Sitemap.md | 80 + docs/content/en/methods/page/Sites.md | 15 + docs/content/en/methods/page/Slug.md | 25 + docs/content/en/methods/page/Store.md | 35 + docs/content/en/methods/page/Summary.md | 48 + .../en/methods/page/TableOfContents.md | 47 + docs/content/en/methods/page/Title.md | 46 + .../content/en/methods/page/TranslationKey.md | 71 + docs/content/en/methods/page/Translations.md | 86 + docs/content/en/methods/page/Truncated.md | 29 + docs/content/en/methods/page/Type.md | 51 + docs/content/en/methods/page/Weight.md | 25 + docs/content/en/methods/page/WordCount.md | 18 + docs/content/en/methods/page/_index.md | 8 + docs/content/en/methods/pager/First.md | 38 + docs/content/en/methods/pager/HasNext.md | 66 + docs/content/en/methods/pager/HasPrev.md | 66 + docs/content/en/methods/pager/Last.md | 38 + docs/content/en/methods/pager/Next.md | 38 + .../en/methods/pager/NumberOfElements.md | 23 + docs/content/en/methods/pager/PageGroups.md | 28 + docs/content/en/methods/pager/PageNumber.md | 29 + docs/content/en/methods/pager/PagerSize.md | 29 + docs/content/en/methods/pager/Pagers.md | 29 + docs/content/en/methods/pager/Pages.md | 21 + docs/content/en/methods/pager/Prev.md | 38 + .../en/methods/pager/TotalNumberOfElements.md | 23 + docs/content/en/methods/pager/TotalPages.md | 39 + docs/content/en/methods/pager/URL.md | 38 + docs/content/en/methods/pager/_index.md | 6 + docs/content/en/methods/pages/ByDate.md | 28 + docs/content/en/methods/pages/ByExpiryDate.md | 28 + docs/content/en/methods/pages/ByLanguage.md | 45 + docs/content/en/methods/pages/ByLastmod.md | 28 + docs/content/en/methods/pages/ByLength.md | 24 + docs/content/en/methods/pages/ByLinkTitle.md | 24 + docs/content/en/methods/pages/ByParam.md | 34 + .../content/en/methods/pages/ByPublishDate.md | 28 + docs/content/en/methods/pages/ByTitle.md | 24 + docs/content/en/methods/pages/ByWeight.md | 26 + docs/content/en/methods/pages/GroupBy.md | 36 + docs/content/en/methods/pages/GroupByDate.md | 62 + .../en/methods/pages/GroupByExpiryDate.md | 62 + .../en/methods/pages/GroupByLastmod.md | 62 + docs/content/en/methods/pages/GroupByParam.md | 36 + .../en/methods/pages/GroupByParamDate.md | 59 + .../en/methods/pages/GroupByPublishDate.md | 62 + docs/content/en/methods/pages/Len.md | 14 + docs/content/en/methods/pages/Limit.md | 16 + docs/content/en/methods/pages/Next.md | 12 + docs/content/en/methods/pages/Prev.md | 12 + docs/content/en/methods/pages/Related.md | 74 + docs/content/en/methods/pages/Reverse.md | 16 + docs/content/en/methods/pages/_index.md | 8 + docs/content/en/methods/resource/Colors.md | 174 + docs/content/en/methods/resource/Content.md | 60 + docs/content/en/methods/resource/Crop.md | 53 + docs/content/en/methods/resource/Data.md | 60 + docs/content/en/methods/resource/Err.md | 15 + docs/content/en/methods/resource/Exif.md | 15 + docs/content/en/methods/resource/Fill.md | 53 + docs/content/en/methods/resource/Filter.md | 75 + docs/content/en/methods/resource/Fit.md | 55 + docs/content/en/methods/resource/Height.md | 26 + docs/content/en/methods/resource/MediaType.md | 30 + docs/content/en/methods/resource/Meta.md | 104 + docs/content/en/methods/resource/Name.md | 89 + docs/content/en/methods/resource/Params.md | 61 + docs/content/en/methods/resource/Permalink.md | 20 + docs/content/en/methods/resource/Process.md | 69 + docs/content/en/methods/resource/Publish.md | 32 + .../en/methods/resource/RelPermalink.md | 20 + docs/content/en/methods/resource/Resize.md | 55 + .../en/methods/resource/ResourceType.md | 43 + docs/content/en/methods/resource/Title.md | 81 + docs/content/en/methods/resource/Width.md | 26 + docs/content/en/methods/resource/_index.md | 7 + docs/content/en/methods/shortcode/Get.md | 46 + docs/content/en/methods/shortcode/Inner.md | 143 + .../en/methods/shortcode/InnerDeindent.md | 97 + .../en/methods/shortcode/IsNamedParams.md | 29 + docs/content/en/methods/shortcode/Name.md | 27 + docs/content/en/methods/shortcode/Ordinal.md | 52 + docs/content/en/methods/shortcode/Page.md | 36 + docs/content/en/methods/shortcode/Params.md | 32 + docs/content/en/methods/shortcode/Parent.md | 50 + docs/content/en/methods/shortcode/Position.md | 30 + docs/content/en/methods/shortcode/Ref.md | 37 + docs/content/en/methods/shortcode/RelRef.md | 37 + docs/content/en/methods/shortcode/Scratch.md | 19 + docs/content/en/methods/shortcode/Site.md | 18 + docs/content/en/methods/shortcode/Store.md | 24 + docs/content/en/methods/shortcode/_index.md | 8 + docs/content/en/methods/site/AllPages.md | 15 + docs/content/en/methods/site/BaseURL.md | 32 + docs/content/en/methods/site/BuildDrafts.md | 15 + docs/content/en/methods/site/Config.md | 54 + docs/content/en/methods/site/Copyright.md | 22 + docs/content/en/methods/site/Data.md | 15 + docs/content/en/methods/site/Dimension.md | 36 + docs/content/en/methods/site/GetPage.md | 105 + docs/content/en/methods/site/Home.md | 39 + docs/content/en/methods/site/IsDefault.md | 50 + docs/content/en/methods/site/Language.md | 104 + .../content/en/methods/site/LanguagePrefix.md | 51 + docs/content/en/methods/site/Languages.md | 15 + docs/content/en/methods/site/Lastmod.md | 21 + docs/content/en/methods/site/MainSections.md | 54 + docs/content/en/methods/site/Menus.md | 89 + docs/content/en/methods/site/Pages.md | 22 + docs/content/en/methods/site/Param.md | 28 + docs/content/en/methods/site/Params.md | 42 + docs/content/en/methods/site/RegularPages.md | 32 + docs/content/en/methods/site/Role.md | 34 + docs/content/en/methods/site/Sections.md | 40 + docs/content/en/methods/site/Sites.md | 15 + docs/content/en/methods/site/Store.md | 112 + docs/content/en/methods/site/Taxonomies.md | 181 + docs/content/en/methods/site/Title.md | 22 + docs/content/en/methods/site/Version.md | 34 + docs/content/en/methods/site/_index.md | 8 + .../en/methods/taxonomy/Alphabetical.md | 69 + docs/content/en/methods/taxonomy/ByCount.md | 69 + docs/content/en/methods/taxonomy/Count.md | 22 + docs/content/en/methods/taxonomy/Get.md | 67 + docs/content/en/methods/taxonomy/Page.md | 24 + docs/content/en/methods/taxonomy/_index.md | 7 + docs/content/en/methods/time/Add.md | 20 + docs/content/en/methods/time/AddDate.md | 39 + docs/content/en/methods/time/After.md | 17 + docs/content/en/methods/time/Before.md | 16 + docs/content/en/methods/time/Day.md | 15 + docs/content/en/methods/time/Equal.md | 17 + docs/content/en/methods/time/Format.md | 89 + docs/content/en/methods/time/Hour.md | 15 + docs/content/en/methods/time/IsDST.md | 18 + docs/content/en/methods/time/IsZero.md | 18 + docs/content/en/methods/time/Local.md | 15 + docs/content/en/methods/time/Minute.md | 15 + docs/content/en/methods/time/Month.md | 24 + docs/content/en/methods/time/Nanosecond.md | 15 + docs/content/en/methods/time/Round.md | 21 + docs/content/en/methods/time/Second.md | 15 + docs/content/en/methods/time/Sub.md | 17 + docs/content/en/methods/time/Truncate.md | 19 + docs/content/en/methods/time/UTC.md | 14 + docs/content/en/methods/time/Unix.md | 19 + docs/content/en/methods/time/UnixMicro.md | 19 + docs/content/en/methods/time/UnixMilli.md | 19 + docs/content/en/methods/time/UnixNano.md | 19 + docs/content/en/methods/time/Weekday.md | 23 + docs/content/en/methods/time/Year.md | 15 + docs/content/en/methods/time/YearDay.md | 15 + docs/content/en/methods/time/_index.md | 7 + docs/content/en/news/_content.gotmpl | 31 + docs/content/en/news/_index.md | 9 + docs/content/en/quick-reference/_index.md | 8 + docs/content/en/quick-reference/emojis.md | 1679 +++ docs/content/en/quick-reference/functions.md | 8 + .../en/quick-reference/glob-patterns.md | 38 + .../en/quick-reference/glossary/_index.md | 20 + .../en/quick-reference/glossary/action.md | 5 + .../en/quick-reference/glossary/archetype.md | 7 + .../en/quick-reference/glossary/argument.md | 5 + .../en/quick-reference/glossary/array.md | 6 + .../glossary/asset-pipeline.md | 5 + .../en/quick-reference/glossary/bool.md | 5 + .../en/quick-reference/glossary/boolean.md | 5 + .../quick-reference/glossary/branch-bundle.md | 6 + .../en/quick-reference/glossary/branch.md | 5 + .../glossary/build-artifacts.md | 5 + .../en/quick-reference/glossary/build.md | 5 + .../en/quick-reference/glossary/bundle.md | 5 + .../en/quick-reference/glossary/cache.md | 5 + .../glossary/canonical-output-format.md | 14 + .../en/quick-reference/glossary/chain.md | 5 + .../en/quick-reference/glossary/cicd.md | 16 + .../en/quick-reference/glossary/cjk.md | 5 + .../en/quick-reference/glossary/cli.md | 5 + .../en/quick-reference/glossary/collection.md | 5 + .../en/quick-reference/glossary/component.md | 16 + .../glossary/content-adapter.md | 6 + .../glossary/content-dimension.md | 5 + .../glossary/content-format.md | 6 + .../quick-reference/glossary/content-type.md | 5 + .../quick-reference/glossary/content-view.md | 8 + .../en/quick-reference/glossary/context.md | 6 + .../glossary/default-language.md | 9 + .../quick-reference/glossary/default-role.md | 9 + .../quick-reference/glossary/default-site.md | 5 + .../glossary/default-sort-order.md | 15 + .../glossary/default-version.md | 9 + .../glossary/dependency-graph.md | 5 + .../en/quick-reference/glossary/dimension.md | 5 + .../en/quick-reference/glossary/duration.md | 5 + .../en/quick-reference/glossary/element.md | 5 + .../glossary/embedded-template.md | 5 + .../quick-reference/glossary/environment.md | 9 + .../en/quick-reference/glossary/field.md | 5 + .../en/quick-reference/glossary/flag.md | 6 + .../en/quick-reference/glossary/float.md | 6 + .../glossary/floating-point.md | 5 + .../en/quick-reference/glossary/fragment.md | 5 + .../quick-reference/glossary/front-matter.md | 6 + .../en/quick-reference/glossary/function.md | 6 + .../quick-reference/glossary/glob-pattern.md | 6 + .../en/quick-reference/glossary/glob-slice.md | 16 + .../glossary/global-resource.md | 5 + .../glossary/headless-bundle.md | 6 + .../en/quick-reference/glossary/i18n.md | 5 + .../en/quick-reference/glossary/iana.md | 6 + .../en/quick-reference/glossary/identifier.md | 7 + .../en/quick-reference/glossary/int.md | 5 + .../en/quick-reference/glossary/integer.md | 5 + .../en/quick-reference/glossary/interleave.md | 5 + .../glossary/internationalization.md | 5 + .../glossary/interpreted-string-literal.md | 6 + .../en/quick-reference/glossary/interval.md | 11 + .../en/quick-reference/glossary/kind.md | 5 + .../en/quick-reference/glossary/l10n.md | 5 + .../en/quick-reference/glossary/language.md | 7 + .../en/quick-reference/glossary/layout.md | 5 + .../quick-reference/glossary/leaf-bundle.md | 6 + .../en/quick-reference/glossary/lexer.md | 5 + .../en/quick-reference/glossary/list-page.md | 5 + .../quick-reference/glossary/localization.md | 6 + .../quick-reference/glossary/logical-path.md | 8 + .../quick-reference/glossary/logical-tree.md | 6 + .../en/quick-reference/glossary/map.md | 6 + .../glossary/markdown-attribute.md | 6 + .../en/quick-reference/glossary/marshal.md | 6 + .../en/quick-reference/glossary/media-type.md | 6 + .../en/quick-reference/glossary/method.md | 5 + .../en/quick-reference/glossary/module.md | 5 + .../en/quick-reference/glossary/mount.md | 7 + .../en/quick-reference/glossary/node.md | 5 + .../en/quick-reference/glossary/noop.md | 5 + .../en/quick-reference/glossary/object.md | 5 + .../glossary/ordered-taxonomy.md | 8 + .../quick-reference/glossary/output-format.md | 6 + .../quick-reference/glossary/page-bundle.md | 6 + .../glossary/page-collection.md | 5 + .../en/quick-reference/glossary/page-kind.md | 6 + .../quick-reference/glossary/page-matcher.md | 8 + .../quick-reference/glossary/page-relative.md | 7 + .../quick-reference/glossary/page-resource.md | 5 + .../en/quick-reference/glossary/pager.md | 5 + .../en/quick-reference/glossary/paginate.md | 5 + .../en/quick-reference/glossary/pagination.md | 6 + .../en/quick-reference/glossary/paginator.md | 5 + .../en/quick-reference/glossary/parameter.md | 5 + .../glossary/partial-decorator.md | 8 + .../en/quick-reference/glossary/partial.md | 5 + .../en/quick-reference/glossary/permalink.md | 5 + .../en/quick-reference/glossary/pipe.md | 5 + .../en/quick-reference/glossary/pipeline.md | 7 + .../en/quick-reference/glossary/pretty-url.md | 5 + .../glossary/primary-output-format.md | 10 + .../glossary/processable-image.md | 20 + .../en/quick-reference/glossary/project.md | 5 + .../en/quick-reference/glossary/publish.md | 5 + .../glossary/raw-string-literal.md | 6 + .../glossary/regular-expression.md | 7 + .../quick-reference/glossary/regular-page.md | 5 + .../glossary/relative-permalink.md | 5 + .../glossary/remote-resource.md | 5 + .../quick-reference/glossary/render-hook.md | 6 + .../quick-reference/glossary/resource-type.md | 5 + .../en/quick-reference/glossary/resource.md | 7 + .../en/quick-reference/glossary/role.md | 7 + .../quick-reference/glossary/rune-literal.md | 10 + .../en/quick-reference/glossary/rune.md | 11 + .../en/quick-reference/glossary/scalar.md | 5 + .../en/quick-reference/glossary/scope.md | 5 + .../quick-reference/glossary/section-page.md | 5 + .../en/quick-reference/glossary/section.md | 5 + .../en/quick-reference/glossary/seed.md | 6 + .../en/quick-reference/glossary/segment.md | 7 + .../glossary/server-relative.md | 9 + .../en/quick-reference/glossary/shortcode.md | 6 + .../quick-reference/glossary/site-relative.md | 7 + .../en/quick-reference/glossary/site-root.md | 13 + .../en/quick-reference/glossary/site.md | 5 + .../glossary/sites-complements.md | 5 + .../quick-reference/glossary/sites-matrix.md | 11 + .../en/quick-reference/glossary/slice.md | 6 + .../en/quick-reference/glossary/string.md | 5 + .../glossary/taxonomic-weight.md | 6 + .../glossary/taxonomy-object.md | 5 + .../quick-reference/glossary/taxonomy-page.md | 5 + .../en/quick-reference/glossary/taxonomy.md | 5 + .../glossary/template-action.md | 6 + .../en/quick-reference/glossary/template.md | 6 + .../en/quick-reference/glossary/term-page.md | 5 + .../en/quick-reference/glossary/term.md | 6 + .../en/quick-reference/glossary/theme.md | 5 + .../en/quick-reference/glossary/token.md | 15 + .../glossary/translation-table.md | 7 + .../en/quick-reference/glossary/type.md | 5 + .../en/quick-reference/glossary/ugly-url.md | 5 + .../glossary/unified-file-system.md | 5 + .../en/quick-reference/glossary/unmarshal.md | 6 + .../en/quick-reference/glossary/utc.md | 6 + .../en/quick-reference/glossary/variable.md | 5 + .../en/quick-reference/glossary/vendor.md | 7 + .../en/quick-reference/glossary/version.md | 9 + .../en/quick-reference/glossary/walk.md | 5 + .../en/quick-reference/glossary/weight.md | 5 + .../quick-reference/glossary/weighted-page.md | 5 + .../en/quick-reference/glossary/workspace.md | 7 + .../glossary/wrapper-component.md | 7 + .../en/quick-reference/glossary/zero-time.md | 7 + docs/content/en/quick-reference/methods.md | 8 + .../en/quick-reference/page-collections.md | 38 + .../syntax-highlighting-styles.md | 36 + docs/content/en/render-hooks/_index.md | 8 + docs/content/en/render-hooks/blockquotes.md | 181 + docs/content/en/render-hooks/code-blocks.md | 135 + docs/content/en/render-hooks/headings.md | 70 + docs/content/en/render-hooks/images.md | 140 + docs/content/en/render-hooks/introduction.md | 86 + docs/content/en/render-hooks/links.md | 113 + docs/content/en/render-hooks/passthrough.md | 118 + docs/content/en/render-hooks/tables.md | 99 + docs/content/en/shortcodes/_index.md | 7 + docs/content/en/shortcodes/details.md | 74 + docs/content/en/shortcodes/figure.md | 109 + docs/content/en/shortcodes/highlight.md | 103 + docs/content/en/shortcodes/instagram.md | 42 + docs/content/en/shortcodes/param.md | 38 + docs/content/en/shortcodes/qr.md | 107 + docs/content/en/shortcodes/ref.md | 66 + docs/content/en/shortcodes/relref.md | 66 + docs/content/en/shortcodes/vimeo.md | 73 + docs/content/en/shortcodes/x.md | 54 + docs/content/en/shortcodes/youtube.md | 83 + docs/content/en/templates/404.md | 50 + docs/content/en/templates/_index.md | 8 + docs/content/en/templates/embedded.md | 332 + docs/content/en/templates/introduction.md | 581 + docs/content/en/templates/lookup-order.md | 95 + docs/content/en/templates/menu.md | 127 + .../templates/new-templatesystem-overview.md | 98 + docs/content/en/templates/pagination.md | 241 + .../en/templates/partial-decorators.md | 125 + docs/content/en/templates/robots.md | 51 + docs/content/en/templates/rss.md | 75 + docs/content/en/templates/shortcode.md | 340 + docs/content/en/templates/sitemap.md | 55 + docs/content/en/templates/types.md | 410 + docs/content/en/tools/_index.md | 7 + docs/content/en/tools/editors.md | 62 + docs/content/en/tools/front-ends.md | 51 + docs/content/en/tools/migrations.md | 129 + docs/content/en/tools/other.md | 29 + docs/content/en/tools/search.md | 73 + docs/content/en/troubleshooting/_index.md | 8 + .../content/en/troubleshooting/audit/index.md | 69 + .../troubleshooting/audit/screen-capture.png | Bin 0 -> 45665 bytes .../content/en/troubleshooting/deprecation.md | 51 + docs/content/en/troubleshooting/faq.md | 112 + docs/content/en/troubleshooting/inspection.md | 44 + docs/content/en/troubleshooting/logging.md | 65 + .../content/en/troubleshooting/performance.md | 103 + docs/data/articles.toml | 731 ++ docs/data/docs.yaml | 4992 +++++++++ docs/data/embedded_template_urls.toml | 42 + docs/data/homepagetweets.toml | 265 + docs/data/keywords.yaml | 17 + docs/data/page_filters.yaml | 93 + docs/data/sponsors.toml | 22 + docs/go.mod | 3 + docs/hugo.toml | 191 + docs/hugo.work | 3 + docs/hugoreleaser.yaml | 29 + docs/layouts/404.html | 22 + docs/layouts/_markup/render-blockquote.html | 33 + docs/layouts/_markup/render-codeblock.html | 113 + docs/layouts/_markup/render-link.html | 300 + docs/layouts/_markup/render-passthrough.html | 9 + docs/layouts/_markup/render-table.html | 31 + .../_partials/docs/functions-aliases.html | 12 + .../_partials/docs/functions-return-type.html | 6 + .../_partials/docs/functions-signatures.html | 12 + .../helpers/debug/list-item-metadata.html | 23 + .../helpers/funcs/color-from-string.html | 25 + .../helpers/funcs/get-github-info.html | 28 + .../helpers/funcs/get-remote-data.html | 23 + docs/layouts/_partials/helpers/gtag.html | 25 + docs/layouts/_partials/helpers/linkcss.html | 28 + docs/layouts/_partials/helpers/linkjs.html | 17 + docs/layouts/_partials/helpers/picture.html | 27 + .../helpers/validation/validate-keywords.html | 21 + .../_partials/layouts/blocks/alert.html | 25 + .../layouts/blocks/feature-state.html | 98 + .../_partials/layouts/blocks/modal.html | 30 + .../_partials/layouts/breadcrumbs.html | 44 + docs/layouts/_partials/layouts/date.html | 5 + .../layouts/_partials/layouts/docsheader.html | 9 + docs/layouts/_partials/layouts/footer.html | 72 + .../_partials/layouts/head/head-js.html | 8 + docs/layouts/_partials/layouts/head/head.html | 41 + .../layouts/head/speculationrules.html | 17 + .../_partials/layouts/header/githubstars.html | 16 + .../_partials/layouts/header/header.html | 83 + .../_partials/layouts/header/mastodon.html | 29 + .../_partials/layouts/header/mobilemenu.html | 106 + docs/layouts/_partials/layouts/header/qr.html | 26 + .../_partials/layouts/header/theme.html | 35 + .../_partials/layouts/home/features.html | 52 + .../_partials/layouts/home/opensource.html | 110 + .../_partials/layouts/home/sponsors.html | 44 + .../_partials/layouts/hooks/body-end.html | 3 + .../layouts/hooks/body-main-start.html | 8 + .../_partials/layouts/hooks/body-start.html | 0 docs/layouts/_partials/layouts/icons.html | 84 + .../_partials/layouts/in-this-section.html | 34 + docs/layouts/_partials/layouts/page-edit.html | 27 + docs/layouts/_partials/layouts/related.html | 20 + .../_partials/layouts/search/algolialogo.html | 45 + .../_partials/layouts/search/button.html | 22 + .../_partials/layouts/search/input.html | 4 + .../_partials/layouts/search/results.html | 91 + docs/layouts/_partials/layouts/templates.html | 7 + docs/layouts/_partials/layouts/toc.html | 46 + .../opengraph/get-featured-image.html | 26 + .../_partials/opengraph/opengraph.html | 84 + docs/layouts/_shortcodes/chroma-lexers.html | 27 + docs/layouts/_shortcodes/code-toggle.html | 119 + .../_shortcodes/current-go-version.html | 5 + docs/layouts/_shortcodes/datatable.html | 38 + docs/layouts/_shortcodes/deprecated-in.html | 29 + docs/layouts/_shortcodes/eturl.html | 25 + docs/layouts/_shortcodes/get-page-desc.html | 18 + docs/layouts/_shortcodes/glossary-term.html | 21 + docs/layouts/_shortcodes/glossary.html | 61 + docs/layouts/_shortcodes/hl.html | 13 + docs/layouts/_shortcodes/img.html | 390 + docs/layouts/_shortcodes/imgproc.html | 38 + docs/layouts/_shortcodes/include.html | 19 + .../_shortcodes/module-mounts-note.html | 2 + docs/layouts/_shortcodes/new-in.html | 29 + .../_shortcodes/newtemplatesystem.html | 11 + .../_shortcodes/per-lang-config-keys.html | 70 + docs/layouts/_shortcodes/quick-reference.html | 38 + .../render-list-of-pages-in-section.html | 76 + .../render-table-of-pages-in-section.html | 88 + .../_shortcodes/root-configuration-keys.html | 44 + .../syntax-highlighting-styles.html | 73 + docs/layouts/baseof.html | 77 + docs/layouts/home.headers | 5 + docs/layouts/home.html | 52 + docs/layouts/home.redir | 6 + docs/layouts/list.html | 69 + docs/layouts/list.rss.xml | 33 + docs/layouts/single.html | 80 + docs/netlify.toml | 55 + docs/package.json | 22 + docs/static/android-chrome-144x144.png | Bin 0 -> 7612 bytes docs/static/android-chrome-192x192.png | Bin 0 -> 10264 bytes docs/static/android-chrome-256x256.png | Bin 0 -> 15088 bytes docs/static/android-chrome-36x36.png | Bin 0 -> 1592 bytes docs/static/android-chrome-48x48.png | Bin 0 -> 2038 bytes docs/static/android-chrome-72x72.png | Bin 0 -> 3467 bytes docs/static/android-chrome-96x96.png | Bin 0 -> 4747 bytes docs/static/apple-touch-icon.png | Bin 0 -> 6238 bytes docs/static/favicon-16x16.png | Bin 0 -> 1000 bytes docs/static/favicon-32x32.png | Bin 0 -> 1648 bytes docs/static/favicon.ico | Bin 0 -> 15086 bytes .../fonts/Mulish-Italic-VariableFont_wght.ttf | Bin 0 -> 219768 bytes .../static/fonts/Mulish-VariableFont_wght.ttf | Bin 0 -> 212500 bytes docs/static/images/gopher-hero.svg | 58 + docs/static/images/gopher-side_color.svg | 31 + docs/static/images/hugo-logo-wide.svg | 7 + docs/static/img/examples/trees.svg | 104 + docs/static/img/hugo-logo-med.png | Bin 0 -> 17402 bytes docs/static/img/hugo-logo.png | Bin 0 -> 10003 bytes docs/static/img/hugo.png | Bin 0 -> 18210 bytes docs/static/img/hugoSM.png | Bin 0 -> 1869 bytes docs/static/manifest.json | 45 + docs/static/mstile-144x144.png | Bin 0 -> 6225 bytes docs/static/mstile-150x150.png | Bin 0 -> 6020 bytes docs/static/mstile-310x310.png | Bin 0 -> 12885 bytes docs/static/safari-pinned-tab.svg | 22 + docs/static/shared/examples/data/books.json | 55 + .../interpreting-the-french-revolution.webp | Bin 0 -> 12852 bytes .../examples/images/les-misérables.webp | Bin 0 -> 13520 bytes .../the-ancien-régime-and-the-revolution.webp | Bin 0 -> 6946 bytes .../images/the-hunchback-of-notre-dame.webp | Bin 0 -> 23780 bytes docshelper/docs.go | 47 + go.mod | 189 + go.sum | 880 ++ helpers/content.go | 185 + helpers/content_test.go | 144 + helpers/docshelper.go | 63 + helpers/emoji.go | 86 + helpers/emoji_test.go | 65 + helpers/general.go | 215 + helpers/general_test.go | 165 + helpers/path.go | 342 + helpers/path_test.go | 414 + helpers/pathspec.go | 69 + helpers/processing_stats.go | 111 + helpers/testhelpers_test.go | 49 + helpers/url.go | 183 + helpers/url_test.go | 283 + htesting/hqt/checkers.go | 207 + htesting/test_helpers.go | 193 + htesting/test_helpers_test.go | 29 + hugofs/component_fs.go | 448 + hugofs/createcounting_fs.go | 105 + hugofs/decorators.go | 170 + hugofs/dirsmerger.go | 43 + hugofs/fileinfo.go | 439 + hugofs/fileinfo_test.go | 47 + hugofs/filename_filter_fs.go | 172 + hugofs/filename_filter_fs_test.go | 83 + hugofs/files/classifier.go | 97 + hugofs/files/classifier_test.go | 37 + hugofs/fs.go | 289 + hugofs/fs_test.go | 50 + hugofs/glob.go | 90 + hugofs/glob_test.go | 79 + hugofs/hasbytes_fs.go | 102 + hugofs/hashing_fs.go | 95 + hugofs/hashing_fs_test.go | 75 + hugofs/hglob/filename_filter.go | 208 + hugofs/hglob/filename_filter_test.go | 75 + hugofs/hglob/glob.go | 195 + hugofs/hglob/glob_test.go | 102 + hugofs/hugofs_integration_test.go | 149 + hugofs/noop_fs.go | 129 + hugofs/nosymlinks_fs.go | 52 + hugofs/openfiles_fs.go | 110 + hugofs/rootmapping_fs.go | 849 ++ hugofs/rootmapping_fs_test.go | 560 + hugofs/stacktracer_fs.go | 77 + hugofs/walk.go | 233 + hugofs/walk_test.go | 192 + hugolib/404_test.go | 115 + hugolib/alias.go | 198 + hugolib/alias_test.go | 867 ++ hugolib/breaking_changes_test.go | 14 + hugolib/cascade_test.go | 600 + hugolib/codeowners.go | 66 + hugolib/collections.go | 46 + hugolib/config.go | 184 + hugolib/config_legacy1_test.go | 42 + hugolib/config_test.go | 1968 ++++ hugolib/configdir_test.go | 185 + hugolib/content_factory.go | 173 + hugolib/content_map.go | 502 + hugolib/content_map_page.go | 1243 +++ hugolib/content_map_page_assembler.go | 1495 +++ hugolib/content_map_page_contentnode.go | 465 + .../content_map_page_contentnodeshifter.go | 188 + hugolib/content_map_test.go | 516 + hugolib/content_render_hooks_test.go | 465 + hugolib/datafiles_test.go | 84 + hugolib/dates_test.go | 341 + hugolib/disableKinds_test.go | 362 + hugolib/doctree/nodeshiftree_test.go | 394 + hugolib/doctree/nodeshifttree.go | 670 ++ hugolib/doctree/nodetransformstate_string.go | 28 + hugolib/doctree/simpletree.go | 220 + hugolib/doctree/simpletree_test.go | 53 + hugolib/doctree/support.go | 289 + hugolib/doctree/treeshifttree.go | 145 + hugolib/doctree/treeshifttree_test.go | 56 + hugolib/fileInfo.go | 46 + hugolib/fileInfo_test.go | 31 + hugolib/filesystems/basefs.go | 918 ++ hugolib/filesystems/basefs_test.go | 682 ++ hugolib/frontmatter_test.go | 39 + hugolib/gitinfo.go | 261 + hugolib/gitinfo_github_test.go | 130 + hugolib/hugo_modules_test.go | 367 + hugolib/hugo_sites.go | 817 ++ hugolib/hugo_sites_build.go | 1363 +++ hugolib/hugo_sites_build_errors_test.go | 639 ++ hugolib/hugo_sites_build_test.go | 212 + hugolib/hugo_sites_multihost_test.go | 286 + hugolib/hugo_smoke_test.go | 870 ++ hugolib/hugolib_integration_test.go | 129 + hugolib/integrationtest_builder.go | 1248 +++ hugolib/language_content_dir_test.go | 305 + hugolib/language_test.go | 138 + hugolib/menu_test.go | 715 ++ hugolib/minify_publisher_test.go | 59 + hugolib/mount_filters_test.go | 96 + hugolib/page.go | 993 ++ hugolib/page__common.go | 115 + hugolib/page__content.go | 1179 ++ hugolib/page__data.go | 62 + hugolib/page__fragments_test.go | 100 + hugolib/page__menus.go | 83 + hugolib/page__meta.go | 1080 ++ hugolib/page__meta_test.go | 82 + hugolib/page__new.go | 120 + hugolib/page__output.go | 187 + hugolib/page__paginator.go | 112 + hugolib/page__paths.go | 209 + hugolib/page__per_output.go | 506 + hugolib/page__position.go | 69 + hugolib/page__ref.go | 114 + hugolib/page__tree.go | 212 + hugolib/page_kinds.go | 18 + hugolib/page_permalink_test.go | 203 + hugolib/page_resource_metadata_test.go | 58 + hugolib/page_test.go | 2343 ++++ hugolib/page_unwrap.go | 53 + hugolib/page_unwrap_test.go | 38 + hugolib/pagebundler_test.go | 830 ++ hugolib/pagecollections.go | 256 + hugolib/pagecollections_test.go | 788 ++ hugolib/pages_capture.go | 392 + hugolib/pages_language_merge_test.go | 215 + hugolib/pages_test.go | 123 + hugolib/pagesfromdata/pagesfromgotmpl.go | 378 + .../pagesfromgotmpl_integration_test.go | 963 ++ hugolib/pagesfromdata/pagesfromgotmpl_test.go | 32 + hugolib/paginator_test.go | 221 + hugolib/params_test.go | 79 + hugolib/paths/paths.go | 163 + hugolib/permalinker.go | 22 + hugolib/prune_resources.go | 19 + hugolib/rebuild_test.go | 2172 ++++ hugolib/rendershortcodes_test.go | 585 + hugolib/renderstring_test.go | 207 + hugolib/resource_chain_test.go | 1400 +++ hugolib/robotstxt_test.go | 49 + hugolib/roles/roles.go | 217 + hugolib/roles/roles_integration_test.go | 124 + hugolib/rss_test.go | 109 + hugolib/securitypolicies_test.go | 326 + hugolib/segments/segments.go | 321 + hugolib/segments/segments_integration_test.go | 189 + hugolib/segments/segments_test.go | 150 + hugolib/shortcode.go | 781 ++ hugolib/shortcode_page.go | 131 + hugolib/shortcode_test.go | 23 + hugolib/site.go | 1809 +++ hugolib/siteJSONEncode_test.go | 43 + hugolib/site_render.go | 388 + hugolib/site_sections.go | 30 + hugolib/site_sections_test.go | 184 + hugolib/site_sites_test.go | 208 + hugolib/site_stats_test.go | 133 + hugolib/site_test.go | 585 + hugolib/site_url_test.go | 35 + hugolib/sitemap_test.go | 231 + hugolib/sitesmatrix/dimensions.go | 309 + hugolib/sitesmatrix/dimensions_test.go | 72 + .../sitematrix_integration_test.go | 1955 ++++ hugolib/sitesmatrix/vectorstores.go | 1060 ++ hugolib/sitesmatrix/vectorstores_test.go | 522 + hugolib/taxonomy_test.go | 899 ++ hugolib/template_test.go | 588 + hugolib/testdata/cities.csv | 130 + hugolib/testdata/fakejson.json | Bin 0 -> 42 bytes hugolib/testdata/fruits.json | 5 + hugolib/testdata/redis.cn.md | 697 ++ hugolib/testdata/sunset.jpg | Bin 0 -> 90587 bytes hugolib/testdata/what-is-markdown.md | 9702 +++++++++++++++++ hugolib/testsite/.gitignore | 1 + hugolib/testsite/CODEOWNERS | 1 + hugolib/testsite/content/first-post.md | 4 + hugolib/testsite/content_nn/first-post.md | 4 + hugolib/versions/versions.go | 230 + hugolib/versions/versions_integration_test.go | 37 + hugoreleaser.env | 167 + hugoreleaser.yaml | 377 + identity/finder.go | 337 + identity/finder_test.go | 58 + identity/identity.go | 515 + identity/identity_test.go | 200 + identity/identitytesting/identitytesting.go | 5 + identity/predicate_identity.go | 60 + identity/predicate_identity_test.go | 41 + identity/question.go | 57 + identity/question_test.go | 38 + internal/js/api.go | 51 + internal/js/esbuild/batch-esm-runner.gotmpl | 20 + internal/js/esbuild/batch.go | 1441 +++ internal/js/esbuild/batch_integration_test.go | 735 ++ internal/js/esbuild/build.go | 253 + internal/js/esbuild/helpers.go | 15 + internal/js/esbuild/options.go | 505 + internal/js/esbuild/options_test.go | 272 + internal/js/esbuild/resolve.go | 435 + internal/js/esbuild/resolve_test.go | 86 + internal/js/esbuild/sourcemap.go | 86 + internal/warpc/avif.go | 426 + internal/warpc/avif_color_test.go | 64 + internal/warpc/avif_integration_test.go | 117 + internal/warpc/buildjs.sh | 4 + internal/warpc/buildwebp.sh | 4 + internal/warpc/deps/parson/package.json | 12 + internal/warpc/deps/parson/parson.c | 2487 +++++ internal/warpc/deps/parson/parson.h | 274 + internal/warpc/genavif/.gitignore | 4 + internal/warpc/genavif/Makefile | 170 + internal/warpc/genavif/avif.c | 895 ++ internal/warpc/genavif/go.mod | 3 + internal/warpc/genavif/wasi-sdk-cross.txt | 21 + internal/warpc/genjs/main.go | 68 + internal/warpc/genwebp/.gitignore | 2 + internal/warpc/genwebp/Makefile | 81 + internal/warpc/genwebp/deps/clib/Makefile | 97 + internal/warpc/genwebp/go.mod | 0 internal/warpc/genwebp/webp.c | 839 ++ internal/warpc/js/.gitignore | 2 + internal/warpc/js/common.js | 83 + internal/warpc/js/greet.bundle.js | 2 + internal/warpc/js/greet.js | 9 + internal/warpc/js/package.json | 14 + internal/warpc/js/renderkatex.bundle.js | 262 + internal/warpc/js/renderkatex.js | 25 + internal/warpc/katex.go | 76 + internal/warpc/warpc.go | 871 ++ internal/warpc/warpc_test.go | 473 + internal/warpc/wasm/avif.wasm | Bin 0 -> 7271589 bytes internal/warpc/wasm/greet.wasm | Bin 0 -> 2540 bytes internal/warpc/wasm/quickjs.wasm | Bin 0 -> 1517209 bytes internal/warpc/wasm/renderkatex.wasm | Bin 0 -> 537651 bytes internal/warpc/wasm/webp.wasm | Bin 0 -> 682686 bytes internal/warpc/watchjs.sh | 5 + internal/warpc/watchwebp.sh | 7 + internal/warpc/webp.go | 438 + internal/warpc/webp_integration_test.go | 202 + langs/config.go | 263 + langs/i18n/i18n.go | 210 + langs/i18n/i18n_integration_test.go | 222 + langs/i18n/i18n_test.go | 519 + langs/i18n/translationProvider.go | 189 + langs/language.go | 264 + langs/language_test.go | 103 + langs/languages_integration_test.go | 380 + livereload/connection.go | 66 + livereload/gen/livereload-hugo-plugin.js | 44 + livereload/gen/main.go | 61 + livereload/hub.go | 56 + livereload/livereload.go | 152 + livereload/livereload.js | 3795 +++++++ livereload/livereload.min.js | 7 + magefile.go | 370 + main.go | 35 + main_test.go | 484 + main_withdeploy_off_test.go | 28 + main_withdeploy_test.go | 28 + .../asciidocext/asciidoc_integration_test.go | 1155 ++ .../asciidocext/asciidocext_config/config.go | 79 + markup/asciidocext/convert.go | 66 + markup/asciidocext/convert_test.go | 406 + markup/asciidocext/internal/converter.go | 411 + markup/blackfriday/anchors.go | 39 + markup/converter/converter.go | 161 + markup/converter/hooks/hooks.go | 231 + markup/goldmark/autoid.go | 159 + markup/goldmark/autoid_test.go | 142 + markup/goldmark/blockquotes/blockquotes.go | 196 + .../blockquotes_integration_test.go | 285 + .../goldmark/blockquotes/blockquotes_test.go | 68 + .../codeblocks/codeblocks_integration_test.go | 439 + markup/goldmark/codeblocks/render.go | 171 + markup/goldmark/convert.go | 361 + markup/goldmark/convert_test.go | 755 ++ markup/goldmark/goldmark_config/config.go | 320 + markup/goldmark/goldmark_integration_test.go | 1070 ++ markup/goldmark/hugocontext/hugocontext.go | 335 + .../goldmark/hugocontext/hugocontext_test.go | 34 + .../images/images_integration_test.go | 88 + markup/goldmark/images/transform.go | 75 + .../extensions/attributes/attributes.go | 204 + .../attributes/attributes_integration_test.go | 110 + markup/goldmark/internal/render/context.go | 264 + markup/goldmark/passthrough/passthrough.go | 166 + .../passthrough_integration_test.go | 62 + markup/goldmark/render_hooks.go | 525 + markup/goldmark/tables/tables.go | 175 + .../tables/tables_integration_test.go | 185 + markup/goldmark/toc.go | 214 + markup/goldmark/toc_integration_test.go | 400 + markup/highlight/chromalexers/chromalexers.go | 34 + markup/highlight/config.go | 345 + markup/highlight/config_test.go | 73 + markup/highlight/highlight.go | 362 + .../highlight/highlight_integration_test.go | 248 + markup/highlight/highlight_test.go | 159 + markup/internal/attributes/attributes.go | 223 + markup/internal/external.go | 72 + markup/markup.go | 152 + markup/markup_config/config.go | 135 + markup/markup_config/config_test.go | 92 + markup/markup_test.go | 47 + markup/org/convert.go | 74 + markup/org/convert_test.go | 41 + markup/pandoc/convert.go | 88 + markup/pandoc/convert_integration_test.go | 97 + markup/pandoc/convert_test.go | 44 + markup/pandoc/testdata/foo.bib | 10 + markup/pandoc/testdata/ieee.csl | 512 + markup/rst/convert.go | 148 + markup/rst/convert_test.go | 47 + markup/rst/rst_config/config.go | 35 + markup/rst/rst_integration_test.go | 105 + markup/tableofcontents/tableofcontents.go | 277 + .../tableofcontents_integration_test.go | 138 + .../tableofcontents/tableofcontents_test.go | 226 + media/builtin.go | 178 + media/config.go | 277 + media/config_test.go | 155 + media/mediaType.go | 401 + media/mediaType_test.go | 216 + media/testdata/fake.js | Bin 0 -> 13327 bytes media/testdata/fake.png | 3 + media/testdata/reosurce.otf | Bin 0 -> 696 bytes media/testdata/resource.bmp | Bin 0 -> 65334 bytes media/testdata/resource.css | 8 + media/testdata/resource.csv | 130 + media/testdata/resource.gif | Bin 0 -> 161 bytes media/testdata/resource.ics | 24 + media/testdata/resource.jpe | Bin 0 -> 116955 bytes media/testdata/resource.jpg | Bin 0 -> 116955 bytes media/testdata/resource.js | 3 + media/testdata/resource.json | 14 + media/testdata/resource.pdf | 198 + media/testdata/resource.png | Bin 0 -> 13327 bytes media/testdata/resource.rss | 20 + media/testdata/resource.sass | 6 + media/testdata/resource.scss | 7 + media/testdata/resource.svg | 5 + media/testdata/resource.ttf | Bin 0 -> 552 bytes media/testdata/resource.webp | Bin 0 -> 59826 bytes media/testdata/resource.xml | 7 + merge-release.sh | 29 + metrics/metrics.go | 292 + metrics/metrics_test.go | 68 + minifiers/config.go | 169 + minifiers/config_test.go | 128 + minifiers/minifiers.go | 131 + minifiers/minifiers_test.go | 170 + modules/client.go | 1092 ++ modules/client_test.go | 272 + modules/collect.go | 930 ++ modules/collect_test.go | 110 + modules/config.go | 517 + modules/config_integration_test.go | 69 + modules/config_test.go | 173 + modules/module.go | 250 + modules/modules_integration_test.go | 49 + modules/mount_filters_integration_test.go | 96 + modules/npm/package_builder.go | 646 ++ .../npm/package_builder_integration_test.go | 105 + modules/npm/package_builder_test.go | 101 + navigation/menu.go | 317 + navigation/menu_cache.go | 87 + navigation/menu_cache_test.go | 80 + navigation/pagemenus.go | 224 + output/config.go | 143 + output/config_test.go | 98 + output/docshelper.go | 23 + output/outputFormat.go | 334 + output/outputFormat_integration_test.go | 30 + output/outputFormat_test.go | 120 + parser/frontmatter.go | 115 + parser/frontmatter_test.go | 78 + parser/lowercase_camel_json.go | 142 + parser/lowercase_camel_json_test.go | 90 + parser/metadecoders/decoder.go | 426 + .../metadecoders/decoder_integration_test.go | 79 + parser/metadecoders/decoder_test.go | 283 + parser/metadecoders/encoder.go | 27 + parser/metadecoders/format.go | 114 + parser/metadecoders/format_test.go | 67 + parser/pageparser/doc.go | 18 + parser/pageparser/item.go | 221 + parser/pageparser/item_test.go | 263 + parser/pageparser/itemtype_string.go | 42 + parser/pageparser/pagelexer.go | 535 + parser/pageparser/pagelexer_intro.go | 161 + parser/pageparser/pagelexer_intro_test.go | 46 + parser/pageparser/pagelexer_shortcode.go | 367 + parser/pageparser/pagelexer_test.go | 28 + parser/pageparser/pageparser.go | 261 + parser/pageparser/pageparser_intro_test.go | 184 + .../pageparser/pageparser_shortcode_test.go | 293 + parser/pageparser/pageparser_test.go | 150 + publisher/htmlElementsCollector.go | 555 + publisher/htmlElementsCollector_test.go | 278 + publisher/publisher.go | 190 + pull-docs.sh | 7 + related/inverted_index.go | 630 ++ related/inverted_index_test.go | 414 + related/related_integration_test.go | 188 + releaser/releaser.go | 244 + resources/assets/sunset.jpg | Bin 0 -> 90587 bytes resources/docs.go | 15 + resources/image.go | 536 + resources/image_cache.go | 126 + resources/image_test.go | 554 + resources/images/auto_orient.go | 54 + resources/images/codec.go | 433 + resources/images/color.go | 202 + resources/images/color_test.go | 125 + resources/images/config.go | 794 ++ resources/images/config_test.go | 218 + resources/images/dither.go | 71 + resources/images/filters.go | 404 + resources/images/filters_test.go | 32 + resources/images/image.go | 525 + resources/images/image_resource.go | 74 + .../images/images_golden_integration_test.go | 652 ++ resources/images/images_integration_test.go | 184 + resources/images/imagetesting/testing.go | 250 + resources/images/mask.go | 63 + resources/images/meta/exif_test.go | 304 + resources/images/meta/meta.go | 530 + .../images/meta/meta_integration_test.go | 333 + resources/images/opacity.go | 39 + resources/images/overlay.go | 43 + resources/images/padding.go | 50 + resources/images/process.go | 43 + resources/images/resampling.go | 214 + resources/images/smartcrop.go | 134 + resources/images/smartcrop_test.go | 72 + .../images_golden/filters/mask/blue.jpg | Bin 0 -> 7875 bytes .../filters/mask/transparant.png | Bin 0 -> 150252 bytes .../images_golden/filters/mask/wide.jpg | Bin 0 -> 8228 bytes .../images_golden/filters/mask/yellow.jpg | Bin 0 -> 8829 bytes .../images_golden/filters/mask2/green.jpg | Bin 0 -> 8821 bytes .../images_golden/filters/mask2/pink.jpg | Bin 0 -> 6144 bytes .../filters/misc/brightness-40.jpg | Bin 0 -> 10285 bytes .../filters/misc/colorbalance-180-50-20.jpg | Bin 0 -> 13669 bytes .../filters/misc/contrast-50.jpg | Bin 0 -> 7539 bytes .../filters/misc/dither-default.jpg | Bin 0 -> 59275 bytes .../filters/misc/gamma-1.667.jpg | Bin 0 -> 12382 bytes .../filters/misc/gaussianblur-5.jpg | Bin 0 -> 6800 bytes .../misc/grayscale+colorize-180-50-20.jpg | Bin 0 -> 10172 bytes .../images_golden/filters/misc/grayscale.jpg | Bin 0 -> 10007 bytes .../images_golden/filters/misc/hue--15.jpg | Bin 0 -> 10697 bytes .../images_golden/filters/misc/invert.jpg | Bin 0 -> 10722 bytes .../filters/misc/opacity-0.65.jpg | Bin 0 -> 9736 bytes .../filters/misc/overlay-20-20.jpg | Bin 0 -> 12403 bytes .../filters/misc/padding-20-40-#976941.jpg | Bin 0 -> 12700 bytes .../filters/misc/pixelate-10.jpg | Bin 0 -> 9721 bytes .../images_golden/filters/misc/rotate270.jpg | Bin 0 -> 688 bytes .../filters/misc/saturation-65.jpg | Bin 0 -> 10924 bytes .../images_golden/filters/misc/sepia-80.jpg | Bin 0 -> 10576 bytes .../filters/misc/sigmoid-0.6--4.jpg | Bin 0 -> 11805 bytes .../images_golden/filters/misc/text.jpg | Bin 0 -> 12629 bytes .../filters/misc/unsharpmask.jpg | Bin 0 -> 10949 bytes .../filters/text/text_alignx-center.jpg | Bin 0 -> 44009 bytes .../text/text_alignx-center_aligny-bottom.jpg | Bin 0 -> 64740 bytes .../text/text_alignx-center_aligny-center.jpg | Bin 0 -> 59756 bytes .../filters/text/text_alignx-left.jpg | Bin 0 -> 43854 bytes .../filters/text/text_alignx-right.jpg | Bin 0 -> 44934 bytes .../losslessvsquality/resize-300x300-jpeg.jpg | Bin 0 -> 12186 bytes .../resize-300x300-webp-lossy.webp | Bin 0 -> 7402 bytes .../resize-300x300-webp.webp | Bin 0 -> 59014 bytes .../methods/crop-sunsetjpg-200x200.jpg | Bin 0 -> 2518 bytes .../crop-sunsetjpg-350x400-center-q20.jpg | Bin 0 -> 4027 bytes .../crop-sunsetjpg-350x400-center-r90.jpg | Bin 0 -> 8498 bytes .../methods/crop-sunsetjpg-350x400-center.jpg | Bin 0 -> 8624 bytes .../methods/crop-sunsetjpg-350x400-smart.jpg | Bin 0 -> 8633 bytes .../methods/fill-sunsetjpg-90x120-left.jpg | Bin 0 -> 1672 bytes .../methods/fill-sunsetjpg-90x120-right.jpg | Bin 0 -> 1631 bytes .../methods/fit-sunsetjpg-200x200.jpg | Bin 0 -> 2879 bytes .../resize-gopherpng-100x-03fc56-jpg.jpg | Bin 0 -> 3186 bytes .../methods/resize-gopherpng-100x-fc03ec.png | Bin 0 -> 3209 bytes .../methods/resize-gopherpng-100x.png | Bin 0 -> 3528 bytes .../methods/resize-sunsetjpg-300x.jpg | Bin 0 -> 4952 bytes .../methods/resize-sunsetjpg-x200.jpg | Bin 0 -> 5430 bytes .../images_golden/process/avif/avif-q79.avif | Bin 0 -> 41594 bytes .../images_golden/process/avif/avif-q80.avif | Bin 0 -> 37941 bytes .../images_golden/process/avif/avif.avif | Bin 0 -> 9233 bytes .../process/avif/crop-300x300-smart-avif.avif | Bin 0 -> 3752 bytes .../avif/crop-300x300-smart-ff9999-avif.avif | Bin 0 -> 1097 bytes .../images_golden/process/avif/gif.gif | Bin 0 -> 140242 bytes .../images_golden/process/avif/q50.avif | Bin 0 -> 14957 bytes .../images_golden/process/avif/r1.avif | Bin 0 -> 25383 bytes .../images_golden/process/avif/r2.avif | Bin 0 -> 21295 bytes .../process/avifstraws/resize-900x.avif | Bin 0 -> 38627 bytes .../process/misc/crop-500x200-smart.jpg | Bin 0 -> 8185 bytes .../process/misc/fill-500x200-smart.jpg | Bin 0 -> 8463 bytes .../process/misc/fit-500x200-smart.jpg | Bin 0 -> 5647 bytes .../process/misc/resize-100x100-gif.gif | Bin 0 -> 23330 bytes .../process/misc/resize-100x100-r180.png | Bin 0 -> 3092 bytes .../misc/resize-300x300-jpg-b31280.jpg | Bin 0 -> 11406 bytes .../process/webp/crop-300x300-gif.gif | Bin 0 -> 180003 bytes .../webp/crop-300x300-smart-ff9999.webp | Bin 0 -> 724 bytes .../process/webp/crop-300x300-smart.webp | Bin 0 -> 8618 bytes .../process/webp/crop-300x300.webp | Bin 0 -> 91902 bytes .../process/webp/crop-500x200-smart-webp.webp | Bin 0 -> 3412 bytes .../process/webp/fit-300x400-webp.webp | Bin 0 -> 1928 bytes .../process/webp/fit-400x500-webp.webp | Bin 0 -> 3568 bytes .../images_golden/process/webp/gif.gif | Bin 0 -> 13447 bytes .../images_golden/process/webp/png.png | Bin 0 -> 65276 bytes .../webp/resize-300x300-webp-drawing.webp | Bin 0 -> 3570 bytes .../webp/resize-300x300-webp-icon.webp | Bin 0 -> 3724 bytes .../webp/resize-300x300-webp-lossless.webp | Bin 0 -> 59014 bytes .../process/webp/resize-300x300-webp-q1.webp | Bin 0 -> 754 bytes .../webp/resize-300x300-webp-q100.webp | Bin 0 -> 21756 bytes .../process/webp/resize-300x300-webp-q33.webp | Bin 0 -> 1764 bytes .../webp/resize-300x300-webp-q50-drawing.webp | Bin 0 -> 2514 bytes .../process/webp/resize-300x300-webp-q75.webp | Bin 0 -> 3376 bytes .../process/webp/resize-300x300-webp.webp | Bin 0 -> 26710 bytes .../process/webp/resize-300x300.gif | Bin 0 -> 152379 bytes .../process/webp/resize-400x.webp | Bin 0 -> 7698 bytes .../animation/anim_hu_58eb49733894e7ce.gif | Bin 0 -> 51965 bytes .../animation/anim_hu_edc2f24aaad2cee6.webp | Bin 0 -> 23356 bytes .../animation/giphy_hu_bb052284cc220165.webp | Bin 0 -> 8408 bytes .../animation/giphy_hu_c6b8060edf0363b1.gif | Bin 0 -> 23330 bytes resources/images/text.go | 150 + resources/internal/key.go | 42 + resources/internal/key_test.go | 36 + resources/internal/resourcepaths.go | 107 + resources/jsconfig/jsconfig.go | 78 + resources/jsconfig/jsconfig_test.go | 40 + resources/kinds/kinds.go | 119 + resources/kinds/kinds_test.go | 40 + resources/page/hugoinfo.go | 176 + resources/page/hugoinfo_test.go | 35 + resources/page/page.go | 618 ++ resources/page/page_author.go | 47 + resources/page/page_data.go | 42 + resources/page/page_data_test.go | 55 + resources/page/page_generate/.gitignore | 1 + .../page_generate/generate_page_wrappers.go | 117 + resources/page/page_integration_test.go | 160 + resources/page/page_kinds.go | 14 + resources/page/page_lazy_contentprovider.go | 138 + resources/page/page_markup.go | 362 + .../page/page_markup_integration_test.go | 422 + resources/page/page_markup_test.go | 208 + resources/page/page_marshaljson.autogen.go | 97 + resources/page/page_matcher.go | 422 + resources/page/page_matcher_test.go | 249 + resources/page/page_nop.go | 597 + resources/page/page_outputformat.go | 115 + resources/page/page_paths.go | 482 + resources/page/page_paths_test.go | 54 + resources/page/page_wrappers.autogen.go | 16 + resources/page/pagegroup.go | 461 + resources/page/pagegroup_test.go | 470 + resources/page/pagemeta/page_frontmatter.go | 1083 ++ .../page/pagemeta/page_frontmatter_test.go | 218 + resources/page/pagemeta/pagemeta.go | 99 + .../pagemeta/pagemeta_integration_test.go | 142 + resources/page/pagemeta/pagemeta_test.go | 149 + resources/page/pages.go | 140 + resources/page/pages_cache.go | 136 + resources/page/pages_cache_test.go | 84 + resources/page/pages_language_merge.go | 63 + resources/page/pages_prev_next.go | 34 + .../page/pages_prev_next_integration_test.go | 82 + resources/page/pages_prev_next_test.go | 91 + resources/page/pages_related.go | 243 + resources/page/pages_related_test.go | 103 + resources/page/pages_sort.go | 447 + resources/page/pages_sort_search.go | 125 + resources/page/pages_sort_search_test.go | 122 + resources/page/pages_sort_test.go | 294 + resources/page/pages_test.go | 72 + resources/page/pagination.go | 409 + resources/page/pagination_test.go | 255 + resources/page/path_integration_test.go | 157 + resources/page/permalinks.go | 601 + resources/page/permalinks_integration_test.go | 772 ++ resources/page/permalinks_test.go | 325 + resources/page/site.go | 476 + resources/page/site_integration_test.go | 44 + resources/page/siteidentities/identities.go | 34 + resources/page/taxonomy.go | 177 + resources/page/taxonomy_integration_test.go | 81 + resources/page/testhelpers_test.go | 620 ++ resources/page/weighted.go | 138 + resources/page/zero_file.autogen.go | 16 + resources/post_publish.go | 35 + resources/postpub/fields.go | 58 + resources/postpub/fields_test.go | 47 + resources/postpub/postpub.go | 182 + resources/resource.go | 747 ++ resources/resource/dates.go | 58 + resources/resource/params.go | 32 + resources/resource/resource_helpers.go | 75 + resources/resource/resources.go | 408 + .../resource/resources_integration_test.go | 105 + resources/resource/resources_test.go | 122 + resources/resource/resourcetypes.go | 296 + resources/resource_cache.go | 155 + .../resource_factories/bundler/bundler.go | 171 + .../bundler/bundler_test.go | 40 + resources/resource_factories/create/create.go | 329 + .../create/create_integration_test.go | 359 + resources/resource_factories/create/remote.go | 537 + .../resource_factories/create/remote_test.go | 136 + resources/resource_metadata.go | 224 + resources/resource_spec.go | 235 + resources/resource_spec_test.go | 48 + resources/resource_test.go | 53 + .../resource_transformers/babel/babel.go | 237 + .../babel/babel_integration_test.go | 190 + .../cssjs/inline_imports.go | 248 + .../cssjs/inline_imports_test.go | 179 + .../resource_transformers/cssjs/postcss.go | 246 + .../cssjs/postcss_integration_test.go | 458 + .../cssjs/tailwindcss.go | 168 + .../cssjs/tailwindcss_integration_test.go | 122 + .../htesting/testhelpers.go | 43 + .../integrity/integrity.go | 120 + .../integrity/integrity_test.go | 71 + resources/resource_transformers/js/build.go | 161 + .../js/js_integration_test.go | 414 + .../resource_transformers/js/transform.go | 76 + .../minifier/minifier_integration_test.go | 42 + .../resource_transformers/minifier/minify.go | 59 + .../minifier/minify_test.go | 44 + .../templates/execute_as_template.go | 75 + .../templates/templates_integration_test.go | 73 + .../tocss/dartsass/client.go | 185 + .../dartsass/dartsass_integration_test.go | 728 ++ .../tocss/dartsass/transform.go | 212 + .../tocss/sass/helpers.go | 176 + .../tocss/sass/helpers_test.go | 43 + .../tocss/scss/client.go | 93 + .../tocss/scss/client_extended.go | 58 + .../tocss/scss/client_notavailable.go | 30 + .../tocss/scss/client_test.go | 49 + .../tocss/scss/scss_integration_test.go | 439 + .../resource_transformers/tocss/scss/tocss.go | 216 + resources/resources_integration_test.go | 260 + ...opasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg | Bin 0 -> 90587 bytes resources/testdata/bep/dock-75-hdr.avif | Bin 0 -> 84960 bytes resources/testdata/bep/license.txt | 1 + resources/testdata/bep/straws.avif | Bin 0 -> 271064 bytes resources/testdata/bw-gopher.png | Bin 0 -> 546 bytes resources/testdata/circle.svg | 5 + resources/testdata/exif/orientation6.jpg | Bin 0 -> 982 bytes resources/testdata/fuzzy-cirlcle.png | Bin 0 -> 26792 bytes resources/testdata/giphy.avif | Bin 0 -> 28886 bytes resources/testdata/giphy.gif | Bin 0 -> 52213 bytes resources/testdata/gohugoio-card.gif | Bin 0 -> 10820 bytes resources/testdata/gohugoio.png | Bin 0 -> 73886 bytes resources/testdata/gohugoio24.png | Bin 0 -> 267952 bytes resources/testdata/gohugoio8.png | Bin 0 -> 73538 bytes resources/testdata/gopher-hero8.png | Bin 0 -> 13327 bytes resources/testdata/gradient-circle.png | Bin 0 -> 20069 bytes resources/testdata/iss8079.jpg | Bin 0 -> 116955 bytes .../issue10738/canon_cr2_fraction.jpg | Bin 0 -> 23024 bytes .../testdata/issue10738/canon_cr2_integer.jpg | Bin 0 -> 20189 bytes .../testdata/issue10738/dji_dng_fraction.jpg | Bin 0 -> 18991 bytes .../testdata/issue10738/fuji_raf_fraction.jpg | Bin 0 -> 22653 bytes .../testdata/issue10738/fuji_raf_integer.jpg | Bin 0 -> 20191 bytes .../issue10738/leica_dng_fraction.jpg | Bin 0 -> 21145 bytes .../issue10738/lumix_rw2_fraction.jpg | Bin 0 -> 22776 bytes .../testdata/issue10738/nikon_nef_d5600.jpg | Bin 0 -> 20085 bytes .../issue10738/nikon_nef_fraction.jpg | Bin 0 -> 20806 bytes .../issue10738/nikon_nef_fraction_2.jpg | Bin 0 -> 100394 bytes .../testdata/issue10738/nikon_nef_integer.jpg | Bin 0 -> 21903 bytes .../testdata/issue10738/sony_arw_fraction.jpg | Bin 0 -> 23298 bytes .../testdata/issue10738/sony_arw_integer.jpg | Bin 0 -> 23159 bytes resources/testdata/mask.png | Bin 0 -> 120244 bytes resources/testdata/mask2.png | Bin 0 -> 36823 bytes resources/testdata/pix.bmp | Bin 0 -> 142 bytes resources/testdata/pix.gif | Bin 0 -> 42 bytes resources/testdata/sub/gohugoio2.png | Bin 0 -> 73886 bytes resources/testdata/sunrise.JPG | Bin 0 -> 90587 bytes resources/testdata/sunrise.webp | Bin 0 -> 95652 bytes resources/testdata/sunset.avif | Bin 0 -> 40485 bytes resources/testdata/sunset.jpg | Bin 0 -> 90587 bytes resources/testdata/sunset.webp | Bin 0 -> 59826 bytes resources/testdata/sunset_420.avif | Bin 0 -> 51275 bytes resources/testdata/webp/anim.webp | Bin 0 -> 142126 bytes resources/testdata/webp/bw-gopher.webp | Bin 0 -> 1150 bytes .../webp/fuzzy-cirlcle-transparent-32.webp | Bin 0 -> 7924 bytes resources/testdata/webp/highcontrast.webp | Bin 0 -> 11466 bytes resources/testdata/webp/invalid.webp | 0 resources/testhelpers_test.go | 150 + resources/transform.go | 839 ++ resources/transform_integration_test.go | 50 + resources/transform_test.go | 447 + scripts/docker/entrypoint.sh | 21 + scripts/fork_go_templates/.gitignore | 1 + scripts/fork_go_templates/main.go | 232 + snap/local/logo.png | Bin 0 -> 73651 bytes snap/snapcraft.yaml | 223 + source/content_directory_test.go | 78 + source/fileInfo.go | 159 + source/sourceSpec.go | 89 + testscripts/.gitattributes | 1 + testscripts/commands/commands_errors.txt | 12 + testscripts/commands/completion.txt | 4 + testscripts/commands/config.txt | 22 + testscripts/commands/config__cachedir.txt | 18 + ...onfig_environment_isolation_issue14763.txt | 23 + testscripts/commands/convert.txt | 129 + testscripts/commands/deprecate.txt | 25 + testscripts/commands/env.txt | 5 + testscripts/commands/gen.txt | 10 + testscripts/commands/gen_chromastyles.txt | 25 + .../commands/gen_chromastyles_mode.txt | 24 + testscripts/commands/hugo.txt | 20 + testscripts/commands/hugo__configdir.txt | 7 + testscripts/commands/hugo__errors.txt | 18 + testscripts/commands/hugo__flags.txt | 33 + .../commands/hugo__minify_issue13988.txt | 13 + testscripts/commands/hugo__noconfig.txt | 8 + .../commands/hugo__paniconwarning_mod.txt | 13 + .../hugo__path-warnings-postprocess.txt | 20 + testscripts/commands/hugo__path-warnings.txt | 26 + .../hugo__path-warnings_issue13164.txt | 15 + .../commands/hugo__processingstats.txt | 49 + .../commands/hugo__processingstats2.txt | 16 + .../commands/hugo__publishdir_in_config.txt | 12 + testscripts/commands/hugo__static.txt | 39 + .../commands/hugo__static_composite.txt | 27 + .../commands/hugo__static_issue14507.txt | 27 + testscripts/commands/hugo_build.txt | 20 + testscripts/commands/hugo_configdev_env.txt | 19 + .../commands/hugo_configdev_environment.txt | 22 + testscripts/commands/hugo_configprod.txt | 18 + .../commands/hugo_printpathwarnings.txt | 17 + .../commands/hugo_printunusedtemplates.txt | 11 + testscripts/commands/import_jekyll.txt | 62 + testscripts/commands/list.txt | 65 + testscripts/commands/mod.txt | 45 + testscripts/commands/mod__disable.txt | 15 + testscripts/commands/mod__hugodirectsum.txt | 15 + .../commands/mod__hugodirectsum_tamper.txt | 16 + testscripts/commands/mod__tamper_gosum.txt | 22 + testscripts/commands/mod__themesdir.txt | 7 + testscripts/commands/mod_get.txt | 15 + testscripts/commands/mod_get_u.txt | 22 + testscripts/commands/mod_init.txt | 14 + testscripts/commands/mod_npm.txt | 214 + testscripts/commands/mod_npm__moduleorder.txt | 67 + testscripts/commands/mod_npm_withexisting.txt | 61 + testscripts/commands/mod_tidy.txt | 27 + testscripts/commands/mod_vendor.txt | 29 + testscripts/commands/mod_vendor__versions.txt | 100 + testscripts/commands/new.txt | 114 + testscripts/commands/new_content.txt | 54 + .../commands/new_content_archetypedir.txt | 40 + testscripts/commands/no_symlinks.txt | 57 + testscripts/commands/noop.txt | 0 testscripts/commands/version.txt | 7 + testscripts/commands/warnf_stderr.txt | 13 + testscripts/server/hugo__watch.txt | 29 + testscripts/server/server.txt | 32 + testscripts/server/server__edit_config.txt | 43 + testscripts/server/server__edit_content.txt | 55 + .../server__edit_content_with_chinese_tag.txt | 25 + .../server__error_recovery_edit_config.txt | 42 + .../server__error_recovery_edit_content.txt | 42 + testscripts/server/server__multihost.txt | 51 + testscripts/server/server__watch_false.txt | 17 + .../server/server__watch_hugo_stats.txt | 18 + .../server/server__watch_moduleconfig.txt | 19 + .../server/server_disablelivereload.txt | 20 + .../server_disablelivereload__config.txt | 21 + .../server/server_render_static_to_disk.txt | 50 + .../server/server_render_to_memory.txt | 26 + testscripts/unfinished/noop.txt | 0 testscripts/withdeploy-off/deploy_off.txt | 3 + testscripts/withdeploy/deploy.txt | 24 + tpl/cast/cast.go | 62 + tpl/cast/cast_test.go | 123 + tpl/cast/docshelper.go | 53 + tpl/cast/init.go | 59 + tpl/collections/append.go | 40 + tpl/collections/append_test.go | 65 + tpl/collections/apply.go | 143 + tpl/collections/collections.go | 763 ++ .../collections_integration_test.go | 655 ++ tpl/collections/collections_test.go | 951 ++ tpl/collections/complement.go | 59 + tpl/collections/complement_test.go | 95 + tpl/collections/index.go | 145 + tpl/collections/index_test.go | 91 + tpl/collections/init.go | 219 + tpl/collections/merge.go | 143 + tpl/collections/merge_test.go | 249 + tpl/collections/querify.go | 125 + tpl/collections/querify_test.go | 118 + tpl/collections/reflect_helpers.go | 118 + tpl/collections/sort.go | 197 + tpl/collections/sort_test.go | 271 + tpl/collections/symdiff.go | 68 + tpl/collections/symdiff_test.go | 75 + tpl/collections/vitter.go | 149 + tpl/collections/vitter_test.go | 95 + tpl/collections/where.go | 727 ++ tpl/collections/where_test.go | 925 ++ tpl/compare/compare.go | 396 + tpl/compare/compare_test.go | 500 + tpl/compare/init.go | 99 + tpl/crypto/crypto.go | 180 + tpl/crypto/crypto_integration_test.go | 46 + tpl/crypto/crypto_test.go | 174 + tpl/crypto/init.go | 77 + tpl/css/build_integration_test.go | 733 ++ tpl/css/css.go | 246 + tpl/debug/debug.go | 185 + tpl/debug/debug_integration_test.go | 68 + tpl/debug/init.go | 75 + tpl/diagrams/diagrams.go | 33 + tpl/diagrams/goat.go | 68 + tpl/diagrams/init.go | 46 + tpl/encoding/encoding.go | 139 + tpl/encoding/encoding_test.go | 175 + tpl/encoding/init.go | 75 + tpl/fmt/fmt.go | 117 + tpl/fmt/fmt_integration_test.go | 65 + tpl/fmt/init.go | 99 + tpl/hash/hash.go | 85 + tpl/hash/hash_test.go | 84 + tpl/hugo/init.go | 44 + tpl/images/images.go | 191 + tpl/images/images_integration_test.go | 220 + tpl/images/images_test.go | 131 + tpl/images/init.go | 53 + .../images_golden/funcs/qr-default.png | Bin 0 -> 663 bytes .../funcs/qr-level-high_scale-6.png | Bin 0 -> 1112 bytes tpl/inflect/inflect.go | 75 + tpl/inflect/inflect_test.go | 49 + tpl/inflect/init.go | 62 + tpl/internal/go_templates/cfg/cfg.go | 74 + .../go_templates/fmtsort/export_test.go | 11 + tpl/internal/go_templates/fmtsort/sort.go | 154 + .../go_templates/fmtsort/sort_test.go | 280 + .../go_templates/htmltemplate/attr.go | 175 + .../go_templates/htmltemplate/attr_string.go | 29 + .../go_templates/htmltemplate/clone_test.go | 282 + .../go_templates/htmltemplate/content.go | 102 + .../go_templates/htmltemplate/content_test.go | 462 + .../go_templates/htmltemplate/context.go | 312 + tpl/internal/go_templates/htmltemplate/css.go | 260 + .../go_templates/htmltemplate/css_test.go | 286 + .../go_templates/htmltemplate/delim_string.go | 26 + tpl/internal/go_templates/htmltemplate/doc.go | 243 + .../htmltemplate/element_string.go | 28 + .../go_templates/htmltemplate/error.go | 252 + .../go_templates/htmltemplate/escape.go | 1006 ++ .../go_templates/htmltemplate/escape_test.go | 2268 ++++ .../go_templates/htmltemplate/example_test.go | 185 + .../htmltemplate/examplefiles_test.go | 229 + .../go_templates/htmltemplate/exec_test.go | 1860 ++++ .../go_templates/htmltemplate/html.go | 270 + .../go_templates/htmltemplate/html_test.go | 100 + .../htmltemplate/hugo_template.go | 94 + tpl/internal/go_templates/htmltemplate/js.go | 490 + .../go_templates/htmltemplate/js_test.go | 441 + .../go_templates/htmltemplate/jsctx_string.go | 25 + .../go_templates/htmltemplate/multi_test.go | 293 + .../go_templates/htmltemplate/state_string.go | 53 + .../go_templates/htmltemplate/template.go | 531 + .../htmltemplate/template_test.go | 222 + .../htmltemplate/testdata/file1.tmpl | 2 + .../htmltemplate/testdata/file2.tmpl | 2 + .../go_templates/htmltemplate/testdata/fs.zip | Bin 0 -> 406 bytes .../htmltemplate/testdata/tmpl1.tmpl | 3 + .../htmltemplate/testdata/tmpl2.tmpl | 3 + .../go_templates/htmltemplate/transition.go | 723 ++ .../htmltemplate/transition_test.go | 62 + tpl/internal/go_templates/htmltemplate/url.go | 216 + .../go_templates/htmltemplate/url_test.go | 172 + .../htmltemplate/urlpart_string.go | 26 + tpl/internal/go_templates/staticcheck.conf | 1 + tpl/internal/go_templates/testenv/exec.go | 242 + tpl/internal/go_templates/testenv/noopt.go | 12 + tpl/internal/go_templates/testenv/opt.go | 12 + tpl/internal/go_templates/testenv/testenv.go | 481 + .../go_templates/testenv/testenv_notunix.go | 21 + .../go_templates/testenv/testenv_notwin.go | 47 + .../go_templates/testenv/testenv_unix.go | 23 + .../go_templates/testenv/testenv_windows.go | 32 + tpl/internal/go_templates/texttemplate/doc.go | 502 + .../go_templates/texttemplate/example_test.go | 113 + .../texttemplate/examplefiles_test.go | 184 + .../texttemplate/examplefunc_test.go | 126 + .../go_templates/texttemplate/exec.go | 1134 ++ .../go_templates/texttemplate/exec_test.go | 2017 ++++ .../go_templates/texttemplate/funcs.go | 774 ++ .../go_templates/texttemplate/helper.go | 178 + .../texttemplate/hugo_template.go | 500 + .../texttemplate/hugo_template_test.go | 95 + .../go_templates/texttemplate/link_test.go | 62 + .../go_templates/texttemplate/multi_test.go | 489 + .../go_templates/texttemplate/option.go | 72 + .../go_templates/texttemplate/parse/lex.go | 687 ++ .../texttemplate/parse/lex_test.go | 583 + .../go_templates/texttemplate/parse/node.go | 1011 ++ .../go_templates/texttemplate/parse/parse.go | 849 ++ .../texttemplate/parse/parse_test.go | 735 ++ .../go_templates/texttemplate/template.go | 236 + .../texttemplate/testdata/file1.tmpl | 2 + .../texttemplate/testdata/file2.tmpl | 2 + .../texttemplate/testdata/tmpl1.tmpl | 3 + .../texttemplate/testdata/tmpl2.tmpl | 3 + tpl/internal/resourcehelpers/helpers.go | 69 + tpl/internal/templatefuncRegistry_test.go | 38 + tpl/internal/templatefuncsRegistry.go | 348 + tpl/js/init.go | 56 + tpl/js/js.go | 117 + tpl/lang/init.go | 89 + tpl/lang/lang.go | 249 + tpl/lang/lang_test.go | 135 + tpl/math/init.go | 250 + tpl/math/math.go | 365 + tpl/math/math_test.go | 892 ++ tpl/math/round.go | 62 + tpl/openapi/docs.go | 2 + tpl/openapi/openapi3/init.go | 55 + tpl/openapi/openapi3/openapi3.go | 202 + .../openapi3/openapi3_integration_test.go | 305 + tpl/os/init.go | 69 + tpl/os/os.go | 170 + tpl/os/os_integration_test.go | 65 + tpl/os/os_test.go | 120 + tpl/page/init.go | 49 + tpl/page/page_integration_test.go | 240 + tpl/partials/init.go | 57 + tpl/partials/partials.go | 264 + tpl/partials/partials_integration_test.go | 337 + tpl/path/init.go | 86 + tpl/path/path.go | 162 + tpl/path/path_test.go | 245 + tpl/reflect/init.go | 53 + tpl/reflect/reflect.go | 74 + tpl/reflect/reflect_integration_test.go | 66 + tpl/reflect/reflect_test.go | 52 + tpl/resources/init.go | 102 + tpl/resources/resources.go | 302 + tpl/resources/resources_integration_test.go | 298 + tpl/safe/init.go | 84 + tpl/safe/safe.go | 66 + tpl/safe/safe_test.go | 184 + tpl/site/init.go | 42 + tpl/strings/init.go | 278 + tpl/strings/regexp.go | 115 + tpl/strings/regexp_test.go | 127 + tpl/strings/strings.go | 606 + tpl/strings/strings_test.go | 969 ++ tpl/strings/truncate.go | 161 + tpl/strings/truncate_test.go | 127 + tpl/template.go | 204 + tpl/template_test.go | 57 + tpl/templates/decorator_falsy_test.go | 90 + tpl/templates/decorator_integration_test.go | 501 + tpl/templates/defer_integration_test.go | 369 + tpl/templates/init.go | 93 + tpl/templates/templates.go | 168 + tpl/templates/templates_integration_test.go | 379 + tpl/time/init.go | 102 + tpl/time/time.go | 150 + tpl/time/time_integration_test.go | 53 + tpl/time/time_test.go | 259 + tpl/tplimpl/category_string.go | 30 + tpl/tplimpl/embedded/.gitattributes | 1 + .../_hugo/build/js/batch-esm-runner.gotmpl | 16 + .../_markup/render-codeblock-goat.html | 18 + .../templates/_markup/render-image.html | 22 + .../templates/_markup/render-link.html | 22 + .../templates/_markup/render-table.html | 35 + .../_partials/_funcs/get-page-images.html | 52 + .../embedded/templates/_partials/disqus.html | 24 + .../templates/_partials/google_analytics.html | 22 + .../templates/_partials/opengraph.html | 74 + .../templates/_partials/pagination.html | 154 + .../embedded/templates/_partials/schema.html | 56 + .../templates/_partials/twitter_cards.html | 28 + .../embedded/templates/_server/error.html | 87 + .../_shortcodes/1__h_simple_assets.html | 34 + .../templates/_shortcodes/comment.html | 2 + .../templates/_shortcodes/details.html | 74 + .../templates/_shortcodes/figure.html | 34 + .../embedded/templates/_shortcodes/gist.html | 1 + .../templates/_shortcodes/highlight.html | 1 + .../templates/_shortcodes/instagram.html | 245 + .../_shortcodes/instagram_simple.html | 1 + .../embedded/templates/_shortcodes/param.html | 5 + .../embedded/templates/_shortcodes/qr.html | 77 + .../embedded/templates/_shortcodes/ref.html | 1 + .../templates/_shortcodes/relref.html | 1 + .../templates/_shortcodes/twitter.html | 1 + .../templates/_shortcodes/twitter_simple.html | 1 + .../embedded/templates/_shortcodes/vimeo.html | 67 + .../templates/_shortcodes/vimeo_simple.html | 52 + .../embedded/templates/_shortcodes/x.html | 29 + .../templates/_shortcodes/x_simple.html | 60 + .../templates/_shortcodes/youtube.html | 125 + tpl/tplimpl/embedded/templates/alias.html | 9 + tpl/tplimpl/embedded/templates/robots.txt | 1 + tpl/tplimpl/embedded/templates/rss.xml | 67 + tpl/tplimpl/embedded/templates/sitemap.xml | 20 + .../embedded/templates/sitemapindex.xml | 11 + .../embedded_templates_integration_test.go | 751 ++ tpl/tplimpl/legacy.go | 129 + tpl/tplimpl/render_hook_integration_test.go | 501 + tpl/tplimpl/shortcodes_integration_test.go | 893 ++ tpl/tplimpl/subcategory_string.go | 25 + tpl/tplimpl/template_funcs.go | 175 + tpl/tplimpl/template_funcs_test.go | 78 + tpl/tplimpl/template_info.go | 49 + tpl/tplimpl/templatedescriptor.go | 263 + tpl/tplimpl/templatedescriptor_test.go | 103 + tpl/tplimpl/templates.go | 326 + tpl/tplimpl/templatestore.go | 2237 ++++ tpl/tplimpl/templatestore_integration_test.go | 1571 +++ .../templatestore_legacy1_integration_test.go | 275 + tpl/tplimpl/templatetransform.go | 588 + tpl/tplimpl/tplimpl_integration_test.go | 264 + tpl/tplimplinit/tplimplinit.go | 95 + tpl/transform/init.go | 154 + tpl/transform/remarshal.go | 87 + tpl/transform/remarshal_test.go | 269 + tpl/transform/testdata/large-katex.md | 373 + tpl/transform/transform.go | 389 + tpl/transform/transform_integration_test.go | 642 ++ tpl/transform/transform_test.go | 235 + tpl/transform/unmarshal.go | 221 + tpl/transform/unmarshal_test.go | 240 + tpl/urls/init.go | 103 + tpl/urls/urls.go | 244 + tpl/urls/urls_test.go | 187 + transform/chain.go | 125 + transform/chain_test.go | 70 + .../livereloadinject/livereloadinject.go | 80 + .../livereloadinject/livereloadinject_test.go | 143 + transform/metainject/hugogenerator.go | 56 + transform/metainject/hugogenerator_test.go | 60 + transform/urlreplacers/absurl.go | 38 + transform/urlreplacers/absurlreplacer.go | 277 + transform/urlreplacers/absurlreplacer_test.go | 232 + watcher/batcher.go | 85 + watcher/filenotify/filenotify.go | 49 + watcher/filenotify/fsnotify.go | 18 + watcher/filenotify/poller.go | 323 + watcher/filenotify/poller_test.go | 298 + watchtestscripts.sh | 7 + 2540 files changed, 316913 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .dockerignore create mode 100644 .gemini/config.yaml create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/SUPPORT.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/image.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .mailmap create mode 100644 .prettierignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 CONTRIBUTING.md create mode 100755 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.wehub.md create mode 100644 SECURITY.md create mode 100644 bufferpool/bufpool.go create mode 100644 bufferpool/bufpool_test.go create mode 100644 cache/docs.go create mode 100644 cache/dynacache/dynacache.go create mode 100644 cache/dynacache/dynacache_test.go create mode 100644 cache/filecache/filecache.go create mode 100644 cache/filecache/filecache_config.go create mode 100644 cache/filecache/filecache_config_test.go create mode 100644 cache/filecache/filecache_integration_test.go create mode 100644 cache/filecache/filecache_pruner.go create mode 100644 cache/filecache/filecache_pruner_test.go create mode 100644 cache/filecache/filecache_test.go create mode 100644 cache/httpcache/httpcache.go create mode 100644 cache/httpcache/httpcache_integration_test.go create mode 100644 cache/httpcache/httpcache_test.go create mode 100755 check.sh create mode 100755 check_gofmt.sh create mode 100644 codegen/methods.go create mode 100644 codegen/methods2_test.go create mode 100644 codegen/methods_test.go create mode 100644 commands/commandeer.go create mode 100644 commands/commands.go create mode 100644 commands/config.go create mode 100644 commands/convert.go create mode 100644 commands/deploy.go create mode 100644 commands/deploy_flags.go create mode 100644 commands/deploy_off.go create mode 100644 commands/env.go create mode 100644 commands/gen.go create mode 100644 commands/helpers.go create mode 100644 commands/hugo_windows.go create mode 100644 commands/hugobuilder.go create mode 100644 commands/import.go create mode 100644 commands/list.go create mode 100644 commands/mod.go create mode 100644 commands/new.go create mode 100644 commands/release.go create mode 100644 commands/server.go create mode 100644 common/collections/append.go create mode 100644 common/collections/append_test.go create mode 100644 common/collections/collections.go create mode 100644 common/collections/order.go create mode 100644 common/collections/slice.go create mode 100644 common/collections/slice_test.go create mode 100644 common/collections/stack.go create mode 100644 common/collections/stack_test.go create mode 100644 common/constants/constants.go create mode 100644 common/docs.go create mode 100644 common/hashing/hashing.go create mode 100644 common/hashing/hashing_test.go create mode 100644 common/hdebug/debug.go create mode 100644 common/herrors/error_locator.go create mode 100644 common/herrors/error_locator_test.go create mode 100644 common/herrors/errors.go create mode 100644 common/herrors/errors_test.go create mode 100644 common/herrors/file_error.go create mode 100644 common/herrors/file_error_test.go create mode 100644 common/herrors/line_number_extractors.go create mode 100644 common/herrors/line_number_extractors_test.go create mode 100644 common/hexec/esmloader.go create mode 100644 common/hexec/esmloader.mjs create mode 100644 common/hexec/exec.go create mode 100644 common/hexec/exec_integration_test.go create mode 100644 common/hexec/exec_test.go create mode 100644 common/himage/image.go create mode 100644 common/hiter/iter.go create mode 100644 common/hmaps/cache.go create mode 100644 common/hmaps/cache_test.go create mode 100644 common/hmaps/maps.go create mode 100644 common/hmaps/maps_test.go create mode 100644 common/hmaps/ordered.go create mode 100644 common/hmaps/ordered_test.go create mode 100644 common/hmaps/orderedintset.go create mode 100644 common/hmaps/orderedintset_test.go create mode 100644 common/hmaps/params.go create mode 100644 common/hmaps/params_test.go create mode 100644 common/hreflect/convert.go create mode 100644 common/hreflect/convert_test.go create mode 100644 common/hreflect/helpers.go create mode 100644 common/hreflect/helpers_test.go create mode 100644 common/hstore/scratch.go create mode 100644 common/hstore/scratch_test.go create mode 100644 common/hstrings/strings.go create mode 100644 common/hstrings/strings_test.go create mode 100644 common/hsync/oncemore.go create mode 100644 common/hsync/oncemore_test.go create mode 100644 common/htime/htime_integration_test.go create mode 100644 common/htime/time.go create mode 100644 common/htime/time_test.go create mode 100644 common/hugio/copy.go create mode 100644 common/hugio/hasBytesWriter.go create mode 100644 common/hugio/hasBytesWriter_test.go create mode 100644 common/hugio/readers.go create mode 100644 common/hugio/writers.go create mode 100644 common/hugo/hugo.go create mode 100644 common/hugo/hugo_integration_test.go create mode 100644 common/hugo/hugo_test.go create mode 100644 common/hugo/vars_extended.go create mode 100644 common/hugo/vars_regular.go create mode 100644 common/hugo/vars_withdeploy.go create mode 100644 common/hugo/vars_withdeploy_off.go create mode 100644 common/hugo/version_current.go create mode 100644 common/loggers/handlerdefault.go create mode 100644 common/loggers/handlersmisc.go create mode 100644 common/loggers/handlerterminal.go create mode 100644 common/loggers/handlerterminal_test.go create mode 100644 common/loggers/logger.go create mode 100644 common/loggers/logger_test.go create mode 100644 common/loggers/loggerglobal.go create mode 100644 common/math/math.go create mode 100644 common/math/math_test.go create mode 100644 common/para/para.go create mode 100644 common/para/para_test.go create mode 100644 common/paths/path.go create mode 100644 common/paths/path_test.go create mode 100644 common/paths/pathparser.go create mode 100644 common/paths/pathparser_test.go create mode 100644 common/paths/paths_integration_test.go create mode 100644 common/paths/testdata/fuzz/FuzzParsePathContent/62fadd5d4f836c18 create mode 100644 common/paths/type_string.go create mode 100644 common/paths/url.go create mode 100644 common/paths/url_test.go create mode 100644 common/predicate/predicate.go create mode 100644 common/predicate/predicate_test.go create mode 100644 common/predicate/rangeop_test.go create mode 100644 common/rungroup/rungroup.go create mode 100644 common/rungroup/rungroup_test.go create mode 100644 common/tasks/tasks.go create mode 100644 common/terminal/colors.go create mode 100644 common/text/position.go create mode 100644 common/text/position_test.go create mode 100644 common/text/transform.go create mode 100644 common/text/transform_test.go create mode 100644 common/types/closer.go create mode 100644 common/types/convert.go create mode 100644 common/types/convert_test.go create mode 100644 common/types/css/csstypes.go create mode 100644 common/types/evictingqueue.go create mode 100644 common/types/evictingqueue_test.go create mode 100644 common/types/hstring/stringtypes.go create mode 100644 common/types/hstring/stringtypes_test.go create mode 100644 common/types/types.go create mode 100644 common/types/types_test.go create mode 100644 common/urls/baseURL.go create mode 100644 common/urls/baseURL_test.go create mode 100644 common/urls/ref.go create mode 100644 common/version/version.go create mode 100644 common/version/version_test.go create mode 100644 compare/compare.go create mode 100644 compare/compare_strings.go create mode 100644 compare/compare_strings_test.go create mode 100644 compare/compare_test.go create mode 100644 config/allconfig/allconfig.go create mode 100644 config/allconfig/allconfig_integration_test.go create mode 100644 config/allconfig/alldecoders.go create mode 100644 config/allconfig/configlanguage.go create mode 100644 config/allconfig/docshelper.go create mode 100644 config/allconfig/load.go create mode 100644 config/allconfig/load_test.go create mode 100644 config/commonConfig.go create mode 100644 config/commonConfig_test.go create mode 100644 config/configLoader.go create mode 100644 config/configLoader_test.go create mode 100644 config/configProvider.go create mode 100644 config/configProvider_test.go create mode 100644 config/defaultConfigProvider.go create mode 100644 config/defaultConfigProvider_test.go create mode 100644 config/env.go create mode 100644 config/env_test.go create mode 100644 config/namespace.go create mode 100644 config/namespace_test.go create mode 100644 config/privacy/privacyConfig.go create mode 100644 config/privacy/privacyConfig_test.go create mode 100644 config/security/securityConfig.go create mode 100644 config/security/securityConfig_test.go create mode 100644 config/security/whitelist.go create mode 100644 config/security/whitelist_test.go create mode 100644 config/services/servicesConfig.go create mode 100644 config/services/servicesConfig_test.go create mode 100644 config/testconfig/testconfig.go create mode 100644 create/content.go create mode 100644 create/content_test.go create mode 100644 create/skeletons/project/assets/.gitkeep create mode 100644 create/skeletons/project/content/.gitkeep create mode 100644 create/skeletons/project/data/.gitkeep create mode 100644 create/skeletons/project/i18n/.gitkeep create mode 100644 create/skeletons/project/layouts/.gitkeep create mode 100644 create/skeletons/project/static/.gitkeep create mode 100644 create/skeletons/project/themes/.gitkeep create mode 100644 create/skeletons/skeletons.go create mode 100644 create/skeletons/theme/assets/css/components/footer.css create mode 100644 create/skeletons/theme/assets/css/components/header.css create mode 100644 create/skeletons/theme/assets/css/main.css create mode 100644 create/skeletons/theme/assets/js/main.js create mode 100644 create/skeletons/theme/content/_index.md create mode 100644 create/skeletons/theme/content/posts/_index.md create mode 100644 create/skeletons/theme/content/posts/post-1.md create mode 100644 create/skeletons/theme/content/posts/post-2.md create mode 100644 create/skeletons/theme/content/posts/post-3/bryce-canyon.jpg create mode 100644 create/skeletons/theme/content/posts/post-3/index.md create mode 100644 create/skeletons/theme/data/.gitkeep create mode 100644 create/skeletons/theme/i18n/.gitkeep create mode 100644 create/skeletons/theme/layouts/_partials/footer.html create mode 100644 create/skeletons/theme/layouts/_partials/head.html create mode 100644 create/skeletons/theme/layouts/_partials/head/css.html create mode 100644 create/skeletons/theme/layouts/_partials/head/js.html create mode 100644 create/skeletons/theme/layouts/_partials/header.html create mode 100644 create/skeletons/theme/layouts/_partials/menu.html create mode 100644 create/skeletons/theme/layouts/_partials/terms.html create mode 100644 create/skeletons/theme/layouts/baseof.html create mode 100644 create/skeletons/theme/layouts/home.html create mode 100644 create/skeletons/theme/layouts/page.html create mode 100644 create/skeletons/theme/layouts/section.html create mode 100644 create/skeletons/theme/layouts/taxonomy.html create mode 100644 create/skeletons/theme/layouts/term.html create mode 100644 create/skeletons/theme/static/favicon.ico create mode 100644 deploy/cloudfront.go create mode 100644 deploy/deploy.go create mode 100644 deploy/deploy_azure.go create mode 100644 deploy/deploy_test.go create mode 100644 deploy/deployconfig/deployConfig.go create mode 100644 deploy/deployconfig/deployConfig_test.go create mode 100644 deploy/google.go create mode 100644 deps/deps.go create mode 100644 deps/deps_test.go create mode 100644 docs/.codespellrc create mode 100644 docs/.cspell.json create mode 100644 docs/.editorconfig create mode 100644 docs/.github/ISSUE_TEMPLATE/config.yml create mode 100644 docs/.github/ISSUE_TEMPLATE/default.md create mode 100644 docs/.github/SUPPORT.md create mode 100644 docs/.github/workflows/codeql-analysis.yml create mode 100644 docs/.github/workflows/lint.yml create mode 100644 docs/.github/workflows/stale.yml create mode 100644 docs/.gitignore create mode 100644 docs/.markdownlint-cli2.yaml create mode 100644 docs/.prettierignore create mode 100644 docs/.prettierrc create mode 100644 docs/AGENTS.md create mode 100644 docs/CLAUDE.md create mode 100644 docs/LICENSE.md create mode 100644 docs/README.md create mode 100644 docs/archetypes/default.md create mode 100644 docs/archetypes/functions.md create mode 100644 docs/archetypes/glossary.md create mode 100644 docs/archetypes/methods.md create mode 100644 docs/archetypes/news.md create mode 100644 docs/assets/css/components/all.css create mode 100644 docs/assets/css/components/chroma.css create mode 100644 docs/assets/css/components/chroma_dark.css create mode 100644 docs/assets/css/components/content.css create mode 100644 docs/assets/css/components/fonts.css create mode 100644 docs/assets/css/components/helpers.css create mode 100644 docs/assets/css/components/highlight.css create mode 100644 docs/assets/css/components/shortcodes.css create mode 100644 docs/assets/css/components/tableofcontents.css create mode 100644 docs/assets/css/components/todo-lists.css create mode 100644 docs/assets/css/components/view-transitions.css create mode 100644 docs/assets/css/styles.css create mode 100644 docs/assets/images/examples/landscape-exif-orientation-5.jpg create mode 100644 docs/assets/images/examples/mask.png create mode 100644 docs/assets/images/examples/zion-national-park.jpg create mode 100644 docs/assets/images/hugo-github-screenshot.png create mode 100644 docs/assets/images/logos/logo-128x128.png create mode 100644 docs/assets/images/logos/logo-256x256.png create mode 100644 docs/assets/images/logos/logo-512x512.png create mode 100644 docs/assets/images/logos/logo-64x64.png create mode 100644 docs/assets/images/logos/logo-96x96.png create mode 100644 docs/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg create mode 100644 docs/assets/images/sponsors/bep-consulting.svg create mode 100644 docs/assets/images/sponsors/butter-dark.svg create mode 100644 docs/assets/images/sponsors/butter-light.svg create mode 100644 docs/assets/images/sponsors/cloudcannon-cms-logo.svg create mode 100644 docs/assets/images/sponsors/esolia-logo.svg create mode 100644 docs/assets/images/sponsors/goland.svg create mode 100644 docs/assets/images/sponsors/graitykit-dark.svg create mode 100644 docs/assets/images/sponsors/linode-logo.svg create mode 100644 docs/assets/images/sponsors/linode-logo_standard_light_medium.png create mode 100644 docs/assets/images/sponsors/logo-pinme.svg create mode 100644 docs/assets/images/sponsors/your-company-dark.svg create mode 100644 docs/assets/images/sponsors/your-company.svg create mode 100644 docs/assets/js/alpinejs/data/index.js create mode 100644 docs/assets/js/alpinejs/data/navbar.js create mode 100644 docs/assets/js/alpinejs/data/search.js create mode 100644 docs/assets/js/alpinejs/data/toc.js create mode 100644 docs/assets/js/alpinejs/magics/helpers.js create mode 100644 docs/assets/js/alpinejs/magics/index.js create mode 100644 docs/assets/js/alpinejs/stores/index.js create mode 100644 docs/assets/js/alpinejs/stores/nav.js create mode 100644 docs/assets/js/head-early.js create mode 100644 docs/assets/js/helpers/helpers.js create mode 100644 docs/assets/js/helpers/index.js create mode 100644 docs/assets/js/helpers/lrucache.js create mode 100644 docs/assets/js/main.js create mode 100644 docs/assets/jsconfig.json create mode 100644 docs/assets/opengraph/gohugoio-card-base-1.png create mode 100644 docs/assets/opengraph/mulish-black.ttf create mode 100644 docs/content/LICENSE.md create mode 100644 docs/content/en/_common/_index.md create mode 100644 docs/content/en/_common/configuration/locale.md create mode 100644 docs/content/en/_common/configuration/page-matcher.md create mode 100644 docs/content/en/_common/content-format-table.md create mode 100644 docs/content/en/_common/embedded-get-page-images.md create mode 100644 docs/content/en/_common/filter-sort-group.md create mode 100644 docs/content/en/_common/functions/fmt/format-string.md create mode 100644 docs/content/en/_common/functions/go-html-template-package.md create mode 100644 docs/content/en/_common/functions/go-template/text-template.md create mode 100644 docs/content/en/_common/functions/hugo/sites-collection.md create mode 100644 docs/content/en/_common/functions/images/apply-image-filter.md create mode 100644 docs/content/en/_common/functions/js/options.md create mode 100644 docs/content/en/_common/functions/locales.md create mode 100644 docs/content/en/_common/functions/reflect/image-reflection-functions.md create mode 100644 docs/content/en/_common/functions/regular-expressions.md create mode 100644 docs/content/en/_common/functions/truthy-falsy.md create mode 100644 docs/content/en/_common/functions/urls/anchorize-vs-urlize.md create mode 100644 docs/content/en/_common/gitignore-public.md create mode 100644 docs/content/en/_common/glob-patterns.md create mode 100644 docs/content/en/_common/gomodules-info.md create mode 100644 docs/content/en/_common/installation/01-editions.md create mode 100644 docs/content/en/_common/installation/02-prerequisites.md create mode 100644 docs/content/en/_common/installation/03-prebuilt-binaries.md create mode 100644 docs/content/en/_common/installation/04-build-from-source.md create mode 100644 docs/content/en/_common/installation/homebrew.md create mode 100644 docs/content/en/_common/menu-entries/pre-and-post.md create mode 100644 docs/content/en/_common/menu-entry-properties.md create mode 100644 docs/content/en/_common/methods/media-type/core-methods.md create mode 100644 docs/content/en/_common/methods/output-formats/to-use-this-method.md create mode 100644 docs/content/en/_common/methods/page/next-and-prev.md create mode 100644 docs/content/en/_common/methods/page/nextinsection-and-previnsection.md create mode 100644 docs/content/en/_common/methods/pages/group-sort-order.md create mode 100644 docs/content/en/_common/methods/pages/next-and-prev.md create mode 100644 docs/content/en/_common/methods/resource/global-page-remote-resources.md create mode 100644 docs/content/en/_common/methods/resource/processing-spec.md create mode 100644 docs/content/en/_common/methods/taxonomy/get-a-taxonomy-object.md create mode 100644 docs/content/en/_common/methods/taxonomy/ordered-taxonomy-element-methods.md create mode 100644 docs/content/en/_common/parsable-date-time-strings.md create mode 100644 docs/content/en/_common/permalink-tokens.md create mode 100644 docs/content/en/_common/ref-and-relref-error-handling.md create mode 100644 docs/content/en/_common/ref-and-relref-options.md create mode 100644 docs/content/en/_common/render-hooks/pageinner.md create mode 100644 docs/content/en/_common/scratch-pad-scope.md create mode 100644 docs/content/en/_common/store-methods.md create mode 100644 docs/content/en/_common/store-scope.md create mode 100644 docs/content/en/_common/syntax-highlighting-options.md create mode 100644 docs/content/en/_common/time-layout-string.md create mode 100644 docs/content/en/_index.md create mode 100644 docs/content/en/about/_index.md create mode 100644 docs/content/en/about/features.md create mode 100644 docs/content/en/about/introduction.md create mode 100644 docs/content/en/about/license.md create mode 100644 docs/content/en/about/security.md create mode 100644 docs/content/en/commands/_index.md create mode 100644 docs/content/en/commands/hugo.md create mode 100644 docs/content/en/commands/hugo_build.md create mode 100644 docs/content/en/commands/hugo_completion.md create mode 100644 docs/content/en/commands/hugo_completion_bash.md create mode 100644 docs/content/en/commands/hugo_completion_fish.md create mode 100644 docs/content/en/commands/hugo_completion_powershell.md create mode 100644 docs/content/en/commands/hugo_completion_zsh.md create mode 100644 docs/content/en/commands/hugo_config.md create mode 100644 docs/content/en/commands/hugo_config_mounts.md create mode 100644 docs/content/en/commands/hugo_convert.md create mode 100644 docs/content/en/commands/hugo_convert_toJSON.md create mode 100644 docs/content/en/commands/hugo_convert_toTOML.md create mode 100644 docs/content/en/commands/hugo_convert_toYAML.md create mode 100644 docs/content/en/commands/hugo_deploy.md create mode 100644 docs/content/en/commands/hugo_env.md create mode 100644 docs/content/en/commands/hugo_gen.md create mode 100644 docs/content/en/commands/hugo_gen_chromastyles.md create mode 100644 docs/content/en/commands/hugo_gen_doc.md create mode 100644 docs/content/en/commands/hugo_gen_man.md create mode 100644 docs/content/en/commands/hugo_import.md create mode 100644 docs/content/en/commands/hugo_import_jekyll.md create mode 100644 docs/content/en/commands/hugo_list.md create mode 100644 docs/content/en/commands/hugo_list_all.md create mode 100644 docs/content/en/commands/hugo_list_drafts.md create mode 100644 docs/content/en/commands/hugo_list_expired.md create mode 100644 docs/content/en/commands/hugo_list_future.md create mode 100644 docs/content/en/commands/hugo_list_published.md create mode 100644 docs/content/en/commands/hugo_mod.md create mode 100644 docs/content/en/commands/hugo_mod_clean.md create mode 100644 docs/content/en/commands/hugo_mod_get.md create mode 100644 docs/content/en/commands/hugo_mod_graph.md create mode 100644 docs/content/en/commands/hugo_mod_init.md create mode 100644 docs/content/en/commands/hugo_mod_npm.md create mode 100644 docs/content/en/commands/hugo_mod_npm_pack.md create mode 100644 docs/content/en/commands/hugo_mod_tidy.md create mode 100644 docs/content/en/commands/hugo_mod_vendor.md create mode 100644 docs/content/en/commands/hugo_mod_verify.md create mode 100644 docs/content/en/commands/hugo_new.md create mode 100644 docs/content/en/commands/hugo_new_content.md create mode 100644 docs/content/en/commands/hugo_new_project.md create mode 100644 docs/content/en/commands/hugo_new_theme.md create mode 100644 docs/content/en/commands/hugo_server.md create mode 100644 docs/content/en/commands/hugo_server_trust.md create mode 100644 docs/content/en/commands/hugo_version.md create mode 100644 docs/content/en/configuration/_index.md create mode 100644 docs/content/en/configuration/all.md create mode 100644 docs/content/en/configuration/build.md create mode 100644 docs/content/en/configuration/caches.md create mode 100644 docs/content/en/configuration/cascade.md create mode 100644 docs/content/en/configuration/content-types.md create mode 100644 docs/content/en/configuration/deployment.md create mode 100644 docs/content/en/configuration/front-matter.md create mode 100644 docs/content/en/configuration/http-cache.md create mode 100644 docs/content/en/configuration/imaging.md create mode 100644 docs/content/en/configuration/introduction.md create mode 100644 docs/content/en/configuration/languages.md create mode 100644 docs/content/en/configuration/markup.md create mode 100644 docs/content/en/configuration/media-types.md create mode 100644 docs/content/en/configuration/menus.md create mode 100644 docs/content/en/configuration/minify.md create mode 100644 docs/content/en/configuration/module.md create mode 100644 docs/content/en/configuration/output-formats.md create mode 100644 docs/content/en/configuration/outputs.md create mode 100644 docs/content/en/configuration/page.md create mode 100644 docs/content/en/configuration/pagination.md create mode 100644 docs/content/en/configuration/params.md create mode 100644 docs/content/en/configuration/permalinks.md create mode 100644 docs/content/en/configuration/privacy.md create mode 100644 docs/content/en/configuration/related-content.md create mode 100644 docs/content/en/configuration/roles.md create mode 100644 docs/content/en/configuration/security.md create mode 100644 docs/content/en/configuration/segments.md create mode 100644 docs/content/en/configuration/server.md create mode 100644 docs/content/en/configuration/services.md create mode 100644 docs/content/en/configuration/sitemap.md create mode 100644 docs/content/en/configuration/taxonomies.md create mode 100644 docs/content/en/configuration/ugly-urls.md create mode 100644 docs/content/en/configuration/versions.md create mode 100644 docs/content/en/content-management/_index.md create mode 100644 docs/content/en/content-management/archetypes.md create mode 100644 docs/content/en/content-management/build-options.md create mode 100644 docs/content/en/content-management/comments.md create mode 100644 docs/content/en/content-management/content-adapters.md create mode 100644 docs/content/en/content-management/data-sources.md create mode 100644 docs/content/en/content-management/diagrams.md create mode 100644 docs/content/en/content-management/formats.md create mode 100644 docs/content/en/content-management/front-matter.md create mode 100644 docs/content/en/content-management/image-processing/index.md create mode 100644 docs/content/en/content-management/image-processing/sunset.jpg create mode 100644 docs/content/en/content-management/markdown-attributes.md create mode 100644 docs/content/en/content-management/mathematics.md create mode 100644 docs/content/en/content-management/menus.md create mode 100644 docs/content/en/content-management/multilingual.md create mode 100644 docs/content/en/content-management/organization/index.md create mode 100644 docs/content/en/content-management/page-bundles.md create mode 100644 docs/content/en/content-management/page-resources.md create mode 100644 docs/content/en/content-management/related-content.md create mode 100644 docs/content/en/content-management/sections.md create mode 100644 docs/content/en/content-management/shortcodes.md create mode 100644 docs/content/en/content-management/summaries.md create mode 100644 docs/content/en/content-management/syntax-highlighting.md create mode 100644 docs/content/en/content-management/taxonomies.md create mode 100644 docs/content/en/content-management/urls.md create mode 100644 docs/content/en/contribute/_index.md create mode 100644 docs/content/en/contribute/development.md create mode 100644 docs/content/en/contribute/documentation/index.md create mode 100644 docs/content/en/contribute/themes.md create mode 100644 docs/content/en/documentation.md create mode 100644 docs/content/en/featured.png create mode 100644 docs/content/en/functions/_index.md create mode 100644 docs/content/en/functions/cast/ToFloat.md create mode 100644 docs/content/en/functions/cast/ToInt.md create mode 100644 docs/content/en/functions/cast/ToString.md create mode 100644 docs/content/en/functions/cast/_index.md create mode 100644 docs/content/en/functions/collections/After.md create mode 100644 docs/content/en/functions/collections/Append.md create mode 100644 docs/content/en/functions/collections/Apply.md create mode 100644 docs/content/en/functions/collections/Complement.md create mode 100644 docs/content/en/functions/collections/D.md create mode 100644 docs/content/en/functions/collections/Delimit.md create mode 100644 docs/content/en/functions/collections/Dictionary.md create mode 100644 docs/content/en/functions/collections/First.md create mode 100644 docs/content/en/functions/collections/Group.md create mode 100644 docs/content/en/functions/collections/In.md create mode 100644 docs/content/en/functions/collections/IndexFunction.md create mode 100644 docs/content/en/functions/collections/Intersect.md create mode 100644 docs/content/en/functions/collections/IsSet.md create mode 100644 docs/content/en/functions/collections/KeyVals.md create mode 100644 docs/content/en/functions/collections/Last.md create mode 100644 docs/content/en/functions/collections/Merge.md create mode 100644 docs/content/en/functions/collections/NewScratch.md create mode 100644 docs/content/en/functions/collections/Querify.md create mode 100644 docs/content/en/functions/collections/Reverse.md create mode 100644 docs/content/en/functions/collections/Seq.md create mode 100644 docs/content/en/functions/collections/Shuffle.md create mode 100644 docs/content/en/functions/collections/Slice.md create mode 100644 docs/content/en/functions/collections/Sort.md create mode 100644 docs/content/en/functions/collections/SymDiff.md create mode 100644 docs/content/en/functions/collections/Union.md create mode 100644 docs/content/en/functions/collections/Uniq.md create mode 100644 docs/content/en/functions/collections/Where.md create mode 100644 docs/content/en/functions/collections/_index.md create mode 100644 docs/content/en/functions/compare/Conditional.md create mode 100644 docs/content/en/functions/compare/Default.md create mode 100644 docs/content/en/functions/compare/Eq.md create mode 100644 docs/content/en/functions/compare/Ge.md create mode 100644 docs/content/en/functions/compare/Gt.md create mode 100644 docs/content/en/functions/compare/Le.md create mode 100644 docs/content/en/functions/compare/Lt.md create mode 100644 docs/content/en/functions/compare/Ne.md create mode 100644 docs/content/en/functions/compare/_index.md create mode 100644 docs/content/en/functions/crypto/HMAC.md create mode 100644 docs/content/en/functions/crypto/Hash.md create mode 100644 docs/content/en/functions/crypto/MD5.md create mode 100644 docs/content/en/functions/crypto/SHA1.md create mode 100644 docs/content/en/functions/crypto/SHA256.md create mode 100644 docs/content/en/functions/crypto/_index.md create mode 100644 docs/content/en/functions/css/Build.md create mode 100644 docs/content/en/functions/css/PostCSS.md create mode 100644 docs/content/en/functions/css/Quoted.md create mode 100644 docs/content/en/functions/css/Sass.md create mode 100644 docs/content/en/functions/css/TailwindCSS.md create mode 100644 docs/content/en/functions/css/Unquoted.md create mode 100644 docs/content/en/functions/css/_index.md create mode 100644 docs/content/en/functions/debug/Dump.md create mode 100644 docs/content/en/functions/debug/Timer.md create mode 100644 docs/content/en/functions/debug/VisualizeSpaces.md create mode 100644 docs/content/en/functions/debug/_index.md create mode 100644 docs/content/en/functions/diagrams/Goat.md create mode 100644 docs/content/en/functions/diagrams/_index.md create mode 100644 docs/content/en/functions/encoding/Base64Decode.md create mode 100644 docs/content/en/functions/encoding/Base64Encode.md create mode 100644 docs/content/en/functions/encoding/HexDecode.md create mode 100644 docs/content/en/functions/encoding/HexEncode.md create mode 100644 docs/content/en/functions/encoding/Jsonify.md create mode 100644 docs/content/en/functions/encoding/_index.md create mode 100644 docs/content/en/functions/fmt/Errorf.md create mode 100644 docs/content/en/functions/fmt/Erroridf.md create mode 100644 docs/content/en/functions/fmt/Print.md create mode 100644 docs/content/en/functions/fmt/Printf.md create mode 100644 docs/content/en/functions/fmt/Println.md create mode 100644 docs/content/en/functions/fmt/Warnf.md create mode 100644 docs/content/en/functions/fmt/Warnidf.md create mode 100644 docs/content/en/functions/fmt/_index.md create mode 100644 docs/content/en/functions/global/_index.md create mode 100644 docs/content/en/functions/global/page.md create mode 100644 docs/content/en/functions/global/site.md create mode 100644 docs/content/en/functions/go-template/_index.md create mode 100644 docs/content/en/functions/go-template/and.md create mode 100644 docs/content/en/functions/go-template/block.md create mode 100644 docs/content/en/functions/go-template/break.md create mode 100644 docs/content/en/functions/go-template/continue.md create mode 100644 docs/content/en/functions/go-template/define.md create mode 100644 docs/content/en/functions/go-template/else.md create mode 100644 docs/content/en/functions/go-template/end.md create mode 100644 docs/content/en/functions/go-template/if.md create mode 100644 docs/content/en/functions/go-template/len.md create mode 100644 docs/content/en/functions/go-template/not.md create mode 100644 docs/content/en/functions/go-template/or.md create mode 100644 docs/content/en/functions/go-template/range.md create mode 100644 docs/content/en/functions/go-template/return.md create mode 100644 docs/content/en/functions/go-template/template.md create mode 100644 docs/content/en/functions/go-template/try.md create mode 100644 docs/content/en/functions/go-template/urlquery.md create mode 100644 docs/content/en/functions/go-template/with.md create mode 100644 docs/content/en/functions/hash/FNV32a.md create mode 100644 docs/content/en/functions/hash/XxHash.md create mode 100644 docs/content/en/functions/hash/_index.md create mode 100644 docs/content/en/functions/hugo/BuildDate.md create mode 100644 docs/content/en/functions/hugo/CommitHash.md create mode 100644 docs/content/en/functions/hugo/Data.md create mode 100644 docs/content/en/functions/hugo/Deps.md create mode 100644 docs/content/en/functions/hugo/Environment.md create mode 100644 docs/content/en/functions/hugo/Generator.md create mode 100644 docs/content/en/functions/hugo/GoVersion.md create mode 100644 docs/content/en/functions/hugo/IsDevelopment.md create mode 100644 docs/content/en/functions/hugo/IsExtended.md create mode 100644 docs/content/en/functions/hugo/IsMultihost.md create mode 100644 docs/content/en/functions/hugo/IsMultilingual.md create mode 100644 docs/content/en/functions/hugo/IsProduction.md create mode 100644 docs/content/en/functions/hugo/IsServer.md create mode 100644 docs/content/en/functions/hugo/Sites.md create mode 100644 docs/content/en/functions/hugo/Store.md create mode 100644 docs/content/en/functions/hugo/Version.md create mode 100644 docs/content/en/functions/hugo/WorkingDir.md create mode 100644 docs/content/en/functions/hugo/_index.md create mode 100644 docs/content/en/functions/images/AutoOrient.md create mode 100644 docs/content/en/functions/images/Brightness.md create mode 100644 docs/content/en/functions/images/ColorBalance.md create mode 100644 docs/content/en/functions/images/Colorize.md create mode 100644 docs/content/en/functions/images/Config.md create mode 100644 docs/content/en/functions/images/Contrast.md create mode 100644 docs/content/en/functions/images/Dither.md create mode 100644 docs/content/en/functions/images/Filter.md create mode 100644 docs/content/en/functions/images/Gamma.md create mode 100644 docs/content/en/functions/images/GaussianBlur.md create mode 100644 docs/content/en/functions/images/Grayscale.md create mode 100644 docs/content/en/functions/images/Hue.md create mode 100644 docs/content/en/functions/images/Invert.md create mode 100644 docs/content/en/functions/images/Mask.md create mode 100644 docs/content/en/functions/images/Opacity.md create mode 100644 docs/content/en/functions/images/Overlay.md create mode 100644 docs/content/en/functions/images/Padding.md create mode 100644 docs/content/en/functions/images/Pixelate.md create mode 100644 docs/content/en/functions/images/Process.md create mode 100644 docs/content/en/functions/images/QR.md create mode 100644 docs/content/en/functions/images/Saturation.md create mode 100644 docs/content/en/functions/images/Sepia.md create mode 100644 docs/content/en/functions/images/Sigmoid.md create mode 100644 docs/content/en/functions/images/Text.md create mode 100644 docs/content/en/functions/images/UnsharpMask.md create mode 100644 docs/content/en/functions/images/_index.md create mode 100644 docs/content/en/functions/inflect/Humanize.md create mode 100644 docs/content/en/functions/inflect/Pluralize.md create mode 100644 docs/content/en/functions/inflect/Singularize.md create mode 100644 docs/content/en/functions/inflect/_index.md create mode 100644 docs/content/en/functions/js/Babel.md create mode 100644 docs/content/en/functions/js/Batch.md create mode 100644 docs/content/en/functions/js/Build.md create mode 100644 docs/content/en/functions/js/_index.md create mode 100644 docs/content/en/functions/lang/FormatAccounting.md create mode 100644 docs/content/en/functions/lang/FormatCurrency.md create mode 100644 docs/content/en/functions/lang/FormatNumber.md create mode 100644 docs/content/en/functions/lang/FormatNumberCustom.md create mode 100644 docs/content/en/functions/lang/FormatPercent.md create mode 100644 docs/content/en/functions/lang/Merge.md create mode 100644 docs/content/en/functions/lang/Translate.md create mode 100644 docs/content/en/functions/lang/_index.md create mode 100644 docs/content/en/functions/math/Abs.md create mode 100644 docs/content/en/functions/math/Acos.md create mode 100644 docs/content/en/functions/math/Add.md create mode 100644 docs/content/en/functions/math/Asin.md create mode 100644 docs/content/en/functions/math/Atan.md create mode 100644 docs/content/en/functions/math/Atan2.md create mode 100644 docs/content/en/functions/math/Ceil.md create mode 100644 docs/content/en/functions/math/Cos.md create mode 100644 docs/content/en/functions/math/Counter.md create mode 100644 docs/content/en/functions/math/Div.md create mode 100644 docs/content/en/functions/math/Floor.md create mode 100644 docs/content/en/functions/math/Log.md create mode 100644 docs/content/en/functions/math/Max.md create mode 100644 docs/content/en/functions/math/MaxInt64.md create mode 100644 docs/content/en/functions/math/Min.md create mode 100644 docs/content/en/functions/math/Mod.md create mode 100644 docs/content/en/functions/math/ModBool.md create mode 100644 docs/content/en/functions/math/Mul.md create mode 100644 docs/content/en/functions/math/Pi.md create mode 100644 docs/content/en/functions/math/Pow.md create mode 100644 docs/content/en/functions/math/Product.md create mode 100644 docs/content/en/functions/math/Rand.md create mode 100644 docs/content/en/functions/math/Round.md create mode 100644 docs/content/en/functions/math/Sin.md create mode 100644 docs/content/en/functions/math/Sqrt.md create mode 100644 docs/content/en/functions/math/Sub.md create mode 100644 docs/content/en/functions/math/Sum.md create mode 100644 docs/content/en/functions/math/Tan.md create mode 100644 docs/content/en/functions/math/ToDegrees.md create mode 100644 docs/content/en/functions/math/ToRadians.md create mode 100644 docs/content/en/functions/math/_index.md create mode 100644 docs/content/en/functions/openapi3/Unmarshal.md create mode 100644 docs/content/en/functions/openapi3/_index.md create mode 100644 docs/content/en/functions/os/FileExists.md create mode 100644 docs/content/en/functions/os/Getenv.md create mode 100644 docs/content/en/functions/os/ReadDir.md create mode 100644 docs/content/en/functions/os/ReadFile.md create mode 100644 docs/content/en/functions/os/Stat.md create mode 100644 docs/content/en/functions/os/_index.md create mode 100644 docs/content/en/functions/partials/Include.md create mode 100644 docs/content/en/functions/partials/IncludeCached.md create mode 100644 docs/content/en/functions/partials/_index.md create mode 100644 docs/content/en/functions/path/Base.md create mode 100644 docs/content/en/functions/path/BaseName.md create mode 100644 docs/content/en/functions/path/Clean.md create mode 100644 docs/content/en/functions/path/Dir.md create mode 100644 docs/content/en/functions/path/Ext.md create mode 100644 docs/content/en/functions/path/Join.md create mode 100644 docs/content/en/functions/path/Split.md create mode 100644 docs/content/en/functions/path/_index.md create mode 100644 docs/content/en/functions/reflect/IsImageResource.md create mode 100644 docs/content/en/functions/reflect/IsImageResourceProcessable.md create mode 100644 docs/content/en/functions/reflect/IsImageResourceWithMeta.md create mode 100644 docs/content/en/functions/reflect/IsMap.md create mode 100644 docs/content/en/functions/reflect/IsPage.md create mode 100644 docs/content/en/functions/reflect/IsResource.md create mode 100644 docs/content/en/functions/reflect/IsSite.md create mode 100644 docs/content/en/functions/reflect/IsSlice.md create mode 100644 docs/content/en/functions/reflect/_index.md create mode 100644 docs/content/en/functions/resources/ByType.md create mode 100644 docs/content/en/functions/resources/Concat.md create mode 100644 docs/content/en/functions/resources/Copy.md create mode 100644 docs/content/en/functions/resources/ExecuteAsTemplate.md create mode 100644 docs/content/en/functions/resources/Fingerprint.md create mode 100644 docs/content/en/functions/resources/FromString.md create mode 100644 docs/content/en/functions/resources/Get.md create mode 100644 docs/content/en/functions/resources/GetMatch.md create mode 100644 docs/content/en/functions/resources/GetRemote.md create mode 100644 docs/content/en/functions/resources/Match.md create mode 100644 docs/content/en/functions/resources/Minify.md create mode 100644 docs/content/en/functions/resources/PostProcess.md create mode 100644 docs/content/en/functions/resources/_index.md create mode 100644 docs/content/en/functions/safe/CSS.md create mode 100644 docs/content/en/functions/safe/HTML.md create mode 100644 docs/content/en/functions/safe/HTMLAttr.md create mode 100644 docs/content/en/functions/safe/JS.md create mode 100644 docs/content/en/functions/safe/JSStr.md create mode 100644 docs/content/en/functions/safe/URL.md create mode 100644 docs/content/en/functions/safe/_index.md create mode 100644 docs/content/en/functions/strings/Chomp.md create mode 100644 docs/content/en/functions/strings/Contains.md create mode 100644 docs/content/en/functions/strings/ContainsAny.md create mode 100644 docs/content/en/functions/strings/ContainsNonSpace.md create mode 100644 docs/content/en/functions/strings/Count.md create mode 100644 docs/content/en/functions/strings/CountRunes.md create mode 100644 docs/content/en/functions/strings/CountWords.md create mode 100644 docs/content/en/functions/strings/Diff/diff-screen-capture.png create mode 100644 docs/content/en/functions/strings/Diff/index.md create mode 100644 docs/content/en/functions/strings/FindRESubmatch.md create mode 100644 docs/content/en/functions/strings/FindRe.md create mode 100644 docs/content/en/functions/strings/FirstUpper.md create mode 100644 docs/content/en/functions/strings/HasPrefix.md create mode 100644 docs/content/en/functions/strings/HasSuffix.md create mode 100644 docs/content/en/functions/strings/Repeat.md create mode 100644 docs/content/en/functions/strings/Replace.md create mode 100644 docs/content/en/functions/strings/ReplacePairs.md create mode 100644 docs/content/en/functions/strings/ReplaceRE.md create mode 100644 docs/content/en/functions/strings/RuneCount.md create mode 100644 docs/content/en/functions/strings/SliceString.md create mode 100644 docs/content/en/functions/strings/Split.md create mode 100644 docs/content/en/functions/strings/Substr.md create mode 100644 docs/content/en/functions/strings/Title.md create mode 100644 docs/content/en/functions/strings/ToLower.md create mode 100644 docs/content/en/functions/strings/ToUpper.md create mode 100644 docs/content/en/functions/strings/Trim.md create mode 100644 docs/content/en/functions/strings/TrimLeft.md create mode 100644 docs/content/en/functions/strings/TrimPrefix.md create mode 100644 docs/content/en/functions/strings/TrimRight.md create mode 100644 docs/content/en/functions/strings/TrimSpace.md create mode 100644 docs/content/en/functions/strings/TrimSuffix.md create mode 100644 docs/content/en/functions/strings/Truncate.md create mode 100644 docs/content/en/functions/strings/_index.md create mode 100644 docs/content/en/functions/templates/Current.md create mode 100644 docs/content/en/functions/templates/Defer.md create mode 100644 docs/content/en/functions/templates/Exists.md create mode 100644 docs/content/en/functions/templates/Inner.md create mode 100644 docs/content/en/functions/templates/_index.md create mode 100644 docs/content/en/functions/time/AsTime.md create mode 100644 docs/content/en/functions/time/Duration.md create mode 100644 docs/content/en/functions/time/Format.md create mode 100644 docs/content/en/functions/time/In.md create mode 100644 docs/content/en/functions/time/Now.md create mode 100644 docs/content/en/functions/time/ParseDuration.md create mode 100644 docs/content/en/functions/time/_index.md create mode 100644 docs/content/en/functions/transform/CanHighlight.md create mode 100644 docs/content/en/functions/transform/Emojify.md create mode 100644 docs/content/en/functions/transform/HTMLEscape.md create mode 100644 docs/content/en/functions/transform/HTMLUnescape.md create mode 100644 docs/content/en/functions/transform/HTMLtoMarkdown.md create mode 100644 docs/content/en/functions/transform/Highlight.md create mode 100644 docs/content/en/functions/transform/HighlightCodeBlock.md create mode 100644 docs/content/en/functions/transform/Markdownify.md create mode 100644 docs/content/en/functions/transform/Plainify.md create mode 100644 docs/content/en/functions/transform/PortableText.md create mode 100644 docs/content/en/functions/transform/Remarshal.md create mode 100644 docs/content/en/functions/transform/ToMath.md create mode 100644 docs/content/en/functions/transform/Unmarshal.md create mode 100644 docs/content/en/functions/transform/XMLEscape.md create mode 100644 docs/content/en/functions/transform/_index.md create mode 100644 docs/content/en/functions/urls/AbsLangURL.md create mode 100644 docs/content/en/functions/urls/AbsURL.md create mode 100644 docs/content/en/functions/urls/Anchorize.md create mode 100644 docs/content/en/functions/urls/JoinPath.md create mode 100644 docs/content/en/functions/urls/Parse.md create mode 100644 docs/content/en/functions/urls/PathEscape.md create mode 100644 docs/content/en/functions/urls/PathUnescape.md create mode 100644 docs/content/en/functions/urls/Ref.md create mode 100644 docs/content/en/functions/urls/RelLangURL.md create mode 100644 docs/content/en/functions/urls/RelRef.md create mode 100644 docs/content/en/functions/urls/RelURL.md create mode 100644 docs/content/en/functions/urls/URLize.md create mode 100644 docs/content/en/functions/urls/_index.md create mode 100644 docs/content/en/getting-started/_index.md create mode 100644 docs/content/en/getting-started/directory-structure.md create mode 100644 docs/content/en/getting-started/external-learning-resources/build-websites-with-hugo.png create mode 100644 docs/content/en/getting-started/external-learning-resources/hugo-in-action.png create mode 100644 docs/content/en/getting-started/external-learning-resources/index.md create mode 100644 docs/content/en/getting-started/quick-start.md create mode 100644 docs/content/en/getting-started/usage.md create mode 100644 docs/content/en/host-and-deploy/_index.md create mode 100644 docs/content/en/host-and-deploy/deploy-with-hugo-deploy.md create mode 100644 docs/content/en/host-and-deploy/deploy-with-rclone.md create mode 100644 docs/content/en/host-and-deploy/deploy-with-rsync.md create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/amplify-06.png create mode 100644 docs/content/en/host-and-deploy/host-on-aws-amplify/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-azure-static-web-apps.md create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-06.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/cloudflare-07.png create mode 100644 docs/content/en/host-and-deploy/host-on-cloudflare/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-firebase.md create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/gh-pages-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/gh-pages-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/gh-pages-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/gh-pages-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/gh-pages-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-github-pages/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-gitlab-pages.md create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-06.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-07.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-08.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-09.png create mode 100644 docs/content/en/host-and-deploy/host-on-netlify/netlify-10.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-06.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-07.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-08.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-09.png create mode 100644 docs/content/en/host-and-deploy/host-on-render/render-10.png create mode 100644 docs/content/en/host-and-deploy/host-on-sourcehut-pages.md create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/index.md create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-01.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-02.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-03.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-04.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-05.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-06.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-07.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-08.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-09.png create mode 100644 docs/content/en/host-and-deploy/host-on-vercel/vercel-10.png create mode 100644 docs/content/en/hugo-modules/_index.md create mode 100644 docs/content/en/hugo-modules/introduction.md create mode 100644 docs/content/en/hugo-modules/nodejs-dependencies.md create mode 100644 docs/content/en/hugo-modules/theme-components.md create mode 100644 docs/content/en/hugo-modules/use-modules.md create mode 100755 docs/content/en/hugo-pipes/_index.md create mode 100755 docs/content/en/hugo-pipes/bundling.md create mode 100755 docs/content/en/hugo-pipes/fingerprint.md create mode 100755 docs/content/en/hugo-pipes/introduction.md create mode 100644 docs/content/en/hugo-pipes/js.md create mode 100755 docs/content/en/hugo-pipes/minification.md create mode 100755 docs/content/en/hugo-pipes/postcss.md create mode 100755 docs/content/en/hugo-pipes/postprocess.md create mode 100755 docs/content/en/hugo-pipes/resource-from-string.md create mode 100755 docs/content/en/hugo-pipes/resource-from-template.md create mode 100644 docs/content/en/hugo-pipes/transpile-sass-to-css.md create mode 100644 docs/content/en/installation/_index.md create mode 100644 docs/content/en/installation/bsd.md create mode 100644 docs/content/en/installation/linux.md create mode 100644 docs/content/en/installation/macos.md create mode 100644 docs/content/en/installation/windows.md create mode 100644 docs/content/en/methods/_index.md create mode 100644 docs/content/en/methods/duration/Abs.md create mode 100644 docs/content/en/methods/duration/Hours.md create mode 100644 docs/content/en/methods/duration/Microseconds.md create mode 100644 docs/content/en/methods/duration/Milliseconds.md create mode 100644 docs/content/en/methods/duration/Minutes.md create mode 100644 docs/content/en/methods/duration/Nanoseconds.md create mode 100644 docs/content/en/methods/duration/Round.md create mode 100644 docs/content/en/methods/duration/Seconds.md create mode 100644 docs/content/en/methods/duration/Truncate.md create mode 100644 docs/content/en/methods/duration/_index.md create mode 100644 docs/content/en/methods/menu-entry/Children.md create mode 100644 docs/content/en/methods/menu-entry/HasChildren.md create mode 100644 docs/content/en/methods/menu-entry/Identifier.md create mode 100644 docs/content/en/methods/menu-entry/KeyName.md create mode 100644 docs/content/en/methods/menu-entry/Menu.md create mode 100644 docs/content/en/methods/menu-entry/Name.md create mode 100644 docs/content/en/methods/menu-entry/Page.md create mode 100644 docs/content/en/methods/menu-entry/PageRef.md create mode 100644 docs/content/en/methods/menu-entry/Params.md create mode 100644 docs/content/en/methods/menu-entry/Parent.md create mode 100644 docs/content/en/methods/menu-entry/Post.md create mode 100644 docs/content/en/methods/menu-entry/Pre.md create mode 100644 docs/content/en/methods/menu-entry/Title.md create mode 100644 docs/content/en/methods/menu-entry/URL.md create mode 100644 docs/content/en/methods/menu-entry/Weight.md create mode 100644 docs/content/en/methods/menu-entry/_index.md create mode 100644 docs/content/en/methods/menu/ByName.md create mode 100644 docs/content/en/methods/menu/ByWeight.md create mode 100644 docs/content/en/methods/menu/Limit.md create mode 100644 docs/content/en/methods/menu/Reverse.md create mode 100644 docs/content/en/methods/menu/_index.md create mode 100644 docs/content/en/methods/output-format/MediaType.md create mode 100644 docs/content/en/methods/output-format/Name.md create mode 100644 docs/content/en/methods/output-format/Permalink.md create mode 100644 docs/content/en/methods/output-format/Rel.md create mode 100644 docs/content/en/methods/output-format/RelPermalink.md create mode 100644 docs/content/en/methods/output-format/_index.md create mode 100644 docs/content/en/methods/page/Aliases.md create mode 100644 docs/content/en/methods/page/AllTranslations.md create mode 100644 docs/content/en/methods/page/AlternativeOutputFormats.md create mode 100644 docs/content/en/methods/page/Ancestors.md create mode 100644 docs/content/en/methods/page/BundleType.md create mode 100644 docs/content/en/methods/page/CodeOwners.md create mode 100644 docs/content/en/methods/page/Content.md create mode 100644 docs/content/en/methods/page/ContentWithoutSummary.md create mode 100644 docs/content/en/methods/page/CurrentSection.md create mode 100644 docs/content/en/methods/page/Data.md create mode 100644 docs/content/en/methods/page/Date.md create mode 100644 docs/content/en/methods/page/Description.md create mode 100644 docs/content/en/methods/page/Draft.md create mode 100644 docs/content/en/methods/page/Eq.md create mode 100644 docs/content/en/methods/page/ExpiryDate.md create mode 100644 docs/content/en/methods/page/File.md create mode 100644 docs/content/en/methods/page/FirstSection.md create mode 100644 docs/content/en/methods/page/Fragments.md create mode 100644 docs/content/en/methods/page/FuzzyWordCount.md create mode 100644 docs/content/en/methods/page/GetPage.md create mode 100644 docs/content/en/methods/page/GetTerms.md create mode 100644 docs/content/en/methods/page/GitInfo.md create mode 100644 docs/content/en/methods/page/HasMenuCurrent.md create mode 100644 docs/content/en/methods/page/HasShortcode.md create mode 100644 docs/content/en/methods/page/HeadingsFiltered.md create mode 100644 docs/content/en/methods/page/InSection.md create mode 100644 docs/content/en/methods/page/IsAncestor.md create mode 100644 docs/content/en/methods/page/IsBranch.md create mode 100644 docs/content/en/methods/page/IsDescendant.md create mode 100644 docs/content/en/methods/page/IsHome.md create mode 100644 docs/content/en/methods/page/IsMenuCurrent.md create mode 100644 docs/content/en/methods/page/IsNode.md create mode 100644 docs/content/en/methods/page/IsPage.md create mode 100644 docs/content/en/methods/page/IsSection.md create mode 100644 docs/content/en/methods/page/IsTranslated.md create mode 100644 docs/content/en/methods/page/Keywords.md create mode 100644 docs/content/en/methods/page/Kind.md create mode 100644 docs/content/en/methods/page/Language.md create mode 100644 docs/content/en/methods/page/Lastmod.md create mode 100644 docs/content/en/methods/page/Layout.md create mode 100644 docs/content/en/methods/page/Len.md create mode 100644 docs/content/en/methods/page/LinkTitle.md create mode 100644 docs/content/en/methods/page/Next.md create mode 100644 docs/content/en/methods/page/NextInSection.md create mode 100644 docs/content/en/methods/page/OutputFormats.md create mode 100644 docs/content/en/methods/page/Page.md create mode 100644 docs/content/en/methods/page/Pages.md create mode 100644 docs/content/en/methods/page/Paginate.md create mode 100644 docs/content/en/methods/page/Paginator.md create mode 100644 docs/content/en/methods/page/Param.md create mode 100644 docs/content/en/methods/page/Params.md create mode 100644 docs/content/en/methods/page/Parent.md create mode 100644 docs/content/en/methods/page/Path.md create mode 100644 docs/content/en/methods/page/Permalink.md create mode 100644 docs/content/en/methods/page/Plain.md create mode 100644 docs/content/en/methods/page/PlainWords.md create mode 100644 docs/content/en/methods/page/Prev.md create mode 100644 docs/content/en/methods/page/PrevInSection.md create mode 100644 docs/content/en/methods/page/PublishDate.md create mode 100644 docs/content/en/methods/page/RawContent.md create mode 100644 docs/content/en/methods/page/ReadingTime.md create mode 100644 docs/content/en/methods/page/Ref.md create mode 100644 docs/content/en/methods/page/RegularPages.md create mode 100644 docs/content/en/methods/page/RegularPagesRecursive.md create mode 100644 docs/content/en/methods/page/RelPermalink.md create mode 100644 docs/content/en/methods/page/RelRef.md create mode 100644 docs/content/en/methods/page/Render.md create mode 100644 docs/content/en/methods/page/RenderShortcodes.md create mode 100644 docs/content/en/methods/page/RenderString.md create mode 100644 docs/content/en/methods/page/Resources.md create mode 100644 docs/content/en/methods/page/Rotate.md create mode 100644 docs/content/en/methods/page/Scratch.md create mode 100644 docs/content/en/methods/page/Section.md create mode 100644 docs/content/en/methods/page/Sections.md create mode 100644 docs/content/en/methods/page/Site.md create mode 100644 docs/content/en/methods/page/Sitemap.md create mode 100644 docs/content/en/methods/page/Sites.md create mode 100644 docs/content/en/methods/page/Slug.md create mode 100644 docs/content/en/methods/page/Store.md create mode 100644 docs/content/en/methods/page/Summary.md create mode 100644 docs/content/en/methods/page/TableOfContents.md create mode 100644 docs/content/en/methods/page/Title.md create mode 100644 docs/content/en/methods/page/TranslationKey.md create mode 100644 docs/content/en/methods/page/Translations.md create mode 100644 docs/content/en/methods/page/Truncated.md create mode 100644 docs/content/en/methods/page/Type.md create mode 100644 docs/content/en/methods/page/Weight.md create mode 100644 docs/content/en/methods/page/WordCount.md create mode 100644 docs/content/en/methods/page/_index.md create mode 100644 docs/content/en/methods/pager/First.md create mode 100644 docs/content/en/methods/pager/HasNext.md create mode 100644 docs/content/en/methods/pager/HasPrev.md create mode 100644 docs/content/en/methods/pager/Last.md create mode 100644 docs/content/en/methods/pager/Next.md create mode 100644 docs/content/en/methods/pager/NumberOfElements.md create mode 100644 docs/content/en/methods/pager/PageGroups.md create mode 100644 docs/content/en/methods/pager/PageNumber.md create mode 100644 docs/content/en/methods/pager/PagerSize.md create mode 100644 docs/content/en/methods/pager/Pagers.md create mode 100644 docs/content/en/methods/pager/Pages.md create mode 100644 docs/content/en/methods/pager/Prev.md create mode 100644 docs/content/en/methods/pager/TotalNumberOfElements.md create mode 100644 docs/content/en/methods/pager/TotalPages.md create mode 100644 docs/content/en/methods/pager/URL.md create mode 100644 docs/content/en/methods/pager/_index.md create mode 100644 docs/content/en/methods/pages/ByDate.md create mode 100644 docs/content/en/methods/pages/ByExpiryDate.md create mode 100644 docs/content/en/methods/pages/ByLanguage.md create mode 100644 docs/content/en/methods/pages/ByLastmod.md create mode 100644 docs/content/en/methods/pages/ByLength.md create mode 100644 docs/content/en/methods/pages/ByLinkTitle.md create mode 100644 docs/content/en/methods/pages/ByParam.md create mode 100644 docs/content/en/methods/pages/ByPublishDate.md create mode 100644 docs/content/en/methods/pages/ByTitle.md create mode 100644 docs/content/en/methods/pages/ByWeight.md create mode 100644 docs/content/en/methods/pages/GroupBy.md create mode 100644 docs/content/en/methods/pages/GroupByDate.md create mode 100644 docs/content/en/methods/pages/GroupByExpiryDate.md create mode 100644 docs/content/en/methods/pages/GroupByLastmod.md create mode 100644 docs/content/en/methods/pages/GroupByParam.md create mode 100644 docs/content/en/methods/pages/GroupByParamDate.md create mode 100644 docs/content/en/methods/pages/GroupByPublishDate.md create mode 100644 docs/content/en/methods/pages/Len.md create mode 100644 docs/content/en/methods/pages/Limit.md create mode 100644 docs/content/en/methods/pages/Next.md create mode 100644 docs/content/en/methods/pages/Prev.md create mode 100644 docs/content/en/methods/pages/Related.md create mode 100644 docs/content/en/methods/pages/Reverse.md create mode 100644 docs/content/en/methods/pages/_index.md create mode 100644 docs/content/en/methods/resource/Colors.md create mode 100644 docs/content/en/methods/resource/Content.md create mode 100644 docs/content/en/methods/resource/Crop.md create mode 100644 docs/content/en/methods/resource/Data.md create mode 100644 docs/content/en/methods/resource/Err.md create mode 100644 docs/content/en/methods/resource/Exif.md create mode 100644 docs/content/en/methods/resource/Fill.md create mode 100644 docs/content/en/methods/resource/Filter.md create mode 100644 docs/content/en/methods/resource/Fit.md create mode 100644 docs/content/en/methods/resource/Height.md create mode 100644 docs/content/en/methods/resource/MediaType.md create mode 100644 docs/content/en/methods/resource/Meta.md create mode 100644 docs/content/en/methods/resource/Name.md create mode 100644 docs/content/en/methods/resource/Params.md create mode 100644 docs/content/en/methods/resource/Permalink.md create mode 100644 docs/content/en/methods/resource/Process.md create mode 100644 docs/content/en/methods/resource/Publish.md create mode 100644 docs/content/en/methods/resource/RelPermalink.md create mode 100644 docs/content/en/methods/resource/Resize.md create mode 100644 docs/content/en/methods/resource/ResourceType.md create mode 100644 docs/content/en/methods/resource/Title.md create mode 100644 docs/content/en/methods/resource/Width.md create mode 100644 docs/content/en/methods/resource/_index.md create mode 100644 docs/content/en/methods/shortcode/Get.md create mode 100644 docs/content/en/methods/shortcode/Inner.md create mode 100644 docs/content/en/methods/shortcode/InnerDeindent.md create mode 100644 docs/content/en/methods/shortcode/IsNamedParams.md create mode 100644 docs/content/en/methods/shortcode/Name.md create mode 100644 docs/content/en/methods/shortcode/Ordinal.md create mode 100644 docs/content/en/methods/shortcode/Page.md create mode 100644 docs/content/en/methods/shortcode/Params.md create mode 100644 docs/content/en/methods/shortcode/Parent.md create mode 100644 docs/content/en/methods/shortcode/Position.md create mode 100644 docs/content/en/methods/shortcode/Ref.md create mode 100644 docs/content/en/methods/shortcode/RelRef.md create mode 100644 docs/content/en/methods/shortcode/Scratch.md create mode 100644 docs/content/en/methods/shortcode/Site.md create mode 100644 docs/content/en/methods/shortcode/Store.md create mode 100644 docs/content/en/methods/shortcode/_index.md create mode 100644 docs/content/en/methods/site/AllPages.md create mode 100644 docs/content/en/methods/site/BaseURL.md create mode 100644 docs/content/en/methods/site/BuildDrafts.md create mode 100644 docs/content/en/methods/site/Config.md create mode 100644 docs/content/en/methods/site/Copyright.md create mode 100644 docs/content/en/methods/site/Data.md create mode 100644 docs/content/en/methods/site/Dimension.md create mode 100644 docs/content/en/methods/site/GetPage.md create mode 100644 docs/content/en/methods/site/Home.md create mode 100644 docs/content/en/methods/site/IsDefault.md create mode 100644 docs/content/en/methods/site/Language.md create mode 100644 docs/content/en/methods/site/LanguagePrefix.md create mode 100644 docs/content/en/methods/site/Languages.md create mode 100644 docs/content/en/methods/site/Lastmod.md create mode 100644 docs/content/en/methods/site/MainSections.md create mode 100644 docs/content/en/methods/site/Menus.md create mode 100644 docs/content/en/methods/site/Pages.md create mode 100644 docs/content/en/methods/site/Param.md create mode 100644 docs/content/en/methods/site/Params.md create mode 100644 docs/content/en/methods/site/RegularPages.md create mode 100644 docs/content/en/methods/site/Role.md create mode 100644 docs/content/en/methods/site/Sections.md create mode 100644 docs/content/en/methods/site/Sites.md create mode 100644 docs/content/en/methods/site/Store.md create mode 100644 docs/content/en/methods/site/Taxonomies.md create mode 100644 docs/content/en/methods/site/Title.md create mode 100644 docs/content/en/methods/site/Version.md create mode 100644 docs/content/en/methods/site/_index.md create mode 100644 docs/content/en/methods/taxonomy/Alphabetical.md create mode 100644 docs/content/en/methods/taxonomy/ByCount.md create mode 100644 docs/content/en/methods/taxonomy/Count.md create mode 100644 docs/content/en/methods/taxonomy/Get.md create mode 100644 docs/content/en/methods/taxonomy/Page.md create mode 100644 docs/content/en/methods/taxonomy/_index.md create mode 100644 docs/content/en/methods/time/Add.md create mode 100644 docs/content/en/methods/time/AddDate.md create mode 100644 docs/content/en/methods/time/After.md create mode 100644 docs/content/en/methods/time/Before.md create mode 100644 docs/content/en/methods/time/Day.md create mode 100644 docs/content/en/methods/time/Equal.md create mode 100644 docs/content/en/methods/time/Format.md create mode 100644 docs/content/en/methods/time/Hour.md create mode 100644 docs/content/en/methods/time/IsDST.md create mode 100644 docs/content/en/methods/time/IsZero.md create mode 100644 docs/content/en/methods/time/Local.md create mode 100644 docs/content/en/methods/time/Minute.md create mode 100644 docs/content/en/methods/time/Month.md create mode 100644 docs/content/en/methods/time/Nanosecond.md create mode 100644 docs/content/en/methods/time/Round.md create mode 100644 docs/content/en/methods/time/Second.md create mode 100644 docs/content/en/methods/time/Sub.md create mode 100644 docs/content/en/methods/time/Truncate.md create mode 100644 docs/content/en/methods/time/UTC.md create mode 100644 docs/content/en/methods/time/Unix.md create mode 100644 docs/content/en/methods/time/UnixMicro.md create mode 100644 docs/content/en/methods/time/UnixMilli.md create mode 100644 docs/content/en/methods/time/UnixNano.md create mode 100644 docs/content/en/methods/time/Weekday.md create mode 100644 docs/content/en/methods/time/Year.md create mode 100644 docs/content/en/methods/time/YearDay.md create mode 100644 docs/content/en/methods/time/_index.md create mode 100644 docs/content/en/news/_content.gotmpl create mode 100644 docs/content/en/news/_index.md create mode 100644 docs/content/en/quick-reference/_index.md create mode 100644 docs/content/en/quick-reference/emojis.md create mode 100644 docs/content/en/quick-reference/functions.md create mode 100644 docs/content/en/quick-reference/glob-patterns.md create mode 100644 docs/content/en/quick-reference/glossary/_index.md create mode 100644 docs/content/en/quick-reference/glossary/action.md create mode 100644 docs/content/en/quick-reference/glossary/archetype.md create mode 100644 docs/content/en/quick-reference/glossary/argument.md create mode 100644 docs/content/en/quick-reference/glossary/array.md create mode 100644 docs/content/en/quick-reference/glossary/asset-pipeline.md create mode 100644 docs/content/en/quick-reference/glossary/bool.md create mode 100644 docs/content/en/quick-reference/glossary/boolean.md create mode 100644 docs/content/en/quick-reference/glossary/branch-bundle.md create mode 100644 docs/content/en/quick-reference/glossary/branch.md create mode 100644 docs/content/en/quick-reference/glossary/build-artifacts.md create mode 100644 docs/content/en/quick-reference/glossary/build.md create mode 100644 docs/content/en/quick-reference/glossary/bundle.md create mode 100644 docs/content/en/quick-reference/glossary/cache.md create mode 100644 docs/content/en/quick-reference/glossary/canonical-output-format.md create mode 100644 docs/content/en/quick-reference/glossary/chain.md create mode 100644 docs/content/en/quick-reference/glossary/cicd.md create mode 100644 docs/content/en/quick-reference/glossary/cjk.md create mode 100644 docs/content/en/quick-reference/glossary/cli.md create mode 100644 docs/content/en/quick-reference/glossary/collection.md create mode 100644 docs/content/en/quick-reference/glossary/component.md create mode 100644 docs/content/en/quick-reference/glossary/content-adapter.md create mode 100644 docs/content/en/quick-reference/glossary/content-dimension.md create mode 100644 docs/content/en/quick-reference/glossary/content-format.md create mode 100644 docs/content/en/quick-reference/glossary/content-type.md create mode 100644 docs/content/en/quick-reference/glossary/content-view.md create mode 100644 docs/content/en/quick-reference/glossary/context.md create mode 100644 docs/content/en/quick-reference/glossary/default-language.md create mode 100644 docs/content/en/quick-reference/glossary/default-role.md create mode 100644 docs/content/en/quick-reference/glossary/default-site.md create mode 100644 docs/content/en/quick-reference/glossary/default-sort-order.md create mode 100644 docs/content/en/quick-reference/glossary/default-version.md create mode 100644 docs/content/en/quick-reference/glossary/dependency-graph.md create mode 100644 docs/content/en/quick-reference/glossary/dimension.md create mode 100644 docs/content/en/quick-reference/glossary/duration.md create mode 100644 docs/content/en/quick-reference/glossary/element.md create mode 100644 docs/content/en/quick-reference/glossary/embedded-template.md create mode 100644 docs/content/en/quick-reference/glossary/environment.md create mode 100644 docs/content/en/quick-reference/glossary/field.md create mode 100644 docs/content/en/quick-reference/glossary/flag.md create mode 100644 docs/content/en/quick-reference/glossary/float.md create mode 100644 docs/content/en/quick-reference/glossary/floating-point.md create mode 100644 docs/content/en/quick-reference/glossary/fragment.md create mode 100644 docs/content/en/quick-reference/glossary/front-matter.md create mode 100644 docs/content/en/quick-reference/glossary/function.md create mode 100644 docs/content/en/quick-reference/glossary/glob-pattern.md create mode 100644 docs/content/en/quick-reference/glossary/glob-slice.md create mode 100644 docs/content/en/quick-reference/glossary/global-resource.md create mode 100644 docs/content/en/quick-reference/glossary/headless-bundle.md create mode 100644 docs/content/en/quick-reference/glossary/i18n.md create mode 100644 docs/content/en/quick-reference/glossary/iana.md create mode 100644 docs/content/en/quick-reference/glossary/identifier.md create mode 100644 docs/content/en/quick-reference/glossary/int.md create mode 100644 docs/content/en/quick-reference/glossary/integer.md create mode 100644 docs/content/en/quick-reference/glossary/interleave.md create mode 100644 docs/content/en/quick-reference/glossary/internationalization.md create mode 100644 docs/content/en/quick-reference/glossary/interpreted-string-literal.md create mode 100644 docs/content/en/quick-reference/glossary/interval.md create mode 100644 docs/content/en/quick-reference/glossary/kind.md create mode 100644 docs/content/en/quick-reference/glossary/l10n.md create mode 100644 docs/content/en/quick-reference/glossary/language.md create mode 100644 docs/content/en/quick-reference/glossary/layout.md create mode 100644 docs/content/en/quick-reference/glossary/leaf-bundle.md create mode 100644 docs/content/en/quick-reference/glossary/lexer.md create mode 100644 docs/content/en/quick-reference/glossary/list-page.md create mode 100644 docs/content/en/quick-reference/glossary/localization.md create mode 100644 docs/content/en/quick-reference/glossary/logical-path.md create mode 100644 docs/content/en/quick-reference/glossary/logical-tree.md create mode 100644 docs/content/en/quick-reference/glossary/map.md create mode 100644 docs/content/en/quick-reference/glossary/markdown-attribute.md create mode 100644 docs/content/en/quick-reference/glossary/marshal.md create mode 100644 docs/content/en/quick-reference/glossary/media-type.md create mode 100644 docs/content/en/quick-reference/glossary/method.md create mode 100644 docs/content/en/quick-reference/glossary/module.md create mode 100644 docs/content/en/quick-reference/glossary/mount.md create mode 100644 docs/content/en/quick-reference/glossary/node.md create mode 100644 docs/content/en/quick-reference/glossary/noop.md create mode 100644 docs/content/en/quick-reference/glossary/object.md create mode 100644 docs/content/en/quick-reference/glossary/ordered-taxonomy.md create mode 100644 docs/content/en/quick-reference/glossary/output-format.md create mode 100644 docs/content/en/quick-reference/glossary/page-bundle.md create mode 100644 docs/content/en/quick-reference/glossary/page-collection.md create mode 100644 docs/content/en/quick-reference/glossary/page-kind.md create mode 100644 docs/content/en/quick-reference/glossary/page-matcher.md create mode 100644 docs/content/en/quick-reference/glossary/page-relative.md create mode 100644 docs/content/en/quick-reference/glossary/page-resource.md create mode 100644 docs/content/en/quick-reference/glossary/pager.md create mode 100644 docs/content/en/quick-reference/glossary/paginate.md create mode 100644 docs/content/en/quick-reference/glossary/pagination.md create mode 100644 docs/content/en/quick-reference/glossary/paginator.md create mode 100644 docs/content/en/quick-reference/glossary/parameter.md create mode 100644 docs/content/en/quick-reference/glossary/partial-decorator.md create mode 100644 docs/content/en/quick-reference/glossary/partial.md create mode 100644 docs/content/en/quick-reference/glossary/permalink.md create mode 100644 docs/content/en/quick-reference/glossary/pipe.md create mode 100644 docs/content/en/quick-reference/glossary/pipeline.md create mode 100644 docs/content/en/quick-reference/glossary/pretty-url.md create mode 100644 docs/content/en/quick-reference/glossary/primary-output-format.md create mode 100644 docs/content/en/quick-reference/glossary/processable-image.md create mode 100644 docs/content/en/quick-reference/glossary/project.md create mode 100644 docs/content/en/quick-reference/glossary/publish.md create mode 100644 docs/content/en/quick-reference/glossary/raw-string-literal.md create mode 100644 docs/content/en/quick-reference/glossary/regular-expression.md create mode 100644 docs/content/en/quick-reference/glossary/regular-page.md create mode 100644 docs/content/en/quick-reference/glossary/relative-permalink.md create mode 100644 docs/content/en/quick-reference/glossary/remote-resource.md create mode 100644 docs/content/en/quick-reference/glossary/render-hook.md create mode 100644 docs/content/en/quick-reference/glossary/resource-type.md create mode 100644 docs/content/en/quick-reference/glossary/resource.md create mode 100644 docs/content/en/quick-reference/glossary/role.md create mode 100644 docs/content/en/quick-reference/glossary/rune-literal.md create mode 100644 docs/content/en/quick-reference/glossary/rune.md create mode 100644 docs/content/en/quick-reference/glossary/scalar.md create mode 100644 docs/content/en/quick-reference/glossary/scope.md create mode 100644 docs/content/en/quick-reference/glossary/section-page.md create mode 100644 docs/content/en/quick-reference/glossary/section.md create mode 100644 docs/content/en/quick-reference/glossary/seed.md create mode 100644 docs/content/en/quick-reference/glossary/segment.md create mode 100644 docs/content/en/quick-reference/glossary/server-relative.md create mode 100644 docs/content/en/quick-reference/glossary/shortcode.md create mode 100644 docs/content/en/quick-reference/glossary/site-relative.md create mode 100644 docs/content/en/quick-reference/glossary/site-root.md create mode 100644 docs/content/en/quick-reference/glossary/site.md create mode 100644 docs/content/en/quick-reference/glossary/sites-complements.md create mode 100644 docs/content/en/quick-reference/glossary/sites-matrix.md create mode 100644 docs/content/en/quick-reference/glossary/slice.md create mode 100644 docs/content/en/quick-reference/glossary/string.md create mode 100644 docs/content/en/quick-reference/glossary/taxonomic-weight.md create mode 100644 docs/content/en/quick-reference/glossary/taxonomy-object.md create mode 100644 docs/content/en/quick-reference/glossary/taxonomy-page.md create mode 100644 docs/content/en/quick-reference/glossary/taxonomy.md create mode 100644 docs/content/en/quick-reference/glossary/template-action.md create mode 100644 docs/content/en/quick-reference/glossary/template.md create mode 100644 docs/content/en/quick-reference/glossary/term-page.md create mode 100644 docs/content/en/quick-reference/glossary/term.md create mode 100644 docs/content/en/quick-reference/glossary/theme.md create mode 100644 docs/content/en/quick-reference/glossary/token.md create mode 100644 docs/content/en/quick-reference/glossary/translation-table.md create mode 100644 docs/content/en/quick-reference/glossary/type.md create mode 100644 docs/content/en/quick-reference/glossary/ugly-url.md create mode 100644 docs/content/en/quick-reference/glossary/unified-file-system.md create mode 100644 docs/content/en/quick-reference/glossary/unmarshal.md create mode 100644 docs/content/en/quick-reference/glossary/utc.md create mode 100644 docs/content/en/quick-reference/glossary/variable.md create mode 100644 docs/content/en/quick-reference/glossary/vendor.md create mode 100644 docs/content/en/quick-reference/glossary/version.md create mode 100644 docs/content/en/quick-reference/glossary/walk.md create mode 100644 docs/content/en/quick-reference/glossary/weight.md create mode 100644 docs/content/en/quick-reference/glossary/weighted-page.md create mode 100644 docs/content/en/quick-reference/glossary/workspace.md create mode 100644 docs/content/en/quick-reference/glossary/wrapper-component.md create mode 100644 docs/content/en/quick-reference/glossary/zero-time.md create mode 100644 docs/content/en/quick-reference/methods.md create mode 100644 docs/content/en/quick-reference/page-collections.md create mode 100644 docs/content/en/quick-reference/syntax-highlighting-styles.md create mode 100644 docs/content/en/render-hooks/_index.md create mode 100755 docs/content/en/render-hooks/blockquotes.md create mode 100755 docs/content/en/render-hooks/code-blocks.md create mode 100755 docs/content/en/render-hooks/headings.md create mode 100755 docs/content/en/render-hooks/images.md create mode 100755 docs/content/en/render-hooks/introduction.md create mode 100755 docs/content/en/render-hooks/links.md create mode 100755 docs/content/en/render-hooks/passthrough.md create mode 100755 docs/content/en/render-hooks/tables.md create mode 100644 docs/content/en/shortcodes/_index.md create mode 100755 docs/content/en/shortcodes/details.md create mode 100755 docs/content/en/shortcodes/figure.md create mode 100755 docs/content/en/shortcodes/highlight.md create mode 100755 docs/content/en/shortcodes/instagram.md create mode 100755 docs/content/en/shortcodes/param.md create mode 100755 docs/content/en/shortcodes/qr.md create mode 100755 docs/content/en/shortcodes/ref.md create mode 100755 docs/content/en/shortcodes/relref.md create mode 100755 docs/content/en/shortcodes/vimeo.md create mode 100755 docs/content/en/shortcodes/x.md create mode 100755 docs/content/en/shortcodes/youtube.md create mode 100644 docs/content/en/templates/404.md create mode 100644 docs/content/en/templates/_index.md create mode 100644 docs/content/en/templates/embedded.md create mode 100644 docs/content/en/templates/introduction.md create mode 100644 docs/content/en/templates/lookup-order.md create mode 100644 docs/content/en/templates/menu.md create mode 100644 docs/content/en/templates/new-templatesystem-overview.md create mode 100644 docs/content/en/templates/pagination.md create mode 100644 docs/content/en/templates/partial-decorators.md create mode 100644 docs/content/en/templates/robots.md create mode 100644 docs/content/en/templates/rss.md create mode 100644 docs/content/en/templates/shortcode.md create mode 100644 docs/content/en/templates/sitemap.md create mode 100644 docs/content/en/templates/types.md create mode 100644 docs/content/en/tools/_index.md create mode 100644 docs/content/en/tools/editors.md create mode 100644 docs/content/en/tools/front-ends.md create mode 100644 docs/content/en/tools/migrations.md create mode 100644 docs/content/en/tools/other.md create mode 100644 docs/content/en/tools/search.md create mode 100644 docs/content/en/troubleshooting/_index.md create mode 100644 docs/content/en/troubleshooting/audit/index.md create mode 100644 docs/content/en/troubleshooting/audit/screen-capture.png create mode 100644 docs/content/en/troubleshooting/deprecation.md create mode 100644 docs/content/en/troubleshooting/faq.md create mode 100644 docs/content/en/troubleshooting/inspection.md create mode 100644 docs/content/en/troubleshooting/logging.md create mode 100644 docs/content/en/troubleshooting/performance.md create mode 100644 docs/data/articles.toml create mode 100644 docs/data/docs.yaml create mode 100644 docs/data/embedded_template_urls.toml create mode 100644 docs/data/homepagetweets.toml create mode 100644 docs/data/keywords.yaml create mode 100644 docs/data/page_filters.yaml create mode 100644 docs/data/sponsors.toml create mode 100644 docs/go.mod create mode 100644 docs/hugo.toml create mode 100644 docs/hugo.work create mode 100644 docs/hugoreleaser.yaml create mode 100644 docs/layouts/404.html create mode 100644 docs/layouts/_markup/render-blockquote.html create mode 100644 docs/layouts/_markup/render-codeblock.html create mode 100644 docs/layouts/_markup/render-link.html create mode 100644 docs/layouts/_markup/render-passthrough.html create mode 100644 docs/layouts/_markup/render-table.html create mode 100644 docs/layouts/_partials/docs/functions-aliases.html create mode 100644 docs/layouts/_partials/docs/functions-return-type.html create mode 100644 docs/layouts/_partials/docs/functions-signatures.html create mode 100644 docs/layouts/_partials/helpers/debug/list-item-metadata.html create mode 100644 docs/layouts/_partials/helpers/funcs/color-from-string.html create mode 100644 docs/layouts/_partials/helpers/funcs/get-github-info.html create mode 100644 docs/layouts/_partials/helpers/funcs/get-remote-data.html create mode 100644 docs/layouts/_partials/helpers/gtag.html create mode 100644 docs/layouts/_partials/helpers/linkcss.html create mode 100644 docs/layouts/_partials/helpers/linkjs.html create mode 100644 docs/layouts/_partials/helpers/picture.html create mode 100644 docs/layouts/_partials/helpers/validation/validate-keywords.html create mode 100644 docs/layouts/_partials/layouts/blocks/alert.html create mode 100644 docs/layouts/_partials/layouts/blocks/feature-state.html create mode 100644 docs/layouts/_partials/layouts/blocks/modal.html create mode 100644 docs/layouts/_partials/layouts/breadcrumbs.html create mode 100644 docs/layouts/_partials/layouts/date.html create mode 100644 docs/layouts/_partials/layouts/docsheader.html create mode 100644 docs/layouts/_partials/layouts/footer.html create mode 100644 docs/layouts/_partials/layouts/head/head-js.html create mode 100644 docs/layouts/_partials/layouts/head/head.html create mode 100644 docs/layouts/_partials/layouts/head/speculationrules.html create mode 100644 docs/layouts/_partials/layouts/header/githubstars.html create mode 100644 docs/layouts/_partials/layouts/header/header.html create mode 100644 docs/layouts/_partials/layouts/header/mastodon.html create mode 100644 docs/layouts/_partials/layouts/header/mobilemenu.html create mode 100644 docs/layouts/_partials/layouts/header/qr.html create mode 100644 docs/layouts/_partials/layouts/header/theme.html create mode 100644 docs/layouts/_partials/layouts/home/features.html create mode 100644 docs/layouts/_partials/layouts/home/opensource.html create mode 100644 docs/layouts/_partials/layouts/home/sponsors.html create mode 100644 docs/layouts/_partials/layouts/hooks/body-end.html create mode 100644 docs/layouts/_partials/layouts/hooks/body-main-start.html create mode 100644 docs/layouts/_partials/layouts/hooks/body-start.html create mode 100644 docs/layouts/_partials/layouts/icons.html create mode 100644 docs/layouts/_partials/layouts/in-this-section.html create mode 100644 docs/layouts/_partials/layouts/page-edit.html create mode 100644 docs/layouts/_partials/layouts/related.html create mode 100644 docs/layouts/_partials/layouts/search/algolialogo.html create mode 100644 docs/layouts/_partials/layouts/search/button.html create mode 100644 docs/layouts/_partials/layouts/search/input.html create mode 100644 docs/layouts/_partials/layouts/search/results.html create mode 100644 docs/layouts/_partials/layouts/templates.html create mode 100644 docs/layouts/_partials/layouts/toc.html create mode 100644 docs/layouts/_partials/opengraph/get-featured-image.html create mode 100644 docs/layouts/_partials/opengraph/opengraph.html create mode 100644 docs/layouts/_shortcodes/chroma-lexers.html create mode 100644 docs/layouts/_shortcodes/code-toggle.html create mode 100644 docs/layouts/_shortcodes/current-go-version.html create mode 100644 docs/layouts/_shortcodes/datatable.html create mode 100644 docs/layouts/_shortcodes/deprecated-in.html create mode 100644 docs/layouts/_shortcodes/eturl.html create mode 100644 docs/layouts/_shortcodes/get-page-desc.html create mode 100644 docs/layouts/_shortcodes/glossary-term.html create mode 100644 docs/layouts/_shortcodes/glossary.html create mode 100644 docs/layouts/_shortcodes/hl.html create mode 100644 docs/layouts/_shortcodes/img.html create mode 100644 docs/layouts/_shortcodes/imgproc.html create mode 100644 docs/layouts/_shortcodes/include.html create mode 100644 docs/layouts/_shortcodes/module-mounts-note.html create mode 100644 docs/layouts/_shortcodes/new-in.html create mode 100644 docs/layouts/_shortcodes/newtemplatesystem.html create mode 100644 docs/layouts/_shortcodes/per-lang-config-keys.html create mode 100644 docs/layouts/_shortcodes/quick-reference.html create mode 100644 docs/layouts/_shortcodes/render-list-of-pages-in-section.html create mode 100644 docs/layouts/_shortcodes/render-table-of-pages-in-section.html create mode 100644 docs/layouts/_shortcodes/root-configuration-keys.html create mode 100644 docs/layouts/_shortcodes/syntax-highlighting-styles.html create mode 100644 docs/layouts/baseof.html create mode 100644 docs/layouts/home.headers create mode 100644 docs/layouts/home.html create mode 100644 docs/layouts/home.redir create mode 100644 docs/layouts/list.html create mode 100644 docs/layouts/list.rss.xml create mode 100644 docs/layouts/single.html create mode 100644 docs/netlify.toml create mode 100644 docs/package.json create mode 100644 docs/static/android-chrome-144x144.png create mode 100644 docs/static/android-chrome-192x192.png create mode 100644 docs/static/android-chrome-256x256.png create mode 100644 docs/static/android-chrome-36x36.png create mode 100644 docs/static/android-chrome-48x48.png create mode 100644 docs/static/android-chrome-72x72.png create mode 100644 docs/static/android-chrome-96x96.png create mode 100644 docs/static/apple-touch-icon.png create mode 100644 docs/static/favicon-16x16.png create mode 100644 docs/static/favicon-32x32.png create mode 100644 docs/static/favicon.ico create mode 100644 docs/static/fonts/Mulish-Italic-VariableFont_wght.ttf create mode 100644 docs/static/fonts/Mulish-VariableFont_wght.ttf create mode 100644 docs/static/images/gopher-hero.svg create mode 100644 docs/static/images/gopher-side_color.svg create mode 100644 docs/static/images/hugo-logo-wide.svg create mode 100644 docs/static/img/examples/trees.svg create mode 100644 docs/static/img/hugo-logo-med.png create mode 100644 docs/static/img/hugo-logo.png create mode 100644 docs/static/img/hugo.png create mode 100644 docs/static/img/hugoSM.png create mode 100644 docs/static/manifest.json create mode 100644 docs/static/mstile-144x144.png create mode 100644 docs/static/mstile-150x150.png create mode 100644 docs/static/mstile-310x310.png create mode 100644 docs/static/safari-pinned-tab.svg create mode 100644 docs/static/shared/examples/data/books.json create mode 100644 docs/static/shared/examples/images/interpreting-the-french-revolution.webp create mode 100644 docs/static/shared/examples/images/les-misérables.webp create mode 100644 docs/static/shared/examples/images/the-ancien-régime-and-the-revolution.webp create mode 100644 docs/static/shared/examples/images/the-hunchback-of-notre-dame.webp create mode 100644 docshelper/docs.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 helpers/content.go create mode 100644 helpers/content_test.go create mode 100644 helpers/docshelper.go create mode 100644 helpers/emoji.go create mode 100644 helpers/emoji_test.go create mode 100644 helpers/general.go create mode 100644 helpers/general_test.go create mode 100644 helpers/path.go create mode 100644 helpers/path_test.go create mode 100644 helpers/pathspec.go create mode 100644 helpers/processing_stats.go create mode 100644 helpers/testhelpers_test.go create mode 100644 helpers/url.go create mode 100644 helpers/url_test.go create mode 100644 htesting/hqt/checkers.go create mode 100644 htesting/test_helpers.go create mode 100644 htesting/test_helpers_test.go create mode 100644 hugofs/component_fs.go create mode 100644 hugofs/createcounting_fs.go create mode 100644 hugofs/decorators.go create mode 100644 hugofs/dirsmerger.go create mode 100644 hugofs/fileinfo.go create mode 100644 hugofs/fileinfo_test.go create mode 100644 hugofs/filename_filter_fs.go create mode 100644 hugofs/filename_filter_fs_test.go create mode 100644 hugofs/files/classifier.go create mode 100644 hugofs/files/classifier_test.go create mode 100644 hugofs/fs.go create mode 100644 hugofs/fs_test.go create mode 100644 hugofs/glob.go create mode 100644 hugofs/glob_test.go create mode 100644 hugofs/hasbytes_fs.go create mode 100644 hugofs/hashing_fs.go create mode 100644 hugofs/hashing_fs_test.go create mode 100644 hugofs/hglob/filename_filter.go create mode 100644 hugofs/hglob/filename_filter_test.go create mode 100644 hugofs/hglob/glob.go create mode 100644 hugofs/hglob/glob_test.go create mode 100644 hugofs/hugofs_integration_test.go create mode 100644 hugofs/noop_fs.go create mode 100644 hugofs/nosymlinks_fs.go create mode 100644 hugofs/openfiles_fs.go create mode 100644 hugofs/rootmapping_fs.go create mode 100644 hugofs/rootmapping_fs_test.go create mode 100644 hugofs/stacktracer_fs.go create mode 100644 hugofs/walk.go create mode 100644 hugofs/walk_test.go create mode 100644 hugolib/404_test.go create mode 100644 hugolib/alias.go create mode 100644 hugolib/alias_test.go create mode 100644 hugolib/breaking_changes_test.go create mode 100644 hugolib/cascade_test.go create mode 100644 hugolib/codeowners.go create mode 100644 hugolib/collections.go create mode 100644 hugolib/config.go create mode 100644 hugolib/config_legacy1_test.go create mode 100644 hugolib/config_test.go create mode 100644 hugolib/configdir_test.go create mode 100644 hugolib/content_factory.go create mode 100644 hugolib/content_map.go create mode 100644 hugolib/content_map_page.go create mode 100644 hugolib/content_map_page_assembler.go create mode 100644 hugolib/content_map_page_contentnode.go create mode 100644 hugolib/content_map_page_contentnodeshifter.go create mode 100644 hugolib/content_map_test.go create mode 100644 hugolib/content_render_hooks_test.go create mode 100644 hugolib/datafiles_test.go create mode 100644 hugolib/dates_test.go create mode 100644 hugolib/disableKinds_test.go create mode 100644 hugolib/doctree/nodeshiftree_test.go create mode 100644 hugolib/doctree/nodeshifttree.go create mode 100644 hugolib/doctree/nodetransformstate_string.go create mode 100644 hugolib/doctree/simpletree.go create mode 100644 hugolib/doctree/simpletree_test.go create mode 100644 hugolib/doctree/support.go create mode 100644 hugolib/doctree/treeshifttree.go create mode 100644 hugolib/doctree/treeshifttree_test.go create mode 100644 hugolib/fileInfo.go create mode 100644 hugolib/fileInfo_test.go create mode 100644 hugolib/filesystems/basefs.go create mode 100644 hugolib/filesystems/basefs_test.go create mode 100644 hugolib/frontmatter_test.go create mode 100644 hugolib/gitinfo.go create mode 100644 hugolib/gitinfo_github_test.go create mode 100644 hugolib/hugo_modules_test.go create mode 100644 hugolib/hugo_sites.go create mode 100644 hugolib/hugo_sites_build.go create mode 100644 hugolib/hugo_sites_build_errors_test.go create mode 100644 hugolib/hugo_sites_build_test.go create mode 100644 hugolib/hugo_sites_multihost_test.go create mode 100644 hugolib/hugo_smoke_test.go create mode 100644 hugolib/hugolib_integration_test.go create mode 100644 hugolib/integrationtest_builder.go create mode 100644 hugolib/language_content_dir_test.go create mode 100644 hugolib/language_test.go create mode 100644 hugolib/menu_test.go create mode 100644 hugolib/minify_publisher_test.go create mode 100644 hugolib/mount_filters_test.go create mode 100644 hugolib/page.go create mode 100644 hugolib/page__common.go create mode 100644 hugolib/page__content.go create mode 100644 hugolib/page__data.go create mode 100644 hugolib/page__fragments_test.go create mode 100644 hugolib/page__menus.go create mode 100644 hugolib/page__meta.go create mode 100644 hugolib/page__meta_test.go create mode 100644 hugolib/page__new.go create mode 100644 hugolib/page__output.go create mode 100644 hugolib/page__paginator.go create mode 100644 hugolib/page__paths.go create mode 100644 hugolib/page__per_output.go create mode 100644 hugolib/page__position.go create mode 100644 hugolib/page__ref.go create mode 100644 hugolib/page__tree.go create mode 100644 hugolib/page_kinds.go create mode 100644 hugolib/page_permalink_test.go create mode 100644 hugolib/page_resource_metadata_test.go create mode 100644 hugolib/page_test.go create mode 100644 hugolib/page_unwrap.go create mode 100644 hugolib/page_unwrap_test.go create mode 100644 hugolib/pagebundler_test.go create mode 100644 hugolib/pagecollections.go create mode 100644 hugolib/pagecollections_test.go create mode 100644 hugolib/pages_capture.go create mode 100644 hugolib/pages_language_merge_test.go create mode 100644 hugolib/pages_test.go create mode 100644 hugolib/pagesfromdata/pagesfromgotmpl.go create mode 100644 hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go create mode 100644 hugolib/pagesfromdata/pagesfromgotmpl_test.go create mode 100644 hugolib/paginator_test.go create mode 100644 hugolib/params_test.go create mode 100644 hugolib/paths/paths.go create mode 100644 hugolib/permalinker.go create mode 100644 hugolib/prune_resources.go create mode 100644 hugolib/rebuild_test.go create mode 100644 hugolib/rendershortcodes_test.go create mode 100644 hugolib/renderstring_test.go create mode 100644 hugolib/resource_chain_test.go create mode 100644 hugolib/robotstxt_test.go create mode 100644 hugolib/roles/roles.go create mode 100644 hugolib/roles/roles_integration_test.go create mode 100644 hugolib/rss_test.go create mode 100644 hugolib/securitypolicies_test.go create mode 100644 hugolib/segments/segments.go create mode 100644 hugolib/segments/segments_integration_test.go create mode 100644 hugolib/segments/segments_test.go create mode 100644 hugolib/shortcode.go create mode 100644 hugolib/shortcode_page.go create mode 100644 hugolib/shortcode_test.go create mode 100644 hugolib/site.go create mode 100644 hugolib/siteJSONEncode_test.go create mode 100644 hugolib/site_render.go create mode 100644 hugolib/site_sections.go create mode 100644 hugolib/site_sections_test.go create mode 100644 hugolib/site_sites_test.go create mode 100644 hugolib/site_stats_test.go create mode 100644 hugolib/site_test.go create mode 100644 hugolib/site_url_test.go create mode 100644 hugolib/sitemap_test.go create mode 100644 hugolib/sitesmatrix/dimensions.go create mode 100644 hugolib/sitesmatrix/dimensions_test.go create mode 100644 hugolib/sitesmatrix/sitematrix_integration_test.go create mode 100644 hugolib/sitesmatrix/vectorstores.go create mode 100644 hugolib/sitesmatrix/vectorstores_test.go create mode 100644 hugolib/taxonomy_test.go create mode 100644 hugolib/template_test.go create mode 100644 hugolib/testdata/cities.csv create mode 100644 hugolib/testdata/fakejson.json create mode 100644 hugolib/testdata/fruits.json create mode 100644 hugolib/testdata/redis.cn.md create mode 100644 hugolib/testdata/sunset.jpg create mode 100644 hugolib/testdata/what-is-markdown.md create mode 100644 hugolib/testsite/.gitignore create mode 100644 hugolib/testsite/CODEOWNERS create mode 100644 hugolib/testsite/content/first-post.md create mode 100644 hugolib/testsite/content_nn/first-post.md create mode 100644 hugolib/versions/versions.go create mode 100644 hugolib/versions/versions_integration_test.go create mode 100644 hugoreleaser.env create mode 100644 hugoreleaser.yaml create mode 100644 identity/finder.go create mode 100644 identity/finder_test.go create mode 100644 identity/identity.go create mode 100644 identity/identity_test.go create mode 100644 identity/identitytesting/identitytesting.go create mode 100644 identity/predicate_identity.go create mode 100644 identity/predicate_identity_test.go create mode 100644 identity/question.go create mode 100644 identity/question_test.go create mode 100644 internal/js/api.go create mode 100644 internal/js/esbuild/batch-esm-runner.gotmpl create mode 100644 internal/js/esbuild/batch.go create mode 100644 internal/js/esbuild/batch_integration_test.go create mode 100644 internal/js/esbuild/build.go create mode 100644 internal/js/esbuild/helpers.go create mode 100644 internal/js/esbuild/options.go create mode 100644 internal/js/esbuild/options_test.go create mode 100644 internal/js/esbuild/resolve.go create mode 100644 internal/js/esbuild/resolve_test.go create mode 100644 internal/js/esbuild/sourcemap.go create mode 100644 internal/warpc/avif.go create mode 100644 internal/warpc/avif_color_test.go create mode 100644 internal/warpc/avif_integration_test.go create mode 100755 internal/warpc/buildjs.sh create mode 100755 internal/warpc/buildwebp.sh create mode 100644 internal/warpc/deps/parson/package.json create mode 100644 internal/warpc/deps/parson/parson.c create mode 100644 internal/warpc/deps/parson/parson.h create mode 100644 internal/warpc/genavif/.gitignore create mode 100644 internal/warpc/genavif/Makefile create mode 100644 internal/warpc/genavif/avif.c create mode 100644 internal/warpc/genavif/go.mod create mode 100644 internal/warpc/genavif/wasi-sdk-cross.txt create mode 100644 internal/warpc/genjs/main.go create mode 100644 internal/warpc/genwebp/.gitignore create mode 100644 internal/warpc/genwebp/Makefile create mode 100644 internal/warpc/genwebp/deps/clib/Makefile create mode 100644 internal/warpc/genwebp/go.mod create mode 100644 internal/warpc/genwebp/webp.c create mode 100644 internal/warpc/js/.gitignore create mode 100644 internal/warpc/js/common.js create mode 100644 internal/warpc/js/greet.bundle.js create mode 100644 internal/warpc/js/greet.js create mode 100644 internal/warpc/js/package.json create mode 100644 internal/warpc/js/renderkatex.bundle.js create mode 100644 internal/warpc/js/renderkatex.js create mode 100644 internal/warpc/katex.go create mode 100644 internal/warpc/warpc.go create mode 100644 internal/warpc/warpc_test.go create mode 100755 internal/warpc/wasm/avif.wasm create mode 100644 internal/warpc/wasm/greet.wasm create mode 100644 internal/warpc/wasm/quickjs.wasm create mode 100644 internal/warpc/wasm/renderkatex.wasm create mode 100755 internal/warpc/wasm/webp.wasm create mode 100755 internal/warpc/watchjs.sh create mode 100755 internal/warpc/watchwebp.sh create mode 100644 internal/warpc/webp.go create mode 100644 internal/warpc/webp_integration_test.go create mode 100644 langs/config.go create mode 100644 langs/i18n/i18n.go create mode 100644 langs/i18n/i18n_integration_test.go create mode 100644 langs/i18n/i18n_test.go create mode 100644 langs/i18n/translationProvider.go create mode 100644 langs/language.go create mode 100644 langs/language_test.go create mode 100644 langs/languages_integration_test.go create mode 100644 livereload/connection.go create mode 100644 livereload/gen/livereload-hugo-plugin.js create mode 100644 livereload/gen/main.go create mode 100644 livereload/hub.go create mode 100644 livereload/livereload.go create mode 100644 livereload/livereload.js create mode 100644 livereload/livereload.min.js create mode 100644 magefile.go create mode 100644 main.go create mode 100644 main_test.go create mode 100644 main_withdeploy_off_test.go create mode 100644 main_withdeploy_test.go create mode 100644 markup/asciidocext/asciidoc_integration_test.go create mode 100644 markup/asciidocext/asciidocext_config/config.go create mode 100644 markup/asciidocext/convert.go create mode 100644 markup/asciidocext/convert_test.go create mode 100644 markup/asciidocext/internal/converter.go create mode 100644 markup/blackfriday/anchors.go create mode 100644 markup/converter/converter.go create mode 100644 markup/converter/hooks/hooks.go create mode 100644 markup/goldmark/autoid.go create mode 100644 markup/goldmark/autoid_test.go create mode 100644 markup/goldmark/blockquotes/blockquotes.go create mode 100644 markup/goldmark/blockquotes/blockquotes_integration_test.go create mode 100644 markup/goldmark/blockquotes/blockquotes_test.go create mode 100644 markup/goldmark/codeblocks/codeblocks_integration_test.go create mode 100644 markup/goldmark/codeblocks/render.go create mode 100644 markup/goldmark/convert.go create mode 100644 markup/goldmark/convert_test.go create mode 100644 markup/goldmark/goldmark_config/config.go create mode 100644 markup/goldmark/goldmark_integration_test.go create mode 100644 markup/goldmark/hugocontext/hugocontext.go create mode 100644 markup/goldmark/hugocontext/hugocontext_test.go create mode 100644 markup/goldmark/images/images_integration_test.go create mode 100644 markup/goldmark/images/transform.go create mode 100644 markup/goldmark/internal/extensions/attributes/attributes.go create mode 100644 markup/goldmark/internal/extensions/attributes/attributes_integration_test.go create mode 100644 markup/goldmark/internal/render/context.go create mode 100644 markup/goldmark/passthrough/passthrough.go create mode 100644 markup/goldmark/passthrough/passthrough_integration_test.go create mode 100644 markup/goldmark/render_hooks.go create mode 100644 markup/goldmark/tables/tables.go create mode 100644 markup/goldmark/tables/tables_integration_test.go create mode 100644 markup/goldmark/toc.go create mode 100644 markup/goldmark/toc_integration_test.go create mode 100644 markup/highlight/chromalexers/chromalexers.go create mode 100644 markup/highlight/config.go create mode 100644 markup/highlight/config_test.go create mode 100644 markup/highlight/highlight.go create mode 100644 markup/highlight/highlight_integration_test.go create mode 100644 markup/highlight/highlight_test.go create mode 100644 markup/internal/attributes/attributes.go create mode 100644 markup/internal/external.go create mode 100644 markup/markup.go create mode 100644 markup/markup_config/config.go create mode 100644 markup/markup_config/config_test.go create mode 100644 markup/markup_test.go create mode 100644 markup/org/convert.go create mode 100644 markup/org/convert_test.go create mode 100644 markup/pandoc/convert.go create mode 100644 markup/pandoc/convert_integration_test.go create mode 100644 markup/pandoc/convert_test.go create mode 100644 markup/pandoc/testdata/foo.bib create mode 100644 markup/pandoc/testdata/ieee.csl create mode 100644 markup/rst/convert.go create mode 100644 markup/rst/convert_test.go create mode 100644 markup/rst/rst_config/config.go create mode 100644 markup/rst/rst_integration_test.go create mode 100644 markup/tableofcontents/tableofcontents.go create mode 100644 markup/tableofcontents/tableofcontents_integration_test.go create mode 100644 markup/tableofcontents/tableofcontents_test.go create mode 100644 media/builtin.go create mode 100644 media/config.go create mode 100644 media/config_test.go create mode 100644 media/mediaType.go create mode 100644 media/mediaType_test.go create mode 100644 media/testdata/fake.js create mode 100644 media/testdata/fake.png create mode 100644 media/testdata/reosurce.otf create mode 100644 media/testdata/resource.bmp create mode 100644 media/testdata/resource.css create mode 100644 media/testdata/resource.csv create mode 100644 media/testdata/resource.gif create mode 100644 media/testdata/resource.ics create mode 100644 media/testdata/resource.jpe create mode 100644 media/testdata/resource.jpg create mode 100644 media/testdata/resource.js create mode 100644 media/testdata/resource.json create mode 100644 media/testdata/resource.pdf create mode 100644 media/testdata/resource.png create mode 100644 media/testdata/resource.rss create mode 100644 media/testdata/resource.sass create mode 100644 media/testdata/resource.scss create mode 100644 media/testdata/resource.svg create mode 100644 media/testdata/resource.ttf create mode 100644 media/testdata/resource.webp create mode 100644 media/testdata/resource.xml create mode 100755 merge-release.sh create mode 100644 metrics/metrics.go create mode 100644 metrics/metrics_test.go create mode 100644 minifiers/config.go create mode 100644 minifiers/config_test.go create mode 100644 minifiers/minifiers.go create mode 100644 minifiers/minifiers_test.go create mode 100644 modules/client.go create mode 100644 modules/client_test.go create mode 100644 modules/collect.go create mode 100644 modules/collect_test.go create mode 100644 modules/config.go create mode 100644 modules/config_integration_test.go create mode 100644 modules/config_test.go create mode 100644 modules/module.go create mode 100644 modules/modules_integration_test.go create mode 100644 modules/mount_filters_integration_test.go create mode 100644 modules/npm/package_builder.go create mode 100644 modules/npm/package_builder_integration_test.go create mode 100644 modules/npm/package_builder_test.go create mode 100644 navigation/menu.go create mode 100644 navigation/menu_cache.go create mode 100644 navigation/menu_cache_test.go create mode 100644 navigation/pagemenus.go create mode 100644 output/config.go create mode 100644 output/config_test.go create mode 100644 output/docshelper.go create mode 100644 output/outputFormat.go create mode 100644 output/outputFormat_integration_test.go create mode 100644 output/outputFormat_test.go create mode 100644 parser/frontmatter.go create mode 100644 parser/frontmatter_test.go create mode 100644 parser/lowercase_camel_json.go create mode 100644 parser/lowercase_camel_json_test.go create mode 100644 parser/metadecoders/decoder.go create mode 100644 parser/metadecoders/decoder_integration_test.go create mode 100644 parser/metadecoders/decoder_test.go create mode 100644 parser/metadecoders/encoder.go create mode 100644 parser/metadecoders/format.go create mode 100644 parser/metadecoders/format_test.go create mode 100644 parser/pageparser/doc.go create mode 100644 parser/pageparser/item.go create mode 100644 parser/pageparser/item_test.go create mode 100644 parser/pageparser/itemtype_string.go create mode 100644 parser/pageparser/pagelexer.go create mode 100644 parser/pageparser/pagelexer_intro.go create mode 100644 parser/pageparser/pagelexer_intro_test.go create mode 100644 parser/pageparser/pagelexer_shortcode.go create mode 100644 parser/pageparser/pagelexer_test.go create mode 100644 parser/pageparser/pageparser.go create mode 100644 parser/pageparser/pageparser_intro_test.go create mode 100644 parser/pageparser/pageparser_shortcode_test.go create mode 100644 parser/pageparser/pageparser_test.go create mode 100644 publisher/htmlElementsCollector.go create mode 100644 publisher/htmlElementsCollector_test.go create mode 100644 publisher/publisher.go create mode 100755 pull-docs.sh create mode 100644 related/inverted_index.go create mode 100644 related/inverted_index_test.go create mode 100644 related/related_integration_test.go create mode 100644 releaser/releaser.go create mode 100644 resources/assets/sunset.jpg create mode 100644 resources/docs.go create mode 100644 resources/image.go create mode 100644 resources/image_cache.go create mode 100644 resources/image_test.go create mode 100644 resources/images/auto_orient.go create mode 100644 resources/images/codec.go create mode 100644 resources/images/color.go create mode 100644 resources/images/color_test.go create mode 100644 resources/images/config.go create mode 100644 resources/images/config_test.go create mode 100644 resources/images/dither.go create mode 100644 resources/images/filters.go create mode 100644 resources/images/filters_test.go create mode 100644 resources/images/image.go create mode 100644 resources/images/image_resource.go create mode 100644 resources/images/images_golden_integration_test.go create mode 100644 resources/images/images_integration_test.go create mode 100644 resources/images/imagetesting/testing.go create mode 100644 resources/images/mask.go create mode 100644 resources/images/meta/exif_test.go create mode 100644 resources/images/meta/meta.go create mode 100644 resources/images/meta/meta_integration_test.go create mode 100644 resources/images/opacity.go create mode 100644 resources/images/overlay.go create mode 100644 resources/images/padding.go create mode 100644 resources/images/process.go create mode 100644 resources/images/resampling.go create mode 100644 resources/images/smartcrop.go create mode 100644 resources/images/smartcrop_test.go create mode 100644 resources/images/testdata/images_golden/filters/mask/blue.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask/transparant.png create mode 100644 resources/images/testdata/images_golden/filters/mask/wide.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask/yellow.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask2/green.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask2/pink.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/brightness-40.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/colorbalance-180-50-20.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/contrast-50.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/dither-default.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/gamma-1.667.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/gaussianblur-5.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/grayscale+colorize-180-50-20.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/grayscale.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/hue--15.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/invert.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/opacity-0.65.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/overlay-20-20.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/padding-20-40-#976941.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/pixelate-10.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/rotate270.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/saturation-65.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/sepia-80.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/sigmoid-0.6--4.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/text.jpg create mode 100644 resources/images/testdata/images_golden/filters/misc/unsharpmask.jpg create mode 100644 resources/images/testdata/images_golden/filters/text/text_alignx-center.jpg create mode 100644 resources/images/testdata/images_golden/filters/text/text_alignx-center_aligny-bottom.jpg create mode 100644 resources/images/testdata/images_golden/filters/text/text_alignx-center_aligny-center.jpg create mode 100644 resources/images/testdata/images_golden/filters/text/text_alignx-left.jpg create mode 100644 resources/images/testdata/images_golden/filters/text/text_alignx-right.jpg create mode 100644 resources/images/testdata/images_golden/losslessvsquality/resize-300x300-jpeg.jpg create mode 100644 resources/images/testdata/images_golden/losslessvsquality/resize-300x300-webp-lossy.webp create mode 100644 resources/images/testdata/images_golden/losslessvsquality/resize-300x300-webp.webp create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-r90.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-smart.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-left.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg create mode 100644 resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-fc03ec.png create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-x200.jpg create mode 100644 resources/images/testdata/images_golden/process/avif/avif-q79.avif create mode 100644 resources/images/testdata/images_golden/process/avif/avif-q80.avif create mode 100644 resources/images/testdata/images_golden/process/avif/avif.avif create mode 100644 resources/images/testdata/images_golden/process/avif/crop-300x300-smart-avif.avif create mode 100644 resources/images/testdata/images_golden/process/avif/crop-300x300-smart-ff9999-avif.avif create mode 100644 resources/images/testdata/images_golden/process/avif/gif.gif create mode 100644 resources/images/testdata/images_golden/process/avif/q50.avif create mode 100644 resources/images/testdata/images_golden/process/avif/r1.avif create mode 100644 resources/images/testdata/images_golden/process/avif/r2.avif create mode 100644 resources/images/testdata/images_golden/process/avifstraws/resize-900x.avif create mode 100644 resources/images/testdata/images_golden/process/misc/crop-500x200-smart.jpg create mode 100644 resources/images/testdata/images_golden/process/misc/fill-500x200-smart.jpg create mode 100644 resources/images/testdata/images_golden/process/misc/fit-500x200-smart.jpg create mode 100644 resources/images/testdata/images_golden/process/misc/resize-100x100-gif.gif create mode 100644 resources/images/testdata/images_golden/process/misc/resize-100x100-r180.png create mode 100644 resources/images/testdata/images_golden/process/misc/resize-300x300-jpg-b31280.jpg create mode 100644 resources/images/testdata/images_golden/process/webp/crop-300x300-gif.gif create mode 100644 resources/images/testdata/images_golden/process/webp/crop-300x300-smart-ff9999.webp create mode 100644 resources/images/testdata/images_golden/process/webp/crop-300x300-smart.webp create mode 100644 resources/images/testdata/images_golden/process/webp/crop-300x300.webp create mode 100644 resources/images/testdata/images_golden/process/webp/crop-500x200-smart-webp.webp create mode 100644 resources/images/testdata/images_golden/process/webp/fit-300x400-webp.webp create mode 100644 resources/images/testdata/images_golden/process/webp/fit-400x500-webp.webp create mode 100644 resources/images/testdata/images_golden/process/webp/gif.gif create mode 100644 resources/images/testdata/images_golden/process/webp/png.png create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-drawing.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-icon.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-lossless.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q1.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q100.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q33.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q50-drawing.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q75.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300-webp.webp create mode 100644 resources/images/testdata/images_golden/process/webp/resize-300x300.gif create mode 100644 resources/images/testdata/images_golden/process/webp/resize-400x.webp create mode 100644 resources/images/testdata/images_golden/webp/animation/anim_hu_58eb49733894e7ce.gif create mode 100644 resources/images/testdata/images_golden/webp/animation/anim_hu_edc2f24aaad2cee6.webp create mode 100644 resources/images/testdata/images_golden/webp/animation/giphy_hu_bb052284cc220165.webp create mode 100644 resources/images/testdata/images_golden/webp/animation/giphy_hu_c6b8060edf0363b1.gif create mode 100644 resources/images/text.go create mode 100644 resources/internal/key.go create mode 100644 resources/internal/key_test.go create mode 100644 resources/internal/resourcepaths.go create mode 100644 resources/jsconfig/jsconfig.go create mode 100644 resources/jsconfig/jsconfig_test.go create mode 100644 resources/kinds/kinds.go create mode 100644 resources/kinds/kinds_test.go create mode 100644 resources/page/hugoinfo.go create mode 100644 resources/page/hugoinfo_test.go create mode 100644 resources/page/page.go create mode 100644 resources/page/page_author.go create mode 100644 resources/page/page_data.go create mode 100644 resources/page/page_data_test.go create mode 100644 resources/page/page_generate/.gitignore create mode 100644 resources/page/page_generate/generate_page_wrappers.go create mode 100644 resources/page/page_integration_test.go create mode 100644 resources/page/page_kinds.go create mode 100644 resources/page/page_lazy_contentprovider.go create mode 100644 resources/page/page_markup.go create mode 100644 resources/page/page_markup_integration_test.go create mode 100644 resources/page/page_markup_test.go create mode 100644 resources/page/page_marshaljson.autogen.go create mode 100644 resources/page/page_matcher.go create mode 100644 resources/page/page_matcher_test.go create mode 100644 resources/page/page_nop.go create mode 100644 resources/page/page_outputformat.go create mode 100644 resources/page/page_paths.go create mode 100644 resources/page/page_paths_test.go create mode 100644 resources/page/page_wrappers.autogen.go create mode 100644 resources/page/pagegroup.go create mode 100644 resources/page/pagegroup_test.go create mode 100644 resources/page/pagemeta/page_frontmatter.go create mode 100644 resources/page/pagemeta/page_frontmatter_test.go create mode 100644 resources/page/pagemeta/pagemeta.go create mode 100644 resources/page/pagemeta/pagemeta_integration_test.go create mode 100644 resources/page/pagemeta/pagemeta_test.go create mode 100644 resources/page/pages.go create mode 100644 resources/page/pages_cache.go create mode 100644 resources/page/pages_cache_test.go create mode 100644 resources/page/pages_language_merge.go create mode 100644 resources/page/pages_prev_next.go create mode 100644 resources/page/pages_prev_next_integration_test.go create mode 100644 resources/page/pages_prev_next_test.go create mode 100644 resources/page/pages_related.go create mode 100644 resources/page/pages_related_test.go create mode 100644 resources/page/pages_sort.go create mode 100644 resources/page/pages_sort_search.go create mode 100644 resources/page/pages_sort_search_test.go create mode 100644 resources/page/pages_sort_test.go create mode 100644 resources/page/pages_test.go create mode 100644 resources/page/pagination.go create mode 100644 resources/page/pagination_test.go create mode 100644 resources/page/path_integration_test.go create mode 100644 resources/page/permalinks.go create mode 100644 resources/page/permalinks_integration_test.go create mode 100644 resources/page/permalinks_test.go create mode 100644 resources/page/site.go create mode 100644 resources/page/site_integration_test.go create mode 100644 resources/page/siteidentities/identities.go create mode 100644 resources/page/taxonomy.go create mode 100644 resources/page/taxonomy_integration_test.go create mode 100644 resources/page/testhelpers_test.go create mode 100644 resources/page/weighted.go create mode 100644 resources/page/zero_file.autogen.go create mode 100644 resources/post_publish.go create mode 100644 resources/postpub/fields.go create mode 100644 resources/postpub/fields_test.go create mode 100644 resources/postpub/postpub.go create mode 100644 resources/resource.go create mode 100644 resources/resource/dates.go create mode 100644 resources/resource/params.go create mode 100644 resources/resource/resource_helpers.go create mode 100644 resources/resource/resources.go create mode 100644 resources/resource/resources_integration_test.go create mode 100644 resources/resource/resources_test.go create mode 100644 resources/resource/resourcetypes.go create mode 100644 resources/resource_cache.go create mode 100644 resources/resource_factories/bundler/bundler.go create mode 100644 resources/resource_factories/bundler/bundler_test.go create mode 100644 resources/resource_factories/create/create.go create mode 100644 resources/resource_factories/create/create_integration_test.go create mode 100644 resources/resource_factories/create/remote.go create mode 100644 resources/resource_factories/create/remote_test.go create mode 100644 resources/resource_metadata.go create mode 100644 resources/resource_spec.go create mode 100644 resources/resource_spec_test.go create mode 100644 resources/resource_test.go create mode 100644 resources/resource_transformers/babel/babel.go create mode 100644 resources/resource_transformers/babel/babel_integration_test.go create mode 100644 resources/resource_transformers/cssjs/inline_imports.go create mode 100644 resources/resource_transformers/cssjs/inline_imports_test.go create mode 100644 resources/resource_transformers/cssjs/postcss.go create mode 100644 resources/resource_transformers/cssjs/postcss_integration_test.go create mode 100644 resources/resource_transformers/cssjs/tailwindcss.go create mode 100644 resources/resource_transformers/cssjs/tailwindcss_integration_test.go create mode 100644 resources/resource_transformers/htesting/testhelpers.go create mode 100644 resources/resource_transformers/integrity/integrity.go create mode 100644 resources/resource_transformers/integrity/integrity_test.go create mode 100644 resources/resource_transformers/js/build.go create mode 100644 resources/resource_transformers/js/js_integration_test.go create mode 100644 resources/resource_transformers/js/transform.go create mode 100644 resources/resource_transformers/minifier/minifier_integration_test.go create mode 100644 resources/resource_transformers/minifier/minify.go create mode 100644 resources/resource_transformers/minifier/minify_test.go create mode 100644 resources/resource_transformers/templates/execute_as_template.go create mode 100644 resources/resource_transformers/templates/templates_integration_test.go create mode 100644 resources/resource_transformers/tocss/dartsass/client.go create mode 100644 resources/resource_transformers/tocss/dartsass/dartsass_integration_test.go create mode 100644 resources/resource_transformers/tocss/dartsass/transform.go create mode 100644 resources/resource_transformers/tocss/sass/helpers.go create mode 100644 resources/resource_transformers/tocss/sass/helpers_test.go create mode 100644 resources/resource_transformers/tocss/scss/client.go create mode 100644 resources/resource_transformers/tocss/scss/client_extended.go create mode 100644 resources/resource_transformers/tocss/scss/client_notavailable.go create mode 100644 resources/resource_transformers/tocss/scss/client_test.go create mode 100644 resources/resource_transformers/tocss/scss/scss_integration_test.go create mode 100644 resources/resource_transformers/tocss/scss/tocss.go create mode 100644 resources/resources_integration_test.go create mode 100644 resources/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg create mode 100644 resources/testdata/bep/dock-75-hdr.avif create mode 100644 resources/testdata/bep/license.txt create mode 100644 resources/testdata/bep/straws.avif create mode 100644 resources/testdata/bw-gopher.png create mode 100644 resources/testdata/circle.svg create mode 100644 resources/testdata/exif/orientation6.jpg create mode 100644 resources/testdata/fuzzy-cirlcle.png create mode 100644 resources/testdata/giphy.avif create mode 100644 resources/testdata/giphy.gif create mode 100644 resources/testdata/gohugoio-card.gif create mode 100644 resources/testdata/gohugoio.png create mode 100644 resources/testdata/gohugoio24.png create mode 100644 resources/testdata/gohugoio8.png create mode 100644 resources/testdata/gopher-hero8.png create mode 100644 resources/testdata/gradient-circle.png create mode 100644 resources/testdata/iss8079.jpg create mode 100644 resources/testdata/issue10738/canon_cr2_fraction.jpg create mode 100644 resources/testdata/issue10738/canon_cr2_integer.jpg create mode 100644 resources/testdata/issue10738/dji_dng_fraction.jpg create mode 100644 resources/testdata/issue10738/fuji_raf_fraction.jpg create mode 100644 resources/testdata/issue10738/fuji_raf_integer.jpg create mode 100644 resources/testdata/issue10738/leica_dng_fraction.jpg create mode 100644 resources/testdata/issue10738/lumix_rw2_fraction.jpg create mode 100644 resources/testdata/issue10738/nikon_nef_d5600.jpg create mode 100644 resources/testdata/issue10738/nikon_nef_fraction.jpg create mode 100644 resources/testdata/issue10738/nikon_nef_fraction_2.jpg create mode 100644 resources/testdata/issue10738/nikon_nef_integer.jpg create mode 100644 resources/testdata/issue10738/sony_arw_fraction.jpg create mode 100644 resources/testdata/issue10738/sony_arw_integer.jpg create mode 100644 resources/testdata/mask.png create mode 100644 resources/testdata/mask2.png create mode 100644 resources/testdata/pix.bmp create mode 100644 resources/testdata/pix.gif create mode 100644 resources/testdata/sub/gohugoio2.png create mode 100644 resources/testdata/sunrise.JPG create mode 100644 resources/testdata/sunrise.webp create mode 100644 resources/testdata/sunset.avif create mode 100644 resources/testdata/sunset.jpg create mode 100644 resources/testdata/sunset.webp create mode 100644 resources/testdata/sunset_420.avif create mode 100644 resources/testdata/webp/anim.webp create mode 100644 resources/testdata/webp/bw-gopher.webp create mode 100644 resources/testdata/webp/fuzzy-cirlcle-transparent-32.webp create mode 100644 resources/testdata/webp/highcontrast.webp create mode 100644 resources/testdata/webp/invalid.webp create mode 100644 resources/testhelpers_test.go create mode 100644 resources/transform.go create mode 100644 resources/transform_integration_test.go create mode 100644 resources/transform_test.go create mode 100755 scripts/docker/entrypoint.sh create mode 100644 scripts/fork_go_templates/.gitignore create mode 100644 scripts/fork_go_templates/main.go create mode 100644 snap/local/logo.png create mode 100644 snap/snapcraft.yaml create mode 100644 source/content_directory_test.go create mode 100644 source/fileInfo.go create mode 100644 source/sourceSpec.go create mode 100644 testscripts/.gitattributes create mode 100644 testscripts/commands/commands_errors.txt create mode 100644 testscripts/commands/completion.txt create mode 100644 testscripts/commands/config.txt create mode 100644 testscripts/commands/config__cachedir.txt create mode 100644 testscripts/commands/config_environment_isolation_issue14763.txt create mode 100644 testscripts/commands/convert.txt create mode 100644 testscripts/commands/deprecate.txt create mode 100644 testscripts/commands/env.txt create mode 100644 testscripts/commands/gen.txt create mode 100644 testscripts/commands/gen_chromastyles.txt create mode 100644 testscripts/commands/gen_chromastyles_mode.txt create mode 100644 testscripts/commands/hugo.txt create mode 100644 testscripts/commands/hugo__configdir.txt create mode 100644 testscripts/commands/hugo__errors.txt create mode 100644 testscripts/commands/hugo__flags.txt create mode 100644 testscripts/commands/hugo__minify_issue13988.txt create mode 100644 testscripts/commands/hugo__noconfig.txt create mode 100644 testscripts/commands/hugo__paniconwarning_mod.txt create mode 100644 testscripts/commands/hugo__path-warnings-postprocess.txt create mode 100644 testscripts/commands/hugo__path-warnings.txt create mode 100644 testscripts/commands/hugo__path-warnings_issue13164.txt create mode 100644 testscripts/commands/hugo__processingstats.txt create mode 100644 testscripts/commands/hugo__processingstats2.txt create mode 100644 testscripts/commands/hugo__publishdir_in_config.txt create mode 100644 testscripts/commands/hugo__static.txt create mode 100644 testscripts/commands/hugo__static_composite.txt create mode 100644 testscripts/commands/hugo__static_issue14507.txt create mode 100644 testscripts/commands/hugo_build.txt create mode 100644 testscripts/commands/hugo_configdev_env.txt create mode 100644 testscripts/commands/hugo_configdev_environment.txt create mode 100644 testscripts/commands/hugo_configprod.txt create mode 100644 testscripts/commands/hugo_printpathwarnings.txt create mode 100644 testscripts/commands/hugo_printunusedtemplates.txt create mode 100644 testscripts/commands/import_jekyll.txt create mode 100644 testscripts/commands/list.txt create mode 100644 testscripts/commands/mod.txt create mode 100644 testscripts/commands/mod__disable.txt create mode 100644 testscripts/commands/mod__hugodirectsum.txt create mode 100644 testscripts/commands/mod__hugodirectsum_tamper.txt create mode 100644 testscripts/commands/mod__tamper_gosum.txt create mode 100644 testscripts/commands/mod__themesdir.txt create mode 100644 testscripts/commands/mod_get.txt create mode 100644 testscripts/commands/mod_get_u.txt create mode 100644 testscripts/commands/mod_init.txt create mode 100644 testscripts/commands/mod_npm.txt create mode 100644 testscripts/commands/mod_npm__moduleorder.txt create mode 100644 testscripts/commands/mod_npm_withexisting.txt create mode 100644 testscripts/commands/mod_tidy.txt create mode 100644 testscripts/commands/mod_vendor.txt create mode 100644 testscripts/commands/mod_vendor__versions.txt create mode 100644 testscripts/commands/new.txt create mode 100644 testscripts/commands/new_content.txt create mode 100644 testscripts/commands/new_content_archetypedir.txt create mode 100644 testscripts/commands/no_symlinks.txt create mode 100644 testscripts/commands/noop.txt create mode 100644 testscripts/commands/version.txt create mode 100644 testscripts/commands/warnf_stderr.txt create mode 100644 testscripts/server/hugo__watch.txt create mode 100644 testscripts/server/server.txt create mode 100644 testscripts/server/server__edit_config.txt create mode 100644 testscripts/server/server__edit_content.txt create mode 100644 testscripts/server/server__edit_content_with_chinese_tag.txt create mode 100644 testscripts/server/server__error_recovery_edit_config.txt create mode 100644 testscripts/server/server__error_recovery_edit_content.txt create mode 100644 testscripts/server/server__multihost.txt create mode 100644 testscripts/server/server__watch_false.txt create mode 100644 testscripts/server/server__watch_hugo_stats.txt create mode 100644 testscripts/server/server__watch_moduleconfig.txt create mode 100644 testscripts/server/server_disablelivereload.txt create mode 100644 testscripts/server/server_disablelivereload__config.txt create mode 100644 testscripts/server/server_render_static_to_disk.txt create mode 100644 testscripts/server/server_render_to_memory.txt create mode 100644 testscripts/unfinished/noop.txt create mode 100644 testscripts/withdeploy-off/deploy_off.txt create mode 100644 testscripts/withdeploy/deploy.txt create mode 100644 tpl/cast/cast.go create mode 100644 tpl/cast/cast_test.go create mode 100644 tpl/cast/docshelper.go create mode 100644 tpl/cast/init.go create mode 100644 tpl/collections/append.go create mode 100644 tpl/collections/append_test.go create mode 100644 tpl/collections/apply.go create mode 100644 tpl/collections/collections.go create mode 100644 tpl/collections/collections_integration_test.go create mode 100644 tpl/collections/collections_test.go create mode 100644 tpl/collections/complement.go create mode 100644 tpl/collections/complement_test.go create mode 100644 tpl/collections/index.go create mode 100644 tpl/collections/index_test.go create mode 100644 tpl/collections/init.go create mode 100644 tpl/collections/merge.go create mode 100644 tpl/collections/merge_test.go create mode 100644 tpl/collections/querify.go create mode 100644 tpl/collections/querify_test.go create mode 100644 tpl/collections/reflect_helpers.go create mode 100644 tpl/collections/sort.go create mode 100644 tpl/collections/sort_test.go create mode 100644 tpl/collections/symdiff.go create mode 100644 tpl/collections/symdiff_test.go create mode 100644 tpl/collections/vitter.go create mode 100644 tpl/collections/vitter_test.go create mode 100644 tpl/collections/where.go create mode 100644 tpl/collections/where_test.go create mode 100644 tpl/compare/compare.go create mode 100644 tpl/compare/compare_test.go create mode 100644 tpl/compare/init.go create mode 100644 tpl/crypto/crypto.go create mode 100644 tpl/crypto/crypto_integration_test.go create mode 100644 tpl/crypto/crypto_test.go create mode 100644 tpl/crypto/init.go create mode 100644 tpl/css/build_integration_test.go create mode 100644 tpl/css/css.go create mode 100644 tpl/debug/debug.go create mode 100644 tpl/debug/debug_integration_test.go create mode 100644 tpl/debug/init.go create mode 100644 tpl/diagrams/diagrams.go create mode 100644 tpl/diagrams/goat.go create mode 100644 tpl/diagrams/init.go create mode 100644 tpl/encoding/encoding.go create mode 100644 tpl/encoding/encoding_test.go create mode 100644 tpl/encoding/init.go create mode 100644 tpl/fmt/fmt.go create mode 100644 tpl/fmt/fmt_integration_test.go create mode 100644 tpl/fmt/init.go create mode 100644 tpl/hash/hash.go create mode 100644 tpl/hash/hash_test.go create mode 100644 tpl/hugo/init.go create mode 100644 tpl/images/images.go create mode 100644 tpl/images/images_integration_test.go create mode 100644 tpl/images/images_test.go create mode 100644 tpl/images/init.go create mode 100644 tpl/images/testdata/images_golden/funcs/qr-default.png create mode 100644 tpl/images/testdata/images_golden/funcs/qr-level-high_scale-6.png create mode 100644 tpl/inflect/inflect.go create mode 100644 tpl/inflect/inflect_test.go create mode 100644 tpl/inflect/init.go create mode 100644 tpl/internal/go_templates/cfg/cfg.go create mode 100644 tpl/internal/go_templates/fmtsort/export_test.go create mode 100644 tpl/internal/go_templates/fmtsort/sort.go create mode 100644 tpl/internal/go_templates/fmtsort/sort_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/attr.go create mode 100644 tpl/internal/go_templates/htmltemplate/attr_string.go create mode 100644 tpl/internal/go_templates/htmltemplate/clone_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/content.go create mode 100644 tpl/internal/go_templates/htmltemplate/content_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/context.go create mode 100644 tpl/internal/go_templates/htmltemplate/css.go create mode 100644 tpl/internal/go_templates/htmltemplate/css_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/delim_string.go create mode 100644 tpl/internal/go_templates/htmltemplate/doc.go create mode 100644 tpl/internal/go_templates/htmltemplate/element_string.go create mode 100644 tpl/internal/go_templates/htmltemplate/error.go create mode 100644 tpl/internal/go_templates/htmltemplate/escape.go create mode 100644 tpl/internal/go_templates/htmltemplate/escape_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/example_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/examplefiles_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/exec_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/html.go create mode 100644 tpl/internal/go_templates/htmltemplate/html_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/hugo_template.go create mode 100644 tpl/internal/go_templates/htmltemplate/js.go create mode 100644 tpl/internal/go_templates/htmltemplate/js_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/jsctx_string.go create mode 100644 tpl/internal/go_templates/htmltemplate/multi_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/state_string.go create mode 100644 tpl/internal/go_templates/htmltemplate/template.go create mode 100644 tpl/internal/go_templates/htmltemplate/template_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/testdata/file1.tmpl create mode 100644 tpl/internal/go_templates/htmltemplate/testdata/file2.tmpl create mode 100644 tpl/internal/go_templates/htmltemplate/testdata/fs.zip create mode 100644 tpl/internal/go_templates/htmltemplate/testdata/tmpl1.tmpl create mode 100644 tpl/internal/go_templates/htmltemplate/testdata/tmpl2.tmpl create mode 100644 tpl/internal/go_templates/htmltemplate/transition.go create mode 100644 tpl/internal/go_templates/htmltemplate/transition_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/url.go create mode 100644 tpl/internal/go_templates/htmltemplate/url_test.go create mode 100644 tpl/internal/go_templates/htmltemplate/urlpart_string.go create mode 100644 tpl/internal/go_templates/staticcheck.conf create mode 100644 tpl/internal/go_templates/testenv/exec.go create mode 100644 tpl/internal/go_templates/testenv/noopt.go create mode 100644 tpl/internal/go_templates/testenv/opt.go create mode 100644 tpl/internal/go_templates/testenv/testenv.go create mode 100644 tpl/internal/go_templates/testenv/testenv_notunix.go create mode 100644 tpl/internal/go_templates/testenv/testenv_notwin.go create mode 100644 tpl/internal/go_templates/testenv/testenv_unix.go create mode 100644 tpl/internal/go_templates/testenv/testenv_windows.go create mode 100644 tpl/internal/go_templates/texttemplate/doc.go create mode 100644 tpl/internal/go_templates/texttemplate/example_test.go create mode 100644 tpl/internal/go_templates/texttemplate/examplefiles_test.go create mode 100644 tpl/internal/go_templates/texttemplate/examplefunc_test.go create mode 100644 tpl/internal/go_templates/texttemplate/exec.go create mode 100644 tpl/internal/go_templates/texttemplate/exec_test.go create mode 100644 tpl/internal/go_templates/texttemplate/funcs.go create mode 100644 tpl/internal/go_templates/texttemplate/helper.go create mode 100644 tpl/internal/go_templates/texttemplate/hugo_template.go create mode 100644 tpl/internal/go_templates/texttemplate/hugo_template_test.go create mode 100644 tpl/internal/go_templates/texttemplate/link_test.go create mode 100644 tpl/internal/go_templates/texttemplate/multi_test.go create mode 100644 tpl/internal/go_templates/texttemplate/option.go create mode 100644 tpl/internal/go_templates/texttemplate/parse/lex.go create mode 100644 tpl/internal/go_templates/texttemplate/parse/lex_test.go create mode 100644 tpl/internal/go_templates/texttemplate/parse/node.go create mode 100644 tpl/internal/go_templates/texttemplate/parse/parse.go create mode 100644 tpl/internal/go_templates/texttemplate/parse/parse_test.go create mode 100644 tpl/internal/go_templates/texttemplate/template.go create mode 100644 tpl/internal/go_templates/texttemplate/testdata/file1.tmpl create mode 100644 tpl/internal/go_templates/texttemplate/testdata/file2.tmpl create mode 100644 tpl/internal/go_templates/texttemplate/testdata/tmpl1.tmpl create mode 100644 tpl/internal/go_templates/texttemplate/testdata/tmpl2.tmpl create mode 100644 tpl/internal/resourcehelpers/helpers.go create mode 100644 tpl/internal/templatefuncRegistry_test.go create mode 100644 tpl/internal/templatefuncsRegistry.go create mode 100644 tpl/js/init.go create mode 100644 tpl/js/js.go create mode 100644 tpl/lang/init.go create mode 100644 tpl/lang/lang.go create mode 100644 tpl/lang/lang_test.go create mode 100644 tpl/math/init.go create mode 100644 tpl/math/math.go create mode 100644 tpl/math/math_test.go create mode 100644 tpl/math/round.go create mode 100644 tpl/openapi/docs.go create mode 100644 tpl/openapi/openapi3/init.go create mode 100644 tpl/openapi/openapi3/openapi3.go create mode 100644 tpl/openapi/openapi3/openapi3_integration_test.go create mode 100644 tpl/os/init.go create mode 100644 tpl/os/os.go create mode 100644 tpl/os/os_integration_test.go create mode 100644 tpl/os/os_test.go create mode 100644 tpl/page/init.go create mode 100644 tpl/page/page_integration_test.go create mode 100644 tpl/partials/init.go create mode 100644 tpl/partials/partials.go create mode 100644 tpl/partials/partials_integration_test.go create mode 100644 tpl/path/init.go create mode 100644 tpl/path/path.go create mode 100644 tpl/path/path_test.go create mode 100644 tpl/reflect/init.go create mode 100644 tpl/reflect/reflect.go create mode 100644 tpl/reflect/reflect_integration_test.go create mode 100644 tpl/reflect/reflect_test.go create mode 100644 tpl/resources/init.go create mode 100644 tpl/resources/resources.go create mode 100644 tpl/resources/resources_integration_test.go create mode 100644 tpl/safe/init.go create mode 100644 tpl/safe/safe.go create mode 100644 tpl/safe/safe_test.go create mode 100644 tpl/site/init.go create mode 100644 tpl/strings/init.go create mode 100644 tpl/strings/regexp.go create mode 100644 tpl/strings/regexp_test.go create mode 100644 tpl/strings/strings.go create mode 100644 tpl/strings/strings_test.go create mode 100644 tpl/strings/truncate.go create mode 100644 tpl/strings/truncate_test.go create mode 100644 tpl/template.go create mode 100644 tpl/template_test.go create mode 100644 tpl/templates/decorator_falsy_test.go create mode 100644 tpl/templates/decorator_integration_test.go create mode 100644 tpl/templates/defer_integration_test.go create mode 100644 tpl/templates/init.go create mode 100644 tpl/templates/templates.go create mode 100644 tpl/templates/templates_integration_test.go create mode 100644 tpl/time/init.go create mode 100644 tpl/time/time.go create mode 100644 tpl/time/time_integration_test.go create mode 100644 tpl/time/time_test.go create mode 100644 tpl/tplimpl/category_string.go create mode 100644 tpl/tplimpl/embedded/.gitattributes create mode 100644 tpl/tplimpl/embedded/templates/_hugo/build/js/batch-esm-runner.gotmpl create mode 100644 tpl/tplimpl/embedded/templates/_markup/render-codeblock-goat.html create mode 100644 tpl/tplimpl/embedded/templates/_markup/render-image.html create mode 100644 tpl/tplimpl/embedded/templates/_markup/render-link.html create mode 100644 tpl/tplimpl/embedded/templates/_markup/render-table.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/_funcs/get-page-images.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/disqus.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/google_analytics.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/opengraph.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/pagination.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/schema.html create mode 100644 tpl/tplimpl/embedded/templates/_partials/twitter_cards.html create mode 100644 tpl/tplimpl/embedded/templates/_server/error.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/1__h_simple_assets.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/comment.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/details.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/figure.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/gist.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/highlight.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/instagram.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/instagram_simple.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/param.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/qr.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/ref.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/relref.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/twitter.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/twitter_simple.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/vimeo.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/vimeo_simple.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/x.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/x_simple.html create mode 100644 tpl/tplimpl/embedded/templates/_shortcodes/youtube.html create mode 100644 tpl/tplimpl/embedded/templates/alias.html create mode 100644 tpl/tplimpl/embedded/templates/robots.txt create mode 100644 tpl/tplimpl/embedded/templates/rss.xml create mode 100644 tpl/tplimpl/embedded/templates/sitemap.xml create mode 100644 tpl/tplimpl/embedded/templates/sitemapindex.xml create mode 100644 tpl/tplimpl/embedded_templates_integration_test.go create mode 100644 tpl/tplimpl/legacy.go create mode 100644 tpl/tplimpl/render_hook_integration_test.go create mode 100644 tpl/tplimpl/shortcodes_integration_test.go create mode 100644 tpl/tplimpl/subcategory_string.go create mode 100644 tpl/tplimpl/template_funcs.go create mode 100644 tpl/tplimpl/template_funcs_test.go create mode 100644 tpl/tplimpl/template_info.go create mode 100644 tpl/tplimpl/templatedescriptor.go create mode 100644 tpl/tplimpl/templatedescriptor_test.go create mode 100644 tpl/tplimpl/templates.go create mode 100644 tpl/tplimpl/templatestore.go create mode 100644 tpl/tplimpl/templatestore_integration_test.go create mode 100644 tpl/tplimpl/templatestore_legacy1_integration_test.go create mode 100644 tpl/tplimpl/templatetransform.go create mode 100644 tpl/tplimpl/tplimpl_integration_test.go create mode 100644 tpl/tplimplinit/tplimplinit.go create mode 100644 tpl/transform/init.go create mode 100644 tpl/transform/remarshal.go create mode 100644 tpl/transform/remarshal_test.go create mode 100644 tpl/transform/testdata/large-katex.md create mode 100644 tpl/transform/transform.go create mode 100644 tpl/transform/transform_integration_test.go create mode 100644 tpl/transform/transform_test.go create mode 100644 tpl/transform/unmarshal.go create mode 100644 tpl/transform/unmarshal_test.go create mode 100644 tpl/urls/init.go create mode 100644 tpl/urls/urls.go create mode 100644 tpl/urls/urls_test.go create mode 100644 transform/chain.go create mode 100644 transform/chain_test.go create mode 100644 transform/livereloadinject/livereloadinject.go create mode 100644 transform/livereloadinject/livereloadinject_test.go create mode 100644 transform/metainject/hugogenerator.go create mode 100644 transform/metainject/hugogenerator_test.go create mode 100644 transform/urlreplacers/absurl.go create mode 100644 transform/urlreplacers/absurlreplacer.go create mode 100644 transform/urlreplacers/absurlreplacer_test.go create mode 100644 watcher/batcher.go create mode 100644 watcher/filenotify/filenotify.go create mode 100644 watcher/filenotify/fsnotify.go create mode 100644 watcher/filenotify/poller.go create mode 100644 watcher/filenotify/poller_test.go create mode 100755 watchtestscripts.sh diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..4a06e47 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,115 @@ +parameters: + +# v2: 11m. +defaults: &defaults + resource_class: large + docker: + - image: bepsays/ci-hugoreleaser:1.22600.20300 +environment: &buildenv + GOMODCACHE: /root/project/gomodcache +version: 2 +jobs: + prepare_release: + <<: *defaults + environment: &buildenv + GOMODCACHE: /root/project/gomodcache + steps: + - setup_remote_docker + - checkout: + path: hugo + - &git-config + run: + command: | + git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com" + git config --global user.name "hugoreleaser" + - run: + command: | + cd hugo + go mod download + go run -tags release main.go release --step 1 + - save_cache: + key: git-sha-{{ .Revision }} + paths: + - hugo + - gomodcache + build_container1: + <<: [*defaults] + environment: + <<: [*buildenv] + steps: + - &restore-cache + restore_cache: + key: git-sha-{{ .Revision }} + - run: + no_output_timeout: 20m + command: | + mkdir -p /tmp/files/dist1 + cd hugo + hugoreleaser build -paths "builds/container1/**" -workers 3 -dist /tmp/files/dist1 -chunks $CIRCLE_NODE_TOTAL -chunk-index $CIRCLE_NODE_INDEX + - &persist-workspace + persist_to_workspace: + root: /tmp/files + paths: + - dist1 + - dist2 + parallelism: 7 + build_container2: + <<: [*defaults] + environment: + <<: [*buildenv] + docker: + - image: bepsays/ci-hugoreleaser-linux-arm64:1.22600.20300 + steps: + - *restore-cache + - &attach-workspace + attach_workspace: + at: /tmp/workspace + - run: + command: | + mkdir -p /tmp/files/dist2 + cd hugo + hugoreleaser build -paths "builds/container2/**" -workers 1 -dist /tmp/files/dist2 + - *persist-workspace + archive_and_release: + <<: [*defaults] + environment: + <<: [*buildenv] + steps: + - *restore-cache + - *attach-workspace + - *git-config + - run: + name: Add github.com to known hosts + command: ssh-keyscan github.com >> ~/.ssh/known_hosts + - run: + command: | + cp -a /tmp/workspace/dist1/. ./hugo/dist + cp -a /tmp/workspace/dist2/. ./hugo/dist + - run: + command: | + cd hugo + hugoreleaser archive + hugoreleaser release + go run -tags release main.go release --step 2 +workflows: + version: 2 + release: + jobs: + - prepare_release: + filters: + branches: + only: /release-.*/ + - build_container1: + requires: + - prepare_release + - build_container2: + requires: + - prepare_release + - archive_and_release: + context: org-global + requires: + - build_container1 + - build_container2 + + + diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a183f6f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +*.md +*.log +*.txt +.git +.github +.circleci +docs +examples +Dockerfile diff --git a/.gemini/config.yaml b/.gemini/config.yaml new file mode 100644 index 0000000..236e043 --- /dev/null +++ b/.gemini/config.yaml @@ -0,0 +1,13 @@ +have_fun: false +memory_config: + disabled: false +code_review: + disable: false + comment_severity_threshold: HIGH + max_review_comments: -1 + pull_request_opened: + help: true + summary: false + code_review: false + include_drafts: false +ignore_patterns: [] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6994810 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Text files have auto line endings +* text=auto + +# Go source files always have LF line endings +*.go text eol=lf + +# SVG files should not be modified +*.svg -text diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..fa27914 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,23 @@ +--- +name: 'Bug report' +labels: 'Bug, NeedsTriage' +assignees: '' +about: Create a report to help us improve +--- + + + + + +### What version of Hugo are you using (`hugo version`)? + +
+$ hugo version
+
+
+ +### Does this issue reproduce with the latest release? diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c84d327 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: SUPPORT, ISSUES and TROUBLESHOOTING + url: https://discourse.gohugo.io/ + about: Please DO NOT use Github for support requests. Please visit https://discourse.gohugo.io for support! You will be helped much faster there. If you ignore this request your issue might be closed with a discourse label. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..c114b3d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: Proposal +about: Propose a new feature for Hugo +title: '' +labels: 'Proposal, NeedsTriage' +assignees: '' + +--- + + + \ No newline at end of file diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..cc9de09 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,3 @@ +### Asking Support Questions + +We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions. Please don't use the GitHub issue tracker to ask questions. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1801e72 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# See https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml new file mode 100644 index 0000000..5d488c6 --- /dev/null +++ b/.github/workflows/image.yml @@ -0,0 +1,49 @@ +name: Build Docker image + +on: + release: + types: [published] + pull_request: +permissions: + packages: write + +env: + REGISTRY_IMAGE: ghcr.io/gohugoio/hugo + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Docker meta + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Login to GHCR + # Login is only needed when the image is pushed + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + id: build + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + provenance: mode=max + sbom: true + push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: HUGO_BUILD_TAGS=extended,withdeploy \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..89dbdde --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,52 @@ +name: 'Close stale and lock closed issues and PRs' +on: + workflow_dispatch: + schedule: + - cron: '30 1 * * *' +permissions: + contents: read +jobs: + stale: + permissions: + issues: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@f5f995c727ac99a91dec92781a8e34e7c839a65e # v6.0.0 + with: + issue-inactive-days: 21 + add-issue-labels: 'Outdated' + issue-comment: > + This issue has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. + pr-comment: > + This pull request has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + with: + operations-per-run: 999 + days-before-issue-stale: 365 + days-before-pr-stale: 365 + days-before-issue-close: 56 + days-before-pr-close: 56 + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. The resources of the Hugo team are limited, and so we are asking for your help. + + If this is a **bug** and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open. + + If this is a **feature request**, and you feel that it is still relevant and valuable, please tell us why. + + This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions. + stale-pr-message: This PR has been automatically marked as stale because it has not had + recent activity. The resources of the Hugo team are limited, and so we are asking for your help. + + Please check https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md#code-contribution and verify that this code contribution fits with the description. If yes, tell us in a comment. + + This PR will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions. + stale-issue-label: 'Stale' + exempt-issue-labels: 'Keep,Security' + stale-pr-label: 'Stale' + exempt-pr-labels: 'Keep,Security' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..cee5b6b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,140 @@ +on: + push: + branches: [master] + pull_request: +name: Test +env: + GOPROXY: https://proxy.golang.org + GO111MODULE: on + SASS_VERSION: 1.80.3 + DART_SASS_SHA_LINUX: 7c933edbad0a7d389192c5b79393485c088bd2c4398e32f5754c32af006a9ffd + DART_SASS_SHA_MACOS: 79e060b0e131c3bb3c16926bafc371dc33feab122bfa8c01aa337a072097967b + DART_SASS_SHA_WINDOWS: 0bc4708b37cd1bac4740e83ac5e3176e66b774f77fd5dd364da5b5cfc9bfb469 +permissions: + contents: read +jobs: + test: + strategy: + matrix: + go-version: [1.26.x] + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - if: matrix.os == 'ubuntu-latest' + name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + with: + tool-cache: true + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: true + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Install Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ matrix.go-version }} + check-latest: true + cache: true + - name: Install Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "22" + - name: Install Ruby + uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0 + with: + ruby-version: "3.4.5" + - name: Install Ruby gems + run: | + gem install asciidoctor -v "2.0.26" + gem install asciidoctor-diagram -v "3.1.0" + - name: Install GoAT + run: go install github.com/blampe/goat/cmd/goat@177de93b192b8ffae608e5d9ec421cc99bf68402 + - name: Install Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.x" + - name: Install Mage + run: go install github.com/magefile/mage@v1.15.0 + - name: Install gotmplfmt + run: go install github.com/gohugoio/gotmplfmt@latest + - name: Install docutils + run: | + pip install docutils Pygments + rst2html --version + - if: matrix.os == 'ubuntu-latest' + name: Install pandoc on Linux + run: | + sudo apt-get update -y + sudo apt-get install -y pandoc + - if: matrix.os == 'macos-latest' + run: | + brew install pandoc + - if: matrix.os == 'windows-latest' + run: | + choco install pandoc + - run: pandoc -v + - if: matrix.os == 'windows-latest' + run: | + choco install mingw + - if: matrix.os == 'ubuntu-latest' + name: Install dart-sass Linux + run: | + echo "Install Dart Sass version ${SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz"; + echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c; + tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz"; + echo "$GOBIN" + echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH + - if: matrix.os == 'macos-latest' + name: Install dart-sass MacOS + run: | + echo "Install Dart Sass version ${SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz"; + echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c; + tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz"; + echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH + - if: matrix.os == 'windows-latest' + name: Install dart-sass Windows + run: | + echo "Install Dart Sass version ${env:SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip"; + Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .; + echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append + - if: matrix.os == 'ubuntu-latest' + name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@latest + - if: matrix.os == 'ubuntu-latest' + name: Check embedded go template formatting + run: "diff <(gotmplfmt -d tpl/tplimpl/embedded/templates) <(printf '')" + - if: matrix.os == 'ubuntu-latest' + name: Run staticcheck + run: | + export STATICCHECK_CACHE="${{ runner.temp }}/staticcheck" + staticcheck ./... + rm -rf ${{ runner.temp }}/staticcheck + - if: matrix.os != 'windows-latest' + name: Check + run: | + sass --version; + mage -v check; + env: + HUGO_BUILD_TAGS: extended,withdeploy + - if: matrix.os == 'windows-latest' + # See issue #11052. We limit the build to regular test (no -race flag) on Windows for now. + name: Test + run: | + mage -v test + env: + HUGO_BUILD_TAGS: extended,withdeploy + - if: matrix.os == 'ubuntu-latest' + name: Build for dragonfly + run: | + go install + go clean -i -cache + env: + GOARCH: amd64 + GOOS: dragonfly diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e812bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ + +*.test +imports.* +dist/ +public/ +.DS_Store +cache/filecache/_gen/ +.claude/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..e93adab --- /dev/null +++ b/.mailmap @@ -0,0 +1,3 @@ +spf13 Steve Francia +bep Bjørn Erik Pedersen + diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..838f027 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +tpl/tplimpl/embedded/templates/** \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..60c9854 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ + +* Brevity is good. +* Assume that the maintainers and readers of the code you write are Go experts: + * Don't use comments to explain the obvious. + * Use self-explanatory variable and function names. + * Use short variable names when the context is clear. +* If you need to add temporary debug printing, use `hdebug.Printf`.[^1] +* Never export symbols that's not needed outside of the package. +* Avoid global state at (almost) all cost. +* This is a project with a long history; assume that a similiar problem has been solved before, look hard for helper functions before creating new ones. +* In tests, almost always write end-to-end integration tests using `hugolib.Test` or one of its siblings. Write unit tests only for isolated utilities. +* In tests, use `qt` matchers (e.g. `b.Assert(err, qt.ErrorMatches, ...)`) instead of raw `if`/`t.Fatal` checks. +* In tests, always use the latest Hugo specification, e.g. for layouts, it's `layouts/page.html` and not `layouts/_default/single.html`, `layouts/list.html` and not `layouts/_default/list.html` +* Never name tests `TestIssue1234`; always give the test function a descriptive name, e.g. `TestDisablePathToLower`, and add any issue reference as a Go doc function comment, e.g. `// See issue 1234.`. +* If you borrow a test case (e.g. from the issue), that test's author must be added as co-author in the commit. +* If you're a security researcher, read @SECURITY.md carefully. +* Brevity is good. This applies to code, comments and commit messages. Don't write a novel. +* Use `./check.sh ./somepackage/...` when iterating. +* Use `./check.sh` when you're done. + + +[^1]: CI build fail if you forget to remove the debug printing. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..13db2dc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,212 @@ +# Contributing to Hugo + +We welcome contributions to Hugo of any kind including documentation, themes, +organization, tutorials, blog posts, bug reports, issues, feature requests, +feature implementations, pull requests, answering questions on the forum, +helping to manage issues, etc. + +The Hugo community and maintainers are [very active](https://github.com/gohugoio/hugo/pulse/monthly) and helpful, and the project benefits greatly from this activity. We created a [step by step guide](https://gohugo.io/tutorials/how-to-contribute-to-hugo/) if you're unfamiliar with GitHub or contributing to open source projects in general. + +*Note that this repository only contains the actual source code of Hugo. For **only** documentation-related pull requests / issues please refer to the [hugoDocs](https://github.com/gohugoio/hugoDocs) repository.* + +*Changes to the codebase **and** related documentation, e.g. for a new feature, should still use a single pull request.* + +## Table of Contents + +* [Asking Support Questions](#asking-support-questions) +* [Reporting Issues](#reporting-issues) +* [Submitting Patches](#submitting-patches) + * [Code Contribution Guidelines](#code-contribution-guidelines) + * [AI Assistance Notice](#ai-assistance-notice) + * [Git Commit Message Guidelines](#git-commit-message-guidelines) + * [Fetching the Sources From GitHub](#fetching-the-sources-from-github) + * [Building Hugo with Your Changes](#building-hugo-with-your-changes) + +## Asking Support Questions + +We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions. +Please don't use the GitHub issue tracker to ask questions. + +## Reporting Issues + +If you believe you have found a defect in Hugo or its documentation, use +the GitHub issue tracker to report +the problem to the Hugo maintainers. If you're not sure if it's a bug or not, +start by asking in the [discussion forum](https://discourse.gohugo.io). +When reporting the issue, please provide the version of Hugo in use (`hugo +version`) and your operating system. + +- [Hugo Issues · gohugoio/hugo](https://github.com/gohugoio/hugo/issues) +- [Hugo Documentation Issues · gohugoio/hugoDocs](https://github.com/gohugoio/hugoDocs/issues) +- [Hugo Website Theme Issues · gohugoio/hugoThemesSite](https://github.com/gohugoio/hugoThemesSite/issues) + +## Code Contribution + +Hugo has become a fully featured static site generator, so any new functionality must: + +* be useful to many. +* fit naturally into _what Hugo does best._ +* strive not to break existing sites. +* close or update an open [Hugo issue](https://github.com/gohugoio/hugo/issues) + +If it is of some complexity, the contributor is expected to maintain and support the new feature in the future (answer questions on the forum, fix any bugs etc.). + +Any non-trivial code change needs to update an open [issue](https://github.com/gohugoio/hugo/issues). A non-trivial code change without an issue reference with one of the labels `bug` or `enhancement` will not be merged. + +Note that we do not accept new features that require [CGO](https://go.dev/wiki/cgo). +We have one exception to this rule which is LibSASS. + +**Bug fixes are, of course, always welcome.** + +## Submitting Patches + +The Hugo project welcomes all contributors and contributions regardless of skill or experience level. If you are interested in helping with the project, we will help you with your contribution. + +### Code Contribution Guidelines + +Because we want to create the best possible product for our users and the best contribution experience for our developers, we have a set of guidelines which ensure that all contributions are acceptable. The guidelines are not intended as a filter or barrier to participation. If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines. + +To make the contribution process as seamless as possible, we ask for the following: + +* Go ahead and fork the project and make your changes. We encourage pull requests to allow for review and discussion of code changes. +* When you’re ready to create a pull request, be sure to: + * Sign the [CLA](https://cla-assistant.io/gohugoio/hugo). + * Have test cases for the new code. If you have questions about how to do this, please ask in your pull request. + * If you borrow a test case (e.g. from the issue), that test's author must be added as [co-author](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors) in the commit. + * Run `go fmt`. + * Add documentation if you are adding new features or changing functionality. The docs site lives in `/docs`. + * Squash your commits into a single commit. `git rebase -i`. It’s okay to force update your pull request with `git push -f`. + * Ensure that `./check.sh` succeeds. Note that some tests are skipped when running locally, some because they are slow. To run these locally, do `CI_LOCAL=true ./check.sh ./somepackage/...`. + * Follow the **Git Commit Message Guidelines** below. + +## AI Assistance Notice + +If a substantial part of your contribution is autogenerated with AI, **this must be disclosed in the pull request**, along with the extent to which AI assistance was used. AI contributions from non-maintainers needs to have a fairly narrow scope (e.g. a bug fix), as we have limited review capacity. + +An example disclosure: + +> This PR was written primarily by Claude Code. + +Also, When using AI assistance: + +* We expect contributors to manually verify that the state of the pull request is OK (e.g. that the CLI is signed). +* We expect contributors to understand the code that is produced and be able to answer critical questions about it + +### Git Commit Message Guidelines + +This [blog article](https://cbea.ms/git-commit/) is a good resource for learning how to write good commit messages, +the most important part being that each commit message should have a title/subject in imperative mood starting with a capital letter and no trailing period: +*"js: Return error when option x is not set"*, **NOT** *"returning some error."* + +Most title/subjects should have a lower-cased prefix with a colon and one whitespace. The prefix can be: + +* The name of the package where (most of) the changes are made (e.g. `media: Add text/calendar`) +* If the package name is deeply nested/long, try to shorten it from the left side, e.g. `markup/goldmark` is OK, `resources/resource_transformers/js` can be shortened to `js`. +* If this commit touches several packages with a common functional topic, use that as a prefix, e.g. `errors: Resolve correct line numbers`) +* If this commit touches many packages without a common functional topic, prefix with `all:` (e.g. `all: Reformat Go code`) +* If this is a documentation update, prefix with `docs:`. +* If nothing of the above applies, just leave the prefix out. +* Note that the above excludes nouns seen in other repositories, e.g. "chore:". + +Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*. +Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*. + +An example: + +```text +tpl: Add custom index function + +Add a custom index template function that deviates from the stdlib simply by not +returning an "index out of range" error if an array, slice or string index is +out of range. Instead, we just return nil values. This should help make the +new default function more useful for Hugo users. + +Fixes #1949 +``` + +### Fetching the Sources From GitHub + +Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. The easiest is to clone Hugo in a directory outside of `GOPATH`, as in the following example: + +```bash +mkdir $HOME/src +cd $HOME/src +git clone https://github.com/gohugoio/hugo.git +cd hugo +go install +``` + +For some convenient build and test targets, you also will want to install Mage: + +```bash +go install github.com/magefile/mage +``` + +Now, to make a change to Hugo's source: + +1. Create a new branch for your changes (the branch name is arbitrary): + + ```bash + git checkout -b iss1234 + ``` + +1. After making your changes, commit them to your new branch: + + ```bash + git commit -a -v + ``` + +1. Fork Hugo in GitHub. + +1. Add your fork as a new remote (the remote name, "fork" in this example, is arbitrary): + + ```bash + git remote add fork git@github.com:USERNAME/hugo.git + ``` + +1. Push the changes to your new remote: + + ```bash + git push --set-upstream fork iss1234 + ``` + +1. You're now ready to submit a PR based upon the new branch in your forked repository. + +### Building Hugo with Your Changes + +Hugo uses [mage](https://github.com/magefile/mage) to sync vendor dependencies, build Hugo, run the test suite and other things. You must run mage from the Hugo directory. + +```bash +cd $HOME/go/src/github.com/gohugoio/hugo +``` + +To build Hugo: + +```bash +mage hugo +``` + +To install hugo in `$HOME/go/bin`: + +```bash +mage install +``` + +To run the tests: + +```bash +mage hugoRace +mage -v check +``` + +To list all available commands along with descriptions: + +```bash +mage -l +``` + +**Note:** From Hugo 0.43 we have added a build tag, `extended` that adds **SCSS support**. This needs a C compiler installed to build. You can enable this when building by: + +```bash +HUGO_BUILD_TAGS=extended mage install +```` diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..ee9f9cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,101 @@ +# GitHub: https://github.com/gohugoio +# Twitter: https://twitter.com/gohugoio +# Website: https://gohugo.io/ + +ARG GO_VERSION="1.26" +ARG ALPINE_VERSION="3.22" +ARG DART_SASS_VERSION="1.79.3" + +FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS gobuild +FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS gorun + + +FROM gobuild AS build + +RUN apk add clang lld + +# Set up cross-compilation helpers +COPY --from=xx / / + +ARG TARGETPLATFORM +RUN xx-apk add musl-dev gcc g++ + +# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so: +# docker build --build-arg HUGO_BUILD_TAGS=withdeploy . +# +# We build the extended version by default. +ARG HUGO_BUILD_TAGS="extended" +ENV CGO_ENABLED=1 +ENV GOPROXY=https://proxy.golang.org +ENV GOCACHE=/root/.cache/go-build +ENV GOMODCACHE=/go/pkg/mod +ARG TARGETPLATFORM + +WORKDIR /go/src/github.com/gohugoio/hugo + +# For --mount=type=cache the value of target is the default cache id, so +# for the go mod cache it would be good if we could share it with other Go images using the same setup, +# but the go build cache needs to be per platform. +# See this comment: https://github.com/moby/buildkit/issues/1706#issuecomment-702238282 +RUN --mount=target=. \ + --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build,id=go-build-$TARGETPLATFORM <Hugo + +A fast and flexible static site generator built with love by [bep][], [spf13][], and [friends][] in Go. + +--- + +[![GoDoc](https://godoc.org/github.com/gohugoio/hugo?status.svg)](https://godoc.org/github.com/gohugoio/hugo) +[![Tests on Linux, MacOS and Windows](https://github.com/gohugoio/hugo/workflows/Test/badge.svg)](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest) +[![Go Report Card](https://goreportcard.com/badge/github.com/gohugoio/hugo)](https://goreportcard.com/report/github.com/gohugoio/hugo) + +[Website][] | [Installation][] | [Documentation][] | [Support][] | [Contributing][] | Mastodon + +## Overview + +Hugo is a [static site generator][] written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less. + +Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create: + +- Corporate, government, nonprofit, education, news, event, and project sites +- Documentation sites +- Image portfolios +- Landing pages +- Business, professional, and personal blogs +- Resumes and CVs + +Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. Then deploy the site to your host, or push changes to your Git provider for automated builds and deployment. + +Hugo's fast asset pipelines include: + +- CSS Processing – Bundle, transform, minify, create source maps, perform SRI hashing, and integrate with PostCSS. +- Image processing – Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract metadata +- JavaScript bundling – Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing. +- Sass processing – Transpile Sass to CSS, bundle, tree shake, minify, create source maps, perform SRI hashing, and integrate with PostCSS +- Tailwind CSS processing – Compile Tailwind CSS utility classes into standard CSS, bundle, tree shake, optimize, minify, perform SRI hashing, and integrate with PostCSS + +And with [Hugo Modules][], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories. + +See the [features][] section of the documentation for a comprehensive summary of Hugo's capabilities. + +## Sponsors + +

 

+

+ The complete IDE crafted for professional Go developers. +     + CloudCannon +

+ +## Editions + +Hugo is available in several editions. Use the standard edition unless you need additional features. + +Feature|standard|deploy|extended|extended/deploy +:--|:-:|:-:|:-:|:-: +Core features|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark: +Direct cloud deployment (1)|:x:|:heavy_check_mark:|:x:|:heavy_check_mark: +LibSass support (2)|:x:|:x:|:heavy_check_mark:|:heavy_check_mark: + +(1) Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details][]. + +(2) [Transpile Sass to CSS][] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass][] transpiler instead, which is compatible with any edition. + +## Installation + +Install Hugo from a [prebuilt binary][], package manager, or package repository. Please see the installation instructions for your operating system: + +- [macOS][] +- [Linux][] +- [Windows][] +- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD][] + +## Build from source + +To build Hugo from source you must install: + +1. [Git][] +1. [Go][] version 1.26.0 or later + +### Standard edition + +To build and install the standard edition: + +```sh +CGO_ENABLED=0 go install github.com/gohugoio/hugo@latest +``` + +### Deploy edition + +To build and install the deploy edition: + +```sh +CGO_ENABLED=0 go install -tags withdeploy github.com/gohugoio/hugo@latest +``` + +### Extended edition + +To build and install the extended edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command. + +```sh +CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest +``` + +### Extended/deploy edition + +To build and install the extended/deploy edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command. + +```sh +CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest +``` + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline) + +## Documentation + +Hugo's [documentation][] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples. + +Please submit documentation issues and pull requests to the [documentation repository][]. + +## Support + +Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum][]. + +Hugo's [forum][] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help][] before asking your first question. + +## Contributing + +You can contribute to the Hugo project by: + +- Answering questions on the [forum][] +- Improving the [documentation][] +- Monitoring the [issue queue][] +- Creating or improving [themes][] +- Squashing [bugs][] + +Please submit documentation issues and pull requests to the [documentation repository][]. + +If you have an idea for an enhancement or new feature, create a new topic on the [forum][] in the "Feature" category. This will help you to: + +- Determine if the capability already exists +- Measure interest +- Refine the concept + +If there is sufficient interest, [create a proposal][]. Do not submit a pull request until the project lead accepts the proposal. + +For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md). + +## License + +For the Hugo source code, see [LICENSE](/LICENSE). + +We also bundle some libraries in binary/WASM form: + +- [libwebp](https://github.com/webmproject/libwebp), [BSD-3-Clause license](https://github.com/webmproject/libwebp?tab=BSD-3-Clause-1-ov-file#readme) +- [Katex](https://github.com/KaTeX/KaTeX), [MIT license](https://github.com/KaTeX/KaTeX?tab=MIT-1-ov-file#readme) +- [QuickJS](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme), [License](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme) + +## Dependencies + +Hugo stands on the shoulders of great open source libraries. Run `hugo env --logLevel info` to display a list of dependencies. + +
+See current dependencies + +```text +github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69" +github.com/JohannesKaufmann/dom="v0.2.0" +github.com/JohannesKaufmann/html-to-markdown/v2="v2.5.0" +github.com/alecthomas/chroma/v2="v2.21.1" +github.com/aymerick/douceur="v0.2.0" +github.com/bep/clocks="v0.5.0" +github.com/bep/debounce="v1.2.0" +github.com/bep/gitmap="v1.9.0" +github.com/bep/goat="v0.5.0" +github.com/bep/godartsass/v2="v2.5.0" +github.com/bep/golibsass="v1.2.0" +github.com/bep/goportabletext="v0.1.0" +github.com/bep/helpers="v0.6.0" +github.com/bep/imagemeta="v0.12.0" +github.com/bep/lazycache="v0.8.0" +github.com/bep/logg="v0.4.0" +github.com/bep/mclib="v1.20400.20402" +github.com/bep/overlayfs="v0.10.0" +github.com/bep/simplecobra="v0.6.1" +github.com/bep/textandbinarywriter="v0.0.0-20251212174530-cd9f0732f60f" +github.com/bep/tmc="v0.5.1" +github.com/bits-and-blooms/bitset="v1.24.4" +github.com/cespare/xxhash/v2="v2.3.0" +github.com/clbanning/mxj/v2="v2.7.0" +github.com/clipperhouse/displaywidth="v0.6.0" +github.com/clipperhouse/stringish="v0.1.1" +github.com/clipperhouse/uax29/v2="v2.3.0" +github.com/cpuguy83/go-md2man/v2="v2.0.6" +github.com/disintegration/gift="v1.2.1" +github.com/dlclark/regexp2="v1.11.5" +github.com/evanw/esbuild="v0.27.2" +github.com/fatih/color="v1.18.0" +github.com/frankban/quicktest="v1.14.6" +github.com/fsnotify/fsnotify="v1.9.0" +github.com/getkin/kin-openapi="v0.133.0" +github.com/go-openapi/jsonpointer="v0.21.0" +github.com/go-openapi/swag="v0.23.0" +github.com/gobuffalo/flect="v1.0.3" +github.com/gobwas/glob="v0.2.3" +github.com/goccy/go-yaml="v1.19.1" +github.com/gohugoio/go-i18n/v2="v2.1.3-0.20251018145728-cfcc22d823c6" +github.com/gohugoio/go-radix="v1.2.0" +github.com/gohugoio/hashstructure="v0.6.0" +github.com/gohugoio/httpcache="v0.8.0" +github.com/gohugoio/hugo-goldmark-extensions/extras="v0.5.0" +github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.3.1" +github.com/gohugoio/locales="v0.14.0" +github.com/gohugoio/localescompressed="v1.0.1" +github.com/google/go-cmp="v0.7.0" +github.com/gorilla/css="v1.0.1" +github.com/gorilla/websocket="v1.5.3" +github.com/hairyhenderson/go-codeowners="v0.7.0" +github.com/hashicorp/golang-lru/v2="v2.0.7" +github.com/jdkato/prose="v1.2.1" +github.com/josharian/intern="v1.0.0" +github.com/kr/pretty="v0.3.1" +github.com/kr/text="v0.2.0" +github.com/kyokomi/emoji/v2="v2.2.13" +github.com/mailru/easyjson="v0.7.7" +github.com/makeworld-the-better-one/dither/v2="v2.4.0" +github.com/marekm4/color-extractor="v1.2.1" +github.com/mattn/go-colorable="v0.1.13" +github.com/mattn/go-isatty="v0.0.20" +github.com/mattn/go-runewidth="v0.0.19" +github.com/microcosm-cc/bluemonday="v1.0.27" +github.com/mitchellh/mapstructure="v1.5.1-0.20231216201459-8508981c8b6c" +github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826" +github.com/muesli/smartcrop="v0.3.0" +github.com/niklasfasching/go-org="v1.9.1" +github.com/oasdiff/yaml3="v0.0.0-20250309153720-d2182401db90" +github.com/oasdiff/yaml="v0.0.0-20250309154309-f31be36b4037" +github.com/olekukonko/cat="v0.0.0-20250911104152-50322a0618f6" +github.com/olekukonko/errors="v1.1.0" +github.com/olekukonko/ll="v0.1.3" +github.com/olekukonko/tablewriter="v1.1.2" +github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58" +github.com/pelletier/go-toml/v2="v2.2.4" +github.com/perimeterx/marshmallow="v1.1.5" +github.com/pkg/browser="v0.0.0-20240102092130-5ac0b6a4141c" +github.com/pkg/errors="v0.9.1" +github.com/rogpeppe/go-internal="v1.14.1" +github.com/russross/blackfriday/v2="v2.1.0" +github.com/sass/dart-sass/compiler="1.97.1" +github.com/sass/dart-sass/implementation="1.97.1" +github.com/sass/dart-sass/protocol="3.2.0" +github.com/spf13/afero="v1.15.0" +github.com/spf13/cast="v1.10.0" +github.com/spf13/cobra="v1.10.2" +github.com/spf13/fsync="v0.10.1" +github.com/spf13/pflag="v1.0.9" +github.com/tdewolff/minify/v2="v2.24.8" +github.com/tdewolff/parse/v2="v2.8.5" +github.com/tetratelabs/wazero="v1.10.1" +github.com/webmproject/libwebp="v1.6.0" +github.com/woodsbury/decimal128="v1.3.0" +github.com/yuin/goldmark-emoji="v1.0.6" +github.com/yuin/goldmark="v1.7.13" +go.uber.org/automaxprocs="v1.5.3" +go.yaml.in/yaml/v3="v3.0.4" +golang.org/x/crypto="v0.46.0" +golang.org/x/image="v0.34.0" +golang.org/x/mod="v0.31.0" +golang.org/x/net="v0.48.0" +golang.org/x/sync="v0.19.0" +golang.org/x/sys="v0.39.0" +golang.org/x/text="v0.32.0" +golang.org/x/tools="v0.40.0" +google.golang.org/protobuf="v1.36.10" +gopkg.in/yaml.v3="v3.0.1" +rsc.io/qr="v0.2.0" +software.sslmate.com/src/go-pkcs12="v0.2.0" +``` +
diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..5ccb56a --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`gohugoio/hugo` +- 原始仓库:https://github.com/gohugoio/hugo +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e6072ed --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +## Security Policy + +### Before You Report + +Please read [Hugo's Security Model](https://gohugo.io/about/security/) first. If the issue reproduces in an upstream project, please report it there — we cannot triage or patch on their behalf. + +### Reporting a Vulnerability + +If, after the above, you believe you have found a vulnerability in Hugo itself with a concrete, reproducible impact, report it privately to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. Include a minimal reproducer, the Hugo version, and the observed vs. expected behavior. + +You should receive an initial response within a few days. Confirmed issues are typically patched within days, depending on complexity. diff --git a/bufferpool/bufpool.go b/bufferpool/bufpool.go new file mode 100644 index 0000000..f05675e --- /dev/null +++ b/bufferpool/bufpool.go @@ -0,0 +1,38 @@ +// Copyright 2015 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package bufferpool provides a pool of bytes buffers. +package bufferpool + +import ( + "bytes" + "sync" +) + +var bufferPool = &sync.Pool{ + New: func() any { + return &bytes.Buffer{} + }, +} + +// GetBuffer returns a buffer from the pool. +func GetBuffer() (buf *bytes.Buffer) { + return bufferPool.Get().(*bytes.Buffer) +} + +// PutBuffer returns a buffer to the pool. +// The buffer is reset before it is put back into circulation. +func PutBuffer(buf *bytes.Buffer) { + buf.Reset() + bufferPool.Put(buf) +} diff --git a/bufferpool/bufpool_test.go b/bufferpool/bufpool_test.go new file mode 100644 index 0000000..023724b --- /dev/null +++ b/bufferpool/bufpool_test.go @@ -0,0 +1,31 @@ +// Copyright 2016-present The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bufferpool + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestBufferPool(t *testing.T) { + c := qt.New(t) + + buff := GetBuffer() + buff.WriteString("do be do be do") + c.Assert(buff.String(), qt.Equals, "do be do be do") + PutBuffer(buff) + + c.Assert(buff.Len(), qt.Equals, 0) +} diff --git a/cache/docs.go b/cache/docs.go new file mode 100644 index 0000000..b9c4984 --- /dev/null +++ b/cache/docs.go @@ -0,0 +1,2 @@ +// Package cache contains the different cache implementations. +package cache diff --git a/cache/dynacache/dynacache.go b/cache/dynacache/dynacache.go new file mode 100644 index 0000000..54bbf3a --- /dev/null +++ b/cache/dynacache/dynacache.go @@ -0,0 +1,647 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dynacache + +import ( + "context" + "fmt" + "math" + "path" + "regexp" + "runtime" + "sync" + "time" + + "github.com/bep/lazycache" + "github.com/bep/logg" + "github.com/gohugoio/hugo/common/collections" + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/common/rungroup" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources/resource" +) + +const minMaxSize = 10 + +type KeyIdentity struct { + Key any + Identity identity.Identity +} + +// New creates a new cache. +func New(opts Options) *Cache { + if opts.CheckInterval == 0 { + opts.CheckInterval = time.Second * 2 + } + + if opts.MaxSize == 0 { + opts.MaxSize = 100000 + } + if opts.Log == nil { + panic("nil Log") + } + + if opts.MinMaxSize == 0 { + opts.MinMaxSize = 30 + } + + stats := &stats{ + opts: opts, + adjustmentFactor: 1.0, + currentMaxSize: opts.MaxSize, + availableMemory: config.GetMemoryLimit(), + } + + infol := opts.Log.InfoCommand("dynacache") + + evictedIdentities := collections.NewStackThreadSafe[KeyIdentity]() + + onEvict := func(k, v any) { + if !opts.Watching { + return + } + identity.WalkIdentitiesShallow(v, func(level int, id identity.Identity) bool { + evictedIdentities.Push(KeyIdentity{Key: k, Identity: id}) + return false + }) + resource.MarkStale(v) + } + + c := &Cache{ + partitions: make(map[string]PartitionManager), + onEvict: onEvict, + evictedIdentities: evictedIdentities, + opts: opts, + stats: stats, + infol: infol, + } + + c.stop = c.start() + + return c +} + +// Options for the cache. +type Options struct { + Log loggers.Logger + CheckInterval time.Duration + MaxSize int + MinMaxSize int + Watching bool +} + +// Options for a partition. +type OptionsPartition struct { + // When to clear the this partition. + ClearWhen ClearWhen + + // Weight is a number between 1 and 100 that indicates how, in general, how big this partition may get. + Weight int +} + +func (o OptionsPartition) WeightFraction() float64 { + return float64(o.Weight) / 100 +} + +func (o OptionsPartition) CalculateMaxSize(maxSizePerPartition int) int { + return int(math.Floor(float64(maxSizePerPartition) * o.WeightFraction())) +} + +// A dynamic partitioned cache. +type Cache struct { + mu sync.RWMutex + + partitions map[string]PartitionManager + + onEvict func(k, v any) + evictedIdentities *collections.StackThreadSafe[KeyIdentity] + + opts Options + infol logg.LevelLogger + + stats *stats + stopOnce sync.Once + stop func() +} + +// DrainEvictedIdentities drains the evicted identities from the cache. +func (c *Cache) DrainEvictedIdentities() []KeyIdentity { + return c.evictedIdentities.Drain() +} + +// DrainEvictedIdentitiesMatching drains the evicted identities from the cache that match the given predicate. +func (c *Cache) DrainEvictedIdentitiesMatching(predicate func(KeyIdentity) bool) []KeyIdentity { + return c.evictedIdentities.DrainMatching(predicate) +} + +// ClearMatching clears all partition for which the predicate returns true. +func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManager) bool, predicateValue func(k, v any) bool) { + if predicatePartition == nil { + predicatePartition = func(k string, p PartitionManager) bool { return true } + } + if predicateValue == nil { + panic("nil predicateValue") + } + g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ + NumWorkers: len(c.partitions), + Handle: func(ctx context.Context, partition PartitionManager) error { + partition.clearMatching(predicateValue) + return nil + }, + }) + + for k, p := range c.partitions { + if !predicatePartition(k, p) { + continue + } + g.Enqueue(p) + } + + g.Wait() +} + +// ClearOnRebuild prepares the cache for a new rebuild taking the given changeset into account. +// predicate is optional and will clear any entry for which it returns true. +func (c *Cache) ClearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { + g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ + NumWorkers: len(c.partitions), + Handle: func(ctx context.Context, partition PartitionManager) error { + partition.clearOnRebuild(predicate, changeset...) + return nil + }, + }) + + for _, p := range c.partitions { + g.Enqueue(p) + } + + g.Wait() + + // Clear any entries marked as stale above. + g = rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ + NumWorkers: len(c.partitions), + Handle: func(ctx context.Context, partition PartitionManager) error { + partition.clearStale() + return nil + }, + }) + + for _, p := range c.partitions { + g.Enqueue(p) + } + + g.Wait() +} + +type keysProvider interface { + Keys() []string +} + +// Keys returns a list of keys in all partitions. +func (c *Cache) Keys(predicate func(s string) bool) []string { + if predicate == nil { + predicate = func(s string) bool { return true } + } + var keys []string + for pn, g := range c.partitions { + pkeys := g.(keysProvider).Keys() + for _, k := range pkeys { + p := path.Join(pn, k) + if predicate(p) { + keys = append(keys, p) + } + } + + } + return keys +} + +func calculateMaxSizePerPartition(maxItemsTotal, totalWeightQuantity, numPartitions int) int { + if numPartitions == 0 { + panic("numPartitions must be > 0") + } + if totalWeightQuantity == 0 { + panic("totalWeightQuantity must be > 0") + } + + avgWeight := float64(totalWeightQuantity) / float64(numPartitions) + return int(math.Floor(float64(maxItemsTotal) / float64(numPartitions) * (100.0 / avgWeight))) +} + +// Stop stops the cache. +func (c *Cache) Stop() { + c.stopOnce.Do(func() { + c.stop() + }) +} + +func (c *Cache) adjustCurrentMaxSize() { + c.mu.RLock() + defer c.mu.RUnlock() + + if len(c.partitions) == 0 { + return + } + var m runtime.MemStats + runtime.ReadMemStats(&m) + s := c.stats + s.memstatsCurrent = m + // fmt.Printf("\n\nAvailable = %v\nAlloc = %v\nTotalAlloc = %v\nSys = %v\nNumGC = %v\nMaxSize = %d\nAdjustmentFactor=%f\n\n", helpers.FormatByteCount(s.availableMemory), helpers.FormatByteCount(m.Alloc), helpers.FormatByteCount(m.TotalAlloc), helpers.FormatByteCount(m.Sys), m.NumGC, c.stats.currentMaxSize, s.adjustmentFactor) + + if s.availableMemory >= s.memstatsCurrent.Alloc { + if s.adjustmentFactor <= 1.0 { + s.adjustmentFactor += 0.2 + } + } else { + // We're low on memory. + s.adjustmentFactor -= 0.4 + } + + if s.adjustmentFactor <= 0 { + s.adjustmentFactor = 0.05 + } + + if !s.adjustCurrentMaxSize() { + return + } + + totalWeight := 0 + for _, pm := range c.partitions { + totalWeight += pm.getOptions().Weight + } + + maxSizePerPartition := calculateMaxSizePerPartition(c.stats.currentMaxSize, totalWeight, len(c.partitions)) + + evicted := 0 + for _, p := range c.partitions { + evicted += p.adjustMaxSize(p.getOptions().CalculateMaxSize(maxSizePerPartition)) + } + + if evicted > 0 { + c.infol. + WithFields( + logg.Fields{ + {Name: "evicted", Value: evicted}, + {Name: "numGC", Value: m.NumGC}, + {Name: "limit", Value: helpers.FormatByteCount(c.stats.availableMemory)}, + {Name: "alloc", Value: helpers.FormatByteCount(m.Alloc)}, + {Name: "totalAlloc", Value: helpers.FormatByteCount(m.TotalAlloc)}, + }, + ).Logf("adjusted partitions' max size") + } +} + +func (c *Cache) start() func() { + ticker := time.NewTicker(c.opts.CheckInterval) + quit := make(chan struct{}) + + go func() { + for { + select { + case <-ticker.C: + c.adjustCurrentMaxSize() + // Reset the ticker to avoid drift. + ticker.Reset(c.opts.CheckInterval) + case <-quit: + ticker.Stop() + return + } + } + }() + + return func() { + close(quit) + } +} + +var partitionNameRe = regexp.MustCompile(`^\/[a-zA-Z0-9]{4}(\/[a-zA-Z0-9]+)?(\/[a-zA-Z0-9]+)?`) + +// GetOrCreatePartition gets or creates a partition with the given name. +func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts OptionsPartition) *Partition[K, V] { + if c == nil { + panic("nil Cache") + } + if opts.Weight < 1 || opts.Weight > 100 { + panic("invalid Weight, must be between 1 and 100") + } + + if !partitionNameRe.MatchString(name) { + panic(fmt.Sprintf("invalid partition name %q", name)) + } + + c.mu.RLock() + p, found := c.partitions[name] + c.mu.RUnlock() + if found { + return p.(*Partition[K, V]) + } + + c.mu.Lock() + defer c.mu.Unlock() + + // Double check. + p, found = c.partitions[name] + if found { + return p.(*Partition[K, V]) + } + + // At this point, we don't know the number of partitions or their configuration, but + // this will be re-adjusted later. + const numberOfPartitionsEstimate = 10 + maxSize := opts.CalculateMaxSize(c.opts.MaxSize / numberOfPartitionsEstimate) + + onEvict := func(k K, v V) { + c.onEvict(k, v) + } + + // Create a new partition and cache it. + partition := &Partition[K, V]{ + c: lazycache.New(lazycache.Options[K, V]{MaxEntries: maxSize, OnEvict: onEvict}), + maxSize: maxSize, + trace: c.opts.Log.Logger().WithLevel(logg.LevelTrace).WithField("partition", name), + opts: opts, + } + + c.partitions[name] = partition + + return partition +} + +// Partition is a partition in the cache. +type Partition[K comparable, V any] struct { + c *lazycache.Cache[K, V] + + zero V + + trace logg.LevelLogger + opts OptionsPartition + + maxSize int +} + +// GetOrCreate gets or creates a value for the given key. +func (p *Partition[K, V]) GetOrCreate(key K, create func(key K) (V, error)) (V, error) { + v, err := p.doGetOrCreate(key, create) + if err != nil { + return p.zero, err + } + if resource.StaleVersion(v) > 0 { + p.c.Delete(key) + return p.doGetOrCreate(key, create) + } + return v, err +} + +func (p *Partition[K, V]) doGetOrCreate(key K, create func(key K) (V, error)) (V, error) { + v, _, err := p.c.GetOrCreate(key, create) + return v, err +} + +func (p *Partition[K, V]) GetOrCreateWitTimeout(key K, duration time.Duration, create func(key K) (V, error)) (V, error) { + v, err := p.doGetOrCreateWitTimeout(key, duration, create) + if err != nil { + return p.zero, err + } + if resource.StaleVersion(v) > 0 { + p.c.Delete(key) + return p.doGetOrCreateWitTimeout(key, duration, create) + } + return v, err +} + +// GetOrCreateWitTimeout gets or creates a value for the given key and times out if the create function +// takes too long. +func (p *Partition[K, V]) doGetOrCreateWitTimeout(key K, duration time.Duration, create func(key K) (V, error)) (V, error) { + resultch := make(chan V, 1) + errch := make(chan error, 1) + + go func() { + var ( + v V + err error + ) + defer func() { + if r := recover(); r != nil { + if rerr, ok := r.(error); ok { + err = rerr + } else { + err = fmt.Errorf("panic: %v", r) + } + } + if err != nil { + errch <- err + } else { + resultch <- v + } + }() + v, _, err = p.c.GetOrCreate(key, create) + }() + + select { + case v := <-resultch: + return v, nil + case err := <-errch: + return p.zero, err + case <-time.After(duration): + return p.zero, &herrors.TimeoutError{ + Duration: duration, + } + } +} + +func (p *Partition[K, V]) clearMatching(predicate func(k, v any) bool) { + p.c.DeleteFunc(func(key K, v V) bool { + if predicate(key, v) { + p.trace.Log( + logg.StringFunc( + func() string { + return fmt.Sprintf("clearing cache key %v", key) + }, + ), + ) + return true + } + return false + }) +} + +func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { + if predicate == nil { + predicate = func(k, v any) bool { + return false + } + } + opts := p.getOptions() + if opts.ClearWhen == ClearNever { + return + } + + if opts.ClearWhen == ClearOnRebuild { + // Clear all. + p.Clear() + return + } + + depsFinder := identity.NewFinder(identity.FinderConfig{}) + + shouldDelete := func(key K, v V) bool { + // We always clear elements marked as stale. + if resource.StaleVersion(v) > 0 { + return true + } + + // Now check if this entry has changed based on the changeset + // based on filesystem events. + if len(changeset) == 0 { + // Nothing changed. + return false + } + + var probablyDependent bool + identity.WalkIdentitiesShallow(v, func(level int, id2 identity.Identity) bool { + for _, id := range changeset { + if r := depsFinder.Contains(id, id2, -1); r > 0 { + // It's probably dependent, evict from cache. + probablyDependent = true + return true + } + } + return false + }) + + return probablyDependent + } + + // First pass. + // Second pass needs to be done in a separate loop to catch any + // elements marked as stale in the other partitions. + p.c.DeleteFunc(func(key K, v V) bool { + if predicate(key, v) || shouldDelete(key, v) { + p.trace.Log( + logg.StringFunc( + func() string { + return fmt.Sprintf("first pass: clearing cache key %v", key) + }, + ), + ) + return true + } + return false + }) +} + +func (p *Partition[K, V]) Keys() []K { + var keys []K + p.c.DeleteFunc(func(key K, v V) bool { + keys = append(keys, key) + return false + }) + return keys +} + +func (p *Partition[K, V]) clearStale() { + p.c.DeleteFunc(func(key K, v V) bool { + staleVersion := resource.StaleVersion(v) + if staleVersion > 0 { + p.trace.Log( + logg.StringFunc( + func() string { + return fmt.Sprintf("second pass: clearing cache key %v", key) + }, + ), + ) + } + + return staleVersion > 0 + }) +} + +// adjustMaxSize adjusts the max size of the and returns the number of items evicted. +func (p *Partition[K, V]) adjustMaxSize(newMaxSize int) int { + if newMaxSize < minMaxSize { + newMaxSize = minMaxSize + } + oldMaxSize := p.maxSize + if newMaxSize == oldMaxSize { + return 0 + } + p.maxSize = newMaxSize + // fmt.Println("Adjusting max size of partition from", oldMaxSize, "to", newMaxSize) + return p.c.Resize(newMaxSize) +} + +func (p *Partition[K, V]) getMaxSize() int { + return p.maxSize +} + +func (p *Partition[K, V]) getOptions() OptionsPartition { + return p.opts +} + +func (p *Partition[K, V]) Clear() { + p.c.DeleteFunc(func(key K, v V) bool { + return true + }) +} + +func (p *Partition[K, V]) Get(ctx context.Context, key K) (V, bool) { + return p.c.Get(key) +} + +type PartitionManager interface { + adjustMaxSize(addend int) int + getMaxSize() int + getOptions() OptionsPartition + clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) + clearMatching(predicate func(k, v any) bool) + clearStale() +} + +const ( + ClearOnRebuild ClearWhen = iota + 1 + ClearOnChange + ClearNever +) + +type ClearWhen int + +type stats struct { + opts Options + memstatsCurrent runtime.MemStats + currentMaxSize int + availableMemory uint64 + + adjustmentFactor float64 +} + +func (s *stats) adjustCurrentMaxSize() bool { + newCurrentMaxSize := int(math.Floor(float64(s.opts.MaxSize) * s.adjustmentFactor)) + + if newCurrentMaxSize < s.opts.MinMaxSize { + newCurrentMaxSize = int(s.opts.MinMaxSize) + } + changed := newCurrentMaxSize != s.currentMaxSize + s.currentMaxSize = newCurrentMaxSize + return changed +} + +// CleanKey turns s into a format suitable for a cache key for this package. +// The key will be a Unix-styled path with a leading slash but no trailing slash. +func CleanKey(s string) string { + return path.Clean(paths.ToSlashPreserveLeading(s)) +} diff --git a/cache/dynacache/dynacache_test.go b/cache/dynacache/dynacache_test.go new file mode 100644 index 0000000..78b2fc8 --- /dev/null +++ b/cache/dynacache/dynacache_test.go @@ -0,0 +1,230 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dynacache + +import ( + "errors" + "fmt" + "path/filepath" + "testing" + "time" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources/resource" +) + +var ( + _ resource.StaleInfo = (*testItem)(nil) + _ identity.Identity = (*testItem)(nil) +) + +type testItem struct { + name string + staleVersion uint32 +} + +func (t testItem) StaleVersion() uint32 { + return t.staleVersion +} + +func (t testItem) IdentifierBase() string { + return t.name +} + +func TestCache(t *testing.T) { + t.Parallel() + c := qt.New(t) + + cache := New(Options{ + Log: loggers.NewDefault(), + }) + + c.Cleanup(func() { + cache.Stop() + }) + + opts := OptionsPartition{Weight: 30} + + c.Assert(cache, qt.Not(qt.IsNil)) + + p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", opts) + c.Assert(p1, qt.Not(qt.IsNil)) + + p2 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", opts) + + c.Assert(func() { GetOrCreatePartition[string, testItem](cache, "foo bar", opts) }, qt.PanicMatches, ".*invalid partition name.*") + c.Assert(func() { GetOrCreatePartition[string, testItem](cache, "/aaaa/cccc", OptionsPartition{Weight: 1234}) }, qt.PanicMatches, ".*invalid Weight.*") + + c.Assert(p2, qt.Equals, p1) + + p3 := GetOrCreatePartition[string, testItem](cache, "/aaaa/cccc", opts) + c.Assert(p3, qt.Not(qt.IsNil)) + c.Assert(p3, qt.Not(qt.Equals), p1) + + c.Assert(func() { New(Options{}) }, qt.PanicMatches, ".*nil Log.*") +} + +func TestCalculateMaxSizePerPartition(t *testing.T) { + t.Parallel() + c := qt.New(t) + + c.Assert(calculateMaxSizePerPartition(1000, 500, 5), qt.Equals, 200) + c.Assert(calculateMaxSizePerPartition(1000, 250, 5), qt.Equals, 400) + c.Assert(func() { calculateMaxSizePerPartition(1000, 250, 0) }, qt.PanicMatches, ".*must be > 0.*") + c.Assert(func() { calculateMaxSizePerPartition(1000, 0, 1) }, qt.PanicMatches, ".*must be > 0.*") +} + +func TestCleanKey(t *testing.T) { + c := qt.New(t) + + c.Assert(CleanKey("a/b/c"), qt.Equals, "/a/b/c") + c.Assert(CleanKey("/a/b/c"), qt.Equals, "/a/b/c") + c.Assert(CleanKey("a/b/c/"), qt.Equals, "/a/b/c") + c.Assert(CleanKey(filepath.FromSlash("/a/b/c/")), qt.Equals, "/a/b/c") +} + +func newTestCache(t *testing.T) *Cache { + cache := New( + Options{ + Log: loggers.NewDefault(), + }, + ) + + p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", OptionsPartition{Weight: 30, ClearWhen: ClearOnRebuild}) + p2 := GetOrCreatePartition[string, testItem](cache, "/aaaa/cccc", OptionsPartition{Weight: 30, ClearWhen: ClearOnChange}) + + p1.GetOrCreate("clearOnRebuild", func(string) (testItem, error) { + return testItem{}, nil + }) + + p2.GetOrCreate("clearBecauseStale", func(string) (testItem, error) { + return testItem{ + staleVersion: 32, + }, nil + }) + + p2.GetOrCreate("clearBecauseIdentityChanged", func(string) (testItem, error) { + return testItem{ + name: "changed", + }, nil + }) + + p2.GetOrCreate("clearNever", func(string) (testItem, error) { + return testItem{ + staleVersion: 0, + }, nil + }) + + t.Cleanup(func() { + cache.Stop() + }) + + return cache +} + +func TestClear(t *testing.T) { + t.Parallel() + c := qt.New(t) + + predicateAll := func(string) bool { + return true + } + + cache := newTestCache(t) + + c.Assert(cache.Keys(predicateAll), qt.HasLen, 4) + + cache.ClearOnRebuild(nil) + + // Stale items are always cleared. + c.Assert(cache.Keys(predicateAll), qt.HasLen, 2) + + cache = newTestCache(t) + cache.ClearOnRebuild(nil, identity.StringIdentity("changed")) + + c.Assert(cache.Keys(nil), qt.HasLen, 1) + + cache = newTestCache(t) + + cache.ClearMatching(nil, func(k, v any) bool { + return k.(string) == "clearOnRebuild" + }) + + c.Assert(cache.Keys(predicateAll), qt.HasLen, 3) + + cache.adjustCurrentMaxSize() +} + +func TestPanicInCreate(t *testing.T) { + t.Parallel() + c := qt.New(t) + cache := newTestCache(t) + + p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", OptionsPartition{Weight: 30, ClearWhen: ClearOnRebuild}) + + willPanic := func(i int) func() { + return func() { + p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + panic(errors.New(key)) + }) + } + } + + // GetOrCreateWitTimeout needs to recover from panics in the create func. + willErr := func(i int) error { + _, err := p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{}, errors.New(key) + }) + return err + } + + for i := range 3 { + for range 3 { + c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("panic-%d", i)) + c.Assert(willErr(i), qt.ErrorMatches, fmt.Sprintf("error-%d", i)) + } + } + + // Test the same keys again without the panic. + for i := range 3 { + for range 3 { + v, err := p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil + }) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("panic-%d", i)) + + v, err = p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil + }) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("error-%d", i)) + } + } +} + +func TestAdjustCurrentMaxSize(t *testing.T) { + t.Parallel() + c := qt.New(t) + cache := newTestCache(t) + alloc := cache.stats.memstatsCurrent.Alloc + cache.adjustCurrentMaxSize() + c.Assert(cache.stats.memstatsCurrent.Alloc, qt.Not(qt.Equals), alloc) +} diff --git a/cache/filecache/filecache.go b/cache/filecache/filecache.go new file mode 100644 index 0000000..f5d5b09 --- /dev/null +++ b/cache/filecache/filecache.go @@ -0,0 +1,570 @@ +// Copyright 2026 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/gohugoio/httpcache" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/hugofs" + + "github.com/gohugoio/hugo/helpers" + + "github.com/BurntSushi/locker" + "github.com/bep/helpers/maphelpers" + "github.com/spf13/afero" +) + +// ErrFatal can be used to signal an unrecoverable error. +var ErrFatal = errors.New("fatal filecache error") + +const ( + FilecacheRootDirname = "filecache" +) + +// Cache caches a set of files in a directory. This is usually a file on +// disk, but since this is backed by an Afero file system, it can be anything. +type Cache struct { + Fs afero.Fs + + cfg FileCacheConfig + + entryLocker *lockTracker + + initOnce sync.Once + isInited bool + initErr error +} + +type lockTracker struct { + seen *maphelpers.ConcurrentSet[string] + + *locker.Locker +} + +// Lock tracks the ids in use. We use this information to do garbage collection +// after a Hugo build. +func (l *lockTracker) Lock(id string) { + l.seen.AddIfAbsent(id) + l.Locker.Lock(id) +} + +// ItemInfo contains info about a cached file. +type ItemInfo struct { + // This is the file's name relative to the cache's filesystem. + Name string +} + +// NewCache creates a new file cache with the given filesystem and max age. +func NewCache(fs afero.Fs, cfg FileCacheConfig) *Cache { + if err := cfg.init(); err != nil { + panic(fmt.Sprintf("invalid cache config: %s", err)) + } + + return &Cache{ + Fs: fs, + entryLocker: &lockTracker{Locker: locker.NewLocker(), seen: maphelpers.NewConcurrentSet[string]()}, + cfg: cfg, + } +} + +// lockedFile is a file with a lock that is released on Close. +type lockedFile struct { + afero.File + unlock func() +} + +func (l *lockedFile) Close() error { + defer l.unlock() + return l.File.Close() +} + +func (c *Cache) init() error { + if c == nil { + panic("cache is nil") + } + + c.initOnce.Do(func() { + c.isInited = true + // Create the base dir if it does not exist. + if err := c.Fs.MkdirAll("", 0o777); err != nil && !os.IsExist(err) { + err = fmt.Errorf("failled to create base cache directory: %s", err) + c.initErr = err + } + }) + return c.initErr +} + +// WriteCloser returns a transactional writer into the cache. +// It's important that it's closed when done. +func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) { + if err := c.init(); err != nil { + return ItemInfo{}, nil, err + } + + id = cleanID(id) + c.entryLocker.Lock(id) + + info := ItemInfo{Name: id} + + f, err := helpers.OpenFileForWriting(c.Fs, id) + if err != nil { + c.entryLocker.Unlock(id) + return info, nil, err + } + + return info, &lockedFile{ + File: f, + unlock: func() { c.entryLocker.Unlock(id) }, + }, nil +} + +// ReadOrCreate tries to lookup the file in cache. +// If found, it is passed to read and then closed. +// If not found a new file is created and passed to create, which should close +// it when done. +func (c *Cache) ReadOrCreate(id string, + read func(info ItemInfo, r io.ReadSeeker) error, + create func(info ItemInfo, w io.WriteCloser) error, +) (info ItemInfo, err error) { + if err := c.init(); err != nil { + return ItemInfo{}, err + } + + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info = ItemInfo{Name: id} + + if r := c.getOrRemove(id); r != nil { + err = read(info, r) + defer r.Close() + if err == nil || err == ErrFatal { + // See https://github.com/gohugoio/hugo/issues/6401 + // To recover from file corruption we handle read errors + // as the cache item was not found. + // Any file permission issue will also fail in the next step. + return + } + } + + f, err := helpers.OpenFileForWriting(c.Fs, id) + if err != nil { + return + } + + err = create(info, f) + + return +} + +// NamedLock locks the given id. The lock is released when the returned function is called. +func (c *Cache) NamedLock(id string) func() { + id = cleanID(id) + c.entryLocker.Lock(id) + return func() { + c.entryLocker.Unlock(id) + } +} + +// GetOrCreate tries to get the file with the given id from cache. If not found or expired, create will +// be invoked and the result cached. +// This method is protected by a named lock using the given id as identifier. +func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (ItemInfo, io.ReadCloser, error) { + if err := c.init(); err != nil { + return ItemInfo{}, nil, err + } + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info := ItemInfo{Name: id} + + if r := c.getOrRemove(id); r != nil { + return info, r, nil + } + + var ( + r io.ReadCloser + err error + ) + + r, err = create() + if err != nil { + return info, nil, err + } + + if c.cfg.MaxAge == 0 { + // No caching. + return info, hugio.ToReadCloser(r), nil + } + + var buff bytes.Buffer + return info, + hugio.ToReadCloser(&buff), + c.writeReader(id, io.TeeReader(r, &buff)) +} + +// AbsFilenameFromID returns the filename for the given id in the cache. +// This will be an absolute path. +func (c *Cache) AbsFilenameFromID(id string) string { + return filepath.Join(c.cfg.DirCompiled, cleanID(id)) +} + +// GetOrCreateInfo tries to get the item info with the given id from cache. If not found or expired, create will +// be invoked with the id. The create function is expected to create the cache item with the given id. The returned ItemInfo will have the id as Name. +// This method is protected by a named lock using the given id as identifier. +func (c *Cache) GetOrCreateInfo(id string, create func(id string) error) (ItemInfo, error) { + if err := c.init(); err != nil { + return ItemInfo{}, err + } + + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info := ItemInfo{Name: id} + + if !c.removeIfNeeded(id) { + // The file exists and is not expired, so we consider it a cache hit. + return info, nil + } + + if err := create(id); err != nil { + c.remove(id) + return info, err + } + + return info, nil +} + +func (c *Cache) writeReader(id string, r io.Reader) error { + dir := filepath.Dir(id) + if dir != "" { + _ = c.Fs.MkdirAll(dir, 0o777) + } + f, err := c.Fs.Create(id) + if err != nil { + return err + } + defer f.Close() + + _, _ = io.Copy(f, r) + + return nil +} + +// GetOrCreateBytes is the same as GetOrCreate, but produces a byte slice. +func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (ItemInfo, []byte, error) { + if err := c.init(); err != nil { + return ItemInfo{}, nil, err + } + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info := ItemInfo{Name: id} + + if r := c.getOrRemove(id); r != nil { + defer r.Close() + b, err := io.ReadAll(r) + return info, b, err + } + + var ( + b []byte + err error + ) + + b, err = create() + if err != nil { + return info, nil, err + } + + if c.cfg.MaxAge == 0 { + return info, b, nil + } + + if err := c.writeReader(id, bytes.NewReader(b)); err != nil { + return info, nil, err + } + + return info, b, nil +} + +// SetBytes sets the file content with the given id in the cache. +func (c *Cache) SetBytes(id string, data []byte) error { + if err := c.init(); err != nil { + return err + } + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + if c.cfg.MaxAge == 0 { + // No caching. + return nil + } + + return c.writeReader(id, bytes.NewReader(data)) +} + +// GetBytes gets the file content with the given id from the cache, nil if none found. +func (c *Cache) GetBytes(id string) ([]byte, error) { + _, b, err := c.GetItemBytes(id) + return b, err +} + +// GetItemBytes gets the ItemInfo and file content with the given id from the cache, nil if none found. +func (c *Cache) GetItemBytes(id string) (ItemInfo, []byte, error) { + if err := c.init(); err != nil { + return ItemInfo{}, nil, err + } + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info := ItemInfo{Name: id} + + if r := c.getOrRemove(id); r != nil { + defer r.Close() + b, err := io.ReadAll(r) + return info, b, err + } + + return info, nil, nil +} + +// Get gets the file with the given id from the cache, nil if none found. +func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) { + if err := c.init(); err != nil { + return ItemInfo{}, nil, err + } + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + info := ItemInfo{Name: id} + + r := c.getOrRemove(id) + + return info, r, nil +} + +// removeIfNeeded checks if the file with the given id should be re-created. +func (c *Cache) removeIfNeeded(id string) bool { + if c.cfg.MaxAge == 0 { + // No caching, remove. + c.remove(id) + return true + } + if removed, err := c.removeIfExpired(id); err != nil || removed { + return true + } + return false +} + +// getOrRemove gets the file with the given id. If it's expired, it will +// be removed. +func (c *Cache) getOrRemove(id string) hugio.ReadSeekCloser { + if c.cfg.MaxAge == 0 { + // No caching. + return nil + } + + if removed, err := c.removeIfExpired(id); err != nil || removed { + return nil + } + + f, err := c.Fs.Open(id) + if err != nil { + return nil + } + + return f +} + +func (c *Cache) getBytesAndRemoveIfExpired(id string) ([]byte, bool) { + if c.cfg.MaxAge == 0 { + // No caching. + return nil, false + } + + f, err := c.Fs.Open(id) + if err != nil { + return nil, false + } + defer f.Close() + + b, err := io.ReadAll(f) + if err != nil { + return nil, false + } + + removed, err := c.removeIfExpired(id) + if err != nil { + return nil, false + } + + return b, removed +} + +func (c *Cache) removeIfExpired(id string) (bool, error) { + if c.cfg.MaxAge <= 0 { + return false, nil + } + + fi, err := c.Fs.Stat(id) + if err != nil { + return false, err + } + + if c.isExpired(fi.ModTime()) { + c.remove(id) + return true, nil + } + + return false, nil +} + +func (c *Cache) remove(id string) { + if c.cfg.entryIsDir { + c.Fs.RemoveAll(id) + } else { + c.Fs.Remove(id) + } +} + +func (c *Cache) isExpired(modTime time.Time) bool { + if c.cfg.MaxAge < 0 { + return false + } + + // Note the use of time.Since here. + // We cannot use Hugo's global Clock for this. + return c.cfg.MaxAge == 0 || time.Since(modTime) > c.cfg.MaxAge +} + +// For testing +func (c *Cache) GetString(id string) string { + id = cleanID(id) + + c.entryLocker.Lock(id) + defer c.entryLocker.Unlock(id) + + f, err := c.Fs.Open(id) + if err != nil { + return "" + } + defer f.Close() + + b, _ := io.ReadAll(f) + return string(b) +} + +// Caches is a named set of caches. +type Caches map[string]*Cache + +func (f Caches) SetResourceFs(fs afero.Fs) { + for _, c := range f { + if c.cfg.IsResourceDir { + if c.isInited { + panic("cannot set resource fs after init") + } + c.Fs = hugofs.NewBasePathFs(fs, c.cfg.DirCompiled) + } + } +} + +// Get gets a named cache, nil if none found. +func (f Caches) Get(name string) *Cache { + return f[strings.ToLower(name)] +} + +// NewCaches creates a new set of file caches from the given +// configuration. +func NewCaches(dcfg Configs, sourceFs afero.Fs) (Caches, error) { + fs := sourceFs + + m := make(Caches) + for k, v := range dcfg { + var cfs afero.Fs + if v.IsResourceDir { + cfs = nil // Set later. TODO(bep) this needs to be cleanded up. + } else { + cfs = hugofs.NewBasePathFs(fs, v.DirCompiled) + } + + c := NewCache(cfs, v) + + m[k] = c + } + + return m, nil +} + +func cleanID(name string) string { + return strings.TrimPrefix(filepath.Clean(name), helpers.FilePathSeparator) +} + +// AsHTTPCache returns an httpcache.Cache implementation for this file cache. +// Note that none of the methods are protected by named locks, so you need to make sure +// to do that in your own code. +func (c *Cache) AsHTTPCache() httpcache.Cache { + return &httpCache{c: c} +} + +type httpCache struct { + c *Cache +} + +func (h *httpCache) Get(id string) (resp []byte, ok bool) { + id = cleanID(id) + b, removed := h.c.getBytesAndRemoveIfExpired(id) + + return b, !removed +} + +func (h *httpCache) Set(id string, resp []byte) { + if h.c.cfg.MaxAge == 0 { + return + } + + id = cleanID(id) + + if err := h.c.writeReader(id, bytes.NewReader(resp)); err != nil { + panic(err) + } +} + +func (h *httpCache) Delete(key string) { + h.c.Fs.Remove(key) +} diff --git a/cache/filecache/filecache_config.go b/cache/filecache/filecache_config.go new file mode 100644 index 0000000..feaa07d --- /dev/null +++ b/cache/filecache/filecache_config.go @@ -0,0 +1,317 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package filecache provides a file based cache for Hugo. +package filecache + +import ( + "encoding/json" + "errors" + "fmt" + "path" + "path/filepath" + "strings" + "time" + + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/config" + + "github.com/mitchellh/mapstructure" + "github.com/spf13/afero" +) + +const ( + resourcesGenDir = ":resourceDir/_gen" + cacheDirProject = ":cacheDir/:project" +) + +const ( + CacheKeyImages = "images" + CacheKeyAssets = "assets" + CacheKeyModules = "modules" + CacheKeyModuleQueries = "modulequeries" + CacheKeyModuleGitInfo = "modulegitinfo" + CacheKeyGetResource = "getresource" + CacheKeyMisc = "misc" +) + +type Configs map[string]FileCacheConfig + +// CacheDirModules returns the compiled path to the modules cache. +// For internal use. +func (c Configs) CacheDirModules() string { + return c[CacheKeyModules].DirCompiled +} + +// CacheDirMisc returns the compiled path to the misc cache. +// For internal use. +func (c Configs) CacheDirMisc() string { + return c[CacheKeyMisc].DirCompiled +} + +var defaultCacheConfigs = Configs{ + CacheKeyModules: { + MaxAge: -1, + Dir: ":cacheDir/modules", + fileCacheConfigInternal: fileCacheConfigInternal{ + entryIsDir: true, + isReadOnly: true, // we need to make it writable when pruning. + }, + }, + CacheKeyModuleQueries: { + MaxAge: 24 * time.Hour, + Dir: ":cacheDir/modules", + }, + CacheKeyModuleGitInfo: { + MaxAge: 24 * time.Hour, + Dir: ":cacheDir/modules", + fileCacheConfigInternal: fileCacheConfigInternal{ + entryIsDir: true, + }, + }, + CacheKeyImages: { + MaxAge: -1, + Dir: resourcesGenDir, + }, + CacheKeyAssets: { + MaxAge: -1, + Dir: resourcesGenDir, + }, + CacheKeyGetResource: { + MaxAge: -1, // Never expire + Dir: cacheDirProject, + }, + CacheKeyMisc: { + MaxAge: -1, + Dir: cacheDirProject, + }, +} + +func init() { + for k, v := range defaultCacheConfigs { + v.name = k + defaultCacheConfigs[k] = v + } +} + +type FileCacheConfig struct { + // Max age of cache entries in this cache. Any items older than this will + // be removed and not returned from the cache. + // A negative value means forever, 0 means cache is disabled. + // Hugo is lenient with what types it accepts here, but we recommend using + // a duration string, a sequence of decimal numbers, each with optional fraction and a unit suffix, + // such as "300ms", "1.5h" or "2h45m". + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + MaxAge time.Duration + + // The directory where files are stored. + Dir string + + fileCacheConfigInternal `json:"-"` +} + +func (cfg *FileCacheConfig) init() error { + if cfg.DirCompiled == "" { + // From unit tests. Just check that it does not contain any placeholders. + if strings.Contains(cfg.Dir, ":") { + return fmt.Errorf("cache dir %q contains unresolved placeholders", cfg.Dir) + } + cfg.DirCompiled = cfg.Dir + } + // Sanity check the config. + if len(cfg.DirCompiled) < 5 { + panic(fmt.Sprintf("invalid cache dir: %q", cfg.DirCompiled)) + } + return nil +} + +type fileCacheConfigInternal struct { + DirCompiled string + + name string // The name of this cache, e.g. "images", "modules" etc. + entryIsDir bool // when set, the cache entries represents directories directly below the base dir. + isReadOnly bool // when set, the cache is read only and needs to be pruned differently. This is used for the Go modules cache. + IsResourceDir bool // resources/_gen will get its own composite filesystem that also checks any theme. TODO(bep) unexport this. +} + +// MarshalJSON marshals FileCacheConfig to JSON with MaxAge as a human-readable string. +func (c FileCacheConfig) MarshalJSON() ([]byte, error) { + var maxAge any + if c.MaxAge == -1 { + maxAge = -1 + } else { + maxAge = strings.TrimSuffix(c.MaxAge.String(), "0m0s") + } + return json.Marshal(&struct { + MaxAge any `json:"maxAge"` + Dir string `json:"dir"` + }{ + MaxAge: maxAge, + Dir: c.Dir, + }) +} + +// ImageCache gets the file cache for processed images. +func (f Caches) ImageCache() *Cache { + return f[CacheKeyImages] +} + +// ModulesCache gets the file cache for Hugo Modules. +func (f Caches) ModulesCache() *Cache { + return f[CacheKeyModules] +} + +// ModuleQueriesCache gets the file cache for Hugo Module version queries. +// Returns nil if not found. +func (f Caches) ModuleQueriesCache() *Cache { + c, ok := f[CacheKeyModuleQueries] + if !ok { + panic("module queries cache not set") + } + return c +} + +// ModuleGitInfoCache gets the file cache for Hugo Module git info. +func (f Caches) ModuleGitInfoCache() *Cache { + c, ok := f[CacheKeyModuleGitInfo] + if !ok { + panic("module git info cache not set") + } + return c +} + +// AssetsCache gets the file cache for assets (processed resources, SCSS etc.). +func (f Caches) AssetsCache() *Cache { + return f[CacheKeyAssets] +} + +// MiscCache gets the file cache for miscellaneous stuff. +func (f Caches) MiscCache() *Cache { + return f[CacheKeyMisc] +} + +// GetResourceCache gets the file cache for remote resources. +func (f Caches) GetResourceCache() *Cache { + return f[CacheKeyGetResource] +} + +func DecodeConfig(fs afero.Fs, bcfg config.BaseConfig, m map[string]any) (Configs, error) { + c := make(Configs) + valid := make(map[string]bool) + // Add defaults + for k, v := range defaultCacheConfigs { + c[k] = v + valid[k] = true + } + + _, isOsFs := fs.(*afero.OsFs) + + for k, v := range m { + if _, ok := v.(hmaps.Params); !ok { + continue + } + var ok bool + cc, ok := c[k] + if !ok { + return nil, fmt.Errorf("%q is not a valid cache name", k) + } + + dc := &mapstructure.DecoderConfig{ + Result: &cc, + DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + WeaklyTypedInput: true, + } + + decoder, err := mapstructure.NewDecoder(dc) + if err != nil { + return c, err + } + + if err := decoder.Decode(v); err != nil { + return nil, fmt.Errorf("failed to decode filecache config: %w", err) + } + + if cc.Dir == "" { + return c, errors.New("must provide cache Dir") + } + + c[k] = cc + + } + + for k, v := range c { + dir := filepath.ToSlash(filepath.Clean(v.Dir)) + hadSlash := strings.HasPrefix(dir, "/") + parts := strings.Split(dir, "/") + + for i, part := range parts { + if strings.HasPrefix(part, ":") { + resolved, isResource, err := resolveDirPlaceholder(fs, bcfg, part) + if err != nil { + return c, err + } + if isResource { + v.IsResourceDir = true + } + parts[i] = resolved + } + } + + dir = path.Join(parts...) + if hadSlash { + dir = "/" + dir + } + v.DirCompiled = filepath.Clean(filepath.FromSlash(dir)) + + if !v.IsResourceDir { + if isOsFs && !filepath.IsAbs(v.DirCompiled) { + return c, fmt.Errorf("%q must resolve to an absolute directory", v.DirCompiled) + } + + // Avoid cache in root, e.g. / (Unix) or c:\ (Windows) + if len(strings.TrimPrefix(v.DirCompiled, filepath.VolumeName(v.DirCompiled))) == 1 { + return c, fmt.Errorf("%q is a root folder and not allowed as cache dir", v.DirCompiled) + } + } + + if !strings.HasPrefix(v.DirCompiled, "_gen") { + // We do cache eviction (file removes) and since the user can set + // his/hers own cache directory, we really want to make sure + // we do not delete any files that do not belong to this cache. + // We do add the cache name as the root, but this is an extra safe + // guard. We skip the files inside /resources/_gen/ because + // that would be breaking. + v.DirCompiled = filepath.Join(v.DirCompiled, FilecacheRootDirname, k) + } else { + v.DirCompiled = filepath.Join(v.DirCompiled, k) + } + + c[k] = v + } + + return c, nil +} + +// Resolves :resourceDir => /myproject/resources etc., :cacheDir => ... +func resolveDirPlaceholder(fs afero.Fs, bcfg config.BaseConfig, placeholder string) (cacheDir string, isResource bool, err error) { + switch strings.ToLower(placeholder) { + case ":resourcedir": + return "", true, nil + case ":cachedir": + return bcfg.CacheDir, false, nil + case ":project": + return filepath.Base(bcfg.WorkingDir), false, nil + } + + return "", false, fmt.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder) +} diff --git a/cache/filecache/filecache_config_test.go b/cache/filecache/filecache_config_test.go new file mode 100644 index 0000000..2215f13 --- /dev/null +++ b/cache/filecache/filecache_config_test.go @@ -0,0 +1,171 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache_test + +import ( + "encoding/json" + "path/filepath" + "runtime" + "testing" + "time" + + "github.com/spf13/afero" + + "github.com/gohugoio/hugo/cache/filecache" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/testconfig" + + qt "github.com/frankban/quicktest" +) + +func TestDecodeConfig(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + configStr := ` +resourceDir = "myresources" +contentDir = "content" +dataDir = "data" +i18nDir = "i18n" +layoutDir = "layouts" +assetDir = "assets" +archetypeDir = "archetypes" + +[caches] +[caches.misc] +maxAge = "10m" +dir = "/path/to/c1" +[caches.images] +dir = "/path/to/c3" +[caches.getResource] +dir = "/path/to/c4" +` + + cfg, err := config.FromConfigString(configStr, "toml") + c.Assert(err, qt.IsNil) + fs := afero.NewMemMapFs() + decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches + c.Assert(len(decoded), qt.Equals, 7) + + c2 := decoded["misc"] + c.Assert(c2.MaxAge.String(), qt.Equals, "10m0s") + c.Assert(c2.DirCompiled, qt.Equals, filepath.FromSlash("/path/to/c1/filecache/misc")) + + c3 := decoded["images"] + c.Assert(c3.MaxAge, qt.Equals, time.Duration(-1)) + c.Assert(c3.DirCompiled, qt.Equals, filepath.FromSlash("/path/to/c3/filecache/images")) + + c4 := decoded["getresource"] + c.Assert(c4.MaxAge, qt.Equals, time.Duration(-1)) + c.Assert(c4.DirCompiled, qt.Equals, filepath.FromSlash("/path/to/c4/filecache/getresource")) +} + +func TestDecodeConfigIgnoreCache(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + configStr := ` +resourceDir = "myresources" +contentDir = "content" +dataDir = "data" +i18nDir = "i18n" +layoutDir = "layouts" +assetDir = "assets" +archeTypedir = "archetypes" + +ignoreCache = true +[caches] +[caches.misc] +maxAge = 1234 +dir = "/path/to/c1" +[caches.images] +dir = "/path/to/c3" +[caches.getResource] +dir = "/path/to/c4" +` + + cfg, err := config.FromConfigString(configStr, "toml") + c.Assert(err, qt.IsNil) + fs := afero.NewMemMapFs() + decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches + c.Assert(len(decoded), qt.Equals, 7) + + for _, v := range decoded { + c.Assert(v.MaxAge, qt.Equals, time.Duration(0)) + } +} + +func TestDecodeConfigDefault(t *testing.T) { + c := qt.New(t) + cfg := config.New() + + if runtime.GOOS == "windows" { + cfg.Set("resourceDir", "c:\\cache\\resources") + cfg.Set("cacheDir", "c:\\cache\\thecache") + + } else { + cfg.Set("resourceDir", "/cache/resources") + cfg.Set("cacheDir", "/cache/thecache") + } + cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject")) + + fs := afero.NewMemMapFs() + decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches + c.Assert(len(decoded), qt.Equals, 7) + + imgConfig := decoded[filecache.CacheKeyImages] + miscConfig := decoded[filecache.CacheKeyMisc] + + if runtime.GOOS == "windows" { + c.Assert(imgConfig.DirCompiled, qt.Equals, filepath.FromSlash("_gen/images")) + } else { + c.Assert(imgConfig.DirCompiled, qt.Equals, "_gen/images") + c.Assert(miscConfig.DirCompiled, qt.Equals, "/cache/thecache/hugoproject/filecache/misc") + } + + c.Assert(imgConfig.IsResourceDir, qt.Equals, true) + c.Assert(miscConfig.IsResourceDir, qt.Equals, false) +} + +func TestFileCacheConfigMarshalJSON(t *testing.T) { + c := qt.New(t) + + cfg := config.New() + cfg.Set("cacheDir", "/cache") + cfg.Set("workingDir", "/my/project") + + fs := afero.NewMemMapFs() + decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches + + moduleQueriesConfig := decoded[filecache.CacheKeyModuleQueries] + c.Assert(moduleQueriesConfig.MaxAge, qt.Equals, 24*time.Hour) + + // Also verify the new moduleGitInfo cache. + moduleGitInfoConfig := decoded[filecache.CacheKeyModuleGitInfo] + c.Assert(moduleGitInfoConfig.MaxAge, qt.Equals, 24*time.Hour) + + b, err := json.Marshal(moduleQueriesConfig) + c.Assert(err, qt.IsNil) + + c.Assert(string(b), qt.Contains, `"maxAge":"24h"`) + c.Assert(string(b), qt.Not(qt.Contains), "86400000000000") + c.Assert(string(b), qt.Not(qt.Contains), "8.64e") + + moduleQueriesConfig.MaxAge = -1 + b, err = json.Marshal(moduleQueriesConfig) + c.Assert(err, qt.IsNil) + c.Assert(string(b), qt.Contains, `"maxAge":-1`) +} diff --git a/cache/filecache/filecache_integration_test.go b/cache/filecache/filecache_integration_test.go new file mode 100644 index 0000000..88873c7 --- /dev/null +++ b/cache/filecache/filecache_integration_test.go @@ -0,0 +1,105 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache_test + +import ( + "path/filepath" + "testing" + "time" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/htesting" + "github.com/gohugoio/hugo/hugolib" +) + +// See issue #10781. That issue wouldn't have been triggered if we kept +// the empty root directories (e.g. _resources/gen/images). +// It's still an upstream Go issue that we also need to handle, but +// this is a test for the first part. +func TestPruneShouldPreserveEmptyCacheRoots(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +-- content/_index.md -- +--- +title: "Home" +--- + +` + + b := hugolib.Test(t, files, hugolib.TestOptOsFs(), hugolib.TestOptWithConfig(func(c *hugolib.IntegrationTestConfig) { + c.RunGC = true + })) + + _, err := b.H.BaseFs.ResourcesCache.Stat(filepath.Join("_gen", "images")) + + b.Assert(err, qt.IsNil) +} + +func TestPruneImages(t *testing.T) { + if htesting.IsCI() { + // TODO(bep) + t.Skip("skip flaky test on CI server") + } + t.Skip("skip flaky test") + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +[caches] +[caches.images] +maxAge = "200ms" +dir = ":resourceDir/_gen" +-- content/_index.md -- +--- +title: "Home" +--- +-- assets/a/pixel.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== +-- layouts/home.html -- +{{ warnf "HOME!" }} +{{ $img := resources.GetMatch "**.png" }} +{{ $img = $img.Resize "3x3" }} +{{ $img.RelPermalink }} + + + +` + + b := hugolib.TestRunning(t, files, hugolib.TestOptOsFs(), hugolib.TestOptInfo(), hugolib.TestOptWithConfig(func(c *hugolib.IntegrationTestConfig) { + c.RunGC = true + })) + + b.Assert(b.GCCount, qt.Equals, 0) + b.Assert(b.H, qt.IsNotNil) + + imagesCacheDir := filepath.Join("_gen", "images") + _, err := b.H.BaseFs.ResourcesCache.Stat(imagesCacheDir) + + b.Assert(err, qt.IsNil) + + // TODO(bep) we need a way to test full rebuilds. + // For now, just sleep a little so the cache elements expires. + time.Sleep(500 * time.Millisecond) + + b.RenameFile("assets/a/pixel.png", "assets/b/pixel2.png").Build() + + b.Assert(b.GCCount, qt.Equals, 1) + // Build it again to GC the empty a dir. + b.Build() + + _, err = b.H.BaseFs.ResourcesCache.Stat(filepath.Join(imagesCacheDir, "a")) + b.Assert(err, qt.Not(qt.IsNil)) + _, err = b.H.BaseFs.ResourcesCache.Stat(imagesCacheDir) + b.Assert(err, qt.IsNil) +} diff --git a/cache/filecache/filecache_pruner.go b/cache/filecache/filecache_pruner.go new file mode 100644 index 0000000..d6322cb --- /dev/null +++ b/cache/filecache/filecache_pruner.go @@ -0,0 +1,172 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache + +import ( + "fmt" + "io" + "os" + + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/hugofs" + + "github.com/spf13/afero" +) + +// Prune removes expired and unused items from this cache. +// The last one requires a full build so the cache usage can be tracked. +// Note that we operate directly on the filesystem here, so this is not +// thread safe. +func (c Caches) Prune() (int, error) { + counter := 0 + for k, cache := range c { + count, err := cache.Prune(false) + + counter += count + + if err != nil { + if herrors.IsNotExist(err) { + continue + } + return counter, fmt.Errorf("failed to prune cache %q: %w", k, err) + } + + } + + return counter, nil +} + +// Prune removes expired and unused items from this cache. +// If force is set, everything will be removed not considering expiry time. +func (c *Cache) Prune(force bool) (int, error) { + if c.cfg.entryIsDir { + return c.pruneRootDirs(force) + } + if err := c.init(); err != nil { + return 0, err + } + + counter := 0 + + err := afero.Walk(c.Fs, "", func(name string, info os.FileInfo, err error) error { + if info == nil { + return nil + } + + name = cleanID(name) + + if info.IsDir() { + f, err := c.Fs.Open(name) + if err != nil { + // This cache dir may not exist. + return nil + } + _, err = f.Readdirnames(1) + f.Close() + if err == io.EOF { + // Empty dir. + if name == "." { + // e.g. /_gen/images -- keep it even if empty. + err = nil + } else { + err = c.Fs.Remove(name) + } + } + + if err != nil && !herrors.IsNotExist(err) { + return err + } + + return nil + } + + shouldRemove := force || c.isExpired(info.ModTime()) + + if !shouldRemove && c.entryLocker.seen.Len() > 0 { + // Remove it if it's not been touched/used in the last build. + shouldRemove = !c.entryLocker.seen.Has(name) + } + + if shouldRemove { + err := c.Fs.Remove(name) + if err == nil { + counter++ + } + + if err != nil && !herrors.IsNotExist(err) { + return err + } + + } + + return nil + }) + + return counter, err +} + +func (c *Cache) pruneRootDirs(force bool) (int, error) { + dirs, err := afero.ReadDir(c.Fs, "") + if err != nil { + if herrors.IsNotExist(err) { + return 0, nil + } + return 0, err + } + + counter := 0 + + for _, dir := range dirs { + if !dir.IsDir() { + continue + } + + count, err := c.pruneRootDir(dir.Name(), force) + if err != nil { + return counter, err + } + counter += count + } + + return counter, nil +} + +func (c *Cache) pruneRootDir(dirname string, force bool) (int, error) { + if err := c.init(); err != nil { + return 0, err + } + + // Sanity check. + if dirname != "pkg" && len(dirname) < 5 { + panic(fmt.Sprintf("invalid cache dir name: %q", dirname)) + } + + info, err := c.Fs.Stat(dirname) + if err != nil { + if herrors.IsNotExist(err) { + return 0, nil + } + return 0, err + } + + if !force && !c.isExpired(info.ModTime()) { + return 0, nil + } + + if c.cfg.isReadOnly { + return hugofs.MakeReadableAndRemoveAllModulePkgDir(c.Fs, dirname) + } + + return 1, c.Fs.RemoveAll(dirname) +} diff --git a/cache/filecache/filecache_pruner_test.go b/cache/filecache/filecache_pruner_test.go new file mode 100644 index 0000000..117ff34 --- /dev/null +++ b/cache/filecache/filecache_pruner_test.go @@ -0,0 +1,115 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache_test + +import ( + "fmt" + "testing" + "testing/synctest" + "time" + + "github.com/gohugoio/hugo/cache/filecache" + "github.com/spf13/afero" + + qt "github.com/frankban/quicktest" +) + +func TestPrune(t *testing.T) { + t.Parallel() + + synctest.Test(t, func(t *testing.T) { + c := qt.New(t) + + configStr := ` +resourceDir = "myresources" +contentDir = "content" +dataDir = "data" +i18nDir = "i18n" +layoutDir = "layouts" +assetDir = "assets" +archeTypedir = "archetypes" + +[caches] +[caches.misc] +maxAge = "200ms" +dir = "/cache/c" +[caches.assets] +maxAge = "200ms" +dir = ":resourceDir/_gen" +[caches.images] +maxAge = "200ms" +dir = ":resourceDir/_gen" +` + + for _, name := range []string{filecache.CacheKeyAssets, filecache.CacheKeyImages} { + msg := qt.Commentf("cache: %s", name) + fs := afero.NewMemMapFs() + p := newPathsSpec(t, fs, configStr) + fileCachConfig := p.Cfg.GetConfigSection("caches").(filecache.Configs) + caches, err := filecache.NewCaches(fileCachConfig, fs) + c.Assert(err, qt.IsNil) + caches.SetResourceFs(fs) + cache := caches[name] + for i := range 10 { + id := fmt.Sprintf("i%d", i) + cache.GetOrCreateBytes(id, func() ([]byte, error) { + return []byte("abc"), nil + }) + if i == 4 { + // This will expire the first 5 + time.Sleep(201 * time.Millisecond) + } + } + + count, err := caches.Prune() + c.Assert(err, qt.IsNil) + c.Assert(count, qt.Equals, 5, msg) + + for i := range 10 { + id := fmt.Sprintf("i%d", i) + v := cache.GetString(id) + if i < 5 { + c.Assert(v, qt.Equals, "") + } else { + c.Assert(v, qt.Equals, "abc") + } + } + + caches, err = filecache.NewCaches(fileCachConfig, fs) + c.Assert(err, qt.IsNil) + caches.SetResourceFs(fs) + cache = caches[name] + // Touch one and then prune. + cache.GetOrCreateBytes("i5", func() ([]byte, error) { + return []byte("abc"), nil + }) + + count, err = caches.Prune() + c.Assert(err, qt.IsNil) + c.Assert(count, qt.Equals, 4) + + // Now only the i5 should be left. + for i := range 10 { + id := fmt.Sprintf("i%d", i) + v := cache.GetString(id) + if i != 5 { + c.Assert(v, qt.Equals, "") + } else { + c.Assert(v, qt.Equals, "abc") + } + } + + } + }) +} diff --git a/cache/filecache/filecache_test.go b/cache/filecache/filecache_test.go new file mode 100644 index 0000000..5beb405 --- /dev/null +++ b/cache/filecache/filecache_test.go @@ -0,0 +1,285 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filecache_test + +import ( + "errors" + "fmt" + "io" + "strings" + "sync" + "testing" + "time" + + "github.com/gohugoio/hugo/htesting" + + "github.com/gohugoio/hugo/cache/filecache" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/testconfig" + "github.com/gohugoio/hugo/helpers" + + "github.com/gohugoio/hugo/hugofs" + "github.com/spf13/afero" + + qt "github.com/frankban/quicktest" +) + +func TestFileCache(t *testing.T) { + t.Parallel() + c := qt.New(t) + + tempWorkingDir := t.TempDir() + tempCacheDir := t.TempDir() + + osfs := afero.NewOsFs() + + for _, test := range []struct { + cacheDir string + workingDir string + }{ + // Run with same dirs twice to make sure that works. + {tempCacheDir, tempWorkingDir}, + {tempCacheDir, tempWorkingDir}, + } { + + configStr := ` +workingDir = "WORKING_DIR" +resourceDir = "resources" +cacheDir = "CACHEDIR" +contentDir = "content" +dataDir = "data" +i18nDir = "i18n" +layoutDir = "layouts" +assetDir = "assets" +archeTypedir = "archetypes" + +[caches] +[caches.misc] +maxAge = "10h" +dir = ":cacheDir/c" + +` + + winPathSep := "\\\\" + + replacer := strings.NewReplacer("CACHEDIR", test.cacheDir, "WORKING_DIR", test.workingDir) + + configStr = replacer.Replace(configStr) + configStr = strings.Replace(configStr, "\\", winPathSep, -1) + + p := newPathsSpec(t, osfs, configStr) + fileCachConfig := p.Cfg.GetConfigSection("caches").(filecache.Configs) + + caches, err := filecache.NewCaches(fileCachConfig, p.Fs.Source) + c.Assert(err, qt.IsNil) + caches.SetResourceFs(p.SourceFs) + + cache := caches.Get("Misc") + c.Assert(cache, qt.Not(qt.IsNil)) + + cache = caches.Get("Images") + c.Assert(cache, qt.Not(qt.IsNil)) + + rf := func(s string) func() (io.ReadCloser, error) { + return func() (io.ReadCloser, error) { + return struct { + io.ReadSeeker + io.Closer + }{ + strings.NewReader(s), + io.NopCloser(nil), + }, nil + } + } + + bf := func() ([]byte, error) { + return []byte("bcd"), nil + } + + for _, ca := range []*filecache.Cache{caches.ImageCache(), caches.AssetsCache()} { + for range 2 { + info, r, err := ca.GetOrCreate("a", rf("abc")) + c.Assert(err, qt.IsNil) + c.Assert(r, qt.Not(qt.IsNil)) + c.Assert(info.Name, qt.Equals, "a") + b, _ := io.ReadAll(r) + r.Close() + c.Assert(string(b), qt.Equals, "abc") + + info, b, err = ca.GetOrCreateBytes("b", bf) + c.Assert(err, qt.IsNil) + c.Assert(r, qt.Not(qt.IsNil)) + c.Assert(info.Name, qt.Equals, "b") + c.Assert(string(b), qt.Equals, "bcd") + + _, b, err = ca.GetOrCreateBytes("a", bf) + c.Assert(err, qt.IsNil) + c.Assert(string(b), qt.Equals, "abc") + + _, r, err = ca.GetOrCreate("a", rf("bcd")) + c.Assert(err, qt.IsNil) + b, _ = io.ReadAll(r) + r.Close() + c.Assert(string(b), qt.Equals, "abc") + } + } + + info, w, err := caches.ImageCache().WriteCloser("mykey") + c.Assert(err, qt.IsNil) + c.Assert(info.Name, qt.Equals, "mykey") + io.WriteString(w, "Hugo is great!") + w.Close() + c.Assert(caches.ImageCache().GetString("mykey"), qt.Equals, "Hugo is great!") + + info, r, err := caches.ImageCache().Get("mykey") + c.Assert(err, qt.IsNil) + c.Assert(r, qt.Not(qt.IsNil)) + c.Assert(info.Name, qt.Equals, "mykey") + b, _ := io.ReadAll(r) + r.Close() + c.Assert(string(b), qt.Equals, "Hugo is great!") + + info, b, err = caches.ImageCache().GetItemBytes("mykey") + c.Assert(err, qt.IsNil) + c.Assert(info.Name, qt.Equals, "mykey") + c.Assert(string(b), qt.Equals, "Hugo is great!") + + } +} + +func TestFileCacheConcurrent(t *testing.T) { + htesting.SkipSlowTestUnlessCI(t) + t.Parallel() + + c := qt.New(t) + + configStr := ` +resourceDir = "myresources" +contentDir = "content" +dataDir = "data" +i18nDir = "i18n" +layoutDir = "layouts" +assetDir = "assets" +archeTypedir = "archetypes" + +[caches] +[caches.misc] +maxAge = "1s" +dir = "/cache/c" + +` + + p := newPathsSpec(t, afero.NewMemMapFs(), configStr) + fileCachConfig := p.Cfg.GetConfigSection("caches").(filecache.Configs) + caches, err := filecache.NewCaches(fileCachConfig, p.Fs.Source) + c.Assert(err, qt.IsNil) + caches.SetResourceFs(p.Fs.Source) + + const cacheName = "misc" + + filenameData := func(i int) (string, string) { + data := fmt.Sprintf("data: %d", i) + filename := fmt.Sprintf("file%d", i) + return filename, data + } + + var wg sync.WaitGroup + + for i := range 50 { + wg.Add(1) + go func(i int) { + defer wg.Done() + for range 20 { + ca := caches.Get(cacheName) + c.Assert(ca, qt.Not(qt.IsNil)) + filename, data := filenameData(i) + _, r, err := ca.GetOrCreate(filename, func() (io.ReadCloser, error) { + return hugio.ToReadCloser(strings.NewReader(data)), nil + }) + c.Assert(err, qt.IsNil) + b, _ := io.ReadAll(r) + r.Close() + c.Assert(string(b), qt.Equals, data) + // Trigger some expiration. + time.Sleep(50 * time.Millisecond) + } + }(i) + + } + wg.Wait() +} + +func TestFileCacheReadOrCreateErrorInRead(t *testing.T) { + t.Parallel() + c := qt.New(t) + + var result string + + rf := func(failLevel int) func(info filecache.ItemInfo, r io.ReadSeeker) error { + return func(info filecache.ItemInfo, r io.ReadSeeker) error { + if failLevel > 0 { + if failLevel > 1 { + return filecache.ErrFatal + } + return errors.New("fail") + } + + b, _ := io.ReadAll(r) + result = string(b) + + return nil + } + } + + bf := func(s string) func(info filecache.ItemInfo, w io.WriteCloser) error { + return func(info filecache.ItemInfo, w io.WriteCloser) error { + defer w.Close() + result = s + _, err := w.Write([]byte(s)) + return err + } + } + + cfg := filecache.FileCacheConfig{ + MaxAge: 100 * time.Hour, + Dir: "cache/c", + } + + cache := filecache.NewCache(afero.NewMemMapFs(), cfg) + + const id = "a32" + + _, err := cache.ReadOrCreate(id, rf(0), bf("v1")) + c.Assert(err, qt.IsNil) + c.Assert(result, qt.Equals, "v1") + _, err = cache.ReadOrCreate(id, rf(0), bf("v2")) + c.Assert(err, qt.IsNil) + c.Assert(result, qt.Equals, "v1") + _, err = cache.ReadOrCreate(id, rf(1), bf("v3")) + c.Assert(err, qt.IsNil) + c.Assert(result, qt.Equals, "v3") + _, err = cache.ReadOrCreate(id, rf(2), bf("v3")) + c.Assert(err, qt.Equals, filecache.ErrFatal) +} + +func newPathsSpec(t *testing.T, fs afero.Fs, configStr string) *helpers.PathSpec { + c := qt.New(t) + cfg, err := config.FromConfigString(configStr, "toml") + c.Assert(err, qt.IsNil) + acfg := testconfig.GetTestConfig(fs, cfg) + p, err := helpers.NewPathSpec(hugofs.NewFrom(fs, acfg.BaseConfig()), acfg, nil, nil) + c.Assert(err, qt.IsNil) + return p +} diff --git a/cache/httpcache/httpcache.go b/cache/httpcache/httpcache.go new file mode 100644 index 0000000..44f243b --- /dev/null +++ b/cache/httpcache/httpcache.go @@ -0,0 +1,240 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpcache + +import ( + "encoding/json" + "time" + + "github.com/gobwas/glob" + "github.com/gohugoio/hugo/common/predicate" + "github.com/gohugoio/hugo/config" + "github.com/mitchellh/mapstructure" +) + +// DefaultConfig holds the default configuration for the HTTP cache. +var DefaultConfig = Config{ + RespectCacheControlNoStoreInRequest: true, + RespectCacheControlNoStoreInResponse: false, + Cache: Cache{ + For: GlobMatcher{ + Excludes: []string{"**"}, + }, + }, + Polls: []PollConfig{ + { + For: GlobMatcher{ + Includes: []string{"**"}, + }, + Disable: true, + }, + }, +} + +// Config holds the configuration for the HTTP cache. +type Config struct { + // When enabled and there's a Cache-Control: no-store directive in the request, response will never be stored in disk cache. + RespectCacheControlNoStoreInRequest bool + + // When enabled and there's a Cache-Control: no-store directive in the response, response will never be stored in disk cache. + RespectCacheControlNoStoreInResponse bool + + // Enables HTTP cache behavior (RFC 9111) for these resources. + // When this is not enabled for a resource, Hugo will go straight to the file cache. + Cache Cache + + // Polls holds a list of configurations for polling remote resources to detect changes in watch mode. + // This can be disabled for some resources, typically if they are known to not change. + Polls []PollConfig +} + +type Cache struct { + // Enable HTTP cache behavior (RFC 9111) for these resources. + For GlobMatcher +} + +func (c *Config) Compile() (ConfigCompiled, error) { + cc := ConfigCompiled{ + Base: *c, + } + + p, err := c.Cache.For.CompilePredicate() + if err != nil { + return cc, err + } + + cc.For = p + + for _, pc := range c.Polls { + + p, err := pc.For.CompilePredicate() + if err != nil { + return cc, err + } + + cc.PollConfigs = append(cc.PollConfigs, PollConfigCompiled{ + For: p, + Config: pc, + }) + } + + return cc, nil +} + +// PollConfig holds the configuration for polling remote resources to detect changes in watch mode. +type PollConfig struct { + // What remote resources to apply this configuration to. + For GlobMatcher + + // Disable polling for this configuration. + Disable bool + + // Low is the lower bound for the polling interval. + // This is the starting point when the resource has recently changed, + // if that resource stops changing, the polling interval will gradually increase towards High. + Low time.Duration + + // High is the upper bound for the polling interval. + // This is the interval used when the resource is stable. + High time.Duration +} + +func (c PollConfig) MarshalJSON() (b []byte, err error) { + // Marshal the durations as strings. + type Alias PollConfig + return json.Marshal(&struct { + Low string + High string + Alias + }{ + Low: c.Low.String(), + High: c.High.String(), + Alias: (Alias)(c), + }) +} + +type GlobMatcher struct { + // Excludes holds a list of glob patterns that will be excluded. + Excludes []string + + // Includes holds a list of glob patterns that will be included. + Includes []string +} + +func (gm GlobMatcher) IsZero() bool { + return len(gm.Includes) == 0 && len(gm.Excludes) == 0 +} + +type ConfigCompiled struct { + Base Config + For predicate.P[string] + PollConfigs []PollConfigCompiled +} + +func (c *ConfigCompiled) PollConfigFor(s string) PollConfigCompiled { + for _, pc := range c.PollConfigs { + if pc.For(s) { + return pc + } + } + return PollConfigCompiled{} +} + +func (c *ConfigCompiled) IsPollingDisabled() bool { + for _, pc := range c.PollConfigs { + if !pc.Config.Disable { + return false + } + } + return true +} + +type PollConfigCompiled struct { + For predicate.P[string] + Config PollConfig +} + +func (p PollConfigCompiled) IsZero() bool { + return p.For == nil +} + +func (gm *GlobMatcher) CompilePredicate() (func(string) bool, error) { + if gm.IsZero() { + panic("no includes or excludes") + } + var b predicate.PR[string] + for _, include := range gm.Includes { + g, err := glob.Compile(include, '/') + if err != nil { + return nil, err + } + fn := func(s string) predicate.Match { + return predicate.BoolMatch(g.Match(s)) + } + b = b.Or(fn) + } + + for _, exclude := range gm.Excludes { + g, err := glob.Compile(exclude, '/') + if err != nil { + return nil, err + } + fn := func(s string) predicate.Match { + return predicate.BoolMatch(!g.Match(s)) + } + b = b.And(fn) + } + + return b.BoolFunc(), nil +} + +func DecodeConfig(_ config.BaseConfig, m map[string]any) (Config, error) { + if len(m) == 0 { + return DefaultConfig, nil + } + + var c Config + + dc := &mapstructure.DecoderConfig{ + Result: &c, + DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + WeaklyTypedInput: true, + } + + decoder, err := mapstructure.NewDecoder(dc) + if err != nil { + return c, err + } + + if err := decoder.Decode(m); err != nil { + return c, err + } + + if c.Cache.For.IsZero() { + c.Cache.For = DefaultConfig.Cache.For + } + + for pci := range c.Polls { + if c.Polls[pci].For.IsZero() { + c.Polls[pci].For = DefaultConfig.Cache.For + c.Polls[pci].Disable = true + } + } + + if len(c.Polls) == 0 { + c.Polls = DefaultConfig.Polls + } + + return c, nil +} diff --git a/cache/httpcache/httpcache_integration_test.go b/cache/httpcache/httpcache_integration_test.go new file mode 100644 index 0000000..4d6a5f7 --- /dev/null +++ b/cache/httpcache/httpcache_integration_test.go @@ -0,0 +1,95 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpcache_test + +import ( + "testing" + "time" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/hugolib" +) + +func TestConfigCustom(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[httpcache] +[httpcache.cache.for] +includes = ["**gohugo.io**"] +[[httpcache.polls]] +low = "5s" +high = "32s" +[httpcache.polls.for] +includes = ["**gohugo.io**"] + + +` + + b := hugolib.Test(t, files) + + httpcacheConf := b.H.Configs.Base.HTTPCache + compiled := b.H.Configs.Base.C.HTTPCache + + b.Assert(httpcacheConf.Cache.For.Includes, qt.DeepEquals, []string{"**gohugo.io**"}) + b.Assert(httpcacheConf.Cache.For.Excludes, qt.IsNil) + + pc := compiled.PollConfigFor("https://gohugo.io/foo.jpg") + b.Assert(pc.Config.Low, qt.Equals, 5*time.Second) + b.Assert(pc.Config.High, qt.Equals, 32*time.Second) + b.Assert(compiled.PollConfigFor("https://example.com/foo.jpg").IsZero(), qt.IsTrue) +} + +func TestConfigDefault(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +` + b := hugolib.Test(t, files) + + compiled := b.H.Configs.Base.C.HTTPCache + + b.Assert(compiled.For("https://gohugo.io/posts.json"), qt.IsFalse) + b.Assert(compiled.For("https://gohugo.io/foo.jpg"), qt.IsFalse) + b.Assert(compiled.PollConfigFor("https://gohugo.io/foo.jpg").Config.Disable, qt.IsTrue) +} + +func TestConfigPollsOnly(t *testing.T) { + t.Parallel() + files := ` +-- hugo.toml -- +[httpcache] +[[httpcache.polls]] +low = "5s" +high = "32s" +[httpcache.polls.for] +includes = ["**gohugo.io**"] + + +` + + b := hugolib.Test(t, files) + + compiled := b.H.Configs.Base.C.HTTPCache + + b.Assert(compiled.For("https://gohugo.io/posts.json"), qt.IsFalse) + b.Assert(compiled.For("https://gohugo.io/foo.jpg"), qt.IsFalse) + + pc := compiled.PollConfigFor("https://gohugo.io/foo.jpg") + b.Assert(pc.Config.Low, qt.Equals, 5*time.Second) + b.Assert(pc.Config.High, qt.Equals, 32*time.Second) + b.Assert(compiled.PollConfigFor("https://example.com/foo.jpg").IsZero(), qt.IsTrue) +} diff --git a/cache/httpcache/httpcache_test.go b/cache/httpcache/httpcache_test.go new file mode 100644 index 0000000..737dff2 --- /dev/null +++ b/cache/httpcache/httpcache_test.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpcache + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" +) + +func TestGlobMatcher(t *testing.T) { + c := qt.New(t) + + g := GlobMatcher{ + Includes: []string{"**/*.jpg", "**.png", "**/bar/**"}, + Excludes: []string{"**/foo.jpg", "**.css"}, + } + + p, err := g.CompilePredicate() + c.Assert(err, qt.IsNil) + + c.Assert(p("foo.jpg"), qt.IsFalse) + c.Assert(p("foo.png"), qt.IsTrue) + c.Assert(p("foo/bar.jpg"), qt.IsTrue) + c.Assert(p("foo/bar.png"), qt.IsTrue) + c.Assert(p("foo/bar/foo.jpg"), qt.IsFalse) + c.Assert(p("foo/bar/foo.css"), qt.IsFalse) + c.Assert(p("foo.css"), qt.IsFalse) + c.Assert(p("foo/bar/foo.css"), qt.IsFalse) + c.Assert(p("foo/bar/foo.xml"), qt.IsTrue) +} + +func TestDefaultConfig(t *testing.T) { + c := qt.New(t) + + _, err := DefaultConfig.Compile() + c.Assert(err, qt.IsNil) +} + +func TestDecodeConfigInjectsDefaultAndCompiles(t *testing.T) { + c := qt.New(t) + + cfg, err := DecodeConfig(config.BaseConfig{}, map[string]any{}) + c.Assert(err, qt.IsNil) + c.Assert(cfg, qt.DeepEquals, DefaultConfig) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) + + cfg, err = DecodeConfig(config.BaseConfig{}, map[string]any{ + "cache": map[string]any{ + "polls": []map[string]any{ + {"disable": true}, + }, + }, + }) + c.Assert(err, qt.IsNil) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) +} diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..20aacb1 --- /dev/null +++ b/check.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +set -e + +# Default to all packages if none specified +PACKAGES="${1:-./...}" + +echo "==> Checking packages: $PACKAGES" + +# Timing arrays +declare -a STEP_NAMES +declare -a STEP_TIMES + +time_step() { + local name="$1" + shift + local start=$(date +%s.%N) + "$@" + local end=$(date +%s.%N) + local elapsed=$(echo "$end - $start" | bc) + STEP_NAMES+=("$name") + STEP_TIMES+=("$elapsed") +} + +# Check gofmt +run_gofmt() { + echo "==> Running gofmt..." + # Convert package pattern to path (e.g., ./hugolib/... -> ./hugolib) + local path="${PACKAGES%/...}" + GOFMT_OUTPUT=$(gofmt -l "$path" 2>&1) || true + if [ -n "$GOFMT_OUTPUT" ]; then + echo "gofmt found issues in:" + echo "$GOFMT_OUTPUT" + exit 1 + fi + echo " OK" +} + +# Run staticcheck +run_staticcheck() { + # Check if staticcheck is installed, install if not + if ! command -v staticcheck &> /dev/null; then + echo "==> Installing staticcheck..." + go install honnef.co/go/tools/cmd/staticcheck@latest + fi + echo "==> Running staticcheck..." + staticcheck $PACKAGES + echo " OK" +} + +# Run tests +run_tests() { + echo "==> Running tests..." + local output + if ! output=$(go test -failfast $PACKAGES 2>&1); then + echo "$output" + exit 1 + fi + echo " OK" +} + +# Run all steps with timing +TOTAL_START=$(date +%s.%N) + +time_step "gofmt" run_gofmt +time_step "staticcheck" run_staticcheck +time_step "tests" run_tests + +TOTAL_END=$(date +%s.%N) +TOTAL_ELAPSED=$(echo "$TOTAL_END - $TOTAL_START" | bc) + +# Print timing summary +echo "" +echo "==> All checks passed!" +echo "" +echo "Timing summary:" +echo "---------------" +for i in "${!STEP_NAMES[@]}"; do + printf " %-15s %6.2fs\n" "${STEP_NAMES[$i]}" "${STEP_TIMES[$i]}" +done +echo "---------------" +printf " %-15s %6.2fs\n" "Total" "$TOTAL_ELAPSED" diff --git a/check_gofmt.sh b/check_gofmt.sh new file mode 100755 index 0000000..c77517d --- /dev/null +++ b/check_gofmt.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +diff <(gofmt -d .) <(printf '') \ No newline at end of file diff --git a/codegen/methods.go b/codegen/methods.go new file mode 100644 index 0000000..cfd425d --- /dev/null +++ b/codegen/methods.go @@ -0,0 +1,538 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// Some functions in this file (see comments) is based on the Go source code, +// copyright The Go Authors and governed by a BSD-style license. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package codegen contains helpers for code generation. +package codegen + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "path/filepath" + "reflect" + "regexp" + "slices" + "sort" + "strings" + "sync" +) + +// Make room for insertions +const weightWidth = 1000 + +// NewInspector creates a new Inspector given a source root. +func NewInspector(root string) *Inspector { + return &Inspector{ProjectRootDir: root} +} + +// Inspector provides methods to help code generation. It uses a combination +// of reflection and source code AST to do the heavy lifting. +type Inspector struct { + ProjectRootDir string + + init sync.Once + + // Determines method order. Go's reflect sorts lexicographically, so + // we must parse the source to preserve this order. + methodWeight map[string]map[string]int +} + +// MethodsFromTypes create a method set from the include slice, excluding any +// method in exclude. +func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.Type) Methods { + c.parseSource() + + var methods Methods + + excludes := make(map[string]bool) + + if len(exclude) > 0 { + for _, m := range c.MethodsFromTypes(exclude, nil) { + excludes[m.Name] = true + } + } + + // There may be overlapping interfaces in types. Do a simple check for now. + seen := make(map[string]bool) + + nameAndPackage := func(t reflect.Type) (string, string) { + var name, pkg string + + isPointer := t.Kind() == reflect.Pointer + + if isPointer { + t = t.Elem() + } + + pkgPrefix := "" + if pkgPath := t.PkgPath(); pkgPath != "" { + pkgPath = strings.TrimSuffix(pkgPath, "/") + _, shortPath := path.Split(pkgPath) + pkgPrefix = shortPath + "." + pkg = pkgPath + } + + name = t.Name() + if name == "" { + // interface{} + name = t.String() + } + + if isPointer { + pkgPrefix = "*" + pkgPrefix + } + + name = pkgPrefix + name + + return name, pkg + } + + for _, t := range include { + for m := range t.Methods() { + if excludes[m.Name] || seen[m.Name] { + continue + } + + seen[m.Name] = true + + if m.PkgPath != "" { + // Not exported + continue + } + + numIn := m.Type.NumIn() + + ownerName, _ := nameAndPackage(t) + + method := Method{Owner: t, OwnerName: ownerName, Name: m.Name} + + for i := range numIn { + in := m.Type.In(i) + + name, pkg := nameAndPackage(in) + + if pkg != "" { + method.Imports = append(method.Imports, pkg) + } + + method.In = append(method.In, name) + } + + numOut := m.Type.NumOut() + + if numOut > 0 { + for i := range numOut { + out := m.Type.Out(i) + name, pkg := nameAndPackage(out) + + if pkg != "" { + method.Imports = append(method.Imports, pkg) + } + + method.Out = append(method.Out, name) + } + } + + methods = append(methods, method) + } + } + + sort.SliceStable(methods, func(i, j int) bool { + mi, mj := methods[i], methods[j] + + wi := c.methodWeight[mi.OwnerName][mi.Name] + wj := c.methodWeight[mj.OwnerName][mj.Name] + + if wi == wj { + return mi.Name < mj.Name + } + + return wi < wj + }) + + return methods +} + +func (c *Inspector) parseSource() { + c.init.Do(func() { + if !strings.Contains(c.ProjectRootDir, "hugo") { + panic("dir must be set to the Hugo root") + } + + c.methodWeight = make(map[string]map[string]int) + dirExcludes := regexp.MustCompile("docs|examples") + fileExcludes := regexp.MustCompile("autogen") + var filenames []string + + filepath.Walk(c.ProjectRootDir, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + if dirExcludes.MatchString(info.Name()) { + return filepath.SkipDir + } + } + + if !strings.HasSuffix(path, ".go") || fileExcludes.MatchString(path) { + return nil + } + + filenames = append(filenames, path) + + return nil + }) + + for _, filename := range filenames { + + pkg := c.packageFromPath(filename) + + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) + if err != nil { + panic(err) + } + + ast.Inspect(node, func(n ast.Node) bool { + switch t := n.(type) { + case *ast.TypeSpec: + if t.Name.IsExported() { + switch it := t.Type.(type) { + case *ast.InterfaceType: + iface := pkg + "." + t.Name.Name + methodNames := collectMethodsRecursive(pkg, it.Methods.List) + weights := make(map[string]int) + weight := weightWidth + for _, name := range methodNames { + weights[name] = weight + weight += weightWidth + } + c.methodWeight[iface] = weights + } + } + } + return true + }) + + } + + // Complement + for _, v1 := range c.methodWeight { + for k2, w := range v1 { + if v, found := c.methodWeight[k2]; found { + for k3, v3 := range v { + v1[k3] = (v3 / weightWidth) + w + } + } + } + } + }) +} + +func (c *Inspector) packageFromPath(p string) string { + p = filepath.ToSlash(p) + base := path.Base(p) + if !strings.Contains(base, ".") { + return base + } + return path.Base(strings.TrimSuffix(p, base)) +} + +// Method holds enough information about it to recreate it. +type Method struct { + // The interface we extracted this method from. + Owner reflect.Type + + // String version of the above, on the form PACKAGE.NAME, e.g. + // page.Page + OwnerName string + + // Method name. + Name string + + // Imports needed to satisfy the method signature. + Imports []string + + // Argument types, including any package prefix, e.g. string, int, interface{}, + // net.Url + In []string + + // Return types. + Out []string +} + +// Declaration creates a method declaration (without any body) for the given receiver. +func (m Method) Declaration(receiver string) string { + return fmt.Sprintf("func (%s %s) %s%s %s", receiverShort(receiver), receiver, m.Name, m.inStr(), m.outStr()) +} + +// DeclarationNamed creates a method declaration (without any body) for the given receiver +// with named return values. +func (m Method) DeclarationNamed(receiver string) string { + return fmt.Sprintf("func (%s %s) %s%s %s", receiverShort(receiver), receiver, m.Name, m.inStr(), m.outStrNamed()) +} + +// Delegate creates a delegate call string. +func (m Method) Delegate(receiver, delegate string) string { + ret := "" + if len(m.Out) > 0 { + ret = "return " + } + return fmt.Sprintf("%s%s.%s.%s%s", ret, receiverShort(receiver), delegate, m.Name, m.inOutStr()) +} + +func (m Method) String() string { + return m.Name + m.inStr() + " " + m.outStr() + "\n" +} + +func (m Method) inOutStr() string { + if len(m.In) == 0 { + return "()" + } + + args := make([]string, len(m.In)) + for i := range args { + args[i] = fmt.Sprintf("arg%d", i) + } + return "(" + strings.Join(args, ", ") + ")" +} + +func (m Method) inStr() string { + if len(m.In) == 0 { + return "()" + } + + args := make([]string, len(m.In)) + for i := range args { + args[i] = fmt.Sprintf("arg%d %s", i, m.In[i]) + } + return "(" + strings.Join(args, ", ") + ")" +} + +func (m Method) outStr() string { + if len(m.Out) == 0 { + return "" + } + if len(m.Out) == 1 { + return m.Out[0] + } + + return "(" + strings.Join(m.Out, ", ") + ")" +} + +func (m Method) outStrNamed() string { + if len(m.Out) == 0 { + return "" + } + + outs := make([]string, len(m.Out)) + for i := range outs { + outs[i] = fmt.Sprintf("o%d %s", i, m.Out[i]) + } + + return "(" + strings.Join(outs, ", ") + ")" +} + +// Methods represents a list of methods for one or more interfaces. +// The order matches the defined order in their source file(s). +type Methods []Method + +// Imports returns a sorted list of package imports needed to satisfy the +// signatures of all methods. +func (m Methods) Imports() []string { + var pkgImports []string + for _, method := range m { + pkgImports = append(pkgImports, method.Imports...) + } + if len(pkgImports) > 0 { + pkgImports = uniqueNonEmptyStrings(pkgImports) + sort.Strings(pkgImports) + } + return pkgImports +} + +// ToMarshalJSON creates a MarshalJSON method for these methods. Any method name +// matching any of the regexps in excludes will be ignored. +func (m Methods) ToMarshalJSON(receiver, pkgPath string, excludes ...string) (string, []string) { + var sb strings.Builder + + r := receiverShort(receiver) + what := firstToUpper(trimAsterisk(receiver)) + pgkName := path.Base(pkgPath) + + fmt.Fprintf(&sb, "func Marshal%sToJSON(%s %s) ([]byte, error) {\n", what, r, receiver) + + var methods Methods + excludeRes := make([]*regexp.Regexp, len(excludes)) + + for i, exclude := range excludes { + excludeRes[i] = regexp.MustCompile(exclude) + } + + for _, method := range m { + // Exclude methods with arguments and incompatible return values + if len(method.In) > 0 || len(method.Out) == 0 || len(method.Out) > 2 { + continue + } + + if len(method.Out) == 2 { + if method.Out[1] != "error" { + continue + } + } + + for _, re := range excludeRes { + if re.MatchString(method.Name) { + continue + } + } + + methods = append(methods, method) + } + + for _, method := range methods { + varn := varName(method.Name) + if len(method.Out) == 1 { + fmt.Fprintf(&sb, "\t%s := %s.%s()\n", varn, r, method.Name) + } else { + fmt.Fprintf(&sb, "\t%s, err := %s.%s()\n", varn, r, method.Name) + fmt.Fprint(&sb, "\tif err != nil {\n\t\treturn nil, err\n\t}\n") + } + } + + fmt.Fprint(&sb, "\n\ts := struct {\n") + + for _, method := range methods { + fmt.Fprintf(&sb, "\t\t%s %s\n", method.Name, typeName(method.Out[0], pgkName)) + } + + fmt.Fprint(&sb, "\n\t}{\n") + + for _, method := range methods { + varn := varName(method.Name) + fmt.Fprintf(&sb, "\t\t%s: %s,\n", method.Name, varn) + } + + fmt.Fprint(&sb, "\n\t}\n\n") + fmt.Fprint(&sb, "\treturn json.Marshal(&s)\n}") + + pkgImports := append(methods.Imports(), "encoding/json") + + if pkgPath != "" { + // Exclude self + for i, pkgImp := range pkgImports { + if pkgImp == pkgPath { + pkgImports = slices.Delete(pkgImports, i, i+1) + } + } + } + + return sb.String(), pkgImports +} + +func collectMethodsRecursive(pkg string, f []*ast.Field) []string { + var methodNames []string + for _, m := range f { + if m.Names != nil { + methodNames = append(methodNames, m.Names[0].Name) + continue + } + + if ident, ok := m.Type.(*ast.Ident); ok && ident.Obj != nil { + switch tt := ident.Obj.Decl.(*ast.TypeSpec).Type.(type) { + case *ast.InterfaceType: + // Embedded interface + methodNames = append( + methodNames, + collectMethodsRecursive( + pkg, + tt.Methods.List)...) + } + } else { + // Embedded, but in a different file/package. Return the + // package.Name and deal with that later. + name := packageName(m.Type) + if !strings.Contains(name, ".") { + // Assume current package + name = pkg + "." + name + } + methodNames = append(methodNames, name) + } + } + + return methodNames +} + +func firstToLower(name string) string { + return strings.ToLower(name[:1]) + name[1:] +} + +func firstToUpper(name string) string { + return strings.ToUpper(name[:1]) + name[1:] +} + +func packageName(e ast.Expr) string { + switch tp := e.(type) { + case *ast.Ident: + return tp.Name + case *ast.SelectorExpr: + return fmt.Sprintf("%s.%s", packageName(tp.X), packageName(tp.Sel)) + } + return "" +} + +func receiverShort(receiver string) string { + return strings.ToLower(trimAsterisk(receiver))[:1] +} + +func trimAsterisk(name string) string { + return strings.TrimPrefix(name, "*") +} + +func typeName(name, pkg string) string { + return strings.TrimPrefix(name, pkg+".") +} + +func uniqueNonEmptyStrings(s []string) []string { + var unique []string + set := map[string]any{} + for _, val := range s { + if val == "" { + continue + } + if _, ok := set[val]; !ok { + unique = append(unique, val) + set[val] = val + } + } + return unique +} + +func varName(name string) string { + name = firstToLower(name) + + // Adjust some reserved keywords, see https://golang.org/ref/spec#Keywords + switch name { + case "type": + name = "typ" + case "package": + name = "pkg" + // Not reserved, but syntax highlighters has it as a keyword. + case "len": + name = "length" + } + + return name +} diff --git a/codegen/methods2_test.go b/codegen/methods2_test.go new file mode 100644 index 0000000..bd36b5e --- /dev/null +++ b/codegen/methods2_test.go @@ -0,0 +1,20 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package codegen + +type IEmbed interface { + MethodEmbed3(s string) string + MethodEmbed1() string + MethodEmbed2() +} diff --git a/codegen/methods_test.go b/codegen/methods_test.go new file mode 100644 index 0000000..bce80ec --- /dev/null +++ b/codegen/methods_test.go @@ -0,0 +1,96 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package codegen + +import ( + "fmt" + "net" + "os" + "reflect" + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/herrors" +) + +func TestMethods(t *testing.T) { + var ( + zeroIE = reflect.TypeFor[IEmbed]() + zeroIEOnly = reflect.TypeFor[IEOnly]() + zeroI = reflect.TypeFor[I]() + ) + + dir, _ := os.Getwd() + insp := NewInspector(dir) + + t.Run("MethodsFromTypes", func(t *testing.T) { + c := qt.New(t) + + methods := insp.MethodsFromTypes([]reflect.Type{zeroI}, nil) + + methodsStr := fmt.Sprint(methods) + + c.Assert(methodsStr, qt.Contains, "Method1(arg0 herrors.ErrorContext)") + c.Assert(methodsStr, qt.Contains, "Method7() interface {}") + c.Assert(methodsStr, qt.Contains, "Method0() string\n Method4() string") + c.Assert(methodsStr, qt.Contains, "MethodEmbed3(arg0 string) string\n MethodEmbed1() string") + + c.Assert(methods.Imports(), qt.Contains, "github.com/gohugoio/hugo/common/herrors") + }) + + t.Run("EmbedOnly", func(t *testing.T) { + c := qt.New(t) + + methods := insp.MethodsFromTypes([]reflect.Type{zeroIEOnly}, nil) + + methodsStr := fmt.Sprint(methods) + + c.Assert(methodsStr, qt.Contains, "MethodEmbed3(arg0 string) string") + }) + + t.Run("ToMarshalJSON", func(t *testing.T) { + c := qt.New(t) + + m, pkg := insp.MethodsFromTypes( + []reflect.Type{zeroI}, + []reflect.Type{zeroIE}).ToMarshalJSON("*page", "page") + + c.Assert(m, qt.Contains, "method6 := p.Method6()") + c.Assert(m, qt.Contains, "Method0: method0,") + c.Assert(m, qt.Contains, "return json.Marshal(&s)") + + c.Assert(pkg, qt.Contains, "github.com/gohugoio/hugo/common/herrors") + c.Assert(pkg, qt.Contains, "encoding/json") + + fmt.Println(pkg) + }) +} + +type I interface { + IEmbed + Method0() string + Method4() string + Method1(myerr herrors.ErrorContext) + Method3(myint int, mystring string) + Method5() (string, error) + Method6() *net.IP + Method7() any + Method8() herrors.ErrorContext + method2() + method9() os.FileInfo +} + +type IEOnly interface { + IEmbed +} diff --git a/commands/commandeer.go b/commands/commandeer.go new file mode 100644 index 0000000..6a9ffc6 --- /dev/null +++ b/commands/commandeer.go @@ -0,0 +1,711 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "errors" + "fmt" + "io" + "log" + "os" + "os/signal" + "path/filepath" + "runtime" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" + + "go.uber.org/automaxprocs/maxprocs" + + "github.com/bep/clocks" + "github.com/bep/lazycache" + "github.com/bep/logg" + "github.com/bep/overlayfs" + "github.com/bep/simplecobra" + + "github.com/gohugoio/hugo/common/hstrings" + "github.com/gohugoio/hugo/common/htime" + "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/allconfig" + "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources/kinds" + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +var errHelp = errors.New("help requested") + +// Execute executes a command. +func Execute(args []string) error { + // Default GOMAXPROCS to be CPU limit aware, still respecting GOMAXPROCS env. + maxprocs.Set() + x, err := newExec() + if err != nil { + return err + } + args = mapLegacyArgs(args) + cd, err := x.Execute(context.Background(), args) + if cd != nil { + if closer, ok := cd.Root.Command.(types.Closer); ok { + closer.Close() + } + } + + if err != nil { + if err == errHelp { + cd.CobraCommand.Help() + fmt.Println() + return nil + } + if simplecobra.IsCommandError(err) { + // Print the help, but also return the error to fail the command. + cd.CobraCommand.Help() + fmt.Println() + } + } + return err +} + +type commonConfig struct { + mu *sync.Mutex + configs *allconfig.Configs + cfg config.Provider + fs *hugofs.Fs +} + +type configKey struct { + counter int32 + ignoreModulesDoesNotExists bool + skipNpmCheck bool +} + +// This is the root command. +type rootCommand struct { + Printf func(format string, v ...any) + Println func(a ...any) + StdOut io.Writer + StdErr io.Writer + + logger loggers.Logger + + // The main cache busting key for the caches below. + configVersionID atomic.Int32 + + // Some, but not all commands need access to these. + // Some needs more than one, so keep them in a small cache. + commonConfigs *lazycache.Cache[configKey, *commonConfig] + hugoSites *lazycache.Cache[configKey, *hugolib.HugoSites] + + // changesFromBuild received from Hugo in watch mode. + changesFromBuild chan []identity.Identity + + commands []simplecobra.Commander + + // Flags + source string + buildWatch bool + environment string + + // Common build flags. + baseURL string + gc bool + poll string + forceSyncStatic bool + panicOnWarning bool + + // Profile flags (for debugging of performance problems) + cpuprofile string + memprofile string + mutexprofile string + traceprofile string + printm bool + + logLevel string + + quiet bool + devMode bool // Hidden flag. + + renderToMemory bool + + cfgFile string + cfgDir string +} + +// resolveEnvironment sets r.environment if not already set. +// server indicates whether the server command is running (defaults to development). +func (r *rootCommand) resolveEnvironment(server bool) { + if r.environment != "" { + return + } + if env := os.Getenv("HUGO_ENVIRONMENT"); env != "" { + r.environment = env + } else if env := os.Getenv("HUGO_ENV"); env != "" { + r.environment = env + } else if server { + r.environment = hugo.EnvironmentDevelopment + } else { + r.environment = hugo.EnvironmentProduction + } +} + +func (r *rootCommand) isVerbose() bool { + return r.logger.Level() <= logg.LevelInfo +} + +func (r *rootCommand) Close() error { + if r.hugoSites != nil { + r.hugoSites.DeleteFunc(func(key configKey, value *hugolib.HugoSites) bool { + if value != nil { + value.Close() + } + return false + }) + } + return nil +} + +func (r *rootCommand) Build(cd *simplecobra.Commandeer, bcfg hugolib.BuildCfg, cfg config.Provider) (*hugolib.HugoSites, error) { + h, err := r.Hugo(cfg) + if err != nil { + return nil, err + } + if err := h.Build(bcfg); err != nil { + return nil, err + } + + return h, nil +} + +func (r *rootCommand) Commands() []simplecobra.Commander { + return r.commands +} + +func (r *rootCommand) ConfigFromConfig(key configKey, oldConf *commonConfig) (*commonConfig, error) { + cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) { + fs := oldConf.fs + configs, err := allconfig.LoadConfig( + allconfig.ConfigSourceDescriptor{ + Flags: oldConf.cfg, + Fs: fs.Source, + Filename: r.cfgFile, + ConfigDir: r.cfgDir, + Logger: r.logger, + Environment: r.environment, + IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists, + SkipNpmCheck: key.skipNpmCheck, + }, + ) + if err != nil { + return nil, err + } + + if !configs.Base.C.Clock.IsZero() { + // TODO(bep) find a better place for this. + htime.Clock = clocks.Start(configs.Base.C.Clock) + } + + return &commonConfig{ + mu: oldConf.mu, + configs: configs, + cfg: oldConf.cfg, + fs: fs, + }, nil + }) + + return cc, err +} + +func (r *rootCommand) ConfigFromProvider(key configKey, cfg config.Provider) (*commonConfig, error) { + if cfg == nil { + panic("cfg must be set") + } + r.resolveEnvironment(false) + cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) { + var dir string + if r.source != "" { + dir, _ = filepath.Abs(r.source) + } else { + dir, _ = os.Getwd() + } + + if cfg == nil { + cfg = config.New() + } + + if !cfg.IsSet("workingDir") { + cfg.Set("workingDir", dir) + } else { + if err := os.MkdirAll(cfg.GetString("workingDir"), 0o777); err != nil { + return nil, fmt.Errorf("failed to create workingDir: %w", err) + } + } + + // Load the config first to allow publishDir to be configured in config file. + configs, err := allconfig.LoadConfig( + allconfig.ConfigSourceDescriptor{ + Flags: cfg, + Fs: hugofs.Os, + Filename: r.cfgFile, + ConfigDir: r.cfgDir, + Environment: r.environment, + Logger: r.logger, + IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists, + SkipNpmCheck: key.skipNpmCheck, + }, + ) + if err != nil { + return nil, err + } + + base := configs.Base + + cfg.Set("publishDir", base.PublishDir) + cfg.Set("publishDirStatic", base.PublishDir) + cfg.Set("publishDirDynamic", base.PublishDir) + + renderStaticToDisk := cfg.GetBool("renderStaticToDisk") + + sourceFs := hugofs.Os + var destinationFs afero.Fs + if cfg.GetBool("renderToMemory") { + destinationFs = afero.NewMemMapFs() + if renderStaticToDisk { + // Hybrid, render dynamic content to Root. + cfg.Set("publishDirDynamic", "/") + } else { + // Rendering to memoryFS, publish to Root regardless of publishDir. + cfg.Set("publishDirDynamic", "/") + cfg.Set("publishDirStatic", "/") + } + } else { + destinationFs = hugofs.Os + } + + fs := hugofs.NewFromSourceAndDestination(sourceFs, destinationFs, cfg) + + if renderStaticToDisk { + dynamicFs := fs.PublishDir + publishDirStatic := cfg.GetString("publishDirStatic") + workingDir := cfg.GetString("workingDir") + absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic) + staticFs := hugofs.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic) + + // Serve from both the static and dynamic fs, + // the first will take priority. + // THis is a read-only filesystem, + // we do all the writes to + // fs.Destination and fs.DestinationStatic. + fs.PublishDirServer = overlayfs.New( + overlayfs.Options{ + Fss: []afero.Fs{ + dynamicFs, + staticFs, + }, + }, + ) + fs.PublishDirStatic = staticFs + + } + + if !base.C.Clock.IsZero() { + // TODO(bep) find a better place for this. + htime.Clock = clocks.Start(configs.Base.C.Clock) + } + + if base.PrintPathWarnings { + // Note that we only care about the "dynamic creates" here, + // so skip the static fs. + fs.PublishDir = hugofs.NewCreateCountingFs(fs.PublishDir) + } + + commonConfig := &commonConfig{ + mu: &sync.Mutex{}, + configs: configs, + cfg: cfg, + fs: fs, + } + + return commonConfig, nil + }) + + return cc, err +} + +func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) { + if conf == nil { + return nil, fmt.Errorf("conf must be set") + } + k := configKey{counter: r.configVersionID.Load()} + h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { + depsCfg := r.newDepsConfig(conf) + return hugolib.NewHugoSites(depsCfg) + }) + return h, err +} + +func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) { + return r.getOrCreateHugo(cfg, false) +} + +func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotExist bool) (*hugolib.HugoSites, error) { + k := configKey{counter: r.configVersionID.Load(), ignoreModulesDoesNotExists: ignoreModuleDoesNotExist} + h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { + conf, err := r.ConfigFromProvider(key, cfg) + if err != nil { + return nil, err + } + depsCfg := r.newDepsConfig(conf) + return hugolib.NewHugoSites(depsCfg) + }) + return h, err +} + +func (r *rootCommand) newDepsConfig(conf *commonConfig) deps.DepsCfg { + return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, StdOut: r.logger.StdOut(), StdErr: r.logger.StdErr(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild} +} + +func (r *rootCommand) Name() string { + return "hugo" +} + +func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + b := newHugoBuilder(r, nil) + + if !r.buildWatch { + defer b.postBuild("Total", time.Now()) + } + + if err := b.loadConfig(cd, false); err != nil { + return err + } + + err := func() error { + if r.buildWatch { + defer r.timeTrack(time.Now(), "Built") + } + err := b.build() + if err != nil { + return err + } + return nil + }() + if err != nil { + return err + } + + if !r.buildWatch { + // Done. + return nil + } + + watchDirs, err := b.getDirList() + if err != nil { + return err + } + + watchGroups := helpers.ExtractAndGroupRootPaths(watchDirs) + + r.Printf("Watching for changes in %s\n", strings.Join(watchGroups, ", ")) + watcher, err := b.newWatcher(r.poll, watchDirs...) + if err != nil { + return err + } + + defer watcher.Close() + + r.Println("Press Ctrl+C to stop") + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + <-sigs + + return nil +} + +func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + r.StdOut = os.Stdout + r.StdErr = os.Stderr + if r.quiet { + r.StdOut = io.Discard + r.StdErr = io.Discard + } + // Used by mkcert (server). + log.SetOutput(r.StdOut) + + r.Printf = func(format string, v ...any) { + if !r.quiet { + fmt.Fprintf(r.StdOut, format, v...) + } + } + r.Println = func(a ...any) { + if !r.quiet { + fmt.Fprintln(r.StdOut, a...) + } + } + _, running := runner.Command.(*serverCommand) + var err error + r.logger, err = r.createLogger(running) + if err != nil { + return err + } + // Set up the global logger early to allow info deprecations during config load. + loggers.SetGlobalLogger(r.logger) + + r.changesFromBuild = make(chan []identity.Identity, 10) + + r.commonConfigs = lazycache.New(lazycache.Options[configKey, *commonConfig]{MaxEntries: 5}) + // We don't want to keep stale HugoSites in memory longer than needed. + r.hugoSites = lazycache.New(lazycache.Options[configKey, *hugolib.HugoSites]{ + MaxEntries: 1, + OnEvict: func(key configKey, value *hugolib.HugoSites) { + value.Close() + runtime.GC() + }, + }) + + return nil +} + +func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { + level := logg.LevelWarn + + if r.devMode { + level = logg.LevelTrace + } else { + if r.logLevel != "" { + switch strings.ToLower(r.logLevel) { + case "debug": + level = logg.LevelDebug + case "info": + level = logg.LevelInfo + case "warn", "warning": + level = logg.LevelWarn + case "error": + level = logg.LevelError + default: + return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel) + } + } + } + + var logHookLast func(e *logg.Entry) error + if r.panicOnWarning { + logHookLast = loggers.PanicOnWarningHook + } + + optsLogger := loggers.Options{ + DistinctLevel: logg.LevelWarn, + Level: level, + StdOut: r.StdOut, + StdErr: r.StdErr, + StoreErrors: running, + HandlerPost: logHookLast, + } + + return loggers.New(optsLogger), nil +} + +func (r *rootCommand) resetLogs() { + r.logger.Reset() + loggers.Log().Reset() +} + +// IsTestRun reports whether the command is running as a test. +func (r *rootCommand) IsTestRun() bool { + return os.Getenv("HUGO_TESTRUN") != "" +} + +func (r *rootCommand) Init(cd *simplecobra.Commandeer) error { + return r.initRootCommand("", cd) +} + +func (r *rootCommand) initRootCommand(subCommandName string, cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + commandName := "hugo" + if subCommandName != "" { + commandName = subCommandName + } + cmd.Use = fmt.Sprintf("%s [flags]", commandName) + cmd.Short = "Build your project" + cmd.Long = `COMMAND_NAME is the main command, used to build your Hugo project. + +Hugo is a Fast and Flexible Static Site Generator +built with love by spf13 and friends in Go. + +Complete documentation is available at https://gohugo.io/.` + + cmd.Long = strings.ReplaceAll(cmd.Long, "COMMAND_NAME", commandName) + + // Configure persistent flags + cmd.PersistentFlags().StringVarP(&r.source, "source", "s", "", "filesystem path to read files relative from") + _ = cmd.MarkFlagDirname("source") + cmd.PersistentFlags().StringP("destination", "d", "", "filesystem path to write files to") + _ = cmd.MarkFlagDirname("destination") + + cmd.PersistentFlags().StringVarP(&r.environment, "environment", "e", "", "build environment") + _ = cmd.RegisterFlagCompletionFunc("environment", cobra.NoFileCompletions) + cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory") + _ = cmd.MarkFlagDirname("themesDir") + cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern") + cmd.PersistentFlags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file") + _ = cmd.RegisterFlagCompletionFunc("ignoreVendorPaths", cobra.NoFileCompletions) + cmd.PersistentFlags().String("clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00") + _ = cmd.RegisterFlagCompletionFunc("clock", cobra.NoFileCompletions) + + cmd.PersistentFlags().StringVar(&r.cfgFile, "config", "", "config file (default is hugo.yaml|json|toml)") + _ = cmd.MarkFlagFilename("config", config.ValidConfigFileExtensions...) + cmd.PersistentFlags().StringVar(&r.cfgDir, "configDir", "config", "config dir") + _ = cmd.MarkFlagDirname("configDir") + cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode") + cmd.PersistentFlags().BoolVarP(&r.renderToMemory, "renderToMemory", "M", false, "render to memory (mostly useful when running the server)") + + cmd.PersistentFlags().BoolVarP(&r.devMode, "devMode", "", false, "only used for internal testing, flag hidden.") + cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)") + _ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp)) + cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed") + + cmd.PersistentFlags().MarkHidden("devMode") + + // Configure local flags + applyLocalFlagsBuild(cmd, r) + + return nil +} + +// A sub set of the complete build flags. These flags are used by new and mod. +func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) { + cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)") + _ = cmd.MarkFlagDirname("theme") + cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/") + cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory") + _ = cmd.MarkFlagDirname("cacheDir") + cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory") + cmd.Flags().StringSliceP("renderSegments", "", []string{}, "named segments to render (configured in the segments config)") +} + +// Flags needed to do a build (used by hugo and hugo server commands) +func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) { + applyLocalFlagsBuildConfig(cmd, r) + cmd.Flags().Bool("cleanDestinationDir", false, "remove files from destination not found in static directories") + cmd.Flags().BoolP("buildDrafts", "D", false, "include content marked as draft") + cmd.Flags().BoolP("buildFuture", "F", false, "include content with publishdate in the future") + cmd.Flags().BoolP("buildExpired", "E", false, "include expired content") + cmd.Flags().BoolP("ignoreCache", "", false, "ignore the configured file caches") + cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages") + cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory") + _ = cmd.MarkFlagDirname("layoutDir") + cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build") + cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes") + _ = cmd.RegisterFlagCompletionFunc("poll", cobra.NoFileCompletions) + cmd.Flags().BoolVar(&r.panicOnWarning, "panicOnWarning", false, "panic on first WARNING log") + cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions") + cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics") + cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.") + cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files") + cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files") + cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations") + cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.") + cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.") + cmd.Flags().StringVarP(&r.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`") + cmd.Flags().StringVarP(&r.memprofile, "profile-mem", "", "", "write memory profile to `file`") + cmd.Flags().BoolVarP(&r.printm, "printMemoryUsage", "", false, "print memory usage to screen at intervals") + cmd.Flags().StringVarP(&r.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`") + cmd.Flags().StringVarP(&r.traceprofile, "trace", "", "", "write trace to `file` (not useful in general)") + + // Hide these for now. + cmd.Flags().MarkHidden("profile-cpu") + cmd.Flags().MarkHidden("profile-mem") + cmd.Flags().MarkHidden("profile-mutex") + + cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)") + _ = cmd.RegisterFlagCompletionFunc("disableKinds", cobra.FixedCompletions(kinds.AllKinds, cobra.ShellCompDirectiveNoFileComp)) + cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)") +} + +func (r *rootCommand) timeTrack(start time.Time, name string) { + elapsed := time.Since(start) + r.Printf("%s in %v ms\n", name, int(1000*elapsed.Seconds())) +} + +type simpleCommand struct { + use string + name string + short string + long string + aliases []string + run func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *rootCommand, args []string) error + withc func(cmd *cobra.Command, r *rootCommand) + initc func(cd *simplecobra.Commandeer) error + + commands []simplecobra.Commander + + rootCmd *rootCommand +} + +func (c *simpleCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *simpleCommand) Name() string { + return c.name +} + +func (c *simpleCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + if c.run == nil { + return nil + } + return c.run(ctx, cd, c.rootCmd, args) +} + +func (c *simpleCommand) Init(cd *simplecobra.Commandeer) error { + c.rootCmd = cd.Root.Command.(*rootCommand) + cmd := cd.CobraCommand + cmd.Short = c.short + cmd.Long = c.long + cmd.Aliases = c.aliases + if c.use != "" { + cmd.Use = c.use + } + if c.withc != nil { + c.withc(cmd, c.rootCmd) + } + return nil +} + +func (c *simpleCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + if c.initc != nil { + return c.initc(cd) + } + return nil +} + +func mapLegacyArgs(args []string) []string { + if len(args) > 1 && args[0] == "new" && !hstrings.EqualAny(args[1], "project", "site", "theme", "content") { + // Insert "content" as the second argument + args = append(args[:1], append([]string{"content"}, args[1:]...)...) + } + return args +} diff --git a/commands/commands.go b/commands/commands.go new file mode 100644 index 0000000..10ab106 --- /dev/null +++ b/commands/commands.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + + "github.com/bep/simplecobra" +) + +// newExec wires up all of Hugo's CLI. +func newExec() (*simplecobra.Exec, error) { + rootCmd := &rootCommand{ + commands: []simplecobra.Commander{ + newHugoBuildCmd(), + newVersionCmd(), + newEnvCommand(), + newServerCommand(), + newDeployCommand(), + newConfigCommand(), + newNewCommand(), + newConvertCommand(), + newImportCommand(), + newListCommand(), + newModCommands(), + newGenCommand(), + newReleaseCommand(), + }, + } + + return simplecobra.New(rootCmd) +} + +func newHugoBuildCmd() simplecobra.Commander { + return &hugoBuildCommand{} +} + +// hugoBuildCommand just delegates to the rootCommand. +type hugoBuildCommand struct { + rootCmd *rootCommand +} + +func (c *hugoBuildCommand) Commands() []simplecobra.Commander { + return nil +} + +func (c *hugoBuildCommand) Name() string { + return "build" +} + +func (c *hugoBuildCommand) Init(cd *simplecobra.Commandeer) error { + c.rootCmd = cd.Root.Command.(*rootCommand) + return c.rootCmd.initRootCommand("build", cd) +} + +func (c *hugoBuildCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + return c.rootCmd.PreRun(cd, runner) +} + +func (c *hugoBuildCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + return c.rootCmd.Run(ctx, cd, args) +} diff --git a/commands/config.go b/commands/config.go new file mode 100644 index 0000000..59433b3 --- /dev/null +++ b/commands/config.go @@ -0,0 +1,240 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "strings" + "time" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/config/allconfig" + "github.com/gohugoio/hugo/hugolib/sitesmatrix" + "github.com/gohugoio/hugo/modules" + "github.com/gohugoio/hugo/parser" + "github.com/gohugoio/hugo/parser/metadecoders" + "github.com/spf13/cobra" +) + +// newConfigCommand creates a new config command and its subcommands. +func newConfigCommand() *configCommand { + return &configCommand{ + commands: []simplecobra.Commander{ + &configMountsCommand{}, + }, + } +} + +type configCommand struct { + r *rootCommand + + format string + lang string + printZero bool + + commands []simplecobra.Commander +} + +func (c *configCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *configCommand) Name() string { + return "config" +} + +func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil)) + if err != nil { + return err + } + var config *allconfig.Config + if c.lang != "" { + var found bool + config, found = conf.configs.LanguageConfigMap[c.lang] + if !found { + return fmt.Errorf("language %q not found", c.lang) + } + } else { + config = conf.configs.LanguageConfigMap[conf.configs.Base.DefaultContentLanguage] + } + + var buf bytes.Buffer + dec := json.NewEncoder(&buf) + dec.SetIndent("", " ") + dec.SetEscapeHTML(false) + + if err := dec.Encode(parser.ReplacingJSONMarshaller{Value: config, KeysToLower: true, OmitEmpty: !c.printZero}); err != nil { + return err + } + + format := strings.ToLower(c.format) + + switch format { + case "json": + os.Stdout.Write(buf.Bytes()) + default: + // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. + var m map[string]any + if err := json.Unmarshal(buf.Bytes(), &m); err != nil { + return err + } + hmaps.ConvertFloat64WithNoDecimalsToInt(m) + switch format { + case "yaml": + return parser.InterfaceToConfig(m, metadecoders.YAML, os.Stdout) + case "toml": + return parser.InterfaceToConfig(m, metadecoders.TOML, os.Stdout) + default: + return fmt.Errorf("unsupported format: %q", format) + } + } + + return nil +} + +func (c *configCommand) Init(cd *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + cmd := cd.CobraCommand + cmd.Short = "Display project configuration" + cmd.Long = `Display project configuration, both default and custom settings.` + cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)") + _ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp)) + cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.") + cmd.Flags().BoolVar(&c.printZero, "printZero", false, `include config options with zero values (e.g. false, 0, "") in the output`) + _ = cmd.RegisterFlagCompletionFunc("lang", cobra.NoFileCompletions) + applyLocalFlagsBuildConfig(cmd, c.r) + + return nil +} + +func (c *configCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + return nil +} + +type configModMount struct { + Source string `json:"source"` + Target string `json:"target"` + Sites sitesmatrix.Sites `json:"sites,omitzero"` +} + +type configModMounts struct { + verbose bool + m modules.Module +} + +// MarshalJSON is for internal use only. +func (m *configModMounts) MarshalJSON() ([]byte, error) { + var mounts []configModMount + + for _, mount := range m.m.Mounts() { + mounts = append(mounts, configModMount{ + Source: mount.Source, + Target: mount.Target, + Sites: mount.Sites, + }) + } + + var ownerPath string + if m.m.Owner() != nil { + ownerPath = m.m.Owner().Path() + } + + if m.verbose { + config := m.m.Config() + return json.Marshal(&struct { + Path string `json:"path"` + Version string `json:"version"` + Time time.Time `json:"time"` + Owner string `json:"owner"` + Dir string `json:"dir"` + Meta map[string]any `json:"meta"` + HugoVersion modules.HugoVersion `json:"hugoVersion"` + + Mounts []configModMount `json:"mounts"` + }{ + Path: m.m.Path(), + Version: m.m.Version(), + Time: m.m.Time(), + Owner: ownerPath, + Dir: m.m.Dir(), + Meta: config.Params, + HugoVersion: config.HugoVersion, + Mounts: mounts, + }) + } + + return json.Marshal(&struct { + Path string `json:"path"` + Version string `json:"version"` + Time time.Time `json:"time"` + Owner string `json:"owner"` + Dir string `json:"dir"` + Mounts []configModMount `json:"mounts"` + }{ + Path: m.m.Path(), + Version: m.m.Version(), + Time: m.m.Time(), + Owner: ownerPath, + Dir: m.m.Dir(), + Mounts: mounts, + }) +} + +type configMountsCommand struct { + r *rootCommand + configCmd *configCommand +} + +func (c *configMountsCommand) Commands() []simplecobra.Commander { + return nil +} + +func (c *configMountsCommand) Name() string { + return "mounts" +} + +func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + r := c.configCmd.r + conf, err := r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil)) + if err != nil { + return err + } + + for _, m := range conf.configs.Modules { + if err := parser.InterfaceToConfig(&configModMounts{m: m, verbose: r.isVerbose()}, metadecoders.JSON, os.Stdout); err != nil { + return err + } + } + return nil +} + +func (c *configMountsCommand) Init(cd *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + cmd := cd.CobraCommand + cmd.Short = "Print the configured file mounts" + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, c.r) + return nil +} + +func (c *configMountsCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.configCmd = cd.Parent.Command.(*configCommand) + return nil +} diff --git a/commands/convert.go b/commands/convert.go new file mode 100644 index 0000000..afc6fc7 --- /dev/null +++ b/commands/convert.go @@ -0,0 +1,315 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "fmt" + "path/filepath" + "slices" + "strings" + "time" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/parser" + "github.com/gohugoio/hugo/parser/metadecoders" + "github.com/gohugoio/hugo/parser/pageparser" + "github.com/gohugoio/hugo/resources/page" + "github.com/spf13/cobra" +) + +func newConvertCommand() *convertCommand { + var c *convertCommand + c = &convertCommand{ + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "toJSON", + short: "Convert front matter to JSON", + long: `toJSON converts all front matter in the content directory +to use JSON for the front matter.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + return c.convertContents(metadecoders.JSON) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "toTOML", + short: "Convert front matter to TOML", + long: `toTOML converts all front matter in the content directory +to use TOML for the front matter.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + return c.convertContents(metadecoders.TOML) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "toYAML", + short: "Convert front matter to YAML", + long: `toYAML converts all front matter in the content directory +to use YAML for the front matter.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + return c.convertContents(metadecoders.YAML) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + }, + } + return c +} + +type convertCommand struct { + // Flags. + outputDir string + unsafe bool + + // Deps. + r *rootCommand + h *hugolib.HugoSites + + // Commands. + commands []simplecobra.Commander +} + +func (c *convertCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *convertCommand) Name() string { + return "convert" +} + +func (c *convertCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + return nil +} + +func (c *convertCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Convert front matter to another format" + cmd.Long = `Convert front matter to another format. + +See convert's subcommands toJSON, toTOML and toYAML for more information.` + + cmd.PersistentFlags().StringVarP(&c.outputDir, "output", "o", "", "filesystem path to write files to") + _ = cmd.MarkFlagDirname("output") + cmd.PersistentFlags().BoolVar(&c.unsafe, "unsafe", false, "enable less safe operations, please backup first") + + cmd.RunE = nil + return nil +} + +func (c *convertCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + cfg := config.New() + cfg.Set("buildDrafts", true) + h, err := c.r.Hugo(flagsToCfg(cd, cfg)) + if err != nil { + return err + } + c.h = h + return nil +} + +func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, targetFormat metadecoders.Format) error { + // The resources are not in .Site.AllPages. + for _, r := range p.Resources().ByType("page") { + if err := c.convertAndSavePage(r.(page.Page), site, targetFormat); err != nil { + return err + } + } + + if p.File() == nil { + // No content file. + return nil + } + + errMsg := fmt.Errorf("error processing file %q", p.File().Path()) + + site.Log.Infoln("attempting to convert", p.File().Filename()) + + f := p.File() + file, err := f.FileInfo().Meta().Open() + if err != nil { + site.Log.Errorln(errMsg) + file.Close() + return nil + } + + pf, err := pageparser.ParseFrontMatterAndContent(file) + if err != nil { + site.Log.Errorln(errMsg) + file.Close() + return err + } + + file.Close() + + // better handling of dates in formats that don't have support for them + if pf.FrontMatterFormat == metadecoders.JSON || pf.FrontMatterFormat == metadecoders.YAML || pf.FrontMatterFormat == metadecoders.TOML { + for k, v := range pf.FrontMatter { + switch vv := v.(type) { + case time.Time: + pf.FrontMatter[k] = vv.Format(time.RFC3339) + } + } + } + + var newContent bytes.Buffer + err = parser.InterfaceToFrontMatter(pf.FrontMatter, targetFormat, &newContent) + if err != nil { + site.Log.Errorln(errMsg) + return err + } + + newContent.Write(pf.Content) + + newFilename := p.File().Filename() + + if c.outputDir != "" { + contentDir := strings.TrimSuffix(newFilename, p.File().Path()) + contentDir = filepath.Base(contentDir) + + newFilename = filepath.Join(c.outputDir, contentDir, p.File().Path()) + } + + fs := hugofs.Os + if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil { + return fmt.Errorf("failed to save file %q:: %w", newFilename, err) + } + + return nil +} + +func (c *convertCommand) copyContentDirsForOutput(pagesBackedByFile page.Pages) error { + contentDirs := make(map[string]bool) + for _, p := range pagesBackedByFile { + filename := p.File().Filename() + contentDir := strings.TrimSuffix(filename, p.File().Path()) + if contentDir == filename { + continue + } + contentDirs[filepath.Clean(contentDir)] = true + } + + var contentDirList []string + for contentDir := range contentDirs { + contentDirList = append(contentDirList, contentDir) + } + slices.Sort(contentDirList) + + outputDirAbs, err := filepath.Abs(c.outputDir) + if err != nil { + return fmt.Errorf("failed to resolve output path %q: %w", c.outputDir, err) + } + + for _, contentDir := range contentDirList { + outputContentDirAbs := filepath.Join(outputDirAbs, filepath.Base(contentDir)) + + skipDirs := make(map[string]bool) + relToOutputDir, err := filepath.Rel(contentDir, outputDirAbs) + if err == nil && relToOutputDir != ".." && !strings.HasPrefix(relToOutputDir, ".."+string(filepath.Separator)) { + skipDirs[filepath.Clean(outputDirAbs)] = true + } + relToOutputContentDir, err := filepath.Rel(contentDir, outputContentDirAbs) + if err == nil && relToOutputContentDir != ".." && !strings.HasPrefix(relToOutputContentDir, ".."+string(filepath.Separator)) { + skipDirs[filepath.Clean(outputContentDirAbs)] = true + } + + var shouldCopy func(filename string) bool + if len(skipDirs) > 0 { + shouldCopy = func(filename string) bool { + return !skipDirs[filepath.Clean(filename)] + } + } + + if err := hugio.CopyDir(hugofs.Os, contentDir, outputContentDirAbs, shouldCopy); err != nil { + return fmt.Errorf("failed to copy %q to %q: %w", contentDir, outputContentDirAbs, err) + } + } + + return nil +} + +func (c *convertCommand) convertContents(format metadecoders.Format) error { + if c.outputDir == "" && !c.unsafe { + return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path") + } + + if err := c.h.Build(hugolib.BuildCfg{SkipRender: true}); err != nil { + return err + } + + site := c.h.Sites[0] + + workingDir := c.h.Sites[0].Deps.Conf.WorkingDir() + string(filepath.Separator) + + isConvertible := func(p page.Page) bool { + // Skip pages not backed by a content file. + if p.File() == nil { + return false + } + // Skip content adapters. + if p.File().IsContentAdapter() { + return false + } + // Skip content files provided by modules, including vendored modules. + if !p.File().FileInfo().Meta().IsProject { + return false + } + // Skip content files in project mounts outside the working directory. + if !strings.HasPrefix(p.File().Filename(), workingDir) { + return false + } + return true + } + + seen := make(map[string]bool) + var pagesBackedByFile page.Pages + for _, p := range c.h.Pages() { + if !isConvertible(p) { + continue + } + filename := p.File().Filename() + if seen[filename] { + continue + } + seen[filename] = true + pagesBackedByFile = append(pagesBackedByFile, p) + } + + if c.outputDir != "" { + if err := c.copyContentDirsForOutput(pagesBackedByFile); err != nil { + return err + } + } + + site.Log.Println("processing", len(pagesBackedByFile), "content files") + for _, p := range pagesBackedByFile { + if err := c.convertAndSavePage(p, site, format); err != nil { + return err + } + } + return nil +} diff --git a/commands/deploy.go b/commands/deploy.go new file mode 100644 index 0000000..2f32bd8 --- /dev/null +++ b/commands/deploy.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package commands + +import ( + "context" + + "github.com/gohugoio/hugo/deploy" + + "github.com/bep/simplecobra" + "github.com/spf13/cobra" +) + +func newDeployCommand() simplecobra.Commander { + return &simpleCommand{ + name: "deploy", + short: "Deploy your project to a cloud provider", + long: `Deploy your project to a cloud provider + +See https://gohugo.io/hosting-and-deployment/hugo-deploy/ for detailed +documentation. +`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + h, err := r.Hugo(flagsToCfgWithAdditionalConfigBase(cd, nil, "deployment")) + if err != nil { + return err + } + deployer, err := deploy.New(h.Configs.GetFirstLanguageConfig(), h.Log, h.PathSpec.PublishFs) + if err != nil { + return err + } + return deployer.Deploy(ctx) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + applyDeployFlags(cmd, r) + }, + } +} diff --git a/commands/deploy_flags.go b/commands/deploy_flags.go new file mode 100644 index 0000000..d432654 --- /dev/null +++ b/commands/deploy_flags.go @@ -0,0 +1,33 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "github.com/gohugoio/hugo/deploy/deployconfig" + "github.com/spf13/cobra" +) + +func applyDeployFlags(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one") + _ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions) + cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target") + cmd.Flags().Bool("dryRun", false, "dry run") + cmd.Flags().Bool("force", false, "force upload of all files") + cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target") + cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable") + _ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions) + cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10") + _ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions) +} diff --git a/commands/deploy_off.go b/commands/deploy_off.go new file mode 100644 index 0000000..8f5eaa2 --- /dev/null +++ b/commands/deploy_off.go @@ -0,0 +1,50 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !withdeploy + +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "errors" + + "github.com/bep/simplecobra" + "github.com/spf13/cobra" +) + +func newDeployCommand() simplecobra.Commander { + return &simpleCommand{ + name: "deploy", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + return errors.New("deploy not supported in this version of Hugo; install a release with 'withdeploy' in the archive filename or build yourself with the 'withdeploy' build tag. Also see https://github.com/gohugoio/hugo/pull/12995") + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + applyDeployFlags(cmd, r) + cmd.Hidden = true + }, + } +} diff --git a/commands/env.go b/commands/env.go new file mode 100644 index 0000000..7535225 --- /dev/null +++ b/commands/env.go @@ -0,0 +1,70 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "runtime" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/common/hugo" + "github.com/spf13/cobra" +) + +func newEnvCommand() simplecobra.Commander { + return &simpleCommand{ + name: "env", + short: "Display version and environment info", + long: "Display version and environment info. This is useful in Hugo bug reports", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + r.Printf("%s\n", hugo.BuildVersionString()) + r.Printf("GOOS=%q\n", runtime.GOOS) + r.Printf("GOARCH=%q\n", runtime.GOARCH) + r.Printf("GOVERSION=%q\n", runtime.Version()) + + if r.isVerbose() { + deps := hugo.GetDependencyList() + for _, dep := range deps { + r.Printf("%s\n", dep) + } + } else { + // These are also included in the GetDependencyList above; + // always print these as these are most likely the most useful to know about. + deps := hugo.GetDependencyListNonGo() + for _, dep := range deps { + r.Printf("%s\n", dep) + } + } + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + } +} + +func newVersionCmd() simplecobra.Commander { + return &simpleCommand{ + name: "version", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + r.Println(hugo.BuildVersionString()) + return nil + }, + short: "Display version", + long: "Display version and environment info. This is useful in Hugo bug reports.", + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + } +} diff --git a/commands/gen.go b/commands/gen.go new file mode 100644 index 0000000..8d9224a --- /dev/null +++ b/commands/gen.go @@ -0,0 +1,395 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "slices" + "strings" + + "github.com/alecthomas/chroma/v2" + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/styles" + "github.com/bep/simplecobra" + "github.com/goccy/go-yaml" + "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/docshelper" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/parser" + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +func newGenCommand() *genCommand { + var ( + // Flags. + gendocdir string + genmandir string + + // Chroma flags. + style string + mode string + modeSelector bool + highlightStyle string + lineNumbersInlineStyle string + lineNumbersTableStyle string + omitEmpty bool + omitClassComments bool + ) + + newChromaStyles := func() simplecobra.Commander { + return &simpleCommand{ + name: "chromastyles", + short: "Generate CSS stylesheet for the Chroma code highlighter", + long: `Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if markup.highlight.noClasses is disabled in config. + +See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles.`, + + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + style = strings.ToLower(style) + if !slices.Contains(styles.Names(), style) { + return fmt.Errorf("invalid style: %s", style) + } + var chromaStyle *chroma.Style + if mode != "" { + var chromaMode chroma.Mode + switch mode { + case "light": + chromaMode = chroma.Light + case "dark": + chromaMode = chroma.Dark + default: + return fmt.Errorf("invalid mode: %s", mode) + } + + chromaStyle = styles.GetForMode(style, chromaMode) + if chromaStyle.Mode() != chromaMode { + return fmt.Errorf("style %q does not have a %q mode", style, mode) + } + } else { + chromaStyle = styles.Get(style) + } + builder := chromaStyle.Builder() + if highlightStyle != "" { + builder.Add(chroma.LineHighlight, highlightStyle) + } + if lineNumbersInlineStyle != "" { + builder.Add(chroma.LineNumbers, lineNumbersInlineStyle) + } + if lineNumbersTableStyle != "" { + builder.Add(chroma.LineNumbersTable, lineNumbersTableStyle) + } + style, err := builder.Build() + if err != nil { + return err + } + + if omitEmpty { + // See https://github.com/alecthomas/chroma/commit/5b2a4c5a26c503c79bc86ba3c4ae5b330028bd3d + hugo.Deprecate("--omitEmpty", "Flag is no longer needed, empty classes are now always omitted.", "v0.149.0") + } + options := []html.Option{ + html.WithCSSComments(!omitClassComments), + } + if !modeSelector { + // Only needed for the shared-scope overlay; --modeSelector + // gives each mode its own scope and makes this redundant. + options = append(options, html.WithCustomCSS(chromaCSSOverrides(style))) + } + + formatter := html.New(options...) + + var buf bytes.Buffer + fmt.Fprintf(&buf, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " ")) + formatter.WriteCSS(&buf, style) + css := buf.String() + if modeSelector { + // Scope every selector under a top level mode class, e.g. ".dark .chroma". + // This allows generating both light and dark stylesheets and toggling + // them with a parent class on the page. + // There's no upstream option for this (I think), so do string replacements for now. + // TODO(bep) upstream option for this. + var prefix string + switch style.Mode() { + case chroma.Light: + prefix = ".light " + case chroma.Dark: + prefix = ".dark " + default: + return fmt.Errorf("style %q does not have a %q mode", style.Name, mode) + } + replacer := strings.NewReplacer( + ".bg {", prefix+".bg {", + ".chroma ", prefix+".chroma ", + ) + css = replacer.Replace(css) + } + + fmt.Print(css) + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style") + _ = cmd.RegisterFlagCompletionFunc("style", cobra.NoFileCompletions) + cmd.PersistentFlags().StringVar(&mode, "mode", "", `style mode ("light", "dark")`) + _ = cmd.RegisterFlagCompletionFunc("mode", cobra.FixedCompletions([]string{"light", "dark"}, cobra.ShellCompDirectiveNoFileComp)) + cmd.PersistentFlags().BoolVar(&modeSelector, "modeSelector", false, `scope selectors under a top level mode class, e.g. ".dark .chroma"`) + _ = cmd.RegisterFlagCompletionFunc("modeSelector", cobra.NoFileCompletions) + cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", `foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`) + _ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions) + cmd.PersistentFlags().StringVar(&lineNumbersInlineStyle, "lineNumbersInlineStyle", "", `foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"`) + _ = cmd.RegisterFlagCompletionFunc("lineNumbersInlineStyle", cobra.NoFileCompletions) + cmd.PersistentFlags().StringVar(&lineNumbersTableStyle, "lineNumbersTableStyle", "", `foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"`) + _ = cmd.RegisterFlagCompletionFunc("lineNumbersTableStyle", cobra.NoFileCompletions) + cmd.PersistentFlags().BoolVar(&omitEmpty, "omitEmpty", false, `omit empty CSS rules (deprecated, no longer needed)`) + _ = cmd.RegisterFlagCompletionFunc("omitEmpty", cobra.NoFileCompletions) + cmd.PersistentFlags().BoolVar(&omitClassComments, "omitClassComments", false, `omit CSS class comment prefixes in the generated CSS`) + _ = cmd.RegisterFlagCompletionFunc("omitClassComments", cobra.NoFileCompletions) + }, + } + } + + newMan := func() simplecobra.Commander { + return &simpleCommand{ + name: "man", + short: "Generate man pages for the Hugo CLI", + long: `This command automatically generates up-to-date man pages of Hugo's + command-line interface. By default, it creates the man page files + in the "man" directory under the current directory.`, + + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + header := &doc.GenManHeader{ + Section: "1", + Manual: "Hugo Manual", + Source: fmt.Sprintf("Hugo %s", hugo.CurrentVersion), + } + if !strings.HasSuffix(genmandir, helpers.FilePathSeparator) { + genmandir += helpers.FilePathSeparator + } + if found, _ := helpers.Exists(genmandir, hugofs.Os); !found { + r.Println("Directory", genmandir, "does not exist, creating...") + if err := hugofs.Os.MkdirAll(genmandir, 0o777); err != nil { + return err + } + } + cd.CobraCommand.Root().DisableAutoGenTag = true + + r.Println("Generating Hugo man pages in", genmandir, "...") + doc.GenManTree(cd.CobraCommand.Root(), header, genmandir) + + r.Println("Done.") + + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.PersistentFlags().StringVar(&genmandir, "dir", "man/", "the directory to write the man pages.") + _ = cmd.MarkFlagDirname("dir") + }, + } + } + + newGen := func() simplecobra.Commander { + const gendocFrontmatterTemplate = `--- +title: "%s" +slug: %s +url: %s +--- +` + + return &simpleCommand{ + name: "doc", + short: "Generate Markdown documentation for the Hugo CLI", + long: `Generate Markdown documentation for the Hugo CLI. + This command is, mostly, used to create up-to-date documentation + of Hugo's command-line interface for https://gohugo.io/. + + It creates one Markdown file per command with front matter suitable + for rendering in Hugo.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + cd.CobraCommand.VisitParents(func(c *cobra.Command) { + // Disable the "Auto generated by spf13/cobra on DATE" + // as it creates a lot of diffs. + c.DisableAutoGenTag = true + }) + if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) { + gendocdir += helpers.FilePathSeparator + } + if found, _ := helpers.Exists(gendocdir, hugofs.Os); !found { + r.Println("Directory", gendocdir, "does not exist, creating...") + if err := hugofs.Os.MkdirAll(gendocdir, 0o777); err != nil { + return err + } + } + prepender := func(filename string) string { + name := filepath.Base(filename) + base := strings.TrimSuffix(name, path.Ext(name)) + url := "/docs/reference/commands/" + strings.ToLower(base) + "/" + return fmt.Sprintf(gendocFrontmatterTemplate, strings.Replace(base, "_", " ", -1), base, url) + } + + linkHandler := func(name string) string { + base := strings.TrimSuffix(name, path.Ext(name)) + return "/docs/reference/commands/" + strings.ToLower(base) + "/" + } + r.Println("Generating Hugo command-line documentation in", gendocdir, "...") + doc.GenMarkdownTreeCustom(cd.CobraCommand.Root(), gendocdir, prepender, linkHandler) + r.Println("Done.") + + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.PersistentFlags().StringVar(&gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.") + _ = cmd.MarkFlagDirname("dir") + }, + } + } + + var docsHelperTarget string + + newDocsHelper := func() simplecobra.Commander { + return &simpleCommand{ + name: "docshelper", + short: "Generate some data files for the Hugo docs", + + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + r.Println("Generate docs data to", docsHelperTarget) + + var buf bytes.Buffer + jsonEnc := json.NewEncoder(&buf) + + configProvider := func() docshelper.DocProvider { + conf := hugolib.DefaultConfig() + conf.CacheDir = "" // The default value does not make sense in the docs. + defaultConfig := parser.NullBoolJSONMarshaller{Wrapped: parser.LowerCaseCamelJSONMarshaller{Value: conf}} + return docshelper.DocProvider{"config": defaultConfig} + } + + docshelper.AddDocProviderFunc(configProvider) + if err := jsonEnc.Encode(docshelper.GetDocProvider()); err != nil { + return err + } + + // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. + var m map[string]any + if err := json.Unmarshal(buf.Bytes(), &m); err != nil { + return err + } + + targetFile := filepath.Join(docsHelperTarget, "docs.yaml") + + f, err := os.Create(targetFile) + if err != nil { + return err + } + defer f.Close() + yamlEnc := yaml.NewEncoder(f, yaml.UseSingleQuote(true), yaml.AutoInt()) + if err := yamlEnc.Encode(m); err != nil { + return err + } + + r.Println("Done!") + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.Hidden = true + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.PersistentFlags().StringVarP(&docsHelperTarget, "dir", "", "docs/data", "data dir") + }, + } + } + + return &genCommand{ + commands: []simplecobra.Commander{ + newChromaStyles(), + newGen(), + newMan(), + newDocsHelper(), + }, + } +} + +// chromaCSSOverrides re-emits leaf token colors that Chroma's minifier drops +// because they equal the style's default foreground (the .chroma color). Without +// modeSelector scoping, a paired light/dark stylesheet shares the same .chroma +// scope, and the light sheet's explicit rule (e.g. .chroma .nx) would otherwise +// leak into dark mode, since an explicit declaration beats inheritance. +func chromaCSSOverrides(style *chroma.Style) map[chroma.TokenType]string { + bg := style.Get(chroma.Background) + m := make(map[chroma.TokenType]string) + for tt := range chroma.StandardTypes { + if tt == chroma.Background || !style.Has(tt) || !chromaLeafToken(tt) { + continue + } + entry := style.Get(tt) + if !entry.Sub(bg).IsZero() || !entry.Colour.IsSet() { + continue + } + if css := html.StyleEntryToCSS(chroma.StyleEntry{Colour: entry.Colour}); css != "" { + m[tt] = css + } + } + return m +} + +func chromaLeafToken(tt chroma.TokenType) bool { + for other := range chroma.StandardTypes { + if other != tt && (other.Category() == tt || other.SubCategory() == tt) { + return false + } + } + return true +} + +type genCommand struct { + rootCmd *rootCommand + + commands []simplecobra.Commander +} + +func (c *genCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *genCommand) Name() string { + return "gen" +} + +func (c *genCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + return nil +} + +func (c *genCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Generate documentation and syntax highlighting styles" + cmd.Long = "Generate documentation for your project using Hugo's documentation engine, including syntax highlighting for various programming languages." + + cmd.RunE = nil + return nil +} + +func (c *genCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.rootCmd = cd.Root.Command.(*rootCommand) + return nil +} diff --git a/commands/helpers.go b/commands/helpers.go new file mode 100644 index 0000000..fbe1d36 --- /dev/null +++ b/commands/helpers.go @@ -0,0 +1,121 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "errors" + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/config" + "github.com/spf13/pflag" +) + +const ( + ansiEsc = "\u001B" + clearLine = "\r\033[K" + hideCursor = ansiEsc + "[?25l" + showCursor = ansiEsc + "[?25h" +) + +func newUserError(a ...any) *simplecobra.CommandError { + return &simplecobra.CommandError{Err: errors.New(fmt.Sprint(a...))} +} + +func setValueFromFlag(flags *pflag.FlagSet, key string, cfg config.Provider, targetKey string, force bool) { + key = strings.TrimSpace(key) + if (force && flags.Lookup(key) != nil) || flags.Changed(key) { + f := flags.Lookup(key) + configKey := key + if targetKey != "" { + configKey = targetKey + } + // Gotta love this API. + switch f.Value.Type() { + case "bool": + bv, _ := flags.GetBool(key) + cfg.Set(configKey, bv) + case "string": + cfg.Set(configKey, f.Value.String()) + case "stringSlice": + bv, _ := flags.GetStringSlice(key) + cfg.Set(configKey, bv) + case "int": + iv, _ := flags.GetInt(key) + cfg.Set(configKey, iv) + default: + panic(fmt.Sprintf("update switch with %s", f.Value.Type())) + } + + } +} + +func flagsToCfg(cd *simplecobra.Commandeer, cfg config.Provider) config.Provider { + return flagsToCfgWithAdditionalConfigBase(cd, cfg, "") +} + +func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.Provider, additionalConfigBase string) config.Provider { + if cfg == nil { + cfg = config.New() + } + + // Flags with a different name in the config. + keyMap := map[string]string{ + "minify": "minify.minifyOutput", + "destination": "publishDir", + "editor": "newContentEditor", + } + + // Flags that we for some reason don't want to expose in the project config. + internalKeySet := map[string]bool{ + "quiet": true, + "verbose": true, + "watch": true, + "liveReloadPort": true, + "renderToMemory": true, + "clock": true, + } + + cmd := cd.CobraCommand + flags := cmd.Flags() + + flags.VisitAll(func(f *pflag.Flag) { + if f.Changed { + targetKey := f.Name + if internalKeySet[targetKey] { + targetKey = "internal." + targetKey + } else if mapped, ok := keyMap[targetKey]; ok { + targetKey = mapped + } + setValueFromFlag(flags, f.Name, cfg, targetKey, false) + if additionalConfigBase != "" { + setValueFromFlag(flags, f.Name, cfg, additionalConfigBase+"."+targetKey, true) + } + } + }) + + return cfg +} + +func mkdir(x ...string) { + p := filepath.Join(x...) + err := os.MkdirAll(p, 0o777) // before umask + if err != nil { + log.Fatal(err) + } +} diff --git a/commands/hugo_windows.go b/commands/hugo_windows.go new file mode 100644 index 0000000..c354e88 --- /dev/null +++ b/commands/hugo_windows.go @@ -0,0 +1,33 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + // For time zone lookups on Windows without Go installed. + // See #8892 + _ "time/tzdata" + + "github.com/spf13/cobra" +) + +func init() { + // This message to show to Windows users if Hugo is opened from explorer.exe + cobra.MousetrapHelpText = ` + + Hugo is a command-line tool for generating static websites. + + You need to open PowerShell and run Hugo from there. + + Visit https://gohugo.io/ for more information.` +} diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go new file mode 100644 index 0000000..2ffe3fa --- /dev/null +++ b/commands/hugobuilder.go @@ -0,0 +1,1197 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "runtime/pprof" + "runtime/trace" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/bep/debounce" + "github.com/bep/simplecobra" + "github.com/fsnotify/fsnotify" + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/common/hstrings" + "github.com/gohugoio/hugo/common/htime" + "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/common/terminal" + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/livereload" + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/watcher" + "github.com/spf13/fsync" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +type hugoBuilder struct { + r *rootCommand + + confmu sync.Mutex + confOld *commonConfig + conf *commonConfig + + // May be nil. + s *serverCommand + + // Currently only set when in "fast render mode". + changeDetector *fileChangeDetector + visitedURLs *types.EvictingQueue[string] + + fullRebuildSem *semaphore.Weighted + debounce func(f func()) + + onConfigLoaded func(reloaded bool) error + + fastRenderMode bool + showErrorInBrowser bool + + errState hugoBuilderErrState +} + +var errConfigNotSet = errors.New("config not set") + +func (c *hugoBuilder) withConfE(fn func(conf *commonConfig) error) error { + c.confmu.Lock() + defer c.confmu.Unlock() + if c.conf == nil { + return errConfigNotSet + } + return fn(c.conf) +} + +func (c *hugoBuilder) withConf(fn func(conf *commonConfig)) { + c.confmu.Lock() + defer c.confmu.Unlock() + fn(c.conf) +} + +func (c *hugoBuilder) withConfOrOldConf(fn func(conf *commonConfig)) { + c.confmu.Lock() + defer c.confmu.Unlock() + if c.conf != nil { + fn(c.conf) + } else if c.confOld != nil { + fn(c.confOld) + } +} + +func (c *hugoBuilder) withConfOrOldConfE(fn func(conf *commonConfig) error) error { + c.confmu.Lock() + defer c.confmu.Unlock() + if c.conf != nil { + return fn(c.conf) + } else if c.confOld != nil { + return fn(c.confOld) + } + return errConfigNotSet +} + +type hugoBuilderErrState struct { + mu sync.Mutex + paused bool + builderr error + waserr bool +} + +func (e *hugoBuilderErrState) setPaused(p bool) { + e.mu.Lock() + defer e.mu.Unlock() + e.paused = p +} + +func (e *hugoBuilderErrState) isPaused() bool { + e.mu.Lock() + defer e.mu.Unlock() + return e.paused +} + +func (e *hugoBuilderErrState) setBuildErr(err error) { + e.mu.Lock() + defer e.mu.Unlock() + e.builderr = err +} + +func (e *hugoBuilderErrState) buildErr() error { + e.mu.Lock() + defer e.mu.Unlock() + return e.builderr +} + +func (e *hugoBuilderErrState) setWasErr(w bool) { + e.mu.Lock() + defer e.mu.Unlock() + e.waserr = w +} + +func (e *hugoBuilderErrState) wasErr() bool { + e.mu.Lock() + defer e.mu.Unlock() + return e.waserr +} + +// getDirList provides NewWatcher() with a list of directories to watch for changes. +func (c *hugoBuilder) getDirList() ([]string, error) { + h, err := c.hugo() + if err != nil { + return nil, err + } + + return hstrings.UniqueStringsSorted(h.PathSpec.BaseFs.WatchFilenames()), nil +} + +func (c *hugoBuilder) initCPUProfile() (func(), error) { + if c.r.cpuprofile == "" { + return nil, nil + } + + f, err := os.Create(c.r.cpuprofile) + if err != nil { + return nil, fmt.Errorf("failed to create CPU profile: %w", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + f.Close() + return nil, fmt.Errorf("failed to start CPU profile: %w", err) + } + return func() { + pprof.StopCPUProfile() + f.Close() + }, nil +} + +func (c *hugoBuilder) initMemProfile() { + if c.r.memprofile == "" { + return + } + + f, err := os.Create(c.r.memprofile) + if err != nil { + c.r.logger.Errorf("could not create memory profile: ", err) + } + defer f.Close() + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + c.r.logger.Errorf("could not write memory profile: ", err) + } +} + +func (c *hugoBuilder) initMemTicker() func() { + memticker := time.NewTicker(5 * time.Second) + quit := make(chan struct{}) + printMem := func() { + var m runtime.MemStats + runtime.ReadMemStats(&m) + fmt.Printf("\n\nAlloc = %v\nTotalAlloc = %v\nSys = %v\nNumGC = %v\n\n", formatByteCount(m.Alloc), formatByteCount(m.TotalAlloc), formatByteCount(m.Sys), m.NumGC) + } + + go func() { + for { + select { + case <-memticker.C: + printMem() + case <-quit: + memticker.Stop() + printMem() + return + } + } + }() + + return func() { + close(quit) + } +} + +func (c *hugoBuilder) initMutexProfile() (func(), error) { + if c.r.mutexprofile == "" { + return nil, nil + } + + f, err := os.Create(c.r.mutexprofile) + if err != nil { + return nil, err + } + + runtime.SetMutexProfileFraction(1) + + return func() { + pprof.Lookup("mutex").WriteTo(f, 0) + f.Close() + }, nil +} + +func (c *hugoBuilder) initProfiling() (func(), error) { + stopCPUProf, err := c.initCPUProfile() + if err != nil { + return nil, err + } + + stopMutexProf, err := c.initMutexProfile() + if err != nil { + return nil, err + } + + stopTraceProf, err := c.initTraceProfile() + if err != nil { + return nil, err + } + + var stopMemTicker func() + if c.r.printm { + stopMemTicker = c.initMemTicker() + } + + return func() { + c.initMemProfile() + + if stopCPUProf != nil { + stopCPUProf() + } + if stopMutexProf != nil { + stopMutexProf() + } + + if stopTraceProf != nil { + stopTraceProf() + } + + if stopMemTicker != nil { + stopMemTicker() + } + }, nil +} + +func (c *hugoBuilder) initTraceProfile() (func(), error) { + if c.r.traceprofile == "" { + return nil, nil + } + + f, err := os.Create(c.r.traceprofile) + if err != nil { + return nil, fmt.Errorf("failed to create trace file: %w", err) + } + + if err := trace.Start(f); err != nil { + return nil, fmt.Errorf("failed to start trace: %w", err) + } + + return func() { + trace.Stop() + f.Close() + }, nil +} + +// newWatcher creates a new watcher to watch filesystem events. +func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*watcher.Batcher, error) { + staticSyncer := &staticSyncer{c: c} + + var pollInterval time.Duration + poll := pollIntervalStr != "" + if poll { + pollInterval, err := types.ToDurationE(pollIntervalStr) + if err != nil { + return nil, fmt.Errorf("invalid value for flag poll: %s", err) + } + c.r.logger.Printf("Use watcher with poll interval %v", pollInterval) + } + + if pollInterval == 0 { + pollInterval = 500 * time.Millisecond + } + + watcher, err := watcher.New(500*time.Millisecond, pollInterval, poll) + if err != nil { + return nil, err + } + + h, err := c.hugo() + if err != nil { + return nil, err + } + spec := h.Deps.SourceSpec + + for _, d := range dirList { + if d != "" { + if spec.IgnoreFile(d) { + continue + } + _ = watcher.Add(d) + } + } + + // Identifies changes to config (config.toml) files. + configSet := make(map[string]bool) + var configFiles []string + c.withConf(func(conf *commonConfig) { + configFiles = conf.configs.LoadingInfo.ConfigFiles + }) + + c.r.Println("Watching for config changes in", strings.Join(configFiles, ", ")) + for _, configFile := range configFiles { + watcher.Add(configFile) + configSet[configFile] = true + } + + go func() { + for { + select { + case changes := <-c.r.changesFromBuild: + unlock, err := h.LockBuild() + if err != nil { + c.r.logger.Errorf("Failed to acquire a build lock: %s", err) + return + } + c.changeDetector.PrepareNew() + err = c.rebuildSitesForChanges(changes) + if err != nil { + c.r.logger.Errorln("Error while watching:", err) + } + if c.s != nil && c.s.doLiveReload { + doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0 + doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil + if doReload { + livereload.ForceRefresh() + } + } + unlock() + + case evs := <-watcher.Events: + unlock, err := h.LockBuild() + if err != nil { + c.r.logger.Errorf("Failed to acquire a build lock: %s", err) + return + } + c.handleEvents(watcher, staticSyncer, evs, configSet) + if c.showErrorInBrowser && c.errState.buildErr() != nil { + // Need to reload browser to show the error + livereload.ForceRefresh() + } + unlock() + case err := <-watcher.Errors(): + if err != nil && !herrors.IsNotExist(err) { + c.r.logger.Errorln("Error while watching:", err) + } + } + } + }() + + return watcher, nil +} + +func (c *hugoBuilder) build() error { + stopProfiling, err := c.initProfiling() + if err != nil { + return err + } + + defer func() { + if stopProfiling != nil { + stopProfiling() + } + }() + + if err := c.fullBuild(false); err != nil { + return err + } + + if !c.r.quiet { + c.r.Println() + h, err := c.hugo() + if err != nil { + return err + } + + h.PrintProcessingStats(os.Stdout) + c.r.Println() + } + + return nil +} + +func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) { + defer func() { + c.errState.setBuildErr(err) + }() + + var h *hugolib.HugoSites + h, err = c.hugo() + if err != nil { + return + } + err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock}) + return +} + +func (c *hugoBuilder) copyStatic() (map[string]uint64, error) { + m, err := c.doWithPublishDirs(c.copyStaticTo) + if err == nil || herrors.IsNotExist(err) { + return m, nil + } + return m, err +} + +func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) { + infol := c.r.logger.InfoCommand("static") + publishDir := helpers.FilePathSeparator + + if sourceFs.PublishFolder != "" { + publishDir = filepath.Join(publishDir, sourceFs.PublishFolder) + } + + fs := &countingStatFs{Fs: sourceFs.Fs} + + syncer := fsync.NewSyncer() + c.withConf(func(conf *commonConfig) { + syncer.NoTimes = conf.configs.Base.NoTimes + syncer.NoChmod = conf.configs.Base.NoChmod + syncer.ChmodFilter = chmodFilter + + syncer.DestFs = conf.fs.PublishDirStatic + // Now that we are using a unionFs for the static directories + // We can effectively clean the publishDir on initial sync + syncer.Delete = conf.configs.Base.CleanDestinationDir + }) + + syncer.SrcFs = fs + + if syncer.Delete { + infol.Logf("removing all files from destination that don't exist in static dirs") + + syncer.DeleteFilter = func(f fsync.FileInfo) bool { + name := f.Name() + + // Keep .gitignore and .gitattributes anywhere + if name == ".gitignore" || name == ".gitattributes" { + return true + } + + // Keep Hugo's original dot-directory behavior + return f.IsDir() && strings.HasPrefix(name, ".") + } + } + start := time.Now() + + // because we are using a baseFs (to get the union right). + // set sync src to root + err := syncer.Sync(publishDir, helpers.FilePathSeparator) + if err != nil { + return 0, err + } + loggers.TimeTrackf(infol, start, nil, "syncing static files to %s", publishDir) + + // Sync runs Stat 2 times for every source file. + numFiles := fs.statCounter / 2 + + return numFiles, err +} + +func (c *hugoBuilder) doWithPublishDirs(f func(sourceFs *filesystems.SourceFilesystem) (uint64, error)) (map[string]uint64, error) { + langCount := make(map[string]uint64) + + h, err := c.hugo() + if err != nil { + return nil, err + } + staticFilesystems := h.BaseFs.SourceFilesystems.Static + + if len(staticFilesystems) == 0 { + c.r.logger.Infoln("No static directories found to sync") + return langCount, nil + } + + for lang, fs := range staticFilesystems { + cnt, err := f(fs) + if err != nil { + return langCount, err + } + if lang == "" { + // Not multihost + c.withConf(func(conf *commonConfig) { + for _, l := range conf.configs.Languages { + langCount[l.Lang] = cnt + } + }) + } else { + langCount[lang] = cnt + } + } + + return langCount, nil +} + +func (c *hugoBuilder) progressIntermediate() { + terminal.ReportProgress(c.r.StdOut, terminal.ProgressIntermediate, 0) +} + +func (c *hugoBuilder) progressHidden() { + terminal.ReportProgress(c.r.StdOut, terminal.ProgressHidden, 0) +} + +func (c *hugoBuilder) fullBuild(noBuildLock bool) error { + var ( + g errgroup.Group + langCount map[string]uint64 + ) + + c.r.logger.Println("Start building sites … ") + c.r.logger.Println(hugo.BuildVersionString()) + c.r.logger.Println() + if terminal.IsTerminal(os.Stdout) { + defer func() { + fmt.Print(showCursor + clearLine) + }() + } + + copyStaticFunc := func() error { + cnt, err := c.copyStatic() + if err != nil { + return fmt.Errorf("error copying static files: %w", err) + } + langCount = cnt + return nil + } + buildSitesFunc := func() error { + if err := c.buildSites(noBuildLock); err != nil { + return fmt.Errorf("error building site: %w", err) + } + return nil + } + // Do not copy static files and build sites in parallel if cleanDestinationDir is enabled. + // This flag deletes all static resources in /public folder that are missing in /static, + // and it does so at the end of copyStatic() call. + var cleanDestinationDir bool + c.withConf(func(conf *commonConfig) { + cleanDestinationDir = conf.configs.Base.CleanDestinationDir + }) + if cleanDestinationDir { + if err := copyStaticFunc(); err != nil { + return err + } + if err := buildSitesFunc(); err != nil { + return err + } + } else { + g.Go(copyStaticFunc) + g.Go(buildSitesFunc) + if err := g.Wait(); err != nil { + return err + } + } + + h, err := c.hugo() + if err != nil { + return err + } + for _, s := range h.Sites { + s.ProcessingStats.Static = langCount[s.Language().Lang] + } + + if c.r.gc { + count, err := h.GC() + if err != nil { + return err + } + for _, s := range h.Sites { + // We have no way of knowing what site the garbage belonged to. + s.ProcessingStats.Cleaned = uint64(count) + } + } + + return nil +} + +func (c *hugoBuilder) fullRebuild(changeType string) { + if changeType == configChangeGoMod { + // go.mod may be changed during the build itself, and + // we really want to prevent superfluous builds. + if !c.fullRebuildSem.TryAcquire(1) { + return + } + c.fullRebuildSem.Release(1) + } + + c.fullRebuildSem.Acquire(context.Background(), 1) + + go func() { + defer c.fullRebuildSem.Release(1) + + c.printChangeDetected(changeType) + + defer func() { + // Allow any file system events to arrive basimplecobra. + // This will block any rebuild on config changes for the + // duration of the sleep. + time.Sleep(2 * time.Second) + }() + + defer c.postBuild("Rebuilt", time.Now()) + + err := c.reloadConfig() + if err != nil { + // Set the processing on pause until the state is recovered. + c.errState.setPaused(true) + c.handleBuildErr(err, "Failed to reload config") + if c.s.doLiveReload { + livereload.ForceRefresh() + } + } else { + c.errState.setPaused(false) + } + + if !c.errState.isPaused() { + _, err := c.copyStatic() + if err != nil { + c.r.logger.Errorln(err) + return + } + err = c.buildSites(false) + if err != nil { + c.r.logger.Errorln(err) + } else if c.s != nil && c.s.doLiveReload { + livereload.ForceRefresh() + } + } + }() +} + +func (c *hugoBuilder) handleBuildErr(err error, msg string) { + c.errState.setBuildErr(err) + c.r.logger.Errorln(msg + ": " + cleanErrorLog(err.Error())) +} + +func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, + staticSyncer *staticSyncer, + evs []fsnotify.Event, + configSet map[string]bool, +) { + defer func() { + c.errState.setWasErr(false) + }() + + var isHandled bool + + // Filter out ghost events (from deleted, renamed directories). + // This seems to be a bug in fsnotify, or possibly MacOS. + var n int + for _, ev := range evs { + keep := true + // Write and rename operations are often followed by CHMOD. + // There may be valid use cases for rebuilding the site on CHMOD, + // but that will require more complex logic than this simple conditional. + // On OS X this seems to be related to Spotlight, see: + // https://github.com/go-fsnotify/fsnotify/issues/15 + // A workaround is to put your site(s) on the Spotlight exception list, + // but that may be a little mysterious for most end users. + // So, for now, we skip reload on CHMOD. + // We do have to check for WRITE though. On slower laptops a Chmod + // could be aggregated with other important events, and we still want + // to rebuild on those + if ev.Op == fsnotify.Chmod { + keep = false + } else if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { + if _, err := os.Stat(ev.Name); err != nil { + keep = false + } + } + if keep { + evs[n] = ev + n++ + } + } + evs = evs[:n] + + for _, ev := range evs { + isConfig := configSet[ev.Name] + configChangeType := configChangeConfig + if isConfig { + if strings.Contains(ev.Name, "go.mod") { + configChangeType = configChangeGoMod + } + if strings.Contains(ev.Name, ".work") { + configChangeType = configChangeGoWork + } + } + if !isConfig { + // It may be one of the /config folders + dirname := filepath.Dir(ev.Name) + if dirname != "." && configSet[dirname] { + isConfig = true + } + } + + if isConfig { + isHandled = true + + if ev.Op&fsnotify.Chmod == fsnotify.Chmod { + continue + } + + if ev.Op&fsnotify.Remove == fsnotify.Remove || ev.Op&fsnotify.Rename == fsnotify.Rename { + c.withConf(func(conf *commonConfig) { + for _, configFile := range conf.configs.LoadingInfo.ConfigFiles { + counter := 0 + for watcher.Add(configFile) != nil { + counter++ + if counter >= 100 { + break + } + time.Sleep(100 * time.Millisecond) + } + } + }) + } + + // Config file(s) changed. Need full rebuild. + c.fullRebuild(configChangeType) + + return + } + } + + if isHandled { + return + } + + if c.errState.isPaused() { + // Wait for the server to get into a consistent state before + // we continue with processing. + return + } + + if len(evs) > 50 { + // This is probably a mass edit of the content dir. + // Schedule a full rebuild for when it slows down. + c.debounce(func() { + c.fullRebuild("") + }) + return + } + + c.r.logger.Debugln("Received System Events:", evs) + + staticEvents := []fsnotify.Event{} + dynamicEvents := []fsnotify.Event{} + + filterDuplicateEvents := func(evs []fsnotify.Event) []fsnotify.Event { + seen := make(map[string]bool) + var n int + for _, ev := range evs { + if seen[ev.Name] { + continue + } + seen[ev.Name] = true + evs[n] = ev + n++ + } + return evs[:n] + } + + h, err := c.hugo() + if err != nil { + c.r.logger.Errorln("Error getting the Hugo object:", err) + return + } + n = 0 + for _, ev := range evs { + if h.ShouldSkipFileChangeEvent(ev) { + continue + } + evs[n] = ev + n++ + } + evs = evs[:n] + + for _, ev := range evs { + ext := filepath.Ext(ev.Name) + baseName := filepath.Base(ev.Name) + istemp := strings.HasSuffix(ext, "~") || + (ext == ".swp") || // vim + (ext == ".swx") || // vim + (ext == ".bck") || // helix + (ext == ".tmp") || // generic temp file + (ext == ".DS_Store") || // OSX Thumbnail + baseName == "4913" || // vim + strings.HasPrefix(ext, ".goutputstream") || // gnome + strings.HasSuffix(ext, "jb_old___") || // intelliJ + strings.HasSuffix(ext, "jb_tmp___") || // intelliJ + strings.HasSuffix(ext, "jb_bak___") || // intelliJ + strings.HasPrefix(ext, ".sb-") || // byword + strings.HasPrefix(baseName, ".#") || // emacs + strings.HasPrefix(baseName, "#") // emacs + if istemp { + continue + } + + if h.Deps.SourceSpec.IgnoreFile(ev.Name) { + continue + } + // Sometimes during rm -rf operations a '"": REMOVE' is triggered. Just ignore these + if ev.Name == "" { + continue + } + + walkAdder := func(ctx context.Context, path string, f hugofs.FileMetaInfo) error { + if f.IsDir() { + c.r.logger.Println("adding created directory to watchlist", path) + if err := watcher.Add(path); err != nil { + return err + } + } else if !staticSyncer.isStatic(h, path) { + // Hugo's rebuilding logic is entirely file based. When you drop a new folder into + // /content on OSX, the above logic will handle future watching of those files, + // but the initial CREATE is lost. + dynamicEvents = append(dynamicEvents, fsnotify.Event{Name: path, Op: fsnotify.Create}) + } + return nil + } + + // recursively add new directories to watch list + if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Rename) { + c.withConf(func(conf *commonConfig) { + if s, err := conf.fs.Source.Stat(ev.Name); err == nil && s.Mode().IsDir() { + _ = helpers.Walk(conf.fs.Source, ev.Name, walkAdder) + } + }) + } + + if staticSyncer.isStatic(h, ev.Name) { + staticEvents = append(staticEvents, ev) + } else { + dynamicEvents = append(dynamicEvents, ev) + } + } + + lrl := c.r.logger.InfoCommand("livereload") + + staticEvents = filterDuplicateEvents(staticEvents) + dynamicEvents = filterDuplicateEvents(dynamicEvents) + + if len(staticEvents) > 0 { + c.printChangeDetected("Static files") + + if c.r.forceSyncStatic { + c.r.logger.Printf("Syncing all static files\n") + _, err := c.copyStatic() + if err != nil { + c.r.logger.Errorln("Error copying static files to publish dir:", err) + return + } + } else { + if err := staticSyncer.syncsStaticEvents(staticEvents); err != nil { + c.r.logger.Errorln("Error syncing static files to publish dir:", err) + return + } + } + + if c.s != nil && c.s.doLiveReload { + // Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized + + if !c.errState.wasErr() && len(staticEvents) == 1 { + h, err := c.hugo() + if err != nil { + c.r.logger.Errorln("Error getting the Hugo object:", err) + return + } + + path := h.BaseFs.SourceFilesystems.MakeStaticPathRelative(staticEvents[0].Name) + path = h.RelURL(paths.ToSlashTrimLeading(path), false) + + lrl.Logf("refreshing static file %q", path) + livereload.RefreshPath(path) + } else { + lrl.Logf("got %d static file change events, force refresh", len(staticEvents)) + livereload.ForceRefresh() + } + } + } + + if len(dynamicEvents) > 0 { + partitionedEvents := partitionDynamicEvents( + h.BaseFs.SourceFilesystems, + dynamicEvents) + + onePageName := pickOneWriteOrCreatePath(h.Conf.ContentTypes(), partitionedEvents.ContentEvents) + + c.printChangeDetected("") + c.changeDetector.PrepareNew() + + func() { + defer c.postBuild("Total", time.Now()) + if err := c.rebuildSites(dynamicEvents); err != nil { + c.handleBuildErr(err, "Rebuild failed") + } + }() + + if c.s != nil && c.s.doLiveReload { + if c.errState.wasErr() { + livereload.ForceRefresh() + return + } + + changed := c.changeDetector.changed() + if c.changeDetector != nil { + if len(changed) >= 10 { + lrl.Logf("build changed %d files", len(changed)) + } else { + lrl.Logf("build changed %d files: %q", len(changed), changed) + } + if len(changed) == 0 { + // Nothing has changed. + return + } + } + + // If this change set also contains one or more CSS files, we need to + // refresh these as well. + var cssChanges []string + var otherChanges []string + + for _, ev := range changed { + if strings.HasSuffix(ev, ".css") { + cssChanges = append(cssChanges, ev) + } else { + otherChanges = append(otherChanges, ev) + } + } + + if len(partitionedEvents.ContentEvents) > 0 { + navigate := c.s != nil && c.s.navigateToChanged + // We have fetched the same page above, but it may have + // changed. + var p page.Page + + if navigate { + if onePageName != "" { + p = h.GetContentPage(onePageName) + } + } + + if p != nil && p.RelPermalink() != "" { + link, port := p.RelPermalink(), p.Site().ServerPort() + lrl.Logf("navigating to %q using port %d", link, port) + livereload.NavigateToPathForPort(link, port) + } else { + lrl.Logf("no page to navigate to, force refresh") + livereload.ForceRefresh() + } + } else if len(otherChanges) > 0 || len(cssChanges) > 0 { + if len(otherChanges) == 1 { + // Allow single changes to be refreshed without a full page reload. + pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(otherChanges[0]), false) + lrl.Logf("refreshing %q", pathToRefresh) + livereload.RefreshPath(pathToRefresh) + } else if len(cssChanges) == 0 || len(otherChanges) > 1 { + lrl.Logf("force refresh") + livereload.ForceRefresh() + } + } else { + lrl.Logf("force refresh") + livereload.ForceRefresh() + } + + if len(cssChanges) > 0 { + // Allow some time for the live reload script to get reconnected. + if len(otherChanges) > 0 { + time.Sleep(200 * time.Millisecond) + } + for _, ev := range cssChanges { + pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(ev), false) + lrl.Logf("refreshing CSS %q", pathToRefresh) + livereload.RefreshPath(pathToRefresh) + } + } + } + } +} + +func (c *hugoBuilder) postBuild(what string, start time.Time) { + if h, err := c.hugo(); err == nil && h.Conf.Running() { + h.LogServerAddresses() + } + c.r.timeTrack(start, what) +} + +func (c *hugoBuilder) hugo() (*hugolib.HugoSites, error) { + var h *hugolib.HugoSites + if err := c.withConfE(func(conf *commonConfig) error { + var err error + h, err = c.r.HugFromConfig(conf) + return err + }); err != nil { + return nil, err + } + + if c.s != nil { + // A running server, register the media types. + for _, s := range h.Sites { + s.RegisterMediaTypes() + } + } + return h, nil +} + +func (c *hugoBuilder) hugoTry() *hugolib.HugoSites { + var h *hugolib.HugoSites + c.withConf(func(conf *commonConfig) { + h, _ = c.r.HugFromConfig(conf) + }) + return h +} + +func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error { + if terminal.PrintANSIColors(os.Stdout) { + defer c.progressHidden() + // If the configuration takes a while to load, we want to show some progress. + // This is typically loading of external modules. + d := debounce.New(500 * time.Millisecond) + d(func() { + c.progressIntermediate() + }) + defer d(func() {}) + } + + cfg := config.New() + cfg.Set("renderToMemory", c.r.renderToMemory) + watch := c.r.buildWatch || (c.s != nil && c.s.serverWatch) + // We need to set the environment as early as possible because we need it to load the correct config. + c.r.resolveEnvironment(c.s != nil) + cfg.Set("environment", c.r.environment) + + cfg.Set("internal", hmaps.Params{ + "running": running, + "watch": watch, + "verbose": c.r.isVerbose(), + "fastRenderMode": c.fastRenderMode, + }) + + conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, cfg)) + if err != nil { + return err + } + + if len(conf.configs.LoadingInfo.ConfigFiles) == 0 { + //lint:ignore ST1005 end user message. + return errors.New("Unable to locate config file or config directory. Perhaps you need to create a new project.\nRun `hugo help new` for details.") + } + + c.conf = conf + c.confOld = conf + if c.onConfigLoaded != nil { + if err := c.onConfigLoaded(false); err != nil { + return err + } + } + + return nil +} + +var rebuildCounter atomic.Uint64 + +func (c *hugoBuilder) printChangeDetected(typ string) { + msg := "\nChange" + if typ != "" { + msg += " of " + typ + } + msg += fmt.Sprintf(" detected, rebuilding site (#%d).", rebuildCounter.Add(1)) + + c.r.logger.Println(msg) + const layout = "2006-01-02 15:04:05.000 -0700" + c.r.logger.Println(htime.Now().Format(layout)) +} + +func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) { + defer func() { + c.errState.setBuildErr(err) + }() + if err := c.errState.buildErr(); err != nil { + ferrs := herrors.UnwrapFileErrorsWithErrorContext(err) + for _, err := range ferrs { + events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write}) + } + } + var h *hugolib.HugoSites + h, err = c.hugo() + if err != nil { + return + } + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...) + return +} + +func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) { + defer func() { + c.errState.setBuildErr(err) + }() + + var h *hugolib.HugoSites + h, err = c.hugo() + if err != nil { + return + } + whatChanged := &hugolib.WhatChanged{} + whatChanged.Add(ids...) + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}) + + return +} + +func (c *hugoBuilder) reloadConfig() error { + c.r.resetLogs() + c.r.configVersionID.Add(1) + + if err := c.withConfOrOldConfE(func(conf *commonConfig) error { + oldConf := conf + c.conf = nil + newConf, err := c.r.ConfigFromConfig(configKey{counter: c.r.configVersionID.Load()}, conf) + if err != nil { + return err + } + sameLen := len(oldConf.configs.Languages) == len(newConf.configs.Languages) + if !sameLen { + if oldConf.configs.IsMultihost || newConf.configs.IsMultihost { + return errors.New("multihost change detected, please restart server") + } + } + c.conf = newConf + return nil + }); err != nil { + return err + } + + if c.onConfigLoaded != nil { + if err := c.onConfigLoaded(true); err != nil { + return err + } + } + + return nil +} diff --git a/commands/import.go b/commands/import.go new file mode 100644 index 0000000..e07ebed --- /dev/null +++ b/commands/import.go @@ -0,0 +1,625 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "log" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + "unicode" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/common/htime" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/parser" + "github.com/gohugoio/hugo/parser/metadecoders" + "github.com/gohugoio/hugo/parser/pageparser" + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +func newImportCommand() *importCommand { + var c *importCommand + c = &importCommand{ + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "jekyll", + short: "hugo import from Jekyll", + long: `hugo import from Jekyll. + +Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + if len(args) < 2 { + return newUserError(`import from jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.") + } + return c.importFromJekyll(args) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.Flags().BoolVar(&c.force, "force", false, "allow import into non-empty target directory") + }, + }, + }, + } + + return c +} + +type importCommand struct { + r *rootCommand + + force bool + + commands []simplecobra.Commander +} + +func (c *importCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *importCommand) Name() string { + return "import" +} + +func (c *importCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + return nil +} + +func (c *importCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Import a project from another system" + cmd.Long = `Import a project from another system. + +Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`." + + cmd.RunE = nil + return nil +} + +func (c *importCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + return nil +} + +func (i *importCommand) createConfigFromJekyll(fs afero.Fs, inpath string, kind metadecoders.Format, jekyllConfig map[string]any) (err error) { + title := "My New Hugo Project" + baseURL := "http://example.org/" + + for key, value := range jekyllConfig { + lowerKey := strings.ToLower(key) + + switch lowerKey { + case "title": + if str, ok := value.(string); ok { + title = str + } + + case "url": + if str, ok := value.(string); ok { + baseURL = str + } + } + } + + in := map[string]any{ + "baseURL": baseURL, + "title": title, + "locale": "en-us", + "disablePathToLower": true, + } + + var buf bytes.Buffer + err = parser.InterfaceToConfig(in, kind, &buf) + if err != nil { + return err + } + + return helpers.WriteToDisk(filepath.Join(inpath, "hugo."+string(kind)), &buf, fs) +} + +func (c *importCommand) getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[string]bool, bool) { + postDirs := make(map[string]bool) + hasAnyPost := false + if entries, err := os.ReadDir(jekyllRoot); err == nil { + for _, entry := range entries { + if entry.IsDir() { + subDir := filepath.Join(jekyllRoot, entry.Name()) + if isPostDir, hasAnyPostInDir := c.retrieveJekyllPostDir(fs, subDir); isPostDir { + postDirs[entry.Name()] = hasAnyPostInDir + if hasAnyPostInDir { + hasAnyPost = true + } + } + } + } + } + return postDirs, hasAnyPost +} + +func (c *importCommand) createProjectFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool) error { + fs := &afero.OsFs{} + if exists, _ := helpers.Exists(targetDir, fs); exists { + if isDir, _ := helpers.IsDir(targetDir, fs); !isDir { + return errors.New("target path \"" + targetDir + "\" exists but is not a directory") + } + + isEmpty, _ := helpers.IsEmpty(targetDir, fs) + + if !isEmpty && !c.force { + return errors.New("target path \"" + targetDir + "\" exists and is not empty") + } + } + + jekyllConfig := c.loadJekyllConfig(fs, jekyllRoot) + + mkdir(targetDir, "layouts") + mkdir(targetDir, "content") + mkdir(targetDir, "archetypes") + mkdir(targetDir, "static") + mkdir(targetDir, "data") + mkdir(targetDir, "themes") + + c.createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig) + + c.copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"), jekyllPostDirs) + + return nil +} + +func (c *importCommand) convertJekyllContent(m any, content string) (string, error) { + metadata, _ := hmaps.ToStringMapE(m) + + lines := strings.Split(content, "\n") + var resultLines []string + for _, line := range lines { + resultLines = append(resultLines, strings.Trim(line, "\r\n")) + } + + content = strings.Join(resultLines, "\n") + + excerptSep := "" + if value, ok := metadata["excerpt_separator"]; ok { + if str, strOk := value.(string); strOk { + content = strings.Replace(content, strings.TrimSpace(str), excerptSep, -1) + } + } + + replaceList := []struct { + re *regexp.Regexp + replace string + }{ + {regexp.MustCompile("(?i)"), ""}, + {regexp.MustCompile(`\{%\s*raw\s*%\}\s*(.*?)\s*\{%\s*endraw\s*%\}`), "$1"}, + {regexp.MustCompile(`{%\s*endhighlight\s*%}`), "{{< / highlight >}}"}, + } + + for _, replace := range replaceList { + content = replace.re.ReplaceAllString(content, replace.replace) + } + + replaceListFunc := []struct { + re *regexp.Regexp + replace func(string) string + }{ + // Octopress image tag: http://octopress.org/docs/plugins/image-tag/ + {regexp.MustCompile(`{%\s+img\s*(.*?)\s*%}`), c.replaceImageTag}, + {regexp.MustCompile(`{%\s*highlight\s*(.*?)\s*%}`), c.replaceHighlightTag}, + } + + for _, replace := range replaceListFunc { + content = replace.re.ReplaceAllStringFunc(content, replace.replace) + } + + var buf bytes.Buffer + if len(metadata) != 0 { + err := parser.InterfaceToFrontMatter(m, metadecoders.YAML, &buf) + if err != nil { + return "", err + } + } + buf.WriteString(content) + + return buf.String(), nil +} + +func (c *importCommand) convertJekyllMetaData(m any, postName string, postDate time.Time, draft bool) (any, error) { + metadata, err := hmaps.ToStringMapE(m) + if err != nil { + return nil, err + } + + if draft { + metadata["draft"] = true + } + + for key, value := range metadata { + lowerKey := strings.ToLower(key) + + switch lowerKey { + case "layout": + delete(metadata, key) + case "permalink": + if str, ok := value.(string); ok { + metadata["url"] = str + } + delete(metadata, key) + case "category": + if str, ok := value.(string); ok { + metadata["categories"] = []string{str} + } + delete(metadata, key) + case "excerpt_separator": + if key != lowerKey { + delete(metadata, key) + metadata[lowerKey] = value + } + case "date": + if str, ok := value.(string); ok { + re := regexp.MustCompile(`(\d+):(\d+):(\d+)`) + r := re.FindAllStringSubmatch(str, -1) + if len(r) > 0 { + hour, _ := strconv.Atoi(r[0][1]) + minute, _ := strconv.Atoi(r[0][2]) + second, _ := strconv.Atoi(r[0][3]) + postDate = time.Date(postDate.Year(), postDate.Month(), postDate.Day(), hour, minute, second, 0, time.UTC) + } + } + delete(metadata, key) + } + + } + + metadata["date"] = postDate.Format(time.RFC3339) + + return metadata, nil +} + +func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft bool) error { + log.Println("Converting", path) + + filename := filepath.Base(path) + postDate, postName, err := c.parseJekyllFilename(filename) + if err != nil { + c.r.Printf("Failed to parse filename '%s': %s. Skipping.", filename, err) + return nil + } + + log.Println(filename, postDate, postName) + + targetFile := filepath.Join(targetDir, relPath) + targetParentDir := filepath.Dir(targetFile) + os.MkdirAll(targetParentDir, 0o777) + + contentBytes, err := os.ReadFile(path) + if err != nil { + c.r.logger.Errorln("Read file error:", path) + return err + } + pf, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(contentBytes)) + if err != nil { + return fmt.Errorf("failed to parse file %q: %s", filename, err) + } + newmetadata, err := c.convertJekyllMetaData(pf.FrontMatter, postName, postDate, draft) + if err != nil { + return fmt.Errorf("failed to convert metadata for file %q: %s", filename, err) + } + + content, err := c.convertJekyllContent(newmetadata, string(pf.Content)) + if err != nil { + return fmt.Errorf("failed to convert content for file %q: %s", filename, err) + } + + fs := hugofs.Os + if err := helpers.WriteToDisk(targetFile, strings.NewReader(content), fs); err != nil { + return fmt.Errorf("failed to save file %q: %s", filename, err) + } + return nil +} + +func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyllPostDirs map[string]bool) (err error) { + fs := hugofs.Os + + fi, err := fs.Stat(jekyllRoot) + if err != nil { + return err + } + if !fi.IsDir() { + return errors.New(jekyllRoot + " is not a directory") + } + err = os.MkdirAll(dest, fi.Mode()) + if err != nil { + return err + } + entries, err := os.ReadDir(jekyllRoot) + if err != nil { + return err + } + + for _, entry := range entries { + sfp := filepath.Join(jekyllRoot, entry.Name()) + dfp := filepath.Join(dest, entry.Name()) + if entry.IsDir() { + if entry.Name()[0] != '_' && entry.Name()[0] != '.' { + if _, ok := jekyllPostDirs[entry.Name()]; !ok { + err = hugio.CopyDir(fs, sfp, dfp, nil) + if err != nil { + c.r.logger.Errorln(err) + } + } + } + } else { + lowerEntryName := strings.ToLower(entry.Name()) + exceptSuffix := []string{ + ".md", ".markdown", ".html", ".htm", + ".xml", ".textile", "rakefile", "gemfile", ".lock", + } + isExcept := false + for _, suffix := range exceptSuffix { + if strings.HasSuffix(lowerEntryName, suffix) { + isExcept = true + break + } + } + + if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' { + err = hugio.CopyFile(fs, sfp, dfp) + if err != nil { + c.r.logger.Errorln(err) + } + } + } + + } + return nil +} + +func (c *importCommand) importFromJekyll(args []string) error { + jekyllRoot, err := filepath.Abs(filepath.Clean(args[0])) + if err != nil { + return newUserError("path error:", args[0]) + } + + targetDir, err := filepath.Abs(filepath.Clean(args[1])) + if err != nil { + return newUserError("path error:", args[1]) + } + + c.r.Println("Import Jekyll from:", jekyllRoot, "to:", targetDir) + + if strings.HasPrefix(filepath.Dir(targetDir), jekyllRoot) { + return newUserError("abort: target path should not be inside the Jekyll root") + } + + fs := afero.NewOsFs() + jekyllPostDirs, hasAnyPost := c.getJekyllDirInfo(fs, jekyllRoot) + if !hasAnyPost { + return errors.New("abort: jekyll root contains neither posts nor drafts") + } + + err = c.createProjectFromJekyll(jekyllRoot, targetDir, jekyllPostDirs) + if err != nil { + return newUserError(err) + } + + c.r.Println("Importing...") + + fileCount := 0 + callback := func(ctx context.Context, path string, fi hugofs.FileMetaInfo) error { + if fi.IsDir() { + return nil + } + + relPath, err := filepath.Rel(jekyllRoot, path) + if err != nil { + return newUserError("get rel path error:", path) + } + + relPath = filepath.ToSlash(relPath) + draft := false + + switch { + case strings.Contains(relPath, "_posts/"): + relPath = filepath.Join("content/post", strings.Replace(relPath, "_posts/", "", -1)) + case strings.Contains(relPath, "_drafts/"): + relPath = filepath.Join("content/draft", strings.Replace(relPath, "_drafts/", "", -1)) + draft = true + default: + return nil + } + + fileCount++ + return c.convertJekyllPost(path, relPath, targetDir, draft) + } + + for jekyllPostDir, hasAnyPostInDir := range jekyllPostDirs { + if hasAnyPostInDir { + if err = helpers.Walk(hugofs.Os, filepath.Join(jekyllRoot, jekyllPostDir), callback); err != nil { + return err + } + } + } + + c.r.Println("Congratulations!", fileCount, "post(s) imported!") + c.r.Println("Now, start Hugo by yourself:") + c.r.Println("cd " + args[1]) + c.r.Println("git init") + c.r.Println("git submodule add https://github.com/gohugo-ananke/ananke themes/ananke") + c.r.Println("echo \"theme: ananke\" >> hugo.yaml") + c.r.Println("hugo server") + + return nil +} + +func (c *importCommand) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]any { + for _, candidate := range []struct { + filename string + format metadecoders.Format + }{ + {"_config.yml", metadecoders.YAML}, + {"_config.yaml", metadecoders.YAML}, + {"_config.toml", metadecoders.TOML}, + } { + path := filepath.Join(jekyllRoot, candidate.filename) + exists, err := helpers.Exists(path, fs) + if err != nil || !exists { + continue + } + + f, err := fs.Open(path) + if err != nil { + continue + } + b, err := io.ReadAll(f) + f.Close() + if err != nil { + continue + } + + m, err := metadecoders.Default.UnmarshalToMap(b, candidate.format) + if err != nil { + continue + } + + return m + } + + c.r.Println("no config file (_config.yml, _config.yaml, or _config.toml) found: is the specified Jekyll root correct?") + return nil +} + +func (c *importCommand) parseJekyllFilename(filename string) (time.Time, string, error) { + re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`) + r := re.FindAllStringSubmatch(filename, -1) + if len(r) == 0 { + return htime.Now(), "", errors.New("filename not match") + } + + postDate, err := time.Parse("2006-1-2", r[0][1]) + if err != nil { + return htime.Now(), "", err + } + + postName := r[0][2] + + return postDate, postName, nil +} + +func (c *importCommand) replaceHighlightTag(match string) string { + r := regexp.MustCompile(`{%\s*highlight\s*(.*?)\s*%}`) + parts := r.FindStringSubmatch(match) + lastQuote := rune(0) + f := func(c rune) bool { + switch { + case c == lastQuote: + lastQuote = rune(0) + return false + case lastQuote != rune(0): + return false + case unicode.In(c, unicode.Quotation_Mark): + lastQuote = c + return false + default: + return unicode.IsSpace(c) + } + } + // splitting string by space but considering quoted section + items := strings.FieldsFunc(parts[1], f) + + result := bytes.NewBufferString("{{< highlight ") + result.WriteString(items[0]) // language + options := items[1:] + for i, opt := range options { + opt = strings.Replace(opt, "\"", "", -1) + if opt == "linenos" { + opt = "linenos=table" + } + if i == 0 { + opt = " \"" + opt + } + if i < len(options)-1 { + opt += "," + } else if i == len(options)-1 { + opt += "\"" + } + result.WriteString(opt) + } + + result.WriteString(" >}}") + return result.String() +} + +func (c *importCommand) replaceImageTag(match string) string { + r := regexp.MustCompile(`{%\s+img\s*(\p{L}*)\s+([\S]*/[\S]+)\s+(\d*)\s*(\d*)\s*(.*?)\s*%}`) + result := bytes.NewBufferString("{{< figure ") + parts := r.FindStringSubmatch(match) + // Index 0 is the entire string, ignore + c.replaceOptionalPart(result, "class", parts[1]) + c.replaceOptionalPart(result, "src", parts[2]) + c.replaceOptionalPart(result, "width", parts[3]) + c.replaceOptionalPart(result, "height", parts[4]) + // title + alt + part := parts[5] + if len(part) > 0 { + splits := strings.Split(part, "'") + lenSplits := len(splits) + if lenSplits == 1 { + c.replaceOptionalPart(result, "title", splits[0]) + } else if lenSplits == 3 { + c.replaceOptionalPart(result, "title", splits[1]) + } else if lenSplits == 5 { + c.replaceOptionalPart(result, "title", splits[1]) + c.replaceOptionalPart(result, "alt", splits[3]) + } + } + result.WriteString(">}}") + return result.String() +} + +func (c *importCommand) replaceOptionalPart(buffer *bytes.Buffer, partName string, part string) { + if len(part) > 0 { + buffer.WriteString(partName + "=\"" + part + "\" ") + } +} + +func (c *importCommand) retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool) { + if strings.HasSuffix(dir, "_posts") || strings.HasSuffix(dir, "_drafts") { + isEmpty, _ := helpers.IsEmpty(dir, fs) + return true, !isEmpty + } + + if entries, err := os.ReadDir(dir); err == nil { + for _, entry := range entries { + if entry.IsDir() { + subDir := filepath.Join(dir, entry.Name()) + if isPostDir, hasAnyPost := c.retrieveJekyllPostDir(fs, subDir); isPostDir { + return isPostDir, hasAnyPost + } + } + } + } + + return false, true +} diff --git a/commands/list.go b/commands/list.go new file mode 100644 index 0000000..42f3408 --- /dev/null +++ b/commands/list.go @@ -0,0 +1,213 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "encoding/csv" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/resources/resource" + "github.com/spf13/cobra" +) + +// newListCommand creates a new list command and its subcommands. +func newListCommand() *listCommand { + createRecord := func(workingDir string, p page.Page) []string { + return []string{ + filepath.ToSlash(strings.TrimPrefix(p.File().Filename(), workingDir+string(os.PathSeparator))), + p.Slug(), + p.Title(), + p.Date().Format(time.RFC3339), + p.ExpiryDate().Format(time.RFC3339), + p.PublishDate().Format(time.RFC3339), + strconv.FormatBool(p.Draft()), + p.Permalink(), + p.Kind(), + p.Section(), + } + } + + list := func(cd *simplecobra.Commandeer, r *rootCommand, shouldInclude func(page.Page) bool, opts ...any) error { + bcfg := hugolib.BuildCfg{SkipRender: true} + cfg := flagsToCfg(cd, nil) + for i := 0; i < len(opts); i += 2 { + cfg.Set(opts[i].(string), opts[i+1]) + } + h, err := r.Build(cd, bcfg, cfg) + if err != nil { + return err + } + + writer := csv.NewWriter(r.StdOut) + defer writer.Flush() + + writer.Write([]string{ + "path", + "slug", + "title", + "date", + "expiryDate", + "publishDate", + "draft", + "permalink", + "kind", + "section", + }) + + for _, p := range h.Pages() { + if shouldInclude(p) { + record := createRecord(h.Conf.BaseConfig().WorkingDir, p) + if err := writer.Write(record); err != nil { + return err + } + } + } + + return nil + } + + return &listCommand{ + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "drafts", + short: "List draft content", + long: `List draft content.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + shouldInclude := func(p page.Page) bool { + if !p.Draft() || p.File() == nil { + return false + } + return true + } + return list(cd, r, shouldInclude, + "buildDrafts", true, + "buildFuture", true, + "buildExpired", true, + ) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "future", + short: "List future content", + long: `List content with a future publication date.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + shouldInclude := func(p page.Page) bool { + if !resource.IsFuture(p) || p.File() == nil { + return false + } + return true + } + return list(cd, r, shouldInclude, + "buildFuture", true, + "buildDrafts", true, + ) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "expired", + short: "List expired content", + long: `List content with a past expiration date.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + shouldInclude := func(p page.Page) bool { + if !resource.IsExpired(p) || p.File() == nil { + return false + } + return true + } + return list(cd, r, shouldInclude, + "buildExpired", true, + "buildDrafts", true, + ) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "all", + short: "List all content", + long: `List all content including draft, future, and expired.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + shouldInclude := func(p page.Page) bool { + return p.File() != nil + } + return list(cd, r, shouldInclude, "buildDrafts", true, "buildFuture", true, "buildExpired", true) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + &simpleCommand{ + name: "published", + short: "List published content", + long: `List content that is not draft, future, or expired.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + shouldInclude := func(p page.Page) bool { + return !p.Draft() && !resource.IsFuture(p) && !resource.IsExpired(p) && p.File() != nil + } + return list(cd, r, shouldInclude) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + }, + }, + } +} + +type listCommand struct { + commands []simplecobra.Commander +} + +func (c *listCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *listCommand) Name() string { + return "list" +} + +func (c *listCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + // Do nothing. + return nil +} + +func (c *listCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "List content" + cmd.Long = `List content. + +List requires a subcommand, e.g. hugo list drafts` + + cmd.RunE = nil + return nil +} + +func (c *listCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + return nil +} diff --git a/commands/mod.go b/commands/mod.go new file mode 100644 index 0000000..eed9cfb --- /dev/null +++ b/commands/mod.go @@ -0,0 +1,354 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "errors" + "os" + "path/filepath" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/modules/npm" + "github.com/spf13/cobra" +) + +const commonUsageMod = ` +Note that Hugo will always start out by resolving the components defined in the project +configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided), +Go Modules, or a folder inside the themes directory, in that order. + +See https://gohugo.io/hugo-modules/ for more information. + +` + +// buildConfigCommands creates a new config command and its subcommands. +func newModCommands() *modCommands { + var ( + clean bool + pattern string + all bool + ) + + npmCommand := &simpleCommand{ + name: "npm", + short: "Various npm helpers", + long: `Various npm (Node package manager) helpers.`, + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "pack", + short: "Merges module Node.js dependencies into an npm workspace", + long: `Merges Node.js dependencies from all Hugo modules into a "packages/hugoautogen" npm workspace. + +The merged dependencies are written to packages/hugoautogen/package.json, and the root package.json +is updated with a "workspaces" entry pointing to "packages/hugoautogen". + +The source entries are read from either package.hugo.json or package.json in the module root, with package.hugo.json taking precedence if both exist. + +See [Node.js dependencies](/hugo-modules/nodejs-dependencies/) for more information. +`, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + cfg := flagsToCfg(cd, nil) + k := configKey{counter: r.configVersionID.Load(), skipNpmCheck: true} + h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { + conf, err := r.ConfigFromProvider(key, cfg) + if err != nil { + return nil, err + } + return hugolib.NewHugoSites(r.newDepsConfig(conf)) + }) + if err != nil { + return err + } + return npm.Pack(h.BaseFs.ProjectSourceFs, h.BaseFs.AssetsWithDuplicatesPreserved.Fs, h.Configs.Modules) + }, + }, + }, + } + + return &modCommands{ + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "init", + short: "Initialize this project as a Hugo Module", + long: `Initialize this project as a Hugo Module. + It will try to guess the module path, but you may help by passing it as an argument, e.g: + + hugo mod init github.com/gohugoio/testshortcodes + + Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module + inside a subfolder on GitHub, as one example. + `, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + h, err := r.getOrCreateHugo(flagsToCfg(cd, nil), true) + if err != nil { + return err + } + var initPath string + if len(args) >= 1 { + initPath = args[0] + } + c := h.Configs.ModulesClient + if err := c.Init(initPath); err != nil { + return err + } + return nil + }, + }, + &simpleCommand{ + name: "verify", + short: "Verify dependencies", + long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) + if err != nil { + return err + } + client := conf.configs.ModulesClient + return client.Verify(clean) + }, + }, + &simpleCommand{ + name: "graph", + short: "Print a module dependency graph", + long: `Print a module dependency graph with information about module status (disabled, vendored). +Note that for vendored modules, that is the version listed and not the one from go.mod. +`, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) + if err != nil { + return err + } + client := conf.configs.ModulesClient + return client.Graph(os.Stdout) + }, + }, + &simpleCommand{ + name: "clean", + short: "Delete the Hugo Module cache for the current project", + long: `Delete the Hugo Module cache for the current project.`, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`) + _ = cmd.RegisterFlagCompletionFunc("pattern", cobra.NoFileCompletions) + cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache") + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + h, err := r.Hugo(flagsToCfg(cd, nil)) + if err != nil { + return err + } + if all { + modCache := h.ResourceSpec.FileCaches.ModulesCache() + count, err := modCache.Prune(true) + r.Printf("Deleted %d directories from module cache.", count) + return err + } + + return h.Configs.ModulesClient.Clean(pattern) + }, + }, + &simpleCommand{ + name: "tidy", + short: "Remove unused entries in go.mod and go.sum", + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + h, err := r.Hugo(flagsToCfg(cd, nil)) + if err != nil { + return err + } + return h.Configs.ModulesClient.Tidy() + }, + }, + &simpleCommand{ + name: "vendor", + short: "Vendor all module dependencies into the _vendor directory", + long: `Vendor all module dependencies into the _vendor directory. + If a module is vendored, that is where Hugo will look for it's dependencies. + `, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + applyLocalFlagsBuildConfig(cmd, r) + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + h, err := r.Hugo(flagsToCfg(cd, nil)) + if err != nil { + return err + } + return h.Configs.ModulesClient.Vendor() + }, + }, + + &simpleCommand{ + name: "get", + short: "Resolves dependencies in your current Hugo project", + long: ` +Resolves dependencies in your current Hugo project. + +Some examples: + +Install the latest version possible for a given module: + + hugo mod get github.com/gohugoio/testshortcodes + +Install a specific version: + + hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 + +Install the latest versions of all direct module dependencies: + + hugo mod get + hugo mod get ./... (recursive) + +Install the latest versions of all module dependencies (direct and indirect): + + hugo mod get -u + hugo mod get -u ./... (recursive) + +Run "go help get" for more information. All flags available for "go get" is also relevant here. +` + commonUsageMod, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.DisableFlagParsing = true + cmd.ValidArgsFunction = cobra.NoFileCompletions + }, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + // We currently just pass on the flags we get to Go and + // need to do the flag handling manually. + if len(args) == 1 && (args[0] == "-h" || args[0] == "--help") { + return errHelp + } + + var lastArg string + if len(args) != 0 { + lastArg = args[len(args)-1] + } + + if lastArg == "./..." { + args = args[:len(args)-1] + // Do a recursive update. + dirname, err := os.Getwd() + if err != nil { + return err + } + + // Sanity chesimplecobra. We do recursive walking and want to avoid + // accidents. + if len(dirname) < 5 { + return errors.New("must not be run from the file system root") + } + + filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + if info.Name() == "go.mod" { + // Found a module. + dir := filepath.Dir(path) + + cfg := config.New() + cfg.Set("workingDir", dir) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Add(1)}, flagsToCfg(cd, cfg)) + if err != nil { + return err + } + r.Println("Update module in", conf.configs.Base.WorkingDir) + client := conf.configs.ModulesClient + return client.Get(args...) + + } + return nil + }) + return nil + } else { + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) + if err != nil { + return err + } + client := conf.configs.ModulesClient + if err := client.Get(args...); err != nil { + return err + } + return nil + } + }, + }, + npmCommand, + }, + } +} + +type modCommands struct { + r *rootCommand + + commands []simplecobra.Commander +} + +func (c *modCommands) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *modCommands) Name() string { + return "mod" +} + +func (c *modCommands) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + _, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, nil) + if err != nil { + return err + } + // config := conf.configs.Base + + return nil +} + +func (c *modCommands) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Manage modules" + cmd.Long = `Various helpers to help manage the modules in your project's dependency graph. +Most operations here requires a Go version installed on your system (>= Go 1.12) and the relevant VCS client (typically Git). +This is not needed if you only operate on modules inside /themes or if you have vendored them via "hugo mod vendor". + +` + commonUsageMod + cmd.RunE = nil + return nil +} + +func (c *modCommands) PreRun(cd, runner *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + return nil +} diff --git a/commands/new.go b/commands/new.go new file mode 100644 index 0000000..e82258a --- /dev/null +++ b/commands/new.go @@ -0,0 +1,230 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "path/filepath" + "strings" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/create" + "github.com/gohugoio/hugo/create/skeletons" + "github.com/spf13/cobra" +) + +func newNewCommand() *newCommand { + var ( + force bool + contentType string + format string + ) + + var c *newCommand + c = &newCommand{ + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "content", + use: "content [path]", + short: "Create new content", + long: `Create a new content file and automatically set the date and title. +It will guess which kind of file to create based on the path provided. + +You can also specify the kind with ` + "`-k KIND`" + `. + +If archetypes are provided in your theme or project, they will be used. + +Ensure you run this within the root directory of your project.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + if len(args) < 1 { + return newUserError("path needs to be provided") + } + cfg := flagsToCfg(cd, nil) + cfg.Set("BuildFuture", true) + h, err := r.Hugo(cfg) + if err != nil { + return err + } + return create.NewContent(h, contentType, args[0], force) + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return []string{}, cobra.ShellCompDirectiveNoFileComp + } + return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs + } + cmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create") + cmd.Flags().String("editor", "", "edit new content with this editor, if provided") + _ = cmd.RegisterFlagCompletionFunc("editor", cobra.NoFileCompletions) + cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists") + applyLocalFlagsBuildConfig(cmd, r) + }, + }, + &simpleCommand{ + name: "project", + use: "project [path]", + short: "Create a new project", + long: `Create a new project at the specified path.`, + aliases: []string{"site"}, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + if len(args) < 1 { + return newUserError("path needs to be provided") + } + createpath, err := filepath.Abs(filepath.Clean(args[0])) + if err != nil { + return err + } + + cfg := config.New() + cfg.Set("workingDir", createpath) + cfg.Set("publishDir", "public") + + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg)) + if err != nil { + return err + } + sourceFs := conf.fs.Source + + err = skeletons.CreateProject(createpath, sourceFs, force, format) + if err != nil { + return err + } + + r.Printf("Congratulations! Your new Hugo project was created in %s.\n\n", createpath) + r.Println(c.newProjectNextStepsText(createpath, format)) + + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return []string{}, cobra.ShellCompDirectiveNoFileComp + } + return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs + } + cmd.Flags().BoolVarP(&force, "force", "f", false, "init inside non-empty directory") + cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)") + _ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp)) + }, + }, + &simpleCommand{ + name: "theme", + use: "theme [name]", + short: "Create a new theme", + long: `Create a new theme with the specified name in the ./themes directory. +This generates a functional theme including template examples and sample content.`, + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + if len(args) < 1 { + return newUserError("theme name needs to be provided") + } + cfg := config.New() + cfg.Set("publishDir", "public") + + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg)) + if err != nil { + return err + } + sourceFs := conf.fs.Source + createpath := paths.AbsPathify(conf.configs.Base.WorkingDir, filepath.Join(conf.configs.Base.ThemesDir, args[0])) + r.Println("Creating new theme in", createpath) + + err = skeletons.CreateTheme(createpath, sourceFs, format) + if err != nil { + return err + } + + return nil + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return []string{}, cobra.ShellCompDirectiveNoFileComp + } + return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs + } + cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)") + _ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp)) + }, + }, + }, + } + + return c +} + +type newCommand struct { + rootCmd *rootCommand + + commands []simplecobra.Commander +} + +func (c *newCommand) Commands() []simplecobra.Commander { + return c.commands +} + +func (c *newCommand) Name() string { + return "new" +} + +func (c *newCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + return nil +} + +func (c *newCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Create new content" + cmd.Long = `Create a new content file and automatically set the date and title. +It will guess which kind of file to create based on the path provided. + +You can also specify the kind with ` + "`-k KIND`" + `. + +If archetypes are provided in your theme or project, they will be used. + +Ensure you run this within the root directory of your project.` + + cmd.RunE = nil + return nil +} + +func (c *newCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.rootCmd = cd.Root.Command.(*rootCommand) + return nil +} + +func (c *newCommand) newProjectNextStepsText(path string, format string) string { + format = strings.ToLower(format) + var nextStepsText bytes.Buffer + + nextStepsText.WriteString(`Just a few more steps... + +1. Change the current directory to ` + path + `. +2. Create or install a theme: + - Create a new theme with the command "hugo new theme " + - Or, install a theme from https://themes.gohugo.io/ +3. Edit hugo.` + format + `, setting the "theme" property to the theme name. +4. Create new content with the command "hugo new content `) + + nextStepsText.WriteString(filepath.Join("", ".")) + + nextStepsText.WriteString(`". +5. Start the embedded web server with the command "hugo server --buildDrafts". + +See documentation at https://gohugo.io/.`) + + return nextStepsText.String() +} diff --git a/commands/release.go b/commands/release.go new file mode 100644 index 0000000..ae97cad --- /dev/null +++ b/commands/release.go @@ -0,0 +1,55 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + + "github.com/bep/simplecobra" + "github.com/gohugoio/hugo/releaser" + "github.com/spf13/cobra" +) + +// Note: This is a command only meant for internal use and must be run +// via "go run -tags release main.go release" on the actual code base that is in the release. +func newReleaseCommand() simplecobra.Commander { + var ( + step int + skipPush bool + try bool + version string + ) + + return &simpleCommand{ + name: "release", + short: "Release a new version of Hugo", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + rel, err := releaser.New(skipPush, try, step, version) + if err != nil { + return err + } + + return rel.Run() + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.Hidden = true + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.PersistentFlags().BoolVarP(&skipPush, "skip-push", "", false, "skip pushing to remote") + cmd.PersistentFlags().BoolVarP(&try, "try", "", false, "no changes") + cmd.PersistentFlags().IntVarP(&step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)") + cmd.PersistentFlags().StringVarP(&version, "version", "", "", "version to release (derived from branch name if not set)") + _ = cmd.RegisterFlagCompletionFunc("step", cobra.FixedCompletions([]string{"1", "2"}, cobra.ShellCompDirectiveNoFileComp)) + }, + } +} diff --git a/commands/server.go b/commands/server.go new file mode 100644 index 0000000..045c375 --- /dev/null +++ b/commands/server.go @@ -0,0 +1,1261 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" + "maps" + "net" + "net/http" + _ "net/http/pprof" + "net/url" + "os" + "os/signal" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/bep/mclib" + "github.com/pkg/browser" + + "github.com/bep/debounce" + "github.com/bep/simplecobra" + "github.com/fsnotify/fsnotify" + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/langs" + "github.com/gohugoio/hugo/tpl/tplimpl" + + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/common/urls" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/livereload" + "github.com/gohugoio/hugo/transform" + "github.com/gohugoio/hugo/transform/livereloadinject" + "github.com/spf13/afero" + "github.com/spf13/cobra" + "github.com/spf13/fsync" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +var ( + logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`) + logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`) +) + +var logReplacer = strings.NewReplacer( + "can't", "can’t", // Chroma lexer doesn't do well with "can't" + "*hugolib.pageState", "page.Page", // Page is the public interface. + "Rebuild failed:", "", +) + +const ( + configChangeConfig = "config file" + configChangeGoMod = "go.mod file" + configChangeGoWork = "go work file" +) + +const ( + hugoHeaderRedirect = "X-Hugo-Redirect" +) + +func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder { + var visitedURLs *types.EvictingQueue[string] + if s != nil && !s.disableFastRender { + visitedURLs = types.NewEvictingQueue[string](20) + } + return &hugoBuilder{ + r: r, + s: s, + visitedURLs: visitedURLs, + fullRebuildSem: semaphore.NewWeighted(1), + debounce: debounce.New(4 * time.Second), + onConfigLoaded: func(reloaded bool) error { + for _, wc := range onConfigLoaded { + if err := wc(reloaded); err != nil { + return err + } + } + return nil + }, + } +} + +func newServerCommand() *serverCommand { + // Flags. + var uninstall bool + + c := &serverCommand{ + quit: make(chan bool), + commands: []simplecobra.Commander{ + &simpleCommand{ + name: "trust", + short: "Install the local CA in the system trust store", + run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + action := "-install" + if uninstall { + action = "-uninstall" + } + os.Args = []string{action} + return mclib.RunMain() + }, + withc: func(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.Flags().BoolVar(&uninstall, "uninstall", false, "Uninstall the local CA (but do not delete it).") + }, + }, + }, + } + + return c +} + +func (c *serverCommand) Commands() []simplecobra.Commander { + return c.commands +} + +type countingStatFs struct { + afero.Fs + statCounter uint64 +} + +func (fs *countingStatFs) Stat(name string) (os.FileInfo, error) { + f, err := fs.Fs.Stat(name) + if err == nil { + if !f.IsDir() { + atomic.AddUint64(&fs.statCounter, 1) + } + } + return f, err +} + +// dynamicEvents contains events that is considered dynamic, as in "not static". +// Both of these categories will trigger a new build, but the asset events +// does not fit into the "navigate to changed" logic. +type dynamicEvents struct { + ContentEvents []fsnotify.Event + AssetEvents []fsnotify.Event +} + +type fileChangeDetector struct { + sync.Mutex + current map[string]uint64 + prev map[string]uint64 + + irrelevantRe *regexp.Regexp +} + +func (f *fileChangeDetector) OnFileClose(name string, checksum uint64) { + f.Lock() + defer f.Unlock() + f.current[name] = checksum +} + +func (f *fileChangeDetector) PrepareNew() { + if f == nil { + return + } + + f.Lock() + defer f.Unlock() + + if f.current == nil { + f.current = make(map[string]uint64) + f.prev = make(map[string]uint64) + return + } + + f.prev = make(map[string]uint64) + maps.Copy(f.prev, f.current) + f.current = make(map[string]uint64) +} + +func (f *fileChangeDetector) changed() []string { + if f == nil { + return nil + } + f.Lock() + defer f.Unlock() + var c []string + for k, v := range f.current { + vv, found := f.prev[k] + if !found || v != vv { + c = append(c, k) + } + } + + return f.filterIrrelevantAndSort(c) +} + +func (f *fileChangeDetector) filterIrrelevantAndSort(in []string) []string { + var filtered []string + for _, v := range in { + if !f.irrelevantRe.MatchString(v) { + filtered = append(filtered, v) + } + } + sort.Strings(filtered) + return filtered +} + +type fileServer struct { + baseURLs []urls.BaseURL + roots []string + errorTemplate func(err any) (io.Reader, error) + c *serverCommand +} + +func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string, string, error) { + r := f.c.r + baseURL := f.baseURLs[i] + root := f.roots[i] + port := f.c.serverPorts[i].p + listener := f.c.serverPorts[i].ln + logger := f.c.r.logger + + if i == 0 { + r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment()) + mainTarget := "disk" + if f.c.r.renderToMemory { + mainTarget = "memory" + } + if f.c.renderStaticToDisk { + r.Printf("Serving pages from %s and static files from disk\n", mainTarget) + } else { + r.Printf("Serving pages from %s\n", mainTarget) + } + } + + var httpFs *afero.HttpFs + f.c.withConf(func(conf *commonConfig) { + httpFs = afero.NewHttpFs(conf.fs.PublishDirServer) + }) + + fs := filesOnlyFs{httpFs.Dir(path.Join("/", root))} + if i == 0 && f.c.fastRenderMode { + r.Println("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender") + } + + decorate := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if f.c.showErrorInBrowser { + // First check the error state + err := f.c.getErrorWithContext() + if err != nil { + f.c.errState.setWasErr(true) + w.WriteHeader(500) + r, err := f.errorTemplate(err) + if err != nil { + logger.Errorln(err) + } + + port = 1313 + f.c.withConfOrOldConf(func(conf *commonConfig) { + if lrport := conf.configs.GetFirstLanguageConfig().BaseURLLiveReload().Port(); lrport != 0 { + port = lrport + } + }) + lr := baseURL.URL() + lr.Host = fmt.Sprintf("%s:%d", lr.Hostname(), port) + fmt.Fprint(w, injectLiveReloadScript(r, lr)) + + return + } + } + + if f.c.noHTTPCache { + w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") + w.Header().Set("Pragma", "no-cache") + } + + var serverConfig config.Server + f.c.withConf(func(conf *commonConfig) { + serverConfig = conf.configs.Base.Server + }) + + // Ignore any query params for the operations below. + requestURI, _ := url.PathUnescape(strings.TrimSuffix(r.RequestURI, "?"+r.URL.RawQuery)) + + for _, header := range serverConfig.MatchHeaders(requestURI) { + w.Header().Set(header.Key, header.Value) + } + + if canRedirect(requestURI, r) { + if redirect := serverConfig.MatchRedirect(requestURI, r.Header); !redirect.IsZero() { + doRedirect := true + // This matches Netlify's behavior and is needed for SPA behavior. + // See https://docs.netlify.com/routing/redirects/rewrites-proxies/ + if !redirect.Force { + path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path())) + if root != "" { + path = filepath.Join(root, path) + } + var fs afero.Fs + f.c.withConf(func(conf *commonConfig) { + fs = conf.fs.PublishDirServer + }) + + fi, err := fs.Stat(path) + + if err == nil { + if fi.IsDir() { + // There will be overlapping directories, so we + // need to check for a file. + _, err = fs.Stat(filepath.Join(path, "index.html")) + doRedirect = err != nil + } else { + doRedirect = false + } + } + } + + if doRedirect { + w.Header().Set(hugoHeaderRedirect, "true") + switch redirect.Status { + case 404: + w.WriteHeader(404) + file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path())) + if err == nil { + defer file.Close() + io.Copy(w, file) + } else { + fmt.Fprintln(w, "

Page Not Found

") + } + return + case 200: + if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil { + requestURI = redirect.To + r = r2 + } + default: + w.Header().Set("Content-Type", "") + http.Redirect(w, r, redirect.To, redirect.Status) + return + + } + } + } + } + + if f.c.fastRenderMode && f.c.errState.buildErr() == nil { + if isNavigation(requestURI, r) { + // See issue 14240. + // Hugo escapes the URL paths when generating them, + // that may not be the case when we receive it back from the browser. + // PathEscape will escape if it is not already escaped. + requestURI = paths.PathEscape(requestURI) + + if !f.c.visitedURLs.Contains(requestURI) { + // If not already on stack, re-render that single page. + if err := f.c.partialReRender(requestURI); err != nil { + f.c.handleBuildErr(err, fmt.Sprintf("Failed to render %q", requestURI)) + if f.c.showErrorInBrowser { + http.Redirect(w, r, requestURI, http.StatusMovedPermanently) + return + } + } + } + + f.c.visitedURLs.Add(requestURI) + + } + } + + h.ServeHTTP(w, r) + }) + } + + fileserver := decorate(http.FileServer(fs)) + mu := http.NewServeMux() + if baseURL.Path() == "" || baseURL.Path() == "/" { + mu.Handle("/", fileserver) + } else { + mu.Handle(baseURL.Path(), http.StripPrefix(baseURL.Path(), fileserver)) + } + if r.IsTestRun() { + var shutDownOnce sync.Once + mu.HandleFunc("/__stop", func(w http.ResponseWriter, r *http.Request) { + shutDownOnce.Do(func() { + close(f.c.quit) + }) + }) + } + + endpoint := net.JoinHostPort(f.c.serverInterface, strconv.Itoa(port)) + + return mu, listener, baseURL.String(), endpoint, nil +} + +func (f *fileServer) rewriteRequest(r *http.Request, toPath string) *http.Request { + r2 := new(http.Request) + *r2 = *r + r2.URL = new(url.URL) + *r2.URL = *r.URL + r2.URL.Path = toPath + r2.Header.Set("X-Rewrite-Original-URI", r.URL.RequestURI()) + + return r2 +} + +type filesOnlyFs struct { + fs http.FileSystem +} + +func (fs filesOnlyFs) Open(name string) (http.File, error) { + f, err := fs.fs.Open(name) + if err != nil { + return nil, err + } + return noDirFile{f}, nil +} + +type noDirFile struct { + http.File +} + +func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) { + return nil, nil +} + +type serverCommand struct { + r *rootCommand + + commands []simplecobra.Commander + + *hugoBuilder + + quit chan bool // Closed when the server should shut down. Used in tests only. + serverPorts []serverPortListener + doLiveReload bool + + // Flags. + renderStaticToDisk bool + navigateToChanged bool + openBrowser bool + serverAppend bool + serverInterface string + tlsCertFile string + tlsKeyFile string + tlsAuto bool + pprof bool + serverPort int + liveReloadPort int + serverWatch bool + noHTTPCache bool + disableLiveReload bool + disableFastRender bool + disableBrowserError bool +} + +func (c *serverCommand) Name() string { + return "server" +} + +func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { + if c.pprof { + go func() { + http.ListenAndServe("localhost:8080", nil) + }() + } + // Watch runs its own server as part of the routine + if c.serverWatch { + + watchDirs, err := c.getDirList() + if err != nil { + return err + } + + watchGroups := helpers.ExtractAndGroupRootPaths(watchDirs) + + c.r.Printf("Watching for changes in %s\n", strings.Join(watchGroups, ", ")) + watcher, err := c.newWatcher(c.r.poll, watchDirs...) + if err != nil { + return err + } + + defer watcher.Close() + + } + + err := func() error { + defer c.r.timeTrack(time.Now(), "Built") + return c.build() + }() + if err != nil { + return err + } + + return c.serve() +} + +func (c *serverCommand) Init(cd *simplecobra.Commandeer) error { + cmd := cd.CobraCommand + cmd.Short = "Start the embedded web server" + cmd.Long = `Hugo provides its own webserver which builds and serves the project. +While hugo server is high performance, it is a webserver with limited options. + +The ` + "`" + `hugo server` + "`" + ` command will by default write and serve files from disk, but +you can render to memory by using the ` + "`" + `--renderToMemory` + "`" + ` flag. This can be +faster in some cases, but it will consume more memory. + +By default hugo will also watch your files for any changes you make and +automatically rebuild the project. It will then live reload any open browser pages +and push the latest content to them. As most Hugo projects are built in a fraction +of a second, you will be able to save and see your changes nearly instantly.` + cmd.Aliases = []string{"serve"} + + cmd.Flags().IntVarP(&c.serverPort, "port", "p", 1313, "port on which the server will listen") + _ = cmd.RegisterFlagCompletionFunc("port", cobra.NoFileCompletions) + cmd.Flags().IntVar(&c.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)") + _ = cmd.RegisterFlagCompletionFunc("liveReloadPort", cobra.NoFileCompletions) + cmd.Flags().StringVarP(&c.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind") + _ = cmd.RegisterFlagCompletionFunc("bind", cobra.NoFileCompletions) + cmd.Flags().StringVarP(&c.tlsCertFile, "tlsCertFile", "", "", "path to TLS certificate file") + _ = cmd.MarkFlagFilename("tlsCertFile", "pem") + cmd.Flags().StringVarP(&c.tlsKeyFile, "tlsKeyFile", "", "", "path to TLS key file") + _ = cmd.MarkFlagFilename("tlsKeyFile", "pem") + cmd.Flags().BoolVar(&c.tlsAuto, "tlsAuto", false, "generate and use locally-trusted certificates.") + cmd.Flags().BoolVar(&c.pprof, "pprof", false, "enable the pprof server (port 8080)") + cmd.Flags().BoolVarP(&c.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed") + cmd.Flags().BoolVar(&c.noHTTPCache, "noHTTPCache", false, "disable browser caching of pages served by the embedded web server") + cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL") + cmd.Flags().BoolVar(&c.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild") + cmd.Flags().BoolVarP(&c.navigateToChanged, "navigateToChanged", "N", false, "navigate to changed content file on live browser reload") + cmd.Flags().BoolVarP(&c.openBrowser, "openBrowser", "O", false, "open the project in a browser after server startup") + cmd.Flags().BoolVar(&c.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory") + cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes") + cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser") + + r := cd.Root.Command.(*rootCommand) + applyLocalFlagsBuild(cmd, r) + + return nil +} + +func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error { + c.r = cd.Root.Command.(*rootCommand) + + c.hugoBuilder = newHugoBuilder( + c.r, + c, + func(reloaded bool) error { + if !reloaded { + if err := c.createServerPorts(cd); err != nil { + return err + } + + if (c.tlsCertFile == "" || c.tlsKeyFile == "") && c.tlsAuto { + c.withConfE(func(conf *commonConfig) error { + return c.createCertificates(conf) + }) + } + } + + if err := c.setServerInfoInConfig(); err != nil { + return err + } + + if !reloaded && c.fastRenderMode { + c.withConf(func(conf *commonConfig) { + conf.fs.PublishDir = hugofs.NewHashingFs(conf.fs.PublishDir, c.changeDetector) + conf.fs.PublishDirStatic = hugofs.NewHashingFs(conf.fs.PublishDirStatic, c.changeDetector) + }) + } + + return nil + }, + ) + + destinationFlag := cd.CobraCommand.Flags().Lookup("destination") + if c.r.renderToMemory && (destinationFlag != nil && destinationFlag.Changed) { + return fmt.Errorf("cannot use --renderToMemory with --destination") + } + c.doLiveReload = !c.disableLiveReload + c.fastRenderMode = !c.disableFastRender + c.showErrorInBrowser = c.doLiveReload && !c.disableBrowserError + + if c.fastRenderMode { + // For now, fast render mode only. It should, however, be fast enough + // for the full variant, too. + c.changeDetector = &fileChangeDetector{ + // We use this detector to decide to do a Hot reload of a single path or not. + // We need to filter out source maps and possibly some other to be able + // to make that decision. + irrelevantRe: regexp.MustCompile(`\.map$`), + } + + c.changeDetector.PrepareNew() + + } + + err := c.loadConfig(cd, true) + if err != nil { + return err + } + + return nil +} + +func (c *serverCommand) setServerInfoInConfig() error { + if len(c.serverPorts) == 0 { + panic("no server ports set") + } + return c.withConfE(func(conf *commonConfig) error { + for i, language := range conf.configs.Languages { + isMultihost := conf.configs.IsMultihost + var serverPort int + if isMultihost { + serverPort = c.serverPorts[i].p + } else { + serverPort = c.serverPorts[0].p + } + langConfig := conf.configs.LanguageConfigMap[language.Lang] + baseURLStr, err := c.fixURL(langConfig.BaseURL, c.r.baseURL, serverPort) + if err != nil { + return err + } + baseURL, err := urls.NewBaseURLFromString(baseURLStr) + if err != nil { + return fmt.Errorf("failed to create baseURL from %q: %s", baseURLStr, err) + } + + baseURLLiveReload := baseURL + if c.liveReloadPort != -1 { + baseURLLiveReload, _ = baseURLLiveReload.WithPort(c.liveReloadPort) + } + langConfig.C.SetServerInfo(baseURL, baseURLLiveReload, c.serverInterface) + + } + return nil + }) +} + +func (c *serverCommand) getErrorWithContext() any { + buildErr := c.errState.buildErr() + if buildErr == nil { + return nil + } + + m := make(map[string]any) + + m["Error"] = cleanErrorLog(c.r.logger.Errors()) + + m["Version"] = hugo.BuildVersionString() + ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr) + m["Files"] = ferrors + + return m +} + +func (c *serverCommand) createCertificates(conf *commonConfig) error { + hostname := "localhost" + if c.r.baseURL != "" { + u, err := url.Parse(c.r.baseURL) + if err != nil { + return err + } + hostname = u.Hostname() + } + + // For now, store these in the Hugo cache dir. + // Hugo should probably introduce some concept of a less temporary application directory. + keyDir := filepath.Join(conf.configs.LoadingInfo.BaseConfig.CacheDir, "_mkcerts") + + // Create the directory if it doesn't exist. + if _, err := os.Stat(keyDir); os.IsNotExist(err) { + if err := os.MkdirAll(keyDir, 0o777); err != nil { + return err + } + } + + c.tlsCertFile = filepath.Join(keyDir, fmt.Sprintf("%s.pem", hostname)) + c.tlsKeyFile = filepath.Join(keyDir, fmt.Sprintf("%s-key.pem", hostname)) + + // Check if the certificate already exists and is valid. + certPEM, err := os.ReadFile(c.tlsCertFile) + if err == nil { + rootPem, err := os.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem")) + if err == nil { + if err := c.verifyCert(rootPem, certPEM, hostname); err == nil { + c.r.Println("Using existing", c.tlsCertFile, "and", c.tlsKeyFile) + return nil + } + } + } + + c.r.Println("Creating TLS certificates in", keyDir) + + // Yes, this is unfortunate, but it's currently the only way to use Mkcert as a library. + os.Args = []string{"-cert-file", c.tlsCertFile, "-key-file", c.tlsKeyFile, hostname} + return mclib.RunMain() +} + +func (c *serverCommand) verifyCert(rootPEM, certPEM []byte, name string) error { + roots := x509.NewCertPool() + ok := roots.AppendCertsFromPEM(rootPEM) + if !ok { + return fmt.Errorf("failed to parse root certificate") + } + + block, _ := pem.Decode(certPEM) + if block == nil { + return fmt.Errorf("failed to parse certificate PEM") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return fmt.Errorf("failed to parse certificate: %v", err.Error()) + } + + opts := x509.VerifyOptions{ + DNSName: name, + Roots: roots, + } + + if _, err := cert.Verify(opts); err != nil { + return fmt.Errorf("failed to verify certificate: %v", err.Error()) + } + + return nil +} + +func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error { + flags := cd.CobraCommand.Flags() + var cerr error + c.withConf(func(conf *commonConfig) { + isMultihost := conf.configs.IsMultihost + c.serverPorts = make([]serverPortListener, 1) + if isMultihost { + if !c.serverAppend { + cerr = errors.New("--appendPort=false not supported when in multihost mode") + return + } + c.serverPorts = make([]serverPortListener, len(conf.configs.Languages)) + } + currentServerPort := c.serverPort + for i := range c.serverPorts { + l, err := net.Listen("tcp", net.JoinHostPort(c.serverInterface, strconv.Itoa(currentServerPort))) + if err == nil { + c.serverPorts[i] = serverPortListener{ln: l, p: currentServerPort} + } else { + if i == 0 && flags.Changed("port") { + // port set explicitly by user -- he/she probably meant it! + cerr = fmt.Errorf("server startup failed: %s", err) + return + } + c.r.Println("port", currentServerPort, "already in use, attempting to use an available port") + l, sp, err := helpers.TCPListen() + if err != nil { + cerr = fmt.Errorf("unable to find alternative port to use: %s", err) + return + } + c.serverPorts[i] = serverPortListener{ln: l, p: sp.Port} + } + + currentServerPort = c.serverPorts[i].p + 1 + } + }) + + return cerr +} + +// fixURL massages the baseURL into a form needed for serving +// all pages correctly. +func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port int) (string, error) { + certsSet := (c.tlsCertFile != "" && c.tlsKeyFile != "") || c.tlsAuto + useLocalhost := false + baseURL := baseURLFromFlag + if baseURL == "" { + baseURL = baseURLFromConfig + useLocalhost = true + } + + if !strings.HasSuffix(baseURL, "/") { + baseURL = baseURL + "/" + } + + // do an initial parse of the input string + u, err := url.Parse(baseURL) + if err != nil { + return "", err + } + + // if no Host is defined, then assume that no schema or double-slash were + // present in the url. Add a double-slash and make a best effort attempt. + if u.Host == "" && baseURL != "/" { + baseURL = "//" + baseURL + + u, err = url.Parse(baseURL) + if err != nil { + return "", err + } + } + + if useLocalhost { + if certsSet { + u.Scheme = "https" + } else if u.Scheme == "https" { + u.Scheme = "http" + } + u.Host = "localhost" + } + + if c.serverAppend { + if strings.Contains(u.Host, ":") { + u.Host, _, err = net.SplitHostPort(u.Host) + if err != nil { + return "", fmt.Errorf("failed to split baseURL hostport: %w", err) + } + } + u.Host += fmt.Sprintf(":%d", port) + } + + return u.String(), nil +} + +func (c *serverCommand) partialReRender(urls ...string) (err error) { + defer func() { + c.errState.setWasErr(false) + }() + visited := types.NewEvictingQueue[string](len(urls)) + for _, url := range urls { + visited.Add(url) + } + + var h *hugolib.HugoSites + h, err = c.hugo() + if err != nil { + return + } + + // Note: We do not set NoBuildLock as the file lock is not acquired at this stage. + err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyTouched: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()}) + + return +} + +func (c *serverCommand) serve() error { + var ( + baseURLs []urls.BaseURL + roots []string + h *hugolib.HugoSites + ) + err := c.withConfE(func(conf *commonConfig) error { + isMultihost := conf.configs.IsMultihost + var err error + h, err = c.r.HugFromConfig(conf) + if err != nil { + return err + } + + // We need the server to share the same logger as the Hugo build (for error counts etc.) + c.r.logger = h.Log + + if isMultihost { + for _, l := range conf.configs.ConfigLangs() { + baseURLs = append(baseURLs, l.BaseURL()) + roots = append(roots, l.Language().(*langs.Language).Lang) + } + } else { + l := conf.configs.GetFirstLanguageConfig() + baseURLs = []urls.BaseURL{l.BaseURL()} + roots = []string{""} + } + + return nil + }) + if err != nil { + return err + } + + // Cache it here. The HugoSites object may be unavailable later on due to intermittent configuration errors. + // To allow the en user to change the error template while the server is running, we use + // the freshest template we can provide. + var ( + errTempl *tplimpl.TemplInfo + templHandler *tplimpl.TemplateStore + ) + getErrorTemplateAndHandler := func(h *hugolib.HugoSites) (*tplimpl.TemplInfo, *tplimpl.TemplateStore) { + if h == nil { + return errTempl, templHandler + } + templHandler := h.GetTemplateStore() + errTempl := templHandler.LookupByPath("/_server/error.html") + if errTempl == nil { + panic("template server/error.html not found") + } + return errTempl, templHandler + } + errTempl, templHandler = getErrorTemplateAndHandler(h) + + srv := &fileServer{ + baseURLs: baseURLs, + roots: roots, + c: c, + errorTemplate: func(ctx any) (io.Reader, error) { + // hugoTry does not block, getErrorTemplateAndHandler will fall back + // to cached values if nil. + templ, handler := getErrorTemplateAndHandler(c.hugoTry()) + b := &bytes.Buffer{} + err := handler.ExecuteWithContext(context.Background(), templ, b, ctx) + return b, err + }, + } + + doLiveReload := !c.disableLiveReload + + if doLiveReload { + livereload.Initialize() + } + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + var servers []*http.Server + + wg1, ctx := errgroup.WithContext(context.Background()) + + for i := range baseURLs { + mu, listener, serverURL, endpoint, err := srv.createEndpoint(i) + var srv *http.Server + if c.tlsCertFile != "" && c.tlsKeyFile != "" { + srv = &http.Server{ + Addr: endpoint, + Handler: mu, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + }, + } + } else { + srv = &http.Server{ + Addr: endpoint, + Handler: mu, + } + } + + servers = append(servers, srv) + + if doLiveReload { + baseURL := baseURLs[i] + mu.HandleFunc(baseURL.Path()+"livereload.js", livereload.ServeJS) + mu.HandleFunc(baseURL.Path()+"livereload", livereload.Handler) + } + c.r.Printf("Web Server is available at %s (bind address %s) %s\n", serverURL, c.serverInterface, roots[i]) + wg1.Go(func() error { + if c.tlsCertFile != "" && c.tlsKeyFile != "" { + err = srv.ServeTLS(listener, c.tlsCertFile, c.tlsKeyFile) + } else { + err = srv.Serve(listener) + } + if err != nil && err != http.ErrServerClosed { + return err + } + return nil + }) + } + + if c.r.IsTestRun() { + // Write a .ready file to disk to signal ready status. + // This is where the test is run from. + var baseURLs []string + for _, baseURL := range srv.baseURLs { + baseURLs = append(baseURLs, baseURL.String()) + } + testInfo := map[string]any{ + "baseURLs": baseURLs, + } + + dir := os.Getenv("WORK") + if dir != "" { + readyFile := filepath.Join(dir, ".ready") + // encode the test info as JSON into the .ready file. + b, err := json.Marshal(testInfo) + if err != nil { + return err + } + err = os.WriteFile(readyFile, b, 0o777) + if err != nil { + return err + } + } + + } + + c.r.Println("Press Ctrl+C to stop") + + if c.openBrowser { + // There may be more than one baseURL in multihost mode, open the first. + if err := browser.OpenURL(baseURLs[0].String()); err != nil { + c.r.logger.Warnf("Failed to open browser: %s", err) + } + } + + err = func() error { + for { + select { + case <-c.quit: + return nil + case <-sigs: + return nil + case <-ctx.Done(): + return ctx.Err() + } + } + }() + if err != nil { + c.r.Println("Error:", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + wg2, ctx := errgroup.WithContext(ctx) + for _, srv := range servers { + wg2.Go(func() error { + return srv.Shutdown(ctx) + }) + } + + err1, err2 := wg1.Wait(), wg2.Wait() + if err1 != nil { + return err1 + } + return err2 +} + +type serverPortListener struct { + p int + ln net.Listener +} + +type staticSyncer struct { + c *hugoBuilder +} + +func (s *staticSyncer) isStatic(h *hugolib.HugoSites, filename string) bool { + return h.BaseFs.SourceFilesystems.IsStatic(filename) +} + +func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error { + c := s.c + + syncFn := func(sourceFs *filesystems.SourceFilesystem) (uint64, error) { + publishDir := helpers.FilePathSeparator + + if sourceFs.PublishFolder != "" { + publishDir = filepath.Join(publishDir, sourceFs.PublishFolder) + } + + syncer := fsync.NewSyncer() + c.withConf(func(conf *commonConfig) { + syncer.NoTimes = conf.configs.Base.NoTimes + syncer.NoChmod = conf.configs.Base.NoChmod + syncer.ChmodFilter = chmodFilter + syncer.SrcFs = sourceFs.Fs + syncer.DestFs = conf.fs.PublishDir + if c.s != nil && c.s.renderStaticToDisk { + syncer.DestFs = conf.fs.PublishDirStatic + } + }) + + logger := s.c.r.logger + + for _, ev := range staticEvents { + // Due to our approach of layering both directories and the content's rendered output + // into one we can't accurately remove a file not in one of the source directories. + // If a file is in the local static dir and also in the theme static dir and we remove + // it from one of those locations we expect it to still exist in the destination + // + // If Hugo generates a file (from the content dir) over a static file + // the content generated file should take precedence. + // + // Because we are now watching and handling individual events it is possible that a static + // event that occupies the same path as a content generated file will take precedence + // until a regeneration of the content takes places. + // + // Hugo assumes that these cases are very rare and will permit this bad behavior + // The alternative is to track every single file and which pipeline rendered it + // and then to handle conflict resolution on every event. + + fromPath := ev.Name + + relPath, found := sourceFs.MakePathRelative(fromPath, true) + + if !found { + // Not member of this virtual host. + continue + } + + // Remove || rename is harder and will require an assumption. + // Hugo takes the following approach: + // If the static file exists in any of the static source directories after this event + // Hugo will re-sync it. + // If it does not exist in all of the static directories Hugo will remove it. + // + // This assumes that Hugo has not generated content on top of a static file and then removed + // the source of that static file. In this case Hugo will incorrectly remove that file + // from the published directory. + if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove { + if _, err := sourceFs.Fs.Stat(relPath); herrors.IsNotExist(err) { + // If file doesn't exist in any static dir, remove it + logger.Println("File no longer exists in static dir, removing", relPath) + c.withConf(func(conf *commonConfig) { + _ = conf.fs.PublishDirStatic.RemoveAll(relPath) + }) + + } else if err == nil { + // If file still exists, sync it + logger.Println("Syncing", relPath, "to", publishDir) + + if err := syncer.Sync(relPath, relPath); err != nil { + c.r.logger.Errorln(err) + } + } else { + c.r.logger.Errorln(err) + } + + continue + } + + // For all other event operations Hugo will sync static. + logger.Println("Syncing", relPath, "to", publishDir) + if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil { + c.r.logger.Errorln(err) + } + } + + return 0, nil + } + + _, err := c.doWithPublishDirs(syncFn) + return err +} + +// chmodFilter is a ChmodFilter for static syncing. +// Returns true to skip syncing permissions for directories and files without +// owner-write permission. The primary use case is files from the module cache (0444). +func chmodFilter(dst, src os.FileInfo) bool { + if src.IsDir() { + return true + } + return src.Mode().Perm()&0o200 == 0 +} + +func cleanErrorLog(content string) string { + content = logReplacer.Replace(content) + content = logDuplicateTemplateExecuteRe.ReplaceAllString(content, "") + content = logDuplicateTemplateParseRe.ReplaceAllString(content, "") + seen := make(map[string]bool) + parts := strings.Split(content, ": ") + keep := make([]string, 0, len(parts)) + for _, part := range parts { + if seen[part] { + continue + } + seen[part] = true + keep = append(keep, part) + } + return strings.Join(keep, ": ") +} + +func injectLiveReloadScript(src io.Reader, baseURL *url.URL) string { + var b bytes.Buffer + chain := transform.Chain{livereloadinject.New(baseURL)} + chain.Apply(&b, src) + + return b.String() +} + +func partitionDynamicEvents(sourceFs *filesystems.SourceFilesystems, events []fsnotify.Event) (de dynamicEvents) { + for _, e := range events { + if !sourceFs.IsContent(e.Name) { + de.AssetEvents = append(de.AssetEvents, e) + } else { + de.ContentEvents = append(de.ContentEvents, e) + } + } + return +} + +func pickOneWriteOrCreatePath(contentTypes config.ContentTypesProvider, events []fsnotify.Event) string { + name := "" + + for _, ev := range events { + if ev.Op&fsnotify.Write == fsnotify.Write || ev.Op&fsnotify.Create == fsnotify.Create { + if contentTypes.IsIndexContentFile(ev.Name) { + return ev.Name + } + + if contentTypes.IsContentFile(ev.Name) { + name = ev.Name + } + + } + } + + return name +} + +func formatByteCount(b uint64) string { + const unit = 1000 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %cB", + float64(b)/float64(div), "kMGTPE"[exp]) +} + +func canRedirect(requestURIWithoutQuery string, r *http.Request) bool { + if r.Header.Get(hugoHeaderRedirect) != "" { + return false + } + return isNavigation(requestURIWithoutQuery, r) +} + +// Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate +// Fall back to the file extension if not set. +// The main take here is that we don't want to have CSS/JS files etc. partake in this logic. +func isNavigation(requestURIWithoutQuery string, r *http.Request) bool { + return r.Header.Get("Sec-Fetch-Mode") == "navigate" || isPropablyHTMLRequest(requestURIWithoutQuery) +} + +func isPropablyHTMLRequest(requestURIWithoutQuery string) bool { + if strings.HasSuffix(requestURIWithoutQuery, "/") || strings.HasSuffix(requestURIWithoutQuery, "html") || strings.HasSuffix(requestURIWithoutQuery, "htm") { + return true + } + return !strings.Contains(requestURIWithoutQuery, ".") +} diff --git a/common/collections/append.go b/common/collections/append.go new file mode 100644 index 0000000..8a5aee4 --- /dev/null +++ b/common/collections/append.go @@ -0,0 +1,140 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "fmt" + "reflect" + + "github.com/gohugoio/hugo/common/hreflect" +) + +// Append appends from to a slice to and returns the resulting slice. +// If length of from is one and the only element is a slice of same type as to, +// it will be appended. +func Append(to any, from ...any) (any, error) { + if len(from) == 0 { + return to, nil + } + tov, toIsNil := hreflect.Indirect(reflect.ValueOf(to)) + + toIsNil = toIsNil || to == nil + var tot reflect.Type + + if !toIsNil { + if tov.Kind() == reflect.Slice { + // Create a copy of tov, so we don't modify the original. + c := reflect.MakeSlice(tov.Type(), tov.Len(), tov.Len()+len(from)) + reflect.Copy(c, tov) + tov = c + } + + if tov.Kind() != reflect.Slice { + return nil, fmt.Errorf("expected a slice, got %T", to) + } + + tot = tov.Type().Elem() + if tot.Kind() == reflect.Slice { + totvt := tot.Elem() + fromvs := make([]reflect.Value, len(from)) + for i, f := range from { + fromv := reflect.ValueOf(f) + fromt := fromv.Type() + if fromt.Kind() == reflect.Slice { + fromt = fromt.Elem() + } + if totvt != fromt { + return nil, fmt.Errorf("cannot append slice of %s to slice of %s", fromt, totvt) + } else { + fromvs[i] = fromv + } + } + return reflect.Append(tov, fromvs...).Interface(), nil + + } + + toIsNil = tov.Len() == 0 + + if len(from) == 1 { + fromv := reflect.ValueOf(from[0]) + if !fromv.IsValid() { + // from[0] is nil + return appendToInterfaceSliceFromValues(tov, fromv) + } + fromt := fromv.Type() + if fromt.Kind() == reflect.Slice { + fromt = fromt.Elem() + } + if fromv.Kind() == reflect.Slice { + if toIsNil { + // If we get nil []string, we just return the []string + return from[0], nil + } + + // If we get []string []string, we append the from slice to to + if tot == fromt { + return reflect.AppendSlice(tov, fromv).Interface(), nil + } else if !fromt.AssignableTo(tot) { + // Fall back to a []interface{} slice. + return appendToInterfaceSliceFromValues(tov, fromv) + } + + } + } + } + + if toIsNil { + return Slice(from...), nil + } + + for _, f := range from { + fv := reflect.ValueOf(f) + if !fv.IsValid() || !fv.Type().AssignableTo(tot) { + // Fall back to a []interface{} slice. + tov, _ := hreflect.Indirect(reflect.ValueOf(to)) + return appendToInterfaceSlice(tov, from...) + } + tov = reflect.Append(tov, fv) + } + + return tov.Interface(), nil +} + +func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, error) { + var tos []any + + for _, slice := range []reflect.Value{slice1, slice2} { + if !slice.IsValid() { + tos = append(tos, nil) + continue + } + for i := range slice.Len() { + tos = append(tos, slice.Index(i).Interface()) + } + } + + return tos, nil +} + +func appendToInterfaceSlice(tov reflect.Value, from ...any) ([]any, error) { + var tos []any + + for i := range tov.Len() { + tos = append(tos, tov.Index(i).Interface()) + } + + tos = append(tos, from...) + + return tos, nil +} diff --git a/common/collections/append_test.go b/common/collections/append_test.go new file mode 100644 index 0000000..c198ca0 --- /dev/null +++ b/common/collections/append_test.go @@ -0,0 +1,149 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "html/template" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestAppend(t *testing.T) { + t.Parallel() + c := qt.New(t) + + for i, test := range []struct { + start any + addend []any + expected any + }{ + {[]string{"a", "b"}, []any{"c"}, []string{"a", "b", "c"}}, + {[]string{"a", "b"}, []any{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}}, + {[]string{"a", "b"}, []any{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}}, + {[]string{"a"}, []any{"b", template.HTML("c")}, []any{"a", "b", template.HTML("c")}}, + {nil, []any{"a", "b"}, []string{"a", "b"}}, + {nil, []any{nil}, []any{nil}}, + {[]any{}, []any{[]string{"c", "d", "e"}}, []string{"c", "d", "e"}}, + { + tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}, + []any{&tstSlicer{"c"}}, + tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}, &tstSlicer{"c"}}, + }, + { + &tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}, + []any{&tstSlicer{"c"}}, + tstSlicers{ + &tstSlicer{"a"}, + &tstSlicer{"b"}, + &tstSlicer{"c"}, + }, + }, + { + testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}}, + []any{&tstSlicerIn1{"c"}}, + testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}, &tstSlicerIn1{"c"}}, + }, + // https://github.com/gohugoio/hugo/issues/5361 + { + []string{"a", "b"}, + []any{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}}, + []any{"a", "b", &tstSlicer{"a"}, &tstSlicer{"b"}}, + }, + { + []string{"a", "b"}, + []any{&tstSlicer{"a"}}, + []any{"a", "b", &tstSlicer{"a"}}, + }, + // Errors + {"", []any{[]string{"a", "b"}}, false}, + // No string concatenation. + { + "ab", + []any{"c"}, + false, + }, + {[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}}, + {[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}}, + {[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}}, + {[]string{"a", "b"}, []any{}, []string{"a", "b"}}, + } { + + result, err := Append(test.start, test.addend...) + + if b, ok := test.expected.(bool); ok && !b { + + c.Assert(err, qt.Not(qt.IsNil)) + continue + } + + c.Assert(err, qt.IsNil) + c.Assert(result, qt.DeepEquals, test.expected, qt.Commentf("test: [%d] %v", i, test)) + } +} + +// #11093 +func TestAppendToMultiDimensionalSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + + for _, test := range []struct { + to any + from []any + expected any + }{ + { + [][]string{{"a", "b"}}, + []any{[]string{"c", "d"}}, + [][]string{ + {"a", "b"}, + {"c", "d"}, + }, + }, + { + [][]string{{"a", "b"}}, + []any{[]string{"c", "d"}, []string{"e", "f"}}, + [][]string{ + {"a", "b"}, + {"c", "d"}, + {"e", "f"}, + }, + }, + { + [][]string{{"a", "b"}}, + []any{[]int{1, 2}}, + false, + }, + } { + result, err := Append(test.to, test.from...) + if b, ok := test.expected.(bool); ok && !b { + c.Assert(err, qt.Not(qt.IsNil)) + } else { + c.Assert(err, qt.IsNil) + c.Assert(result, qt.DeepEquals, test.expected) + } + } +} + +func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + slice := make([]string, 0, 100) + slice = append(slice, "a", "b") + result, err := Append(slice, "c") + c.Assert(err, qt.IsNil) + slice[0] = "d" + c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"}) + c.Assert(slice, qt.DeepEquals, []string{"d", "b"}) +} diff --git a/common/collections/collections.go b/common/collections/collections.go new file mode 100644 index 0000000..0b46abe --- /dev/null +++ b/common/collections/collections.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package collections contains common Hugo functionality related to collection +// handling. +package collections + +// Grouper defines a very generic way to group items by a given key. +type Grouper interface { + Group(key any, items any) (any, error) +} diff --git a/common/collections/order.go b/common/collections/order.go new file mode 100644 index 0000000..4bdc3b4 --- /dev/null +++ b/common/collections/order.go @@ -0,0 +1,20 @@ +// Copyright 2020 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +type Order interface { + // Ordinal is a zero-based ordinal that represents the order of an object + // in a collection. + Ordinal() int +} diff --git a/common/collections/slice.go b/common/collections/slice.go new file mode 100644 index 0000000..731f489 --- /dev/null +++ b/common/collections/slice.go @@ -0,0 +1,95 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "reflect" + "sort" +) + +// Slicer defines a very generic way to create a typed slice. This is used +// in collections.Slice template func to get types such as Pages, PageGroups etc. +// instead of the less useful []interface{}. +type Slicer interface { + Slice(items any) (any, error) +} + +// Slice returns a slice of all passed arguments. +func Slice(args ...any) any { + if len(args) == 0 { + return args + } + + first := args[0] + firstType := reflect.TypeOf(first) + + if firstType == nil { + return args + } + + if g, ok := first.(Slicer); ok { + v, err := g.Slice(args) + if err == nil { + return v + } + + // If Slice fails, the items are not of the same type and + // []interface{} is the best we can do. + return args + } + + if len(args) > 1 { + // This can be a mix of types. + for i := 1; i < len(args); i++ { + if firstType != reflect.TypeOf(args[i]) { + // []interface{} is the best we can do + return args + } + } + } + + slice := reflect.MakeSlice(reflect.SliceOf(firstType), len(args), len(args)) + for i, arg := range args { + slice.Index(i).Set(reflect.ValueOf(arg)) + } + return slice.Interface() +} + +// StringSliceToInterfaceSlice converts ss to []interface{}. +func StringSliceToInterfaceSlice(ss []string) []any { + result := make([]any, len(ss)) + for i, s := range ss { + result[i] = s + } + return result +} + +type SortedStringSlice []string + +// Contains returns true if s is in ss. +func (ss SortedStringSlice) Contains(s string) bool { + i := sort.SearchStrings(ss, s) + return i < len(ss) && ss[i] == s +} + +// Count returns the number of times s is in ss. +func (ss SortedStringSlice) Count(s string) int { + var count int + i := sort.SearchStrings(ss, s) + for i < len(ss) && ss[i] == s { + count++ + i++ + } + return count +} diff --git a/common/collections/slice_test.go b/common/collections/slice_test.go new file mode 100644 index 0000000..4008a5e --- /dev/null +++ b/common/collections/slice_test.go @@ -0,0 +1,172 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "errors" + "testing" + + qt "github.com/frankban/quicktest" +) + +var ( + _ Slicer = (*tstSlicer)(nil) + _ Slicer = (*tstSlicerIn1)(nil) + _ Slicer = (*tstSlicerIn2)(nil) + _ testSlicerInterface = (*tstSlicerIn1)(nil) + _ testSlicerInterface = (*tstSlicerIn1)(nil) +) + +type testSlicerInterface interface { + Name() string +} + +type testSlicerInterfaces []testSlicerInterface + +type tstSlicerIn1 struct { + TheName string +} + +type tstSlicerIn2 struct { + TheName string +} + +type tstSlicer struct { + TheName string +} + +func (p *tstSlicerIn1) Slice(in any) (any, error) { + items := in.([]any) + result := make(testSlicerInterfaces, len(items)) + for i, v := range items { + switch vv := v.(type) { + case testSlicerInterface: + result[i] = vv + default: + return nil, errors.New("invalid type") + } + } + return result, nil +} + +func (p *tstSlicerIn2) Slice(in any) (any, error) { + items := in.([]any) + result := make(testSlicerInterfaces, len(items)) + for i, v := range items { + switch vv := v.(type) { + case testSlicerInterface: + result[i] = vv + default: + return nil, errors.New("invalid type") + } + } + return result, nil +} + +func (p *tstSlicerIn1) Name() string { + return p.TheName +} + +func (p *tstSlicerIn2) Name() string { + return p.TheName +} + +func (p *tstSlicer) Slice(in any) (any, error) { + items := in.([]any) + result := make(tstSlicers, len(items)) + for i, v := range items { + switch vv := v.(type) { + case *tstSlicer: + result[i] = vv + default: + return nil, errors.New("invalid type") + } + } + return result, nil +} + +type tstSlicers []*tstSlicer + +func TestSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + + for i, test := range []struct { + args []any + expected any + }{ + {[]any{"a", "b"}, []string{"a", "b"}}, + {[]any{&tstSlicer{"a"}, &tstSlicer{"b"}}, tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}}, + {[]any{&tstSlicer{"a"}, "b"}, []any{&tstSlicer{"a"}, "b"}}, + {[]any{}, []any{}}, + {[]any{nil}, []any{nil}}, + {[]any{5, "b"}, []any{5, "b"}}, + {[]any{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}, testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}}, + {[]any{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}, []any{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}}, + } { + errMsg := qt.Commentf("[%d] %v", i, test.args) + + result := Slice(test.args...) + + c.Assert(test.expected, qt.DeepEquals, result, errMsg) + } +} + +func TestSortedStringSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + + var s SortedStringSlice = []string{"a", "b", "b", "b", "c", "d"} + + c.Assert(s.Contains("a"), qt.IsTrue) + c.Assert(s.Contains("b"), qt.IsTrue) + c.Assert(s.Contains("z"), qt.IsFalse) + c.Assert(s.Count("b"), qt.Equals, 3) + c.Assert(s.Count("z"), qt.Equals, 0) + c.Assert(s.Count("a"), qt.Equals, 1) +} + +func TestStringSliceToInterfaceSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + + tests := []struct { + name string + in []string + want []any + }{ + { + name: "empty slice", + in: []string{}, + want: []any{}, + }, + { + name: "single element", + in: []string{"hello"}, + want: []any{"hello"}, + }, + { + name: "multiple elements", + in: []string{"a", "b", "c"}, + want: []any{"a", "b", "c"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := StringSliceToInterfaceSlice(tt.in) + c.Assert(got, qt.DeepEquals, tt.want) + }) + } +} diff --git a/common/collections/stack.go b/common/collections/stack.go new file mode 100644 index 0000000..3eb6ab5 --- /dev/null +++ b/common/collections/stack.go @@ -0,0 +1,135 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "iter" + "slices" + "sync" + + "github.com/gohugoio/hugo/common/hiter" +) + +// StackThreadSafe is a simple LIFO stack that is safe for concurrent use. +type StackThreadSafe[T any] struct { + items []T + zero T + mu sync.RWMutex +} + +func NewStackThreadSafe[T any]() *StackThreadSafe[T] { + return &StackThreadSafe[T]{} +} + +func (s *StackThreadSafe[T]) Push(item T) { + s.mu.Lock() + defer s.mu.Unlock() + s.items = append(s.items, item) +} + +func (s *StackThreadSafe[T]) Pop() (T, bool) { + s.mu.Lock() + defer s.mu.Unlock() + if len(s.items) == 0 { + return s.zero, false + } + item := s.items[len(s.items)-1] + s.items = s.items[:len(s.items)-1] + return item, true +} + +func (s *StackThreadSafe[T]) Peek() (T, bool) { + s.mu.RLock() + defer s.mu.RUnlock() + if len(s.items) == 0 { + return s.zero, false + } + return s.items[len(s.items)-1], true +} + +func (s *StackThreadSafe[T]) Len() int { + s.mu.RLock() + defer s.mu.RUnlock() + return len(s.items) +} + +// All returns all items in the stack, from bottom to top. +func (s *StackThreadSafe[T]) All() iter.Seq2[int, T] { + return hiter.Lock2(slices.All(s.items), s.mu.RLock, s.mu.RUnlock) +} + +func (s *StackThreadSafe[T]) Drain() []T { + s.mu.Lock() + defer s.mu.Unlock() + items := s.items + s.items = nil + return items +} + +func (s *StackThreadSafe[T]) DrainMatching(predicate func(T) bool) []T { + s.mu.Lock() + defer s.mu.Unlock() + var items []T + for i := len(s.items) - 1; i >= 0; i-- { + if predicate(s.items[i]) { + items = append(items, s.items[i]) + s.items = slices.Delete(s.items, i, i+1) + } + } + return items +} + +// Stack is a simple LIFO stack that is not safe for concurrent use. +type Stack[T any] struct { + items []T + zero T +} + +func NewStack[T any]() *Stack[T] { + return &Stack[T]{} +} + +func (s *Stack[T]) Push(item T) { + s.items = append(s.items, item) +} + +func (s *Stack[T]) Pop() (T, bool) { + if len(s.items) == 0 { + return s.zero, false + } + item := s.items[len(s.items)-1] + s.items = s.items[:len(s.items)-1] + return item, true +} + +func (s *Stack[T]) Peek() (T, bool) { + if len(s.items) == 0 { + return s.zero, false + } + return s.items[len(s.items)-1], true +} + +func (s *Stack[T]) Len() int { + return len(s.items) +} + +func (s *Stack[T]) All() iter.Seq2[int, T] { + return slices.All(s.items) +} + +func (s *Stack[T]) Drain() []T { + items := s.items + s.items = nil + return items +} diff --git a/common/collections/stack_test.go b/common/collections/stack_test.go new file mode 100644 index 0000000..4b2b1a5 --- /dev/null +++ b/common/collections/stack_test.go @@ -0,0 +1,77 @@ +package collections + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestNewStack(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStackThreadSafe[int]() + + c.Assert(s, qt.IsNotNil) +} + +func TestStackBasic(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStackThreadSafe[int]() + + c.Assert(s.Len(), qt.Equals, 0) + + s.Push(1) + s.Push(2) + s.Push(3) + + c.Assert(s.Len(), qt.Equals, 3) + + top, ok := s.Peek() + c.Assert(ok, qt.Equals, true) + c.Assert(top, qt.Equals, 3) + + popped, ok := s.Pop() + c.Assert(ok, qt.Equals, true) + c.Assert(popped, qt.Equals, 3) + + c.Assert(s.Len(), qt.Equals, 2) + + _, _ = s.Pop() + _, _ = s.Pop() + _, ok = s.Pop() + + c.Assert(ok, qt.Equals, false) +} + +func TestStackDrain(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStackThreadSafe[string]() + s.Push("a") + s.Push("b") + + got := s.Drain() + + c.Assert(got, qt.DeepEquals, []string{"a", "b"}) + c.Assert(s.Len(), qt.Equals, 0) +} + +func TestStackDrainMatching(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStackThreadSafe[int]() + s.Push(1) + s.Push(2) + s.Push(3) + s.Push(4) + + got := s.DrainMatching(func(v int) bool { return v%2 == 0 }) + + c.Assert(got, qt.DeepEquals, []int{4, 2}) + c.Assert(s.Drain(), qt.DeepEquals, []int{1, 3}) +} diff --git a/common/constants/constants.go b/common/constants/constants.go new file mode 100644 index 0000000..1bc0f6a --- /dev/null +++ b/common/constants/constants.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package constants + +// Error/Warning IDs. +// Do not change these values. +const ( + WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html" + WarnGoldmarkRawHTML = "warning-goldmark-raw-html" + WarnPartialSuperfluousPrefix = "warning-partial-superfluous-prefix" + WarnHomePageIsLeafBundle = "warning-home-page-is-leaf-bundle" +) + +// Field/method names with special meaning. +const ( + FieldRelPermalink = "RelPermalink" + FieldPermalink = "Permalink" +) + +// IsFieldRelOrPermalink returns whether the given name is a RelPermalink or Permalink. +func IsFieldRelOrPermalink(name string) bool { + return name == FieldRelPermalink || name == FieldPermalink +} + +// Resource transformations. +const ( + ResourceTransformationFingerprint = "fingerprint" +) + +// IsResourceTransformationPermalinkHash returns whether the given name is a resource transformation that changes the permalink based on the content. +func IsResourceTransformationPermalinkHash(name string) bool { + return name == ResourceTransformationFingerprint +} diff --git a/common/docs.go b/common/docs.go new file mode 100644 index 0000000..041a62a --- /dev/null +++ b/common/docs.go @@ -0,0 +1,2 @@ +// Package common provides common helper functionality for Hugo. +package common diff --git a/common/hashing/hashing.go b/common/hashing/hashing.go new file mode 100644 index 0000000..ad28bbc --- /dev/null +++ b/common/hashing/hashing.go @@ -0,0 +1,225 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hashing provides common hashing utilities. +package hashing + +import ( + "crypto/md5" + "encoding/hex" + "io" + "strconv" + "sync" + + "github.com/cespare/xxhash/v2" + "github.com/gohugoio/hashstructure" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/identity" +) + +// XXHashFromReader calculates the xxHash for the given reader. +func XXHashFromReader(r io.Reader) (uint64, int64, error) { + h := getXxHashReadFrom() + defer putXxHashReadFrom(h) + + size, err := io.Copy(h, r) + if err != nil { + return 0, 0, err + } + return h.Sum64(), size, nil +} + +type Hasher interface { + io.StringWriter + io.Writer + io.ReaderFrom + Sum64() uint64 +} + +type HashCloser interface { + Hasher + io.Closer +} + +// XxHasher returns a Hasher that uses xxHash. +// Remember to call Close when done. +func XxHasher() HashCloser { + h := getXxHashReadFrom() + return struct { + Hasher + io.Closer + }{ + Hasher: h, + Closer: hugio.CloserFunc(func() error { + putXxHashReadFrom(h) + return nil + }), + } +} + +// XxHashFromReaderHexEncoded calculates the xxHash for the given reader +// and returns the hash as a hex encoded string. +func XxHashFromReaderHexEncoded(r io.Reader) (string, error) { + h := getXxHashReadFrom() + defer putXxHashReadFrom(h) + _, err := io.Copy(h, r) + if err != nil { + return "", err + } + hash := h.Sum(nil) + return hex.EncodeToString(hash), nil +} + +// XXHashFromString calculates the xxHash for the given string. +func XXHashFromString(s string) (uint64, error) { + h := xxhash.New() + h.WriteString(s) + return h.Sum64(), nil +} + +// XxHashFromStringHexEncoded calculates the xxHash for the given strings +// and returns the hash as a hex encoded string. +func XxHashFromStringHexEncoded(s ...string) string { + h := xxhash.New() + for _, f := range s { + h.WriteString(f) + } + hash := h.Sum(nil) + return hex.EncodeToString(hash) +} + +// MD5FromStringHexEncoded returns the MD5 hash of the given string. +func MD5FromStringHexEncoded(f string) string { + h := md5.New() + h.Write([]byte(f)) + return hex.EncodeToString(h.Sum(nil)) +} + +// HashString returns a hash from the given elements. +// It will panic if the hash cannot be calculated. +// Note that this hash should be used primarily for identity, not for change detection as +// it in the more complex values (e.g. Page) will not hash the full content. +func HashString(vs ...any) string { + hash := HashUint64(vs...) + return strconv.FormatUint(hash, 10) +} + +// HashStringHex returns a hash from the given elements as a hex encoded string. +// See HashString for more information. +func HashStringHex(vs ...any) string { + hash := HashUint64(vs...) + return strconv.FormatUint(hash, 16) +} + +var hashOptsPool = sync.Pool{ + New: func() any { + return &hashstructure.HashOptions{ + Hasher: xxhash.New(), + } + }, +} + +func getHashOpts() *hashstructure.HashOptions { + return hashOptsPool.Get().(*hashstructure.HashOptions) +} + +func putHashOpts(opts *hashstructure.HashOptions) { + opts.Hasher.Reset() + hashOptsPool.Put(opts) +} + +// HashUint64 returns a hash from the given elements. +// It will panic if the hash cannot be calculated. +// Note that this hash should be used primarily for identity, not for change detection as +// it in the more complex values (e.g. Page) will not hash the full content. +func HashUint64(vs ...any) uint64 { + var o any + if len(vs) == 1 { + o = toHashable(vs[0]) + } else { + elements := make([]any, len(vs)) + for i, e := range vs { + elements[i] = toHashable(e) + } + o = elements + } + + hash, err := Hash(o) + if err != nil { + panic(err) + } + return hash +} + +// Hash returns a hash from vs. +func Hash(vs ...any) (uint64, error) { + hashOpts := getHashOpts() + defer putHashOpts(hashOpts) + var v any = vs + if len(vs) == 1 { + v = vs[0] + } + return hashstructure.Hash(v, hashOpts) +} + +type keyer interface { + Key() string +} + +// For structs, hashstructure.Hash only works on the exported fields, +// so rewrite the input slice for known identity types. +func toHashable(v any) any { + switch t := v.(type) { + case keyer: + return t.Key() + case identity.IdentityProvider: + return t.GetIdentity() + default: + return v + } +} + +type xxhashReadFrom struct { + buff []byte + *xxhash.Digest +} + +func (x *xxhashReadFrom) ReadFrom(r io.Reader) (int64, error) { + for { + n, err := r.Read(x.buff) + if n > 0 { + x.Digest.Write(x.buff[:n]) + } + if err != nil { + if err == io.EOF { + err = nil + } + return int64(n), err + } + } +} + +var xXhashReadFromPool = sync.Pool{ + New: func() any { + return &xxhashReadFrom{Digest: xxhash.New(), buff: make([]byte, 48*1024)} + }, +} + +func getXxHashReadFrom() *xxhashReadFrom { + return xXhashReadFromPool.Get().(*xxhashReadFrom) +} + +func putXxHashReadFrom(h *xxhashReadFrom) { + h.Reset() + xXhashReadFromPool.Put(h) +} diff --git a/common/hashing/hashing_test.go b/common/hashing/hashing_test.go new file mode 100644 index 0000000..de2fed8 --- /dev/null +++ b/common/hashing/hashing_test.go @@ -0,0 +1,152 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hashing + +import ( + "fmt" + "math" + "strings" + "sync" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestXxHashFromReader(t *testing.T) { + c := qt.New(t) + s := "Hello World" + r := strings.NewReader(s) + got, size, err := XXHashFromReader(r) + c.Assert(err, qt.IsNil) + c.Assert(size, qt.Equals, int64(len(s))) + c.Assert(got, qt.Equals, uint64(7148569436472236994)) +} + +func TestXxHashFromReaderPara(t *testing.T) { + c := qt.New(t) + + var wg sync.WaitGroup + for i := range 10 { + wg.Go(func() { + for j := range 100 { + s := strings.Repeat("Hello ", i+j+1*42) + r := strings.NewReader(s) + got, size, err := XXHashFromReader(r) + c.Assert(size, qt.Equals, int64(len(s))) + c.Assert(err, qt.IsNil) + expect, _ := XXHashFromString(s) + c.Assert(got, qt.Equals, expect) + } + }) + } + + wg.Wait() +} + +func TestXxHashFromString(t *testing.T) { + c := qt.New(t) + s := "Hello World" + got, err := XXHashFromString(s) + c.Assert(err, qt.IsNil) + c.Assert(got, qt.Equals, uint64(7148569436472236994)) +} + +func TestXxHashFromStringHexEncoded(t *testing.T) { + c := qt.New(t) + s := "The quick brown fox jumps over the lazy dog" + got := XxHashFromStringHexEncoded(s) + // Facit: https://asecuritysite.com/encryption/xxhash?val=The%20quick%20brown%20fox%20jumps%20over%20the%20lazy%20dog + c.Assert(got, qt.Equals, "0b242d361fda71bc") +} + +func BenchmarkXXHashFromReader(b *testing.B) { + r := strings.NewReader("Hello World") + + for b.Loop() { + XXHashFromReader(r) + r.Seek(0, 0) + } +} + +func BenchmarkXXHashFromString(b *testing.B) { + s := "Hello World" + + for b.Loop() { + XXHashFromString(s) + } +} + +func BenchmarkXXHashFromStringHexEncoded(b *testing.B) { + s := "The quick brown fox jumps over the lazy dog" + + for b.Loop() { + XxHashFromStringHexEncoded(s) + } +} + +func TestHashString(t *testing.T) { + c := qt.New(t) + + c.Assert(HashString("a", "b"), qt.Equals, "3176555414984061461") + c.Assert(HashString("ab"), qt.Equals, "7347350983217793633") + + var vals []any = []any{"a", "b", tstKeyer{"c"}} + + c.Assert(HashString(vals...), qt.Equals, "4438730547989914315") + c.Assert(vals[2], qt.Equals, tstKeyer{"c"}) +} + +type tstKeyer struct { + key string +} + +func (t tstKeyer) Key() string { + return t.key +} + +func (t tstKeyer) String() string { + return "key: " + t.key +} + +func BenchmarkHashString(b *testing.B) { + word := " hello " + + var tests []string + + for i := 1; i <= 5; i++ { + sentence := strings.Repeat(word, int(math.Pow(4, float64(i)))) + tests = append(tests, sentence) + } + + b.ResetTimer() + + for _, test := range tests { + b.Run(fmt.Sprintf("n%d", len(test)), func(b *testing.B) { + for b.Loop() { + HashString(test) + } + }) + } +} + +func BenchmarkHashMap(b *testing.B) { + m := map[string]any{} + for i := range 1000 { + m[fmt.Sprintf("key%d", i)] = i + } + + for b.Loop() { + HashString(m) + } +} diff --git a/common/hdebug/debug.go b/common/hdebug/debug.go new file mode 100644 index 0000000..a977367 --- /dev/null +++ b/common/hdebug/debug.go @@ -0,0 +1,61 @@ +// Copyright 2025 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hdebug + +import ( + "fmt" + "strings" + + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/htesting" +) + +// Printf is a debug print function that should be removed before committing code to the repository. +func Printf(format string, args ...any) { + panicIfRealCI() + if len(args) == 1 && !strings.Contains(format, "%") { + format = format + ": %v" + } + if !strings.HasSuffix(format, "\n") { + format = format + "\n" + } + fmt.Printf(format, args...) +} + +func AssertNotNil(a ...any) { + panicIfRealCI() + for _, v := range a { + if types.IsNil(v) { + panic("hdebug.AssertNotNil: value is nil") + } + } +} + +func Panicf(format string, args ...any) { + panicIfRealCI() + // fmt.Println(stack()) + if len(args) == 1 && !strings.Contains(format, "%") { + format = format + ": %v" + } + if !strings.HasSuffix(format, "\n") { + format = format + "\n" + } + panic(fmt.Sprintf(format, args...)) +} + +func panicIfRealCI() { + if htesting.IsRealCI() { + panic("This debug statement should be removed before committing code!") + } +} diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go new file mode 100644 index 0000000..acaebb4 --- /dev/null +++ b/common/herrors/error_locator.go @@ -0,0 +1,170 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package herrors contains common Hugo errors and error related utilities. +package herrors + +import ( + "io" + "path/filepath" + "strings" + + "github.com/gohugoio/hugo/common/text" +) + +// LineMatcher contains the elements used to match an error to a line +type LineMatcher struct { + Position text.Position + Error error + + LineNumber int + Offset int + Line string +} + +// LineMatcherFn is used to match a line with an error. +// It returns the column number or 0 if the line was found, but column could not be determined. Returns -1 if no line match. +type LineMatcherFn func(m LineMatcher) int + +// SimpleLineMatcher simply matches by line number. +var SimpleLineMatcher = func(m LineMatcher) int { + if m.Position.LineNumber == m.LineNumber { + // We found the line, but don't know the column. + return 0 + } + return -1 +} + +// NopLineMatcher is a matcher that always returns 1. +// This will effectively give line 1, column 1. +var NopLineMatcher = func(m LineMatcher) int { + return 1 +} + +// OffsetMatcher is a line matcher that matches by offset. +var OffsetMatcher = func(m LineMatcher) int { + if m.Offset+len(m.Line) >= m.Position.Offset { + // We found the line, but return 0 to signal that we want to determine + // the column from the error. + return 0 + } + return -1 +} + +// ContainsMatcher is a line matcher that matches by line content. +func ContainsMatcher(text string) func(m LineMatcher) int { + return func(m LineMatcher) int { + if idx := strings.Index(m.Line, text); idx != -1 { + return idx + 1 + } + return -1 + } +} + +// ErrorContext contains contextual information about an error. This will +// typically be the lines surrounding some problem in a file. +type ErrorContext struct { + // If a match will contain the matched line and up to 2 lines before and after. + // Will be empty if no match. + Lines []string + + // The position of the error in the Lines above. 0 based. + LinesPos int + + // The position of the content in the file. Note that this may be different from the error's position set + // in FileError. + Position text.Position + + // The lexer to use for syntax highlighting. + // https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages + ChromaLexer string +} + +func chromaLexerFromType(fileType string) string { + switch fileType { + case "html", "htm": + return "go-html-template" + } + return fileType +} + +func extNoDelimiter(filename string) string { + return strings.TrimPrefix(filepath.Ext(filename), ".") +} + +func chromaLexerFromFilename(filename string) string { + if strings.Contains(filename, "layouts") { + return "go-html-template" + } + + ext := extNoDelimiter(filename) + return chromaLexerFromType(ext) +} + +func locateErrorInString(src string, matcher LineMatcherFn) *ErrorContext { + return locateError(strings.NewReader(src), &fileError{}, matcher) +} + +func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext { + if le == nil { + panic("must provide an error") + } + + ectx := &ErrorContext{LinesPos: -1, Position: text.Position{Offset: -1}} + + b, err := io.ReadAll(r) + if err != nil { + return ectx + } + + lines := strings.Split(string(b), "\n") + + lineNo := 0 + posBytes := 0 + + for li, line := range lines { + lineNo = li + 1 + m := LineMatcher{ + Position: le.Position(), + Error: le, + LineNumber: lineNo, + Offset: posBytes, + Line: line, + } + v := matches(m) + if ectx.LinesPos == -1 && v != -1 { + ectx.Position.LineNumber = lineNo + ectx.Position.ColumnNumber = v + break + } + + posBytes += len(line) + } + + if ectx.Position.LineNumber > 0 { + low := max(ectx.Position.LineNumber-3, 0) + + if ectx.Position.LineNumber > 2 { + ectx.LinesPos = 2 + } else { + ectx.LinesPos = ectx.Position.LineNumber - 1 + } + + high := min(ectx.Position.LineNumber+2, len(lines)) + + ectx.Lines = lines[low:high] + + } + + return ectx +} diff --git a/common/herrors/error_locator_test.go b/common/herrors/error_locator_test.go new file mode 100644 index 0000000..62f1521 --- /dev/null +++ b/common/herrors/error_locator_test.go @@ -0,0 +1,152 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package herrors contains common Hugo errors and error related utilities. +package herrors + +import ( + "strings" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestErrorLocator(t *testing.T) { + c := qt.New(t) + + lineMatcher := func(m LineMatcher) int { + if strings.Contains(m.Line, "THEONE") { + return 1 + } + return -1 + } + + lines := `LINE 1 +LINE 2 +LINE 3 +LINE 4 +This is THEONE +LINE 6 +LINE 7 +LINE 8 +` + + location := locateErrorInString(lines, lineMatcher) + pos := location.Position + c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"}) + + c.Assert(pos.LineNumber, qt.Equals, 5) + c.Assert(location.LinesPos, qt.Equals, 2) + + locate := func(s string, m LineMatcherFn) *ErrorContext { + ctx := locateErrorInString(s, m) + return ctx + } + + c.Assert(locate(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"}) + + location = locateErrorInString(`L1 +This is THEONE +L2 +`, lineMatcher) + pos = location.Position + c.Assert(pos.LineNumber, qt.Equals, 2) + c.Assert(location.LinesPos, qt.Equals, 1) + c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""}) + + location = locate(`This is THEONE +L2 +`, lineMatcher) + c.Assert(location.LinesPos, qt.Equals, 0) + c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""}) + + location = locate(`L1 +This THEONE +`, lineMatcher) + c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""}) + c.Assert(location.LinesPos, qt.Equals, 1) + + location = locate(`L1 +L2 +This THEONE +`, lineMatcher) + c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "L2", "This THEONE", ""}) + c.Assert(location.LinesPos, qt.Equals, 2) + + location = locateErrorInString("NO MATCH", lineMatcher) + pos = location.Position + c.Assert(pos.LineNumber, qt.Equals, 0) + c.Assert(location.LinesPos, qt.Equals, -1) + c.Assert(len(location.Lines), qt.Equals, 0) + + lineMatcher = func(m LineMatcher) int { + if m.LineNumber == 6 { + return 1 + } + return -1 + } + + location = locateErrorInString(`A +B +C +D +E +F +G +H +I +J`, lineMatcher) + pos = location.Position + + c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"}) + c.Assert(pos.LineNumber, qt.Equals, 6) + c.Assert(location.LinesPos, qt.Equals, 2) + + // Test match EOF + lineMatcher = func(m LineMatcher) int { + if m.LineNumber == 4 { + return 1 + } + return -1 + } + + location = locateErrorInString(`A +B +C +`, lineMatcher) + + pos = location.Position + + c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""}) + c.Assert(pos.LineNumber, qt.Equals, 4) + c.Assert(location.LinesPos, qt.Equals, 2) + + offsetMatcher := func(m LineMatcher) int { + if m.Offset == 1 { + return 1 + } + return -1 + } + + location = locateErrorInString(`A +B +C +D +E`, offsetMatcher) + + pos = location.Position + + c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"}) + c.Assert(pos.LineNumber, qt.Equals, 2) + c.Assert(location.LinesPos, qt.Equals, 1) +} diff --git a/common/herrors/errors.go b/common/herrors/errors.go new file mode 100644 index 0000000..5e223f4 --- /dev/null +++ b/common/herrors/errors.go @@ -0,0 +1,180 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package herrors contains common Hugo errors and error related utilities. +package herrors + +import ( + "errors" + "fmt" + "os" + "regexp" + "runtime" + "strings" + "time" +) + +// ErrorSender is a, typically, non-blocking error handler. +type ErrorSender interface { + SendError(err error) +} + +// Recover is a helper function that can be used to capture panics. +// Put this at the top of a method/function that crashes in a template: +// +// defer herrors.Recover() +func Recover(args ...any) { + if r := recover(); r != nil { + fmt.Println("ERR:", r) + buf := make([]byte, 64<<10) + buf = buf[:runtime.Stack(buf, false)] + args = append(args, "stacktrace from panic: \n"+string(buf), "\n") + fmt.Println(args...) + } +} + +// IsTimeoutError returns true if the given error is or contains a TimeoutError. +func IsTimeoutError(err error) bool { + return errors.Is(err, &TimeoutError{}) +} + +type TimeoutError struct { + Duration time.Duration +} + +func (e *TimeoutError) Error() string { + return fmt.Sprintf("timeout after %s", e.Duration) +} + +func (e *TimeoutError) Is(target error) bool { + _, ok := target.(*TimeoutError) + return ok +} + +// errMessage wraps an error with a message. +type errMessage struct { + msg string + err error +} + +func (e *errMessage) Error() string { + return e.msg +} + +func (e *errMessage) Unwrap() error { + return e.err +} + +// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError. +func IsFeatureNotAvailableError(err error) bool { + return errors.Is(err, &FeatureNotAvailableError{}) +} + +// ErrFeatureNotAvailable denotes that a feature is unavailable. +// +// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional, +// and this error is used to signal those situations. +var ErrFeatureNotAvailable = &FeatureNotAvailableError{Cause: errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")} + +// FeatureNotAvailableError is an error type used to signal that a feature is not available. +type FeatureNotAvailableError struct { + Cause error +} + +func (e *FeatureNotAvailableError) Unwrap() error { + return e.Cause +} + +func (e *FeatureNotAvailableError) Error() string { + return e.Cause.Error() +} + +func (e *FeatureNotAvailableError) Is(target error) bool { + _, ok := target.(*FeatureNotAvailableError) + return ok +} + +// Must panics if err != nil. +func Must(err error) { + if err != nil { + panic(err) + } +} + +// IsNotExist returns true if the error is a file not found error. +// Unlike os.IsNotExist, this also considers wrapped errors. +func IsNotExist(err error) bool { + if os.IsNotExist(err) { + return true + } + + // os.IsNotExist does not consider wrapped errors. + if os.IsNotExist(errors.Unwrap(err)) { + return true + } + + return false +} + +// IsExist returns true if the error is a file exists error. +// Unlike os.IsExist, this also considers wrapped errors. +func IsExist(err error) bool { + if os.IsExist(err) { + return true + } + + // os.IsExist does not consider wrapped errors. + if os.IsExist(errors.Unwrap(err)) { + return true + } + + return false +} + +var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`) + +const deferredPrefix = "__hdeferred/" + +var deferredStringToRemove = regexp.MustCompile(`executing "__hdeferred/.*?" `) + +// ImproveRenderErr improves the error message for rendering errors. +func ImproveRenderErr(inErr error) (outErr error) { + outErr = inErr + msg := improveIfNilPointerMsg(inErr) + if msg != "" { + outErr = &errMessage{msg: msg, err: outErr} + } + + if strings.Contains(inErr.Error(), deferredPrefix) { + msg := deferredStringToRemove.ReplaceAllString(inErr.Error(), "executing ") + outErr = &errMessage{msg: msg, err: outErr} + } + return +} + +func improveIfNilPointerMsg(inErr error) string { + m := nilPointerErrRe.FindStringSubmatch(inErr.Error()) + if len(m) == 0 { + return "" + } + call := m[1] + field := m[2] + parts := strings.Split(call, ".") + if len(parts) < 2 { + return "" + } + receiverName := parts[len(parts)-2] + receiver := strings.Join(parts[:len(parts)-1], ".") + s := fmt.Sprintf("– %s is nil; wrap it in if or with: {{ with %s }}{{ .%s }}{{ end }}", receiverName, receiver, field) + return nilPointerErrRe.ReplaceAllString(inErr.Error(), s) +} diff --git a/common/herrors/errors_test.go b/common/herrors/errors_test.go new file mode 100644 index 0000000..2f53a1e --- /dev/null +++ b/common/herrors/errors_test.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "errors" + "fmt" + "testing" + + qt "github.com/frankban/quicktest" + "github.com/spf13/afero" +) + +func TestIsNotExist(t *testing.T) { + c := qt.New(t) + + c.Assert(IsNotExist(afero.ErrFileNotFound), qt.Equals, true) + c.Assert(IsNotExist(afero.ErrFileExists), qt.Equals, false) + c.Assert(IsNotExist(afero.ErrDestinationExists), qt.Equals, false) + c.Assert(IsNotExist(nil), qt.Equals, false) + + c.Assert(IsNotExist(fmt.Errorf("foo")), qt.Equals, false) + + // os.IsNotExist returns false for wrapped errors. + c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true) +} + +func TestIsFeatureNotAvailableError(t *testing.T) { + c := qt.New(t) + + c.Assert(IsFeatureNotAvailableError(ErrFeatureNotAvailable), qt.Equals, true) + c.Assert(IsFeatureNotAvailableError(&FeatureNotAvailableError{}), qt.Equals, true) + c.Assert(IsFeatureNotAvailableError(errors.New("asdf")), qt.Equals, false) +} diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go new file mode 100644 index 0000000..62690de --- /dev/null +++ b/common/herrors/file_error.go @@ -0,0 +1,449 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable lfmtaw or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "path/filepath" + + "github.com/bep/godartsass/v2" + "github.com/bep/golibsass/libsass/libsasserrors" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/common/text" + "github.com/pelletier/go-toml/v2" + "github.com/spf13/afero" + "github.com/tdewolff/parse/v2" +) + +// FileError represents an error when handling a file: Parsing a config file, +// execute a template etc. +type FileError interface { + error + + // ErrorContext holds some context information about the error. + ErrorContext() *ErrorContext + + text.Positioner + + // UpdatePosition updates the position of the error. + UpdatePosition(pos text.Position) FileError + + // UpdateContent updates the error with a new ErrorContext from the content of the file. + UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError + + // SetFilename sets the filename of the error. + SetFilename(filename string) FileError +} + +// Unwrapper can unwrap errors created with fmt.Errorf. +type Unwrapper interface { + Unwrap() error +} + +var ( + _ FileError = (*fileError)(nil) + _ Unwrapper = (*fileError)(nil) +) + +func (fe *fileError) SetFilename(filename string) FileError { + fe.position.Filename = filename + return fe +} + +func (fe *fileError) UpdatePosition(pos text.Position) FileError { + oldFilename := fe.Position().Filename + if pos.Filename != "" && fe.fileType == "" { + _, fe.fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename)) + } + if pos.Filename == "" { + pos.Filename = oldFilename + } + fe.position = pos + return fe +} + +func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError { + if linematcher == nil { + linematcher = SimpleLineMatcher + } + + var ( + posle = fe.position + ectx *ErrorContext + ) + + if posle.LineNumber <= 1 && posle.Offset > 0 { + // Try to locate the line number from the content if offset is set. + ectx = locateError(r, fe, func(m LineMatcher) int { + if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) { + lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber + m.Position = text.Position{LineNumber: lno} + return linematcher(m) + } + return -1 + }) + } else { + ectx = locateError(r, fe, linematcher) + } + + if ectx.ChromaLexer == "" { + if fe.fileType != "" { + ectx.ChromaLexer = chromaLexerFromType(fe.fileType) + } else { + ectx.ChromaLexer = chromaLexerFromFilename(fe.Position().Filename) + } + } + + fe.errorContext = ectx + + if ectx.Position.LineNumber > 0 && ectx.Position.LineNumber > fe.position.LineNumber { + fe.position.LineNumber = ectx.Position.LineNumber + } + + if ectx.Position.ColumnNumber > 0 && ectx.Position.ColumnNumber > fe.position.ColumnNumber { + fe.position.ColumnNumber = ectx.Position.ColumnNumber + } + + return fe +} + +type fileError struct { + position text.Position + errorContext *ErrorContext + + fileType string + + cause error +} + +func (e *fileError) ErrorContext() *ErrorContext { + return e.errorContext +} + +// Position returns the text position of this error. +func (e fileError) Position() text.Position { + return e.position +} + +func (e *fileError) Error() string { + return fmt.Sprintf("%s: %s", e.position, e.causeString()) +} + +func (e *fileError) causeString() string { + if e.cause == nil { + return "" + } + switch v := e.cause.(type) { + // Avoid repeating the file info in the error message. + case godartsass.SassError: + return v.Message + case libsasserrors.Error: + return v.Message + default: + return v.Error() + } +} + +func (e *fileError) Unwrap() error { + return e.cause +} + +// NewFileError creates a new FileError that wraps err. +// It will try to extract the filename and line number from err. +func NewFileError(err error) FileError { + // Filetype is used to determine the Chroma lexer to use. + fileType, pos := extractFileTypePos(err) + return &fileError{cause: err, fileType: fileType, position: pos} +} + +// NewFileErrorFromName creates a new FileError that wraps err. +// The value for name should identify the file, the best +// being the full filename to the file on disk. +func NewFileErrorFromName(err error, name string) FileError { + // Filetype is used to determine the Chroma lexer to use. + fileType, pos := extractFileTypePos(err) + pos.Filename = name + + if fileType == "" { + _, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name)) + } + + return &fileError{cause: err, fileType: fileType, position: pos} +} + +// NewFileErrorFromPos will use the filename and line number from pos to create a new FileError, wrapping err. +func NewFileErrorFromPos(err error, pos text.Position) FileError { + // Filetype is used to determine the Chroma lexer to use. + fileType, _ := extractFileTypePos(err) + if fileType == "" { + _, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename)) + } + return &fileError{cause: err, fileType: fileType, position: pos} +} + +func NewFileErrorFromFileInErr(err error, fs afero.Fs, linematcher LineMatcherFn) FileError { + fe := NewFileError(err) + pos := fe.Position() + if pos.Filename == "" { + return fe + } + + f, realFilename, err2 := openFile(pos.Filename, fs) + if err2 != nil { + return fe + } + + pos.Filename = realFilename + defer f.Close() + return fe.UpdateContent(f, linematcher) +} + +func NewFileErrorFromFileInPos(err error, pos text.Position, fs afero.Fs, linematcher LineMatcherFn) FileError { + if err == nil { + panic("err is nil") + } + f, realFilename, err2 := openFile(pos.Filename, fs) + if err2 != nil { + return NewFileErrorFromPos(err, pos) + } + pos.Filename = realFilename + defer f.Close() + return NewFileErrorFromPos(err, pos).UpdateContent(f, linematcher) +} + +// NewFileErrorFromFile is a convenience method to create a new FileError from a file. +func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher LineMatcherFn) FileError { + if err == nil { + panic("err is nil") + } + f, realFilename, err2 := openFile(filename, fs) + if err2 != nil { + return NewFileErrorFromName(err, realFilename) + } + defer f.Close() + fe := NewFileErrorFromName(err, realFilename) + fe = fe.UpdateContent(f, linematcher) + return fe +} + +func openFile(filename string, fs afero.Fs) (afero.File, string, error) { + realFilename := filename + + // We want the most specific filename possible in the error message. + fi, err2 := fs.Stat(filename) + if err2 == nil { + if s, ok := fi.(interface { + Filename() string + }); ok { + realFilename = s.Filename() + } + } + + f, err2 := fs.Open(filename) + if err2 != nil { + return nil, realFilename, err2 + } + + return f, realFilename, nil +} + +// Cause returns the underlying error, that is, +// it unwraps errors until it finds one that does not implement +// the Unwrap method. +// For a shallow variant, see Unwrap. +func Cause(err error) error { + type unwrapper interface { + Unwrap() error + } + + for err != nil { + cause, ok := err.(unwrapper) + if !ok { + break + } + err = cause.Unwrap() + } + return err +} + +// Unwrap returns the underlying error or itself if it does not implement Unwrap. +func Unwrap(err error) error { + if u := errors.Unwrap(err); u != nil { + return u + } + return err +} + +// UnwrapFileErrors returns all FileError contained in err. +func UnwrapFileErrors(err error) []FileError { + if err == nil { + return nil + } + errs := Errors(err) + var fileErrors []FileError + for _, e := range errs { + if v, ok := e.(FileError); ok { + fileErrors = append(fileErrors, v) + } + fileErrors = append(fileErrors, UnwrapFileErrors(errors.Unwrap(e))...) + } + return fileErrors +} + +// UnwrapFileErrorsWithErrorContext tries to unwrap all FileError in err that has an ErrorContext. +func UnwrapFileErrorsWithErrorContext(err error) []FileError { + errs := UnwrapFileErrors(err) + var n int + for _, e := range errs { + if e.ErrorContext() != nil { + errs[n] = e + n++ + } + } + return errs[:n] +} + +// Errors returns the list of errors contained in err. +func Errors(err error) []error { + if err == nil { + return nil + } + + type unwrapper interface { + Unwrap() []error + } + if u, ok := err.(unwrapper); ok { + return u.Unwrap() + } + return []error{err} +} + +func extractFileTypePos(err error) (string, text.Position) { + err = Unwrap(err) + + var fileType string + + // LibSass, DartSass + if pos := extractPosition(err); pos.LineNumber > 0 || pos.Offset > 0 { + _, fileType = paths.FileAndExtNoDelimiter(pos.Filename) + return fileType, pos + } + + // Default to line 1 col 1 if we don't find any better. + pos := text.Position{ + Offset: -1, + LineNumber: 1, + ColumnNumber: 1, + } + + // JSON errors. + offset, typ := extractOffsetAndType(err) + if fileType == "" { + fileType = typ + } + + if offset >= 0 { + pos.Offset = offset + } + + // The error type from the minifier contains line number and column number. + if line, col := extractLineNumberAndColumnNumber(err); line >= 0 { + pos.LineNumber = line + pos.ColumnNumber = col + return fileType, pos + } + + // Look in the error message for the line number. + if lno, col := commonLineNumberExtractor(err); lno > 0 { + pos.ColumnNumber = col + pos.LineNumber = lno + } + + if fileType == "" && pos.Filename != "" { + _, fileType = paths.FileAndExtNoDelimiter(pos.Filename) + } + + return fileType, pos +} + +// UnwrapFileError tries to unwrap a FileError from err. +// It returns nil if this is not possible. +func UnwrapFileError(err error) FileError { + for err != nil { + switch v := err.(type) { + case FileError: + return v + default: + err = errors.Unwrap(err) + } + } + return nil +} + +func extractOffsetAndType(e error) (int, string) { + switch v := e.(type) { + case *json.UnmarshalTypeError: + return int(v.Offset), "json" + case *json.SyntaxError: + return int(v.Offset), "json" + default: + return -1, "" + } +} + +func extractLineNumberAndColumnNumber(e error) (int, int) { + switch v := e.(type) { + case *parse.Error: + return v.Line, v.Column + case *toml.DecodeError: + return v.Position() + + } + + return -1, -1 +} + +func extractPosition(e error) (pos text.Position) { + switch v := e.(type) { + case godartsass.SassError: + span := v.Span + start := span.Start + filename, _ := paths.UrlStringToFilename(span.Url) + pos.Filename = filename + pos.Offset = start.Offset + pos.ColumnNumber = start.Column + case libsasserrors.Error: + pos.Filename = v.File + pos.LineNumber = v.Line + pos.ColumnNumber = v.Column + } + return +} + +// TextSegmentError is an error with a text segment attached. +type TextSegmentError struct { + Segment string + Err error +} + +func (e TextSegmentError) Unwrap() error { + return e.Err +} + +func (e TextSegmentError) Error() string { + return e.Err.Error() +} diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go new file mode 100644 index 0000000..e7e66ca --- /dev/null +++ b/common/herrors/file_error_test.go @@ -0,0 +1,80 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "errors" + "fmt" + "strings" + "testing" + + "github.com/gohugoio/hugo/common/text" + + qt "github.com/frankban/quicktest" +) + +func TestNewFileError(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + fe := NewFileErrorFromName(errors.New("bar"), "foo.html") + c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`) + + var lines strings.Builder + for i := 1; i <= 100; i++ { + lines.WriteString(fmt.Sprintf("line %d\n", i)) + } + + fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2}) + c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`) + fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212}) + fe.UpdateContent(strings.NewReader(lines.String()), nil) + c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`) + errorContext := fe.ErrorContext() + c.Assert(errorContext, qt.IsNotNil) + c.Assert(errorContext.Lines, qt.DeepEquals, []string{"line 30", "line 31", "line 32", "line 33", "line 34"}) + c.Assert(errorContext.LinesPos, qt.Equals, 2) + c.Assert(errorContext.ChromaLexer, qt.Equals, "go-html-template") +} + +func TestNewFileErrorExtractFromMessage(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + for i, test := range []struct { + in error + offset int + lineNumber int + columnNumber int + }{ + {errors.New("no line number for you"), 0, 1, 1}, + {errors.New(`template: _default/single.html:4:15: executing "_default/single.html" at <.Titles>: can't evaluate field Titles in type *hugolib.PageOutput`), 0, 4, 15}, + {errors.New("parse failed: template: _default/bundle-resource-meta.html:11: unexpected in operand"), 0, 11, 1}, + {errors.New(`failed:: template: _default/bundle-resource-meta.html:2:7: executing "main" at <.Titles>`), 0, 2, 7}, + {errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7}, + {errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at : error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5}, + } { + + got := NewFileErrorFromName(test.in, "test.txt") + + errMsg := qt.Commentf("[%d][%T]", i, got) + + pos := got.Position() + c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg) + c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg) + c.Assert(errors.Unwrap(got), qt.Not(qt.IsNil)) + } +} diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go new file mode 100644 index 0000000..121506b --- /dev/null +++ b/common/herrors/line_number_extractors.go @@ -0,0 +1,73 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "regexp" + "strconv" +) + +var lineNumberExtractors = []lineNumberExtractor{ + // YAML parse errors. + newLineNumberErrHandlerFromRegexp(`\[(\d+):(\d+)\]`), + + // Template/shortcode parse errors + newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`), + newLineNumberErrHandlerFromRegexp(`:(\d+):`), + + // i18n bundle errors + newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`), +} + +func commonLineNumberExtractor(e error) (int, int) { + for _, handler := range lineNumberExtractors { + lno, col := handler(e) + if lno > 0 { + return lno, col + } + } + return 0, 0 +} + +type lineNumberExtractor func(e error) (int, int) + +func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor { + re := regexp.MustCompile(expression) + return extractLineNo(re) +} + +func extractLineNo(re *regexp.Regexp) lineNumberExtractor { + return func(e error) (int, int) { + if e == nil { + panic("no error") + } + col := 1 + s := e.Error() + m := re.FindStringSubmatch(s) + if len(m) >= 2 { + lno, _ := strconv.Atoi(m[1]) + if len(m) > 2 { + col, _ = strconv.Atoi(m[2]) + } + + if col <= 0 { + col = 1 + } + + return lno, col + } + + return 0, col + } +} diff --git a/common/herrors/line_number_extractors_test.go b/common/herrors/line_number_extractors_test.go new file mode 100644 index 0000000..482c0dd --- /dev/null +++ b/common/herrors/line_number_extractors_test.go @@ -0,0 +1,31 @@ +// Copyright 2025 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "errors" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestCommonLineNumberExtractor(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + lno, col := commonLineNumberExtractor(errors.New("[4:9] value is not allowed in this context")) + c.Assert(lno, qt.Equals, 4) + c.Assert(col, qt.Equals, 9) +} diff --git a/common/hexec/esmloader.go b/common/hexec/esmloader.go new file mode 100644 index 0000000..2c643b7 --- /dev/null +++ b/common/hexec/esmloader.go @@ -0,0 +1,30 @@ +// Copyright 2026 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hexec + +import ( + _ "embed" + "encoding/base64" + "sync" +) + +//go:embed esmloader.mjs +var esmLoaderSource string + +// nodeESMLoaderImportArg returns a "--import=data:..." argument that installs +// a Node.js ESM resolver hook making NODE_PATH a fallback for failed bare +// imports. See esmloader.mjs for the rationale. +var nodeESMLoaderImportArg = sync.OnceValue(func() string { + return "--import=data:text/javascript;base64," + base64.StdEncoding.EncodeToString([]byte(esmLoaderSource)) +}) diff --git a/common/hexec/esmloader.mjs b/common/hexec/esmloader.mjs new file mode 100644 index 0000000..725a434 --- /dev/null +++ b/common/hexec/esmloader.mjs @@ -0,0 +1,61 @@ +// Node.js ESM resolver hook installed by Hugo. +// +// Node's ESM resolver does not consult NODE_PATH, unlike CJS require(). +// That breaks postcss.config.js / babel.config.js / etc. files written in +// ESM and loaded from outside the project tree (typically the Hugo module +// cache): bare imports like `import x from "postcss-import"` cannot be +// resolved by walking up from the file's location. +// +// This hook makes the ESM resolver fall back to NODE_PATH for bare +// specifiers when Node's normal resolution fails. It is a no-op for +// relative/absolute paths and URL-scheme specifiers, and it never fires +// unless Node would itself have thrown ERR_MODULE_NOT_FOUND or +// ERR_ACCESS_DENIED. +// +// ERR_ACCESS_DENIED is handled because Node's resolver walks up the +// directory tree looking for node_modules. Under the permission model that +// walk can hit a node_modules outside the allow-list (e.g. Netlify stores +// its node_modules cache in the same tree as the Hugo file cache), aborting +// resolution even though the package is reachable via NODE_PATH. If the +// NODE_PATH fallback also fails we re-throw the original error so the +// access-denied resource is still reported. +// +// Uses the synchronous registerHooks API so it runs on the main thread and +// does not require --allow-worker under the Node permission model. + +import { registerHooks, createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; + +const resolvers = []; +const np = process.env.NODE_PATH; +if (np) { + const sep = process.platform === 'win32' ? ';' : ':'; + for (const p of np.split(sep)) { + if (p) resolvers.push(createRequire(p + '/_')); + } +} + +function isBareSpecifier(s) { + if (!s) return false; + if (s.startsWith('.') || s.startsWith('/') || s.startsWith('#')) return false; + if (/^[a-z][a-z0-9+.-]*:/i.test(s)) return false; + return true; +} + +registerHooks({ + resolve(specifier, context, nextResolve) { + try { + return nextResolve(specifier, context); + } catch (err) { + if (err?.code !== 'ERR_MODULE_NOT_FOUND' && err?.code !== 'ERR_ACCESS_DENIED') throw err; + if (!isBareSpecifier(specifier)) throw err; + for (const r of resolvers) { + try { + const resolved = r.resolve(specifier); + return { url: pathToFileURL(resolved).href, shortCircuit: true, format: null }; + } catch (_) { /* try next */ } + } + throw err; + } + }, +}); diff --git a/common/hexec/exec.go b/common/hexec/exec.go new file mode 100644 index 0000000..1da5433 --- /dev/null +++ b/common/hexec/exec.go @@ -0,0 +1,557 @@ +// Copyright 2026 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hexec + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "slices" + "strings" + + "github.com/bep/logg" + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/security" +) + +var WithDir = func(dir string) func(c *commandeer) { + return func(c *commandeer) { + c.dir = dir + } +} + +var WithContext = func(ctx context.Context) func(c *commandeer) { + return func(c *commandeer) { + c.ctx = ctx + } +} + +var WithStdout = func(w io.Writer) func(c *commandeer) { + return func(c *commandeer) { + c.stdout = w + } +} + +var WithStderr = func(w io.Writer) func(c *commandeer) { + return func(c *commandeer) { + c.stderr = w + } +} + +var WithStdin = func(r io.Reader) func(c *commandeer) { + return func(c *commandeer) { + c.stdin = r + } +} + +var WithEnviron = func(env []string) func(c *commandeer) { + return func(c *commandeer) { + setOrAppend := func(s string) { + k1, _ := config.SplitEnvVar(s) + var found bool + for i, v := range c.env { + k2, _ := config.SplitEnvVar(v) + if k1 == k2 { + found = true + c.env[i] = s + } + } + + if !found { + c.env = append(c.env, s) + } + } + + for _, s := range env { + setOrAppend(s) + } + } +} + +// New creates a new Exec using the provided security config. +func New(cfg security.Config, workingDir string, log loggers.Logger) *Exec { + var baseEnviron []string + for _, v := range os.Environ() { + k, _ := config.SplitEnvVar(v) + if cfg.Exec.OsEnv.Accept(k) { + baseEnviron = append(baseEnviron, v) + } + } + + return &Exec{ + sc: cfg, + workingDir: workingDir, + infol: log.InfoCommand("exec"), + baseEnviron: baseEnviron, + nodeRunnerCache: hmaps.NewCache[string, func(arg ...any) (Runner, error)](), + } +} + +// IsNotFound reports whether this is an error about a binary not found. +func IsNotFound(err error) bool { + var notFoundErr *NotFoundError + return errors.As(err, ¬FoundErr) +} + +// Exec enforces a security policy for commands run via os/exec. +type Exec struct { + sc security.Config + workingDir string + infol logg.LevelLogger + + // os.Environ filtered by the Exec.OsEnviron whitelist filter. + baseEnviron []string + + // Additional absolute paths to allow reading from in the Node.js permission model. + nodeReadPaths []string + + nodeRunnerCache *hmaps.Cache[string, func(arg ...any) (Runner, error)] +} + +// SetNodeReadPaths sets additional absolute paths to allow reading from +// in the Node.js permission model (e.g. Hugo module cache directories). +func (e *Exec) SetNodeReadPaths(paths []string) { + e.nodeReadPaths = paths +} + +func (e *Exec) New(name string, arg ...any) (Runner, error) { + return e.new(name, "", arg...) +} + +// New will fail if name is not allowed according to the configured security policy. +// Else a configured Runner will be returned ready to be Run. +func (e *Exec) new(name string, fullyQualifiedName string, arg ...any) (Runner, error) { + if err := e.sc.CheckAllowedExec(name); err != nil { + return nil, err + } + + env := make([]string, len(e.baseEnviron)) + copy(env, e.baseEnviron) + + cm := &commandeer{ + name: name, + fullyQualifiedName: fullyQualifiedName, + env: env, + } + + return cm.command(arg...) +} + +type binaryLocation int + +func (b binaryLocation) String() string { + switch b { + case binaryLocationNodeModules: + return "node_modules/.bin" + case binaryLocationPath: + return "PATH" + } + return "unknown" +} + +const ( + binaryLocationNodeModules binaryLocation = iota + 1 + binaryLocationPath +) + +// Npx finds and runs a Node.js tool. The binary is located first in +// WORKINGDIR/node_modules/.bin, then in PATH. The tool is always invoked via +// "node [--permission ] + {{- else }} + {{- with . | fingerprint }} + + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/create/skeletons/theme/layouts/_partials/header.html b/create/skeletons/theme/layouts/_partials/header.html new file mode 100644 index 0000000..7980a00 --- /dev/null +++ b/create/skeletons/theme/layouts/_partials/header.html @@ -0,0 +1,2 @@ +

{{ site.Title }}

+{{ partial "menu.html" (dict "menuID" "main" "page" .) }} diff --git a/create/skeletons/theme/layouts/_partials/menu.html b/create/skeletons/theme/layouts/_partials/menu.html new file mode 100644 index 0000000..14245b5 --- /dev/null +++ b/create/skeletons/theme/layouts/_partials/menu.html @@ -0,0 +1,51 @@ +{{- /* +Renders a menu for the given menu ID. + +@context {page} page The current page. +@context {string} menuID The menu ID. + +@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }} +*/}} + +{{- $page := .page }} +{{- $menuID := .menuID }} + +{{- with index site.Menus $menuID }} + +{{- end }} + +{{- define "_partials/inline/menu/walk.html" }} + {{- $page := .page }} + {{- range .menuEntries }} + {{- $attrs := dict "href" .URL }} + {{- if $page.IsMenuCurrent .Menu . }} + {{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }} + {{- else if $page.HasMenuCurrent .Menu .}} + {{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }} + {{- end }} + {{- $name := .Name }} + {{- with .Identifier }} + {{- with T . }} + {{- $name = . }} + {{- end }} + {{- end }} +
  • + {{ $name }} + {{- with .Children }} +
      + {{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }} +
    + {{- end }} +
  • + {{- end }} +{{- end }} diff --git a/create/skeletons/theme/layouts/_partials/terms.html b/create/skeletons/theme/layouts/_partials/terms.html new file mode 100644 index 0000000..8a6ebec --- /dev/null +++ b/create/skeletons/theme/layouts/_partials/terms.html @@ -0,0 +1,23 @@ +{{- /* +For a given taxonomy, renders a list of terms assigned to the page. + +@context {page} page The current page. +@context {string} taxonomy The taxonomy. + +@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }} +*/}} + +{{- $page := .page }} +{{- $taxonomy := .taxonomy }} + +{{- with $page.GetTerms $taxonomy }} + {{- $label := (index . 0).Parent.LinkTitle }} +
    +
    {{ $label }}:
    + +
    +{{- end }} diff --git a/create/skeletons/theme/layouts/baseof.html b/create/skeletons/theme/layouts/baseof.html new file mode 100644 index 0000000..7d17aa5 --- /dev/null +++ b/create/skeletons/theme/layouts/baseof.html @@ -0,0 +1,17 @@ + + + + {{ partial "head.html" . }} + + +
    + {{ partial "header.html" . }} +
    +
    + {{ block "main" . }}{{ end }} +
    +
    + {{ partial "footer.html" . }} +
    + + diff --git a/create/skeletons/theme/layouts/home.html b/create/skeletons/theme/layouts/home.html new file mode 100644 index 0000000..0c76425 --- /dev/null +++ b/create/skeletons/theme/layouts/home.html @@ -0,0 +1,9 @@ +{{ define "main" }} + {{ .Content }} + {{ range site.RegularPages }} +
    +

    {{ .LinkTitle }}

    + {{ .Summary }} +
    + {{ end }} +{{ end }} diff --git a/create/skeletons/theme/layouts/page.html b/create/skeletons/theme/layouts/page.html new file mode 100644 index 0000000..7e286c8 --- /dev/null +++ b/create/skeletons/theme/layouts/page.html @@ -0,0 +1,10 @@ +{{ define "main" }} +

    {{ .Title }}

    + + {{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }} + {{ $dateHuman := .Date | time.Format ":date_long" }} + + + {{ .Content }} + {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }} +{{ end }} diff --git a/create/skeletons/theme/layouts/section.html b/create/skeletons/theme/layouts/section.html new file mode 100644 index 0000000..748f2f5 --- /dev/null +++ b/create/skeletons/theme/layouts/section.html @@ -0,0 +1,10 @@ +{{ define "main" }} +

    {{ .Title }}

    + {{ .Content }} + {{ range .Pages }} +
    +

    {{ .LinkTitle }}

    + {{ .Summary }} +
    + {{ end }} +{{ end }} diff --git a/create/skeletons/theme/layouts/taxonomy.html b/create/skeletons/theme/layouts/taxonomy.html new file mode 100644 index 0000000..c2e7875 --- /dev/null +++ b/create/skeletons/theme/layouts/taxonomy.html @@ -0,0 +1,7 @@ +{{ define "main" }} +

    {{ .Title }}

    + {{ .Content }} + {{ range .Pages }} +

    {{ .LinkTitle }}

    + {{ end }} +{{ end }} diff --git a/create/skeletons/theme/layouts/term.html b/create/skeletons/theme/layouts/term.html new file mode 100644 index 0000000..c2e7875 --- /dev/null +++ b/create/skeletons/theme/layouts/term.html @@ -0,0 +1,7 @@ +{{ define "main" }} +

    {{ .Title }}

    + {{ .Content }} + {{ range .Pages }} +

    {{ .LinkTitle }}

    + {{ end }} +{{ end }} diff --git a/create/skeletons/theme/static/favicon.ico b/create/skeletons/theme/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..67f8b777851849527933b78b1f98bd564d8255d1 GIT binary patch literal 15406 zcmeI3%WEH16vro|f`O{FV5Nc>unpZA42Xe(zeP4Fn0Fu>#YKe(c41L;qszMJ-bXDi z>L1W57+rSdLS5OG#!V^IKxvbj6n@gC#QOREdT$vg^US^Tf(#t)%-r+%o^#KA&hHll zeL;V)Z(l$;7%c7yg4co|7#w``eB{|6_?CCW!=`>H2!44k2#&ym2ojIpQ{=j)r>9?; zot^z+et!PXxw*M}BHinq>XMXKboc`~d=&+uPNklCgSOc%pC zkdxhQd};55Lm1#0hSD%6>D`)|ni^JI((I(w8QJADo{YL>VsSr%Ckg*U$-2s8+X=sG z{FIYm*irl0d{!y{D{f@u4p-n@#^$z+5S`e?RJu8p?Lq>0x`@}vIu7kfT z8Od5bMN4D{&~ch_$NgBcM#n%A`@~N^|ABlif3x_ci!t%#JQ&cWarai1&Z6~TkWOa9 zw}_GQ!9UZ~o(wFidYcy6DoF0rq<yz2-5e zTyl`ty!pUE-U+vC*KNN6@vklal>5+`8rxs}uh#LW?(tW105Q;7(Wx&Lm6uJ~)`e`X z^RF;~r?T~jY{_Q$A_iJ-m%jcoHny$5gr&~)r;P=1C}sVP-puv4#i0EM{FfkKed1Em zy4%`}me{Us{}sjpj#~DgVGO#rYen+cu9FI~=yTd>3AzfcLA`SnbFXu|pT#pXGc&>2(2!(N+83v|%y-!B zH|JI#fb9Zw1NxWuxyA*n7Wlp875YYr(Gs{zKTE)l_AX1Q6wmo%>|9k+6aP<2dk>lant}U*_E8rG3D-5uK;;yPx-8W%=Kfda!;c%(fl0 ze&XHrrr^uM0oGf>Z1DFnHhqLl>1%^5TzTuk|3H|*ul4(0o-gE;Z3>^kyq|VB2&R>$ z;3|M)g|TQza{|6pwl*t(FLk^UW^(9F%1?ditXdD)PeI>TZP%AQuxqb<#h1RS^?+T! z8Ck2^t}lCF{~KLC_TZ^~%@^LN9`I|g*;@RHecST~{FkHc(w1kiUj^&=w&0Dy`6E8G zdj4Yla1TF#N4vKWh(zA3A5dm)G|O@NEjS_AkOu zAJA`duQdQy-kiND%)+h<+}gi?*&_V<{_uGJE+68oBmMcH-Rl|t`yCgB%e&5Zsqq=>yyS- z+H#S&ls3nqdmTC0`~O|AJhXPJKi1jW{{U!5hztM# literal 0 HcmV?d00001 diff --git a/deploy/cloudfront.go b/deploy/cloudfront.go new file mode 100644 index 0000000..3202a73 --- /dev/null +++ b/deploy/cloudfront.go @@ -0,0 +1,72 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package deploy + +import ( + "context" + "net/url" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" + "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "github.com/gohugoio/hugo/deploy/deployconfig" + gcaws "gocloud.dev/aws" +) + +// V2ConfigFromURLParams will fail for any unknown params, so we need to remove them. +// This is a mysterious API, but inspecting the code the known params are: +var v2ConfigValidParams = map[string]bool{ + "endpoint": true, + "region": true, + "profile": true, + "awssdk": true, +} + +// InvalidateCloudFront invalidates the CloudFront cache for distributionID. +// Uses AWS credentials config from the bucket URL. +func InvalidateCloudFront(ctx context.Context, target *deployconfig.Target) error { + u, err := url.Parse(target.URL) + if err != nil { + return err + } + vals := u.Query() + + // Remove any unknown params. + for k := range vals { + if !v2ConfigValidParams[k] { + vals.Del(k) + } + } + + cfg, err := gcaws.V2ConfigFromURLParams(ctx, vals) + if err != nil { + return err + } + cf := cloudfront.NewFromConfig(cfg) + req := &cloudfront.CreateInvalidationInput{ + DistributionId: aws.String(target.CloudFrontDistributionID), + InvalidationBatch: &types.InvalidationBatch{ + CallerReference: aws.String(time.Now().Format("20060102150405")), + Paths: &types.Paths{ + Items: []string{"/*"}, + Quantity: aws.Int32(1), + }, + }, + } + _, err = cf.CreateInvalidation(ctx, req) + return err +} diff --git a/deploy/deploy.go b/deploy/deploy.go new file mode 100644 index 0000000..57e1f41 --- /dev/null +++ b/deploy/deploy.go @@ -0,0 +1,763 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package deploy + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/md5" + "encoding/hex" + "errors" + "fmt" + "io" + "mime" + "os" + "path/filepath" + "regexp" + "runtime" + "sort" + "strings" + "sync" + + "github.com/dustin/go-humanize" + "github.com/gobwas/glob" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/para" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/deploy/deployconfig" + "github.com/gohugoio/hugo/media" + "github.com/spf13/afero" + "golang.org/x/text/unicode/norm" + + "gocloud.dev/blob" + _ "gocloud.dev/blob/fileblob" // import + _ "gocloud.dev/blob/gcsblob" // import + _ "gocloud.dev/blob/s3blob" // import + "gocloud.dev/gcerrors" +) + +// Deployer supports deploying the site to target cloud providers. +type Deployer struct { + localFs afero.Fs + bucket *blob.Bucket + + mediaTypes media.Types // Hugo's MediaType to guess ContentType + quiet bool // true reduces STDOUT // TODO(bep) remove, this is a global feature. + + cfg deployconfig.DeployConfig + logger loggers.Logger + + target *deployconfig.Target // the target to deploy to + + // For tests... + summary deploySummary // summary of latest Deploy results +} + +type deploySummary struct { + NumLocal, NumRemote, NumUploads, NumDeletes int +} + +const metaMD5Hash = "md5chksum" // the meta key to store md5hash in + +// New constructs a new *Deployer. +func New(cfg config.AllProvider, logger loggers.Logger, localFs afero.Fs) (*Deployer, error) { + dcfg := cfg.GetConfigSection(deployconfig.DeploymentConfigKey).(deployconfig.DeployConfig) + targetName := dcfg.Target + + if len(dcfg.Targets) == 0 { + return nil, errors.New("no deployment targets found") + } + mediaTypes := cfg.GetConfigSection("mediaTypes").(media.Types) + + // Find the target to deploy to. + var tgt *deployconfig.Target + if targetName == "" { + // Default to the first target. + tgt = dcfg.Targets[0] + } else { + for _, t := range dcfg.Targets { + if t.Name == targetName { + tgt = t + } + } + if tgt == nil { + return nil, fmt.Errorf("deployment target %q not found", targetName) + } + } + + return &Deployer{ + localFs: localFs, + target: tgt, + quiet: cfg.BuildExpired(), + mediaTypes: mediaTypes, + cfg: dcfg, + }, nil +} + +func (d *Deployer) openBucket(ctx context.Context) (*blob.Bucket, error) { + if d.bucket != nil { + return d.bucket, nil + } + d.logger.Printf("Deploying to target %q (%s)\n", d.target.Name, d.target.URL) + return blob.OpenBucket(ctx, d.target.URL) +} + +// Deploy deploys the site to a target. +func (d *Deployer) Deploy(ctx context.Context) error { + if d.logger == nil { + d.logger = loggers.NewDefault() + } + + bucket, err := d.openBucket(ctx) + if err != nil { + return err + } + + if d.cfg.Workers <= 0 { + d.cfg.Workers = 10 + } + + // Load local files from the source directory. + var include, exclude glob.Glob + var mappath func(string) string + if d.target != nil { + include, exclude = d.target.IncludeGlob, d.target.ExcludeGlob + if d.target.StripIndexHTML { + mappath = stripIndexHTML + } + } + local, err := d.walkLocal(d.localFs, d.cfg.Matchers, include, exclude, d.mediaTypes, mappath) + if err != nil { + return err + } + d.logger.Infof("Found %d local files.\n", len(local)) + d.summary.NumLocal = len(local) + + // Load remote files from the target. + remote, err := d.walkRemote(ctx, bucket, include, exclude) + if err != nil { + return err + } + d.logger.Infof("Found %d remote files.\n", len(remote)) + d.summary.NumRemote = len(remote) + + // Diff local vs remote to see what changes need to be applied. + uploads, deletes := d.findDiffs(local, remote, d.cfg.Force) + d.summary.NumUploads = len(uploads) + d.summary.NumDeletes = len(deletes) + if len(uploads)+len(deletes) == 0 { + if !d.quiet { + d.logger.Println("No changes required.") + } + return nil + } + if !d.quiet { + d.logger.Println(summarizeChanges(uploads, deletes)) + } + + // Ask for confirmation before proceeding. + if d.cfg.Confirm && !d.cfg.DryRun { + fmt.Printf("Continue? (Y/n) ") + var confirm string + if _, err := fmt.Scanln(&confirm); err != nil { + return err + } + if confirm != "" && confirm[0] != 'y' && confirm[0] != 'Y' { + return errors.New("aborted") + } + } + + // Order the uploads. They are organized in groups; all uploads in a group + // must be complete before moving on to the next group. + uploadGroups := applyOrdering(d.cfg.Ordering, uploads) + + nParallel := d.cfg.Workers + var errs []error + var errMu sync.Mutex // protects errs + + for _, uploads := range uploadGroups { + // Short-circuit for an empty group. + if len(uploads) == 0 { + continue + } + + // Within the group, apply uploads in parallel. + sem := make(chan struct{}, nParallel) + for _, upload := range uploads { + if d.cfg.DryRun { + if !d.quiet { + d.logger.Printf("[DRY RUN] Would upload: %v\n", upload) + } + continue + } + + sem <- struct{}{} + go func(upload *fileToUpload) { + if err := d.doSingleUpload(ctx, bucket, upload); err != nil { + errMu.Lock() + defer errMu.Unlock() + errs = append(errs, err) + } + <-sem + }(upload) + } + // Wait for all uploads in the group to finish. + for n := nParallel; n > 0; n-- { + sem <- struct{}{} + } + } + + if d.cfg.MaxDeletes != -1 && len(deletes) > d.cfg.MaxDeletes { + d.logger.Warnf("Skipping %d deletes because it is more than --maxDeletes (%d). If this is expected, set --maxDeletes to a larger number, or -1 to disable this check.\n", len(deletes), d.cfg.MaxDeletes) + d.summary.NumDeletes = 0 + } else { + // Apply deletes in parallel. + sort.Slice(deletes, func(i, j int) bool { return deletes[i] < deletes[j] }) + sem := make(chan struct{}, nParallel) + for _, del := range deletes { + if d.cfg.DryRun { + if !d.quiet { + d.logger.Printf("[DRY RUN] Would delete %s\n", del) + } + continue + } + sem <- struct{}{} + go func(del string) { + d.logger.Infof("Deleting %s...\n", del) + if err := bucket.Delete(ctx, del); err != nil { + if gcerrors.Code(err) == gcerrors.NotFound { + d.logger.Warnf("Failed to delete %q because it wasn't found: %v", del, err) + } else { + errMu.Lock() + defer errMu.Unlock() + errs = append(errs, err) + } + } + <-sem + }(del) + } + // Wait for all deletes to finish. + for n := nParallel; n > 0; n-- { + sem <- struct{}{} + } + } + + if len(errs) > 0 { + if !d.quiet { + d.logger.Printf("Encountered %d errors.\n", len(errs)) + } + return errs[0] + } + if !d.quiet { + d.logger.Println("Success!") + } + + if d.cfg.InvalidateCDN { + if d.target.CloudFrontDistributionID != "" { + if d.cfg.DryRun { + if !d.quiet { + d.logger.Printf("[DRY RUN] Would invalidate CloudFront CDN with ID %s\n", d.target.CloudFrontDistributionID) + } + } else { + d.logger.Println("Invalidating CloudFront CDN...") + if err := InvalidateCloudFront(ctx, d.target); err != nil { + d.logger.Printf("Failed to invalidate CloudFront CDN: %v\n", err) + return err + } + } + } + if d.target.GoogleCloudCDNOrigin != "" { + if d.cfg.DryRun { + if !d.quiet { + d.logger.Printf("[DRY RUN] Would invalidate Google Cloud CDN with origin %s\n", d.target.GoogleCloudCDNOrigin) + } + } else { + d.logger.Println("Invalidating Google Cloud CDN...") + if err := InvalidateGoogleCloudCDN(ctx, d.target.GoogleCloudCDNOrigin); err != nil { + d.logger.Printf("Failed to invalidate Google Cloud CDN: %v\n", err) + return err + } + } + } + d.logger.Println("Success!") + } + return nil +} + +// summarizeChanges creates a text description of the proposed changes. +func summarizeChanges(uploads []*fileToUpload, deletes []string) string { + uploadSize := int64(0) + for _, u := range uploads { + uploadSize += u.Local.UploadSize + } + return fmt.Sprintf("Identified %d file(s) to upload, totaling %s, and %d file(s) to delete.", len(uploads), humanize.Bytes(uint64(uploadSize)), len(deletes)) +} + +// doSingleUpload executes a single file upload. +func (d *Deployer) doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error { + d.logger.Infof("Uploading %v...\n", upload) + opts := &blob.WriterOptions{ + CacheControl: upload.Local.CacheControl(), + ContentEncoding: upload.Local.ContentEncoding(), + ContentType: upload.Local.ContentType(), + Metadata: map[string]string{metaMD5Hash: hex.EncodeToString(upload.Local.MD5())}, + } + w, err := bucket.NewWriter(ctx, upload.Local.SlashPath, opts) + if err != nil { + return err + } + r, err := upload.Local.Reader() + if err != nil { + return err + } + defer r.Close() + _, err = io.Copy(w, r) + if err != nil { + return err + } + if err := w.Close(); err != nil { + return err + } + return nil +} + +// localFile represents a local file from the source. Use newLocalFile to +// construct one. +type localFile struct { + // NativePath is the native path to the file (using file.Separator). + NativePath string + // SlashPath is NativePath converted to use /. + SlashPath string + // UploadSize is the size of the content to be uploaded. It may not + // be the same as the local file size if the content will be + // gzipped before upload. + UploadSize int64 + + fs afero.Fs + matcher *deployconfig.Matcher + md5 []byte // cache + gzipped bytes.Buffer // cached of gzipped contents if gzipping + mediaTypes media.Types +} + +// newLocalFile initializes a *localFile. +func newLocalFile(fs afero.Fs, nativePath, slashpath string, m *deployconfig.Matcher, mt media.Types) (*localFile, error) { + f, err := fs.Open(nativePath) + if err != nil { + return nil, err + } + defer f.Close() + lf := &localFile{ + NativePath: nativePath, + SlashPath: slashpath, + fs: fs, + matcher: m, + mediaTypes: mt, + } + if m != nil && m.Gzip { + // We're going to gzip the content. Do it once now, and cache the result + // in gzipped. The UploadSize is the size of the gzipped content. + gz := gzip.NewWriter(&lf.gzipped) + if _, err := io.Copy(gz, f); err != nil { + return nil, err + } + if err := gz.Close(); err != nil { + return nil, err + } + lf.UploadSize = int64(lf.gzipped.Len()) + } else { + // Raw content. Just get the UploadSize. + info, err := f.Stat() + if err != nil { + return nil, err + } + lf.UploadSize = info.Size() + } + return lf, nil +} + +// Reader returns an io.ReadCloser for reading the content to be uploaded. +// The caller must call Close on the returned ReaderCloser. +// The reader content may not be the same as the local file content due to +// gzipping. +func (lf *localFile) Reader() (io.ReadCloser, error) { + if lf.matcher != nil && lf.matcher.Gzip { + // We've got the gzipped contents cached in gzipped. + // Note: we can't use lf.gzipped directly as a Reader, since we it discards + // data after it is read, and we may read it more than once. + return io.NopCloser(bytes.NewReader(lf.gzipped.Bytes())), nil + } + // Not expected to fail since we did it successfully earlier in newLocalFile, + // but could happen due to changes in the underlying filesystem. + return lf.fs.Open(lf.NativePath) +} + +// CacheControl returns the Cache-Control header to use for lf, based on the +// first matching matcher (if any). +func (lf *localFile) CacheControl() string { + if lf.matcher == nil { + return "" + } + return lf.matcher.CacheControl +} + +// ContentEncoding returns the Content-Encoding header to use for lf, based +// on the matcher's Content-Encoding and Gzip fields. +func (lf *localFile) ContentEncoding() string { + if lf.matcher == nil { + return "" + } + if lf.matcher.Gzip { + return "gzip" + } + return lf.matcher.ContentEncoding +} + +// ContentType returns the Content-Type header to use for lf. +// It first checks if there's a Content-Type header configured via a matching +// matcher; if not, it tries to generate one based on the filename extension. +// If this fails, the Content-Type will be the empty string. In this case, Go +// Cloud will automatically try to infer a Content-Type based on the file +// content. +func (lf *localFile) ContentType() string { + if lf.matcher != nil && lf.matcher.ContentType != "" { + return lf.matcher.ContentType + } + + ext := filepath.Ext(lf.NativePath) + if mimeType, _, found := lf.mediaTypes.GetFirstBySuffix(strings.TrimPrefix(ext, ".")); found { + return mimeType.Type + } + + return mime.TypeByExtension(ext) +} + +// Force returns true if the file should be forced to re-upload based on the +// matching matcher. +func (lf *localFile) Force() bool { + return lf.matcher != nil && lf.matcher.Force +} + +// MD5 returns an MD5 hash of the content to be uploaded. +func (lf *localFile) MD5() []byte { + if len(lf.md5) > 0 { + return lf.md5 + } + h := md5.New() + r, err := lf.Reader() + if err != nil { + return nil + } + defer r.Close() + if _, err := io.Copy(h, r); err != nil { + return nil + } + lf.md5 = h.Sum(nil) + return lf.md5 +} + +// knownHiddenDirectory checks if the specified name is a well known +// hidden directory. +func knownHiddenDirectory(name string) bool { + knownDirectories := []string{ + ".well-known", + } + + for _, dir := range knownDirectories { + if name == dir { + return true + } + } + return false +} + +// walkLocal walks the source directory and returns a flat list of files, +// using localFile.SlashPath as the map keys. +func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, include, exclude glob.Glob, mediaTypes media.Types, mappath func(string) string) (map[string]*localFile, error) { + retval := make(map[string]*localFile) + var mu sync.Mutex + + workers := para.New(d.cfg.Workers) + g, _ := workers.Start(context.Background()) + + err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + // Skip hidden directories. + if path != "" && strings.HasPrefix(info.Name(), ".") { + // Except for specific hidden directories + if !knownHiddenDirectory(info.Name()) { + return filepath.SkipDir + } + } + return nil + } + + // .DS_Store is an internal MacOS attribute file; skip it. + if info.Name() == ".DS_Store" { + return nil + } + + // Process each file in a worker + g.Run(func() error { + // When a file system is HFS+, its filepath is in NFD form. + if runtime.GOOS == "darwin" { + path = norm.NFC.String(path) + } + + // Check include/exclude matchers. + slashpath := filepath.ToSlash(path) + if include != nil && !include.Match(slashpath) { + d.logger.Infof(" dropping %q due to include\n", slashpath) + return nil + } + if exclude != nil && exclude.Match(slashpath) { + d.logger.Infof(" dropping %q due to exclude\n", slashpath) + return nil + } + + // Find the first matching matcher (if any). + var m *deployconfig.Matcher + for _, cur := range matchers { + if cur.Matches(slashpath) { + m = cur + break + } + } + // Apply any additional modifications to the local path, to map it to + // the remote path. + if mappath != nil { + slashpath = mappath(slashpath) + } + lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes) + if err != nil { + return err + } + mu.Lock() + retval[lf.SlashPath] = lf + mu.Unlock() + return nil + }) + return nil + }) + if err != nil { + return nil, err + } + if err := g.Wait(); err != nil { + return nil, err + } + return retval, nil +} + +// stripIndexHTML remaps keys matching "/index.html" to "/". +func stripIndexHTML(slashpath string) string { + const suffix = "/index.html" + if strings.HasSuffix(slashpath, suffix) { + return slashpath[:len(slashpath)-len(suffix)+1] + } + return slashpath +} + +// walkRemote walks the target bucket and returns a flat list. +func (d *Deployer) walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.Glob) (map[string]*blob.ListObject, error) { + retval := map[string]*blob.ListObject{} + iter := bucket.List(nil) + for { + obj, err := iter.Next(ctx) + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + // Check include/exclude matchers. + if include != nil && !include.Match(obj.Key) { + d.logger.Infof(" remote dropping %q due to include\n", obj.Key) + continue + } + if exclude != nil && exclude.Match(obj.Key) { + d.logger.Infof(" remote dropping %q due to exclude\n", obj.Key) + continue + } + // If the remote didn't give us an MD5, use remote attributes MD5, if that doesn't exist compute one. + // This can happen for some providers (e.g., fileblob, which uses the + // local filesystem), but not for the most common Cloud providers + // (S3, GCS, Azure). Although, it can happen for S3 if the blob was uploaded + // via a multi-part upload. + // Although it's unfortunate to have to read the file, it's likely better + // than assuming a delta and re-uploading it. + if len(obj.MD5) == 0 { + var attrMD5 []byte + attrs, err := bucket.Attributes(ctx, obj.Key) + if err == nil { + md5String, exists := attrs.Metadata[metaMD5Hash] + if exists { + attrMD5, _ = hex.DecodeString(md5String) + } + } + if len(attrMD5) == 0 { + r, err := bucket.NewReader(ctx, obj.Key, nil) + if err == nil { + h := md5.New() + if _, err := io.Copy(h, r); err == nil { + obj.MD5 = h.Sum(nil) + } + r.Close() + } + } else { + obj.MD5 = attrMD5 + } + } + retval[obj.Key] = obj + } + return retval, nil +} + +// uploadReason is an enum of reasons why a file must be uploaded. +type uploadReason string + +const ( + reasonUnknown uploadReason = "unknown" + reasonNotFound uploadReason = "not found at target" + reasonForce uploadReason = "--force" + reasonSize uploadReason = "size differs" + reasonMD5Differs uploadReason = "md5 differs" + reasonMD5Missing uploadReason = "remote md5 missing" +) + +// fileToUpload represents a single local file that should be uploaded to +// the target. +type fileToUpload struct { + Local *localFile + Reason uploadReason +} + +func (u *fileToUpload) String() string { + details := []string{humanize.Bytes(uint64(u.Local.UploadSize))} + if s := u.Local.CacheControl(); s != "" { + details = append(details, fmt.Sprintf("Cache-Control: %q", s)) + } + if s := u.Local.ContentEncoding(); s != "" { + details = append(details, fmt.Sprintf("Content-Encoding: %q", s)) + } + if s := u.Local.ContentType(); s != "" { + details = append(details, fmt.Sprintf("Content-Type: %q", s)) + } + return fmt.Sprintf("%s (%s): %v", u.Local.SlashPath, strings.Join(details, ", "), u.Reason) +} + +// findDiffs diffs localFiles vs remoteFiles to see what changes should be +// applied to the remote target. It returns a slice of *fileToUpload and a +// slice of paths for files to delete. +func (d *Deployer) findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.ListObject, force bool) ([]*fileToUpload, []string) { + var uploads []*fileToUpload + var deletes []string + + found := map[string]bool{} + for path, lf := range localFiles { + upload := false + reason := reasonUnknown + + if remoteFile, ok := remoteFiles[path]; ok { + // The file exists in remote. Let's see if we need to upload it anyway. + + // TODO: We don't register a diff if the metadata (e.g., Content-Type + // header) has changed. This would be difficult/expensive to detect; some + // providers return metadata along with their "List" result, but others + // (notably AWS S3) do not, so gocloud.dev's blob.Bucket doesn't expose + // it in the list result. It would require a separate request per blob + // to fetch. At least for now, we work around this by documenting it and + // providing a "force" flag (to re-upload everything) and a "force" bool + // per matcher (to re-upload all files in a matcher whose headers may have + // changed). + // Idea: extract a sample set of 1 file per extension + 1 file per matcher + // and check those files? + if force { + upload = true + reason = reasonForce + } else if lf.Force() { + upload = true + reason = reasonForce + } else if lf.UploadSize != remoteFile.Size { + upload = true + reason = reasonSize + } else if len(remoteFile.MD5) == 0 { + // This shouldn't happen unless the remote didn't give us an MD5 hash + // from List, AND we failed to compute one by reading the remote file. + // Default to considering the files different. + upload = true + reason = reasonMD5Missing + } else if !bytes.Equal(lf.MD5(), remoteFile.MD5) { + upload = true + reason = reasonMD5Differs + } + found[path] = true + } else { + // The file doesn't exist in remote. + upload = true + reason = reasonNotFound + } + if upload { + d.logger.Debugf("%s needs to be uploaded: %v\n", path, reason) + uploads = append(uploads, &fileToUpload{lf, reason}) + } else { + d.logger.Debugf("%s exists at target and does not need to be uploaded", path) + } + } + + // Remote files that weren't found locally should be deleted. + for path := range remoteFiles { + if !found[path] { + deletes = append(deletes, path) + } + } + return uploads, deletes +} + +// applyOrdering returns an ordered slice of slices of uploads. +// +// The returned slice will have length len(ordering)+1. +// +// The subslice at index i, for i = 0 ... len(ordering)-1, will have all of the +// uploads whose Local.SlashPath matched the regex at ordering[i] (but not any +// previous ordering regex). +// The subslice at index len(ordering) will have the remaining uploads that +// didn't match any ordering regex. +// +// The subslices are sorted by Local.SlashPath. +func applyOrdering(ordering []*regexp.Regexp, uploads []*fileToUpload) [][]*fileToUpload { + // Sort the whole slice by Local.SlashPath first. + sort.Slice(uploads, func(i, j int) bool { return uploads[i].Local.SlashPath < uploads[j].Local.SlashPath }) + + retval := make([][]*fileToUpload, len(ordering)+1) + for _, u := range uploads { + matched := false + for i, re := range ordering { + if re.MatchString(u.Local.SlashPath) { + retval[i] = append(retval[i], u) + matched = true + break + } + } + if !matched { + retval[len(ordering)] = append(retval[len(ordering)], u) + } + } + return retval +} diff --git a/deploy/deploy_azure.go b/deploy/deploy_azure.go new file mode 100644 index 0000000..b1ce735 --- /dev/null +++ b/deploy/deploy_azure.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !solaris && withdeploy + +package deploy + +import ( + _ "gocloud.dev/blob" + _ "gocloud.dev/blob/azureblob" // import +) diff --git a/deploy/deploy_test.go b/deploy/deploy_test.go new file mode 100644 index 0000000..bdc8299 --- /dev/null +++ b/deploy/deploy_test.go @@ -0,0 +1,1102 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package deploy + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/md5" + "fmt" + "io" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "testing" + + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/deploy/deployconfig" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/media" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/spf13/afero" + "gocloud.dev/blob" + "gocloud.dev/blob/fileblob" + "gocloud.dev/blob/memblob" +) + +func TestFindDiffs(t *testing.T) { + hash1 := []byte("hash 1") + hash2 := []byte("hash 2") + makeLocal := func(path string, size int64, hash []byte) *localFile { + return &localFile{NativePath: path, SlashPath: filepath.ToSlash(path), UploadSize: size, md5: hash} + } + makeRemote := func(path string, size int64, hash []byte) *blob.ListObject { + return &blob.ListObject{Key: path, Size: size, MD5: hash} + } + + tests := []struct { + Description string + Local []*localFile + Remote []*blob.ListObject + Force bool + WantUpdates []*fileToUpload + WantDeletes []string + }{ + { + Description: "empty -> no diffs", + }, + { + Description: "local == remote -> no diffs", + Local: []*localFile{ + makeLocal("aaa", 1, hash1), + makeLocal("bbb", 2, hash1), + makeLocal("ccc", 3, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, hash1), + makeRemote("bbb", 2, hash1), + makeRemote("ccc", 3, hash2), + }, + }, + { + Description: "local w/ separators == remote -> no diffs", + Local: []*localFile{ + makeLocal(filepath.Join("aaa", "aaa"), 1, hash1), + makeLocal(filepath.Join("bbb", "bbb"), 2, hash1), + makeLocal(filepath.Join("ccc", "ccc"), 3, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa/aaa", 1, hash1), + makeRemote("bbb/bbb", 2, hash1), + makeRemote("ccc/ccc", 3, hash2), + }, + }, + { + Description: "local == remote with force flag true -> diffs", + Local: []*localFile{ + makeLocal("aaa", 1, hash1), + makeLocal("bbb", 2, hash1), + makeLocal("ccc", 3, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, hash1), + makeRemote("bbb", 2, hash1), + makeRemote("ccc", 3, hash2), + }, + Force: true, + WantUpdates: []*fileToUpload{ + {makeLocal("aaa", 1, nil), reasonForce}, + {makeLocal("bbb", 2, nil), reasonForce}, + {makeLocal("ccc", 3, nil), reasonForce}, + }, + }, + { + Description: "local == remote with route.Force true -> diffs", + Local: []*localFile{ + {NativePath: "aaa", SlashPath: "aaa", UploadSize: 1, matcher: &deployconfig.Matcher{Force: true}, md5: hash1}, + makeLocal("bbb", 2, hash1), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, hash1), + makeRemote("bbb", 2, hash1), + }, + WantUpdates: []*fileToUpload{ + {makeLocal("aaa", 1, nil), reasonForce}, + }, + }, + { + Description: "extra local file -> upload", + Local: []*localFile{ + makeLocal("aaa", 1, hash1), + makeLocal("bbb", 2, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, hash1), + }, + WantUpdates: []*fileToUpload{ + {makeLocal("bbb", 2, nil), reasonNotFound}, + }, + }, + { + Description: "extra remote file -> delete", + Local: []*localFile{ + makeLocal("aaa", 1, hash1), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, hash1), + makeRemote("bbb", 2, hash2), + }, + WantDeletes: []string{"bbb"}, + }, + { + Description: "diffs in size or md5 -> upload", + Local: []*localFile{ + makeLocal("aaa", 1, hash1), + makeLocal("bbb", 2, hash1), + makeLocal("ccc", 1, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("aaa", 1, nil), + makeRemote("bbb", 1, hash1), + makeRemote("ccc", 1, hash1), + }, + WantUpdates: []*fileToUpload{ + {makeLocal("aaa", 1, nil), reasonMD5Missing}, + {makeLocal("bbb", 2, nil), reasonSize}, + {makeLocal("ccc", 1, nil), reasonMD5Differs}, + }, + }, + { + Description: "mix of updates and deletes", + Local: []*localFile{ + makeLocal("same", 1, hash1), + makeLocal("updated", 2, hash1), + makeLocal("updated2", 1, hash2), + makeLocal("new", 1, hash1), + makeLocal("new2", 2, hash2), + }, + Remote: []*blob.ListObject{ + makeRemote("same", 1, hash1), + makeRemote("updated", 1, hash1), + makeRemote("updated2", 1, hash1), + makeRemote("stale", 1, hash1), + makeRemote("stale2", 1, hash1), + }, + WantUpdates: []*fileToUpload{ + {makeLocal("new", 1, nil), reasonNotFound}, + {makeLocal("new2", 2, nil), reasonNotFound}, + {makeLocal("updated", 2, nil), reasonSize}, + {makeLocal("updated2", 1, nil), reasonMD5Differs}, + }, + WantDeletes: []string{"stale", "stale2"}, + }, + } + + for _, tc := range tests { + t.Run(tc.Description, func(t *testing.T) { + local := map[string]*localFile{} + for _, l := range tc.Local { + local[l.SlashPath] = l + } + remote := map[string]*blob.ListObject{} + for _, r := range tc.Remote { + remote[r.Key] = r + } + d := newDeployer() + gotUpdates, gotDeletes := d.findDiffs(local, remote, tc.Force) + gotUpdates = applyOrdering(nil, gotUpdates)[0] + sort.Slice(gotDeletes, func(i, j int) bool { return gotDeletes[i] < gotDeletes[j] }) + if diff := cmp.Diff(gotUpdates, tc.WantUpdates, cmpopts.IgnoreUnexported(localFile{})); diff != "" { + t.Errorf("updates differ:\n%s", diff) + } + if diff := cmp.Diff(gotDeletes, tc.WantDeletes); diff != "" { + t.Errorf("deletes differ:\n%s", diff) + } + }) + } +} + +func TestWalkLocal(t *testing.T) { + tests := map[string]struct { + Given []string + Expect []string + MapPath func(string) string + }{ + "Empty": { + Given: []string{}, + Expect: []string{}, + }, + "Normal": { + Given: []string{"file.txt", "normal_dir/file.txt"}, + Expect: []string{"file.txt", "normal_dir/file.txt"}, + }, + "Hidden": { + Given: []string{"file.txt", ".hidden_dir/file.txt", "normal_dir/file.txt"}, + Expect: []string{"file.txt", "normal_dir/file.txt"}, + }, + "Well Known": { + Given: []string{"file.txt", ".hidden_dir/file.txt", ".well-known/file.txt"}, + Expect: []string{"file.txt", ".well-known/file.txt"}, + }, + "StripIndexHTML": { + Given: []string{"index.html", "file.txt", "dir/index.html", "dir/file.txt"}, + Expect: []string{"index.html", "file.txt", "dir/", "dir/file.txt"}, + MapPath: stripIndexHTML, + }, + } + + for desc, tc := range tests { + t.Run(desc, func(t *testing.T) { + fs := afero.NewMemMapFs() + for _, name := range tc.Given { + dir, _ := path.Split(name) + if dir != "" { + if err := fs.MkdirAll(dir, 0o755); err != nil { + t.Fatal(err) + } + } + if fd, err := fs.Create(name); err != nil { + t.Fatal(err) + } else { + fd.Close() + } + } + d := newDeployer() + if got, err := d.walkLocal(fs, nil, nil, nil, media.DefaultTypes, tc.MapPath); err != nil { + t.Fatal(err) + } else { + expect := map[string]any{} + for _, path := range tc.Expect { + if _, ok := got[path]; !ok { + t.Errorf("expected %q in results, but was not found", path) + } + expect[path] = nil + } + for path := range got { + if _, ok := expect[path]; !ok { + t.Errorf("got %q in results unexpectedly", path) + } + } + } + }) + } +} + +func TestStripIndexHTML(t *testing.T) { + tests := map[string]struct { + Input string + Output string + }{ + "Unmapped": {Input: "normal_file.txt", Output: "normal_file.txt"}, + "Stripped": {Input: "directory/index.html", Output: "directory/"}, + "NoSlash": {Input: "prefix_index.html", Output: "prefix_index.html"}, + "Root": {Input: "index.html", Output: "index.html"}, + } + for desc, tc := range tests { + t.Run(desc, func(t *testing.T) { + got := stripIndexHTML(tc.Input) + if got != tc.Output { + t.Errorf("got %q, expect %q", got, tc.Output) + } + }) + } +} + +func TestStripIndexHTMLMatcher(t *testing.T) { + // StripIndexHTML should not affect matchers. + fs := afero.NewMemMapFs() + if err := fs.Mkdir("dir", 0o755); err != nil { + t.Fatal(err) + } + for _, name := range []string{"index.html", "dir/index.html", "file.txt"} { + if fd, err := fs.Create(name); err != nil { + t.Fatal(err) + } else { + fd.Close() + } + } + d := newDeployer() + const pattern = `\.html$` + matcher := &deployconfig.Matcher{Pattern: pattern, Gzip: true, Re: regexp.MustCompile(pattern)} + if got, err := d.walkLocal(fs, []*deployconfig.Matcher{matcher}, nil, nil, media.DefaultTypes, stripIndexHTML); err != nil { + t.Fatal(err) + } else { + for _, name := range []string{"index.html", "dir/"} { + lf := got[name] + if lf == nil { + t.Errorf("missing file %q", name) + } else if lf.matcher == nil { + t.Errorf("file %q has nil matcher, expect %q", name, pattern) + } + } + const name = "file.txt" + lf := got[name] + if lf == nil { + t.Errorf("missing file %q", name) + } else if lf.matcher != nil { + t.Errorf("file %q has matcher %q, expect nil", name, lf.matcher.Pattern) + } + } +} + +func TestLocalFile(t *testing.T) { + const ( + content = "hello world!" + ) + contentBytes := []byte(content) + contentLen := int64(len(contentBytes)) + contentMD5 := md5.Sum(contentBytes) + var buf bytes.Buffer + gz := gzip.NewWriter(&buf) + if _, err := gz.Write(contentBytes); err != nil { + t.Fatal(err) + } + gz.Close() + gzBytes := buf.Bytes() + gzLen := int64(len(gzBytes)) + gzMD5 := md5.Sum(gzBytes) + + tests := []struct { + Description string + Path string + Matcher *deployconfig.Matcher + MediaTypesConfig map[string]any + WantContent []byte + WantSize int64 + WantMD5 []byte + WantContentType string // empty string is always OK, since content type detection is OS-specific + WantCacheControl string + WantContentEncoding string + }{ + { + Description: "file with no suffix", + Path: "foo", + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + }, + { + Description: "file with .txt suffix", + Path: "foo.txt", + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + }, + { + Description: "CacheControl from matcher", + Path: "foo.txt", + Matcher: &deployconfig.Matcher{CacheControl: "max-age=630720000"}, + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + WantCacheControl: "max-age=630720000", + }, + { + Description: "ContentEncoding from matcher", + Path: "foo.txt", + Matcher: &deployconfig.Matcher{ContentEncoding: "foobar"}, + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + WantContentEncoding: "foobar", + }, + { + Description: "ContentType from matcher", + Path: "foo.txt", + Matcher: &deployconfig.Matcher{ContentType: "foo/bar"}, + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + WantContentType: "foo/bar", + }, + { + Description: "gzipped content", + Path: "foo.txt", + Matcher: &deployconfig.Matcher{Gzip: true}, + WantContent: gzBytes, + WantSize: gzLen, + WantMD5: gzMD5[:], + WantContentEncoding: "gzip", + }, + { + Description: "Custom MediaType", + Path: "foo.hugo", + MediaTypesConfig: map[string]any{ + "hugo/custom": map[string]any{ + "suffixes": []string{"hugo"}, + }, + }, + WantContent: contentBytes, + WantSize: contentLen, + WantMD5: contentMD5[:], + WantContentType: "hugo/custom", + }, + } + + for _, tc := range tests { + t.Run(tc.Description, func(t *testing.T) { + fs := new(afero.MemMapFs) + if err := afero.WriteFile(fs, tc.Path, []byte(content), os.ModePerm); err != nil { + t.Fatal(err) + } + mediaTypes := media.DefaultTypes + if len(tc.MediaTypesConfig) > 0 { + mt, err := media.DecodeTypes(tc.MediaTypesConfig) + if err != nil { + t.Fatal(err) + } + mediaTypes = mt.Config + } + lf, err := newLocalFile(fs, tc.Path, filepath.ToSlash(tc.Path), tc.Matcher, mediaTypes) + if err != nil { + t.Fatal(err) + } + if got := lf.UploadSize; got != tc.WantSize { + t.Errorf("got size %d want %d", got, tc.WantSize) + } + if got := lf.MD5(); !bytes.Equal(got, tc.WantMD5) { + t.Errorf("got MD5 %x want %x", got, tc.WantMD5) + } + if got := lf.CacheControl(); got != tc.WantCacheControl { + t.Errorf("got CacheControl %q want %q", got, tc.WantCacheControl) + } + if got := lf.ContentEncoding(); got != tc.WantContentEncoding { + t.Errorf("got ContentEncoding %q want %q", got, tc.WantContentEncoding) + } + if tc.WantContentType != "" { + if got := lf.ContentType(); got != tc.WantContentType { + t.Errorf("got ContentType %q want %q", got, tc.WantContentType) + } + } + // Verify the reader last to ensure the previous operations don't + // interfere with it. + r, err := lf.Reader() + if err != nil { + t.Fatal(err) + } + gotContent, err := io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(gotContent, tc.WantContent) { + t.Errorf("got content %q want %q", string(gotContent), string(tc.WantContent)) + } + r.Close() + // Verify we can read again. + r, err = lf.Reader() + if err != nil { + t.Fatal(err) + } + gotContent, err = io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + r.Close() + if !bytes.Equal(gotContent, tc.WantContent) { + t.Errorf("got content %q want %q", string(gotContent), string(tc.WantContent)) + } + }) + } +} + +func TestOrdering(t *testing.T) { + tests := []struct { + Description string + Uploads []string + Ordering []*regexp.Regexp + Want [][]string + }{ + { + Description: "empty", + Want: [][]string{nil}, + }, + { + Description: "no ordering", + Uploads: []string{"c", "b", "a", "d"}, + Want: [][]string{{"a", "b", "c", "d"}}, + }, + { + Description: "one ordering", + Uploads: []string{"db", "c", "b", "a", "da"}, + Ordering: []*regexp.Regexp{regexp.MustCompile("^d")}, + Want: [][]string{{"da", "db"}, {"a", "b", "c"}}, + }, + { + Description: "two orderings", + Uploads: []string{"db", "c", "b", "a", "da"}, + Ordering: []*regexp.Regexp{ + regexp.MustCompile("^d"), + regexp.MustCompile("^b"), + }, + Want: [][]string{{"da", "db"}, {"b"}, {"a", "c"}}, + }, + } + + for _, tc := range tests { + t.Run(tc.Description, func(t *testing.T) { + uploads := make([]*fileToUpload, len(tc.Uploads)) + for i, u := range tc.Uploads { + uploads[i] = &fileToUpload{Local: &localFile{SlashPath: u}} + } + gotUploads := applyOrdering(tc.Ordering, uploads) + var got [][]string + for _, subslice := range gotUploads { + var gotsubslice []string + for _, u := range subslice { + gotsubslice = append(gotsubslice, u.Local.SlashPath) + } + got = append(got, gotsubslice) + } + if diff := cmp.Diff(got, tc.Want); diff != "" { + t.Error(diff) + } + }) + } +} + +type fileData struct { + Name string // name of the file + Contents string // contents of the file +} + +// initLocalFs initializes fs with some test files. +func initLocalFs(ctx context.Context, fs afero.Fs) ([]*fileData, error) { + // The initial local filesystem. + local := []*fileData{ + {"aaa", "aaa"}, + {"bbb", "bbb"}, + {"subdir/aaa", "subdir-aaa"}, + {"subdir/nested/aaa", "subdir-nested-aaa"}, + {"subdir2/bbb", "subdir2-bbb"}, + } + if err := writeFiles(fs, local); err != nil { + return nil, err + } + return local, nil +} + +// fsTest represents an (afero.FS, Go CDK blob.Bucket) against which end-to-end +// tests can be run. +type fsTest struct { + name string + fs afero.Fs + bucket *blob.Bucket +} + +// initFsTests initializes a pair of tests for end-to-end test: +// 1. An in-memory afero.Fs paired with an in-memory Go CDK bucket. +// 2. A filesystem-based afero.Fs paired with an filesystem-based Go CDK bucket. +// It returns the pair of tests and a cleanup function. +func initFsTests(t *testing.T) []*fsTest { + t.Helper() + + tmpfsdir := t.TempDir() + tmpbucketdir := t.TempDir() + + memfs := afero.NewMemMapFs() + membucket := memblob.OpenBucket(nil) + t.Cleanup(func() { membucket.Close() }) + + filefs := hugofs.NewBasePathFs(afero.NewOsFs(), tmpfsdir) + filebucket, err := fileblob.OpenBucket(tmpbucketdir, nil) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { filebucket.Close() }) + + tests := []*fsTest{ + {"mem", memfs, membucket}, + {"file", filefs, filebucket}, + } + return tests +} + +// TestEndToEndSync verifies that basic adds, updates, and deletes are working +// correctly. +func TestEndToEndSync(t *testing.T) { + ctx := context.Background() + tests := initFsTests(t) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + local, err := initLocalFs(ctx, test.fs) + if err != nil { + t.Fatal(err) + } + deployer := &Deployer{ + localFs: test.fs, + bucket: test.bucket, + mediaTypes: media.DefaultTypes, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, + } + + // Initial deployment should sync remote with local. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("initial deploy: failed: %v", err) + } + wantSummary := deploySummary{NumLocal: 5, NumRemote: 0, NumUploads: 5, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("initial deploy: got %v, want %v", deployer.summary, wantSummary) + } + if diff, err := verifyRemote(ctx, deployer.bucket, local); err != nil { + t.Errorf("initial deploy: failed to verify remote: %v", err) + } else if diff != "" { + t.Errorf("initial deploy: remote snapshot doesn't match expected:\n%v", diff) + } + + // A repeat deployment shouldn't change anything. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("no-op deploy: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 0, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("no-op deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // Make some changes to the local filesystem: + // 1. Modify file [0]. + // 2. Delete file [1]. + // 3. Add a new file (sorted last). + updatefd := local[0] + updatefd.Contents = "new contents" + deletefd := local[1] + local = append(local[:1], local[2:]...) // removing deleted [1] + newfd := &fileData{"zzz", "zzz"} + local = append(local, newfd) + if err := writeFiles(test.fs, []*fileData{updatefd, newfd}); err != nil { + t.Fatal(err) + } + if err := test.fs.Remove(deletefd.Name); err != nil { + t.Fatal(err) + } + + // A deployment should apply those 3 changes. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy after changes: failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 2, NumDeletes: 1} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy after changes: got %v, want %v", deployer.summary, wantSummary) + } + if diff, err := verifyRemote(ctx, deployer.bucket, local); err != nil { + t.Errorf("deploy after changes: failed to verify remote: %v", err) + } else if diff != "" { + t.Errorf("deploy after changes: remote snapshot doesn't match expected:\n%v", diff) + } + + // Again, a repeat deployment shouldn't change anything. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("no-op deploy: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 0, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("no-op deploy: got %v, want %v", deployer.summary, wantSummary) + } + }) + } +} + +// TestMaxDeletes verifies that the "maxDeletes" flag is working correctly. +func TestMaxDeletes(t *testing.T) { + ctx := context.Background() + tests := initFsTests(t) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + local, err := initLocalFs(ctx, test.fs) + if err != nil { + t.Fatal(err) + } + deployer := &Deployer{ + localFs: test.fs, + bucket: test.bucket, + mediaTypes: media.DefaultTypes, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, + } + + // Sync remote with local. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("initial deploy: failed: %v", err) + } + wantSummary := deploySummary{NumLocal: 5, NumRemote: 0, NumUploads: 5, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("initial deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // Delete two files, [1] and [2]. + if err := test.fs.Remove(local[1].Name); err != nil { + t.Fatal(err) + } + if err := test.fs.Remove(local[2].Name); err != nil { + t.Fatal(err) + } + + // A deployment with maxDeletes=0 shouldn't change anything. + deployer.cfg.MaxDeletes = 0 + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 3, NumRemote: 5, NumUploads: 0, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // A deployment with maxDeletes=1 shouldn't change anything either. + deployer.cfg.MaxDeletes = 1 + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 3, NumRemote: 5, NumUploads: 0, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // A deployment with maxDeletes=2 should make the changes. + deployer.cfg.MaxDeletes = 2 + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 3, NumRemote: 5, NumUploads: 0, NumDeletes: 2} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // Delete two more files, [0] and [3]. + if err := test.fs.Remove(local[0].Name); err != nil { + t.Fatal(err) + } + if err := test.fs.Remove(local[3].Name); err != nil { + t.Fatal(err) + } + + // A deployment with maxDeletes=-1 should make the changes. + deployer.cfg.MaxDeletes = -1 + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 1, NumRemote: 3, NumUploads: 0, NumDeletes: 2} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy: got %v, want %v", deployer.summary, wantSummary) + } + }) + } +} + +// TestIncludeExclude verifies that the include/exclude options for targets work. +func TestIncludeExclude(t *testing.T) { + ctx := context.Background() + tests := []struct { + Include string + Exclude string + Want deploySummary + }{ + { + Want: deploySummary{NumLocal: 5, NumUploads: 5}, + }, + { + Include: "**aaa", + Want: deploySummary{NumLocal: 3, NumUploads: 3}, + }, + { + Include: "**bbb", + Want: deploySummary{NumLocal: 2, NumUploads: 2}, + }, + { + Include: "aaa", + Want: deploySummary{NumLocal: 1, NumUploads: 1}, + }, + { + Exclude: "**aaa", + Want: deploySummary{NumLocal: 2, NumUploads: 2}, + }, + { + Exclude: "**bbb", + Want: deploySummary{NumLocal: 3, NumUploads: 3}, + }, + { + Exclude: "aaa", + Want: deploySummary{NumLocal: 4, NumUploads: 4}, + }, + { + Include: "**aaa", + Exclude: "**nested**", + Want: deploySummary{NumLocal: 2, NumUploads: 2}, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("include %q exclude %q", test.Include, test.Exclude), func(t *testing.T) { + fsTests := initFsTests(t) + fsTest := fsTests[1] // just do file-based test + + _, err := initLocalFs(ctx, fsTest.fs) + if err != nil { + t.Fatal(err) + } + tgt := &deployconfig.Target{ + Include: test.Include, + Exclude: test.Exclude, + } + if err := tgt.ParseIncludeExclude(); err != nil { + t.Error(err) + } + deployer := &Deployer{ + localFs: fsTest.fs, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket, + target: tgt, + mediaTypes: media.DefaultTypes, + } + + // Sync remote with local. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy: failed: %v", err) + } + if !cmp.Equal(deployer.summary, test.Want) { + t.Errorf("deploy: got %v, want %v", deployer.summary, test.Want) + } + }) + } +} + +// TestIncludeExcludeRemoteDelete verifies deleted local files that don't match include/exclude patterns +// are not deleted on the remote. +func TestIncludeExcludeRemoteDelete(t *testing.T) { + ctx := context.Background() + + tests := []struct { + Include string + Exclude string + Want deploySummary + }{ + { + Want: deploySummary{NumLocal: 3, NumRemote: 5, NumUploads: 0, NumDeletes: 2}, + }, + { + Include: "**aaa", + Want: deploySummary{NumLocal: 2, NumRemote: 3, NumUploads: 0, NumDeletes: 1}, + }, + { + Include: "subdir/**", + Want: deploySummary{NumLocal: 1, NumRemote: 2, NumUploads: 0, NumDeletes: 1}, + }, + { + Exclude: "**bbb", + Want: deploySummary{NumLocal: 2, NumRemote: 3, NumUploads: 0, NumDeletes: 1}, + }, + { + Exclude: "bbb", + Want: deploySummary{NumLocal: 3, NumRemote: 4, NumUploads: 0, NumDeletes: 1}, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("include %q exclude %q", test.Include, test.Exclude), func(t *testing.T) { + fsTests := initFsTests(t) + fsTest := fsTests[1] // just do file-based test + + local, err := initLocalFs(ctx, fsTest.fs) + if err != nil { + t.Fatal(err) + } + deployer := &Deployer{ + localFs: fsTest.fs, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket, + mediaTypes: media.DefaultTypes, + } + + // Initial sync to get the files on the remote + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy: failed: %v", err) + } + + // Delete two files, [1] and [2]. + if err := fsTest.fs.Remove(local[1].Name); err != nil { + t.Fatal(err) + } + if err := fsTest.fs.Remove(local[2].Name); err != nil { + t.Fatal(err) + } + + // Second sync + tgt := &deployconfig.Target{ + Include: test.Include, + Exclude: test.Exclude, + } + if err := tgt.ParseIncludeExclude(); err != nil { + t.Error(err) + } + deployer.target = tgt + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy: failed: %v", err) + } + + if !cmp.Equal(deployer.summary, test.Want) { + t.Errorf("deploy: got %v, want %v", deployer.summary, test.Want) + } + }) + } +} + +// TestCompression verifies that gzip compression works correctly. +// In particular, MD5 hashes must be of the compressed content. +func TestCompression(t *testing.T) { + ctx := context.Background() + + tests := initFsTests(t) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + local, err := initLocalFs(ctx, test.fs) + if err != nil { + t.Fatal(err) + } + deployer := &Deployer{ + localFs: test.fs, + bucket: test.bucket, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}}, + mediaTypes: media.DefaultTypes, + } + + // Initial deployment should sync remote with local. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("initial deploy: failed: %v", err) + } + wantSummary := deploySummary{NumLocal: 5, NumRemote: 0, NumUploads: 5, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("initial deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // A repeat deployment shouldn't change anything. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("no-op deploy: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 0, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("no-op deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // Make an update to the local filesystem, on [1]. + updatefd := local[1] + updatefd.Contents = "new contents" + if err := writeFiles(test.fs, []*fileData{updatefd}); err != nil { + t.Fatal(err) + } + + // A deployment should apply the changes. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("deploy after changes: failed: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 1, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("deploy after changes: got %v, want %v", deployer.summary, wantSummary) + } + }) + } +} + +// TestMatching verifies that matchers match correctly, and that the Force +// attribute for matcher works. +func TestMatching(t *testing.T) { + ctx := context.Background() + tests := initFsTests(t) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := initLocalFs(ctx, test.fs) + if err != nil { + t.Fatal(err) + } + deployer := &Deployer{ + localFs: test.fs, + bucket: test.bucket, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}}, + mediaTypes: media.DefaultTypes, + } + + // Initial deployment to sync remote with local. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("initial deploy: failed: %v", err) + } + wantSummary := deploySummary{NumLocal: 5, NumRemote: 0, NumUploads: 5, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("initial deploy: got %v, want %v", deployer.summary, wantSummary) + } + + // A repeat deployment should upload a single file, the one that matched the Force matcher. + // Note that matching happens based on the ToSlash form, so this matches + // even on Windows. + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("no-op deploy with single force matcher: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 1, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("no-op deploy with single force matcher: got %v, want %v", deployer.summary, wantSummary) + } + + // Repeat with a matcher that should now match 3 files. + deployer.cfg.Matchers = []*deployconfig.Matcher{{Pattern: "aaa", Force: true, Re: regexp.MustCompile("aaa")}} + if err := deployer.Deploy(ctx); err != nil { + t.Errorf("no-op deploy with triple force matcher: %v", err) + } + wantSummary = deploySummary{NumLocal: 5, NumRemote: 5, NumUploads: 3, NumDeletes: 0} + if !cmp.Equal(deployer.summary, wantSummary) { + t.Errorf("no-op deploy with triple force matcher: got %v, want %v", deployer.summary, wantSummary) + } + }) + } +} + +// writeFiles writes the files in fds to fd. +func writeFiles(fs afero.Fs, fds []*fileData) error { + for _, fd := range fds { + dir := path.Dir(fd.Name) + if dir != "." { + err := fs.MkdirAll(dir, os.ModePerm) + if err != nil { + return err + } + } + f, err := fs.Create(fd.Name) + if err != nil { + return err + } + defer f.Close() + _, err = f.WriteString(fd.Contents) + if err != nil { + return err + } + } + return nil +} + +// verifyRemote that the current contents of bucket matches local. +// It returns an empty string if the contents matched, and a non-empty string +// capturing the diff if they didn't. +func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (string, error) { + var cur []*fileData + iter := bucket.List(nil) + for { + obj, err := iter.Next(ctx) + if err == io.EOF { + break + } + if err != nil { + return "", err + } + contents, err := bucket.ReadAll(ctx, obj.Key) + if err != nil { + return "", err + } + cur = append(cur, &fileData{obj.Key, string(contents)}) + } + if cmp.Equal(cur, local) { + return "", nil + } + diff := "got: \n" + for _, f := range cur { + diff += fmt.Sprintf(" %s: %s\n", f.Name, f.Contents) + } + diff += "want: \n" + for _, f := range local { + diff += fmt.Sprintf(" %s: %s\n", f.Name, f.Contents) + } + return diff, nil +} + +func newDeployer() *Deployer { + return &Deployer{ + logger: loggers.NewDefault(), + cfg: deployconfig.DeployConfig{Workers: 2}, + } +} diff --git a/deploy/deployconfig/deployConfig.go b/deploy/deployconfig/deployConfig.go new file mode 100644 index 0000000..94dbc0c --- /dev/null +++ b/deploy/deployconfig/deployConfig.go @@ -0,0 +1,179 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deployconfig + +import ( + "errors" + "fmt" + "regexp" + + "github.com/gobwas/glob" + "github.com/gohugoio/hugo/config" + hglob "github.com/gohugoio/hugo/hugofs/hglob" + "github.com/mitchellh/mapstructure" +) + +const DeploymentConfigKey = "deployment" + +// DeployConfig is the complete configuration for deployment. +type DeployConfig struct { + Targets []*Target + Matchers []*Matcher + Order []string + + // Usually set via flags. + // Target deployment Name; defaults to the first one. + Target string + // Show a confirm prompt before deploying. + Confirm bool + // DryRun will try the deployment without any remote changes. + DryRun bool + // Force will re-upload all files. + Force bool + // Invalidate the CDN cache listed in the deployment target. + InvalidateCDN bool + // MaxDeletes is the maximum number of files to delete. + MaxDeletes int + // Number of concurrent workers to use when uploading files. + Workers int + + Ordering []*regexp.Regexp `json:"-"` // compiled Order +} + +type Target struct { + Name string + URL string + + CloudFrontDistributionID string + + // GoogleCloudCDNOrigin specifies the Google Cloud project and CDN origin to + // invalidate when deploying this target. It is specified as /. + GoogleCloudCDNOrigin string + + // Optional patterns of files to include/exclude for this target. + // Parsed using github.com/gobwas/glob. + Include string + Exclude string + + // Parsed versions of Include/Exclude. + IncludeGlob glob.Glob `json:"-"` + ExcludeGlob glob.Glob `json:"-"` + + // If true, any local path matching /index.html will be mapped to the + // remote path /. This does not affect the top-level index.html file, + // since that would result in an empty path. + StripIndexHTML bool +} + +func (tgt *Target) ParseIncludeExclude() error { + var err error + if tgt.Include != "" { + tgt.IncludeGlob, err = hglob.GetGlob(tgt.Include) + if err != nil { + return fmt.Errorf("invalid deployment.target.include %q: %v", tgt.Include, err) + } + } + if tgt.Exclude != "" { + tgt.ExcludeGlob, err = hglob.GetGlob(tgt.Exclude) + if err != nil { + return fmt.Errorf("invalid deployment.target.exclude %q: %v", tgt.Exclude, err) + } + } + return nil +} + +// Matcher represents configuration to be applied to files whose paths match +// a specified pattern. +type Matcher struct { + // Pattern is the string pattern to match against paths. + // Matching is done against paths converted to use / as the path separator. + Pattern string + + // CacheControl specifies caching attributes to use when serving the blob. + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + CacheControl string + + // ContentEncoding specifies the encoding used for the blob's content, if any. + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding + ContentEncoding string + + // ContentType specifies the MIME type of the blob being written. + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type + ContentType string + + // Gzip determines whether the file should be gzipped before upload. + // If so, the ContentEncoding field will automatically be set to "gzip". + Gzip bool + + // Force indicates that matching files should be re-uploaded. Useful when + // other route-determined metadata (e.g., ContentType) has changed. + Force bool + + // Re is Pattern compiled. + Re *regexp.Regexp `json:"-"` +} + +func (m *Matcher) Matches(path string) bool { + return m.Re.MatchString(path) +} + +var DefaultConfig = DeployConfig{ + Workers: 10, + InvalidateCDN: true, + MaxDeletes: 256, +} + +// DecodeConfig creates a config from a given Hugo configuration. +func DecodeConfig(cfg config.Provider) (DeployConfig, error) { + dcfg := DefaultConfig + + if !cfg.IsSet(DeploymentConfigKey) { + return dcfg, nil + } + if err := mapstructure.WeakDecode(cfg.GetStringMap(DeploymentConfigKey), &dcfg); err != nil { + return dcfg, err + } + + if dcfg.Workers <= 0 { + dcfg.Workers = 10 + } + + for _, tgt := range dcfg.Targets { + if *tgt == (Target{}) { + return dcfg, errors.New("empty deployment target") + } + if err := tgt.ParseIncludeExclude(); err != nil { + return dcfg, err + } + } + var err error + for _, m := range dcfg.Matchers { + if *m == (Matcher{}) { + return dcfg, errors.New("empty deployment matcher") + } + m.Re, err = regexp.Compile(m.Pattern) + if err != nil { + return dcfg, fmt.Errorf("invalid deployment.matchers.pattern: %v", err) + } + } + for _, o := range dcfg.Order { + re, err := regexp.Compile(o) + if err != nil { + return dcfg, fmt.Errorf("invalid deployment.orderings.pattern: %v", err) + } + dcfg.Ordering = append(dcfg.Ordering, re) + } + + return dcfg, nil +} diff --git a/deploy/deployconfig/deployConfig_test.go b/deploy/deployconfig/deployConfig_test.go new file mode 100644 index 0000000..38d0aad --- /dev/null +++ b/deploy/deployconfig/deployConfig_test.go @@ -0,0 +1,198 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package deployconfig + +import ( + "fmt" + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" +) + +func TestDecodeConfigFromTOML(t *testing.T) { + c := qt.New(t) + + tomlConfig := ` + +someOtherValue = "foo" + +[deployment] + +order = ["o1", "o2"] + +# All lowercase. +[[deployment.targets]] +name = "name0" +url = "url0" +cloudfrontdistributionid = "cdn0" +include = "*.html" + +# All uppercase. +[[deployment.targets]] +NAME = "name1" +URL = "url1" +CLOUDFRONTDISTRIBUTIONID = "cdn1" +INCLUDE = "*.jpg" + +# Camelcase. +[[deployment.targets]] +name = "name2" +url = "url2" +cloudFrontDistributionID = "cdn2" +exclude = "*.png" + +# All lowercase. +[[deployment.matchers]] +pattern = "^pattern0$" +cachecontrol = "cachecontrol0" +contentencoding = "contentencoding0" +contenttype = "contenttype0" + +# All uppercase. +[[deployment.matchers]] +PATTERN = "^pattern1$" +CACHECONTROL = "cachecontrol1" +CONTENTENCODING = "contentencoding1" +CONTENTTYPE = "contenttype1" +GZIP = true +FORCE = true + +# Camelcase. +[[deployment.matchers]] +pattern = "^pattern2$" +cacheControl = "cachecontrol2" +contentEncoding = "contentencoding2" +contentType = "contenttype2" +gzip = true +force = true +` + cfg, err := config.FromConfigString(tomlConfig, "toml") + c.Assert(err, qt.IsNil) + + dcfg, err := DecodeConfig(cfg) + c.Assert(err, qt.IsNil) + + // Order. + c.Assert(len(dcfg.Order), qt.Equals, 2) + c.Assert(dcfg.Order[0], qt.Equals, "o1") + c.Assert(dcfg.Order[1], qt.Equals, "o2") + c.Assert(len(dcfg.Ordering), qt.Equals, 2) + + // Targets. + c.Assert(len(dcfg.Targets), qt.Equals, 3) + wantInclude := []string{"*.html", "*.jpg", ""} + wantExclude := []string{"", "", "*.png"} + for i := 0; i < 3; i++ { + tgt := dcfg.Targets[i] + c.Assert(tgt.Name, qt.Equals, fmt.Sprintf("name%d", i)) + c.Assert(tgt.URL, qt.Equals, fmt.Sprintf("url%d", i)) + c.Assert(tgt.CloudFrontDistributionID, qt.Equals, fmt.Sprintf("cdn%d", i)) + c.Assert(tgt.Include, qt.Equals, wantInclude[i]) + if wantInclude[i] != "" { + c.Assert(tgt.IncludeGlob, qt.Not(qt.IsNil)) + } + c.Assert(tgt.Exclude, qt.Equals, wantExclude[i]) + if wantExclude[i] != "" { + c.Assert(tgt.ExcludeGlob, qt.Not(qt.IsNil)) + } + } + + // Matchers. + c.Assert(len(dcfg.Matchers), qt.Equals, 3) + for i := 0; i < 3; i++ { + m := dcfg.Matchers[i] + c.Assert(m.Pattern, qt.Equals, fmt.Sprintf("^pattern%d$", i)) + c.Assert(m.Re, qt.Not(qt.IsNil)) + c.Assert(m.CacheControl, qt.Equals, fmt.Sprintf("cachecontrol%d", i)) + c.Assert(m.ContentEncoding, qt.Equals, fmt.Sprintf("contentencoding%d", i)) + c.Assert(m.ContentType, qt.Equals, fmt.Sprintf("contenttype%d", i)) + c.Assert(m.Gzip, qt.Equals, i != 0) + c.Assert(m.Force, qt.Equals, i != 0) + } +} + +func TestInvalidOrderingPattern(t *testing.T) { + c := qt.New(t) + + tomlConfig := ` + +someOtherValue = "foo" + +[deployment] +order = ["["] # invalid regular expression +` + cfg, err := config.FromConfigString(tomlConfig, "toml") + c.Assert(err, qt.IsNil) + + _, err = DecodeConfig(cfg) + c.Assert(err, qt.Not(qt.IsNil)) +} + +func TestInvalidMatcherPattern(t *testing.T) { + c := qt.New(t) + + tomlConfig := ` + +someOtherValue = "foo" + +[deployment] +[[deployment.matchers]] +Pattern = "[" # invalid regular expression +` + cfg, err := config.FromConfigString(tomlConfig, "toml") + c.Assert(err, qt.IsNil) + + _, err = DecodeConfig(cfg) + c.Assert(err, qt.Not(qt.IsNil)) +} + +func TestDecodeConfigDefault(t *testing.T) { + c := qt.New(t) + + dcfg, err := DecodeConfig(config.New()) + c.Assert(err, qt.IsNil) + c.Assert(len(dcfg.Targets), qt.Equals, 0) + c.Assert(len(dcfg.Matchers), qt.Equals, 0) +} + +func TestEmptyTarget(t *testing.T) { + c := qt.New(t) + + tomlConfig := ` +[deployment] +[[deployment.targets]] +` + cfg, err := config.FromConfigString(tomlConfig, "toml") + c.Assert(err, qt.IsNil) + + _, err = DecodeConfig(cfg) + c.Assert(err, qt.Not(qt.IsNil)) +} + +func TestEmptyMatcher(t *testing.T) { + c := qt.New(t) + + tomlConfig := ` +[deployment] +[[deployment.matchers]] +` + cfg, err := config.FromConfigString(tomlConfig, "toml") + c.Assert(err, qt.IsNil) + + _, err = DecodeConfig(cfg) + c.Assert(err, qt.Not(qt.IsNil)) +} diff --git a/deploy/google.go b/deploy/google.go new file mode 100644 index 0000000..5b302e9 --- /dev/null +++ b/deploy/google.go @@ -0,0 +1,39 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package deploy + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/api/compute/v1" +) + +// Invalidate all of the content in a Google Cloud CDN distribution. +func InvalidateGoogleCloudCDN(ctx context.Context, origin string) error { + parts := strings.Split(origin, "/") + if len(parts) != 2 { + return fmt.Errorf("origin must be /") + } + service, err := compute.NewService(ctx) + if err != nil { + return err + } + rule := &compute.CacheInvalidationRule{Path: "/*"} + _, err = service.UrlMaps.InvalidateCache(parts[0], parts[1], rule).Context(ctx).Do() + return err +} diff --git a/deps/deps.go b/deps/deps.go new file mode 100644 index 0000000..8e7780c --- /dev/null +++ b/deps/deps.go @@ -0,0 +1,542 @@ +// Copyright 2025 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deps + +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "sync/atomic" + + "github.com/bep/logg" + "github.com/gohugoio/hugo/cache/dynacache" + "github.com/gohugoio/hugo/cache/filecache" + "github.com/gohugoio/hugo/common/hexec" + "github.com/gohugoio/hugo/common/hmaps" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/allconfig" + "github.com/gohugoio/hugo/config/security" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/internal/js" + "github.com/gohugoio/hugo/internal/warpc" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/resources/postpub" + "github.com/gohugoio/hugo/tpl/tplimpl" + + "github.com/gohugoio/hugo/metrics" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/source" + "github.com/gohugoio/hugo/tpl" + "github.com/spf13/afero" +) + +// Deps holds dependencies used by many. +// There will be normally only one instance of deps in play +// at a given time, i.e. one per Site built. +type Deps struct { + // The logger to use. + Log loggers.Logger `json:"-"` + + ExecHelper *hexec.Exec + + // The file systems to use. + Fs *hugofs.Fs `json:"-"` + + // The PathSpec to use + *helpers.PathSpec `json:"-"` + + // The ContentSpec to use + *helpers.ContentSpec `json:"-"` + + // The SourceSpec to use + SourceSpec *source.SourceSpec `json:"-"` + + // The Resource Spec to use + ResourceSpec *resources.Spec + + // The configuration to use + Conf config.AllProvider `json:"-"` + + // The memory cache to use. + MemCache *dynacache.Cache + + // The translation func to use + Translate func(ctx context.Context, translationID string, templateData any) string `json:"-"` + + // The site building. + Site page.Site + + TemplateStore *tplimpl.TemplateStore + + // Used in tests + OverloadedTemplateFuncs map[string]any + + TranslationProvider ResourceProvider + + Metrics metrics.Provider + + // BuildStartListeners will be notified before a build starts. + BuildStartListeners *Listeners[any] + + // BuildEndListeners will be notified after a build finishes. + BuildEndListeners *Listeners[any] + + // OnChangeListeners will be notified when something changes. + OnChangeListeners *Listeners[identity.Identity] + + // Resources that gets closed when the build is done or the server shuts down. + BuildClosers *types.Closers + + // This is common/global for all sites. + BuildState *BuildState + + // Misc counters. + Counters *Counters + + // Holds RPC dispatchers for Katex etc. + // TODO(bep) rethink this re. a plugin setup, but this will have to do for now. + WasmDispatchers *warpc.Dispatchers + + // The JS batcher client. + JSBatcherClient js.BatcherClient + + isClosed bool + + *globalErrHandler +} + +func (d Deps) Clone(s page.Site, conf config.AllProvider) (*Deps, error) { + d.Conf = conf + d.Site = s + d.ExecHelper = nil + d.ContentSpec = nil + + if err := d.Init(); err != nil { + return nil, err + } + + return &d, nil +} + +func (d *Deps) GetTemplateStore() *tplimpl.TemplateStore { + return d.TemplateStore +} + +func (d *Deps) Init() error { + if d.Conf == nil { + panic("conf is nil") + } + + if d.Fs == nil { + // For tests. + d.Fs = hugofs.NewFrom(afero.NewMemMapFs(), d.Conf.BaseConfig()) + } + + if d.Log == nil { + d.Log = loggers.NewDefault() + } + + if d.globalErrHandler == nil { + d.globalErrHandler = &globalErrHandler{ + logger: d.Log, + } + } + if d.BuildState == nil { + d.BuildState = &BuildState{} + } + if d.Counters == nil { + d.Counters = &Counters{} + } + if d.BuildState.DeferredExecutions == nil { + if d.BuildState.DeferredExecutionsGroupedByRenderingContext == nil { + d.BuildState.DeferredExecutionsGroupedByRenderingContext = make(map[tpl.RenderingContext]*DeferredExecutions) + } + d.BuildState.DeferredExecutions = &DeferredExecutions{ + Executions: hmaps.NewCache[string, *tpl.DeferredExecution](), + FilenamesWithPostPrefix: hmaps.NewCache[string, bool](), + } + } + + if d.BuildStartListeners == nil { + d.BuildStartListeners = &Listeners[any]{} + } + + if d.BuildEndListeners == nil { + d.BuildEndListeners = &Listeners[any]{} + } + + if d.BuildClosers == nil { + d.BuildClosers = &types.Closers{} + } + + if d.OnChangeListeners == nil { + d.OnChangeListeners = &Listeners[identity.Identity]{} + } + + if d.Metrics == nil && d.Conf.TemplateMetrics() { + d.Metrics = metrics.NewProvider(d.Conf.TemplateMetricsHints()) + } + + if d.ExecHelper == nil { + d.ExecHelper = hexec.New(d.Conf.GetConfigSection("security").(security.Config), d.Conf.WorkingDir(), d.Log) + } + + if d.MemCache == nil { + d.MemCache = dynacache.New(dynacache.Options{Watching: d.Conf.Watching(), Log: d.Log}) + } + + if d.PathSpec == nil { + hashBytesReceiverFunc := func(name string, match []byte) { + s := string(match) + switch s { + case postpub.PostProcessPrefix: + d.BuildState.AddFilenameWithPostPrefix(name) + case tpl.HugoDeferredTemplatePrefix: + d.BuildState.DeferredExecutions.FilenamesWithPostPrefix.Set(name, true) + } + } + + // Skip binary files. + mediaTypes := d.Conf.GetConfigSection("mediaTypes").(media.Types) + hashBytesShouldCheck := func(name string) bool { + ext := strings.TrimPrefix(filepath.Ext(name), ".") + return mediaTypes.IsTextSuffix(ext) + } + d.Fs.PublishDir = hugofs.NewHasBytesReceiver( + d.Fs.PublishDir, + hashBytesShouldCheck, + hashBytesReceiverFunc, + []byte(tpl.HugoDeferredTemplatePrefix), + []byte(postpub.PostProcessPrefix)) + + pathSpec, err := helpers.NewPathSpec(d.Fs, d.Conf, d.Log, nil) + if err != nil { + return err + } + d.PathSpec = pathSpec + } else { + var err error + d.PathSpec, err = helpers.NewPathSpec(d.Fs, d.Conf, d.Log, d.PathSpec.BaseFs) + if err != nil { + return err + } + } + + d.ExecHelper.SetNodeReadPaths(d.BaseFs.Assets.RealPaths("")) + + if d.ContentSpec == nil { + contentSpec, err := helpers.NewContentSpec(d.Conf, d.Log, d.Content.Fs, d.ExecHelper) + if err != nil { + return err + } + d.ContentSpec = contentSpec + } + + if d.SourceSpec == nil { + d.SourceSpec = source.NewSourceSpec(d.PathSpec, nil, d.Fs.Source) + } + + var common *resources.SpecCommon + if d.ResourceSpec != nil { + common = d.ResourceSpec.SpecCommon + } + + d.Cfg.BaseConfig() + + fileCaches := d.Cfg.FileCaches().(filecache.Caches) + fileCaches.SetResourceFs(d.BaseFs.ResourcesCache) + + resourceSpec, err := resources.NewSpec(d.PathSpec, common, d.WasmDispatchers, fileCaches, d.MemCache, d.BuildState, d.Log, d, d.ExecHelper, d.BuildClosers, d.BuildState) + if err != nil { + return fmt.Errorf("failed to create resource spec: %w", err) + } + d.ResourceSpec = resourceSpec + + return nil +} + +// TODO(bep) rework this to get it in line with how we manage templates. +func (d *Deps) Compile(prototype *Deps) error { + var err error + if prototype == nil { + + if err = d.TranslationProvider.NewResource(d); err != nil { + return err + } + return nil + } + + if err = d.TranslationProvider.CloneResource(d, prototype); err != nil { + return err + } + + return nil +} + +// MkdirTemp returns a temporary directory path that will be cleaned up on exit. +func (d Deps) MkdirTemp(pattern string) (string, error) { + filename, err := os.MkdirTemp("", pattern) + if err != nil { + return "", err + } + d.BuildClosers.Add( + types.CloserFunc( + func() error { + return os.RemoveAll(filename) + }, + ), + ) + + return filename, nil +} + +type globalErrHandler struct { + logger loggers.Logger + + // Channel for some "hard to get to" build errors + buildErrors chan error + // Used to signal that the build is done. + quit chan struct{} +} + +// SendError sends the error on a channel to be handled later. +// This can be used in situations where returning and aborting the current +// operation isn't practical. +func (e *globalErrHandler) SendError(err error) { + if e.buildErrors != nil { + select { + case <-e.quit: + case e.buildErrors <- err: + default: + } + return + } + e.logger.Errorln(err) +} + +func (e *globalErrHandler) StartErrorCollector() chan error { + e.quit = make(chan struct{}) + e.buildErrors = make(chan error, 10) + return e.buildErrors +} + +func (e *globalErrHandler) StopErrorCollector() { + if e.buildErrors != nil { + close(e.quit) + close(e.buildErrors) + } +} + +// Listeners represents an event listener. +type Listeners[T any] struct { + sync.Mutex + + // A list of funcs to be notified about an event. + // If the return value is true, the listener will be removed. + listeners []func(...T) bool +} + +// Add adds a function to a Listeners instance. +func (b *Listeners[T]) Add(f func(...T) bool) { + if b == nil { + return + } + b.Lock() + defer b.Unlock() + b.listeners = append(b.listeners, f) +} + +// Notify executes all listener functions. +func (b *Listeners[T]) Notify(vs ...T) { + b.Lock() + defer b.Unlock() + temp := b.listeners[:0] + for _, notify := range b.listeners { + if !notify(vs...) { + temp = append(temp, notify) + } + } + b.listeners = temp +} + +// ResourceProvider is used to create and refresh, and clone resources needed. +type ResourceProvider interface { + NewResource(dst *Deps) error + CloneResource(dst, src *Deps) error +} + +func (d *Deps) Close() error { + if d.isClosed { + return nil + } + d.isClosed = true + + if d.MemCache != nil { + d.MemCache.Stop() + } + if d.WasmDispatchers != nil { + d.WasmDispatchers.Close() + } + return d.BuildClosers.Close() +} + +// DepsCfg contains configuration options that can be used to configure Hugo +// on a global level, i.e. logging etc. +// Nil values will be given default values. +type DepsCfg struct { + // The logger to use. Only set in some tests. + // TODO(bep) get rid of this. + TestLogger loggers.Logger + + // The logging level to use. + LogLevel logg.Level + + // Logging output. + StdErr io.Writer + + // The console output. + StdOut io.Writer + + // The file systems to use + Fs *hugofs.Fs + + // The Site in use + Site page.Site + + Configs *allconfig.Configs + + // Template handling. + TemplateProvider ResourceProvider + + // i18n handling. + TranslationProvider ResourceProvider + + // Build triggered by the IntegrationTest framework. + IsIntegrationTest bool + + // TestCfg holds configuration used only in tests. + // It is a programming error to set this when IsIntegrationTest is not set, + // and doing so will panic. + TestCfg TestConfig + + // ChangesFromBuild for changes passed back to the server/watch process. + ChangesFromBuild chan []identity.Identity +} + +// TestConfig holds configuration used only in tests. +// See DepsCfg.TestCfg. +type TestConfig struct { + // WarpcMemory, if set, overrides the memory limit in MiB for the WASM based + // image processors (WebP and AVIF). Used to provoke memory allocation failures. + WarpcMemory int +} + +// IsZero reports whether c holds no test configuration. +func (c TestConfig) IsZero() bool { + return c == TestConfig{} +} + +// BuildState are state used during a build. +type BuildState struct { + counter uint64 + + // Tracks invocations of the Build method. + BuildCounter atomic.Uint64 + + mu sync.Mutex // protects state below. + + OnSignalRebuild func(ids ...identity.Identity) + + // A set of filenames in /public that + // contains a post-processing prefix. + filenamesWithPostPrefix map[string]bool + + DeferredExecutions *DeferredExecutions + + // Deferred executions grouped by rendering context. + DeferredExecutionsGroupedByRenderingContext map[tpl.RenderingContext]*DeferredExecutions +} + +// Misc counters. +type Counters struct { + // Counter for the math.Counter function. + MathCounter atomic.Uint64 +} + +type DeferredExecutions struct { + // A set of filenames in /public that + // contains a post-processing prefix. + FilenamesWithPostPrefix *hmaps.Cache[string, bool] + + // Maps a placeholder to a deferred execution. + Executions *hmaps.Cache[string, *tpl.DeferredExecution] +} + +var _ identity.SignalRebuilder = (*BuildState)(nil) + +// IsRebuild reports whether this is a rebuild. +func (b *BuildState) IsRebuild() bool { + return b.BuildCounter.Load() > 0 +} + +// StartStageRender will be called before a stage is rendered. +func (b *BuildState) StartStageRender(stage tpl.RenderingContext) { +} + +// StopStageRender will be called after a stage is rendered. +func (b *BuildState) StopStageRender(stage tpl.RenderingContext) { + b.DeferredExecutionsGroupedByRenderingContext[stage] = b.DeferredExecutions + b.DeferredExecutions = &DeferredExecutions{ + Executions: hmaps.NewCache[string, *tpl.DeferredExecution](), + FilenamesWithPostPrefix: hmaps.NewCache[string, bool](), + } +} + +func (b *BuildState) SignalRebuild(ids ...identity.Identity) { + b.OnSignalRebuild(ids...) +} + +func (b *BuildState) AddFilenameWithPostPrefix(filename string) { + b.mu.Lock() + defer b.mu.Unlock() + if b.filenamesWithPostPrefix == nil { + b.filenamesWithPostPrefix = make(map[string]bool) + } + b.filenamesWithPostPrefix[filename] = true +} + +func (b *BuildState) GetFilenamesWithPostPrefix() []string { + b.mu.Lock() + defer b.mu.Unlock() + var filenames []string + for filename := range b.filenamesWithPostPrefix { + filenames = append(filenames, filename) + } + sort.Strings(filenames) + return filenames +} + +func (b *BuildState) Incr() int { + return int(atomic.AddUint64(&b.counter, uint64(1))) +} diff --git a/deps/deps_test.go b/deps/deps_test.go new file mode 100644 index 0000000..e92ed23 --- /dev/null +++ b/deps/deps_test.go @@ -0,0 +1,31 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deps_test + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/deps" +) + +func TestBuildFlags(t *testing.T) { + c := qt.New(t) + var bf deps.BuildState + bf.Incr() + bf.Incr() + bf.Incr() + + c.Assert(bf.Incr(), qt.Equals, 4) +} diff --git a/docs/.codespellrc b/docs/.codespellrc new file mode 100644 index 0000000..eaa1c65 --- /dev/null +++ b/docs/.codespellrc @@ -0,0 +1,13 @@ +# Config file for codespell. +# https://github.com/codespell-project/codespell#using-a-config-file + +[codespell] + +# Comma separated list of dirs to be skipped. +skip = *.ai,chroma.css,chroma_dark.css,.cspell.json,./data/docs.yaml + +# Comma separated list of words to be ignored. Words must be lowercased. +ignore-words-list = abl,edn,ist,januar,te,trys,ue,womens + +# Check file names as well. +check-filenames = true diff --git a/docs/.cspell.json b/docs/.cspell.json new file mode 100644 index 0000000..36636b3 --- /dev/null +++ b/docs/.cspell.json @@ -0,0 +1,204 @@ +{ + "version": "0.2", + "allowCompoundWords": true, + "overrides": [ + { + "filename": "**/*", + "enabled": false + }, + { + "filename": "**/*.md", + "enabled": true + } + ], + "flagWords": [ + "alot", + "hte", + "langauge", + "reccommend", + "seperate", + "teh" + ], + "ignorePaths": [ + "**/emojis.md", + "**/commands/*", + "**/showcase/*", + "**/tools/*" + ], + "ignoreRegExpList": [ + // cspell: ignore fenced code blocks + "^(\\s*`{3,}).*[\\s\\S]*?^\\1$", + // cspell: ignore words joined with dot + "\\w+\\.\\w+", + // cspell: ignore strings within backticks + "`.+`", + // cspell: ignore strings within double quotes + "\".+\"", + // cspell: ignore strings within brackets + "\\[.+\\]", + // cspell: ignore strings within parentheses + "\\(.+\\)", + // cspell: ignore words that begin with a slash + "/\\w+", + // cspell: ignore everything within action delimiters + "\\{\\{.+\\}\\}", + // cspell: ignore everything after a right arrow + "\\s+→\\s+.+", + ], + "language": "en", + "words": [ + "composability", + "configurators", + "defang", + "deindent", + "downscale", + "downscaling", + "exif", + "geolocalized", + "grayscale", + "marshal", + "marshaling", + "multihost", + "multiplatform", + "performantly", + "preconfigured", + "prerendering", + "redirection", + "redirections", + "slugified", + "slugify", + "subexpression", + "suppressible", + "synchronisation", + "templating", + "transpile", + "unmarshal", + "unmarshaled", + "unmarshaling", + "unmarshals", + // ------------------------------------------------------------------------ + // cspell: ignore hugo terminology + // ------------------------------------------------------------------------ + "alignx", + "aligny", + "attrlink", + "canonify", + "codeowners", + "dynacache", + "eturl", + "getenv", + "gohugo", + "gohugoio", + "keyvals", + "leftdelim", + "linkify", + "numworkermultiplier", + "rightdelim", + "shortcode", + "stringifier", + "struct", + "toclevels", + "unmarshal", + "unpublishdate", + "zgotmplz", + // ------------------------------------------------------------------------ + // cspell: ignore foreign language words + // ------------------------------------------------------------------------ + "Bokmål", + "Norsk", + "bezpieczeństwo", + "blatt", + "buch", + "descripción", + "dokumentation", + "erklärungen", + "español", + "français", + "libros", + "mercredi", + "miesiąc", + "miesiąca", + "miesiące", + "miesięcy", + "misérables", + "mittwoch", + "muchos", + "novembre", + "otro", + "pocos", + "produkte", + "projekt", + "prywatność", + "referenz", + "régime", + // ------------------------------------------------------------------------ + // cspell: ignore names + // ------------------------------------------------------------------------ + "Atishay", + "Cosette", + "Eliott", + "Furet", + "Gregor", + "Jaco", + "Lanczos", + "Ninke", + "Noll", + "Pastorius", + "Pontmercy", + "Samsa", + "Stucki", + "Thénardier", + "Vitter", + "WASI", + // ------------------------------------------------------------------------ + // cspell: ignore operating systems and software packages + // ------------------------------------------------------------------------ + "ananke", + "asciidoctor", + "brotli", + "cifs", + "corejs", + "disqus", + "docutils", + "dpkg", + "doas", + "eopkg", + "forgejo", + "gitee", + "goldmark", + "katex", + "kubuntu", + "lubuntu", + "mathjax", + "nosql", + "pandoc", + "pkgin", + "rclone", + "xubuntu", + // ------------------------------------------------------------------------ + // cspell: ignore miscellaneous + // ------------------------------------------------------------------------ + "achristie", + "ccpa", + "cpra", + "ddmaurier", + "dring", + "fleqn", + "inor", + "iptc", + "jausten", + "jdoe", + "jsmith", + "leqno", + "milli", + "monokai", + "mysanityprojectid", + "rgba", + "rsmith", + "tdewolff", + "tjones", + "vcard", + "wcag", + "xfeff" + ] +} diff --git a/docs/.editorconfig b/docs/.editorconfig new file mode 100644 index 0000000..dd2a009 --- /dev/null +++ b/docs/.editorconfig @@ -0,0 +1,20 @@ +# https://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + +[*.go] +indent_size = 8 +indent_style = tab + +[*.js] +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/docs/.github/ISSUE_TEMPLATE/config.yml b/docs/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/docs/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/docs/.github/ISSUE_TEMPLATE/default.md b/docs/.github/ISSUE_TEMPLATE/default.md new file mode 100644 index 0000000..ada35b3 --- /dev/null +++ b/docs/.github/ISSUE_TEMPLATE/default.md @@ -0,0 +1,6 @@ +--- +name: Default +about: This is the default issue template. +labels: + - NeedsTriage +--- diff --git a/docs/.github/SUPPORT.md b/docs/.github/SUPPORT.md new file mode 100644 index 0000000..96a4400 --- /dev/null +++ b/docs/.github/SUPPORT.md @@ -0,0 +1,3 @@ +### Asking support questions + +We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions. Please don't use the GitHub issue tracker to ask questions. diff --git a/docs/.github/workflows/codeql-analysis.yml b/docs/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..1354ffe --- /dev/null +++ b/docs/.github/workflows/codeql-analysis.yml @@ -0,0 +1,26 @@ +name: "CodeQL" + +on: + schedule: + - cron: "0 0 1 * *" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: "javascript" + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/docs/.github/workflows/lint.yml b/docs/.github/workflows/lint.yml new file mode 100644 index 0000000..00800ab --- /dev/null +++ b/docs/.github/workflows/lint.yml @@ -0,0 +1,61 @@ +name: Lint +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +jobs: + markdownlint: + name: Lint Markdown + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Run Markdown linter + uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23.2.0 + with: + globs: # set to null to override default of *.{md,markdown} + + spellcheck: + name: Check spelling + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Check spelling with cspell + uses: streetsidesoftware/cspell-action@de2a73e963e7443969755b648a1008f77033c5b2 # v8.4.0 + with: + incremental_files_only: true + strict: true + # cspell uses the .cspell.json configuration file + - name: Check spelling with codespell + uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2 + with: + check_filenames: true + check_hidden: true + # codespell uses the .codespellrc file + + template-formatting: + name: Check template formatting + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Install Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: "1.26" + check-latest: true + cache: true + cache-dependency-path: | + **/go.sum + **/go.mod + - name: Install gotmplfmt + run: go install github.com/gohugoio/gotmplfmt@623175f49b3d07a11da381ff85228d0d03101880 # v0.4.1 + - name: Check template formatting + run: "diff <(gotmplfmt -d layouts) <(printf '')" diff --git a/docs/.github/workflows/stale.yml b/docs/.github/workflows/stale.yml new file mode 100644 index 0000000..9513953 --- /dev/null +++ b/docs/.github/workflows/stale.yml @@ -0,0 +1,32 @@ +name: Close stale issues and pull requests +on: + workflow_dispatch: + schedule: + - cron: "30 1 * * *" +permissions: + contents: read +jobs: + stale: + permissions: + contents: read + issues: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 + with: + days-before-stale: 90 # default is 60 + days-before-close: 14 # default is 7 + exempt-all-assignees: true + exempt-draft-pr: true + exempt-issue-labels: Keep, InProgress, NeedsTriage + exempt-pr-labels: Keep + operations-per-run: 100 + stale-issue-message: > + This issue has been marked as stale because there hasn't been any + recent activity. It will be closed soon if there are no further + updates. + stale-pr-message: > + This pull request has been marked as stale because there hasn't + been any recent activity. It will be closed soon if there are no + further updates. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..a178881 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +.hugo_build.lock +.hvm +/.idea +/.vscode +/dist +/public +/resources +hugo_stats.json +node_modules/ +nohup.out +package-lock.json +trace.out diff --git a/docs/.markdownlint-cli2.yaml b/docs/.markdownlint-cli2.yaml new file mode 100644 index 0000000..68c28a3 --- /dev/null +++ b/docs/.markdownlint-cli2.yaml @@ -0,0 +1,94 @@ +# Glob patterns to include +globs: + - "content/**/*.md" + +# Glob patterns to exclude +ignores: + - "content/**/commands/**" + - "content/en/about/license.md" + - "content/LICENSE.md" + +# Markdownlint rules and configuration +# https://github.com/DavidAnson/markdownlint?tab=readme-ov-file#rules--aliases +config: + MD001: true + # MD002 deprecated + MD003: + style: atx + MD004: + style: dash + MD005: true + # MD006 deprecated + MD007: false # if enabled, throws errors when definition descriptions contain list items + # MD008 deprecated + MD009: true + MD010: true + MD011: true + MD012: true + MD013: false + MD014: true + # MD015 deprecated + # MD016 deprecated + # MD017 deprecated + MD018: true + MD019: true + MD020: true + MD021: true + MD022: true + MD023: true + MD024: true + MD025: true + MD026: true + MD027: true + MD028: false + MD029: + style: one + MD030: true + MD031: true + MD032: true + MD033: true + MD034: false + MD035: + style: --- + MD036: true + MD037: true + MD038: true + MD039: true + MD040: true + MD041: false + MD042: true + MD043: false + MD044: false + MD045: true + MD046: false + MD047: true + MD048: + style: backtick + MD049: + style: underscore + MD050: + style: asterisk + MD051: false + MD052: true + MD053: true + MD054: + autolink: true + collapsed: true + full: true + inline: true + shortcut: true + url_inline: false + MD055: + style: consistent + MD056: false # interferes with Markdown attributes + # MD057 deprecated + MD058: true + MD059: + prohibited_texts: + - click here + - here + - link + - more + MD060: + aligned_delimiter: false + style: any diff --git a/docs/.prettierignore b/docs/.prettierignore new file mode 100644 index 0000000..bd712be --- /dev/null +++ b/docs/.prettierignore @@ -0,0 +1,3 @@ +# Auto generated. +assets/css/components/chroma*.css +assets/jsconfig.json diff --git a/docs/.prettierrc b/docs/.prettierrc new file mode 100644 index 0000000..11c748a --- /dev/null +++ b/docs/.prettierrc @@ -0,0 +1,12 @@ +{ + "overrides": [ + { + "files": ["*.js", "*.ts"], + "options": { + "useTabs": true, + "printWidth": 120, + "singleQuote": true + } + } + ] +} diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 0000000..7524fd4 --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,25 @@ +## Theme + +### Role +You are an expert front-end and Tailwind CSS developer. Your primary goal is to build reliable, readable, and scalable user interfaces using the existing project structure and design system. + +### Directives +* **ALWAYS** use existing Tailwind utility classes for styling. +* **NEVER** write raw CSS in `.css` files unless absolutely necessary for a non-utility-based global reset. +* **NEVER** use inline styles (e.g., `
    `). + +### Project Knowledge + +* **Tech Stack:** Hugo (static site generator), AlpineJS, Tailwind CSS. Icons from https://heroicons.com/. +* **Core Files and Directories:** + - `hugo.toml`: Hugo configuration file. + - `assets/css/styles.css`: Main CSS file with Tailwind directives. + - `assets/js/main.js`: Main JavaScript with AlpineJS configuration. + - `package.json`: Project dependencies and scripts. + - `content/`: Directory for markdown content files. + - `layouts/`: Directory for Hugo templates and partials. + - `layouts/_partials/icons.html`: SVG icon sprites. + + + + diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md new file mode 100644 index 0000000..eef4bd2 --- /dev/null +++ b/docs/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md \ No newline at end of file diff --git a/docs/LICENSE.md b/docs/LICENSE.md new file mode 100644 index 0000000..d4facbf --- /dev/null +++ b/docs/LICENSE.md @@ -0,0 +1,3 @@ +See [content/LICENSE.md](content/LICENSE.md) for the license of the content of this repository. + +The theme (layouts, CSS, JavaScript etc.) of this repository has no open source license. It is custom made for the Hugo sites and is not meant for reuse. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..06b952d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,25 @@ +Hugo + +A fast and flexible static site generator built with love by [bep], [spf13], and [friends] in [Go]. + +--- + +[![Netlify Status](https://api.netlify.com/api/v1/badges/e0dbbfc7-34f1-4393-a679-c16e80162705/deploy-status)](https://app.netlify.com/sites/gohugoio/deploys) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://gohugo.io/contribute/documentation/) + +This is the repository for the [Hugo](https://github.com/gohugoio/hugo) documentation site. + +Please see the [contributing] section for guidelines, examples, and process. + +[bep]: https://github.com/bep +[spf13]: https://github.com/spf13 +[friends]: https://github.com/gohugoio/hugo/graphs/contributors +[go]: https://go.dev/ +[contributing]: https://gohugo.io/contribute/documentation + +# Install + +```sh +npm i +hugo server +``` diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md new file mode 100644 index 0000000..58a60ed --- /dev/null +++ b/docs/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " | strings.FirstUpper }} +description: +categories: [] +keywords: [] +--- diff --git a/docs/archetypes/functions.md b/docs/archetypes/functions.md new file mode 100644 index 0000000..de2d720 --- /dev/null +++ b/docs/archetypes/functions.md @@ -0,0 +1,11 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " | title }} +description: +categories: [] +keywords: [] +params: + functions_and_methods: + aliases: [] + returnType: + signatures: [] +--- diff --git a/docs/archetypes/glossary.md b/docs/archetypes/glossary.md new file mode 100644 index 0000000..ea3c3b8 --- /dev/null +++ b/docs/archetypes/glossary.md @@ -0,0 +1,13 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " }} +params: + reference: +--- + + diff --git a/docs/archetypes/methods.md b/docs/archetypes/methods.md new file mode 100644 index 0000000..944fe52 --- /dev/null +++ b/docs/archetypes/methods.md @@ -0,0 +1,10 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " | title }} +description: +categories: [] +keywords: [] +params: + functions_and_methods: + returnType: + signatures: [] +--- diff --git a/docs/archetypes/news.md b/docs/archetypes/news.md new file mode 100644 index 0000000..04792a1 --- /dev/null +++ b/docs/archetypes/news.md @@ -0,0 +1,7 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " | strings.FirstUpper }} +description: +categories: [] +keywords: [] +publishDate: {{ .Date }} +--- diff --git a/docs/assets/css/components/all.css b/docs/assets/css/components/all.css new file mode 100644 index 0000000..62c2b2a --- /dev/null +++ b/docs/assets/css/components/all.css @@ -0,0 +1,8 @@ +/* The order of these does not matter. */ +@import "./content.css"; +@import "./fonts.css"; +@import "./helpers.css"; +@import "./shortcodes.css"; +@import "./tableofcontents.css"; +@import "./view-transitions.css"; +@import "./todo-lists.css"; diff --git a/docs/assets/css/components/chroma.css b/docs/assets/css/components/chroma.css new file mode 100644 index 0000000..9d4c91f --- /dev/null +++ b/docs/assets/css/components/chroma.css @@ -0,0 +1,85 @@ +/* Background */ .bg { background-color: var(--color-light); } +/* PreWrapper */ .chroma { background-color: var(--color-light); } +/* Other */ .chroma .x { } +/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } +/* CodeLine */ .chroma .cl { } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .chroma .hl { background-color: #ffffcc } +/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { font-weight: bold } +/* KeywordConstant */ .chroma .kc { font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { font-weight: bold } +/* KeywordNamespace */ .chroma .kn { font-weight: bold } +/* KeywordPseudo */ .chroma .kp { font-weight: bold } +/* KeywordReserved */ .chroma .kr { font-weight: bold } +/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold } +/* Name */ .chroma .n { } +/* NameAttribute */ .chroma .na { color: #008080 } +/* NameBuiltin */ .chroma .nb { color: #999999 } +/* NameBuiltinPseudo */ .chroma .bp { } +/* NameClass */ .chroma .nc { color: #445588; font-weight: bold } +/* NameConstant */ .chroma .no { color: #008080 } +/* NameDecorator */ .chroma .nd { } +/* NameEntity */ .chroma .ni { color: #800080 } +/* NameException */ .chroma .ne { color: #990000; font-weight: bold } +/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold } +/* NameFunctionMagic */ .chroma .fm { } +/* NameLabel */ .chroma .nl { } +/* NameNamespace */ .chroma .nn { color: #555555 } +/* NameOther */ .chroma .nx { } +/* NameProperty */ .chroma .py { } +/* NameTag */ .chroma .nt { color: #000080 } +/* NameVariable */ .chroma .nv { color: #008080 } +/* NameVariableClass */ .chroma .vc { } +/* NameVariableGlobal */ .chroma .vg { } +/* NameVariableInstance */ .chroma .vi { } +/* NameVariableMagic */ .chroma .vm { } +/* Literal */ .chroma .l { } +/* LiteralDate */ .chroma .ld { } +/* LiteralString */ .chroma .s { color: #bb8844 } +/* LiteralStringAffix */ .chroma .sa { color: #bb8844 } +/* LiteralStringBacktick */ .chroma .sb { color: #bb8844 } +/* LiteralStringChar */ .chroma .sc { color: #bb8844 } +/* LiteralStringDelimiter */ .chroma .dl { color: #bb8844 } +/* LiteralStringDoc */ .chroma .sd { color: #bb8844 } +/* LiteralStringDouble */ .chroma .s2 { color: #bb8844 } +/* LiteralStringEscape */ .chroma .se { color: #bb8844 } +/* LiteralStringHeredoc */ .chroma .sh { color: #bb8844 } +/* LiteralStringInterpol */ .chroma .si { color: #bb8844 } +/* LiteralStringOther */ .chroma .sx { color: #bb8844 } +/* LiteralStringRegex */ .chroma .sr { color: #808000 } +/* LiteralStringSingle */ .chroma .s1 { color: #bb8844 } +/* LiteralStringSymbol */ .chroma .ss { color: #bb8844 } +/* LiteralNumber */ .chroma .m { color: #009999 } +/* LiteralNumberBin */ .chroma .mb { color: #009999 } +/* LiteralNumberFloat */ .chroma .mf { color: #009999 } +/* LiteralNumberHex */ .chroma .mh { color: #009999 } +/* LiteralNumberInteger */ .chroma .mi { color: #009999 } +/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 } +/* LiteralNumberOct */ .chroma .mo { color: #009999 } +/* Operator */ .chroma .o { font-weight: bold } +/* OperatorWord */ .chroma .ow { font-weight: bold } +/* Punctuation */ .chroma .p { } +/* Comment */ .chroma .c { color: #999988; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold } +/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold } +/* Generic */ .chroma .g { } +/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd } +/* GenericEmph */ .chroma .ge { font-style: italic } +/* GenericError */ .chroma .gr { color: #aa0000 } +/* GenericHeading */ .chroma .gh { color: #999999 } +/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd } +/* GenericOutput */ .chroma .go { color: #888888 } +/* GenericPrompt */ .chroma .gp { color: #555555 } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #aaaaaa } +/* GenericTraceback */ .chroma .gt { color: #aa0000 } +/* GenericUnderline */ .chroma .gl { text-decoration: underline } +/* TextWhitespace */ .chroma .w { color: #bbbbbb } diff --git a/docs/assets/css/components/chroma_dark.css b/docs/assets/css/components/chroma_dark.css new file mode 100644 index 0000000..0b0ae30 --- /dev/null +++ b/docs/assets/css/components/chroma_dark.css @@ -0,0 +1,85 @@ +/* Background */.dark .bg { background-color: var(--color-dark); } +/* PreWrapper */ .dark .chroma { background-color: var(--color-dark); } +/* Other */ .dark .chroma .x { } +/* Error */ .dark .chroma .err { color: #ef6155 } +/* CodeLine */ .dark .chroma .cl { } +/* LineTableTD */ .dark .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .dark .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .dark .chroma .hl { background-color: rgb(0,19,28) } +/* LineNumbersTable */ .dark .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .dark .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .dark .chroma .line { display: flex; } +/* Keyword */ .dark .chroma .k { color: #815ba4 } +/* KeywordConstant */ .dark .chroma .kc { color: #815ba4 } +/* KeywordDeclaration */ .dark .chroma .kd { color: #815ba4 } +/* KeywordNamespace */ .dark .chroma .kn { color: #5bc4bf } +/* KeywordPseudo */ .dark .chroma .kp { color: #815ba4 } +/* KeywordReserved */ .dark .chroma .kr { color: #815ba4 } +/* KeywordType */ .dark .chroma .kt { color: #fec418 } +/* Name */ .dark .chroma .n { } +/* NameAttribute */ .dark .chroma .na { color: #06b6ef } +/* NameBuiltin */ .dark .chroma .nb { } +/* NameBuiltinPseudo */ .dark .chroma .bp { } +/* NameClass */ .dark .chroma .nc { color: #fec418 } +/* NameConstant */ .dark .chroma .no { color: #ef6155 } +/* NameDecorator */ .dark .chroma .nd { color: #5bc4bf } +/* NameEntity */ .dark .chroma .ni { } +/* NameException */ .dark .chroma .ne { color: #ef6155 } +/* NameFunction */ .dark .chroma .nf { color: #06b6ef } +/* NameFunctionMagic */ .dark .chroma .fm { } +/* NameLabel */ .dark .chroma .nl { } +/* NameNamespace */ .dark .chroma .nn { color: #fec418 } +/* NameOther */ .dark .chroma .nx { color: #06b6ef } +/* NameProperty */ .dark .chroma .py { } +/* NameTag */ .dark .chroma .nt { color: #5bc4bf } +/* NameVariable */ .dark .chroma .nv { color: #ef6155 } +/* NameVariableClass */ .dark .chroma .vc { } +/* NameVariableGlobal */ .dark .chroma .vg { } +/* NameVariableInstance */ .dark .chroma .vi { } +/* NameVariableMagic */ .dark .chroma .vm { } +/* Literal */ .dark .chroma .l { color: #f99b15 } +/* LiteralDate */ .dark .chroma .ld { color: #48b685 } +/* LiteralString */ .dark .chroma .s { color: #48b685 } +/* LiteralStringAffix */ .dark .chroma .sa { color: #48b685 } +/* LiteralStringBacktick */ .dark .chroma .sb { color: #48b685 } +/* LiteralStringChar */ .dark .chroma .sc { } +/* LiteralStringDelimiter */ .dark .chroma .dl { color: #48b685 } +/* LiteralStringDoc */ .dark .chroma .sd { color: #776e71 } +/* LiteralStringDouble */ .dark .chroma .s2 { color: #48b685 } +/* LiteralStringEscape */ .dark .chroma .se { color: #f99b15 } +/* LiteralStringHeredoc */ .dark .chroma .sh { color: #48b685 } +/* LiteralStringInterpol */ .dark .chroma .si { color: #f99b15 } +/* LiteralStringOther */ .dark .chroma .sx { color: #48b685 } +/* LiteralStringRegex */ .dark .chroma .sr { color: #48b685 } +/* LiteralStringSingle */ .dark .chroma .s1 { color: #48b685 } +/* LiteralStringSymbol */ .dark .chroma .ss { color: #48b685 } +/* LiteralNumber */ .dark .chroma .m { color: #f99b15 } +/* LiteralNumberBin */ .dark .chroma .mb { color: #f99b15 } +/* LiteralNumberFloat */ .dark .chroma .mf { color: #f99b15 } +/* LiteralNumberHex */ .dark .chroma .mh { color: #f99b15 } +/* LiteralNumberInteger */ .dark .chroma .mi { color: #f99b15 } +/* LiteralNumberIntegerLong */ .dark .chroma .il { color: #f99b15 } +/* LiteralNumberOct */ .dark .chroma .mo { color: #f99b15 } +/* Operator */ .dark .chroma .o { color: #5bc4bf } +/* OperatorWord */ .dark .chroma .ow { color: #5bc4bf } +/* Punctuation */ .dark .chroma .p { } +/* Comment */ .dark .chroma .c { color: #776e71 } +/* CommentHashbang */ .dark .chroma .ch { color: #776e71 } +/* CommentMultiline */ .dark .chroma .cm { color: #776e71 } +/* CommentSingle */ .dark .chroma .c1 { color: #776e71 } +/* CommentSpecial */ .dark .chroma .cs { color: #776e71 } +/* CommentPreproc */ .dark .chroma .cp { color: #776e71 } +/* CommentPreprocFile */ .dark .chroma .cpf { color: #776e71 } +/* Generic */ .dark .chroma .g { } +/* GenericDeleted */ .dark .chroma .gd { color: #ef6155 } +/* GenericEmph */ .dark .chroma .ge { font-style: italic } +/* GenericError */ .dark .chroma .gr { } +/* GenericHeading */ .dark .chroma .gh { font-weight: bold } +/* GenericInserted */ .dark .chroma .gi { color: #48b685 } +/* GenericOutput */ .dark .chroma .go { } +/* GenericPrompt */ .dark .chroma .gp { color: #776e71; font-weight: bold } +/* GenericStrong */ .dark .chroma .gs { font-weight: bold } +/* GenericSubheading */ .dark .chroma .gu { color: #5bc4bf; font-weight: bold } +/* GenericTraceback */ .dark .chroma .gt { } +/* GenericUnderline */ .dark .chroma .gl { } +/* TextWhitespace */ .dark .chroma .w { } diff --git a/docs/assets/css/components/content.css b/docs/assets/css/components/content.css new file mode 100644 index 0000000..7bd5c62 --- /dev/null +++ b/docs/assets/css/components/content.css @@ -0,0 +1,49 @@ +@import "./chroma_dark.css"; +@import "./chroma.css"; +@import "./highlight.css"; + +/* Some contrast ratio fixes as reported by Google Page Speed. */ +.chroma .c1 { + @apply text-gray-500; +} + +.dark .chroma .c1 { + @apply text-gray-400; +} + +.highlight code { + @apply text-sm/6; +} + +.content { + @apply prose prose-sm sm:prose-base prose-stone max-w-none dark:prose-invert dark:text-slate-200; + /* headings */ + @apply prose-headings:font-semibold; + /* lead */ + @apply prose-lead:text-slate-500 prose-lead:text-xl prose-lead:mt-2 sm:prose-lead:mt-4 prose-lead:leading-relaxed dark:prose-lead:text-slate-400; + /* links */ + @apply prose-a:text-primary dark:prose-a:text-blue-500 prose-a:hover:text-blue-500 dark:prose-a:hover:text-blue-400 prose-a:underline; + @apply prose-a:prose-code:underline prose-a:prose-code:hover:text-blue-500 prose-a:prose-code:hover:underline; + /* pre */ + @apply prose-pre:text-gray-800 prose-pre:border-1 prose-pre:border-gray-100 prose-pre:bg-light dark:prose-pre:bg-dark dark:prose-pre:ring-1 dark:prose-pre:ring-slate-300/10; + /* code */ + @apply prose-code:px-0.5 prose-code:text-gray-600 prose-code:dark:text-gray-300 border-none; + @apply prose-code:before:hidden prose-code:after:hidden prose-code:font-mono; + @apply prose-table:prose-th:prose-code:text-white; + /* tables */ + @apply prose-table:w-auto prose-table:border-2 prose-table:border-gray-100 prose-table:dark:border-gray-800 prose-table:prose-th:font-bold prose-table:prose-th:bg-blue-500 dark:prose-table:prose-th:bg-blue-500/50 prose-table:prose-th:p-2 prose-table:prose-td:p-2 prose-table:prose-th:text-white; + /* hr */ + @apply dark:prose-hr:border-slate-800; + /* ol */ + @apply prose-ol:marker:prose dark:prose-ol:marker:text-gray-300; + /* ul */ + @apply prose-ul:marker:text-gray-500 dark:prose-ul:marker:text-gray-300; +} + +/* This will not match highlighting inside e.g. the code-toggle shortcode. */ +/* For more fine grained control of this, see components/shortcodes.css. */ +.content > .highlight, +.content dd > .highlight, +.content li > .highlight { + @apply border-1 border-gray-200 dark:border-slate-600 mt-6 mb-8; +} diff --git a/docs/assets/css/components/fonts.css b/docs/assets/css/components/fonts.css new file mode 100644 index 0000000..06f40b4 --- /dev/null +++ b/docs/assets/css/components/fonts.css @@ -0,0 +1,15 @@ +@font-face { + font-family: "Mulish"; + font-style: normal; + src: url("../fonts/Mulish-VariableFont_wght.ttf") format("truetype"); + font-weight: 1 999; + font-display: swap; +} + +@font-face { + font-family: "Mulish"; + font-style: italic; + src: url("../fonts/Mulish-Italic-VariableFont_wght.ttf") format("truetype"); + font-weight: 1 999; + font-display: swap; +} diff --git a/docs/assets/css/components/helpers.css b/docs/assets/css/components/helpers.css new file mode 100644 index 0000000..8eb6930 --- /dev/null +++ b/docs/assets/css/components/helpers.css @@ -0,0 +1,19 @@ +/* Helper class to limit a text block to two lines. */ +.two-lines-ellipsis { + display: block; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Helper class to limit a text block to three lines. */ +.three-lines-ellipsis { + display: block; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/docs/assets/css/components/highlight.css b/docs/assets/css/components/highlight.css new file mode 100644 index 0000000..5f25fe3 --- /dev/null +++ b/docs/assets/css/components/highlight.css @@ -0,0 +1,11 @@ +.highlight { + @apply bg-light dark:bg-dark rounded-none; +} + +.highlight pre { + @apply m-0 p-3 w-full h-full overflow-x-auto dark:border-black rounded-none; +} + +.highlight pre code { + @apply m-0 p-0 w-full h-full; +} diff --git a/docs/assets/css/components/shortcodes.css b/docs/assets/css/components/shortcodes.css new file mode 100644 index 0000000..7314d5b --- /dev/null +++ b/docs/assets/css/components/shortcodes.css @@ -0,0 +1,4 @@ +.shortcode-code { + .highlight { + } +} diff --git a/docs/assets/css/components/tableofcontents.css b/docs/assets/css/components/tableofcontents.css new file mode 100644 index 0000000..3640adf --- /dev/null +++ b/docs/assets/css/components/tableofcontents.css @@ -0,0 +1,14 @@ +.tableofcontents { + ul { + @apply list-none; + li { + @apply mb-2; + a { + @apply text-primary; + &:hover { + @apply text-primary/60; + } + } + } + } +} diff --git a/docs/assets/css/components/todo-lists.css b/docs/assets/css/components/todo-lists.css new file mode 100644 index 0000000..886beb4 --- /dev/null +++ b/docs/assets/css/components/todo-lists.css @@ -0,0 +1,15 @@ +ul.todo { + padding-left: 0; +} + +ul.todo li { + display: flex; + gap: 0.8em; +} + +ul.todo li input[type="checkbox"] { + width: 1em; + height: 1em; + flex-shrink: 0; + margin-top: 0.3em; +} diff --git a/docs/assets/css/components/view-transitions.css b/docs/assets/css/components/view-transitions.css new file mode 100644 index 0000000..a63e7c8 --- /dev/null +++ b/docs/assets/css/components/view-transitions.css @@ -0,0 +1,24 @@ +/* Opt in to native cross-document view transitions on navigation. */ +@view-transition { + navigation: auto; +} + +/* Global slight fade */ +::view-transition-old(root), +::view-transition-new(root) { + animation-duration: 200ms; +} + +/* + * Persistent chrome (header/footer) is named so it gets its own snapshot + * instead of being part of the root crossfade. Holding it static prevents + * the flicker that the sticky header otherwise shows on every navigation. + */ +::view-transition-group(site-header), +::view-transition-group(site-footer), +::view-transition-old(site-header), +::view-transition-new(site-header), +::view-transition-old(site-footer), +::view-transition-new(site-footer) { + animation: none; +} diff --git a/docs/assets/css/styles.css b/docs/assets/css/styles.css new file mode 100644 index 0000000..79ce316 --- /dev/null +++ b/docs/assets/css/styles.css @@ -0,0 +1,143 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; +@variant dark (&:where(.dark, .dark *)); + +@import "components/all.css"; + +/* TailwindCSS ignores files in .gitignore, so make it explicit. */ +@source "hugo_stats.json"; + +@theme { + /* Breakpoints. */ + --breakpoint-sm: 40rem; + --breakpoint-md: 48rem; + --breakpoint-lg: 68rem; /* Default 64rem; */ + --breakpoint-xl: 80rem; + --breakpoint-2xl: 96rem; + + /* Colors. */ + --color-primary: var(--color-blue-600); + --color-dark: #000; + --color-light: var(--color-gray-50); + --color-accent: var(--color-orange-500); + --color-accent-light: var(--color-pink-500); + --color-accent-dark: var(--color-green-500); + + /* https://www.tints.dev/blue/0594CB */ + --color-blue-50: #e1f6fe; + --color-blue-100: #c3edfe; + --color-blue-200: #88dbfc; + --color-blue-300: #4cc9fb; + --color-blue-400: #15b9f9; + --color-blue-500: #0594cb; + --color-blue-600: #0477a4; + --color-blue-700: #035677; + --color-blue-800: #023a50; + --color-blue-900: #011d28; + --color-blue-950: #000e14; + + /* https://www.tints.dev/orange/EBB951 */ + --color-orange-50: #fdf8ed; + --color-orange-100: #fbf1da; + --color-orange-200: #f7e4ba; + --color-orange-300: #f3d596; + --color-orange-400: #efc976; + --color-orange-500: #ebb951; + --color-orange-600: #e5a51a; + --color-orange-700: #a97a13; + --color-orange-800: #72520d; + --color-orange-900: #372806; + --color-orange-950: #1b1403; + + /* https://www.tints.dev/pink/FF4088 */ + --color-pink-50: #ffebf2; + --color-pink-100: #ffdbe9; + --color-pink-200: #ffb3d0; + --color-pink-300: #ff8fba; + --color-pink-400: #ff66a1; + --color-pink-500: #ff4088; + --color-pink-600: #ff0062; + --color-pink-700: #c2004a; + --color-pink-800: #800031; + --color-pink-900: #420019; + --color-pink-950: #1f000c; + + /* https://www.tints.dev/green/33BA91 */ + --color-green-50: #ebfaf5; + --color-green-100: #d3f3e9; + --color-green-200: #abe8d6; + --color-green-300: #7fdcc0; + --color-green-400: #53d0aa; + --color-green-500: #33ba91; + --color-green-600: #299474; + --color-green-700: #1f7058; + --color-green-800: #154c3b; + --color-green-900: #0a241c; + --color-green-950: #051410; + + /* Fonts. */ + --font-sans: + "Mulish", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; +} + +html { + scroll-padding-top: 100px; +} + +body { + @apply antialiased font-sans text-black dark:text-gray-100; +} + +.p-safe-area-x { + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); +} + +.p-safe-area-y { + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); +} + +.px-main { + padding-left: max(env(safe-area-inset-left), 1rem); + padding-right: max(env(safe-area-inset-right), 1rem); +} + +@media (min-width: theme('--breakpoint-md')) { + .px-main { + padding-left: max(env(safe-area-inset-left), 2rem); + padding-right: max(env(safe-area-inset-right), 2rem); + } +} + +@media (min-width: theme('--breakpoint-lg')) { + .px-main { + padding-left: max(env(safe-area-inset-left), 3rem); + padding-right: max(env(safe-area-inset-right), 3rem); + } +} + +/* Algolia DocSearch */ +.algolia-docsearch-suggestion--highlight { + color: var(--color-primary); +} + +/* Footnotes */ +.footnote-backref, +.footnote-ref { + text-decoration: none; + padding-left: .0625em; +} + +/* Code spans within paragraphs, tables cells, list items, etc. */ +:not(pre) > code { + white-space: nowrap; +} + +/* Utility class for tables to prevent word wrapping in the first column. */ +.no-wrap-first-col td:first-child, +.no-wrap-first-col th:first-child { + white-space: nowrap; +} diff --git a/docs/assets/images/examples/landscape-exif-orientation-5.jpg b/docs/assets/images/examples/landscape-exif-orientation-5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad64835eb2209c960dfb21c4437fa72754cd98d2 GIT binary patch literal 38639 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�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 HT0j5UC*8$J literal 0 HcmV?d00001 diff --git a/docs/assets/images/examples/mask.png b/docs/assets/images/examples/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..c3005a66949218c44670352d0b53bf53878786a3 GIT binary patch literal 137283 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�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
    N1Co01*wcW8lXIgWT)|4}7v6X{UnVAil3 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%0DITkC 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�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%0DI z(8raj93jRKj*o5-n5wXloAqyYhRENZ2^hiaF1Ju@F7#?PC$0$nrmtuJmZ%5l9?pEf zJm<9%Cvu!3u1AjozIA0ox#ZHP_4f>Y#dWl2fn3~)X(e2DWAz*Ecfue#Vhl+z`Vbp+ zWTVyVqWC+gZu8yjAOiJDSD`%-3#3H+i%~%T0X($`tIh^hKL6DFGvqT4M(HGt*`b41 zltxY19)1mw-abYCj46CpTuN`jHXhNhmI+dJ(+96dm)$Ef4+tV$W$xY^_qBL#b$fZU ze%ne1=r4bIgInT_7xdQBuJf7(d;e|I^(*1;imnF@j!U6WgoN?>$001)hb~NE6b4FP z(Um`bE8cnAY_C#gXC|bniP>dlI5Os>i$$($dlHVFum7lU#LeF zntl|wf91wvj+U0ne~x7Py2WWg0V|8-auX)U@7H0cu&~tkf)Yx!u8kn|Ca5G z|F2+4Oq~BlwL#r4jG3U?)h`@_KmHDFYTVndmzbcKai4XP)a(TtBPo~o-FuLR(wZ#k zwfB;~i)wt}mu0sl<(PE^L?c_GlEy~$8gsA8Zb4(+BwsC(<_7jz=r#oQH!H#9CYrtY z?MVF(hm}9pa7bYbYM?f)(0y*wja}s3Y%wYODl5b=Z(EUkY{0uXS3+w1TC>L7#KN(z z*KG#~+)$cxYFY@$d9~e&HRw4e#r^>${Y&0QB(W7>h8KZ+oqp`B(YB`RfZs|&C454fH= znFNPw3ZZiO{jq$97g8F+lsRo>PXsSlarOTWzx(3BKU*AA0OYk(hYulqUz7_`{kR$Z zZvoSU**BAQ*PQsTdHXRF{-|D6zqAf0dqM?6K4M?d4tc|-WK(i*ddOYSsj81BN%@o(w_4z^v*cQ`R8L%Z%)GZd40WTCA&*XpQJzN&j_6UF=v;{ z>-v3Fgv_=#9w$%~l1D~6&V2kiYj~BpOkiRIfFL$KM^IGNtj^ZQ(PB0`TF|tWQHRUW7 z7J4R9ubiI5(UGFz_7O(Pex7T%`gY^c8V30{OwuXI+USq=#P#tjUnwPf^p=>A<5Gq# zIP{!WquzG5?BPLkBTus&Dz@IUZZbhc?cdt|S~@zF2k41eQ zdsyJ_O@W%7sDEYf59S#?Wk=Z51I?y00bmfZ%$I(5nb)>wmlH1IF|hY}4>-b8lT|sN z^=?QL3kUd!VuyKV%62x)ZnJNHEdG48I=z%D+DQaP^BkdDacHtmSrJO~CSI>@<`t22 zf7VW8jQspN-qL|ZwABmOY(`zT$z-*-zMuI$>*!+ED|#d813EJCT8!7$z=qKMMkIB> z7I`zg<}4`jG{k`;=?Bvcq7T@Tb2?sL<)#Qcrw&{V7m)2-N7@(s z^Txym@9{AVz&h-i7W3?oe7A>3)FT}1khA!zpcXD^qu~mq`|0f$4@x6yI*==FDcH}KG$U~bMqydMu=3>D5BOkq$k}$(Vz41lvU+SD(HR@ z=oL0Q?~>2+vrqH~nv8KflkuOIBcni5qvCLGKdk~G5Vzl4{U84>$~nw4SKt2S^3dzw z`%?%bbxq0z&5e0(#G?U-5M$Z^_&s&GQLbDOm?-6&LLfv7BGJ{7MCv9~=|a)10= z2c%8>vC{rD3*I?8Z(`^ly1dt@wZ+VU(@{VmlxDAi)PGx^ARghsI5)XJPq$Wz4ylC@ zFtfS9q{Mc){><8>F~SINy+i{@NvcZ&%>A8I81` z|H7h$6&@g#tOozuN4eA^b<69wFG=*qrmXwmCk9_yQT&zN)h!A6JE*|XXiM&q4?}iq z!g0;>yzZUkHOI73`05>B;I*~Yo4*~JwtuZ}^N!tcJYze(xO(oMure{yjWT5OJ**YB zn4(C=E=-fW#mNyDX~A0Gh>vI`iPV$)!p6l6ve#(*WrwvaRsh8w zJoOg8XwLJyWtKRP_6*!4$h*5LB3b$P?hqs4wP#P_X+za-bLI49pk30%V?}9je-wnx z!N)hX&A!;L)zBJqgb1%WFierA;lQfXL{3|;+nr{Nhc+hv;N*^T=NQwoEO;^;OR?JR1h^oN9W|rSM``NvkDXO~h z8wQN~qUY@3L`;FD?x*oS{0PUKD7jT@l9ieGfU|d@VA*+G1ECR4h=)g>#I6kX21o&F zXs_Yzxo~N5X~5tU8Nycz8dQSo$c)exifR_^g%iWKfyyH+*OvC_6u7?8w;K0LC+o;3 z_8SaYp4Wv-wLJA;8SF9&JfDsi-MlRDFZ+!x(Sc%ZpxAFFgg9IqCjTzq=IUzRN-dvj zK@X8u?o*#<_EJ;xPXv=92Lr9kr1GjerzIT;ocziUQ)#Vl!WBuh(xIYeea}79P!W;L z-%!0iq9pOkVf`k;)U>J(2KB`a4TWW!gOYC?@6JY6?4ayLFDy>UU5^rz znTg^DEJ^nBILHYaxsue~a42T|sIpF)+8mDu&K0i0AvX7{IN8kIKB6)C-2v^7s`LLo z{%sK^j#wub@S0&^C9QTIFC5eiI0Kg&|14~8Z-CJ!a7oxc)}2fV^mJpUH+I5|KvfQ> zIea)57?GTj^Djwl$lh5~F;>Pm8Mr;l8Mi*osT~ zb|`$>zdK;-Iqzv66W#VJmz__xs1>o~kDX#rnk?MHihfw!(T6g8Jhj&7Y2h+BZdZQt zPGjdh*0qzm%G?rU54eii-n808tla6Ajbn*b4X;w$q(Y@n-mxTe(u8*}V_DJO4E%g5S&U`OURPtZwtve`&Klrn#)Jo_b~)BXEoZqa+%7P;y(5q3@bElVXx zD}e0ViNke}@TJmpR&o{R^zGBK`qI06P>(3mVZUyGy^Mv+Fq?mm7D57mL}NuYp(PKV zU;Tv=MHAW4Zd>2yd1E2!v$cz4*_lrl)Zp?)#Ybp8DN8 zwbwU7;n6EnLAp)49{dO$5wbZ)j+;RYFc`dMjIzpKb4DVH9HFHHgI|DON{ld@uiU4I z!AMWM$=>>z#xs&yy8RE@8Z(KpL44LBqz{?Uok3)4Lu7`i!+YQMU-i`1KO3~ptVPYr zsh;hWh{bBQnEx!y!qIJ;Mpoj<+ITzmXsJGmE}#Y)4T z(TJt;g?YteI_hhwi6pa>J>UGWU0yHO+9+Ul5)JJ2R~4HW9bFC9iv5;GL_(bQJ#OeR zcb{8$=AkaPZ`Rz7(qr$$M*K^2K4g7fG&kVkpd>G#;yyGdV7s8KZnT6d(A)oRR`fwJ zCf8Ub39?xCzoRSgutNaz_=nV(^Hk27|CpXY{X7&@+tFghWu7Fp*^?A z5;;Du^lY{nJ$2!R3Z2| zvxt~G-s_-D={mOjnr%5_1jKPw+%T^Zh|3^`YrZZt2zeiI*u*x+>z~@%A;pZoZaT3i zMTu_MkQ*AFg}eXv8145YNRihI3YW}tK5Z0u<|P!fD)=@}GX%q(cWKQz=_aPLwgFG2 zhV-^#C#NbNR^S*3_YcqgCMV59suH~luR@D)AVxzeS5y$~x7SDIxv1++7o+>$_B9{Q zMoFJ57$qc%c*gH$-jg;a_GM-N)zR))!Hjt~qt+?wHZ5>oOXsObU6mtfO{{Fr0-*&? z3-7xN=g-G^IfWl+2VKn4uUzxcI`|$ttH+;y{rh4XrFlhCqV!cgATikkC;r**8b`J2 z4tljYEL)#|sh_O-aR0suQ(FLs{Y5)tqG_@cT5SZ!Nr6$qSqd(F<^}-5Pig5Wr>5_Ec+GxzZ}F&?78& z`@^7py!tlvz%Okrclu8{Iqgw3it$YC=&^g9z=S*x6a;O@uNN2n?dB;mm8Uk?qPODB z<6(MSlAWD`N6VFbcRS3KZh`kW$2zr&e2heES%!X2fw~Il#`#KQKf3tq)6UbsG1@v= z{Sn?}-~9^f$RM#&&xT}R{h7h`?;4V=uO>IcgUymE&4gOkn8YBO!O15~ev5=ynes4e zz1btgdkRZ**)Z{UREkxB#`1i8*yX7dO50-K`BC_QCd|ttXRMv1u<2FVk8W}c#|`A_ z#tT&_aap>>YC)?YIdsDn))Sm`#I<#H@e&mQU^3o>ob*=Ldu73|ceQt|6U^rNOl18j z>Xn(K)OhGHiFP=1n?CzDm(m`7y6Ar?o-;F0s8)M$gwEvzpIzpD=Y)Mz zW4;8}WSO-a@lFkq$LFa#?w83j5rUh@nEFFpF^RCgDmTIm6+@lZ&EXV4CGB4j2AM z%b42%3+c+)$4IK!&j~9?(nIEMm~wZu6n3^u0o<^)&tq+ZFx;!d|t;m<(vBnC>=J zCt!Z7tJ-dO)wucGvBjQY6K^IuPtyiv3q})m4BFXKVvCsegvGL!OYW@j#tyGsVi~+d znnk32C3l_ayR;KL;0diQ%EM{g9hx{Nk8gbnoRb}n{LW0+{j@%t(EaQ#?EQVK7;7*v zk$x|bM9{qio|$tP-#2b}MH~bGg)+DG zFtZN5KR3}zMWdy}P-aArtY&%q#2^QW zXvgqv0&U!W5x?Vn0(#r}hJ&lyFC_m>dN~HVo9hcNjrm|WKG0F^llUJ)*Ne2on}!DxLT|BY4q|1W_oX@aYeCa=X~8~ZNg#rXw>o0(>Mqx##=LSw_ZGwiKd;dv4*P5i0t?dNwH-tnpD zZgr;Su{*y_btF1TIaW>b3tVg{_OS?xcXHyxcb{hZ~Ko6lMOxZ_FC&3|`nv_qa z0PuBsp>R)AylF{9qciz3nMh9#DV&%wb!V?xZ?VYt-p5jVN_@KG>x{~YXeHz{*D zih`_#D46S{_^(p(VZxN}Z?d1zZCcV?0~L-19J$zz1Mr1I*Err9_8SwM+w0_CONb;9 zl&cJ)PR&yc`leGJm}Z*-hNZROsg8nsYs@xh)>q(7p-|WOCx)w5t3htgOBIg7)wD8#_eHAfIb>Cj zaz1xJTK$IT)S#lA$!+)Jnic z?EPy>m&?lc{U6reGANENdiTYG%iwMkT!IJpK@tcO++BjZyEC{1w+u-jxC9Ln+=r0h z?ht$k7W8!9|EW6n)~&jAKisaV`9Kvt-Mx43z4qGA?^z=I~Ug%Jdd$b5rr{)lzLiAR#tl48mPixKEXS^NOfXHw)p4jtbb-42y? zv|O(z!;tzL_Gb7hnMuJ!FFf#J!hzvUEbY@zvOF|Lqa}419SqL)XeM! zme{zu3A*GblPmN#Khqc9_)Zj!w1swv|Bf)9{8|?+r;XyZf~7tSiP1ejZ#CwPC^I#g ztG%F+V@&`#EPBl9jWPC_ktl2%FU8zsBmG%1B}6X{e)mjpU}+-Qyue!j7V;m&Wm$F|EGNq36CM*8B^ne#pytjRFedP@j7^jWE)y)nAy> z=1vsM=Jw1%5psGd?#XA?ej9AZ>MR)}+o$Qy>m%NhdCRlU+gcJ&h|^G6+m{tG$gi1z z6(iPKDcAZmm25xQk`Kjn+Hwl?b{Z@d#L`*utP&&{V+7)v#Z+Z?0I~)41kPz0VYyHl zN;6wkCmQJtUxzoXC1TZ>6J=dHd3#b%i04#aL{MGh-B<4pl#(fFfy24{K%BHo%hS~0 zcILn;IYaymNC)Ef;&NUtCUj6$|2fx@nX ziZGF*0()?8pp)1H4)y>+Hw67+z(}Ei6FE63buaR*L_CX4qe%2?=S(b80_(a~N*lVI zFYNRc3N8b96clnlbeWaDzYh7WNzfmym{I|KD{|I#8TK+}L$u>kdP$}4I>s$d)Ls7V z?AIOM0eUl zeYpUlD#~%})iZGI@HLhFC)1x)2-Sel? z)Yg3i#>Ew$6%N-rEjMcXTw;KyPt8pz<_GX`2aP;cnznz%SXiH3L|XO!ff^w!y7L)P98w6mgIY-whi{ z`|lpYWJ+_EC_$(~O4CRJF_cy&?SZ|wNt|>{0WHKg14*Je`0nav2T5V47>{+**Ef^n z8#!`mzys-J{0Hh9fsm2KtG!y&=w9ybE-S}gxjzlDlW4k9J{Rsr;XTfuauf8nS}(uQ zmLY$bK%VjqF6k7U<}%i3(yJ-?-MWV)<#FgA{Z!N!u>48Z%o7M)I$E<~q=`32!kVQo zkM0Q*G%-D?qChb7X>-lWocg_26a$-l4j|1+tvJQ=O)jXSCyBy!DWLz4m=V)LWQN0_ z=_e;AH77OcgP){}g{clHMSLC+D<&(z$0SLO&g8cLViPgayQswFa2X$(co%M$xtFzc zFdiT3)$=YX*k+_n`bSEKe)s1+Nnx6%2WLcD%q4B)*Y4_1^@lkr#w~7IcT>Nw=6|{Q z*m_PG^5*H!n$6$vqMPU%uNQ|;J=1yi2QKM*Y(}1hOJF^_(@Hl{)uL)T{))u26`Jz} z5{aOruJPQ$l27TZ__HC%9)(M%-Lp00PqIa;8_hfZU4aqrg9)S#*XIiO9xptUx(wNm*^eb>rT^jmMu>bU0hQc9^bafixJ)>| zRcaSA3zGVkeek5!8W3`a-Lkc2S}`c= zGGaN)XH_XsN-KFbv8!P}d$R<7VqmH^)q|}RgMBrmtkQTgle;AN8-nkKl?Q&F$ISl1 z9fm56aWu6T$AoF zAGWXpk~>^{{IIje6KMK$GFZC9+Q7HF@vPizDZ1rs_Ho>NSkvc%O@B7I<7Wf&LV(M| zRo)UX3Ie&BHXD3eKynqSG!9>3+E@y^WGhh?RC^=S|D29ku3VqA6dG3UfSE)t&>jl0 z1*ccCHNL0-V5sY|Va)_0Uv;uK_?uRJ^rkBdZJkYf_hucJJ0CuO)o7?b->;y*^zw0W zb#^}fyH|UnSXHZkRr#{gl0?eTyZ_#?yEJ|AwhNZ-aX)1i`mBD_%->Gd!vEV-kCG{O z=;r|gn-#s&d_8!tUC0?3z4*E8zLEY!<>m%YKwwJeqoQ`JRh zV{0hrQaj1PRtbN9PsY4uUYhJE!Wwf?Fq)Di3s`?xp>SW&BRsizehbCXpI)-(NUj&G zJ3;pQCO~&se)8*x__`rkhh9X!=8!P^7oaL=OAR*r*adD#>--fQ$mhuK99O9THaW3DX}CVZNE^AUicG$ z3vOq1?^X25ffgB%`?gThC&T#c_Y+!5%w>}-C8eTpQOKS-VPer)kyJs=H78ZiaMoR~8bWU~^vU?V!4R6!<67>+pZ!@5f$))U{%vphihG+=v(iX^r0h2T+ z0~>6ij~!;gGP|G15vVa7)1MMMsNO0w9`&PUv)|I+y*vKqfvxMX>L;h2Y$99&|GnYf zK%^psK#wnT!9XyMiqCSwy6lfUt0emkIrxyASnBflFNN7s5_wICG%aOkp=n2o)=n9 z#RVUp_kXX127)A^+M!Vr>FsKm!4eZhKDj8VF;*qoX8fkPC`=AdpijSci3R`*qyCJ zl-`lPJr4jY{YR|OG+SQu%)iR4O-j;$^ZeZr;&#~@ioU`X$+f*cL0<(wlt{;S`YGA!xOab_>1i$Eifo?TywW ztjlqN5LQ{Q;T_$uqN1t#C737x)DwyK?7)N3;TS;em0B68UmPgorlb?%G1@r2MbaQq z5R79aDJ+0|2xg}$4Avwt;_LSS9ZP|d{*hy(kePBeUB0P{G@_z-9GK#O6(nhKKZ}4q zS^{0?Ckm83Z=S9iMhVtQ4w@*f6- z%KlH2$3q925{R}(%jCs+2_?os*Cee)DHk!uWF$j1eHNt6mFa0w7FW(^^`V0TVrvHR zB+d1nQs+c@IQmq6$38U20<7bKzxG{#@zGL>uw-aH&mGPOWv6Y#&$Su^?Md0?QZfx+AD{R-o!P@_Bu zf!uAO{Qv{Svw9c@g-$=Q*qIJ5^#1{v&bb}GvEU$-6oiwBqx~ z38E;V$Mk983elAW=QP(DadcEbI1KS1>uZUjfs7=GCs-oLG~G=5sbU>&D0>1DG~HG) z2S4Q0gI1}Ybr4novQJkAfPMtuX`-z&Ou9?Q0~9Ay`Zwi&`{u7;qzM>REiiM<6iqaq z2!$Zbc-YGVh|I93slV5$2CQ_`m7W z6`Q>D`2PS?+v5K>ywp&W*=vWo*8F=pNYNyqVL&j77X(d;{r?1)Dm5yy(~-swi2??)Khva`N_vhoGlY=QdtHKV8B0O<`N%@kd z@-`7wkR?(zt;Vd<_rL~mHX9k6D7FaXrf5*WEOt{j3#KWJ4Cn9ZOe76RY{iEP6mq<$ zfcD%8gAOATKZKL~GyAq|{UAj3JOyh_OCgC|MkY=SQJNFyrwJnQDoe$tDh%HpsQS16 zE6c7(FkGbJ2vENh?vg-Gd71xCZn`kxSXvV#6#IoO58xRGOXhi@qyv;1rSJg3|7Kyk zANzx|XF1N5V14aIM!U{`{y5Ib;Mi7*EIv(J@U5{&L7H+6STq5kX#>_F_*PSUKwo=C zm}1}h_LIet_-hJ-=cWp!Awk-_6t$RB;!s=pI18urD0ax!pG{=Ye?dIJdG{1;)qvXo zw41;|^L~Sa|C7CfHkvAhH5QDGr64-m_23_9`u`7HdXzDfNQHUb7lRK5cD~FKftRG7 z0!r((X(Jh3=|2IUm#>)B0@!}ZgpJ6wNWf=lax=4-(s8=HLb-M76^YIOM6a!?9IB5+ z?pb<@XE}|dsI5~~uES-j-GgMx1lV0o(HCrf2jJ`uMZ)WgSURzf4#8{*ALhdW3j}zP z{X-^r!@d$btb(BIZx(NyjOL{&a@i{C%R3w7XP2u;YKSn>Ugq!)KYgekC~FRU9>38X zOYp^zeiSy6P)nc*cHvvmG|Gkp(VbpOVt-$dZc>N%meY)1Krxx9aqfs`xBHUOV zZxRriWe@uHTr8_>TdYp=^+QbRfNdy{O}Gj#zthA`Sy&3xWY#)wZgF}uS#J{R-REoS zm^{Tk&NX}66d!=O4NQPKQ+}U8)i8z#a|zvNh!049B%pqKuTn>nb98Akeg{%ydj3=m zE}mr43>t;vZ{=-J#eZu+p!=A{f0+DX6LQnvd{P0X%o|$Cu2zqp5Gho1@x0E=)L?j*``}YVpj&NTQmv&d9x}|UtNR|kUKIjGuS8?dwIORKpS9V0&mvA zBFrU;1`?rukxFHt=nj!FnCQO8{cH6N2iq6PC(3YF|Fh_+H6h6$L+`){E7A7O^+ZUu^sA-icExShzs0V)X6?S(1(+^+R@LtESa8*uUA{P*XMuYDrqka={qI zSxY7LobI{9w4#|}Cr3HdQe&V(lhbM->>dx<`Ruc=g~FOZ1E3q-VPnlQTI{9;zhmAGeTR9!(Lh!eIx4h z_xW@odG@3jh@V|Ix6_S7Btxs`g2a&3Wh1Qzihv;B&ksgxs|hsGb3gg-)N!d-kcog8 z28h5*&|OJsHl6pYG&%KA%kdalR~1u61W{(g3_CR4U<(?}`lba8>6OE({qKcqv~4v7 z+jiN0LBBTzGI|u&(x`rl5nEgJJni%$O*-#*xLLtN{gaQb73ukUZT*Vr+(JlcqB9K` z7#n7hQUj|gMZa}+T7G+zP4Lnu2-vlF@%r0K$I0c@SusCS)nUH&&LBkEOZVB;ZMND^ zHdmzp_y__FQFMX&&s-Fw>aATLKd7%(sF9*fn5N*pU_dDk1M zQf=-Y$rh-^sn0jCgti7KRJNnKv|{Kj_GtP-iTlfEM*)gs>*lmN)aO_IOC8?R8F%-I zm&NlTzlDokLP}mX{#B$nkO16H5}s(yy@b>1z;{unIZ|GS&z@*bFrXX_!5>@w>;=gQHp{pU%(M9;MZ zLxL9~uY>Y^2a&3fkbSK@Bc}sfl+yAI(MOYLI-!7C{8%I{)oPwct;*X+{WmX42X8Yc z?HFIch+j2fB!$(mgc?UZ_Wu1k=O=ELAj$JF?*_zuiulo-DWAg94}>A=dP&JIn*pa7 zTPO{sI>REr=)XrRz4MTv9Q?Syj7#=FlN6=-%O zMr$;wLC*A9EhGxqbmcJ#-6OoUpqm7Egn{dj>~EM8V~?8$lF-MtEu%?!TOp;~mqT@@ zx%HUA(h^C7v~Uh424nS~$Mu<%swOvoB;!N=^nO55U)!xK?KAmiG0dEA%ar@sdh7`-}s{GZQJD`ud42JVw|c8 zUHh@-{f)Bbq}e!j3sT6Mh0;w@ydD5g$PhXRCy%O7r}2z{t|R$z47F5V%{`I6_ww{F zo`)~2i$YaO-3JR%_UB2(7^JlRlWB(`vo~q@??T49n{Z4M=Qgh2X9>^I-Z4k)JE|FE zO`jh)>ij759RENmOEg0T zt-z^K)R_DnUd|EMyCB6_%MiEmWw1FyYsj%s66a|@tL!E6&3k(Prnb_~hJ&0+O&Xv! zZo8PO(#~$<7Lr+YfP?wOyA@g%L_Z&S1z641UrEO%5a#&s%#kKyuUBAChuq`6Aqm$Qx4h@T^&MeWbP76=4 zq&evGEbOQlFe(-4V6!X;z>BOr-&~D<;mb#PMW$FNMqmnN|Yju7l;CidWS{FUS>Ib)_%H#MC{ZmrGil}s1 z!!irEP$=Xw`Fsm8=&|($F{DiWvXRiT=^ zUAUnYm$p)9Vbb{1v*pXAXl`*14s^Pthcj%F3TfR-2QXiz{i&-M=hnv8pZ)>CYY(KT z;(5EX#wnM^op6?7x*8xJJ3Q2^CD)m0W8a1!tlUXiWJ zId6?_&3R#^4a+BS)|zRtu7dHyxuYEmAmC_6szOD}IJOXu<6`&`LA%)~u=k81CScJ0GiJx=s=@~uWvPbZh)Rb#qJht-R9y8af9 zF~1(63!4b#cYp8ooF_@U58hFw&PP)uPktP&2npO7N`7fG77`@}XQweX_1~@yG2vy^ zyig3&O`0&exgSansP}rj*D1fdlz;d+-F!1}`=ObZVC^-~ogJ#fP*b`ssCRJU4bdN4 zzIthL4sMKS3w|U@t@geee;BCCuMPZg_4v7;uUqUqu^}Ut2Zicqwaxp^A+B0ACTOkv zU)q$({4MMh(JN>E`EE+3Evu!miq*UPyX0gck|2st4k48n$sKOnm7a^s`4$~E){{o+ zaPCHOU^Vcc(bs?@vH7n-RD*{U;c+8+UP!H4Of}Zp6rBhJo+>>a23_(HhyOK?-7Tbfx(!kDcx0Szp zj~6EJfViir?!xYigd*6Vw|_S9p4_HVa5N8TgiQ^+qU}*%zhE@X;mzlUaiBkk5aFu@Yloe$-vUfmdq6# z?j4@_URAHggT}V=6JHb<&XuWBDtQcAk^cq*))kieqZc^hJL7 zJzUD1?>Fca^i))@ToFoim)ob%`WJ337@6jFAAdX2cGQH(rX{^ddn6(HnooTW7=Kz@ycMdbn(J$hU3^#^#2tKgP=l@ z?PwU_(Q4G>V&tS&4UEPV*6R`*QU)wh!(;*$jgtvhumx5yQt~1W+;8xbIe{bVp%r;= z<0xV^`qO}d)K#%}Pz}VFLaU?HUQJ84LkGJ1EixJ>VZL| zRj{kNyS4Uu=0wYN(BEt5`{%1tLt(B~VvIP|;^)R=e#v=O)p~!e`>-5nKTQbtycNO5 zU~L*#c`W*CQxxmV^Eg!5-_mEM!^Lq|=0xXO@jyxDB2sJD){RWpdCGdNA2wt3<0-n^ znTbBvrj;eMCis9 z1yiux3xIXeA_>#Z(y-?Y_;T#Ia_d*2io*8#?i(}vnJ}OR%D5D$aZD63Jqo>Ler}W4 zK^j$gBtd!1O-svq>*izF4EIlV z8bi89p*VaN&YYPvBj!-EKN&%5gV&9BSbr&9^mJWZzmSbrj4U%L7erj3cz*28YWa@d z_~$%!w`<0B@ya=OYV= z{De%kJG^y=vP!tj1QGH9h;xuDzoIIYDLb@>lH^$u>Uy<_P`u`iwi z{n4w5ZyRC((#=s9X>ff|!A-hbo-co^)Vt(1-bTkQ~pYi$><69(YjWci)C3#v5teuI7XpO9 zHbyF_agNsegm8%*wF)SuDZAlOt&e#&V3nNnB)qVS#1c0r#FyG1&?UIn@q7)v)X?C1 zU5NKy`n~J^61K{dM(5VKquXO!TKKFqL+VeP?!cOF;veZvaP#<;)_1W)9n_k3W5ubm z4i{QXyu4>P6I#r`uaJx?BaT{s$LttUqZ^*u1*F|+l?gT%Uw&zXi>Xv#yy*8^--)K{ z=ry3G&J)&eJ-Z)!V`tGZ+0H!ueK(uRu*1_{pIPkA2 z4RV6lt#*OVh1^X?Wngf*o*nL7Kl1}O*PIJ91Z+{sXazs$|Dl+oC8+_Qb z{*5Vd34AlWmjK8lXi<#pKw1kk)hg=xBf^ zv*PxS5IfVxkdJC---onO)I;G<6R`&gYeis{(9`)RfX@~s5OU$kz2k8`p6$0ZK~T@t zn_K<9-?*%1#s6f_vE z`Zc-sH|?f;HWc2hQu$9CJ@dP5OMB@xC$$_JH+}Cy(dzaMOy53NQ{bthdo$=t&-3}a zQS%f)a6sSAUvIZ{66;D6Q*YBhRX^Z{wZl;{0c58W30>;6 zWdCQA^+o%~1j91(0)&3;Ahpm7EEp+gsTQq19Obv`3~bC{d6x{no#zQ9+i>*O$;>r?^$FbUjV@gt9@P&;j={GBha5&0O2G^6os-fyZoZP^D{uK0 z73Ec4wNXC4_yxVUU9X4sP{{0+yC+Z^J)^=HoouD!eateO{3W}|IwbVql>aaz8Z?Tm zZTppmCC-A`zv}t>s_+at?^4(97{ox8Q5gvSeE>CJ>r`>cvODS(_n$S+jCgh9l$O?m z4J|G9K}Xs}PO<%<_50Emc%T|dBnGQ4P@D0_tZxqguz}kbIrTU~KqS=LTm9Hhc)lld z+PmP~4fgw|fZXiNN{-W*`LJ`~1zGm5D-rprU5=Po_3@gxNx$ncue3+JKRV|ye6$`12imB{?-(VoohzZ&{52$s2bj!>iFi1YBh$;PkwgOAP z&6=l&@^rSRVclI}SsvDDhoDJ`cQg9Xc4Y7LH;)&JUterAHznlfhfzP?N_GeP`IQH% zn2G0jmLPeIrg56fCCgoz&aTV_T-GXbauj@-zh7Fl!#fmqBN_JYHU>1Fqe_;4vhK-$ zB{-eT-K2zEl+r(3C`kvDS3P9Y_n`iKi$t+Qt;DTDgd@`sLEcojyW3aRPG>KRI+h3M)_8bN|b{> zk=r9Rv>=M-&6Plbmm)y=fD)P_b%U6nJ+`s&tm3XkdH(TfFK&W#vDy8OKDkTkr=l-W zHvWNtMq_i^Vz>-{^T=XbzLV2Q0dscG0JY^qLN>hq>MPTF?pP;dB%6|ebMi7DyOQj{ zu7;yl-R&>w+k{__8`<4{WH#*8<~rN$Pi?0gF*qS#4^*D1a;7h6q}wASqvpnt9+TOB zLOce7GKoixQi3A#>ED?wK{1_p$ij96ou$cB4lWOisqzTwz??thZ0yTFwEB=ByWGzY z)eJMBCs6R|lWZNTS38@znO)@|O`FUk853*yR}tb$+`&Eo>21u(~X z%$%L>`nnZ6Q+HB8)_#2rXn0 zj#wO~8Mh1tbBrD(@E^kHz<@^#6@B}$LgF(*R;b>VY$5ryI4Q7IEXG{B8c0$G+a#== z1xFBH9waIC9S{9;3^pKXB-f^=&HOxnCCnqbGdn+4yDsTf({4BAde8ZZ)N|LO}K^T}4!68(HHa|^`Y-8sfAo=7m&2ICc#g)Lot3O-i3 z%M)?_`FrL~-p4_>HAbBk^-wZk^Aq-6RgOp*^Y?4}#lVNh`8@|DtQT@}xYx~mC7&MJ z91A^xdbvl9lTtfK-;i#1?B)0TsCla@$m_RFW3d!D-`CF><#|w_J`vIQh=yz?-kLlq zkoX&X+FNbytG-n}Y!9v+N4ZfoJEWOb7?(zN*HtDg zX~nM{AOCQ7wS!I|-R*k{UyiH{*)7Owc-J`2^JgsUFS}osiT_SCfxrHP+CwXWbaJ-% zXA;`-cz=1SM zA5@1@a>zsFBRu)6#N$qeaE&Qw{rBGNi9(_gf%bF_2HNK!e?+v~z|NNjHBgQ|FifD< z%G-)cVPEX9DH(a6j4m6~c7IZ9PR-9Z8f4U==kn;9Yc}{eP;lPc{;E*s;bLUV@p=co z;OrSP!x%K_JvTA2q*w99*<+U}k#p{dr-HVdySCZGXDa}u>qj$^+P(ib-@w$f$_%M_ zvRzd=vmcLYf5GUw6O-5p!6xvVpZ^_s73wneRXqDB_XNVt?_HM>$>o11KMC- z&z`_DvfAVFFXrwjQl7{Jh$I=@o`y}Cv^4UJHk%rD!b&vt)uBOlwrr=6t6mJG&t9W} z!pKHY2eB2|&D!#9c<0xBv%2qvqWPSI6BwoEozxzyhvlzGPxGUMU2u*OY>nrGBJKCX zqN`an*({=THZ#}3S3DuXQ+8`c2Fr&2ceOl*7gIO{)wc|3@5I|ZPr=*`0aj|k%rdQC zU`~tT@Z!tPCDM$+q3ieUzE{~3t2t{r(y>T~HTl2GE?Wy~$cvEJ@@L(V*ONAXW*wWi z?@uk*wfe5+Hv6tR5(MB^+{%kmiw|b+_Urj_kUt8Bsx5ez9O%9DE->+4calCG`cdof z+UVYeMq%qyFS3O0@v^uiw?IX)KN51=>4byyF|F`%;+$>-B73WSm&BYcBv8*$*lq5b z#aC@fh?UiK*Mp!nTDoS~`?TX1|3g9OJCVg|yX}@C)3hoPG8naXCQU5@&PqrhYtFu@ z&>8np{xc)3916>4cSUOAU*QuGUvR-hipH64w@}7wVd06tE~qUpb<^iSFZ|p)NPay~ z7)vIZWRu=K`NA{vD%LuPT#Dx37F_pMm5JX%^l#4}jaHZcSRrLSc)8^7YTvJy^0;(= z-KhBHf0GN{l=L)R^LVExukb**=JgnJ4^$rz&D|c}5>oKJ*ija#sOXc^!_}zL*Q6!+ zO??PFDvh*alZ<-?wbE?~V?W)c3)gMl-|BD^qF69Amj8Ms0VllJ9S?CpS~(ZwO<*sx zx796Dlr zP8p}Hd&C3HBSO3EEC!{mOR$j*sZ=O2(Sk)%(1m`<{7gi`{7EhbfvB1BDaTmSC>F*m zH+W9oUBE|gbB%0g-xx`MXg%b+7<~22n`O&!U>W07QGV4%^nDgp=+5y)yw65NR^cDJ zH5>+?r@Y2RdG`sfch19RjDxh!)YP?0{Eao*zUuHNmV4W+#Lb-tkHlZocv)8Z4R!m; z7TSfXLD%`B^^WZRpB8IdC-JOAJvKfG{gU?d4oJy|r&-g_n?WX&V?GeDpbRt8C-%%G zZu}Hwsl}~e2|=ZKG4$~ur}WbfMD@{sKUO&M*nPNy0M5r*gl2i(Bchx*=^^+>pWiU| zWASRlU<_TAFiGq0w?tq``TSf3!^3Dg^{UFO-s)L1}=a&XM z-oF^`_nY9tpPT)^6x5%%fPNcm=W(!;?Xxz#sP#5=r*SP?Hk)&HJV^NDvaG|&dlN7z z+IZmjR;MvUGY1>#3(J1C6$Ss$Jh$hCj#3)nbbivMCmYfijB6)~;)b)n4}@1i>mHe{ zHGLXj?AQ=qbTa`2QQe1+Wmsb+4#pyHlV8p7DUoB0T|q?@Fz(K5>m>0FE_+0aAh3dF zOBx~1IyMx`k+v6bm0K4lJKO$0NNFXE__uM4wYC`1>9+VPat3@Ws*3W)XYo$~qU7}> zzoZtfcJEGnc66p^HY762R?dmYpY)Ca=a}^4;vYrQ1@r2HE^FSQFc$M7}*zNgwoc@Dr96UQ`yfkvR`V?1aE$yW7M%@ zG4*qC-^&Obwim^JK2#;bZ+Wgc1}$ZSHg5Z#eUv_NIMA$TiaNxJj}IVSyKat+@&aiN zLra;{rOZ+$eoA{4ttLexZ$^wpYzmtm1`C&5lSr@RtlG+a7L_v9@*3jPo(P+(%if9@ z9lbR>kz|}z)sKz^#CQfcX~CN;h({ZZK3!Qs^~I^hU# z6|e_|OJ12MskG%-sxsXf0=xFZcaGU2WvVE&}K_GBZwX+oiwF(SQd6jDwLe^ zo4Adm#cT0#@!?X5^=gOBVwh%6cdv#*aVC~9wvk5st+b=v$GLIMUABT6%jHA?&4@>PsscV) zeT!2de4;j~U_Re!&GBQu#V(uJ?HJ+=Tvo08*^=TJFpA1}Mc^RpdCCcK(Ub>WTFv>G z6zjHG4*V9^BL=Q{lNgcHf5lYIt6P&m#rP>$MW&7?%@bEjOMGkb)bT_!3`zq;5 z#L$dDtr*k4DDT4bZDIPlGz?;&S~!Q?Sl}AOLPAmV&vTqBd|UXA(#~u0nm49~$4gtay6^_Zv&3eq@SWcVs+x zfk4y`^4<=d61ZCqlQ7~U22BvnvtSxrmSrCs_krEtwm&YuQxqk!$`4nRAKP+R`zs;& zr8cpNE{*q8{O4}Uf*dRdGFO+E@^3Wc-7Q%n&>@lG1M#3~8WAk8eO!vmfH^OrxF|%8 zJ8g?S#K!CdTP>5unMtE>Vmivx@Wtssv5#b5fIoKhY% zxL~gJ8m}E}WQhu!ifL6&ILF=q5eM7)NcOH0ZoA+Qv^Y{{2&^&wk+=W49uA28=8a&2TaRybHoCxY2=rFR~!LUJOg(>*rYy0 zO@?qCGRen@59&IHU5Dlg2h-Q8g(OS$!Bd2ui_z5ma{c~ z(Z9FCLDp^6uvrfEk%O8oWPgK&HWHd88@NX)P|NdK52O*>XlSDS06oQ0bY2$^Et$M` zvuA5t(YcHMS>L6hd$i`12ssjgrp$4>f0DnpNKub@^S-&Hlc#L9wDtN z$rKX&d8|aFjJ#PKqopNHje)L2VZZfe9kJNZ*7HquJ@;W-1H?@N2e97-LypBn;Oq9p zV=5!*VBse*x}&^cn$R}EWnPo~NlxXgnXJg9m8SLDYe;KbbFg?{6d&BK33_qOq%1-0#@OZshHp;FGW8?Q5p`?*x&3LGak&E;W&%xH&piaexlsO1OPV>smoQrO zw6+=MH5^n3ErLeLRAM7*>lL@5u0%K_(a$lH!U4NHUSRVnTtHQ*JXhs5RPL5V-&jVd zJj!eMyOV~d2p}-lhN!!w)gCntVL)GQirRAZ#wBp%La7=yDdM84=FPn1B=nQ!j5AC^ zltWlWt4`?!w6rpMR`wBg0f~S2uZY=Icw%85kdxxJ(E5RW$^e1Q;=6^u?m=e=a)lYRc=< zplSlG?>y7Ssdhm_0E{qStNi2=h}87bWEB)t72arKfYnqdw9{4eSR8~og+6t!$Cd1J#I+ z*SAFszyBPj;wlmf3~cCZA_XuopkP`a^fpGZ3r{gPRe*ve}6 z1<07S(lQjjswya&TJr)5qYR7hTBn6dSe9^ntc?yL5UpSoIumD6k>xGRXJXNzsvJ^v zS@%?1_*NWoCvo7>FKKe}#MK{R`UOk2KT^rb*G1w}N!7XdD7ii5_HsxYH{U&fos5zG z4r{YWx{cKUf{sNPx1tfur_^nWDvu#F28|`g;Ck#!K%@|>CMul(?|WqY$he9D;v>1} zxYDr~iBPgTe{6{zeY8Tf743IljN>G{4$`+-#-{& zg@Z6#w%5C0Yqmk#A*HXiM`I@L5y17UlMpJJ0%w};8MqK_`Zd3Y8nZbrc(TnH3}c94 zjQf*!^2kW3!;YSXkH-Qn=z>|Z8SVnzWy23ztXA|PKowT#q+>T=5%2RhZhnNzL4ySO z6xs7Lo2&#HJ&86F0YwE{j9)~`Q|c-R`dL0QK8sX6n9n6}h^_aP?xi^lxD9z+)+V;j zQm)3@qr-XA5e?;@f>#}3VV9;y7%~C!FZ2rtzwks6=@*z?J#_M#AKll-zp`iJdpQPu z{v6-lM}!Z5mD)fv2nbboxekZRo_9Y7iZ&PAlMGx0egZbux63F%hYZs1GYGscNCY5_ zResu=sS|2Mh|}f)`rK5Sp@c8-N&}8@^t;xa&k*I$HF19vn9bW$PTP}!dTi1^DpR<&Gx-cQwCC0!*YU0uLeNf$#5 z_7%Bmfj+OzYa`iUS>OYF$;v+TKRnZ7AtRZdQ!jX{!TX6D9bJ&xxqe-nn6+qo6DW=< z<#QuS2yx$y5MW*HFCf4~d=!YmtccJfra$c#U_Imfvas_D3l8akw(%PCy^CQhr?b;k zGx~4Y(qSKB;#~P7_1mEX= zh>5=y2}3%ssc|U|Bxm>-`bQtaC%|VX>x2fuqJ-+}N<>fyS`m&x`-mEYPZs8sJLziK z+fn!2SD$&(@%2L_`-h~&2$H?nR^;ceX(or(ZWbtsB53XN7au46JCD-(x3#a$r~C_2 zdp_gTT^x&2*3t2BhHwjTP3+%I!AYO&TgupE@J>z~OnqtV^Xw>b(&aX_vI zEM^O{dGQ+HaPm&6sIdGN8Z%A!!|-bfc7#}hklm*id*dYP$jYp-nfZx zwzim}RpO~Idz@CC=WqPhynjjX5i0l78Ap)=OM|1(a^`rhnsg}Dc%zx#Q>9(`Z#MY@V^I7!w?}7A&(mb zazEHAtU|;MKZ~WhDTxTcJ;oy}paN;T9pHJ&GIAD)fQK{_^XD}a9(!kM8XG+q9bJtE zbiT2CyL{Uos_8p{4!A76Qz>?p z+s`8z_@YF$`U>>p9z@AJhoQl+#khp$#aqg8o|T?@^HT|=qSsu68WulDn2D7LbQ%&I`YAo znEn27g$xlbVXy-%Of*kq5FsOw!gAhL7}L(&VqZnaB^iMPRaaQ9wH;ooithEir@zc^ zIm6euKAz(U;2kXPg!aX&6i&Y>eBWLB-DqjI0mF$x=bCsWd$wTCkLN{du~`sKtTt`3 zBAP97P5nE|M~@i&WF=^|r772MH!$Iab=#`Obxy|Cy0bJ^}M_u~zZ?Xr|p65Qyq5XenBpFboxad zcygxX87#uW%sMKjtJc4(7j@sjqu)QKjh=MvC@*ZES$hR5Th*KeZ;YtxRd@ytXp6uku1v4^f1U&Tf7`6jAH)9E{jS6ohXpQu)`y;6 z(S1LC@2k5y?St<0ZNFzzUx>hE$pCBVVgmg~YUl#ZLr7uFvvZC&E5x2^;pgfxl3jf~NU5lvMB`BXs`&)7|* zACqDe)i2$)lQDuY-@TyU?O*U)@D?Us1U?h)BLsFyeuEv4jQs3(%8=Yli+=nBJ91F` zFRK1Bs;#aI7lv_nx8hb@i(7#L#fv+|J!o-)6sJ(!rMSC0#oeuVaCf(N-_Lo^C;0^< zBiTD^By+BL+0xyqOvShK31(cVc)2w7Ht;V1o5`_&Dig zR&z0`m^nBlG}XhyU8`Zc+IGOa(}JbWEfZfER+Tj@T#{u1g(asZRAnv@3og8=!;68@ zW+J1QySGnHg}tzYN`3AFgKEr|xp;9}7gcV&Q;&$%FQXE<=9FG2sOcny2AVsg*k3IS z!J_>9rY^q2^5gw3>;ZqIoBrfiAJfa;gXR6x@1_ey;@iiDo-Za9HkDs?@jG+duYXa$ zm#{7A;}jin@XH)^LQz`2xc5F(*8X5D!%pF_{nmS3S!Qze>8-WC_$8)WSnX5eB2Pj= z+1_~LQD&tTNIH09tJKdN1u0U$G`A?XeZM=h&6c1q)~_8dpwn-i4eFsz88AEGeyR z>bxWDpFd|a609F8c>dShJn6vhQ7{o^DVkXFv@kQS9R3bji11a0HsKsTN6A6haUc_cM+2I|!++x{7$4Jf$;CqNd9o*`)XIJGmf5Q;gmB+A zvEsrE^vyRiqn+BKUoXFigXaex*} zOAWpTtR*%7*bLQqU@Sf+jzI`DLraR?FKbmUywq)ZcghX{)=p3S2Zao9X zmEMzkO@9T6zL~YKxBqxj$`n4YYg)skzi2@&5jgi|_2?8lEXgJ0{6_kD@N3Neuv~ij zrye^xY@@t@E*Pto<;#Pa&hsMM_8x-U&6Fus%TbP$rp}8KF-n7mI1`!uMd&x?#isMg zUV}KJ`Ym7gQcUGNCD6JUMGn0eK9t%(!*ex`%+^T9ZJQ`eaxwwvlR40z#+@ zFMBjZCj_HLDBUN=$NM6GX|6}^4d>uLKit10iBuEgTyEX0x3`~|i*Nm@eF*Z9W%f9m zPsM)^{M`F9ZtX_%17jus1rrR<_?TzK>HE}-Hg~7VM`-O>CRRf@rwnher7;SP{2vBr zL($#76X@{Kq1SOw9i;(&|f zVPya%HJfK$vv1|X^r>sTyA&h?7yEQOdhYhyNJm|6GnIpR|DDWwd~Fn;={XAcV~!Zm_~;?`9=#t?dK{zw+mi@!O8e*G z>$KO^TK%8|&o@FKm52tz3jQy=b{f}?;f7lGY@zt_XTCRx_r4e92KI>pdQ_k{wNuai z3d0VsTkbN;g!=X&_%E;IQpjJB4)@pkS`4jVA>Y%+~@;yj6v+Nl;wEDTeO}n>7ET&)xKXU6>?Et|YnOF#7hKB*D$id{6#= zJ{%|Eu=)){M1tcyev;|a6*(?gDuIV;J;IrL~c z{NB~AAwhYnGQ%ZuA)_G##qSBj^^CESJ$ts!SIfZWen2`Df18wA%l8`HLO4b*yEe|P z&N5rX`>s6mJpC4S-O+CJmjD7Lt*I2;U@wU80$#S#BXB<}f3oA_+jnE09lQh=aB1Pg zo1X@v6|?NRud^yT=)x#?SC3ql#7%b?juATU?0NK0Kg1Z3B}>fX?(AbNtoj5$_}}Xx zpV0cTZXKkA>R*nD64!E6YV0aP0bGoX`V7@)5aZop9wfdAt#3t2RoLTCm5x|!xrbB@ z-w!ivA?W@GLEUMKclljX!g|}w^vYfLP9@~UqPre3_V!}i!8yE=y(0mm8LECg?*TsH z)UY#mNk!re+@tlb7hWiIcPDnjuI_f(?~n&=F9nF(@?+i9BSeuwu{1W2x>Uuc}LYEyV5+Hr?94? zezaD(P0IaUE^F=nknI=y{KeZ6#t5_iw_F5uP8Xyj{O->$wMhRLp}|t&aO=n>sqyL%zQKF=F}k6L7|mR&d5~I)ifS)i?!9 zBCw=cg+Ag)S@4=_mx8W~MI?|jp4IoM;Id|rX0QR8Mf79W^&e7WC*Tcud!V~cT38NU z1_wvZ{*cFg&t%&}syk+w04be%hWKen+Q=Vg4P1In_sMkT5? ze%H3_!&vxSx{KBiG5Z{%mA$@dF_*FlU8NxtR}ym>E*9l77F7Twf{oYlCrF_OZgjv&Pd)oVsXZd>{VSUGDq?3#9OK$5`+&iizp5j_3l7D)WsL*%x%CdZiJD+Yb zXSbA9_(;pxR$@$4i~0^#msG9Jfr!qm%AlRY-IMRZ7TS8a*gnAd3=-zL0#szb`0&TNqkWfi^`T21|%FQ|mfkk|aZhg@_2F4xm78 z;{F_P6G|3RzX9+z+t2UeeQa!GeuFa5`R?1IcF~P*uN8A($f*(PQ)E>%Guv6q*OSg; zZ>HT4`>ugTw^@r2p-iyJdUMre=cc~llN`oVo-l!5Qc|tn&H_Q6H@y1sfC#Zd@+~9(g2(?16|6~wy}Rkuu0$r-9h%&wB*8*vU+5< z-?)PIy52Ue9=rA3uFr%Uzxi7HHTw zV`N=)^5my)Yn*yGHP8^)b}uhch%U>f#_}(GdZDJdB6B%=rL_AFcdEtv z@rP2GZ_AIxJN3=oMe&F30rlg#?5fit+ZB;(cE{i3C_0m<9&gnp6>g9_P?gAUHBn0{ zNWbGZ;U@d%wW!UsX>6m#JX+^(VHtBKN{-|`Nc16|kQ;>c)ILx8en*LMG~905yVJqv zoH6)5h(02TR6B%N;b*zWNMuLVRyZbOil`n)?|f3Cv#vIZW|ycqdAXe3!dCCnLF-1G zjm@#>y`pk_Mn$z;apU~k<~sa2v;KzTM>_AL#ejWwib{0++7^>N^0>-+*Q(J zn|~(l*D+zhez!OOBA&=O^{W9UqUj>D%;c#Rs7lFBW7FWGc?JRP?G&&d17wv z!ZPk#Pa9OBJrsV$GXUpZyxk=&$bbBo{9eM$iH5EvX7s9rEEs5ufO|PHpb#}aAZykq zEl|?`;EPp+erGkcW5)lO^7TCGFG&V^m|TV`5uYq0b5<=iSAO$j*r zb%@{gk%IGy?!xjx(2+~mRSq3Bss*0nDD&H9q8yE;$n$8OdjT78KCjIL=`{cUo z<-5fI>-R(ct2bjvXIGymx~qY+yhpP^F6y=+l4f724j0A6r_KAP3B1LKV5P7R_!NC!aP_Mh@5kXc*niedEwYGAvXtZ0eQlLi z`oNG)cxRmQ<8lwKFyL#ZKz6uHv#srgEY1~v(L=?NLA>d|^GI=tN8Q2$3>Lhkp3_|T zz|d&4?A{x(6`9C7%V3|HuJ!HPw*?M9cICTD*;u*g?xq#Yz9M{DldJ7cvR@D6zT+q2 z8)<*CWv?u(|LT%YIPqQ=zSCyP;-!}1bvkr_=0FC6_rFZgbc~+lkm#mf@7+7rV;5_~ zvGxC}bL6a-jHLuzxcz{hNEIEu!!q4)E>D8iN*ry@OESz0Jml6Am+gi8^?_1O0>rUX*l~-P|*5@h@7bWR?zEC^?k6?`2%4CHJ z_wlwOuxZ99?I2p8wp$mOvK2te;>)Ox)p+<*9c9Af))Be&Y|SIhkp3O>#HQM8@0;#7 z;g6(}>O-BtWctc4Aho&_?uREO5N+bMLQ>eAz{%Ly?{`QKKyUEt)GNS4b9Z50l~FGyqzf zip8!(D*}u~B0tX+kxK%0N!sMTmPWSAtF3c&^LI^KL^L`Z1c5h28J$~!$&HVxyH+%Y z4XK2D5R(A`fEwss3g>FfMW>=Za=N^r2&ZviIh>CH%VE64lgc~=I}(h%sdxS+RK{cR z+vW3M-oZ`qLP}yF9%}-S(S|i26j3JLFfgIN2u@Z(b<)0S*^QNkeA2Bt= zbx0k;qBL-ITH*Qrk65z!J5UwmZ}{9b+}~`QuK$s?S`VCk+%R4;i8BRX61fOFkD(&Ad%pt5vx)@ifjS8e)dH&#N*@WRGMqh9}Pv8lS<~(X2B3+GOFS zgW4xu518!#zKbpP;z%oLaeflgcC87es%Y^@N{0m*_UE$>$||CIoF10W7yks?-~lg1=P zF%+R(A5WATUS&`31o z|J(qF)Q9My3W50w($a~pVnsepuHvC!dwWMX)i1(>y^&AS$hg-`X(;RjsIz-`!sHy+B`A?Xto?H=Y;dqHu_!6YwQJ!b z0B#G$1112A{RJURxgufHOIkJ*g8YRN3siOVM#apDjy$)rK^(Usm)1{)N#Mr8`MZ;z zV$LKQC&OJd)AYSGDAI)oO~(zdKPW^M|0C!$W${wce}zrnP1JQ#!i3WEuktrt+0q}O z5fC}{sUVj*v*}IYVZl=a>pn8r*G0E%evf%z$uzvVD!iXa zznGw$kn|^k##Qe2R)z2&_H1F%7>C_+cs}BFcgM>3On7rRirjlbJ0et;k4YFx7tAn! z)2#Nv-yETh;HbPJh8E6EIK$R=^jFe7tGW3VnCt%#;q$ahl#BGO#m9VAPP*neRcVHr za(5V(o{|Wd{{`xgnlbU)5 z7>d#@Z@D+zhSxd=O85_tY7J8fAdEn2mG{FQcH^NwGRwPP4wqVHAL2YYyGm-I3kye; zV$xu^2#{wUa1pXHTlB}vCQR;#mRf|}h=fL&y=RE>pR`${_*=deSga?dqMNgKgb?96 z)H=oNK_$F<$)Jy&2w#iNDG*E;be*fEyMNx#c(+4v^jltU@KkxpYU^sg%P6^r5`I}7 zBM~gS7Y?`A3$g}ciuTqq->^F^M;8T8*$q3!$O}{qQJT#&p`fa-TH}a}+N@wj& z;YuYlodGm%s=1$f4{6sv3#AACSx6c^J6=S=Rd&uBHP$HF|3a?9J5lDr$%~tR?^jI| zdHMNnIy}I(eD0oJ1aLIpz;(Gu6;eN z69pHf7<*(!Ht+=5LT^0_X?@jA;+%p&e7a2*8T7l89Tyyu~I8LePIVLtMP>qrw)HVnO`DrjuD9nt* z&-qJbdj|kG4S2s?PWA*u^ZW% z3`lZgM5Rx~s_8BoG=O~cK2&eEw1?vgIt$t_Cfc5El3`Y)DWA7}mo}P5SejP%?b`*k zB2mr7aV5o6%;XY){OdD7ytksR{kys8d7Rwgxl`ZuJ69_k3s?1&TO{?=)ziE`O#E{7 zQKbz$Iudy;6p^+vxOme1YOO7_vf6t(!8RsD8IOvZ-qyaGeVtVI*AsrN z{oE*~ZbbQny|Or4TYJ{!$Aq*V$^GAo%E;*9{$Ks}=*spQ>1uPTHz)%Y4UdXy_e!I6 z{|-HI|CS-$gD`Fj{J=4Z^Sd3k6XKi<12YTi=5Ol}ftBIo4cQ(fcXrw8-_+xbF8}^K z?P|jjU7r6fl=Xt5=es4NB`7&Q7M@Zp*LrlbFBI`r@aJvWKXnDg1P?x4qeWq!E^X zJMOh4uIlRK-(z|q0RjG;TZf+N#%%KTnY+-}(mTx)$**i)@ZTRNvM0(WKQfK2eX4QO zchUR6k`8bye~PPf!!orxY-B10F@hDeGH*(L0VUKHn+so3Q#jWu1I}b4^xh251=7eW z{`gCXZ+v}z%W_xD6%*RQ?&PC`Xc22yW$I2~L5gbIb1VAzv9ZK{`vR*tgG%!Du1Jgh zHfgs1^?rG}j@y=C=kvbh;o0HF%CVNH@Hh&A*x}>+V%d+yO5m^U6D4SMrKjPsx=!fT zMjcCqb@{8O0e5EKNmLNrURP5;^il_?e(WE|GkJ4E#om)nOrUoA{n6ilA*|2o>o+WH zX}Wq)8y-Kv=8LA?miHR3Xl&(~`t$WL_frGcjjn_8Wh6*}y1>G_71UeVq+o&P#PGWC z712;h&}Mhgtor)aQHMzMeb%u}*rO)TTbtZ3BSo4$H5DfbBP+KSCX6=>pzJ30yS>SG zir!646T5kS@4^B*GZE}r9)y!NJJUsVbAvyk< z-SYKy&uOt?*ALbH>9d0#Ddn95ESSwut4S~IiDl`ne<8|k9!R&$&0Y-bZ&}46r!D+)9#nYT^ z1MW3MvJqMQY|{JFsUBjx=5*E(!j=F2WgVRuBX`7Z z{VJZuUD-8)9@Z~|m2r686h&-RDbN?Mf!u;e&`i(^g!y-dx4Kvh?hI{ht4WZG@q4Hk zHv@+j56(dBobndPQ{iI!#m=4Jg6229n{|UO)Qh~AkGmXsxj=OBg}a}Frm`ftNlzdG z8U>^u1>22sh9M0z5FSLmpyyVN1rx%@i7d;pj};z+`$2KKGiC9{f!CLs9dFL9t96HD zt7bT$Wphalh|2X{H>x}Px7KgD=K?xA3*47@n1{?Qzr#rlMp$0%3h5me35SFrGvEDl?M||{EgSP5^WJzpjRvIg6VoA;PoIp5G$_81 z?E%EU>K8+_|MWM>HCCp~Pc^tTJyP%*sxUxiqmbr?8u)!gY1yMAC|NHuVv(i8c`ZU9 z2GT=nQ)+O0uKP#=#eFo3nX`l9Q6}uRdk;N$nq2Ww)D#6U-gr!sbEGs^S;eTNwz{7G zqbzqJIv&n{|R{&0(hiHBKmny-p^rpFbw+;EKp^=HSOZ;&RY>UJN*ev`FZBI<7&Ywx| zKb`YGBfX>*nPMuWOuZd(L*0wEUO6c456x7Gw!YZ-4>VHyL3arRmu!SUc?+!}`19fy zZoupx9FAGD8aeNjZbN%~YaqSc^|%TH#K$agB?rm`)4%>4AmF-ChEZutn_8Qi(#>_$ z|O1o7cF>{Aj@#?)rLUlxpmKOaZ) zYx|C1SX7#|^5To(#+-D%XTtcGooRy~*MoP@UqD?*SUI(WG}k93E)ZJOo%v+gwI(A& z?&Bv1e-%r&5`H4R@y>6Ci%Aqn^e1a=EpAUq`MTV192)b_mok}!VOx|H7-0s8|@8^FPZj_>} z3MQ6-Vd?n3H>S8Lyt8pnK{5Hgtix%`m(-{6PZnQPYLm1MGhU5f#~Yr?iDequq55U$;1 zQFJ~zYjGW&3U%YR8-wP$FhaUFj3l1H9NvWJ$)7j0%dS7NopzV>FbHly<@_Tg7#puJ zyraO}pB1{`%3e5j?|bDdpmXb<`&@e0`8>j0b>g)|;5E;bK*<^E!9f+wbwVU!Ef1O@PhtilXablbO4<5Y^v-Z3Ld1PEXAR0-<@yNgJ+x7efu}MJ6dkxS1{V2 z;#Vl(ez}C?IQ9rwy6-o2avBZ}c3d)SgHOL$1n>umvAz6xCrba5kxvW-`bN_i9;!CJ z4_kf1+I5+jw4m7frWS+MW}cJqbla8OuIIg~)80Xa*APP?*6I85Q1Sc^S{)x*jQ{=i zZ=2~8)t-lu!9AI-F2h(!2OZ&j8&gF+po8Xc+Xg8_jAsy97Sn4Wx*g7_V-qUiAF~hq zM#VwXK&6Cx;w5E`eb%y9niox-HIiZ`wUWROVr_@L=w?-X9G~YNI9|9Ff-d-Dc3&`c z5nt5i)UCzmdTP(g_kQ)1emIWpK)TNtd?0@zO&Vw|`WFMh+1);u?+C z=Y=gx0z}(+v^JW;e}LKu0Qy!z5h#q^{7dm&KkVQBy<~Rglp=eubnTxkmpJL!5SR|@ z(+|`#J7=xWdtJV?+|I6+?_7_$%(YTDi>44h#Nc^%BF;kE^1-3<>#^+-9`H>fi9bom zeXsK#xhEawGufLwA@K9`J5dx+VXD|n0R*XrMjS3@t1T}36Kf6UM_{@vOrplCt2H&% zZk@Po+jkG!$?}X#-YUDt+w9<+d*4^@$@(nM95d72K}f&ZIg4kZoSj8y6Hm2Sr9U(^ zkq0l_XBBb0Yo}@jdO)vl=C3`?QROu;>uu)$u=e+g^*xe5yW7qITD!w`bbTSRX<{u? zNFF3sR6W^8_w!TXG*`bZ9Yg62#u#vscgZoT&7zGn&=(kMm4#>LYvf zbb#-U;!RHDhI`rhoAx9hE^suZAtsn(Gg^Pt8z$%jkX8|4Ni|kQ6+{U5F0yZDqKkcm zgtb|>#=;jw}H{0M)FSNZF) z+UMCz22*OJlecbV3qNOrDgH|Z=e8*g%3YxJo<0R*=Tthp8f+W3yAC#AdGUxg^&w3R z1_x0c&R}(qc`8PBXv8}Zgi?FX@Clhy=ca}`$r@9Kj66Oc@B5seS3{drs1S>HARMkw zCt+f?s|dIGt$oef)cUai-Se1rhq)+!x-%Dj0|B4%();#+bk*cQ(ed{hPvE}en81l6 zD0)$?*z4W4i|*)QdnmfDTlEq|8jiJ;$L!ayr0q5iWyG5El99az(T9MPv_ne3zMuc; zz*sfNB&^$k35*fgH{6)9^*AFEbkO8DYwHH7oII`^f8VDWzYWn+|M^M2>t7xcP0r@F4~S& z(6R6y=P#^K6O{h?yh>XcjPcw0Cul|komej-%ZQ}ZAzQg+lKN} z_MG-Q5ZSWzs6>xib(f^uto&HXQkn@~>AeOuH#-YM-MjJr{AVc~fT>)?d_+AwjcIUe z4_lg~P$n7$$V5w+{zneww)O3ue(c`kh*S_0XCDsR# zbF15SSPw73PihJ-Dt!M(T~akKph?s3Y`cD3-i#E&irJYeZLR-Z{B=72 zOCve+cG|bA$!0R$G8(n&^DCVvp`D=ZDuto%bfx|F)f);gFHiTtJ+IbD+e6;7-+Sy! zS$H$TFnit5$ky71)W zWhk_Y%@4fJJbT;y#dQ1mUO~95!pXh_cN zM`UDn}>;u zt)(=h@5$KItfr-1>Bup;R_*J{V%v`v7cELo8DvS#H9&-H^8e0SWG;YDslQXCA6tqK zCHLPU6IXFDcdPA{ygc*8s_$de)4%qCti7i=_CR?9m`JPms%Z_F7e!%G>7BHPT2 zEamMvUSIFqx+jnB?AqX~m3b$X61xl?p4&jpKp6PQ+M!wF94|B6OGu{Y?_?PP7M6L&J~x0O}2YS5b0%IZJehlzF(cv85cRe?bYS&7H7Z|~P3u6&)S zJ-&x(pVoE9iMsB}!uI@*qbl%k+kVU^@A%vs0%3LQf_3ZXwGkQ09pvf66M>XD9fl zG#NMF?)wo-Kekuezx_2>VKyOL>da`IF#w13&ar;zIt_kGV6U(9eyb_tlR0~+`c}7a z^pr$+y|B2D^YsicS`JULr$4$e#p}grb-Mic?yEXG7D1f67DO+=Wj622P8g^)UVrqb zKPYK?n8x{Egc(zaDi)y3l!}cI(}wLTckelPye4FRZ98XwSP|*Y7ke~t{p>dk9vrkx0Ad`Yy)$p* zVdL3Sp1=0v${qB!{bOt4M$Eaoe?MJ#HsH{H^*JI`GkpL1VN&?MD#nMl^Lnw%t0(KD z{keDd6OM#J6sl!LN993WR%jps=jzVA^-97V-Kb!{W2fB!!-^j#U4yN!W$n^Tg(Eo@ zI_%fT8imir%Ln0aG|9JYRfJ2oY136Y+wZ=R9zuw>2?|Rt(^sHSwUuNZgI4*p- zOp8q33%p)k&^El$Q0dLFTxO5B$1^zU?DHopdinm$UYKuK()M0s^uE%{IGFq%^vhu@ z87s(~J>wm9-N>W=Zzov|RheJA#Zgg+>h1Tw?`$$&raG5W_}di3;WcerL%F~45hMQk zC(~jYqmy(m|15Ov_LSNVVt_*5^z-UCUsRCHp5a?;zYe!D=n`@ml^%uG7H+XTH@_E( zA+l;eATikY4&JHyDhon?l}6_r4EhZFwq|r9Z`22461uZw6!p9upkZq)UZ;vL`Gk1* zeuw*waj^<>!9QdegN57qRe(QTuR@s;t`jP+(OjH334 z4mI*@^8jdE{A0WPy3|yXu|Zl^n!?%&X#gRPClQux0NwBN!Xo5?L6Js@Oq}8lJqHcl zV}$#A3FW$Sqv_{Qzwxnc@!^W9%=Y62knpv~DcCk*!&wm6BeSr zA}!r3XC+5j!^b%FeouGUl<%ThYvE)Grmd-wsPYqOD{e0R+%d@;X^1WaTLw^s`*LU5 zdFTdiJJGGcrn*-@ngAq2>cnZr1lUEqO2gThqgxN8k2@&*X7AxDrs89Ve@41pGw*K$;2sk74{$ zWK39nWEhcDy<|AFA!B5~Iekn{Dt=f+m&T7O(`4?Vpu|6JX~!*%*5~-gW^jG6lTHfK zd^je}K2>5>C#wtsU#B~Y7~Iy+GJdsvE_L|=+dC9151H=OR`=DH0!BC*HZCSW-x=2e z`2K)$z*T(E7eHJbkxE5vxV>&P4xb2>OWgyfQ5BLKQrc%(YvW^677-SELyS(^JiKb!3 z(0O-^B0N}YsMqM@pN{(n@d z6yZDDGm$HR&kJ7N*qOvdy`Uoqi{qUfV&s{t>!9>*b16#mv zY*y~%G>N#f`6T$=lP)iX1`Rtf)*$*J){vValHUw)5xz*{_k<%(i~EQ(2TD3p!{CT( z1WKC9X85DZacdAt4!g(P1b@*>$ip61-$}q(yTtQMxE!SZ_LlHG$QBY$(_IrSo1EvMfFm3<`@J6o8?oicXFReWjnEjn0=EV+nam zE0Af6X&jmkGh-SFi=;PY0DQBldInc(0lDPAhMotLg*Q66hRwss5OJZSs9exOpiF+O z9Iqsm?@>-aKt{{#o8Y2wO$4&$a?Sg3W&2E&i^BT-l;`@0FWC~`ATBJK`CpFRVP8p?+{oV?=_fI_^|Mc$hrwr}0dvBEUVdDJZ z8_Z|VW(Q4JW7_U+mQv`Se|@#!IjkY$l2PZcK1Ms?fy7? zTS3c1X;D~69~7#>`JH3B7V?qZ&&%j6LeweDLNWsHlR~^w>PrQc3mH=^$j+sW+3;R+ zXDFD@aIR+2zpQCTfz@vsgP`i4pzkQLEoY0{KyFs6seW`#>W5ipVUa!$O2J%{4T}u? zewBH%q8M0M#KQl7TXbJQ0!E!H5+RI^Q_BAo{@&KA(VDCOs9Bdln3>yH)LkBx2DVi~ zvb32r+!1$%2uSxzDFl^`LDFYkLoI^=PUxNA5hV9zPq0N^obk}+xXPaD3I2aP)L4)I zd8qqjD!y{mc;Jt6NQ4`tSp3gK&3O0`9RZiute+EWTf}})&JG9!;Ix}2P&iCcpAn$~ zi^!zX%v)BJ6f^1ysQ_1Zv1anc=Zxhudfa{AHpb5K_H-fLoZxR`wG+0BPGyye&OIvU z56t~Jt3md{oflS8{P9SUO;O=k(h9{NNp_6Qsd=NZ{nF-jE60PrLG?s1{!Kfw2j49Ed+{S@?Mxo( zk?2u2DK#{;%sf#2Z}*0PT*;`1Gy!dArbKEtSK#zG@ z4r@kbxBHdBuYZoH-dtpgJ)k%fA{xFIbPuRpJZIDtb*kjsD!&TUBYN^C=h&Q7Re_$c zWNOG_YLC>EX#X>11c5DBJhP-_v$bI-B2l4flI@G)+D6rg1d7>LHyBui4*)pEP5w6#uYR*UFOT+ThsFnceO@>D%h?q8!V?FO?a7IE z6|R?nMgOc;y+l9RzN9cNN8F7%t(oFb%;4g&eh$yj66mazKm*h;fmzbd;-15ng;IB|iW0kE0NNfCfUzre7gH>$>p0nlwlCY2Nqs_sm0g<)3OuBfXg7#+}MgkC(X z=#{+@S_s$IoTlblW_@J(=Y|mmP=fx) z>n4Ho>H{*lG3v!mqxz+ZKfGt{yNJJd+0tJarbt#-*DbYg8N1NtVt%=9996T264G5QIA-stw|*_J!(f<61shC! zYu?RNKhkO6xBaeknoAkh1{$g7LL3VrS8nIx#r?TVxT6ezfa12@2}6{S0^{}(>fs)p zTMfVPrakN=WIdR7X7tjPfcSrrGV1YI+0sX!xlqRPAQ!pxmjjx(f$)7}G-21&1m)~- zCYLYWp4g-c3A5S>Xwum&0Iv!NuQ||S+sbrNQT}_pxKO%k=F-RV}uSC4fVwovKCzb z5fgE&fd(?G7?!%-HzPJNX1If!8E`CYZGXRT7da$}3>F4tg`3FZ?vFwtEECxjn@VtbtN?0zne4buP|;TH*49j6>5 z9EI&>qXbvwj+mlllRyt#>FuL#+7DNSI8kqZZVIL(rGb^9I+}vyIc*3i#7YTQb($G! z^uPI{b8`=Ivg?216!Sd2*%jAOWfU$xx<0tSPOZDd4BG|8sVsl)_n38}{pmrMJcNOSiXdj)n7a z?r$@a;7j0HM&~ej{Q9$m_P;<7iMbkFbJ^GxA~6`q;@(MR=i@-`0{7Won1J(SYQM-@ zJiTHXkM&IBUn-mY#7zEB)V-sV@CC)n@ItfzYmoSP< zGps-v9w-wV0wCIF`io)TkcwGfKSw@NefNbFyLD{q%oX3N-^bMO5SrZs+X44SFq|c{ zyZK>#Wan2KeW&qlc_|SVdo=Dd*c?RR;b3Uhb9Y6?G$DHvRaHX&CfBFLTV5+lB2tT{ zb04$W^-}io8()E3s@`29uJR`y0!3~DKoA@&g&q`11(o|noI^PSZGDcoW^%9}i$JDd zNU}!!B@B0Cp)4@OctnmBn9QQuivf!nBo~cU6a`JqwSzigPM1;XbPmn==27uO8#YoH zJ3E_0VDv-43V8T_X%o$v8+Yxha#rsRff1%E_t&qh3YG>U(AqnqDWP|*1c z3T^;-+#LlK+f0X}ob++i&h=xZqA04$ywm1vA?5tSoUbV&tz}GL24&6>uE0=NBa5M?N>{-{<72X+a9b&g9YZCL&k_!fS3gmOg92P^^zjI{KYV$WCiT@+fJWixkXu#ZNB?W!4`l#PB)a zCPSNQkGZAaHO_(5W{EkYYo*bfOhVlL{K-u9(wT{3q3h$Et$@jMMw_yi>E8lzm7B5x z7W%i^VNTdF;Y64c+&I%{(kJ!!_8gr1XN1KtHYKGUW|pUP>>3iR>JnF$_8P3}^P2OT zs(SDZ8Vz#g5&ue;P6|p7+0l_cP=8CuH?32}#bku&-uD(7nH`3m&ZfLVT(w^u^T6(Ri$uR!SlUa=!c;QuP{1lbR`XNiD zLSMTot`-KJCU97}NdD4qu-uE)V5A^KN~8zA2u6Y~Cxno_4~vR_4ym-KEI>}&ail?G zb^$UcPKU;7az3H`KxrKd`f*f`nS=gr+T23^{DA3#*Zs-o<8wWN2 zhVsdMT=v?`9K%4%6_5WlTSWBCt0B%M1D}^{2r{lC0grI|bqRN8^-n67x!=(rL2x85%D)jseizgl)&$+UdnN!H)Bc4I{IO${uky2;l@KA zRMiJE8Zo|nQ84VC;CvgXmcoiMuTognuMXtY0dmDssGVCQ4ggR=I4Au14Z4b7LBgIE!LZUU#+YQ&y2% zR#IH}0tHg;!qS@{AnghfdyR@(bMA8DrtVx&s(vd%a_>Kg;~*RZ*O4B|1f{ry1cMhB zi?$fq0%LAY-?110i>y%M_iv0;N!|?q+$=ciw3_1Y`N`WzVLs;B6-10EG?}IY)M$D; z!O>6qVx3px)&DQ$2rA(Bswa~F_dxnJKo~XWD~Lz?Ngnfy zY$(UfpYo*@7PV<52d8Slt#1|wi0B(^KSWDP)2MNprED{XQRjU6k^AcpBNg)=5fda*y;tlRv+zLfg zoZ?Q=BE>COv0}j;0uAm2irc?@-|u%alg!R!v)Q|Q_uO;O`@Ax8PsPzW<4@C70Am%^ zTj-~ai^hu+Bme2yv*;G`=!n}IdwDp4k2~VaENJ$&E3_LBTAI@X>5k4?ZZ9+z@7Mu3 zVgLihQl8EDbb@LrXc^yXym=`2^0!xeB)B;PZq{@4$Ypbo7<+sWk7PwItXTZY(+&vw zNzLnsRPX+EER$rdiO`S%i~JV6fjoQ!07mU2AURf58C8~L3Vdm1#ac>F%0lt^1@{*7 zHf{kaDhWRk%NCA20lzw3F-fimk!i{n}ksACE7%6q0JK`5dL+LSLEwcg!+a+fG2VX z2Zaz7SC|OC5-ps#^aE8x$;m_IoitWEy@0iXm|s^^uhqwzrgRI|@DU(Ap#8tD0cQEZ zXqNg&DxZWYz2t6Lou~kuy=SNq8E|0Dm?RQ~6YHxmk`}?Xj4*n-9ip;THLHH3`E8A%$Vg^)%@Cew~IWlO_kqlpsHry27=c3is- zcvfc2;h=DS0hadB;HT>km{q_1K|atD_v1-vLi@Bt);tlD02GJ-3R)r;D=t~#0MEln zcl7lTY(mdLp^#TmMVVN;>dt^pjirDh+JY-OCcRW9dZaS&m!s@ceo{Ef7g+4+Ba-(B zIWvRPrHgAKfdfvV4!;QWZ+to0dpOZ}6g^*mtTC-iAmh}Uh%LGL9w1a9{K*Yq){Qfy zgLkQ@V;TDmkLp0qe_j_4w(@61@lVUHlf0JM#ii$xh}Xeo%p)z4{lD;<|K;@wYq-?| zNHad-q+PpO_&8{ig9zwl7daB--oL~|{6+*%SWObF0D4|`Y3=O41d2g2!ZGxOGXx{N zrK~Jbf5@W)H~L(}d?TjmOI z$xt!`qRVu0+x|sDaBt=*KFJ{?p(T!-mm7H5lVOw9{PkWWSdguNs*U=dHS*JeEQR5J z((Ny(K{T`k8gU;_BkiKbj``#8L8SvB{qgCqgayQ;aWtH}jUr7+Ke`GRNVdo!sh&{B zB?0A63dO*wq1T!Kde}jZLfZr0g%rb&Vb3#75K>yiz1%8Hn;q_f8M9rN#vemJifU0OtKLlJKa6A(fI_Xe; zbuhrF1u0^>gG#?7ZNvrUuW^`Ut3;qjvz1f=C*p%}Ec`$ECL+gPcj}jb2(pGirS1uI z1?t8M?~lEj_hC!RD~&WOWgvL>tyi=-d97El=5%jP|s%l^n#4-+PM5S*$ zsfNt{D;fe4ukxylS{hFZ3RfHz2Z~E4%HFKwY}MM!qQRzqI&J}((VMM2YNKysO_600 z^{nANEGk8&tb|i#)GUCi3%NH?$;*Xsm+fdvw%qqgXUVecCRQMvxA0ev<4NZNSWrTP z&{1T&^51v$uBU5$d}_OGZ9_;A z4B_>`7>sDc=kCQ`#0|w3_sdOP$lIU4H;`^9AKQ$k!zRRR2fo-|hn)-H!=+Rl2UCPT z9176ELYGaG|H;9xW*8}_6Rm`mR&~MHk?17**a>*qKME|^`mK0-@9LHe$Nq6|kCYoB zJn=EW=*@9k=gkfA`QBft4|m+f^uJBv;}9Iq_e#5s=zk?71A+-4)%+^*TZqCRAlJ2` zXYlc_Q%t~RxE@cRsQzl>r<;x1oZ9wv!zF(JED&7-pFM&C8wM@KEp=K{?tU=}tm}2N z7k@e2=1e=m`;hh7JJ?2*P%#3NMkymbAG6>YmoOGhoyQ+hl8t`^yF?$wyd;a-OdobU z$S*0-NyG(^D4zO@9+WlKu1Aa-t0Kk<#1j;NK$bM%NyPAq8<=#uX@W)AoAa&h+$~=w zek&qO0?bwyTR*S4r3lVsE?q=bhmvdZUQcvj>%jdMhuwYEP+)ULuy#Bt_)6WqER)&) z5)HU2|5r3{@D640-k{{m{8CRr{^`iQqtoYC`z?hnrR!gA@m`Gjs$nXiOEFPhbQk>K z{^Ex|oDk~v&r=~5IY}BQ_1~HZLn#*|VhSyfAjLO{m45{Y<1K5g7=t4%`v{2->qlHY zWT~{Z^@Ui7AJ_`;WFh3;Z(P{FyH=0;JepJWUk{-O5#k6=BTI<$e^nQd_}+;G(Wi5E z-%k;Dr3>t`2v$Hklm4})(n}plK&Ow1+>@Dx%J`I*?+Zc0)raI?d%U=Azc=vxTu7HS zGoG^AEzR(=HSy>bHR?$PTiu#?cpRA)V_K-0#`GX{qIuW4E;_bEcHw2>&p{uPkqN)k zp&g=^AQ`;?Dwlsc5y1l?Xp_5{1E5F!y9EvIfu0TToeaQk00`Zr|1aYW4cVT8GFspgn;bG4wmY-t#&Yj}C(FBf3%I`z4%@EG{YQ>h{df3y8hUMF zd=SVI@MoR{E@nsC$2d#1$8sT{6rn2Wiul?X#ePv)$Z-KyQpdX^W7ho zG{yI;HXK<@r_8dZ5El0ZKG}7t1*oKIul+ND#8tB-h2`g~a#{dZ85;UwIBzea$sxNl z=XxJGv9mV_>1=8E;USnt5kS9B+cTe0$92vhr>f3V$0>fJ{Z=6yT`-L0f0WJRW&J_m z0w;~)d7sv)Z50ZUwANhXwAbnbvFAj2og*je1d`RWTqlL^os`_@8&!U$-sp-n4zJiy z|F_dadlGsEnhHg5qhXX@2G-3+W`h?ko63|r=}4S6l9GedXJPG_&o^I=zwI7xS} zhjcX2Dr1!Huv>g()``2>cAZ@8rRRFHw!f^Gn}09f-4%qi+Rs{Oe)bGvAc`3M7(Ry%V_ayCQfURY1X*y5{;x3iH)Y(>jEx18W2M6>~5VOHmfTj zZ%Xo-**g&p3#&J}rm;<`&i>{XUnU{O#$z4U>}iiqULO&$LpeEhCOL}>%evOe<=n#* zdRbcW`lguqKzP$3(j3?6X7UqEu;y-H&YT87d;jpaXS!&(- z8S67RY=w;L?qnY$7QiH?th*9(il-bVF=1qw{{1?F`1PM+^#LDil&_w)}7 z@k{a8hh=ls_U|G{QSk2^ho=SVqA8o)5!rfHjaDgMu1)T?8jh%Q=e$vZoA_6D{8BmH z%)-d-VhKf7#CxFDn-#kB<3l+KO<22f}~@M(z(fDLnR7>H(PVc zT&1}>oBKc9F5AhfL~ZzKJVyVdK!2YJmkYR~x{~kvsw41)hN}I4DxqZiWWL@8E~jQN z0OOCJ(!mLhE)UkNSO_KbNBSidY>13$x7A&0`xAd0mv&x!L1a5Cb6w(_&(&rX#e?LR z(~OT)C7*h;lhX$h5o0!($cbad3>RUTW~eiHign(C1&dT~%jEco*WM<1^V zmfFv=SMJC18e9LwF45O$Zf7MWkkL~|Bs+^@qA;SluYfF~fTixk_bad0P6lBSZ8LBf*qNhsZ611FRDY3P!PC>`DKfPU)q6S6LhCq_ z$!>eYpG9RcaD*x!ZSnA7HdVv8(#&Ik=e857!TKvr(1U5;)=~Z^ak^jD~$TDQhZbz;>f6%sd57SheEYooeMk;u43q3>^YlA`fa_{pVyW= zN1h%b`s&TzHsDjUfzW`Se66?H5<@KaxR|VaMW!i?uZe-WyIit13Q}?8f+D^DZsbJA z28Bzwekr=>h(+WNOOJ+tfN&ySn(GFnF%qCZUSwvzmyA3os6 zM3Eu(Z<9Ru2*IQ!`EXxwPt>KiQLoyn#glMCB`WxK4JD@DH0;dI^IFaA;-R~pcR(23 ze!QI47&Aa8;#;}u#k!*P-{4f=tkrkReYji(4@YoUr{knwcw4!=4KDV95x(#iH1f25 z>LgC8ZTjO2@l7Px_i^Lg#I&rIgZPvAKlbVsZa&%Cj9YvtnP>UFk>(E>@s-)N;tkl( z7UR!0SAdNyT6NyiOF2;A%LRMP;53~7+w}f=$Xh4CKLr1DN!&vM)Sy$zj%e^#bgT{-U5dQFSDiUX{z^gp{Z|7v+ zTnB;>dqef=6g)0+lwU1a`iWyob?)p^JVt}#@f22M8ML_BaW?)UuNI#2c(Xe98PF5E z(Z+XH6#^ULFH`-n)>r}nTji?ZOfRsB>-XrA)BKgiGcP~5Nr&GJ*+XmWP_)`j=f177 z-YDPUT2eR=MBgf?JOa`~onRvQ2nKbGMeE%ou;|Ga-$<5yJ+C(XR^N!3>Q zctqT*S z**|p!6$xK#N~OJ~f@8$`??vbH`bYQMCSPBg7d%%t+}NkOv&Q<iE$Gytt zrpJ-2wkq^cjUc8ED*O{cs1rs9W^b zK?1tSuLN)_%i%l+g6EUTCqN=iqGff}5KaND%9&iLK}6~Ro^aI0Gg{CCr|TGuzu-zO zY-ZCmQ4ycrFXeDw7oo$KsgI}X1@n+tZEr#xmVPqRx79!WI3Ld{9>x65LQ50;`ucrn zO)a6!s}SG0wzjdAz{8=4=90=5md>B+cj4vdR)0pRbXELHj+U`HJ1DadgPhs+28|s$ zdFMjapobVDy%9gJZoSSKy@qVbhio+Gu@>I#lBveA#~r)iqQ_a6ZtkX>`|O->0juxe z)!RKanU#$&C0&#a<@wVbJ!cxz^Q1XaRn{sn*py7@jU=JxveD^bkY?!OilIbl4`{)mMhlRadD;qW1t79+i zx_x$#t;vfpLbEGftyL57-+oYpY}__tL;kzzW2o1mKdXhDlZwB;PH zHWLS%vCdyFS8d%8kDuiuwnvv~*bICdmr-dJbnh;lANe|60tSoqXpMXmxW%}2<}TER zcYfXwRgxG=@COuTH<@^fT@!S153x5sSj~moJHAXFbFSbN zR6lNuV~!{bynJtDs4KaBT&>zDzkTd=#gNt_($aknpv*U)ksVQ)ZekBr1nBOgv3Fjy zK=r&-gzNH4Fmb*9)u*at4&{_da?)I`wbT88&g)JDKw(wUH!F@FDcN9kHj+2cr8KdZcoCaq z!~(!lf_rfI@9_6uEna+D_38%fLo9bYDz(fXca|-*UHIeV4N z_plolJ|giTDx=%+msv=^**8W)q(>%x$Lp->@^_fdq-e}Y zxt@W(E~N&1>l4HJb)`tEw!Z*IpP$qrZ)%Y4d#83g6TpCQ!nbmaJofDCeCF~vyUjKK zD~IG}z_15my_eE9w(a#j09#F8%u8NwY5i6lf5dyeRp)2*dmGk_n^RwXF`vDoM*bkj ze80Wb=WWn$Kj|;rYm+#^L$rux?f2b&ru@Ol`z8|>E@#l8#?xb=?jC@@0Xc3!g6Ieb zEWc`TIivb6SH0fCqsEnXH4#TH6qF}%J~%*-cl&J{_9kon3sit8Xg}qCS1orN+{vNO ze`qNDc>Qv+eNBLL1Tg#x^{UHRY8twlO++?~J9qMspQqtB1X^{o zhg^rscB95!gb`Rj4>60BCp<$33LQ575&w;KtW#n?Qsds1aBb5cpzgZWX}DcQLSu95 z_WmV8!8*1gCm^83_CYt4Z}*EeO$$Y^?Jt@)B0RzFCaB*IIlm)Gvd%)xyB_B1&+ zvKON)^Z#UospgK!`-uc^~yY6f#Is78Un~e%aXaT%7FL z=C9oZBOTr8#=+uEo~FExf2*nF6=_CdKano@3OBP^R(a^Wn@uFky zFJG|OE~1MnQc>a8#ANoOs;oB!ruHm{dwak?6dCsAlc)}f-+ z`a++lx*tb2Y(nQK*RJeUMAyD%hyoO4pdD2ZGHQ*?IAfp_Wl!f(b24Ly1X4WeYMbj& z(V{(fwrei?DWvh~VoQa@I0DU8Onv&|cOjC>lqdXI*oTNE6Ea2+k?t%92kKW*HZIm2`~y7CKzfGXN+fD(h=a^U?OGNZ57@TF z9_NUK;ca@1;?}!YdE^kXwTwy1n3xHp@sG!Uvvmc}FKN)L0`vYBm!UM#3~!`4`Az3H z4I~g7n^3!IG?9I+I$Hg8wRcF+hUn`F7+;*3Ru8h(aW%H}8%q}gBV}!w-08{yP1?9; zIl`(SL~A*}-?FVZh$b#Q&N?ClmjKrpR+vH^p7lajJ+AbUG)|vR;~PEq+dYPL>5axv z_Thz-^Vaaz@bFgb++4+K@6Sp0_MW)MfmJ@v_DkTd(J)I(&Q*U)OS$>(xGN&{`4XGm zB&~6}yVICrwz-psDP!Z^e5JvSKfn79Umo>U2mbss(}+Uzh(H4?Eg2NgYQs#Y@mMd% zcJfLNzlYyYP}w&LCOq}ijlr6?J#I2G%oOoZIh@LE+TnjaJ5|-ix6cbxf2sU0Gb2Mx z9t=)FHu08Uj#+h?H}Sa=&Hd=P--8&QK#y6AS)3}*YVbRv(ENY8Gm zNL_J5;OoXAj&7ff;+#c{*bblHT^?!nt~r-TG!zj?e?`e`a%Mx07crG?7xHv+Y&_@i zD}?w%YX$6QD_ZuBD{Ma@cNNfD&zc*(lny>b>Xd(#YSdsgnjxU~y>?fMNywwV?s`_K zN~{nwcG&;ZnakY*k!?K<;;1Lc~=gx` zk9?Lea!5K@s0va>&Y+N{T`(Nke5j`CC~hw|7hOfd-rA@g6lap47g)1iagp8kS6xfz z3>odnr~UQxp^ahZT5TEl0$KWs{m|xTGtCGV73H?TqPD3+Hq+&*0<*kR)kino&*sBL z4P%Z@uo^70nYpp@s`ArO(^W04p6XA~hYmN_!6H#lCr(6(V4nPjw?}1*P>wjYUM2FC zd!D)c?KcYkt+Cw4VcVB=esizR&&<>l&W64e_B}d9`mGKSJ<9V#%a_jk!>WV!Geilm zs;{btFhsrPgb19C8a_A91Pk<-_Cu?hh2N$)!XVA0~j9qnGA=na*=#}d=9AZ zk!IWizuNLbvPFq0M2hi)l%>%Nc}_KEYTOTdE#cbr$06x9}qS zNhdyW=lv%he)TN!zpNUled#Zrl}6)7mg~vLN@cTsjgKrSUEFvP@P6Z^fGY`iwNVGe zt$(>`iXDW@t;Z$S$Njh#Zd&&7!Cjq3#Lvh0zQFSGL1XjVBuc54Zo5cz`7M7`^WS^9 zKGD<73)r&3Q{<&l2U;f(a8-hnOi~Op*mJwKJq*1C7??1Q6`!P==uRyHAX<$PAp}qptofve3 zWd?0SvN_cxm}r;WeYxZrRI7r%ZiUVP6@4}i5mbH8jLH$)B=Vsb#pInSYjYqyIEliZ zI225zrK*WYd{}?pa;$b61@&CTDqWy$wjG6H{8mAb0b^X(v#Xhp3q&qL{h#7xW^NW$29vI9X6aQbztoZj z2R&9~;+s(#neysAJ~s=Enp+XK?EfM$5j1gkh)g=j?k)AsT%O*$s(61H|9oUwN2Bgi z#C)9=IdULX+p*&ieZ)upI{#GMg;^1E-WaR&@i3Q!16jo`W?cC2kueTD-M}w8r5t{? zDj9SJFTpe;ZqyYRMcrY&J;o*!Go~$fwCnpo>A25AytZ`Ow%URmssg5j`^uIh--Lh9 zxipw6<392i>Ke&@oYluXXmOuCJ`~m8zi8^%rhQHUyO?jIF|VO^_}fEpj~S_Kz-vK- z?S%SfZ9SPf2iC&(;@ieWxjtXa`{g<$8-))PS3lA+zF3l>@g&1Q0YvULr*{sW@!5<8 zi@*AQSPE<6ASR(gmsJlI2)l4%6T!mrLubFvchJkg`C#hX=zOaKvj?)kO=HWLy2Y0!Yx~TmMdvo7wfPAE1T4pFwp+j;Uu+!u!$Xzxa_n z(6b^!$~Y-%<8j|SHab+bAD(eM=maALUEHd;ccwGewp&}b%pYJU5p)@KoqadOA2+ne zIRJE!HfvDF+nvZxgARPLS)UcjK)$V@lS@O=L)2d(pIvchb9k$L7UCYSWOQ#PMi|p_ zKami~Us~iuC@f}d?|(VYj=ewnEW~5B;D;>+u~Td@9Sg49CgV`(hS zqm1+d5oUQy$z`$Tz3`P$K$7eGgf|^#!61XoLL(;!+F{jTsa80W+m0nJ(%XnuGd`)h z7=0v?adSxMpgwj!Wc5CWvRAh{EUr^+qBJYV4s}t-(Zhyc{+qcerN$@|V5BOD?#18F zHNO|5Twa4eCZ`@>adcC6U1KkO@ah(5Te>@5`?BgJNxN3XUR8=-3aPGOqy8E*e#HQH zi69dXq>;v4)@|Rc*Lh!x$`=~s*&FwX=<>V~=Qs8Rb2Ac#XUca+@O4u@+rc!(YO8p^ zF13xQHy)DYA~Kr`BJrFA4K`X@Z2;VsjzY+krsPF`q(gW>eh=K9^w2#UAHeb&t$k#?b}aC04Vm!R^nb z=-{@TCNiWP&K=*!U8E+bcSCuT8nc{Re+O4m?+*-c%Be~^8kVK0y~$}&OP^&;U>Z#= z@sYFJcA(;W9dOul>}8j}LNyd$8#|qYgNB%TyJ;ZZW{dY&mUfkDlYK> zBi~D3KkD#5`q7^zqb*3VWnH>M6(W=NZ3)~>IA9Uqk?i2vdi=Pcf^HXy*RxdbMrGUe zoo#-|)vOb&hp9a0g|(+OGjK>Yw-sJFWkn5qw9}A$?8D`6OL4LBy|om=14@ZxkBoJ? z5p%5??2rCk5;Q0A%QY3b4`Mu}^LMn{AFr{UO{NCR9!)hA&FwGUp;H=Kh(Gf7^LI%t zQkYe%{aRBcB`Zm{9}il8CB-?2JD(kM=C6lPr6W5qe54PAVk-r@22ALAS?dmT<_PLp z`M0(|obWr7PCq=$dDxNkH1Soz$kxKEADfpMN*Sy@Z#I)@R5r<410j<-Z$^@xA-eT@ z26%-OBkACZ%v6Hh&`fM(6s-3+gmA1lezN!S_3MP34K^*L_odO44~m}3gORY85#Fy9 z&aFF}B5q?7fYS({`SeJ=ezIUblMyH6g00{ zo8x)N&Y=4PjY7ZFTYPOD6c7itQRw+>hGPRZFz7esw*IvGFg7}<=y@%Hv;3_saI&Vz z$^G+3E&1JtOk|r}UN8B3?($)pqlu!R=&1Xth~c-bYK}Ruhxq~*mV*d6QNB#X=rPwM z3iqP*BXJ|LLLsMLJEXF{1yMis_EMa?@zd6aMSPVSts=+%`!Yl3;aT(7jRSupH~J8- z%F(d5c(i1q$WY-t&fOuOqJ4CuM7$jsx9xRBX zg)@Z+R4t(Nj3n5L5THkY`eSf5*YU$bN$;uY0Ixe!;BAh(trcjbDU}?O-pUC6>Z{b; z^0dhoEz7_7g1vvQE=hH!M>IIEjEpajnouq~)isRAl3s#M{otf5EcHGB3aMpg70fo-lg`^DhQB{Y8BPn=ZAz=@oGnE6v`l)~z0;##>CJcJ`k8 z-M8sp-vNRp-kO)nbQt)QLh3qB#}g&P%#PMl{S4|0&nU#}PBe%G^kr*le2#bo&KuDC z8M54xM5ck=_5AO!(Y`ya7;M3X_USNDH|owIc1 z1a1u4c`sFAV>jW&4b{fI{NC>jU}V=II&}Sq)dv!rxvkcZ4`V$oUN0H6_GtqHbF%bK zIxp$SAC$6!c+=0XPP8tKZ>G+ft_<*~WkbWON@(u&B#gXaOp2A(rT{}TuB~&=4m!)n zJ3znqwo0Bu$1pC9{7;K_#i%!5H2j%x9OJ9@EQ}bGLAY4CH?OZbC45(fZ=l@lUgkb} zvu}{)!9{ELa}R;@YHe6|iY12VW6|0?$cS(3qyDkW3M?`V*+O&wA%A4z_4);D+(Y8J zxDWqTZ}u(N5B)Ci)D?QkO=;dF*;sm;i+_2q8kDe6MR7m3j?@%4c42;zLjS|g-e|88 z3MSG?wrIo~rdA~c^VcwM+oXrSV`j|fP8JEn4z}qWp8!3Ad|anxf3rsGRkO7pUG)`u z8Qx#SBewHz=Kk=0h#}Sq zn0*jtR)9F%f0X_oarAT6$h6Y3G@yd{|5#Il3%N!)N@bbA3MM1jJDOm@42CWQSk(m? zjU)@@OJ1okhH4g1{SGx7&IqB3v3+deY}aO2jojb{rV%ui{KssWVYq}wBJwwu<=Mou z%57meCHB_og4oKP$>#1W#7hzGK4A7=r1FX&_cs^`XHV zeP0+v$nLL*Z@KBuygl&TsmThce_6UXXz(#;CPI$3P~DTW=6t`(<{Bi|GZ4wu*E6(` z4Kha?K%!r|SW=66^zvm(;Y7Yho~X$8&Sm6Q{lI(C5_e3krvL9asVSh5p;F(SYkL_) z)H&

    JR=1a8ecAycA{qS36mbo0Q)b--BF_S}XEjMaNVq`~j~kDSIBRza5~C9K0`2 zE(lL51il83(p;nGzhOSQ_B)nWOd-f$#pu;{dsM3X8q1nO7RQ9Ae?LIFPP?xBP4p6x zZ7#^0UdUMqi0Uf64`kmn4=A#^eXCxyAbwd&E=)8H&zcG+z(;3$0odwNX(nAEcz-Z6 z_VDfES`vP*A+dZG`Sw}CBH$GNIm~`QCH4nt4x$G`yHMAw}sypZ1g3`;1B_I=d9=IU5H`q$H^1}VuwIeJbrZ9OuDk?)4$H$d*ViKH47C|hKxq~D_fJ<&i&b1a&chzq~)XPLa zg63i;JWS00i~I%HS*H<;Gh?^`2-)4hynnF9t`VTn%4YeF zz{fDTgr~bOzF(2&UxZ_-;$pH1pNF^P#gh%h{b7wp`gTSwub2!GPFCEwfgI!+ESJ+mu5oF##WDa6o|pv~PJk4cpb$_^$!gwn$Dqa6?7_#|4OZ zz^^#p@~X=)kw+@HSTWv9gqNj#;E@?8cT0PB(CYOfGT)nDil5BTr$()Q8Jwa^Od*F* zy2YNuBc3qY7A^*kB41V$Jh&4N5q2xUyk_d$uu$A`*MrB<$9*`hdi92moN{_P{Q>@%RuXA*R~Hh}-wB*C(L%0+^SjrTrLd55zjB1bvr1Td$9suB zPey+$B4@5t(ADTbyy1X(_lnHJW57aO;W6pCHxXv9fR8Xtun-N!VzalovkfSk0}AdT z^^&YL3{5+OUi#&Sp-YXviTtbZPN9>)E*#i|O~}AA2@eJhkk&xb$XH@oCH<4`5>4HH zK`0LH@KHjdz9@A0B#Jwn>K1G;buiEqG58dbfEfF-A}qkFg3G{n&fuLa)zUb}A-3ad5Nrk-zDPFDNg6}snKIB`h!Y# zJ^5gv`^Q^A(aPW@(lp#4Ng1<(n=w2R3ws{(#Arb%umKQG z`rd0i-Ke54cQFITb{ij8Bk&96p9;DYQE8a0<>A0^+h6y~d$2MMOwYp+`z@}I*c}lz z3ktW4wj^ZYjt#RZeBJMqj>ujwjRg7ddKE095+?8xC5>KTPDB+T$Z&C3ib3eTR&FmI zU!ssDUIz85#g6s#<1w%x6qU0meO$lx={qb((Jq)ar?}<-JK5;V@4M<#pa&9N1|AEt zCB;_E1uyV1(kL>PH{C$YaM>9b#<~Ar^tI7PNZNBnEV}o6jt}ZS0=@@!>L5D^ZULN# zm|KPK{lpG2c#s6-saXjhReB=_(6Z=3hyrxJv;F~@$h4k6;3VylZz&@5yiv+o25J^S z;(CzdgY>a`5M!Kl-*y&30!RK8PO@#MK3{>!|MMK>S1X8N=Y?{*hcNIz=DCdS%o=cV z=TI{YT$QW5*FY%;xE1Hm&FJyI;Qhy-kZCKwrfTwUw0l8_NR|uTU_k-8XxyT=0k+8u}m1iIm>T1xIN+b$cq(=%>8U@OziiOVw zrNap+jcO4VBkDq9`WHT(MUU7+W_bbL?1O<4HF0)Mx%|U{xjGr0@H8 z?0cAziMsy^Uz=cOrTrm!Kw~4#>1Ul;L{j3kgU+oplBN?d5E)G8(LL+NP7?~IgOy$7 z1qWlZ^@cuY4vCgG=OtrF7LrowWzTV703OaJ2gc#-za1Xgrz#4ssysycQ#<`a2EHK$ zfRJAKRiekH2OHNF+CO7EHx_=O)Z1?3!nL^C+V}a z_w*)Gl*b~0^ILqR?H_!o5*GW1uoVv~<}P&?e+R#5^w`BUql}RREgS?hg;>+QY+?u%S#em}WFEtE} zRNdz+M3gX;8`4_maWsd|XAvt{H(0DZpE3we()y2@O-kh%U2va-k97(sYDx>O(y@yY zGDmFv7#Jmb^C|fsQ+!o)e{ACS4!(;${Nd;0Y0_b5>B2vp*KQmlmoX(mjt1qTxy$vo zyHMMXvIz>)D_5_X{MuUi?L$%jhh@Fn%|FJg*PCB5*jm;^~L#JJU zIkj#cbl-bRGkEUHxXsD$Dmw{I$Dj^>vHt4Pr$%UlcGayOcnLgf_?54Gz{EZm0AA;R&^l?+a2{zwQ z*!>;bqN7t;sjH(SSY#9McQcN>$q9$V5BuGx!Q`@8od@cFTf%LXwVtr#GLlTa_F4#J zyCPN0sP^*~>{pBRAL>sAeaSJ^KZ}gw20a91D&@7(>p_|vF~AV%T_B@D)cf%ukj!2B zjj%iA8wtY9uf4LNrR9I&)QFH^A_5{3ZJ9ehD?-bV$mSZN&<&%JfYctD!|Sq%JbMIcroqq{qdoM)`nW!p#wjXI~L+;K8j8DNXEf#YdtsA`TXSjabxsh zv5>sMh;Q=wH#KXJUh{rmb&q_$ut&uRmVo3+TRH4i#{5@G|#Ild3<2gY?$7e z`ogyaC-LVkPU-8!>vbe8#GH&B%D8YdSyqm%F^YgW%nPBCwiY4~SYYyXf0rhSoWq)j!>O z%K2euKhj-iL`yoimq}6~$I?7B2^7bsK{JN5xfl5ZypMZJRuyc9;+Fz$D|{JejWiTM z%vnUnSxF8^D8-^xsmb$Px6E({y_h32un{%7WZQ*P@mTqkv^jj%lrqKd5BO^#!jlC% z-o%o%oi2w6u%4vE&r)lzzJkdCu7|}BCv->hESz>As|f2&ex0IfbZa=4>P77MxT|(4v5Zh>xbP5c zf4E9nl#3`)<9xuwc@4YC9hUjK^?rGDjPrgW2Vcx%6XKO~(_k9Zj+|Y5+mM z)iLYC8^-!kj-@nOQ49oBw&P5@AeCN(^RR?xqyZE0PX$86S!K2di(?Gs$^;s!TpB>y zLDb{Ctq+%@r#cgEaFMO^d&TD_}A^(~j3OLtUy*t0Z zS^n3$J$A6T#4T@PXxti9m7{n2rAQ5mjp0Z0?l7_P+^^>dYEQYhWMAG%>6~32RpT~m zh@BE}+cNio6mlfwcsEx4(6?+Ws(XLP9S9qo@%sQXslQ{#&AqbhUM3TIH+m;-*|x>q zVGWJRKHjggZ@I1;yNB1Ga_!oU=nRvWJ{NJ=hj>p$H9Hg(bcdkGn9KWHc|)+C_r?RV z*%!|TL$$bQ=)QJMh0}hR7DlAe=Rdl$YVBmO4ZSEr_>SViySumh(#9csPo2DWWAmL?Zv91 zWKCvxNSkY*Occ%09_TawG^Y1Xn%@;7sClBfr2?c?hO)RI@JJ=pGJJ8`dRWz={y+Z`2GP}9S#wE5d9;k^7FTm>exKC# zaw@bolW;6&>z)L6yY7hiGUL0>!P`r7q4TKuYf*>SFSY9)vJJ@NWX?=K>c0)pQpxPZ z2P*3)=8CRT48{Bxe4ZyclY^j--UdG-kb*o|Mvd`?C{+z@BD;UzE5AbWg8NVA$yCTI z&G9Mhr2u@!%LZWfMIsPej!NiKO2~sig^)4joMJ@Cd<&+R2xb9RSQyA_$O{?6*9jHb z-87?|-pH-Reu{D#?i@RJw`wmj-C@&%)q2df^VI**Ios&U_i6r->$J;$xE*x(Q&RjF zY)g4%np<9?;jmNJqg3lH*VV@!0oS1WLM1P#L#}zsD%W74mwp8_RMZq_u-XvM*R3Lt9Y%3Lj-8wbpZ+U4#B2=X79N$6tWSU=_)jG;Kj@ zjhaZ0$XHJNA9bCHJJetJ_w7ptBg@E6c7`TPBSM&zC4?~6kTo%6mpz7I5Lw5PJ?mGt z$r56cEh!|~8ZokOgKRyYzTfA0uIst3-}CtcX3n|J=X1_|-uLUi--EFoiCOSdLn1VC z4lJ%N&mx-{YH}LH5rKcT-&TStWA8fJUw@xy`p1AB^@nVWJ@agYsNSEUiTBe=8 z|2L(uUo4p7=n}Ik5!WPgt)RKA>5h%az0T^A(gi=h{KYX5&vN&H8CvkLNzDuWE{c#XsKl5>9t4LGllcFWAV-kbsp*j9D*ZjTh zX3ytUO3{fWE9`q6@&yyqMe85K64W7nPxM{n(_Rt$4%Pf7Zp11!Pi8>b$AMtU3EI{q za#;wtzX?@cBr&j1g6bU-0~2qH>uhre5yn9XFsDz?9h{yjV{k>ZGbdD@MeMm9MI*eOOVtQA4jHkf%q!C`G=c$Cgir<)4op%w-e5X26? zA{KC})vbuVr6N+w5a&q6W#$;@v@9*e{S7gK(M~9Z(0;gh9XXC8!SNqDb$_}MKVvG? zZ)&F=oii_-zJ9&Up*wl(Hin-Yh?=&)mDXjmulKxYyB)FTH5%;ZYYx-BeJ&n*b+`cA zzO>QYxDF+r!!z}KLEX@`@V4`r{PtX=K%AZ%O-4IzYNmeP@6cZHG9^tG1^$%OtjHT$& zV&I+=#hL*IVm%mICY~!{_tQGE^-;?e5u{lugl}F6bHQSBw+{mO(cUKZN7mW0cm9u; zqy_7Wa8(3WZ5mnox#?z~XwgU>gjR*aD6cj4tPFF`A}G-LE)-sW!9y)5>0`^!V2$u` zc*W`CRzw!34dYXl01hsy^G}6!b+vWDG`V@#Ce#vW+gN?JS!lKA zzk+-Q!!BLbJ-SovSvmEBE%>B|?DtMpkXG*7{aasN-m=`wx7s?R%2;O^Vo`g-+x5W2We#g zn*^DUqrBCAvu+wm^8)wy#-f&J?TSR=%6nr#<*)#E8|WaQb}PsZ8f_a2gIM@4(c4$R zQ9?YJm;a}+%@82z_{!0pZw^1L2(NW=+Zha$Q*FAIEEzyldH;2t*{DV((Eb-d{)c>g zDVNhg;@vuFjH8aPDgfpta8{9JWaXeCd|5s00B0f72Gj{Dc!xYRDY4#Rh(Gr$;a_#% z=KUzYDV_PTQQG1w6xUMwV|-U5Qom)g>k?8`@K;bC*{pLXYd*sOo7LseC^vIYnQSB= zmDg~#^*DZSx^3BZr!H$+WBTgT)!oA`vydIylWV`X2fkOndYm;KKU`GW_NeDep@-X9 zzf5Tk=|Egt!3baLH+Ho|0=bIRx8rjkyE*?zA$4ER1O11`zI1`8diortEC*sBgf=}T z05b2c)RPvQ_vVM{oL*&9uwcx#jFEJ9e*7>V);~RG}GJepPf%lXD4mdOQBz4 zGSn5GVCmPYwJc^YcNTa09-nGKnU$L}jK|JWJjzt1THm)|V^sa`3iC;-TYA{G-~Ew3 zZ*1G_U*7U`UHG7@)|$2T>@d>+t9A$qXzG*{qeql-EX{^Kjm9IoAe+4PzG$9${(N(o)Bd`#x08~+}$QaZZ#O@xKC zjK8W{VD^a7uN+~q2XW)HjB!=8U@BdX#`WT5JLij-Cdw())?2aQp7qtTl8)j>gEcR8$}POSW&z`OjhzLJjHcF2>E_k<@7+f{ zVUq@orW-Z0pPp3n%g&mAo)gfkkel=WL&&xlw3`i|wvVpQo1Eer{OauMJovC8H zUKLNz9<8un)1sWV9wy?oZdM59ovNXn;A9AWTwtb}lAyMwH$#1#Cc_1^qa8Cky*Cf? zLHv1WT7n30aJ@nhi6DJ}m!M0qBMR=ghiyIDE1cBX{LtvFAykoD0ve;Tx}Y+rDx|A# z?MXXjT0C81DtU)P0P*9IFL)x{-K&j1Qmsk;ia7Y$woDja(4TIohqxzf5MOr5sg(x zV_S1Vl5NLZ?U3e?xy2uw3XpG7=HDZndfi>UH)1tp>~^-HJ2naQ;Xa!pYmcjI+S^mj zn(uy@>~9q?(i#KyDR)Auc?OlB{V)h0bbm|DQD`~rYA2xcIHG^ZvTLCsbn5JbnBI#{ zvM)Xivv$7ydD2cHvuV`FAV=+v`vW}Wv%NgxyN4uWjj$TOt)8??#ne=v@XgiTC^u=Y4 zxqG8Mz+6eob>rm;er@Uf@m;+DVT&+vwyodc4F$i0Va&MjTc{KL(Vkq_)ta>2^2~C& zzJ*EM^41rC2lFf|e4+J1S?NPTdQas?pNe{Z(2wk_nwNbgFMUWe=dGCxf<68|Q6wLz zBq6-8zsCZZb=t2dqtMPRPhr77!u3v3?0V zlNUda?HFkKKr|>jf(~8WKipRv9u)&gM_tXIIA?nDGfcn)pGUs+(8pCzHvHnw*J~Xn zK1D5$R+FmnMJ1m44A#pnEplRsVJ>oydTk=>h~-kM4u;xOPb!q4c}4sB1j6XN_mK6)4g3Zy#&t~@ir)zoI5~`I5AdI`+4oKv_Vy?| zIZU|`_VU(PLq&&kB0!N*xzB$chK_2E&Oz*ED%JvVyNHEH-^)gf&U&5vC-}oJ{ZsPH zs+nx~hfELEUgjRGWoS%CU#I`!;tV!=%hoRvsO4keAs#Oo! z=U*RCyK-1`Oj5cwb7gAq9_!eJ_Gdr~!?<7WJKP!DjUPiyf8Jj2`S`VNO2g}GRMrtO z>hosV{0hw4UbX${?=K&@vee&fkIO5zL}&xv>U1M8te?gv(p6{RTZq~%Y{}&d>E=m! zs0(Ask(J6;0I^4_c?C6BV)s0uzK7PYA^zTYsF~K)jEm_GaHhzFkWbE9SFfOYaL+oX z?1580Mf-H%kYei0|L8geKS8E23WyCenyJ^%c@_Y(!84tP&1+Ls?}4(I%Jyef|Ah8> ztAl)oluWBa7H$b3`88IC`8O1HUi?$MQVu6cR$fEJEZyEBMzeYIl)WM^5ZRgw;3B}F zp2z}`=ox}mDHUs3r{WVVa}Y=~jzPh0vo*Imz(8 zKx!eBarM!vs^^iqrtPbZphF1JjXCUf?}q#?Q|&wzM}fU`iuT6-=T>b);Mvie?RZ1i z?-BVe#r%e9EstAyZ6oN|x9O(ePoy;9>;8E%#AgSM6wLY$+biXSc1lViayp8Kq^}dk zX+Zx9yC$aE=on}^m>A6y&2w9G(VTh`axdA*ZSfGS{)9nch%<4&zT7`XUSW)Jo=V1`r?V~ z?Lpa!7FAF7>aS(Qi z;QuY|9yzYuU{oAlq~gJ;xH7zyj}3-s=KmlXTBp8Ejf|VJf<+<3V{f~>SzNBGbJu4b zfAJ4h$Ko5Zg0!@CIsep*&us5NJ;xQ}nh-A!XxO(bxyLlE=+<$s89Zw7ee%nMkqGJ( z+xGD-%aEcv=l7p2-=gL&951=WkQlBc{WqqjBRG&KPQBlR!-Tv}kdw#aEhBTYvuRD3 zZW_esstK_2^z|iizZ<_yq)dqxF7LsSnJxh|fgzYpL%44jFVRVI-P1Vt`l zCl%BmnJw||Z9rMi{T^h|713*Gs+Bza(p2255I8QUqs_#8&r$`l@aCB}&h%~1pYY&A z#f<{w?%Oi=FxKm$FH z6@e<+E$mGT#5EfoaPU?l+`n z$sJH+%j z=_tCyanq2ZtF6|ZGANt=mG4ilZuDeG5~8`OIMp+ajjaL3-dV~umLZY}yytM3mZsSV zz5c6^ZTll;>7!ml{qzSa z9~%zKLuc!9%eXaJsR{2wS~6|FDZ9o&@Z}Ge-V1=uo80bakiR7P+XF z4zd;5V{`YADa~$9lGA8(Qodv^vI(FCfS|~5j&XyE(wMox0Ugx%v$IAHOhwb2%J^U> z`3;Yd<|l9%E_mvnzoG7WW^iS46(=~!v)r3e(d^zEF%)N%~EArHfY37tvBj?tP{-sZ2GQbVw+IpAqPRX>tk(b9}NlR&l9>N&A_H0wp@h0TtNp^Yj5`7^>ShH-a(Kxw5$F3FII0=HLvh7{e=j#ZwI?cMz!G?;UY|f8!-=0i1D*mTWd$m-;%}- zn|^=(9k^&;!~7+nYwdI6!3m-6LM;Cw2JIScS;OEPr7PGg(z{ zI?QeC>}rWu3Xgs5y!Gc*SD={LI>5X2U0f@^5ouj6iF(S7a0gh z)X`O2RGakiJkyC;T&)|q(~YyLCFOo=F2MduHwt^}ub5t9WWDBuruMvn(#dZUagvI= zBlFyR{HIW)o6y@L&K_ZS+Hixm7+f)~j}f1$5>SZ|HUq~4#y3cyUGW{6tHL_duJSjY z$l&udjFC6GDm-@19`~$cV!)e^LprM}s;ind@A^~r&+3Ow82ka95zM_an@2Y|x6~q* zhJWw5wm{n|47-)oQIE}yz79|7$^<}v3(BwC8k41;sut(tz>x+NAVLw6THD zN;!e*J&j^F7jP|vnv^bc4)00MaTxH)1A6;iyrRSo`UTM%7X{cl!Y$3FVS8G#UU!u4 zxThxdM~N@w{zD`9yaZjP)xq*$qx$^T7c{k(BNlyrxD*tGos&f5xLQpcI)zOKR0Lx zFfHi{?;>-xF7kjCOik0*h0P2;hy$XRFzomDg<=r69*?gp9emesq{AoL|2Nt6U4rG_ zI*kXgW`8sGw>}cyj=<#;Usj3w@xZV6IC$VS(ZYz^;)PFOqBLgAxP*l(L$N->sgQW7 ztJ01;n^sUS)2( z&FGF>+CG32p`d!%H6m6Bgt3V!2N;DCp{Au}V~25K;u&v_L`$z4!z4U%QrQ)lBv(I3 z$8}_@j+Jq3+)kSh+N5Wl|K%%ru#}C%AF|IfChC%aubXSkSAKOW@$tIOfOK0lg-b(S z->*TClBNv>tj7%SHtJ`H1hkGEI@e_e53c&y7<>m@QuMh#!RjT1XZgDJsZ!eP2yj?~(^z>C&@k-p~K3A2-e5*J^b>G#Ke z(`o(-5%4WmD_`ti}I7CL&Dy(Em(C45jq` z(r8KLLC~Z^J^;)C&Oz`1hMF*})Qsuep^768n?V`HAQv3D0YD>XvyeNnq9K#_kq<2SF3$&E$m`&L2a17b2w(`r z7}!&SnQZuenn>_YDMZ0_b5W(g1kpV6_rA=nFAQuJ6T6NPV~mdFT;666+ZJgtt&a%* zHT``P&;8t36V^)x4yhLqqx&XLjDgbFk+SR)z7(!r2J|~;PWnerN}vE=A)L2Dyu2lc zrV&#&VEq5kld0U=0>%z|HziLhU|BIrSm@_l3#^$*FBK*XpLFggSYVMPD0+{M9C>@?{mo6X2EiSC!nfyPqm5X7Uy%4F6 zr}MjoBf&#C75Pdfjwuh^{Y0y?*HfN9)bYqyd9=?i!^-CEw}gLq>z4C+Uyy@=*!$`@ z4gk9aD9XMZ6;ti#1o)1_M@5R~DqC3&dbrji`3yduJ`P+E8Ug{Z;LC%OiL$jaHfwP* zJcx*+m8REH@fBOw8%>tGMy{0&!x|6OwUSt$R&uw>Uth!7hcfuudv0dic6F6>1$|w= zME~73ELx=k>h~E~8GslD3K7N(+|LOm$gnm1AMWv0o&vJmf**~GFnz+6nM&q*Qd{`{k!l==GYnWu7DmX!3g6M zj7x;sX-i=K>k59$CqAwgqY4tnp0_=q%0-j-Pv%d^m(Ti$$K^R+l#$2epxEq~%u7x% zF8#}Kf3x*OW`ePxCfJXLn5VbRWyVYv7IM%-la8ZPwp>k&G=)Uz!ED0vEF93o;xf*M ziipT45qE@3#4?#$DcOjF%R(;rNpQpTYe+!-TYl`|j%34H?$<$8WhIT95o1G2p@Xbm z9_Q3F%AWoeJr2zR*ZZwAZOxZr-hi={%}|Cf%Lk*VU*#(NWZPGsPn+QDeEpc)9%+Ba z#coD5z^kF?c6CjZrFp8hFm||4=q+gg_maoamDOXJp|6_JFB4%lfKlO?xR-eGKeEu1 zTE|dox;Ng|($uS=33ZLXA~PM z9cb}}xLNR<6=QOE&?#nGaHk+wrEOAsd5SErbOL#sSvEO!iC#KY;*I*X4KH=`pV2VKZ3D}9eEjx%`K9r zcq(G?w5z>F%J>!zbVA6^>PC%Edy4n>mo+>={6U_)MN;o4YXmxEb8hyr lyRD_MUDAjBjUaqh2s=l-~J=givAoV}kl`<%Vz?7g0oW^HNA%5;_q0036B3Cflh z@qfVprL}K$Jcnt4&QIS$9{}pJnU6f_Y4^fjCbkv;@Q(y7?-l?Y{LSA4fJh|(_~HQo zS_J^W8(P$EbBQ(py>4cV0#5&4rCn8dv>wJ#6Q^(hVCMJ>&;%*|h}H>X zG|$x(=Cc6+!VQhmw~v|oUL24k=jZf3A@1S)sE?oDnA<3tZTX!z6H1st;sa8P*O805yxbr{huNR}lELEK@FR`rr{#qm}@=-(}zvQe6% zzUJF5_l}FEJ!LuKro_K9%#6S(Vd+Beg#cGY|2@$X=5FqP72`Us6fy4VqiyP`P1cOZ z`lHCbZ;7xRK2Lh1N%>3`5U%5dj^a!##NVz{d%VJGAogHN+MBgLkK2%;0 zfA1ML>FG6Gi5YBX;OT*OgF0bU4zU8QEIN?OdR5}rT-Vcux`3rCowYV2{3E;;4QB)m z;BQ>tBSed;=W3?p-Va)SYz5M0 zO=;xL2oG?O+#^_&&&R+h7SICqa~JcY9iH>;&w*9%P&PKr(5=!EJS{8G?gjId1V z#3^DAKzbnO`MdJ@(?Q=heb&{;A^3d|9BiqZHqJYE5Or)wv><-XMHhP{{=pvHzB?G$ z^G+4YwYJBeW&7z1n_0ERjpB+s*^mSM+X+D7m_x9;l}~?zz_0SOgYna z)@?wh_-?7#kHh@ah;rPY#zc*^;VovTV$Md!pLU4we~jBix4@5#xcKL|^RbH6g#nYO zxuDt)HtBoqpWrtlVUpM8{2}|IJ^Oj=r1PlwnBJ-*A%mnG_Vr=Is=S+b3QaC{X>>Uc zAlEJ!ZjMYjCSe@b^LWa`Fa#6~$Xusb9g? zk^qfwWokbc7sMq+-=^C6bv<+o={H^Zl|tsEY>)LsHD10L*D!}^$9R2yf{+h7lClk6 zO1(}mYBQ^mk#ND5j1ej6>i>{7OV`&K7H_#Um|SFv40}PK@8A{bXmTG{t2ZaPIO}kd zWd58u$&`gJ7twt%^U3(~&pf8w-Pm+sC(dK}cQfX?Gv<%^DKq;n*5i)fW8W$Zg*IWs z=N{=lJ0ezV&Q#N=gB5vj!v$J9V{qe5U!>z z8LtxJY3e5-bR+rtuQL^$`V!o&y9&_9i01BM`hky6$g9~0te1Zu5>P5-b|bN%<;0Sz zoLH8m1wC!B3*a*KB_+bvcQu;`ALwq^o z*rizNsyX|LaA+@r)8qdHWw$QC7%euiO=fi~)|*_TcR0o&J|nz38S1)OciP~i_J_NV zi7rgRZBPZRDC#OJV}AzF%$oi9_T!2b{Zz!!Gi7BQm3IHFh?=Q{KZDa~D(9V8r@`TRTx=U!OzT1TYAy(LRI zW=37Jhvk`=(1YN#C?1bAhy<$J`ZZ~DaxEw`tA5hnlQ3?E=llwUxA&c49N1nuHQw>EsScQOlVj&6JSE6}HL|OB zX%1c3(QGD8=U51&>Vc`|{Z|nZg2bt;x7fbBifMPITaPHck$YLTMba9xGfYjrUx`&9 z$$=w-Cgivnaby4Vqt}Az=O->1u06TLE8VsYyJHUGt?=37)4A=OGP=-*UJ#)wL#1b! zv&}E5%d-+!5-zcK5KiO`&yJqhP7${P?}M+t?$oPLV!cB< z@p{dZZtXicaqr1}>smgHts?(q*1oyg0u59rcf-osXG^LGc8smR&xFb?Ex*RpH9_5v zL;UAH)_%CKulIDh&i1vVQzJ&TD_@bqw*;=i0D|{{{i`jd^+M~tVr`}|rm5FSXFv$LPXImykxH!miU&|FZCyNwk?C6)!haq!+`uKL2$R*W)HV0ioz%IUEwsxq$QZ|Jc&zBqT+(Cmx{AeM+Ur~k7;HxOw;)_&1d;v z%jFIMxzojGtDj#IjyNCxfDRgZnG#I?InMvo2tgOKz5Td1UH*hJ)E|Q14&$SNS5T(1{qGz-<7(-#Y4CVvPBTMxi zR8s%EZtt46%`T;~mW$rxrC1QPGFLADLVeN+u%>gP@8k|+E_=V9s&tQ|#agDIpg*Ra zo}TvA$X$JuAMkq{b%m}^BQXKAP1l{NT{+FXZjf#GO{~|D$e*YIyf>gy0~wa z?siR<&X?dGV{&mN7k2bYP}CeR_Q*JfU3zPgIlnAAeSe%+`Y`F8Y|gJWDEJGsBl>F} zEZImpq|Vw@>E4v_7{fQ&wfq2{;cg_Gzs(nWD?kXM13nT;`L*l0S&efQEzf0Ic}e`- z6stxG#^sDZls!AbZDGsU;-fqg?CCDz_cFNglamOnlnKX_w*AJe&>>iHJUZ-P0m=B> zW{K|7OHw-fUaep*{*?VGS+k#=GN`0TMmXNnl-(^vU8 zWr{D^2>cJzxv1fxM@AyDr~$Yqt`slWk`rFrh|D5HT-yPkutl?+KFfzgW{edO=Hyrt z^q+q`WCHIP(@l;`ahvn_nyU~p;W(aW80pc-s{^rgRQ8E>*QBRcI#Ut{#y5B>@g>G9 zgz6lkGW$7%En{)pnY=bkp7#m^ska+x8A($-<#rMGZ>Ech=atIZStaSuxM<*I^VZRW0*~3qP%Q9;MJO<0=aX)%|Xa>@q9=SBQ9inaJ zM{3@+KV2boA+rqId?RfK>T^zxiivTp@HrO3PfrsDau2W2C80EUEkwmL;OGafhTnKQZp}hozbKfp+Z957dV89H16{t zYzM*|6*QhlIt(~LPIy}Rscatb>XpkZ@-)T@p-RlK+#TfGJM~S-tgs}vz9|Y7D$v*8 z{MNMZ$waKkMm%kiwBOdozzwVbcz0M+iNQqw1JR&OJ?bHITa%?oK+cCvaf_h+4y$Id zHeB3%nM2F$3|5PgB0!!e9^%K8@W@e5WfeO7?gA1-Jx}%}&!RHnddW5{#UzZlv2B%<*x=G^;O960#Yj$!DoQFBi4`gU26bE(^`{>@)B3>mUza~rE zgH;>=04^n6O*K>h$?f+6)xu8#a&sTA{Q5P~7&Mqx$@rUqmVg}P_%bp+{<>YBuw6A; zI8v-3LXCz_-?$Qi_@0)!_yG|a$E%5=Z8y|}i*}(3XlBFFWCVRWR8#~n8H<8O*)C@$lnf^k1|DN(l9uw$L5W zh!Gu+ZfytHe?xpxA{bjt5V{fRPB3>{Gr!PI)T|6hO+v4U*pT^4W4SSrXce`W=)3F2 z%C+?VB&W>S>*$Bb0JMo1zltEh%Popy#+qVy(S5X#jGlEyU(_yvHJ}WgCf_QfNxja1 zq@$4=vnKsY)x=JVs-HddGy_^;!SD$-mya~5)C@=h`iTu|qOTBtrG*h8IuXnPx*x5i zY8jcQSO#26LSxk$j%5HD?gS-;9lsUyB6=Ggon#+=oB9rtO!a$=%SjdC6)%=twQla@ zg=R2@19<|))5o&rsT*3E0%YnAEnyHWA7(81G=ymadd2L23!7e6SX~XyjYOYnCpBKS z5rk5BoQGHoRFmOIvC5dxypQUJ!luBv&Qb+l7{i7ZLe+%MJtR_$(Ij_+vTI#JBh?7= zs5P^L>(I|pjRff@7p>x5&{otIkutdp@|^fQo=kNB)@4l`N;k37Mxs0153^UT4M~i0 zbZkX5Aa8MBq1v=MqC&I?Id4$?IGl^?JWO=-6tOpu@$ajxcCD_s!Q3Q9WRE`iE)yNu z&aI)8L3{;21+YM%@EW-wAV&ko!?Aij=6FHl&|{;*Mq%s|+ zvWiwJcyOOdCRakUhS5MMMdn1s&;ZeM9nR{=5E>jE-MT9UdKB4Hc%LHLYS>pD8QiZ}x4n zD+Fbde`m%zW2lqWc_>+&;>c8g!n)>Qp;Q5zD!Hkzebsq<*j|^5MK79E!b5Kp`$Fp# zl=YrJdiy}hcPbR?E`C6(826G=g%xL>NXV2=kVKc?*~;Lid%AWcvwxuEUnMNfPATGcW#1yPO}&mI5|cd z-}bmpPI}qPWvF$dl^VsIyIyTZS8NE(uB7f%SR36D)-LmGsf zkYpmZG03iF!{reuLgrR`OZ^1->OVI7t(E|l;WCf36eQT=-+w1~Eji=rz7E-LUou(i2npp}gK(F&i-GuKRoj=<^94HeoObFA2pI?TH! zL#+l+`Qy2=ng!A`Nj>*_+loxv1fV|hQOi<^k-_azi2#RMdlYqnZ3mA@fn%BqYEK54 zt7dmT#%0hpGVZr8$2e?Hb))3DB+HN49gednP+#8=Mho`7&ByGv9c|j}QMu}Y3?ITU zy6~4T-pYgP4ksDUjB(k5jK2@I)yH?%_jNwq%MRWPLq}Yk#M|2IwMT7EL{s{qZt;Kz z4&r+sHeisMZnmM>aPdY*VxW>I;a0}603qcSP-vp6RIpePGEeq+yMtpM0`Y_AM<74{ME|{j!CT3U%~1y@!7!u0I*b zub!WDN zL~PrUkRC9O4Mn(|>e>)~xGw$amoezGtYaMZza>$S`;~O@mZ&Ilie3JaW0js}#G@x4 z$ZS^6vx|RSD#7HYk+D$WCKX5G4n%sg!le73Yc^i+P{yv`OQ1B=SGmVM_;d4)OhM$^ z?oqduD->fbeN!eGr5MwiBNH4iEAFw#)`RTVf9FZn%vkrvxXC0kBlPi>ACJX$ZPUhf zG^K>TP$&&4%WT3a8PTMQXi}eqxXl4`XG^ZRguzrp2HaEDz00wv+@SRnNkWfz;p%Cx037E0Z zPtzO6UZa!7aYCg#iw>X5?2oq5#jek(M)k{fM->}{*>w(7FqL{a9d|^1Wbb>P8BIxv zb?wykCl&{X+jb09Vs!Mwm_em5l+Ws=J(U<(1a0AtCpO;Jd+JH+a?RTinF6JvpIE-4 z4Uf`a+=f5XSmAgYEWOVB!0Vn`rqzsVJJ*VtO6j;ZI7r%~4nDG5PJnXLI;WaekB!bq zeI~{6tn19FNMmqMvTE<8Q?`UuiDZd2bAg6F<3pw6eI~noE{t@Mgi$#TT5SF^8Im=| zlNtOlzh5@3&W6He_e}Vck6Ev>%eYn`T`TpvQ4`fNmik(#tM)rjWnwX$HR5AN=y;rW zN!NIF*P&itJ{R9}OYwgGQZ7DgO&rv^5T>C|*3!;n&`Pf}cR7dXaLVpKSDR1hIwYp9 z3Zjvoo|Ssp%zB^0&bVipEp{C*ys*@g5J%)9pYQ{`RHl96OTdxuJYss=KXmVZ&{nvp zp7*1nJ%tgn*%_A|v3u?-v1QhrFc+yLmjmERG)?mb>z#Ry)~2;=$#s-l=1Qi!5k zDJrZK}q zt(NgRUUL@LMu^icdMP9J2a?4@3A!4(;jwl$Ua0}m&ZUt%plb?Bo@K{~gbN zR6T#GbA3}^i*q=Qp?52Ymu_Fj>Pt|GQ+8(eVLWPu#!8gX-~(aIYRP6tp~$#%`aM`2 zWGd?5hV7^1Tz?D3`UacON0pg>yvbW=BW*k4zR-q{lO~6;)zYF&4>k)m)kAKn4&Y+r z@Ui`}&wnjR*$l(QuKcX2>HC8j>m0vNQhi;m!@f6JrU#H*#Iqi3RzU)%q5HcEWC@hH zc*g1Thrj#RPOh!psdMxGV-;U+r2U1qSfz7G8;MVlYzpCg&A}@HA@LVn*lt7gre}}F zNyMzH8AOf{a(eBAQDTKW+qBuU^H(ggRV){W z-n=NE6k2>>Rt{q{e(LKrx?D>hmGPb;NRM@k#BPC`>U(bVaKcz@?*<9hrAQ|1(}2=^ z2eaF_m4{1AuaU^K(VcKoy?(ZLFS3rra=X(NE^Iqso2PKB0y;!T%}B853zQ_)87{E; zO>y*q{FNWyZPuGgZ39agJk%A;3cAHxnp+B6{2>#)@~izlZk+SUnC|2qGXskj@-mQ5@(PR$OLJs=0;O}^fK)m=u4cg-iGQ0;J86!%vS8W-EVRfc#V^8~F94?` zm$sV6Dsbf_kmU91v+ohcsSvbIy^T|Q+I7oIv>H63l+*UR1#=71^pmg`&n@k!7l+5e z;lyIfTOP^GZQ@U3IB3YhTjby5@ea>%%ROZmokh;h5;|YpKXJN%8lSfkS&uMEwWE`3 zPl%Vb^8b3f|MS8Q!A;m>!f{E1B)$YD z_Ef9=Bh}mE+*ol;K~dwI(M{iLr;dSJ%TL0swkLGc%RQw+iwRzKL;4oRi~5#gK3%}M zJ>;+^Ny4`{MIcSD;7`Jk$zOgtok5Yf8YV-R&~%6&)*~ z6W{dZB#K=QqA1fVQp5#3Yn5|;AL6Lg)8ab(O8yiW10GnZsJcL6 ztg!NSal6dUd@4*UfS{7TCAJK@XB_^9L=~#5xfYxM z?uBTMrBP~#aNnybSYS##7Q_y)4p3@_aOaPZ;jD6&A}T|NYRX03o0r5$Ktkh@X}3x2g0qCej-WLnMjsOl+eKL z$qfG!FiY?8f7Enhd)k(mW(ZJFP=6m(E}Zi#$c8c?3+yd1W;W?AEUt0W-n~8_pQXCo z7kHJz_kXE_|4sn^OZ_P%$iKT;4^!1TmV&<_2&&0_g3jmT@;6s6c?9psi4uKv6TUEm zewF3W=LSUbyhrfkwZar22S3e4;rfaP<3#?mxS+r*8#)MfT@6qjpm_Yym*7=FwgJJ^ zS}#>0f)=S{Q$9xhS%GK<#{ZLk-M_}AP>(`^LRSzVR|+dzT)ZHy{J;89}3wX?P( zsmK7`9e8QjEm5!Vh0rjSzYFerOwk1w{#Xi3-NgD&Z}QP1sjv}`RYtbkk8|80*LYuz zMOMt1`PNV<(w%&%fpIRl;%*kGYIoFvA+CMP>#77*tqE$}?$+7XXM0lg2`auR{oXrL zf0gRM5xng5xXae5&mlaI-=_CC>_r`m3^69uMrmjH4H<|Loq(LQ*pH8o+9L~);~kSF zOCv}AS98!-(mgMLvl;kAV7?P^tv>mdFgRo(lW@Y(dCGCui^bU#I!nLG_qEd2`ESSABc!pG zaPOt{`1#$dMI_lpoq1dqWgYptGtxG|SvmJUyvVR}5 z3%XH$e-=yS*C?`Q(GbTxF(hHDdxrITHS^#BweeLD@o$6ujOj3CN*{pJvP)-(MdpKls5>>?VOPdzfO4K?~~u}`_)uO zOF%k04*%ze^o*O!;vDQM1~w69G_*x}N`oauNcuapYgd2XJm^u+`j+qtDQ*Lsy87|= zW}0-m;ZxS7Hvutp8fliPM>Qd5&x=z{Z`Io;;jX&V<%3V6T`gE* zSwp$}AdSFd3->TUCL%YC@U#kjA5Hiz6g(mNhT^zy-4v^XqZv$o9$T1sLaMasyuwIJ!i{m&qK1K`n@m46OF(3$EWvBf^fW&S0?D)l)N3h zOOe|hzrk-TXvi$#SK_iCXF1sKat%KYn>u3Gnfs$=a!+E#483g%NZ;OdBeMxMo1*=r z+ko8fhBvS7@r^uMXlU7H>I;7_?Xtr*t>L7?bHRuRxf#6jP;s=8ifo3CSy|_Oe;ag` z+-ErWLgV)CkSfdxbpox@*?*KzBN^qAaPP}6{icy;53ba0^urRk7?&lkXogEE2C<&Y z*y%Vimt7_hU=d`ibJEw?fdn z0QHSC<-S^j14_kH|14tYjeL%~To1JKQ^bhYd?;U6P*E#>>84JnY9F1KzZ*LUqG$v7 z%;a)uVr>YEzX)~?Y405D?k`-{%qT&Bv-`#)<|E}Z;?(bawJlrcQU6l&G%6N_t5AWU zSGPI#R`@Eq~AT(3?6PZE~%^GZ}piC~ZTR$7Z z1aD1rxtPY|o=KR`hH6u`5do}l$D=GZ9f;wYzn-EG#7W8`!xOJ#>{?VJf$1_)OL zBnL1)V7&U_is6Zwb78NQ2|d&G;--OI_g4awu|3JQFcp_IGY|j-iQ^<)w`v1_r?uK5 z6ISbL`6v}YpO_yZ5Yea{^Bu2@g^2r{zK^x4;PGPp2afj*x8CN5X-K`E05hXEH;=fE zsQI-ESrGfbO0#;rNBbaQul>(=5fQTZH_0uJVB9v4F#n0!^$mpr7UZ{EN#yvQZq5U(^U2QTb^J_fExdRc31h9hPq_H2-W+3JniKe=Q z4oiX1AGqEHoW~?!#ge^((!EXTQB+hLVK8m^Mhp4cVFy;@{0k1s1;3pD!!wt2=v?Ng zbAi$3e{kBHJd_H3*0shSMK;|}oIm#sz4Nm;a=yJ;K#Ak%#I~va{#}^cMNR0B*Ghn; za~Xw?LA;%i`R=LQYGDNR;8ke>bh?vc=zk&U0j~L>!IJ* zz!d7R;i1=huo`5aH+Wi6o|t0v6q1A2No&4&{DgX(aWOy4X1wbG9*wpQ*UUM)pZGUk zpFTi+G>JX4k$X7kJa9nedZg7tvi;*k+DgeY$xUBpa%?J;qWU^w zU%29h^X^I3FGMo2&2^@fgJ`rhFr_7hOH#jepd<2K0wn{jLKFvv^Bdt+?R%Vjha7#| zZOcloP~zONir?kh()76RT|W&%6rBj<5nlRX*&>ZU?dk~bkt119xf%t;<#b2XagyMP zZq|=456jbE%I<+;Ila2t*vbP%rseQfsZNfdfzzXJLEP2ODP*0kg*G_Ayd@*cmx(2z zh~Z;c9z0thxpcXvAybcgkQ=Lu2ssHoPx_HgRBYKri$_PIS&%XW_xcws{?sn6dkQE# zz^m4f(1WI~^kCHc?ou<8?$U%v;e2$euYmRjSxzh~`D0T3l92aLCp_(YR>0)MrEM}r z;9~2>{-+n_7X7@*rO@ek_!XXDG@o@9=WTQ@#Y&<^UoPMugdMy+IToA4tDR-+t0=dD zkqs2?iR?Kz`Vy5EfAh6SV#Um_!80P=r87YM?1N#fqApVmFUuaaKG$8HFmiusrh3KK zzCt-9KNGb7swA;R(*;O_x;w(r1t=2*$At6kBOLSIKI>HRpaqe!p0#%2%3{~v{SJ7? zXr=6_#TjD%6Es(=J@CX3qnT4gu@o3D$wO>i z)+2|%hjip`i`eC-z}3c`5+^`pI2ocJrWA*%PNf`m<$GUkfY5CkIDKm(VsH93DSbeS zab{XT+1BY;((KC{bxjZIMayy`^U2^&SaPP}1d?AqCC_jtuDI_$6vZaKg6&kH>ybCecpH z@$Lr`m-pjg;?F)B|I_EUzkWpAj@|b(vR}5T8@Tp55zna5D6L*Z9l0`9q?vFJE~2|p z)ZE;dkMp?$=*kvZ&B+ucYB|QQFXN_Cq&6A7p|fv4-HTPs)mS(?0bHeU5)v6gH@Q$W zagbXYQz<|2XRHMQjzst3E<8$7Ye=Wkbmx_1)6CqoGbc46< zQUHnWLbJ$e9dqbE+>(J1lwV2KGAU2}>O+T@zEq%idZw-z1+#nJq!LtqSnw(nnK_lGxcSj1&$^_K(5ltQXJ|N zo}ehlw;v1*SPWxa|7Lv#8eSQIz!+NAHgdZ5mJGKJ%r`7wy6H9K_S9|Eq=kG>P}chnU7tN~u4WD;!Wtif9{Z!2^9!6LQ^U1)HYSz*$?RK~XTNNn6xUR2O1&kq zAsxp#gFge)(~hR;L9>zmi>(7olRRLVay7}sQhcli&f=Q^uVG^GEgKVdSzWWUp()(; zH_mGNGT|69Rl4Z7gCVC6m`xm6yH&7?vjU5O8Q@B8nmAbIXI`k-&^w{U!Dtrgey*(i zcR!Mz+fEVRc8U8glk$0N2ktu=wf!h}TPZi;$?@Rnyq{BNhYsD~wvHdW6pqyh(gSYX zhRB~md`RK_8#aoGoWqH&{+T@FDYfUZwDN`LB)cc_{nWKAM^G9~f~CG@XKES8Aa4+b);N@EoXH3;)KK{LCj zp~(rdCr8V-KOLt(@>eq_ynw(^bpM&&TiPJ>qnP-owpu=q2i z=>4u@Ls2V+`z$rH1zZF@J6#63(A3-@zex~ETYjsF89unq*nql7j$#cX!l~pHMp-{L z1@7o9Jr#{b&|jKhZX`YW4qIoVrC9hAM&r3N!-O*#JcWjnsrHg~qP5Ox6xo`H{25d9 zJhC(giAlhz}%95;{J!T(n9`e>Q?IN16)%G-N6 zf(;-pDlQ=+CMqH(W-2Z!FC`%_E+-@^DlaPf_e#Oxe-OC3J2*Q9{BHtrkmmmrERVF- zfCQ}nRxojQ^6|6vas<>I-0d7WZ##QA`nh{O_vX|vk@Eb!C=HGSbhQjLE7fhn{~vf^ BSGNEF literal 0 HcmV?d00001 diff --git a/docs/assets/images/logos/logo-512x512.png b/docs/assets/images/logos/logo-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..76d463600e7b79b94799f775417836a59ff977dc GIT binary patch literal 73651 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

    j*cBgxACBE(u}mHT(xPj4mGo7#XMy3b;( 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 bkI2(7fsHSq4oLei(_>$F literal 0 HcmV?d00001 diff --git a/docs/assets/images/logos/logo-96x96.png b/docs/assets/images/logos/logo-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..48d0cb98e3fd8fde32c3ccabd3307cde2949a384 GIT binary patch literal 3644 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg b/docs/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg new file mode 100644 index 0000000..d4334e8 --- /dev/null +++ b/docs/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/assets/images/sponsors/bep-consulting.svg b/docs/assets/images/sponsors/bep-consulting.svg new file mode 100644 index 0000000..598a1eb --- /dev/null +++ b/docs/assets/images/sponsors/bep-consulting.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/assets/images/sponsors/butter-dark.svg b/docs/assets/images/sponsors/butter-dark.svg new file mode 100644 index 0000000..657b75c --- /dev/null +++ b/docs/assets/images/sponsors/butter-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/images/sponsors/butter-light.svg b/docs/assets/images/sponsors/butter-light.svg new file mode 100644 index 0000000..a0697df --- /dev/null +++ b/docs/assets/images/sponsors/butter-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/images/sponsors/cloudcannon-cms-logo.svg b/docs/assets/images/sponsors/cloudcannon-cms-logo.svg new file mode 100644 index 0000000..9860c59 --- /dev/null +++ b/docs/assets/images/sponsors/cloudcannon-cms-logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/assets/images/sponsors/esolia-logo.svg b/docs/assets/images/sponsors/esolia-logo.svg new file mode 100644 index 0000000..3f5344c --- /dev/null +++ b/docs/assets/images/sponsors/esolia-logo.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/sponsors/goland.svg b/docs/assets/images/sponsors/goland.svg new file mode 100644 index 0000000..c32f25d --- /dev/null +++ b/docs/assets/images/sponsors/goland.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/sponsors/graitykit-dark.svg b/docs/assets/images/sponsors/graitykit-dark.svg new file mode 100644 index 0000000..fd7d12f --- /dev/null +++ b/docs/assets/images/sponsors/graitykit-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/assets/images/sponsors/linode-logo.svg b/docs/assets/images/sponsors/linode-logo.svg new file mode 100644 index 0000000..8736783 --- /dev/null +++ b/docs/assets/images/sponsors/linode-logo.svg @@ -0,0 +1 @@ + diff --git a/docs/assets/images/sponsors/linode-logo_standard_light_medium.png b/docs/assets/images/sponsors/linode-logo_standard_light_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..269e6af84b4053038ad2c125dcb802146845cd84 GIT binary patch literal 11972 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%0AJz5q5wV>N)Gl1w^%Y*kQ0b=_fXywq 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 UzWnF?MnkyieoMpq2F$qs0k%!pAOHXW literal 0 HcmV?d00001 diff --git a/docs/assets/images/sponsors/logo-pinme.svg b/docs/assets/images/sponsors/logo-pinme.svg new file mode 100644 index 0000000..fc807be --- /dev/null +++ b/docs/assets/images/sponsors/logo-pinme.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/sponsors/your-company-dark.svg b/docs/assets/images/sponsors/your-company-dark.svg new file mode 100644 index 0000000..58fd601 --- /dev/null +++ b/docs/assets/images/sponsors/your-company-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/assets/images/sponsors/your-company.svg b/docs/assets/images/sponsors/your-company.svg new file mode 100644 index 0000000..3b85ece --- /dev/null +++ b/docs/assets/images/sponsors/your-company.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/assets/js/alpinejs/data/index.js b/docs/assets/js/alpinejs/data/index.js new file mode 100644 index 0000000..7bf0532 --- /dev/null +++ b/docs/assets/js/alpinejs/data/index.js @@ -0,0 +1,3 @@ +export * from './navbar'; +export * from './search'; +export * from './toc'; diff --git a/docs/assets/js/alpinejs/data/navbar.js b/docs/assets/js/alpinejs/data/navbar.js new file mode 100644 index 0000000..1075f3f --- /dev/null +++ b/docs/assets/js/alpinejs/data/navbar.js @@ -0,0 +1,28 @@ +export const navbar = (Alpine) => ({ + init: function () { + Alpine.bind(this.$root, this.root); + + return this.$nextTick(() => { + let contentEl = document.querySelector('.content:not(.content--ready)'); + if (contentEl) { + contentEl.classList.add('content--ready'); + let anchorTemplate = document.getElementById('anchor-heading'); + if (anchorTemplate) { + let els = contentEl.querySelectorAll('h2[id], h3[id], h4[id], h5[id], h6[id], dt[id]'); + for (let i = 0; i < els.length; i++) { + let el = els[i]; + el.classList.add('group'); + let a = anchorTemplate.content.cloneNode(true).firstElementChild; + a.href = '#' + el.id; + el.appendChild(a); + } + } + } + }); + }, + root: { + ['@scroll.window.debounce.10ms'](event) { + this.$store.nav.scroll.atTop = window.scrollY < 40 ? true : false; + }, + }, +}); diff --git a/docs/assets/js/alpinejs/data/search.js b/docs/assets/js/alpinejs/data/search.js new file mode 100644 index 0000000..4ad2e44 --- /dev/null +++ b/docs/assets/js/alpinejs/data/search.js @@ -0,0 +1,124 @@ +import { LRUCache } from '../../helpers'; + +const designMode = false; + +const groupByLvl0 = (array) => { + if (!array) return []; + return array.reduce((result, currentValue) => { + (result[currentValue.hierarchy.lvl0] = result[currentValue.hierarchy.lvl0] || []).push(currentValue); + return result; + }, {}); +}; + +const adjustHits = (array, isServer) => { + if (!array) return []; + return array.map((item) => { + item.getHeadingHTML = function () { + let lvl2 = this._highlightResult.hierarchy.lvl2; + let lvl3 = this._highlightResult.hierarchy.lvl3; + + if (!lvl3) { + if (lvl2) { + return lvl2.value; + } + return ''; + } + + if (!lvl2) { + return lvl3.value; + } + + return `${lvl2.value}  >  ${lvl3.value}`; + }; + + if (isServer) { + // Trim https://gohugo.io from the url to make it work locally. + item.url = item.url.replace('https://gohugo.io', ''); + } + return item; + }); +}; + +export const search = (Alpine, cfg) => ({ + query: designMode ? 'apac' : '', + open: designMode, + result: {}, + cache: new LRUCache(10), // Small cache, avoids network requests on e.g. backspace. + init() { + Alpine.bind(this.$root, this.root); + + this.checkOpen(); + return this.$nextTick(() => { + this.$watch('query', () => { + this.search(); + }); + }); + }, + toggleOpen: function () { + this.open = !this.open; + this.checkOpen(); + }, + checkOpen: function () { + if (!this.open) { + return; + } + this.search(); + this.$nextTick(() => { + this.$refs.input.focus(); + }); + }, + + search: function () { + if (!this.query) { + this.result = {}; + return; + } + + // Check cache first. + const cached = this.cache.get(this.query); + if (cached) { + this.result = cached; + return; + } + var queries = { + requests: [ + { + indexName: cfg.index, + params: `query=${encodeURIComponent(this.query)}`, + attributesToHighlight: ['hierarchy', 'content'], + attributesToRetrieve: ['hierarchy', 'url', 'content'], + }, + ], + }; + + const host = `https://${cfg.app_id}-dsn.algolia.net`; + const url = `${host}/1/indexes/*/queries`; + + fetch(url, { + method: 'POST', + headers: { + 'X-Algolia-Application-Id': cfg.app_id, + 'X-Algolia-API-Key': cfg.api_key, + }, + body: JSON.stringify(queries), + }) + .then((response) => response.json()) + .then((data) => { + this.result = groupByLvl0(adjustHits(data.results[0].hits, cfg.params.isServer)); + this.cache.put(this.query, this.result); + }); + }, + root: { + ['@click']() { + if (!this.open) { + this.toggleOpen(); + } + }, + ['@search-toggle.window']() { + this.toggleOpen(); + }, + ['@keydown.slash.window.prevent']() { + this.toggleOpen(); + }, + }, +}); diff --git a/docs/assets/js/alpinejs/data/toc.js b/docs/assets/js/alpinejs/data/toc.js new file mode 100644 index 0000000..233f877 --- /dev/null +++ b/docs/assets/js/alpinejs/data/toc.js @@ -0,0 +1,71 @@ +var debug = 0 ? console.log.bind(console, '[toc]') : function () {}; + +export const toc = (Alpine) => ({ + contentScrollSpy: null, + activeHeading: '', + justClicked: false, + + setActive(id) { + debug('setActive', id); + this.activeHeading = id; + // Prevent the intersection observer from changing the active heading right away. + this.justClicked = true; + setTimeout(() => { + this.justClicked = false; + }, 200); + }, + + init() { + this.$watch('$store.nav.scroll.atTop', (value) => { + if (!value) return; + this.activeHeading = ''; + this.$root.scrollTop = 0; + }); + + return this.$nextTick(() => { + let contentEl = document.getElementById('article'); + if (contentEl) { + const handleIntersect = (entries) => { + if (this.justClicked) { + return; + } + for (let entry of entries) { + if (entry.isIntersecting) { + let id = entry.target.id; + this.activeHeading = id; + let liEl = this.$refs[id]; + if (liEl) { + // If liEl is not in the viewport, scroll it into view. + let bounding = liEl.getBoundingClientRect(); + if (bounding.top < 0 || bounding.bottom > window.innerHeight) { + this.$root.scrollTop = liEl.offsetTop - 100; + } + } + debug('intersecting', id); + break; + } + } + }; + + let opts = { + rootMargin: '0px 0px -75%', + threshold: 0.75, + }; + + this.contentScrollSpy = new IntersectionObserver(handleIntersect, opts); + // Observe all headings. + let headings = contentEl.querySelectorAll('h2, h3, h4, h5, h6'); + for (let heading of headings) { + this.contentScrollSpy.observe(heading); + } + } + }); + }, + + destroy() { + if (this.contentScrollSpy) { + debug('disconnecting'); + this.contentScrollSpy.disconnect(); + } + }, +}); diff --git a/docs/assets/js/alpinejs/magics/helpers.js b/docs/assets/js/alpinejs/magics/helpers.js new file mode 100644 index 0000000..de9fa24 --- /dev/null +++ b/docs/assets/js/alpinejs/magics/helpers.js @@ -0,0 +1,36 @@ +'use strict'; + +export function registerMagics(Alpine) { + Alpine.magic('copy', (currentEl) => { + return function (el) { + if (!el) { + el = currentEl; + } + + // Select the element to copy. + let range = document.createRange(); + range.selectNode(el); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + + // Remove the selection after some time. + setTimeout(() => { + window.getSelection().removeAllRanges(); + }, 500); + + // Trim whitespace. + let text = el.textContent.trim(); + + navigator.clipboard.writeText(text); + }; + }); + + Alpine.magic('isScrollX', (currentEl) => { + return function (el) { + if (!el) { + el = currentEl; + } + return el.clientWidth < el.scrollWidth; + }; + }); +} diff --git a/docs/assets/js/alpinejs/magics/index.js b/docs/assets/js/alpinejs/magics/index.js new file mode 100644 index 0000000..c5f595c --- /dev/null +++ b/docs/assets/js/alpinejs/magics/index.js @@ -0,0 +1 @@ +export * from './helpers'; diff --git a/docs/assets/js/alpinejs/stores/index.js b/docs/assets/js/alpinejs/stores/index.js new file mode 100644 index 0000000..17e2a34 --- /dev/null +++ b/docs/assets/js/alpinejs/stores/index.js @@ -0,0 +1 @@ +export * from './nav.js'; diff --git a/docs/assets/js/alpinejs/stores/nav.js b/docs/assets/js/alpinejs/stores/nav.js new file mode 100644 index 0000000..1dad17c --- /dev/null +++ b/docs/assets/js/alpinejs/stores/nav.js @@ -0,0 +1,104 @@ +var debug = 1 ? console.log.bind(console, '[navStore]') : function () {}; + +var ColorScheme = { + System: 1, + Light: 2, + Dark: 3, +}; + +const localStorageUserSettingsKey = 'hugoDocsUserSettings'; + +export const navStore = (Alpine) => ({ + init() { + // There is no $watch available in Alpine stores, + // but this has the same effect. + this.userSettings.onColorSchemeChanged = Alpine.effect(() => { + if (this.userSettings.settings.colorScheme) { + this.userSettings.isDark = isDark(this.userSettings.settings.colorScheme); + toggleDarkMode(this.userSettings.isDark); + } + }); + + // Also react to changes in system settings. + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + this.userSettings.setColorScheme(ColorScheme.System); + }); + }, + + destroy() {}, + + scroll: { + atTop: true, + }, + + mobileMenu: { + open: false, + toggle() { + this.open = !this.open; + }, + close() { + this.open = false; + }, + }, + + userSettings: { + // settings gets persisted between page navigations. + settings: Alpine.$persist({ + // light, dark or system mode. + // If not set, we use the OS setting. + colorScheme: ColorScheme.System, + // Used to show the most relevant tab in config listings etc. + configFileType: 'toml', + }).as(localStorageUserSettingsKey), + + isDark: false, + + setColorScheme(colorScheme) { + this.settings.colorScheme = colorScheme; + this.isDark = isDark(colorScheme); + }, + + toggleColorScheme() { + let next = this.settings.colorScheme + 1; + if (next > ColorScheme.Dark) { + next = ColorScheme.System; + } + this.setColorScheme(next); + }, + colorScheme() { + return this.settings.colorScheme ? this.settings.colorScheme : ColorScheme.System; + }, + }, +}); + +function isMediaDark() { + return window.matchMedia('(prefers-color-scheme: dark)').matches; +} + +function isDark(colorScheme) { + if (!colorScheme || colorScheme == ColorScheme.System) { + return isMediaDark(); + } + + return colorScheme == ColorScheme.Dark; +} + +export function initColorScheme() { + // The AlpineJS store has not have been initialized yet, so access the + // localStorage directly. + let settingsJSON = localStorage[localStorageUserSettingsKey]; + if (settingsJSON) { + let settings = JSON.parse(settingsJSON); + toggleDarkMode(isDark(settings.colorScheme)); + return; + } + toggleDarkMode(isDark(null)); +} + +const toggleDarkMode = function (dark) { + if (dark) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } +}; diff --git a/docs/assets/js/head-early.js b/docs/assets/js/head-early.js new file mode 100644 index 0000000..b21eb58 --- /dev/null +++ b/docs/assets/js/head-early.js @@ -0,0 +1,20 @@ +import { scrollToActive } from 'js/helpers/index'; +import { initColorScheme } from './alpinejs/stores/index'; + +(function () { + // This allows us to initialize the color scheme before AlpineJS etc. is loaded. + initColorScheme(); + + // Now we know that the browser has JS enabled. + document.documentElement.classList.remove('no-js'); + + // Add os-macos class to body if user is using macOS. + if (navigator.userAgent.indexOf('Mac') > -1) { + document.documentElement.classList.add('os-macos'); + } + + // Wait for the DOM to be ready. + document.addEventListener('DOMContentLoaded', function () { + scrollToActive('DOMContentLoaded'); + }); +})(); diff --git a/docs/assets/js/helpers/helpers.js b/docs/assets/js/helpers/helpers.js new file mode 100644 index 0000000..6d721f2 --- /dev/null +++ b/docs/assets/js/helpers/helpers.js @@ -0,0 +1,17 @@ +export const scrollToActive = (when) => { + let els = document.querySelectorAll('.scroll-active'); + if (!els.length) { + return; + } + els.forEach((el) => { + // Find scrolling container. + let container = el.closest('[data-preserve-scroll-container]'); + if (container) { + // Avoid scrolling if el is already in view. + if (el.offsetTop >= container.scrollTop && el.offsetTop <= container.scrollTop + container.clientHeight) { + return; + } + container.scrollTop = el.offsetTop - container.offsetTop; + } + }); +}; diff --git a/docs/assets/js/helpers/index.js b/docs/assets/js/helpers/index.js new file mode 100644 index 0000000..203a954 --- /dev/null +++ b/docs/assets/js/helpers/index.js @@ -0,0 +1,2 @@ +export * from './helpers'; +export * from './lrucache'; diff --git a/docs/assets/js/helpers/lrucache.js b/docs/assets/js/helpers/lrucache.js new file mode 100644 index 0000000..258848c --- /dev/null +++ b/docs/assets/js/helpers/lrucache.js @@ -0,0 +1,19 @@ +// A simple LRU cache implementation backed by a map. +export class LRUCache { + constructor(maxSize) { + this.maxSize = maxSize; + this.cache = new Map(); + } + + get(key) { + return this.cache.get(key); + } + + put(key, value) { + if (this.cache.size >= this.maxSize) { + const firstKey = this.cache.keys().next().value; + this.cache.delete(firstKey); + } + this.cache.set(key, value); + } +} diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js new file mode 100644 index 0000000..96e8ed1 --- /dev/null +++ b/docs/assets/js/main.js @@ -0,0 +1,57 @@ +import Alpine from 'alpinejs'; +import { registerMagics } from './alpinejs/magics/index'; +import { navbar, search, toc } from './alpinejs/data/index'; +import { navStore } from './alpinejs/stores/index'; +import persist from '@alpinejs/persist'; +import focus from '@alpinejs/focus'; +import * as params from '@params'; + +var debug = 0 ? console.log.bind(console, '[index]') : function () {}; + +// Set up and start Alpine. +(function () { + // Register AlpineJS plugins. + { + Alpine.plugin(focus); + Alpine.plugin(persist); + } + // Register AlpineJS magics and directives. + { + // Handles copy to clipboard etc. + registerMagics(Alpine); + } + + // Register AlpineJS controllers. + { + // Register AlpineJS data controllers. + let searchConfig = { + index: 'hugodocs', + app_id: 'D1BPLZHGYQ', + api_key: '6df94e1e5d55d258c56f60d974d10314', + params: params, + }; + + Alpine.data('navbar', () => navbar(Alpine)); + Alpine.data('search', () => search(Alpine, searchConfig)); + Alpine.data('toc', () => toc(Alpine)); + } + + // Register AlpineJS stores. + { + Alpine.store('nav', navStore(Alpine)); + } + + // Start AlpineJS. + Alpine.start(); + + // On cross-document navigation the browser snapshots the current page for + // the view transition. An open overlay (e.g. the search modal) would + // otherwise linger in that outgoing snapshot while the page crossfades. + // `pageswap` runs right before the snapshot is taken, so hide such + // elements here to make them disappear instantly on navigation. + window.addEventListener('pageswap', () => { + document.querySelectorAll('[data-hide-on-navigate]').forEach((el) => { + el.classList.add('hidden'); + }); + }); +})(); diff --git a/docs/assets/jsconfig.json b/docs/assets/jsconfig.json new file mode 100644 index 0000000..8f9413a --- /dev/null +++ b/docs/assets/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "paths": { + "*": [ + "*" + ] + } + } +} \ No newline at end of file diff --git a/docs/assets/opengraph/gohugoio-card-base-1.png b/docs/assets/opengraph/gohugoio-card-base-1.png new file mode 100644 index 0000000000000000000000000000000000000000..65555845bf5822ceb6dc06a3ef04850ad5e434dd GIT binary patch literal 242906 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

    db?Vtve7`qf3Z3Nmy!`PID6Oz) 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 literal 0 HcmV?d00001 diff --git a/docs/assets/opengraph/mulish-black.ttf b/docs/assets/opengraph/mulish-black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..db680a088a13b2a8ec3aaff424d1c7ed4d8bfdbe GIT binary patch literal 105920 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

  • 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 T!Z1Nq_#;kIo|bXGvW)rvxp>Xu literal 0 HcmV?d00001 diff --git a/docs/content/LICENSE.md b/docs/content/LICENSE.md new file mode 100644 index 0000000..b09cd78 --- /dev/null +++ b/docs/content/LICENSE.md @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/content/en/_common/_index.md b/docs/content/en/_common/_index.md new file mode 100644 index 0000000..612165e --- /dev/null +++ b/docs/content/en/_common/_index.md @@ -0,0 +1,13 @@ +--- +cascade: + build: + list: never + publishResources: false + render: never +--- + + diff --git a/docs/content/en/_common/configuration/locale.md b/docs/content/en/_common/configuration/locale.md new file mode 100644 index 0000000..32ad89a --- /dev/null +++ b/docs/content/en/_common/configuration/locale.md @@ -0,0 +1,22 @@ +--- +_comment: Do not remove front matter. +--- + +`locale` +: (`string`) The language tag as described in [RFC 5646][]. This is the primary value used by the [`language.Translate`][] function to select a translation table, and for localization of dates, currencies, numbers, and percentages, falling back to the [language key][] in both cases. + + Hugo also uses this value to populate: + + - The `lang` attribute of the `html` element in the [embedded alias template][] + - The `language` element in the [embedded RSS template][] + - The `locale` property in the [embedded Open Graph template][] + + Access this value from a template using the [`Language.Locale`][] method on a `Site` or `Page` object. + + [RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 + [`Language.Locale`]: /methods/site/language/#locale + [`language.Translate`]: /functions/lang/translate/ + [embedded Open Graph template]: <{{% eturl opengraph %}}> + [embedded RSS template]: <{{% eturl rss %}}> + [embedded alias template]: <{{% eturl alias %}}> + [language key]: /configuration/languages/#language-keys diff --git a/docs/content/en/_common/configuration/page-matcher.md b/docs/content/en/_common/configuration/page-matcher.md new file mode 100644 index 0000000..807bad1 --- /dev/null +++ b/docs/content/en/_common/configuration/page-matcher.md @@ -0,0 +1,22 @@ +--- +_comment: Do not remove front matter. +--- + +A _page matcher_ filters pages by logical path, page kind, environment, or site. Specify filtering criteria using any combination of the following keywords. + +`environment` +: (`string`) A [glob pattern](g) matching the build [environment](g). For example: `{staging,production}`. + +`kind` +: (`string`) A [glob pattern](g) matching the [page kind](g). For example: `{taxonomy,term}`. + +`lang` +: {{< deprecated-in 0.153.0 />}} +: Use the [`sites`](#sites) setting instead. + +`path` +: (`string`) A [glob pattern](g) matching the page's [logical path](g). For example: `{/books,/books/**}`. + +`sites` +: {{< new-in 0.153.0 />}} +: (`map`) A [sites matrix](g) matching any combination of [content dimensions](g) including language, version, and role. diff --git a/docs/content/en/_common/content-format-table.md b/docs/content/en/_common/content-format-table.md new file mode 100644 index 0000000..c0a66a1 --- /dev/null +++ b/docs/content/en/_common/content-format-table.md @@ -0,0 +1,13 @@ +--- +_comment: Do not remove front matter. +--- + +Content format|Media type|Identifier|File extensions +:--|:--|:--|:-- +Markdown|`text/markdown`|`markdown`|`markdown`,`md`, `mdown` +HTML|`text/html`|`html`|`htm`, `html` +Emacs Org Mode|`text/org`|`org`|`org` +AsciiDoc|`text/asciidoc`|`asciidoc`|`ad`, `adoc`, `asciidoc` +Pandoc|`text/pandoc`|`pandoc`|`pandoc`, `pdc` +reStructuredText|`text/rst`|`rst`|`rst` + diff --git a/docs/content/en/_common/embedded-get-page-images.md b/docs/content/en/_common/embedded-get-page-images.md new file mode 100644 index 0000000..bd178e5 --- /dev/null +++ b/docs/content/en/_common/embedded-get-page-images.md @@ -0,0 +1,7 @@ +--- +_comment: Do not remove front matter. +--- + +When the `images` front matter parameter is set, Hugo processes each value. For internal paths, it searches page resources then global resources, using the resource permalink if found or converting the path to an absolute URL if not. External URLs are used as-is. + +When `images` is not set, Hugo searches page resources for a name matching `*feature*`, falling back to `*cover*` or `*thumbnail*` if none is found. If still no image is found, Hugo uses the first entry in the site configuration's `params.images` array, if present, and processes it as described above. diff --git a/docs/content/en/_common/filter-sort-group.md b/docs/content/en/_common/filter-sort-group.md new file mode 100644 index 0000000..82ec805 --- /dev/null +++ b/docs/content/en/_common/filter-sort-group.md @@ -0,0 +1,8 @@ +--- +_comment: Do not remove front matter. +--- + +> [!NOTE] +> The [page collections quick reference guide][] describes methods and functions to filter, sort, and group page collections. + +[page collections quick reference guide]: /quick-reference/page-collections/ diff --git a/docs/content/en/_common/functions/fmt/format-string.md b/docs/content/en/_common/functions/fmt/format-string.md new file mode 100644 index 0000000..556f378 --- /dev/null +++ b/docs/content/en/_common/functions/fmt/format-string.md @@ -0,0 +1,7 @@ +--- +_comment: Do not remove front matter. +--- + +The documentation for Go's [`fmt`][] package describes the structure and content of the format string. + +[`fmt`]: https://pkg.go.dev/fmt diff --git a/docs/content/en/_common/functions/go-html-template-package.md b/docs/content/en/_common/functions/go-html-template-package.md new file mode 100644 index 0000000..497c1d4 --- /dev/null +++ b/docs/content/en/_common/functions/go-html-template-package.md @@ -0,0 +1,14 @@ +--- +_comment: Do not remove front matter. +--- + +Hugo uses Go's [`text/template`][] and [`html/template`][] packages. + +The `text/template` package implements data-driven templates for generating textual output, while the `html/template` package implements data-driven templates for generating HTML output safe against code injection. + +By default, Hugo uses the `html/template` package when rendering HTML files. + +To generate HTML output that is safe against code injection, the `html/template` package escapes strings in certain contexts. + +[`html/template`]: https://pkg.go.dev/html/template +[`text/template`]: https://pkg.go.dev/text/template diff --git a/docs/content/en/_common/functions/go-template/text-template.md b/docs/content/en/_common/functions/go-template/text-template.md new file mode 100644 index 0000000..c321557 --- /dev/null +++ b/docs/content/en/_common/functions/go-template/text-template.md @@ -0,0 +1,7 @@ +--- +_comment: Do not remove front matter. +--- + +See Go's [`text/template`][] documentation for more information. + +[`text/template`]: https://pkg.go.dev/text/template diff --git a/docs/content/en/_common/functions/hugo/sites-collection.md b/docs/content/en/_common/functions/hugo/sites-collection.md new file mode 100644 index 0000000..9f4b81b --- /dev/null +++ b/docs/content/en/_common/functions/hugo/sites-collection.md @@ -0,0 +1,9 @@ +--- +_comment: Do not remove front matter. +--- + +The returned collection follows a hierarchical sort where each subsequent dimension acts as a tie-breaker for the one above it. + +1. [Language](g) is sorted by [weight](g) in ascending order, falling back to lexicographical order if weights are tied or undefined. +1. [Version](g) is then sorted by weight in ascending order, with Hugo defaulting to a descending semantic sort for any ties. +1. [Role](g) is finally sorted by weight in ascending order, using lexicographical order as the final fallback. diff --git a/docs/content/en/_common/functions/images/apply-image-filter.md b/docs/content/en/_common/functions/images/apply-image-filter.md new file mode 100644 index 0000000..abde78b --- /dev/null +++ b/docs/content/en/_common/functions/images/apply-image-filter.md @@ -0,0 +1,26 @@ +--- +_comment: Do not remove front matter. +--- + +Apply the filter using the [`images.Filter`][] function: + +```go-html-template +{{ with resources.Get "images/original.jpg" }} + {{ with . | images.Filter $filter }} + + {{ end }} +{{ end }} +``` + +You can also apply the filter using the [`Filter`][] method on a `Resource` object: + +```go-html-template +{{ with resources.Get "images/original.jpg" }} + {{ with .Filter $filter }} + + {{ end }} +{{ end }} +``` + +[`Filter`]: /methods/resource/filter/ +[`images.Filter`]: /functions/images/filter/ diff --git a/docs/content/en/_common/functions/js/options.md b/docs/content/en/_common/functions/js/options.md new file mode 100644 index 0000000..ae41fa4 --- /dev/null +++ b/docs/content/en/_common/functions/js/options.md @@ -0,0 +1,107 @@ +--- +_comment: Do not remove front matter. +--- + +`params` +: (`map` or `slice`) Params that can be imported as JSON in your JS files, e.g. + + ```go-html-template + {{ $js := resources.Get "js/main.js" | js.Build (dict "params" (dict "api" "https://example.org/api")) }} + ``` + + And then in your JS file: + + ```js + import * as params from '@params'; + ``` + + Note that this is meant for small data sets, e.g., configuration settings. For larger data sets, please put/mount the files into `assets` and import them directly. + +`minify` +: (`bool`) Whether to minify the generated JS code. Default is `false`. + +`loaders` +: {{< new-in 0.140.0 />}} +: (`map`) Configuring a loader for a given file type lets you load that file type with an `import` statement or a `require` call. For example, configuring the `.png` file extension to use the data URL loader means importing a `.png` file gives you a data URL containing the contents of that image. Loaders available are `none`, `base64`, `binary`, `copy`, `css`, `dataurl`, `default`, `empty`, `file`, `global-css`, `js`, `json`, `jsx`, `local-css`, `text`, `ts`, `tsx`. See . + +`inject` +: (`slice`) This option allows you to automatically replace a global variable with an import from another file. The path names must be relative to `assets`. See . + +`shims` +: (`map`) This option allows swapping out a component with another. A common use case is to load dependencies like React from a CDN (with _shims_) when in production, but running with the full bundled `node_modules` dependency during development: + + ```go-html-template + {{ $shims := dict "react" "js/shims/react.js" "react-dom" "js/shims/react-dom.js" }} + {{ $js = $js | js.Build dict "shims" $shims }} + ``` + + The _shim_ files may look like these: + + ```js + // js/shims/react.js + module.exports = window.React; + ``` + + ```js + // js/shims/react-dom.js + module.exports = window.ReactDOM; + ``` + + With the above, these imports should work in both scenarios: + + ```js + import * as React from 'react'; + import * as ReactDOM from 'react-dom/client'; + ``` + +`target` +: (`string`) The language target. One of: `es5`, `es2015`, `es2016`, `es2017`, `es2018`, `es2019`, `es2020`, `es2021`, `es2022`, `es2023`, `es2024`, or `esnext`. Default is `esnext`. + +`platform` +: {{< new-in 0.140.0 />}} +: (`string`) One of `browser`, `node`, `neutral`. Default is `browser`. See . + +`externals` +: (`slice`) External dependencies. Use this to trim dependencies you know will never be executed. See . + +`defines` +: (`map`) This option allows you to define a set of string replacements to be performed when building. It must be a map where each key will be replaced by its value. + + ```go-html-template + {{ $defines := dict "process.env.NODE_ENV" `"development"` }} + ``` + +`drop` +: {{< new-in 0.144.0 />}} +: (`string`) Edit your source code before building to drop certain constructs: One of `debugger` or `console`. +: See + +`sourceMap` +: (`string`) The type of source map to generate. One of `external`, `inline`, `linked`, or `none`. Default is `none`. Linked and external source maps will be written to the target with the output file name + ".map". When `linked` a `sourceMappingURL` will also be written to the output file. + +`sourcesContent` +: {{< new-in 0.140.0 />}} +: (`bool`) Whether to include the content of the source files in the source map. Default is `true`. + +`JSX` +: (`string`) How to handle/transform JSX syntax. One of: `transform`, `preserve`, `automatic`. Default is `transform`. Notably, the `automatic` transform was introduced in React 17+ and will cause the necessary JSX helper functions to be imported automatically. See . + +`JSXImportSource` +: (`string`) Which library to use to automatically import its JSX helper functions from. This only works if `JSX` is set to `automatic`. The specified library needs to be installed through npm and expose certain exports. See . + + The combination of `JSX` and `JSXImportSource` is helpful if you want to use a non-React JSX library like Preact, e.g.: + + ```go-html-template + {{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }} + ``` + + With the above, you can use Preact components and JSX without having to manually import `h` and `Fragment` every time: + + ```jsx + import { render } from 'preact'; + + const App = () => <>Hello world!; + + const container = document.getElementById('app'); + if (container) render(, container); + ``` diff --git a/docs/content/en/_common/functions/locales.md b/docs/content/en/_common/functions/locales.md new file mode 100644 index 0000000..6420599 --- /dev/null +++ b/docs/content/en/_common/functions/locales.md @@ -0,0 +1,9 @@ +--- +_comment: Do not remove front matter. +--- + +> [!NOTE] +> Localization of dates, currencies, numbers, and percentages is performed by the [`bep/golocales`][] package. Hugo determines the locale using the [`locale`][] configuration setting, falling back to the language key itself. The resolved value must be a locale supported by the package. + +[`bep/golocales`]: https://github.com/bep/golocales +[`locale`]: /configuration/all/#locale diff --git a/docs/content/en/_common/functions/reflect/image-reflection-functions.md b/docs/content/en/_common/functions/reflect/image-reflection-functions.md new file mode 100644 index 0000000..5fc1395 --- /dev/null +++ b/docs/content/en/_common/functions/reflect/image-reflection-functions.md @@ -0,0 +1,49 @@ +--- +_comment: Do not remove front matter. +--- + +## Image operations + +Use these functions to determine which operations Hugo supports for a given resource. While Hugo classifies a variety of file types as image resources, its ability to process them or extract metadata varies by format. + +- [`reflect.IsImageResource`][]: {{% get-page-desc "/functions/reflect/isimageresource" %}} +- [`reflect.IsImageResourceProcessable`][]: {{% get-page-desc "/functions/reflect/isimageresourceprocessable" %}} +- [`reflect.IsImageResourceWithMeta`][]: {{% get-page-desc "/functions/reflect/isimageresourcewithmeta" %}} + +The table below shows the values these functions return for various file formats. Use it to determine which checks are required before calling specific methods in your templates. + +|Format|IsImageResource|IsImageResourceProcessable|IsImageResourceWithMeta| +|:-----|:--------------|:-------------------------|:----------------------| +|AVIF |true |true |true | +|BMP |true |true |true | +|GIF |true |true |true | +|HEIC |true |**false** |true | +|HEIF |true |**false** |true | +|ICO |true |**false** |**false** | +|JPEG |true |true |true | +|PNG |true |true |true | +|SVG |true |**false** |**false** | +|TIFF |true |true |true | +|WebP |true |true |true | + +This contrived example demonstrates how to iterate through resources and use these functions to apply the appropriate handling for each image format. + +```go-html-template +{{ range resources.Match "**" }} + {{ if reflect.IsImageResource . }} + {{ if reflect.IsImageResourceProcessable . }} + {{ with .Process "resize 300x webp" }} + + {{ end }} + {{ else if reflect.IsImageResourceWithMeta . }} + + {{ else }} + + {{ end }} + {{ end }} +{{ end }} +``` + +[`reflect.IsImageResourceProcessable`]: /functions/reflect/isimageresourceprocessable/ +[`reflect.IsImageResourceWithMeta`]: /functions/reflect/isimageresourcewithmeta/ +[`reflect.IsImageResource`]: /functions/reflect/isimageresource/ diff --git a/docs/content/en/_common/functions/regular-expressions.md b/docs/content/en/_common/functions/regular-expressions.md new file mode 100644 index 0000000..4a8fd4a --- /dev/null +++ b/docs/content/en/_common/functions/regular-expressions.md @@ -0,0 +1,12 @@ +--- +_comment: Do not remove front matter. +--- + +When specifying the regular expression, use a raw [string literal][] (backticks) instead of an interpreted string literal (double quotes) to simplify the syntax. With an interpreted string literal you must escape backslashes. + +Go's regular expression package implements the [RE2 syntax][]. The RE2 syntax is a subset of that accepted by [PCRE][], roughly speaking, and with various [caveats][]. Note that the RE2 `\C` escape sequence is not supported. + +[PCRE]: https://www.pcre.org/ +[RE2 syntax]: https://github.com/google/re2/wiki/Syntax/ +[caveats]: https://swtch.com/~rsc/regexp/regexp3.html#caveats +[string literal]: https://go.dev/ref/spec#String_literals diff --git a/docs/content/en/_common/functions/truthy-falsy.md b/docs/content/en/_common/functions/truthy-falsy.md new file mode 100644 index 0000000..e15e58d --- /dev/null +++ b/docs/content/en/_common/functions/truthy-falsy.md @@ -0,0 +1,7 @@ +--- +_comment: Do not remove front matter. +--- + +The falsy values are `false`, `0`, any `nil` pointer or interface value, any array, slice, map, or string of length zero, and zero `time.Time` values. + +Everything else is truthy. diff --git a/docs/content/en/_common/functions/urls/anchorize-vs-urlize.md b/docs/content/en/_common/functions/urls/anchorize-vs-urlize.md new file mode 100644 index 0000000..1d47844 --- /dev/null +++ b/docs/content/en/_common/functions/urls/anchorize-vs-urlize.md @@ -0,0 +1,35 @@ +--- +_comment: Do not remove front matter. +--- + +The [`anchorize`][] and [`urlize`][] functions are similar: + +- Use the `anchorize` function to generate an HTML `id` attribute value +- Use the `urlize` function to sanitize a string for usage in a URL + +For example: + +```go-html-template +{{ $s := "A B C" }} +{{ $s | anchorize }} → a-b-c +{{ $s | urlize }} → a-b-c + +{{ $s := "a b c" }} +{{ $s | anchorize }} → a-b---c +{{ $s | urlize }} → a-b-c + +{{ $s := "< a, b, & c >" }} +{{ $s | anchorize }} → -a-b--c- +{{ $s | urlize }} → a-b-c + +{{ $s := "main.go" }} +{{ $s | anchorize }} → maingo +{{ $s | urlize }} → main.go + +{{ $s := "Hugö" }} +{{ $s | anchorize }} → hugö +{{ $s | urlize }} → hug%C3%B6 +``` + +[`anchorize`]: /functions/urls/anchorize/ +[`urlize`]: /functions/urls/urlize/ diff --git a/docs/content/en/_common/gitignore-public.md b/docs/content/en/_common/gitignore-public.md new file mode 100644 index 0000000..7802347 --- /dev/null +++ b/docs/content/en/_common/gitignore-public.md @@ -0,0 +1,8 @@ +--- +_comment: Do not remove front matter. +--- + +> [!NOTE] +> Do not commit the contents of the [`publishDir`][] directory to your repository. Hugo recreates this directory when you build your project. + +[`publishDir`]: /configuration/all/#publishdir diff --git a/docs/content/en/_common/glob-patterns.md b/docs/content/en/_common/glob-patterns.md new file mode 100644 index 0000000..d3092de --- /dev/null +++ b/docs/content/en/_common/glob-patterns.md @@ -0,0 +1,23 @@ +--- +_comment: Do not remove front matter. +--- + +Path|Pattern|Match +:--|:--|:-- +`images/foo/a.jpg`|`images/foo/*.jpg`|`true` +`images/foo/a.jpg`|`images/foo/*.*`|`true` +`images/foo/a.jpg`|`images/foo/*`|`true` +`images/foo/a.jpg`|`images/*/*.jpg`|`true` +`images/foo/a.jpg`|`images/*/*.*`|`true` +`images/foo/a.jpg`|`images/*/*`|`true` +`images/foo/a.jpg`|`*/*/*.jpg`|`true` +`images/foo/a.jpg`|`*/*/*.*`|`true` +`images/foo/a.jpg`|`*/*/*`|`true` +`images/foo/a.jpg`|`**/*.jpg`|`true` +`images/foo/a.jpg`|`**/*.*`|`true` +`images/foo/a.jpg`|`**/*`|`true` +`images/foo/a.jpg`|`**`|`true` +`images/foo/a.jpg`|`*/*.jpg`|`false` +`images/foo/a.jpg`|`*.jpg`|`false` +`images/foo/a.jpg`|`*.*`|`false` +`images/foo/a.jpg`|`*`|`false` diff --git a/docs/content/en/_common/gomodules-info.md b/docs/content/en/_common/gomodules-info.md new file mode 100644 index 0000000..8312356 --- /dev/null +++ b/docs/content/en/_common/gomodules-info.md @@ -0,0 +1,16 @@ +--- +_comment: Do not remove front matter. +--- + +> [!NOTE] Hugo modules are Go modules +> You need [Go][] version 1.18 or later and [Git][] to use Hugo modules. For older sites hosted on Netlify, please ensure the `GO_VERSION` environment variable is set to `1.18` or higher. +> +> Go module resources: +> +> - [go.dev/wiki/Modules][] +> - [blog.golang.org/using-go-modules][] + +[Git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git +[Go]: https://go.dev/doc/install +[blog.golang.org/using-go-modules]: https://go.dev/blog/using-go-modules +[go.dev/wiki/Modules]: https://go.dev/wiki/Modules diff --git a/docs/content/en/_common/installation/01-editions.md b/docs/content/en/_common/installation/01-editions.md new file mode 100644 index 0000000..e16258d --- /dev/null +++ b/docs/content/en/_common/installation/01-editions.md @@ -0,0 +1,23 @@ +--- +_comment: Do not remove front matter. +--- + +## Editions + +Hugo is available in several editions. Use the standard edition unless you need additional features. + +Feature|standard|deploy (1)|extended|extended/deploy +:--|:-:|:-:|:-:|:-: +Core features|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark: +Direct cloud deployment (2)|:x:|:heavy_check_mark:|:x:|:heavy_check_mark: +LibSass support (3)|:x:|:x:|:heavy_check_mark:|:heavy_check_mark: + +(1) {{< new-in v0.159.2 />}} + +(2) Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details][]. + +(3) [Transpile Sass to CSS][] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass][] transpiler instead, which is compatible with any edition. + +[Dart Sass]: /functions/css/sass/#dart-sass +[Transpile Sass to CSS]: /functions/css/sass/ +[details]: /host-and-deploy/deploy-with-hugo-deploy/ diff --git a/docs/content/en/_common/installation/02-prerequisites.md b/docs/content/en/_common/installation/02-prerequisites.md new file mode 100644 index 0000000..63803dd --- /dev/null +++ b/docs/content/en/_common/installation/02-prerequisites.md @@ -0,0 +1,43 @@ +--- +_comment: Do not remove front matter. +--- + +## Prerequisites + +Although not required in all cases, [Git][], [Go][], and [Dart Sass][] are commonly used when working with Hugo. + +Git is required to: + +- Build Hugo from source +- Use [Hugo modules][] +- Install a theme as a Git submodule +- Access [commit information][] from a local Git repository +- Host your site on [CI/CD](g) platforms such as [Cloudflare][], [GitHub Pages][], [GitLab Pages][], [Netlify][], [Render][], or [Vercel][] + +Go is required to: + +- Build Hugo from source +- Use Hugo modules + +Dart Sass is required to transpile Sass to CSS when using the latest features of the Sass language. + +Please refer to the relevant documentation for installation instructions: + +- [Git][git install] +- [Go][go install] +- [Dart Sass][dart sass install] + +[Cloudflare]: /host-and-deploy/host-on-cloudflare/ +[Dart Sass]: https://sass-lang.com/dart-sass +[GitHub Pages]: /host-and-deploy/host-on-github-pages/ +[GitLab Pages]: /host-and-deploy/host-on-gitlab-pages/ +[Git]: https://git-scm.com/ +[Go]: https://go.dev/ +[Hugo modules]: /hugo-modules/ +[Netlify]: /host-and-deploy/host-on-netlify/ +[Render]: /host-and-deploy/host-on-render/ +[Vercel]: /host-and-deploy/host-on-vercel/ +[commit information]: /methods/page/GitInfo/ +[dart sass install]: /functions/css/sass/#dart-sass +[git install]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git +[go install]: https://go.dev/doc/install diff --git a/docs/content/en/_common/installation/03-prebuilt-binaries.md b/docs/content/en/_common/installation/03-prebuilt-binaries.md new file mode 100644 index 0000000..c5544cb --- /dev/null +++ b/docs/content/en/_common/installation/03-prebuilt-binaries.md @@ -0,0 +1,19 @@ +--- +_comment: Do not remove front matter. +--- + +## Prebuilt binaries + +Prebuilt binaries are available for a variety of operating systems and architectures. Visit the [latest release][] page, and scroll down to the Assets section. + +1. Download the archive for the desired edition, operating system, and architecture +1. Extract the archive +1. Move the executable to the desired directory +1. Add this directory to the PATH environment variable +1. Verify that you have _execute_ permission on the file + +Please consult your operating system documentation if you need help setting file permissions or modifying your PATH environment variable. + +If you do not see a prebuilt binary for the desired edition, operating system, and architecture, install Hugo using one of the methods described below. + +[latest release]: https://github.com/gohugoio/hugo/releases/latest diff --git a/docs/content/en/_common/installation/04-build-from-source.md b/docs/content/en/_common/installation/04-build-from-source.md new file mode 100644 index 0000000..e6b9d57 --- /dev/null +++ b/docs/content/en/_common/installation/04-build-from-source.md @@ -0,0 +1,49 @@ +--- +_comment: Do not remove front matter. +--- + +## Build from source + +To build Hugo from source you must install: + +1. [Git][] +1. [Go][] version {{% current-go-version %}} or later + +### Standard edition + +To build and install the standard edition: + +```sh +CGO_ENABLED=0 go install github.com/gohugoio/hugo@latest +``` + +### Deploy edition + +{{< new-in v0.159.2 />}} + +To build and install the deploy edition: + +```sh +CGO_ENABLED=0 go install -tags withdeploy github.com/gohugoio/hugo@latest +``` + +### Extended edition + +To build and install the extended edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command: + +```sh +CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest +``` + +### Extended/deploy edition + +To build and install the extended/deploy edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command: + +```sh +CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest +``` + +[Clang]: https://clang.llvm.org/ +[GCC]: https://gcc.gnu.org/ +[Git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git +[Go]: https://go.dev/doc/install diff --git a/docs/content/en/_common/installation/homebrew.md b/docs/content/en/_common/installation/homebrew.md new file mode 100644 index 0000000..6dacb40 --- /dev/null +++ b/docs/content/en/_common/installation/homebrew.md @@ -0,0 +1,13 @@ +--- +_comment: Do not remove front matter. +--- + +### Homebrew + +[Homebrew][] is a free and open-source package manager for macOS and Linux. To install the extended/deploy edition of Hugo: + +```sh +brew install hugo +``` + +[Homebrew]: https://brew.sh/ diff --git a/docs/content/en/_common/menu-entries/pre-and-post.md b/docs/content/en/_common/menu-entries/pre-and-post.md new file mode 100644 index 0000000..03a2162 --- /dev/null +++ b/docs/content/en/_common/menu-entries/pre-and-post.md @@ -0,0 +1,39 @@ +--- +_comment: Do not remove front matter. +--- + +In this project configuration we enable rendering of [emoji shortcodes][], and add emoji shortcodes before (pre) and after (post) each menu entry: + +{{< code-toggle file=hugo >}} +enableEmoji = true + +[[menus.main]] +name = 'About' +pageRef = '/about' +post = ':point_left:' +pre = ':point_right:' +weight = 10 + +[[menus.main]] +name = 'Contact' +pageRef = '/contact' +post = ':arrow_left:' +pre = ':arrow_right:' +weight = 20 +{{< /code-toggle >}} + +To render the menu: + +```go-html-template + +``` + +[emoji shortcodes]: /quick-reference/emojis/ diff --git a/docs/content/en/_common/menu-entry-properties.md b/docs/content/en/_common/menu-entry-properties.md new file mode 100644 index 0000000..ae49263 --- /dev/null +++ b/docs/content/en/_common/menu-entry-properties.md @@ -0,0 +1,31 @@ +--- +_comment: Do not remove front matter. +--- + + + +`identifier` +: (`string`) Required when two or more menu entries have the same `name`, or when localizing the `name` using translation tables. Must start with a letter, followed by letters, digits, or underscores. + +`name` +: (`string`) The text to display when rendering the menu entry. + +`params` +: (`map`) User-defined properties for the menu entry. + +`parent` +: (`string`) The `identifier` of the parent menu entry. If `identifier` is not defined, use `name`. Required for child entries in a nested menu. + +`post` +: (`string`) The HTML to append when rendering the menu entry. + +`pre` +: (`string`) The HTML to prepend when rendering the menu entry. + +`title` +: (`string`) The HTML `title` attribute of the rendered menu entry. + +`weight` +: (`int`) A non-zero integer indicating the entry's position relative the root of the menu, or to its parent for a child entry. Lighter entries float to the top, while heavier entries sink to the bottom. diff --git a/docs/content/en/_common/methods/media-type/core-methods.md b/docs/content/en/_common/methods/media-type/core-methods.md new file mode 100644 index 0000000..075dc4b --- /dev/null +++ b/docs/content/en/_common/methods/media-type/core-methods.md @@ -0,0 +1,18 @@ +--- +_comment: Do not remove front matter. +--- + +`Type` +: (`string`) Returns the media type. + +`MainType` +: (`string`) Returns the main type of the media type. + +`SubType` +: (`string`) Returns the subtype of the media type. + +`Suffixes` +: (`slice`) Returns a slice of possible file suffixes for the media type. + +`FirstSuffix.Suffix` +: (`string`) Returns the first of the possible file suffixes for the media type. diff --git a/docs/content/en/_common/methods/output-formats/to-use-this-method.md b/docs/content/en/_common/methods/output-formats/to-use-this-method.md new file mode 100644 index 0000000..027e89b --- /dev/null +++ b/docs/content/en/_common/methods/output-formats/to-use-this-method.md @@ -0,0 +1,9 @@ +--- +_comment: Do not remove front matter. +--- + +To use this method you must first select a specific [output format](g) from a page's [`OutputFormats`][] collection using the [`Get`][] or [`Canonical`][] methods. + +[`Canonical`]: /methods/page/outputformats/#canonical +[`Get`]: /methods/page/outputformats/#get +[`OutputFormats`]: /methods/page/outputformats/ diff --git a/docs/content/en/_common/methods/page/next-and-prev.md b/docs/content/en/_common/methods/page/next-and-prev.md new file mode 100644 index 0000000..18230fc --- /dev/null +++ b/docs/content/en/_common/methods/page/next-and-prev.md @@ -0,0 +1,59 @@ +--- +_comment: Do not remove front matter. +--- + +Hugo determines the _next_ and _previous_ page by sorting the site's collection of regular pages according to this sorting hierarchy: + +Field|Precedence|Sort direction +:--|:--|:-- +[`weight`][]|1|descending +[`date`][]|2|descending +[`linkTitle`][]|3|descending +[`path`][]|4|descending + +The sorted page collection used to determine the _next_ and _previous_ page is independent of other page collections, which may lead to unexpected behavior. + +For example, with this content structure: + +```tree +content/ +├── pages/ +│ ├── _index.md +│ ├── page-1.md <-- front matter: weight = 10 +│ ├── page-2.md <-- front matter: weight = 20 +│ └── page-3.md <-- front matter: weight = 30 +└── _index.md +``` + +And these templates: + +```go-html-template {file="layouts/section.html"} +{{ range .Pages.ByWeight }} +

    {{ .LinkTitle }}

    +{{ end }} +``` + +```go-html-template {file="layouts/page.html"} +{{ with .Prev }} + Previous +{{ end }} + +{{ with .Next }} + Next +{{ end }} +``` + +When you visit page-2: + +- The `Prev` method points to page-3 +- The `Next` method points to page-1 + +To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [project configuration][], or use the [`Next`][] and [`Prev`][] methods on a `Pages` object for more flexibility. + +[`Next`]: /methods/pages/next/ +[`Prev`]: /methods/pages/prev/ +[`date`]: /methods/page/date/ +[`linkTitle`]: /methods/page/linktitle/ +[`path`]: /methods/page/path/ +[`weight`]: /methods/page/weight/ +[project configuration]: /configuration/page/ diff --git a/docs/content/en/_common/methods/page/nextinsection-and-previnsection.md b/docs/content/en/_common/methods/page/nextinsection-and-previnsection.md new file mode 100644 index 0000000..45c8e3c --- /dev/null +++ b/docs/content/en/_common/methods/page/nextinsection-and-previnsection.md @@ -0,0 +1,77 @@ +--- +_comment: Do not remove front matter. +--- + +Hugo determines the _next_ and _previous_ page by sorting the current section's regular pages according to this sorting hierarchy: + +Field|Precedence|Sort direction +:--|:--|:-- +[`weight`][]|1|descending +[`date`][]|2|descending +[`linkTitle`][]|3|descending +[`path`][]|4|descending + +The sorted page collection used to determine the _next_ and _previous_ page is independent of other page collections, which may lead to unexpected behavior. + +For example, with this content structure: + +```tree +content/ +├── pages/ +│ ├── _index.md +│ ├── page-1.md <-- front matter: weight = 10 +│ ├── page-2.md <-- front matter: weight = 20 +│ └── page-3.md <-- front matter: weight = 30 +└── _index.md +``` + +And these templates: + +```go-html-template {file="layouts/section.html"} +{{ range .Pages.ByWeight }} +

    {{ .LinkTitle }}

    +{{ end }} +``` + +```go-html-template {file="layouts/page.html"} +{{ with .PrevInSection }} + Previous +{{ end }} + +{{ with .NextInSection }} + Next +{{ end }} +``` + +When you visit page-2: + +- The `PrevInSection` method points to page-3 +- The `NextInSection` method points to page-1 + +To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [project configuration][], or use the [`Next`][] and [`Prev`][] methods on a `Pages` object for more flexibility. + +## Example + +Code defensively by checking for page existence: + +```go-html-template +{{ with .PrevInSection }} + Previous +{{ end }} + +{{ with .NextInSection }} + Next +{{ end }} +``` + +## Alternative + +Use the [`Next`][] and [`Prev`][] methods on a `Pages` object for more flexibility. + +[`Next`]: /methods/pages/next/ +[`Prev`]: /methods/pages/prev/ +[`date`]: /methods/page/date/ +[`linkTitle`]: /methods/page/linktitle/ +[`path`]: /methods/page/path/ +[`weight`]: /methods/page/weight/ +[project configuration]: /configuration/page/ diff --git a/docs/content/en/_common/methods/pages/group-sort-order.md b/docs/content/en/_common/methods/pages/group-sort-order.md new file mode 100644 index 0000000..e2997a1 --- /dev/null +++ b/docs/content/en/_common/methods/pages/group-sort-order.md @@ -0,0 +1,5 @@ +--- +_comment: Do not remove front matter. +--- + +For the optional sort order, specify either `asc` for ascending order, or `desc` for descending order. diff --git a/docs/content/en/_common/methods/pages/next-and-prev.md b/docs/content/en/_common/methods/pages/next-and-prev.md new file mode 100644 index 0000000..06a1cdf --- /dev/null +++ b/docs/content/en/_common/methods/pages/next-and-prev.md @@ -0,0 +1,71 @@ +--- +_comment: Do not remove front matter. +--- + +Hugo determines the _next_ and _previous_ page by sorting the page collection according to this sorting hierarchy: + +Field|Precedence|Sort direction +:--|:--|:-- +[`weight`][]|1|descending +[`date`][]|2|descending +[`linkTitle`][]|3|descending +[`path`][]|4|descending + +The sorted page collection used to determine the _next_ and _previous_ page is independent of other page collections, which may lead to unexpected behavior. + +For example, with this content structure: + +```tree +content/ +├── pages/ +│ ├── _index.md +│ ├── page-1.md <-- front matter: weight = 10 +│ ├── page-2.md <-- front matter: weight = 20 +│ └── page-3.md <-- front matter: weight = 30 +└── _index.md +``` + +And these templates: + +```go-html-template {file="layouts/section.html"} +{{ range .Pages.ByWeight }} +

    {{ .LinkTitle }}

    +{{ end }} +``` + +```go-html-template {file="layouts/page.html"} +{{ $pages := .CurrentSection.Pages.ByWeight }} + +{{ with $pages.Prev . }} + Previous +{{ end }} + +{{ with $pages.Next . }} + Next +{{ end }} +``` + +When you visit page-2: + +- The `Prev` method points to page-3 +- The `Next` method points to page-1 + +To reverse the meaning of _next_ and _previous_ you can chain the [`Reverse`][] method to the page collection definition: + +```go-html-template {file="layouts/page.html"} +{{ $pages := .CurrentSection.Pages.ByWeight.Reverse }} + +{{ with $pages.Prev . }} + Previous +{{ end }} + +{{ with $pages.Next . }} + Next +{{ end }} +``` + +[`Reverse`]: /methods/pages/reverse/ +[`date`]: /methods/page/date/ +[`linkTitle`]: /methods/page/linktitle/ +[`path`]: /methods/page/path/ +[`weight`]: /methods/page/weight/ diff --git a/docs/content/en/_common/methods/resource/global-page-remote-resources.md b/docs/content/en/_common/methods/resource/global-page-remote-resources.md new file mode 100644 index 0000000..154b4d5 --- /dev/null +++ b/docs/content/en/_common/methods/resource/global-page-remote-resources.md @@ -0,0 +1,6 @@ +--- +_comment: Do not remove front matter. +--- + +> [!NOTE] +> Use this method with [global resources](g), [page resources](g), or [remote resources](g). diff --git a/docs/content/en/_common/methods/resource/processing-spec.md b/docs/content/en/_common/methods/resource/processing-spec.md new file mode 100644 index 0000000..6aa018e --- /dev/null +++ b/docs/content/en/_common/methods/resource/processing-spec.md @@ -0,0 +1,67 @@ +--- +_comment: Do not remove front matter. +--- + +## Processing specification + +The processing specification is a space-delimited, case-insensitive list containing one or more of the following options in any sequence: + +action +: Specify one of `crop`, `fill`, `fit`, or `resize`. This is applicable to the [`Process`][] method and the [`images.Process`][] filter. If you specify an action, you must also provide [`dimensions`](#dimensions). + +anchor +: The focal point used when cropping or filling an image. Valid options include `TopLeft`, `Top`, `TopRight`, `Left`, `Center`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`, or `Smart`. The `Smart` option utilizes the [`muesli/smartcrop`][] package to identify the most interesting area of the image. This defaults to the `anchor` setting in your [imaging configuration][]. + +background color +: The background color used when converting transparent images to formats that do not support transparency, such as PNG to JPEG. This color also fills the empty space created when rotating an image by a non-orthogonal angle if the space is not transparent and a background color is not specified in the processing specification. The value must be an RGB [hexadecimal color][]. This defaults to the `bgColor` setting in your [imaging configuration][]. + +compression +: {{< new-in 0.153.5 />}} +: The encoding strategy, applicable to AVIF and WebP images. Options are `lossy` or `lossless`. This defaults to the format-specific `compression` setting in your [imaging configuration][]. + +dimensions +: The dimensions of the resulting image, in pixels. The format is `WIDTHxHEIGHT` where `WIDTH` and `HEIGHT` are whole numbers. When resizing an image, you may specify only the width (such as `600x`) or only the height (such as `x400`) for proportional scaling. Specifying both width and height when resizing an image may result in non-proportional scaling. When cropping, fitting, or filling, you must provide both width and height such as `600x400`. + +format +: The format of the resulting image. Valid options include `avif`, `bmp`, `gif`, `jpeg`, `png`, `tiff`, or `webp`. This defaults to the format of the source image. + +hint +: The content hint, applicable to AVIF and WebP images. Valid options include `drawing`, `icon`, `photo`, `picture`, or `text`. This defaults to the format-specific `hint` setting in your [imaging configuration][]. + + Value|Example + :--|:-- + `drawing`|Hand or line drawing with high-contrast details + `icon`|Small colorful image + `photo`|Outdoor photograph with natural lighting + `picture`|Indoor photograph such as a portrait + `text`|Image that is primarily text + +quality +: The visual fidelity, applicable to JPEG images and to AVIF and WebP images when using `lossy` compression. The format is `qQUALITY` where `QUALITY` is a whole number between `1` and `100`, inclusive. Lower numbers prioritize smaller file size, while higher numbers prioritize visual clarity. This defaults to the format-specific `quality` setting in your [imaging configuration][]. + +resampling filter +: The algorithm used to calculate new pixels when resizing, fitting, or filling an image. Common options include `box`, `lanczos`, `catmullRom`, `mitchellNetravali`, `linear`, or `nearestNeighbor`. This defaults to the `resampleFilter` setting in your [imaging configuration][]. + + Filter|Description + :--|:-- + `box`|Simple and fast averaging filter appropriate for downscaling + `lanczos`|High-quality resampling filter for photographic images yielding sharp results + `catmullRom`|Sharp cubic filter that is faster than the Lanczos filter while providing similar results + `mitchellNetravali`|Cubic filter that produces smoother results with less ringing artifacts than CatmullRom + `linear`|Bilinear resampling filter, produces smooth output, faster than cubic filters + `nearestNeighbor`|Fastest resampling filter, no antialiasing + + Refer to the [source documentation][] for a complete list of available resampling filters. If you wish to improve image quality at the expense of performance, you may wish to experiment with the alternative filters. + +rotation +: The number of whole degrees to rotate an image counter-clockwise. The format is `rDEGREES` where `DEGREES` is a whole number. Hugo performs rotation before any other transformations, so your [target dimensions](#dimensions) and any [anchor](#anchor) should refer to the image orientation after rotation. Use `r90`, `r180`, or `r270` for orthogonal rotations, or arbitrary angles such as `r45`. To rotate clockwise, use a negative number such as `r-45`. To automatically rotate an image based on its Exif orientation tag, use the [`images.AutoOrient`][] filter instead of manual rotation. + + Rotating by non-orthogonal values increases the image extents to fit the rotated corners. For formats supporting alpha channels such as AVIF, PNG, or WebP, this resulting empty space is transparent by default. If the target format does not support transparency such as JPEG, or if you explicitly specify a [background color](#background-color) in the processing specification, the space is filled. If a color is required but not specified in the processing string, it defaults to the `bgColor` setting in your [imaging configuration][]. + +[`Process`]: /methods/resource/process/ +[`images.AutoOrient`]: /functions/images/autoorient/ +[`images.Process`]: /functions/images/process/ +[`muesli/smartcrop`]: https://github.com/muesli/smartcrop +[hexadecimal color]: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color +[imaging configuration]: /configuration/imaging/ +[source documentation]: https://github.com/disintegration/imaging#image-resizing diff --git a/docs/content/en/_common/methods/taxonomy/get-a-taxonomy-object.md b/docs/content/en/_common/methods/taxonomy/get-a-taxonomy-object.md new file mode 100644 index 0000000..bfc8a29 --- /dev/null +++ b/docs/content/en/_common/methods/taxonomy/get-a-taxonomy-object.md @@ -0,0 +1,66 @@ +--- +_comment: Do not remove front matter. +--- + +Before we can use a `Taxonomy` method, we need to capture a `Taxonomy` object. + +## Capture a Taxonomy object + +Consider this project configuration: + +{{< code-toggle file=hugo >}} +[taxonomies] +genre = 'genres' +author = 'authors' +{{< /code-toggle >}} + +And this content structure: + +```tree +content/ +├── books/ +│ ├── and-then-there-were-none.md --> genres: suspense +│ ├── death-on-the-nile.md --> genres: suspense +│ └── jamaica-inn.md --> genres: suspense, romance +│ └── pride-and-prejudice.md --> genres: romance +└── _index.md +``` + +To capture the "genres" `Taxonomy` object from within any template, use the [`Taxonomies`][] method on a `Site` object. + +```go-html-template +{{ $taxonomyObject := .Site.Taxonomies.genres }} +``` + +To capture the "genres" `Taxonomy` object when rendering its page with a _taxonomy_ template, use the [`Terms`][] method on the page's [`Data`][] object: + +```go-html-template {file="layouts/taxonomy.html"} +{{ $taxonomyObject := .Data.Terms }} +``` + +To inspect the data structure: + +```go-html-template +
    {{ debug.Dump $taxonomyObject }}
    +``` + +Although the [`Alphabetical`][] and [`ByCount`][] methods provide a better data structure for ranging through the taxonomy, you can render the weighted pages by term directly from the `Taxonomy` object: + +```go-html-template +{{ range $term, $weightedPages := $taxonomyObject }} +

    {{ .Page.LinkTitle }}

    + +{{ end }} +``` + +In the example above, the first anchor element is a link to the term page. + +[`Alphabetical`]: /methods/taxonomy/alphabetical/ +[`ByCount`]: /methods/taxonomy/bycount/ +[`Data`]: /methods/page/data/ +[`Taxonomies`]: /methods/site/taxonomies/ +[`Terms`]: /methods/page/data/#in-a-taxonomy-template diff --git a/docs/content/en/_common/methods/taxonomy/ordered-taxonomy-element-methods.md b/docs/content/en/_common/methods/taxonomy/ordered-taxonomy-element-methods.md new file mode 100644 index 0000000..07afadb --- /dev/null +++ b/docs/content/en/_common/methods/taxonomy/ordered-taxonomy-element-methods.md @@ -0,0 +1,24 @@ +--- +_comment: Do not remove front matter. +--- + +An ordered taxonomy is a slice, where each element is an object that contains the term and a slice of its weighted pages. + +Each element of the slice provides these methods: + +`Count` +: (`int`) Returns the number of pages to which the term is assigned. + +`Page` +: (`page.Page`) Returns the term's `Page` object, useful for linking to the term page. + +`Pages` +: (`page.Pages`) Returns a `Pages` object containing the `Page` objects to which the term is assigned, sorted by [taxonomic weight](g). To sort or group, use any of the [methods][] available to the `Pages` object. For example, sort by the last modification date. + +`Term` +: (`string`) Returns the term name. + +`WeightedPages` +: (`page.WeightedPages`) Returns a slice of weighted pages to which the term is assigned, sorted by taxonomic weight. The `Pages` method above is more flexible, allowing you to sort and group. + +[methods]: /methods/pages/ diff --git a/docs/content/en/_common/parsable-date-time-strings.md b/docs/content/en/_common/parsable-date-time-strings.md new file mode 100644 index 0000000..9284276 --- /dev/null +++ b/docs/content/en/_common/parsable-date-time-strings.md @@ -0,0 +1,14 @@ +--- +_comment: Do not remove front matter. +--- + +Format|Time zone +:--|:-- +`2023-10-15T13:18:50-07:00`|`America/Los_Angeles` +`2023-10-15T13:18:50-0700`|`America/Los_Angeles` +`2023-10-15T13:18:50Z`|`Etc/UTC` +`2023-10-15T13:18:50`|Default is `Etc/UTC` +`2023-10-15`|Default is `Etc/UTC` +`15 Oct 2023`|Default is `Etc/UTC` + +The last three examples are not fully qualified, and default to the `Etc/UTC` time zone. diff --git a/docs/content/en/_common/permalink-tokens.md b/docs/content/en/_common/permalink-tokens.md new file mode 100644 index 0000000..bf14e09 --- /dev/null +++ b/docs/content/en/_common/permalink-tokens.md @@ -0,0 +1,70 @@ +--- +_comment: Do not remove front matter. +--- + +`:year` +: The 4-digit year as defined in the front matter `date` field. + +`:month` +: The 2-digit month as defined in the front matter `date` field. + +`:monthname` +: The name of the month as defined in the front matter `date` field. + +`:day` +: The 2-digit day as defined in the front matter `date` field. + +`:weekday` +: The 1-digit day of the week as defined in the front matter `date` field (Sunday = `0`). + +`:weekdayname` +: The name of the day of the week as defined in the front matter `date` field. + +`:yearday` +: The 1- to 3-digit day of the year as defined in the front matter `date` field. + +`:section` +: The content's section. + +`:sectionslug` +: {{< new-in 0.149.0 />}} +: The content's section using slugified section name. The slugified section name is the `slug` as defined in front matter, else the `title` as defined in front matter, else the automatic title. + +`:sections` +: The content's sections hierarchy. You can use a selection of the sections using _slice syntax_: `:sections[1:]` includes all but the first, `:sections[:last]` includes all but the last, `:sections[last]` includes only the last, `:sections[1:2]` includes section 2 and 3. Note that this slice access will not throw any out-of-bounds errors, so you don't have to be exact. + +`:sectionslugs` +: {{< new-in 0.149.0 />}} +: The content's sections hierarchy using slugified section names. The slugified section name is the `slug` as defined in front matter, else the `title` as defined in front matter, else the automatic title. You can use a selection of the sections using _slice syntax_: `:sectionslugs[1:]` includes all but the first, `:sectionslugs[:last]` includes all but the last, `:sectionslugs[last]` includes only the last, `:sectionslugs[1:2]` includes section 2 and 3. Note that this slice access will not throw any out-of-bounds errors, so you don't have to be exact. + +`:title` +: The `title` as defined in front matter, else the automatic title. Hugo generates titles automatically for section, taxonomy, and term pages that are not backed by a file. + +`:slug` +: The `slug` as defined in front matter, else the `title` as defined in front matter, else the automatic title. Hugo generates titles automatically for section, taxonomy, and term pages that are not backed by a file. + +`:filename` +: {{< deprecated-in v0.144.0 />}} +: Use the [`:contentbasename`](#contentbasename) token instead. + +`:slugorfilename` +: {{< deprecated-in v0.144.0 />}} +: Use the [`:slugorcontentbasename`](#slugorcontentbasename) token instead. + +`:contentbasename` +: {{< new-in 0.144.0 />}} +: The [content base name][]. + +`:slugorcontentbasename` +: {{< new-in 0.144.0 />}} +: The `slug` as defined in front matter, else the [content base name][]. + +For time-related values, you can also use the layout string components defined in Go's [time package][]. For example: + +{{< code-toggle file=hugo >}} +permalinks: + posts: /:06/:1/:2/:title/ +{{< /code-toggle >}} + +[content base name]: /methods/page/file/#contentbasename +[time package]: https://pkg.go.dev/time#pkg-constants diff --git a/docs/content/en/_common/ref-and-relref-error-handling.md b/docs/content/en/_common/ref-and-relref-error-handling.md new file mode 100644 index 0000000..9ec185d --- /dev/null +++ b/docs/content/en/_common/ref-and-relref-error-handling.md @@ -0,0 +1,10 @@ +--- +_comment: Do not remove front matter. +--- + +By default, Hugo will throw an error and fail the build if it cannot resolve the path. You can change this to a warning in your project configuration, and specify a URL to return when the path cannot be resolved. + +{{< code-toggle file=hugo >}} +refLinksErrorLevel = 'warning' +refLinksNotFoundURL = '/some/other/url' +{{< /code-toggle >}} diff --git a/docs/content/en/_common/ref-and-relref-options.md b/docs/content/en/_common/ref-and-relref-options.md new file mode 100644 index 0000000..79808c3 --- /dev/null +++ b/docs/content/en/_common/ref-and-relref-options.md @@ -0,0 +1,12 @@ +--- +_comment: Do not remove front matter. +--- + +`path` +: (`string`) The path to the target page. Paths without a leading slash (`/`) are resolved first relative to the current page, and then relative to the rest of the site. + +`lang` +: (`string`) The language of the target page. Default is the current language. Optional. + +`outputFormat` +: (`string`) The output format of the target page. Default is the current output format. Optional. diff --git a/docs/content/en/_common/render-hooks/pageinner.md b/docs/content/en/_common/render-hooks/pageinner.md new file mode 100644 index 0000000..dc9e59d --- /dev/null +++ b/docs/content/en/_common/render-hooks/pageinner.md @@ -0,0 +1,45 @@ +--- +_comment: Do not remove front matter. +--- + +## PageInner details + +The primary use case for `PageInner` is to resolve links and [page resources](g) relative to an included `Page`. For example, create an "include" shortcode to compose a page from multiple content files, while preserving a global context for footnotes and the table of contents: + +```go-html-template {file="layouts/_shortcodes/include.html" copy=true} +{{ with .Get 0 }} + {{ with $.Page.GetPage . }} + {{- .RenderShortcodes }} + {{ else }} + {{ errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} + {{ end }} +{{ else }} + {{ errorf "The %q shortcode requires a positional parameter indicating the logical path of the file to include. See %s" .Name .Position }} +{{ end }} +``` + +Then call the shortcode in your Markdown: + +```md {file="content/posts/post-1.md"} +{{%/* include "/posts/post-2" */%}} +``` + +Any render hook triggered while rendering `/posts/post-2` will get: + +- `/posts/post-1` when calling `Page` +- `/posts/post-2` when calling `PageInner` + +`PageInner` falls back to the value of `Page` if not relevant, and always returns a value. + +> [!NOTE] +> The `PageInner` method is only relevant for shortcodes that invoke the [`RenderShortcodes`][] method, and you must call the shortcode using [Markdown notation][]. + +As a practical example, Hugo's embedded link and image render hooks use the `PageInner` method to resolve markdown link and image destinations. See the source code for each: + +- [Embedded link render hook][] +- [Embedded image render hook][] + +[Embedded image render hook]: <{{% eturl render-image %}}> +[Embedded link render hook]: <{{% eturl render-link %}}> +[Markdown notation]: /content-management/shortcodes/#notation +[`RenderShortcodes`]: /methods/page/rendershortcodes/ diff --git a/docs/content/en/_common/scratch-pad-scope.md b/docs/content/en/_common/scratch-pad-scope.md new file mode 100644 index 0000000..b63877c --- /dev/null +++ b/docs/content/en/_common/scratch-pad-scope.md @@ -0,0 +1,21 @@ +--- +_comment: Do not remove front matter. +--- + +## Scope + +The method or function used to create the data structure determines its scope. For example, use the `Store` method on a `Page` object to create a data structure scoped to the page. + +Scope|Method or function +:--|:-- +page|[`PAGE.Store`][] +site|[`SITE.Store`][] +global|[`hugo.Store`][] +local|[`collections.NewScratch`][] +shortcode|[`SHORTCODE.Store`][] + +[`PAGE.Store`]: /methods/page/store/ +[`SHORTCODE.Store`]: /methods/shortcode/store/ +[`SITE.Store`]: /methods/site/store/ +[`collections.NewScratch`]: /functions/collections/newscratch/ +[`hugo.Store`]: /functions/hugo/store/ diff --git a/docs/content/en/_common/store-methods.md b/docs/content/en/_common/store-methods.md new file mode 100644 index 0000000..d632c10 --- /dev/null +++ b/docs/content/en/_common/store-methods.md @@ -0,0 +1,81 @@ +--- +# Do not remove front matter. +--- + +## Methods + +Use these methods on the data structure. + +`Set` +: Sets the value of the given key. + + ```go-html-template + {{ .Store.Set "greeting" "Hello" }} + ``` + +`Get` +: (`any`) Gets the value of the given key. + + ```go-html-template + {{ .Store.Set "greeting" "Hello" }} + {{ .Store.Get "greeting" }} → Hello + ``` + +`Add` +: Adds the given value to the existing value(s) of the given key. + + For single values, `Add` accepts values that support Go's `+` operator. If the first `Add` for a key is an array or slice, the following adds will be appended to that list. + + ```go-html-template + {{ .Store.Set "greeting" "Hello" }} + {{ .Store.Add "greeting" "Welcome" }} + {{ .Store.Get "greeting" }} → HelloWelcome + ``` + + ```go-html-template + {{ .Store.Set "total" 3 }} + {{ .Store.Add "total" 7 }} + {{ .Store.Get "total" }} → 10 + ``` + + ```go-html-template + {{ .Store.Set "greetings" (slice "Hello") }} + {{ .Store.Add "greetings" (slice "Welcome" "Cheers") }} + {{ .Store.Get "greetings" }} → [Hello Welcome Cheers] + ``` + +`SetInMap` +: Takes a `key`, `mapKey` and `value` and adds a map of `mapKey` and `value` to the given `key`. + + ```go-html-template + {{ .Store.SetInMap "greetings" "english" "Hello" }} + {{ .Store.SetInMap "greetings" "french" "Bonjour" }} + {{ .Store.Get "greetings" }} → map[english:Hello french:Bonjour] + ``` + +`DeleteInMap` +: Takes a `key` and `mapKey` and removes the map of `mapKey` from the given `key`. + + ```go-html-template + {{ .Store.SetInMap "greetings" "english" "Hello" }} + {{ .Store.SetInMap "greetings" "french" "Bonjour" }} + {{ .Store.DeleteInMap "greetings" "english" }} + {{ .Store.Get "greetings" }} → map[french:Bonjour] + ``` + +`GetSortedMapValues` +: (`[]any`) Returns an array of values from `key` sorted by `mapKey`. + + ```go-html-template + {{ .Store.SetInMap "greetings" "english" "Hello" }} + {{ .Store.SetInMap "greetings" "french" "Bonjour" }} + {{ .Store.GetSortedMapValues "greetings" }} → [Hello Bonjour] + ``` + +`Delete` +: Removes the given key. + + ```go-html-template + {{ .Store.Set "greeting" "Hello" }} + {{ .Store.Delete "greeting" }} + ``` diff --git a/docs/content/en/_common/store-scope.md b/docs/content/en/_common/store-scope.md new file mode 100644 index 0000000..b63877c --- /dev/null +++ b/docs/content/en/_common/store-scope.md @@ -0,0 +1,21 @@ +--- +_comment: Do not remove front matter. +--- + +## Scope + +The method or function used to create the data structure determines its scope. For example, use the `Store` method on a `Page` object to create a data structure scoped to the page. + +Scope|Method or function +:--|:-- +page|[`PAGE.Store`][] +site|[`SITE.Store`][] +global|[`hugo.Store`][] +local|[`collections.NewScratch`][] +shortcode|[`SHORTCODE.Store`][] + +[`PAGE.Store`]: /methods/page/store/ +[`SHORTCODE.Store`]: /methods/shortcode/store/ +[`SITE.Store`]: /methods/site/store/ +[`collections.NewScratch`]: /functions/collections/newscratch/ +[`hugo.Store`]: /functions/hugo/store/ diff --git a/docs/content/en/_common/syntax-highlighting-options.md b/docs/content/en/_common/syntax-highlighting-options.md new file mode 100644 index 0000000..2f57cb0 --- /dev/null +++ b/docs/content/en/_common/syntax-highlighting-options.md @@ -0,0 +1,57 @@ +--- +_comment: Do not remove front matter. +--- + +`anchorLineNos` +: (`bool`) Whether to render each line number as an HTML anchor element, setting the `id` attribute of the surrounding `span` element to the line number. Irrelevant if `lineNos` is `false`. Default is `false`. + +`codeFences` +: (`bool`) Whether to highlight fenced code blocks. Default is `true`. + +`guessSyntax` +: (`bool`) Whether to automatically detect the language if the `LANG` argument is blank or set to a language for which there is no corresponding [lexer](g). Falls back to a plain text lexer if unable to automatically detect the language. Default is `false`. + + > [!NOTE] + > The syntax highlighter includes lexers for approximately 300 languages, but only 5 of these have implemented automatic language detection. + +`hl_Lines` +: (`string`) A space-delimited list of lines to emphasize within the highlighted code. To emphasize lines 2, 3, 4, and 7, set this value to `2-4 7`. This option is independent of the `lineNoStart` option. + +`hl_inline` +: (`bool`) Whether to render the highlighted code without a wrapping container. Default is `false`. + +`lineAnchors` +: (`string`) When rendering a line number as an HTML anchor element, prepend this value to the `id` attribute of the surrounding `span` element. This provides unique `id` attributes when a page contains two or more code blocks. Irrelevant if `lineNos` or `anchorLineNos` is `false`. + +`lineNoStart` +: (`int`) The number to display at the beginning of the first line. Irrelevant if `lineNos` is `false`. Default is `1`. + +`lineNos` +: (`any`) Controls line number display. Default is `false`. + + - `true`: Enable line numbers, controlled by `lineNumbersInTable`. + - `false`: Disable line numbers. + - `inline`: Enable inline line numbers (sets `lineNumbersInTable` to `false`). + - `table`: Enable table-based line numbers (sets `lineNumbersInTable` to `true`). + +`lineNumbersInTable` +: (`bool`) Whether to render the highlighted code in an HTML table with two cells. The left table cell contains the line numbers, while the right table cell contains the code. Irrelevant if `lineNos` is `false`. Default is `true`. + +`noClasses` +: (`bool`) Whether to use inline CSS styles instead of an external CSS file. Default is `true`. To use an external CSS file, set this value to `false` and generate the CSS file from the command line: + + ```sh + hugo gen chromastyles --style=monokai > syntax.css + ``` + +`style` +: (`string`) The CSS styles to apply to the highlighted code. This value is case-insensitive. Default is `monokai`. See [syntax highlighting styles][]. + +`tabWidth` +: (`int`) Substitute this number of spaces for each tab character in your highlighted code. Irrelevant if `noClasses` is `false`. Default is `4`. + +`wrapperClass` +: {{< new-in 0.140.2 />}} +: (`string`) The class or classes to use for the outermost element of the highlighted code. Default is `highlight`. + +[syntax highlighting styles]: /quick-reference/syntax-highlighting-styles/ diff --git a/docs/content/en/_common/time-layout-string.md b/docs/content/en/_common/time-layout-string.md new file mode 100644 index 0000000..f07a322 --- /dev/null +++ b/docs/content/en/_common/time-layout-string.md @@ -0,0 +1,46 @@ +--- +_comment: Do not remove front matter. +--- + +Format a `time.Time` value based on [Go's reference time][]: + +```text +Mon Jan 2 15:04:05 MST 2006 +``` + +Create a layout string using these components: + +Description|Valid components +:--|:-- +Year|`"2006" "06"` +Month|`"Jan" "January" "01" "1"` +Day of the week|`"Mon" "Monday"` +Day of the month|`"2" "_2" "02"` +Day of the year|`"__2" "002"` +Hour|`"15" "3" "03"` +Minute|`"4" "04"` +Second|`"5" "05"` +AM/PM mark|`"PM"` +Time zone offsets|`"-0700" "-07:00" "-07" "-070000" "-07:00:00"` + +Replace the sign in the layout string with a Z to print Z instead of an offset for the UTC zone. + +Description|Valid components +:--|:-- +Time zone offsets|`"Z0700" "Z07:00" "Z07" "Z070000" "Z07:00:00"` + +```go-html-template +{{ $t := "2023-01-27T23:44:58-08:00" }} +{{ $t = time.AsTime $t }} +{{ $t = $t.Format "Jan 02, 2006 3:04 PM Z07:00" }} + +{{ $t }} → Jan 27, 2023 11:44 PM -08:00 +``` + +Strings such as `PST` and `CET` are not time zones. They are time zone _abbreviations_. + +Strings such as `-07:00` and `+01:00` are not time zones. They are time zone _offsets_. + +A time zone is a geographic area with the same local time. For example, the time zone abbreviated by `PST` and `PDT` (depending on Daylight Savings Time) is `America/Los_Angeles`. + +[Go's reference time]: https://pkg.go.dev/time#pkg-constants diff --git a/docs/content/en/_index.md b/docs/content/en/_index.md new file mode 100644 index 0000000..358f6a4 --- /dev/null +++ b/docs/content/en/_index.md @@ -0,0 +1,4 @@ +--- +title: The world's fastest framework for building websites +description: Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again. +--- diff --git a/docs/content/en/about/_index.md b/docs/content/en/about/_index.md new file mode 100644 index 0000000..e558009 --- /dev/null +++ b/docs/content/en/about/_index.md @@ -0,0 +1,9 @@ +--- +title: About Hugo +linkTitle: About +description: Learn about Hugo and its features, privacy protections, and security model. +categories: [] +keywords: [] +weight: 10 +aliases: [/about-hugo/,/docs/] +--- diff --git a/docs/content/en/about/features.md b/docs/content/en/about/features.md new file mode 100644 index 0000000..cb93db9 --- /dev/null +++ b/docs/content/en/about/features.md @@ -0,0 +1,140 @@ +--- +title: Features +description: Hugo's rich and powerful feature set provides the framework and tools to create static sites that build in seconds, often less. +categories: [] +keywords: [] +weight: 20 +--- + +## Framework + +[Multiplatform][] +: Install Hugo's single executable on Linux, macOS, Windows, and more. + +[Multilingual][] +: Localize your project for each language and region, including translations, images, dates, currencies, numbers, percentages, and collation sequence. Hugo's multilingual framework supports single-host and multihost configurations. + +[Output formats][] +: Render each page of your project to one or more output formats, with granular control by page kind, section, and path. While HTML is the default output format, you can add JSON, RSS, CSV, and more. For example, create a REST API to access content. + +[Templates][] +: Create templates using variables, functions, and methods to transform your content, resources, and data into a published page. While HTML templates are the most common, you can create templates for any output format. + +[Themes][] +: Reduce development time and cost by using one of the hundreds of themes contributed by the Hugo community. Themes are available for corporate sites, documentation projects, image portfolios, landing pages, personal and professional blogs, resumes, CVs, and more. + +[Modules][] +: Reduce development time and cost by creating or importing packaged combinations of archetypes, assets, content, data, templates, translation tables, static files, or configuration settings. A module may serve as the basis for a new project, or to augment an existing project. + +[Privacy][] +: Configure your project to help comply with regional privacy regulations. + +[Security][] +: Hugo's security model is based on the premise that template and configuration authors are trusted, but content authors are not. This model enables generation of HTML output safe against code injection. Other protections prevent "shelling out" to arbitrary applications, limit access to specific environment variables, prevent connections to arbitrary remote data sources, and more. + +## Content authoring + +[Content formats][] +: Create your content using Markdown, HTML, AsciiDoc, Emacs Org Mode, Pandoc, or reStructuredText. Markdown is the default content format, conforming to the [CommonMark][] and [GitHub Flavored Markdown][] specifications. + +[Markdown attributes][] +: Apply HTML attributes such as `class` and `id` to Markdown images and block elements including blockquotes, fenced code blocks, headings, horizontal rules, lists, paragraphs, and tables. + +[Markdown extensions][] +: Leverage the embedded Markdown extensions to create tables, definition lists, footnotes, task lists, inserted text, mark text, subscripts, superscripts, and more. + +[Markdown render hooks][] +: Override the conversion of Markdown to HTML when rendering blockquotes, fenced code blocks, headings, images, links, and tables. For example, render every standalone image as an HTML `figure` element. + +[Diagrams][] +: Use fenced code blocks and Markdown render hooks to include diagrams in your content. + +[Mathematics][] +: Include mathematical equations and expressions in Markdown using LaTeX markup. + +[Syntax highlighting][] +: Syntactically highlight code examples using Hugo's embedded syntax highlighter, enabled by default for fenced code blocks in Markdown. The syntax highlighter supports hundreds of code languages and dozens of styles. + +[Shortcodes][] +: Use Hugo's embedded shortcodes, or create your own, to insert complex content. For example, use shortcodes to include `audio` and `video` elements, render tables from local or remote data sources, insert snippets from other pages, and more. + +## Content management + +[Multidimensional content model][] +: Generate pages across any combination of language, version, and role from a single source. This allows a single piece of content to be published to multiple [sites](g) within your project, removing the need to duplicate files for different audiences or versions. + +[Content adapters][] +: Create content adapters to dynamically add content when building your project. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML. + +[Taxonomies][] +: Classify content to establish simple or complex logical relationships between pages. For example, create an authors taxonomy, and assign one or more authors to each page. Among other uses, the taxonomy system provides an inverted, weighted index to render a list of related pages, ordered by relevance. + +[Data][] +: Augment your content using local or remote data sources including CSV, JSON, TOML, YAML, and XML. For example, create a shortcode to render an HTML table from a remote CSV file. + +[Menus][] +: Provide rapid access to content via Hugo's menu system, configured automatically, globally, or on a page-by-page basis. The menu system is a key component of Hugo's multilingual architecture. + +[URL management][] +: Serve any page from any path via global configuration or on a page-by-page basis. + +## Asset pipelines + +[CSS Processing][] +: Bundle, transform, minify, create source maps, perform SRI hashing, and integrate with PostCSS. + +[Image processing][] +: Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract metadata. + +[JavaScript bundling][] +: Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing. + +[Sass processing][] +: Transpile Sass to CSS, bundle, tree shake, minify, create source maps, perform SRI hashing, and integrate with PostCSS. + +[Tailwind CSS processing][] +: Compile Tailwind CSS utility classes into standard CSS, bundle, tree shake, optimize, minify, perform SRI hashing, and integrate with PostCSS. + +## Performance + +[Caching][] +: Reduce build time and cost by rendering a _partial_ template once then cache the result, either globally or within a given context. For example, cache the result of an asset pipeline to prevent reprocessing on every rendered page. + +[Segmentation][] +: Reduce build time and cost by partitioning your sites into segments. For example, render the home page and the "news section" every hour, and render the entire project once a week. + +[Minification][] +: Minify HTML, CSS, and JavaScript to reduce file size, bandwidth consumption, and loading times. + +[CSS Processing]: /functions/css/build/ +[Caching]: /functions/partials/includecached/ +[CommonMark]: https://spec.commonmark.org/current/ +[Content adapters]: /content-management/content-adapters/ +[Content formats]: /content-management/formats/ +[Data]: /content-management/data-sources/ +[Diagrams]: /content-management/diagrams/ +[GitHub Flavored Markdown]: https://github.github.com/gfm/ +[Image processing]: /content-management/image-processing/ +[JavaScript bundling]: /functions/js/build/ +[Markdown attributes]: /content-management/markdown-attributes/ +[Markdown extensions]: /configuration/markup/#extensions +[Markdown render hooks]: /render-hooks/introduction/ +[Mathematics]: /content-management/mathematics/ +[Menus]: /content-management/menus/ +[Minification]: /configuration/minify/ +[Modules]: /hugo-modules/ +[Multidimensional content model]: /quick-reference/glossary/#sites-matrix +[Multilingual]: /content-management/multilingual/ +[Multiplatform]: /installation/ +[Output formats]: /configuration/output-formats/ +[Privacy]: /configuration/privacy/ +[Sass processing]: /functions/css/sass/ +[Security]: /about/security/ +[Segmentation]: /configuration/segments/ +[Shortcodes]: /content-management/shortcodes/ +[Syntax highlighting]: /content-management/syntax-highlighting/ +[Tailwind CSS processing]: /functions/css/tailwindcss/ +[Taxonomies]: /content-management/taxonomies/ +[Templates]: /templates/introduction/ +[Themes]: https://themes.gohugo.io/ +[URL management]: /content-management/urls/ diff --git a/docs/content/en/about/introduction.md b/docs/content/en/about/introduction.md new file mode 100644 index 0000000..2a046a7 --- /dev/null +++ b/docs/content/en/about/introduction.md @@ -0,0 +1,34 @@ +--- +title: Introduction +description: Hugo is a static site generator written in Go, optimized for speed and designed for flexibility. +categories: [] +keywords: [] +weight: 10 +aliases: [/about/what-is-hugo/,/about/benefits/] +--- + +Hugo is a [static site generator][] written in [Go][], optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less. + +Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create: + +- Corporate, government, nonprofit, education, news, event, and project sites +- Documentation sites +- Image portfolios +- Landing pages +- Business, professional, and personal blogs +- Resumes and CVs + +Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. Then deploy the site to your host, or push changes to your Git provider for automated builds and deployment. + +And with [modules][] you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories. + +Learn more about Hugo's [features][], [privacy protections][], and [security model][]. + +{{< youtube 0RKpf3rK57I >}} + +[Go]: https://go.dev +[features]: /about/features/ +[modules]: /hugo-modules/ +[privacy protections]: /configuration/privacy/ +[security model]: /about/security/ +[static site generator]: https://en.wikipedia.org/wiki/Static_site_generator diff --git a/docs/content/en/about/license.md b/docs/content/en/about/license.md new file mode 100644 index 0000000..cd190da --- /dev/null +++ b/docs/content/en/about/license.md @@ -0,0 +1,75 @@ +--- +title: License +description: Hugo is released under the Apache 2.0 license. +categories: [] +keywords: [] +weight: 40 +--- + +## Apache License + +_Version 2.0, January 2004_ +__ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means **(i)** the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/docs/content/en/about/security.md b/docs/content/en/about/security.md new file mode 100644 index 0000000..6ffd423 --- /dev/null +++ b/docs/content/en/about/security.md @@ -0,0 +1,63 @@ +--- +title: Security model +linkTitle: Security +description: A summary of Hugo's security model. +categories: [] +keywords: [] +weight: 30 +aliases: [/about/security-model/] +--- + +## Security Boundaries + +- The templates inside `layouts` are trusted. +- The assets inside `archetypes`, `assets`, `resources`, `data`, `i18n` and `static` are trusted. +- The content and the content produced by [content adapters][] inside `content` is not trusted. The one exception here is if [inline shortcodes][] is enabled. Note that for content adapters, this is scoped to the result of the adapter. +- The development server, `hugo server`, and its livereload script is trusted and meant for _local_ development only. + +## Runtime security + +Hugo generates static websites, meaning the final output runs directly in the browser and interacts with any integrated APIs. However, during development and site building, the `hugo` executable itself is the runtime environment. + +Securing a runtime is a complex task. Hugo addresses this through a robust sandboxing approach and a strict security policy with default protections. Key features include: + +- Virtual file system: Hugo employs a virtual file system, limiting file access. Only the main project, not external components, can access files or directories outside the project root. +- Read-Only access: User-defined components have read-only access to the file system, preventing unintended modifications. +- Controlled external binaries: While Hugo utilizes external binaries for features like Asciidoctor support, these are strictly predefined with specific flags and are disabled by default. The [security policy][] details these limitations. +- No arbitrary commands: To mitigate risks, Hugo intentionally avoids implementing general functions that would allow users to execute arbitrary operating system commands. +- Pragmatic defaults: The default [security policy][] aims to balance security and usability, enabling common workflows out of the box while keeping more sensitive capabilities opt-in. These defaults may be tightened in future releases, but each project is ultimately responsible for reviewing the policy and adjusting it to match its own trust model and requirements. + +This combination of sandboxing and strict defaults effectively minimizes potential security vulnerabilities during the Hugo build process. + +## Dependency security + +Hugo utilizes [Go modules][] to manage its dependencies, compiling as a static binary. Go modules create a `go.sum` file, a critical security feature. This file acts as a database, storing the expected cryptographic checksums of all dependencies, including those required indirectly (transitive dependencies). + +[Hugo modules][], which extend the functionality of Go modules, also produce a `go.sum` file. To ensure dependency integrity, commit this `go.sum` file to your version control. If Hugo detects a checksum mismatch during the build process, it will fail, indicating a possible attempt to [tamper with your project's dependencies][]. + +## Web application security + +Hugo's security philosophy is rooted in established security standards, primarily aligning with the threats defined by [OWASP][]. For HTML output, Hugo operates under a clear trust model. This model assumes that template and configuration authors, the developers, are trustworthy. However, the data supplied to these templates is inherently considered untrusted. This distinction is crucial for understanding how Hugo handles potential security risks. + +To prevent unintended escaping of data that developers know is safe, Hugo provides [`safe`][] functions, such as [`safe.HTML`][]. These functions allow developers to explicitly mark data as trusted, bypassing the default escaping mechanisms. This is essential for scenarios where data is generated or sourced from reliable sources. However, an exception exists: enabling [inline shortcodes][]. By activating this feature, you are implicitly trusting the logic within the shortcodes and the data contained within your content files. + +It's vital to remember that Hugo is a static site generator. This architectural choice significantly reduces the attack surface by eliminating the complexities and vulnerabilities associated with dynamic user input. Unlike dynamic websites, Hugo generates static HTML files, minimizing the risk of real-time attacks. Regarding content, Hugo's default Markdown renderer is [configured to sanitize][] potentially unsafe content. This default behavior ensures that potentially malicious code or scripts are removed or escaped. However, this setting can be reconfigured if you have a high degree of confidence in the safety of your content sources. + +In essence, Hugo prioritizes secure output by establishing a clear trust boundary between developers and data. By default, it errs on the side of caution, sanitizing potentially unsafe content and escaping data. Developers have the flexibility to adjust these defaults through [`safe`][] functions and [configuration settings][], but they must do so with a clear understanding of the security implications. Hugo's static site generation model further strengthens its security posture by minimizing dynamic vulnerabilities. + +## Configuration + +See [configure security][]. + +[Go modules]: https://go.dev/wiki/Modules#modules +[Hugo modules]: /hugo-modules/ +[OWASP]: https://en.wikipedia.org/wiki/OWASP +[`safe.HTML`]: /functions/safe/html/ +[`safe`]: /functions/safe/ +[configuration settings]: /configuration/security/ +[configure security]: /configuration/security/ +[configured to sanitize]: /configuration/markup/#rendererunsafe +[content adapters]: /content-management/content-adapters/ +[inline shortcodes]: /content-management/shortcodes/#inline +[security policy]: /configuration/security/ +[tamper with your project's dependencies]: https://julienrenaux.fr/2019/12/20/github-actions-security-risk/ diff --git a/docs/content/en/commands/_index.md b/docs/content/en/commands/_index.md new file mode 100644 index 0000000..b97b6e2 --- /dev/null +++ b/docs/content/en/commands/_index.md @@ -0,0 +1,8 @@ +--- +title: Command line interface +linkTitle: CLI +description: Use the command line interface (CLI) to manage your project. +categories: [] +keywords: [] +weight: 10 +--- diff --git a/docs/content/en/commands/hugo.md b/docs/content/en/commands/hugo.md new file mode 100644 index 0000000..5d3a349 --- /dev/null +++ b/docs/content/en/commands/hugo.md @@ -0,0 +1,83 @@ +--- +title: "hugo" +slug: hugo +url: /commands/hugo/ +--- +## hugo + +Build your project + +### Synopsis + +hugo is the main command, used to build your Hugo project. + +Hugo is a Fast and Flexible Static Site Generator +built with love by spf13 and friends in Go. + +Complete documentation is available at https://gohugo.io/. + +``` +hugo [flags] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + -D, --buildDrafts include content marked as draft + -E, --buildExpired include expired content + -F, --buildFuture include content with publishdate in the future + --cacheDir string filesystem path to cache directory + --cleanDestinationDir remove files from destination not found in static directories + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -c, --contentDir string filesystem path to content directory + -d, --destination string filesystem path to write files to + --disableKinds strings disable different kind of pages (home, RSS etc.) + --enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages + -e, --environment string build environment + --forceSyncStatic copy all files when static is changed. + --gc enable to run some cleanup tasks (remove unused cache files) after the build + -h, --help help for hugo + --ignoreCache ignore the configured file caches + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + -l, --layoutDir string filesystem path to layout directory + --logLevel string log level (debug|info|warn|error) + --minify minify any supported output format (HTML, XML etc.) + --noBuildLock don't create .hugo_build.lock file + --noChmod don't sync permission mode of files + --noTimes don't sync modification time of files + --panicOnWarning panic on first WARNING log + --poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes + --printI18nWarnings print missing translations + --printMemoryUsage print memory usage to screen at intervals + --printPathWarnings print warnings on duplicate target paths etc. + --printUnusedTemplates print warnings on unused templates. + --quiet build in quiet mode + --renderSegments strings named segments to render (configured in the segments config) + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --templateMetrics display metrics about template executions + --templateMetricsHints calculate some improvement hints when combined with --templateMetrics + -t, --theme strings themes to use (located in /themes/THEMENAME/) + --themesDir string filesystem path to themes directory + --trace file write trace to file (not useful in general) + -w, --watch watch filesystem for changes and recreate as needed +``` + +### SEE ALSO + +* [hugo build](/commands/hugo_build/) - Build your project +* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell +* [hugo config](/commands/hugo_config/) - Display project configuration +* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format +* [hugo deploy](/commands/hugo_deploy/) - Deploy your project to a cloud provider +* [hugo env](/commands/hugo_env/) - Display version and environment info +* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles +* [hugo import](/commands/hugo_import/) - Import a project from another system +* [hugo list](/commands/hugo_list/) - List content +* [hugo mod](/commands/hugo_mod/) - Manage modules +* [hugo new](/commands/hugo_new/) - Create new content +* [hugo server](/commands/hugo_server/) - Start the embedded web server +* [hugo version](/commands/hugo_version/) - Display version diff --git a/docs/content/en/commands/hugo_build.md b/docs/content/en/commands/hugo_build.md new file mode 100644 index 0000000..b9311aa --- /dev/null +++ b/docs/content/en/commands/hugo_build.md @@ -0,0 +1,71 @@ +--- +title: "hugo build" +slug: hugo_build +url: /commands/hugo_build/ +--- +## hugo build + +Build your project + +### Synopsis + +build is the main command, used to build your Hugo project. + +Hugo is a Fast and Flexible Static Site Generator +built with love by spf13 and friends in Go. + +Complete documentation is available at https://gohugo.io/. + +``` +hugo build [flags] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + -D, --buildDrafts include content marked as draft + -E, --buildExpired include expired content + -F, --buildFuture include content with publishdate in the future + --cacheDir string filesystem path to cache directory + --cleanDestinationDir remove files from destination not found in static directories + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -c, --contentDir string filesystem path to content directory + -d, --destination string filesystem path to write files to + --disableKinds strings disable different kind of pages (home, RSS etc.) + --enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages + -e, --environment string build environment + --forceSyncStatic copy all files when static is changed. + --gc enable to run some cleanup tasks (remove unused cache files) after the build + -h, --help help for build + --ignoreCache ignore the configured file caches + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + -l, --layoutDir string filesystem path to layout directory + --logLevel string log level (debug|info|warn|error) + --minify minify any supported output format (HTML, XML etc.) + --noBuildLock don't create .hugo_build.lock file + --noChmod don't sync permission mode of files + --noTimes don't sync modification time of files + --panicOnWarning panic on first WARNING log + --poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes + --printI18nWarnings print missing translations + --printMemoryUsage print memory usage to screen at intervals + --printPathWarnings print warnings on duplicate target paths etc. + --printUnusedTemplates print warnings on unused templates. + --quiet build in quiet mode + --renderSegments strings named segments to render (configured in the segments config) + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --templateMetrics display metrics about template executions + --templateMetricsHints calculate some improvement hints when combined with --templateMetrics + -t, --theme strings themes to use (located in /themes/THEMENAME/) + --themesDir string filesystem path to themes directory + --trace file write trace to file (not useful in general) + -w, --watch watch filesystem for changes and recreate as needed +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project diff --git a/docs/content/en/commands/hugo_completion.md b/docs/content/en/commands/hugo_completion.md new file mode 100644 index 0000000..34b4114 --- /dev/null +++ b/docs/content/en/commands/hugo_completion.md @@ -0,0 +1,45 @@ +--- +title: "hugo completion" +slug: hugo_completion +url: /commands/hugo_completion/ +--- +## hugo completion + +Generate the autocompletion script for the specified shell + +### Synopsis + +Generate the autocompletion script for hugo for the specified shell. +See each sub-command's help for details on how to use the generated script. + + +### Options + +``` + -h, --help help for completion +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo completion bash](/commands/hugo_completion_bash/) - Generate the autocompletion script for bash +* [hugo completion fish](/commands/hugo_completion_fish/) - Generate the autocompletion script for fish +* [hugo completion powershell](/commands/hugo_completion_powershell/) - Generate the autocompletion script for powershell +* [hugo completion zsh](/commands/hugo_completion_zsh/) - Generate the autocompletion script for zsh diff --git a/docs/content/en/commands/hugo_completion_bash.md b/docs/content/en/commands/hugo_completion_bash.md new file mode 100644 index 0000000..9f18169 --- /dev/null +++ b/docs/content/en/commands/hugo_completion_bash.md @@ -0,0 +1,64 @@ +--- +title: "hugo completion bash" +slug: hugo_completion_bash +url: /commands/hugo_completion_bash/ +--- +## hugo completion bash + +Generate the autocompletion script for bash + +### Synopsis + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + + source <(hugo completion bash) + +To load completions for every new session, execute once: + +#### Linux: + + hugo completion bash > /etc/bash_completion.d/hugo + +#### macOS: + + hugo completion bash > $(brew --prefix)/etc/bash_completion.d/hugo + +You will need to start a new shell for this setup to take effect. + + +``` +hugo completion bash +``` + +### Options + +``` + -h, --help help for bash + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell diff --git a/docs/content/en/commands/hugo_completion_fish.md b/docs/content/en/commands/hugo_completion_fish.md new file mode 100644 index 0000000..3015d23 --- /dev/null +++ b/docs/content/en/commands/hugo_completion_fish.md @@ -0,0 +1,55 @@ +--- +title: "hugo completion fish" +slug: hugo_completion_fish +url: /commands/hugo_completion_fish/ +--- +## hugo completion fish + +Generate the autocompletion script for fish + +### Synopsis + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + hugo completion fish | source + +To load completions for every new session, execute once: + + hugo completion fish > ~/.config/fish/completions/hugo.fish + +You will need to start a new shell for this setup to take effect. + + +``` +hugo completion fish [flags] +``` + +### Options + +``` + -h, --help help for fish + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell diff --git a/docs/content/en/commands/hugo_completion_powershell.md b/docs/content/en/commands/hugo_completion_powershell.md new file mode 100644 index 0000000..780f7fc --- /dev/null +++ b/docs/content/en/commands/hugo_completion_powershell.md @@ -0,0 +1,52 @@ +--- +title: "hugo completion powershell" +slug: hugo_completion_powershell +url: /commands/hugo_completion_powershell/ +--- +## hugo completion powershell + +Generate the autocompletion script for powershell + +### Synopsis + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + hugo completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + + +``` +hugo completion powershell [flags] +``` + +### Options + +``` + -h, --help help for powershell + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell diff --git a/docs/content/en/commands/hugo_completion_zsh.md b/docs/content/en/commands/hugo_completion_zsh.md new file mode 100644 index 0000000..e5743e9 --- /dev/null +++ b/docs/content/en/commands/hugo_completion_zsh.md @@ -0,0 +1,66 @@ +--- +title: "hugo completion zsh" +slug: hugo_completion_zsh +url: /commands/hugo_completion_zsh/ +--- +## hugo completion zsh + +Generate the autocompletion script for zsh + +### Synopsis + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions in your current shell session: + + source <(hugo completion zsh) + +To load completions for every new session, execute once: + +#### Linux: + + hugo completion zsh > "${fpath[1]}/_hugo" + +#### macOS: + + hugo completion zsh > $(brew --prefix)/share/zsh/site-functions/_hugo + +You will need to start a new shell for this setup to take effect. + + +``` +hugo completion zsh [flags] +``` + +### Options + +``` + -h, --help help for zsh + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell diff --git a/docs/content/en/commands/hugo_config.md b/docs/content/en/commands/hugo_config.md new file mode 100644 index 0000000..3de89f1 --- /dev/null +++ b/docs/content/en/commands/hugo_config.md @@ -0,0 +1,52 @@ +--- +title: "hugo config" +slug: hugo_config +url: /commands/hugo_config/ +--- +## hugo config + +Display project configuration + +### Synopsis + +Display project configuration, both default and custom settings. + +``` +hugo config [command] [flags] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + --format string preferred file format (toml, yaml or json) (default "toml") + -h, --help help for config + --lang string the language to display config for. Defaults to the first language defined. + --printZero include config options with zero values (e.g. false, 0, "") in the output + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo config mounts](/commands/hugo_config_mounts/) - Print the configured file mounts diff --git a/docs/content/en/commands/hugo_config_mounts.md b/docs/content/en/commands/hugo_config_mounts.md new file mode 100644 index 0000000..eb540ac --- /dev/null +++ b/docs/content/en/commands/hugo_config_mounts.md @@ -0,0 +1,44 @@ +--- +title: "hugo config mounts" +slug: hugo_config_mounts +url: /commands/hugo_config_mounts/ +--- +## hugo config mounts + +Print the configured file mounts + +``` +hugo config mounts [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for mounts + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo config](/commands/hugo_config/) - Display project configuration diff --git a/docs/content/en/commands/hugo_convert.md b/docs/content/en/commands/hugo_convert.md new file mode 100644 index 0000000..f293fa4 --- /dev/null +++ b/docs/content/en/commands/hugo_convert.md @@ -0,0 +1,46 @@ +--- +title: "hugo convert" +slug: hugo_convert +url: /commands/hugo_convert/ +--- +## hugo convert + +Convert front matter to another format + +### Synopsis + +Convert front matter to another format. + +See convert's subcommands toJSON, toTOML and toYAML for more information. + +### Options + +``` + -h, --help help for convert + -o, --output string filesystem path to write files to + --unsafe enable less safe operations, please backup first +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo convert toJSON](/commands/hugo_convert_tojson/) - Convert front matter to JSON +* [hugo convert toTOML](/commands/hugo_convert_totoml/) - Convert front matter to TOML +* [hugo convert toYAML](/commands/hugo_convert_toyaml/) - Convert front matter to YAML diff --git a/docs/content/en/commands/hugo_convert_toJSON.md b/docs/content/en/commands/hugo_convert_toJSON.md new file mode 100644 index 0000000..59a6376 --- /dev/null +++ b/docs/content/en/commands/hugo_convert_toJSON.md @@ -0,0 +1,46 @@ +--- +title: "hugo convert toJSON" +slug: hugo_convert_toJSON +url: /commands/hugo_convert_tojson/ +--- +## hugo convert toJSON + +Convert front matter to JSON + +### Synopsis + +toJSON converts all front matter in the content directory +to use JSON for the front matter. + +``` +hugo convert toJSON [flags] [args] +``` + +### Options + +``` + -h, --help help for toJSON +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + -o, --output string filesystem path to write files to + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory + --unsafe enable less safe operations, please backup first +``` + +### SEE ALSO + +* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format diff --git a/docs/content/en/commands/hugo_convert_toTOML.md b/docs/content/en/commands/hugo_convert_toTOML.md new file mode 100644 index 0000000..de48f71 --- /dev/null +++ b/docs/content/en/commands/hugo_convert_toTOML.md @@ -0,0 +1,46 @@ +--- +title: "hugo convert toTOML" +slug: hugo_convert_toTOML +url: /commands/hugo_convert_totoml/ +--- +## hugo convert toTOML + +Convert front matter to TOML + +### Synopsis + +toTOML converts all front matter in the content directory +to use TOML for the front matter. + +``` +hugo convert toTOML [flags] [args] +``` + +### Options + +``` + -h, --help help for toTOML +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + -o, --output string filesystem path to write files to + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory + --unsafe enable less safe operations, please backup first +``` + +### SEE ALSO + +* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format diff --git a/docs/content/en/commands/hugo_convert_toYAML.md b/docs/content/en/commands/hugo_convert_toYAML.md new file mode 100644 index 0000000..ae14e8e --- /dev/null +++ b/docs/content/en/commands/hugo_convert_toYAML.md @@ -0,0 +1,46 @@ +--- +title: "hugo convert toYAML" +slug: hugo_convert_toYAML +url: /commands/hugo_convert_toyaml/ +--- +## hugo convert toYAML + +Convert front matter to YAML + +### Synopsis + +toYAML converts all front matter in the content directory +to use YAML for the front matter. + +``` +hugo convert toYAML [flags] [args] +``` + +### Options + +``` + -h, --help help for toYAML +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + -o, --output string filesystem path to write files to + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory + --unsafe enable less safe operations, please backup first +``` + +### SEE ALSO + +* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format diff --git a/docs/content/en/commands/hugo_deploy.md b/docs/content/en/commands/hugo_deploy.md new file mode 100644 index 0000000..b8c08a3 --- /dev/null +++ b/docs/content/en/commands/hugo_deploy.md @@ -0,0 +1,54 @@ +--- +title: "hugo deploy" +slug: hugo_deploy +url: /commands/hugo_deploy/ +--- +## hugo deploy + +Deploy your project to a cloud provider + +### Synopsis + +Deploy your project to a cloud provider + +See https://gohugo.io/hosting-and-deployment/hugo-deploy/ for detailed +documentation. + + +``` +hugo deploy [flags] [args] +``` + +### Options + +``` + --confirm ask for confirmation before making changes to the target + --dryRun dry run + --force force upload of all files + -h, --help help for deploy + --invalidateCDN invalidate the CDN cache listed in the deployment target (default true) + --maxDeletes int maximum # of files to delete, or -1 to disable (default 256) + --target string target deployment from deployments section in config file; defaults to the first one + --workers int number of workers to transfer files. defaults to 10 (default 10) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project diff --git a/docs/content/en/commands/hugo_env.md b/docs/content/en/commands/hugo_env.md new file mode 100644 index 0000000..007eb2a --- /dev/null +++ b/docs/content/en/commands/hugo_env.md @@ -0,0 +1,43 @@ +--- +title: "hugo env" +slug: hugo_env +url: /commands/hugo_env/ +--- +## hugo env + +Display version and environment info + +### Synopsis + +Display version and environment info. This is useful in Hugo bug reports + +``` +hugo env [flags] [args] +``` + +### Options + +``` + -h, --help help for env +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project diff --git a/docs/content/en/commands/hugo_gen.md b/docs/content/en/commands/hugo_gen.md new file mode 100644 index 0000000..605a8fa --- /dev/null +++ b/docs/content/en/commands/hugo_gen.md @@ -0,0 +1,42 @@ +--- +title: "hugo gen" +slug: hugo_gen +url: /commands/hugo_gen/ +--- +## hugo gen + +Generate documentation and syntax highlighting styles + +### Synopsis + +Generate documentation for your project using Hugo's documentation engine, including syntax highlighting for various programming languages. + +### Options + +``` + -h, --help help for gen +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo gen chromastyles](/commands/hugo_gen_chromastyles/) - Generate CSS stylesheet for the Chroma code highlighter +* [hugo gen doc](/commands/hugo_gen_doc/) - Generate Markdown documentation for the Hugo CLI +* [hugo gen man](/commands/hugo_gen_man/) - Generate man pages for the Hugo CLI diff --git a/docs/content/en/commands/hugo_gen_chromastyles.md b/docs/content/en/commands/hugo_gen_chromastyles.md new file mode 100644 index 0000000..522d357 --- /dev/null +++ b/docs/content/en/commands/hugo_gen_chromastyles.md @@ -0,0 +1,51 @@ +--- +title: "hugo gen chromastyles" +slug: hugo_gen_chromastyles +url: /commands/hugo_gen_chromastyles/ +--- +## hugo gen chromastyles + +Generate CSS stylesheet for the Chroma code highlighter + +### Synopsis + +Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if markup.highlight.noClasses is disabled in config. + +See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles. + +``` +hugo gen chromastyles [flags] [args] +``` + +### Options + +``` + -h, --help help for chromastyles + --highlightStyle string foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff" + --lineNumbersInlineStyle string foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff" + --lineNumbersTableStyle string foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff" + --omitClassComments omit CSS class comment prefixes in the generated CSS + --omitEmpty omit empty CSS rules (deprecated, no longer needed) + --style string highlighter style (default "friendly") +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles diff --git a/docs/content/en/commands/hugo_gen_doc.md b/docs/content/en/commands/hugo_gen_doc.md new file mode 100644 index 0000000..28f0617 --- /dev/null +++ b/docs/content/en/commands/hugo_gen_doc.md @@ -0,0 +1,49 @@ +--- +title: "hugo gen doc" +slug: hugo_gen_doc +url: /commands/hugo_gen_doc/ +--- +## hugo gen doc + +Generate Markdown documentation for the Hugo CLI + +### Synopsis + +Generate Markdown documentation for the Hugo CLI. + This command is, mostly, used to create up-to-date documentation + of Hugo's command-line interface for https://gohugo.io/. + + It creates one Markdown file per command with front matter suitable + for rendering in Hugo. + +``` +hugo gen doc [flags] [args] +``` + +### Options + +``` + --dir string the directory to write the doc. (default "/tmp/hugodoc/") + -h, --help help for doc +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles diff --git a/docs/content/en/commands/hugo_gen_man.md b/docs/content/en/commands/hugo_gen_man.md new file mode 100644 index 0000000..7ebc9ff --- /dev/null +++ b/docs/content/en/commands/hugo_gen_man.md @@ -0,0 +1,46 @@ +--- +title: "hugo gen man" +slug: hugo_gen_man +url: /commands/hugo_gen_man/ +--- +## hugo gen man + +Generate man pages for the Hugo CLI + +### Synopsis + +This command automatically generates up-to-date man pages of Hugo's + command-line interface. By default, it creates the man page files + in the "man" directory under the current directory. + +``` +hugo gen man [flags] [args] +``` + +### Options + +``` + --dir string the directory to write the man pages. (default "man/") + -h, --help help for man +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles diff --git a/docs/content/en/commands/hugo_import.md b/docs/content/en/commands/hugo_import.md new file mode 100644 index 0000000..5e38a68 --- /dev/null +++ b/docs/content/en/commands/hugo_import.md @@ -0,0 +1,42 @@ +--- +title: "hugo import" +slug: hugo_import +url: /commands/hugo_import/ +--- +## hugo import + +Import a project from another system + +### Synopsis + +Import a project from another system. + +Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_path`. + +### Options + +``` + -h, --help help for import +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo import jekyll](/commands/hugo_import_jekyll/) - hugo import from Jekyll diff --git a/docs/content/en/commands/hugo_import_jekyll.md b/docs/content/en/commands/hugo_import_jekyll.md new file mode 100644 index 0000000..d1f16b9 --- /dev/null +++ b/docs/content/en/commands/hugo_import_jekyll.md @@ -0,0 +1,46 @@ +--- +title: "hugo import jekyll" +slug: hugo_import_jekyll +url: /commands/hugo_import_jekyll/ +--- +## hugo import jekyll + +hugo import from Jekyll + +### Synopsis + +hugo import from Jekyll. + +Import from Jekyll requires two paths, e.g. `hugo import jekyll jekyll_root_path target_path`. + +``` +hugo import jekyll [flags] [args] +``` + +### Options + +``` + --force allow import into non-empty target directory + -h, --help help for jekyll +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo import](/commands/hugo_import/) - Import a project from another system diff --git a/docs/content/en/commands/hugo_list.md b/docs/content/en/commands/hugo_list.md new file mode 100644 index 0000000..90b8e6d --- /dev/null +++ b/docs/content/en/commands/hugo_list.md @@ -0,0 +1,46 @@ +--- +title: "hugo list" +slug: hugo_list +url: /commands/hugo_list/ +--- +## hugo list + +List content + +### Synopsis + +List content. + +List requires a subcommand, e.g. hugo list drafts + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo list all](/commands/hugo_list_all/) - List all content +* [hugo list drafts](/commands/hugo_list_drafts/) - List draft content +* [hugo list expired](/commands/hugo_list_expired/) - List expired content +* [hugo list future](/commands/hugo_list_future/) - List future content +* [hugo list published](/commands/hugo_list_published/) - List published content diff --git a/docs/content/en/commands/hugo_list_all.md b/docs/content/en/commands/hugo_list_all.md new file mode 100644 index 0000000..6b324ab --- /dev/null +++ b/docs/content/en/commands/hugo_list_all.md @@ -0,0 +1,43 @@ +--- +title: "hugo list all" +slug: hugo_list_all +url: /commands/hugo_list_all/ +--- +## hugo list all + +List all content + +### Synopsis + +List all content including draft, future, and expired. + +``` +hugo list all [flags] [args] +``` + +### Options + +``` + -h, --help help for all +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo list](/commands/hugo_list/) - List content diff --git a/docs/content/en/commands/hugo_list_drafts.md b/docs/content/en/commands/hugo_list_drafts.md new file mode 100644 index 0000000..c6a8907 --- /dev/null +++ b/docs/content/en/commands/hugo_list_drafts.md @@ -0,0 +1,43 @@ +--- +title: "hugo list drafts" +slug: hugo_list_drafts +url: /commands/hugo_list_drafts/ +--- +## hugo list drafts + +List draft content + +### Synopsis + +List draft content. + +``` +hugo list drafts [flags] [args] +``` + +### Options + +``` + -h, --help help for drafts +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo list](/commands/hugo_list/) - List content diff --git a/docs/content/en/commands/hugo_list_expired.md b/docs/content/en/commands/hugo_list_expired.md new file mode 100644 index 0000000..b0618ca --- /dev/null +++ b/docs/content/en/commands/hugo_list_expired.md @@ -0,0 +1,43 @@ +--- +title: "hugo list expired" +slug: hugo_list_expired +url: /commands/hugo_list_expired/ +--- +## hugo list expired + +List expired content + +### Synopsis + +List content with a past expiration date. + +``` +hugo list expired [flags] [args] +``` + +### Options + +``` + -h, --help help for expired +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo list](/commands/hugo_list/) - List content diff --git a/docs/content/en/commands/hugo_list_future.md b/docs/content/en/commands/hugo_list_future.md new file mode 100644 index 0000000..4f2fdca --- /dev/null +++ b/docs/content/en/commands/hugo_list_future.md @@ -0,0 +1,43 @@ +--- +title: "hugo list future" +slug: hugo_list_future +url: /commands/hugo_list_future/ +--- +## hugo list future + +List future content + +### Synopsis + +List content with a future publication date. + +``` +hugo list future [flags] [args] +``` + +### Options + +``` + -h, --help help for future +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo list](/commands/hugo_list/) - List content diff --git a/docs/content/en/commands/hugo_list_published.md b/docs/content/en/commands/hugo_list_published.md new file mode 100644 index 0000000..a497687 --- /dev/null +++ b/docs/content/en/commands/hugo_list_published.md @@ -0,0 +1,43 @@ +--- +title: "hugo list published" +slug: hugo_list_published +url: /commands/hugo_list_published/ +--- +## hugo list published + +List published content + +### Synopsis + +List content that is not draft, future, or expired. + +``` +hugo list published [flags] [args] +``` + +### Options + +``` + -h, --help help for published +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo list](/commands/hugo_list/) - List content diff --git a/docs/content/en/commands/hugo_mod.md b/docs/content/en/commands/hugo_mod.md new file mode 100644 index 0000000..5cc9eef --- /dev/null +++ b/docs/content/en/commands/hugo_mod.md @@ -0,0 +1,58 @@ +--- +title: "hugo mod" +slug: hugo_mod +url: /commands/hugo_mod/ +--- +## hugo mod + +Manage modules + +### Synopsis + +Various helpers to help manage the modules in your project's dependency graph. +Most operations here requires a Go version installed on your system (>= Go 1.12) and the relevant VCS client (typically Git). +This is not needed if you only operate on modules inside /themes or if you have vendored them via "hugo mod vendor". + + +Note that Hugo will always start out by resolving the components defined in the project +configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided), +Go Modules, or a folder inside the themes directory, in that order. + +See https://gohugo.io/hugo-modules/ for more information. + + + +### Options + +``` + -h, --help help for mod +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo mod clean](/commands/hugo_mod_clean/) - Delete the Hugo Module cache for the current project +* [hugo mod get](/commands/hugo_mod_get/) - Resolves dependencies in your current Hugo project +* [hugo mod graph](/commands/hugo_mod_graph/) - Print a module dependency graph +* [hugo mod init](/commands/hugo_mod_init/) - Initialize this project as a Hugo Module +* [hugo mod npm](/commands/hugo_mod_npm/) - Various npm helpers +* [hugo mod tidy](/commands/hugo_mod_tidy/) - Remove unused entries in go.mod and go.sum +* [hugo mod vendor](/commands/hugo_mod_vendor/) - Vendor all module dependencies into the _vendor directory +* [hugo mod verify](/commands/hugo_mod_verify/) - Verify dependencies diff --git a/docs/content/en/commands/hugo_mod_clean.md b/docs/content/en/commands/hugo_mod_clean.md new file mode 100644 index 0000000..9abb1cb --- /dev/null +++ b/docs/content/en/commands/hugo_mod_clean.md @@ -0,0 +1,50 @@ +--- +title: "hugo mod clean" +slug: hugo_mod_clean +url: /commands/hugo_mod_clean/ +--- +## hugo mod clean + +Delete the Hugo Module cache for the current project + +### Synopsis + +Delete the Hugo Module cache for the current project. + +``` +hugo mod clean [flags] [args] +``` + +### Options + +``` + --all clean entire module cache + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for clean + --pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*" + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_get.md b/docs/content/en/commands/hugo_mod_get.md new file mode 100644 index 0000000..a7eebd7 --- /dev/null +++ b/docs/content/en/commands/hugo_mod_get.md @@ -0,0 +1,74 @@ +--- +title: "hugo mod get" +slug: hugo_mod_get +url: /commands/hugo_mod_get/ +--- +## hugo mod get + +Resolves dependencies in your current Hugo project + +### Synopsis + + +Resolves dependencies in your current Hugo project. + +Some examples: + +Install the latest version possible for a given module: + + hugo mod get github.com/gohugoio/testshortcodes + +Install a specific version: + + hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 + +Install the latest versions of all direct module dependencies: + + hugo mod get + hugo mod get ./... (recursive) + +Install the latest versions of all module dependencies (direct and indirect): + + hugo mod get -u + hugo mod get -u ./... (recursive) + +Run "go help get" for more information. All flags available for "go get" is also relevant here. + +Note that Hugo will always start out by resolving the components defined in the project +configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided), +Go Modules, or a folder inside the themes directory, in that order. + +See https://gohugo.io/hugo-modules/ for more information. + + + +``` +hugo mod get [flags] [args] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_graph.md b/docs/content/en/commands/hugo_mod_graph.md new file mode 100644 index 0000000..2e84091 --- /dev/null +++ b/docs/content/en/commands/hugo_mod_graph.md @@ -0,0 +1,51 @@ +--- +title: "hugo mod graph" +slug: hugo_mod_graph +url: /commands/hugo_mod_graph/ +--- +## hugo mod graph + +Print a module dependency graph + +### Synopsis + +Print a module dependency graph with information about module status (disabled, vendored). +Note that for vendored modules, that is the version listed and not the one from go.mod. + + +``` +hugo mod graph [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + --clean delete module cache for dependencies that fail verification + -c, --contentDir string filesystem path to content directory + -h, --help help for graph + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_init.md b/docs/content/en/commands/hugo_mod_init.md new file mode 100644 index 0000000..05d785c --- /dev/null +++ b/docs/content/en/commands/hugo_mod_init.md @@ -0,0 +1,55 @@ +--- +title: "hugo mod init" +slug: hugo_mod_init +url: /commands/hugo_mod_init/ +--- +## hugo mod init + +Initialize this project as a Hugo Module + +### Synopsis + +Initialize this project as a Hugo Module. + It will try to guess the module path, but you may help by passing it as an argument, e.g: + + hugo mod init github.com/gohugoio/testshortcodes + + Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module + inside a subfolder on GitHub, as one example. + + +``` +hugo mod init [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for init + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_npm.md b/docs/content/en/commands/hugo_mod_npm.md new file mode 100644 index 0000000..e9bd9d9 --- /dev/null +++ b/docs/content/en/commands/hugo_mod_npm.md @@ -0,0 +1,44 @@ +--- +title: "hugo mod npm" +slug: hugo_mod_npm +url: /commands/hugo_mod_npm/ +--- +## hugo mod npm + +Various npm helpers + +### Synopsis + +Various npm (Node package manager) helpers. + +``` +hugo mod npm [command] [flags] +``` + +### Options + +``` + -h, --help help for npm +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules +* [hugo mod npm pack](/commands/hugo_mod_npm_pack/) - Merges module Node.js dependencies into an npm workspace diff --git a/docs/content/en/commands/hugo_mod_npm_pack.md b/docs/content/en/commands/hugo_mod_npm_pack.md new file mode 100644 index 0000000..4482a4d --- /dev/null +++ b/docs/content/en/commands/hugo_mod_npm_pack.md @@ -0,0 +1,56 @@ +--- +title: "hugo mod npm pack" +slug: hugo_mod_npm_pack +url: /commands/hugo_mod_npm_pack/ +--- +## hugo mod npm pack + +Merges module Node.js dependencies into an npm workspace + +### Synopsis + +Merges Node.js dependencies from all Hugo modules into a "packages/hugoautogen" npm workspace. + +The merged dependencies are written to packages/hugoautogen/package.json, and the root package.json +is updated with a "workspaces" entry pointing to "packages/hugoautogen". + +The source entries are read from either package.hugo.json or package.json in the module root, with package.hugo.json taking precedence if both exist. + +See [Node.js dependencies](/hugo-modules/nodejs-dependencies/) for more information. + + +``` +hugo mod npm pack [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for pack + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod npm](/commands/hugo_mod_npm/) - Various npm helpers diff --git a/docs/content/en/commands/hugo_mod_tidy.md b/docs/content/en/commands/hugo_mod_tidy.md new file mode 100644 index 0000000..1114772 --- /dev/null +++ b/docs/content/en/commands/hugo_mod_tidy.md @@ -0,0 +1,44 @@ +--- +title: "hugo mod tidy" +slug: hugo_mod_tidy +url: /commands/hugo_mod_tidy/ +--- +## hugo mod tidy + +Remove unused entries in go.mod and go.sum + +``` +hugo mod tidy [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for tidy + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_vendor.md b/docs/content/en/commands/hugo_mod_vendor.md new file mode 100644 index 0000000..4e828fe --- /dev/null +++ b/docs/content/en/commands/hugo_mod_vendor.md @@ -0,0 +1,50 @@ +--- +title: "hugo mod vendor" +slug: hugo_mod_vendor +url: /commands/hugo_mod_vendor/ +--- +## hugo mod vendor + +Vendor all module dependencies into the _vendor directory + +### Synopsis + +Vendor all module dependencies into the _vendor directory. + If a module is vendored, that is where Hugo will look for it's dependencies. + + +``` +hugo mod vendor [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + -h, --help help for vendor + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_mod_verify.md b/docs/content/en/commands/hugo_mod_verify.md new file mode 100644 index 0000000..6c15a88 --- /dev/null +++ b/docs/content/en/commands/hugo_mod_verify.md @@ -0,0 +1,49 @@ +--- +title: "hugo mod verify" +slug: hugo_mod_verify +url: /commands/hugo_mod_verify/ +--- +## hugo mod verify + +Verify dependencies + +### Synopsis + +Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded. + +``` +hugo mod verify [flags] [args] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + --clean delete module cache for dependencies that fail verification + -c, --contentDir string filesystem path to content directory + -h, --help help for verify + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo mod](/commands/hugo_mod/) - Manage modules diff --git a/docs/content/en/commands/hugo_new.md b/docs/content/en/commands/hugo_new.md new file mode 100644 index 0000000..a6587e4 --- /dev/null +++ b/docs/content/en/commands/hugo_new.md @@ -0,0 +1,49 @@ +--- +title: "hugo new" +slug: hugo_new +url: /commands/hugo_new/ +--- +## hugo new + +Create new content + +### Synopsis + +Create a new content file and automatically set the date and title. +It will guess which kind of file to create based on the path provided. + +You can also specify the kind with `-k KIND`. + +If archetypes are provided in your theme or project, they will be used. + +Ensure you run this within the root directory of your project. + +### Options + +``` + -h, --help help for new +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo new content](/commands/hugo_new_content/) - Create new content +* [hugo new project](/commands/hugo_new_project/) - Create a new project +* [hugo new theme](/commands/hugo_new_theme/) - Create a new theme diff --git a/docs/content/en/commands/hugo_new_content.md b/docs/content/en/commands/hugo_new_content.md new file mode 100644 index 0000000..0a5b543 --- /dev/null +++ b/docs/content/en/commands/hugo_new_content.md @@ -0,0 +1,58 @@ +--- +title: "hugo new content" +slug: hugo_new_content +url: /commands/hugo_new_content/ +--- +## hugo new content + +Create new content + +### Synopsis + +Create a new content file and automatically set the date and title. +It will guess which kind of file to create based on the path provided. + +You can also specify the kind with `-k KIND`. + +If archetypes are provided in your theme or project, they will be used. + +Ensure you run this within the root directory of your project. + +``` +hugo new content [path] [flags] +``` + +### Options + +``` + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --cacheDir string filesystem path to cache directory + -c, --contentDir string filesystem path to content directory + --editor string edit new content with this editor, if provided + -f, --force overwrite file if it already exists + -h, --help help for content + -k, --kind string content type to create + --renderSegments strings named segments to render (configured in the segments config) + -t, --theme strings themes to use (located in /themes/THEMENAME/) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo new](/commands/hugo_new/) - Create new content diff --git a/docs/content/en/commands/hugo_new_project.md b/docs/content/en/commands/hugo_new_project.md new file mode 100644 index 0000000..652eefe --- /dev/null +++ b/docs/content/en/commands/hugo_new_project.md @@ -0,0 +1,45 @@ +--- +title: "hugo new project" +slug: hugo_new_project +url: /commands/hugo_new_project/ +--- +## hugo new project + +Create a new project + +### Synopsis + +Create a new project at the specified path. + +``` +hugo new project [path] [flags] +``` + +### Options + +``` + -f, --force init inside non-empty directory + --format string preferred file format (toml, yaml or json) (default "toml") + -h, --help help for project +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo new](/commands/hugo_new/) - Create new content diff --git a/docs/content/en/commands/hugo_new_theme.md b/docs/content/en/commands/hugo_new_theme.md new file mode 100644 index 0000000..7a7e040 --- /dev/null +++ b/docs/content/en/commands/hugo_new_theme.md @@ -0,0 +1,45 @@ +--- +title: "hugo new theme" +slug: hugo_new_theme +url: /commands/hugo_new_theme/ +--- +## hugo new theme + +Create a new theme + +### Synopsis + +Create a new theme with the specified name in the ./themes directory. +This generates a functional theme including template examples and sample content. + +``` +hugo new theme [name] [flags] +``` + +### Options + +``` + --format string preferred file format (toml, yaml or json) (default "toml") + -h, --help help for theme +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo new](/commands/hugo_new/) - Create new content diff --git a/docs/content/en/commands/hugo_server.md b/docs/content/en/commands/hugo_server.md new file mode 100644 index 0000000..ec99b1a --- /dev/null +++ b/docs/content/en/commands/hugo_server.md @@ -0,0 +1,97 @@ +--- +title: "hugo server" +slug: hugo_server +url: /commands/hugo_server/ +--- +## hugo server + +Start the embedded web server + +### Synopsis + +Hugo provides its own webserver which builds and serves the project. +While hugo server is high performance, it is a webserver with limited options. + +The `hugo server` command will by default write and serve files from disk, but +you can render to memory by using the `--renderToMemory` flag. This can be +faster in some cases, but it will consume more memory. + +By default hugo will also watch your files for any changes you make and +automatically rebuild the project. It will then live reload any open browser pages +and push the latest content to them. As most Hugo projects are built in a fraction +of a second, you will be able to save and see your changes nearly instantly. + +``` +hugo server [command] [flags] +``` + +### Options + +``` + --appendPort append port to baseURL (default true) + -b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/ + --bind string interface to which the server will bind (default "127.0.0.1") + -D, --buildDrafts include content marked as draft + -E, --buildExpired include expired content + -F, --buildFuture include content with publishdate in the future + --cacheDir string filesystem path to cache directory + --cleanDestinationDir remove files from destination not found in static directories + -c, --contentDir string filesystem path to content directory + --disableBrowserError do not show build errors in the browser + --disableFastRender enables full re-renders on changes + --disableKinds strings disable different kind of pages (home, RSS etc.) + --disableLiveReload watch without enabling live browser reload on rebuild + --enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages + --forceSyncStatic copy all files when static is changed. + --gc enable to run some cleanup tasks (remove unused cache files) after the build + -h, --help help for server + --ignoreCache ignore the configured file caches + -l, --layoutDir string filesystem path to layout directory + --liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1) + --minify minify any supported output format (HTML, XML etc.) + -N, --navigateToChanged navigate to changed content file on live browser reload + --noChmod don't sync permission mode of files + --noHTTPCache disable browser caching of pages served by the embedded web server + --noTimes don't sync modification time of files + -O, --openBrowser open the project in a browser after server startup + --panicOnWarning panic on first WARNING log + --poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes + -p, --port int port on which the server will listen (default 1313) + --pprof enable the pprof server (port 8080) + --printI18nWarnings print missing translations + --printMemoryUsage print memory usage to screen at intervals + --printPathWarnings print warnings on duplicate target paths etc. + --printUnusedTemplates print warnings on unused templates. + --renderSegments strings named segments to render (configured in the segments config) + --renderStaticToDisk serve static files from disk and dynamic files from memory + --templateMetrics display metrics about template executions + --templateMetricsHints calculate some improvement hints when combined with --templateMetrics + -t, --theme strings themes to use (located in /themes/THEMENAME/) + --tlsAuto generate and use locally-trusted certificates. + --tlsCertFile string path to TLS certificate file + --tlsKeyFile string path to TLS key file + --trace file write trace to file (not useful in general) + -w, --watch watch filesystem for changes and recreate as needed (default true) +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project +* [hugo server trust](/commands/hugo_server_trust/) - Install the local CA in the system trust store diff --git a/docs/content/en/commands/hugo_server_trust.md b/docs/content/en/commands/hugo_server_trust.md new file mode 100644 index 0000000..8649838 --- /dev/null +++ b/docs/content/en/commands/hugo_server_trust.md @@ -0,0 +1,40 @@ +--- +title: "hugo server trust" +slug: hugo_server_trust +url: /commands/hugo_server_trust/ +--- +## hugo server trust + +Install the local CA in the system trust store + +``` +hugo server trust [flags] [args] +``` + +### Options + +``` + -h, --help help for trust + --uninstall Uninstall the local CA (but do not delete it). +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo server](/commands/hugo_server/) - Start the embedded web server diff --git a/docs/content/en/commands/hugo_version.md b/docs/content/en/commands/hugo_version.md new file mode 100644 index 0000000..e71abdc --- /dev/null +++ b/docs/content/en/commands/hugo_version.md @@ -0,0 +1,43 @@ +--- +title: "hugo version" +slug: hugo_version +url: /commands/hugo_version/ +--- +## hugo version + +Display version + +### Synopsis + +Display version and environment info. This is useful in Hugo bug reports. + +``` +hugo version [flags] [args] +``` + +### Options + +``` + -h, --help help for version +``` + +### Options inherited from parent commands + +``` + --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 + --config string config file (default is hugo.yaml|json|toml) + --configDir string config dir (default "config") + -d, --destination string filesystem path to write files to + -e, --environment string build environment + --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern + --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file + --quiet build in quiet mode + -M, --renderToMemory render to memory (mostly useful when running the server) + -s, --source string filesystem path to read files relative from + --themesDir string filesystem path to themes directory +``` + +### SEE ALSO + +* [hugo](/commands/hugo/) - Build your project diff --git a/docs/content/en/configuration/_index.md b/docs/content/en/configuration/_index.md new file mode 100644 index 0000000..7cb08cc --- /dev/null +++ b/docs/content/en/configuration/_index.md @@ -0,0 +1,7 @@ +--- +title: Configuration +description: Configure your site. +categories: [] +keywords: [] +weight: 10 +--- diff --git a/docs/content/en/configuration/all.md b/docs/content/en/configuration/all.md new file mode 100644 index 0000000..5e57a25 --- /dev/null +++ b/docs/content/en/configuration/all.md @@ -0,0 +1,416 @@ +--- +title: All settings +description: The complete list of Hugo configuration settings. +categories: [] +keywords: [] +weight: 20 +aliases: [/getting-started/configuration/] +--- + +## Settings + +`archetypeDir` +: (`string`) The designated directory for [archetypes](g). Default is `archetypes`. {{% module-mounts-note %}} + +`assetDir` +: (`string`) The designated directory for [global resources](g). Default is `assets`. {{% module-mounts-note %}} + +`baseURL` +: (`string`) The absolute URL of your published site including the protocol, host, path, and a trailing slash. + +`build` +: See [configure build][]. + +`buildDrafts` +: (`bool`) Whether to include draft content when building a site. Default is `false`. + +`buildExpired` +: (`bool`) Whether to include expired content when building a site. Default is `false`. + +`buildFuture` +: (`bool`) Whether to include future content when building a site. Default is `false`. + +`cacheDir` +: (`string`) The designated cache directory. See [details](#cache-directory). + +`caches` +: See [configure file caches][]. + +`canonifyURLs` +: (`bool`) See [details][canonical-urls] before enabling this feature. Default is `false`. + +`capitalizeListTitles` +: (`bool`) Whether to capitalize automatic list titles. Applicable to section, taxonomy, and term pages. Use the [`titleCaseStyle`](#titlecasestyle) setting to configure capitalization rules. Default is `true`. + +`cascade` +: See [configure cascade][]. + +`cleanDestinationDir` +: (`bool`) Whether to remove files from the [`publishDir`](#publishdir) that do not exist in the [`staticDir`](#staticdir) when building the site. This setting will not take effect if the `staticDir` does not exist. Note that `.gitignore` and `.gitattributes` files, along with directories named `.git`, are always preserved in the `publishDir`. Default is `false`. + +`contentDir` +: (`string`) The designated directory for content files. Default is `content`. {{% module-mounts-note %}} + +`copyright` +: (`string`) The copyright notice for a site, typically displayed in the footer. + +`dataDir` +: (`string`) The designated directory for data files. Default is `data`. {{% module-mounts-note %}} + +`defaultContentLanguage` +: (`string`) The projects's [default language](g), conforming to the syntax described in [RFC 5646][]. + +`defaultContentLanguageInSubdir` +: (`bool`) Whether to publish the default content language to a subdirectory matching the [`defaultContentLanguage`](#defaultcontentlanguage). Default is `false`. + +`defaultContentRole` +: {{< new-in 0.153.0 />}} +: (`string`) The project's [default role](g). + +`defaultContentRoleInSubdir` +: {{< new-in 0.153.0 />}} +: (`bool`) Whether to publish the default content [role](g) to a subdirectory matching the [`defaultContentRole`](#defaultcontentrole). Default is `false`. + +`defaultContentVersion` +: {{< new-in 0.153.0 />}} +: (`string`) The project's [default version](g). + +`defaultContentVersionInSubdir` +: {{< new-in 0.153.0 />}} +: (`bool`) Whether to publish the default content version to a subdirectory matching the [`defaultContentVersion`](#defaultcontentversion). Default is `false`. + +`defaultOutputFormat` +: (`string`) The default output format for the site. If unspecified, the first available format in the defined order (by weight, then alphabetically) will be used. + +`deployment` +: See [configure deployment][]. + +`disableAliases` +: (`bool`) Whether to disable the generation of HTML redirect files for each path defined in the [`aliases`][aliases_front_matter] front matter field. When `true`, Hugo will not create physical files for [client-side redirection][], but the alias data remains available via the [`Aliases`][aliases_page_method] method on a `Page` object. Default is `false`. + +`disableDefaultLanguageRedirect` +: {{< new-in 0.140.0 />}} +: (`bool`) Whether to disable generation of the alias redirect for the default content language. When [`defaultContentLanguageInSubdir`](#defaultcontentlanguageinsubdir) is `true`, this setting prevents the root directory from redirecting to the language subdirectory. Conversely, when `defaultContentLanguageInSubdir` is `false`, this setting prevents the language subdirectory from redirecting to the root directory. This is superseded by the more general [`disableDefaultSiteRedirect`](#disabledefaultsiteredirect) setting. Default is `false`. + +`disableDefaultSiteRedirect` +: {{< new-in 0.154.5 />}} +: (`bool`) Whether to disable generation of the alias redirect to the [default site](g). When [`defaultContentLanguageInSubdir`](#defaultcontentlanguageinsubdir), [`defaultContentRoleInSubdir`](#defaultcontentroleinsubdir), or [`defaultContentVersionInSubdir`](#defaultcontentversioninsubdir) is `true`, this prevents the root directory from redirecting to the default site's subdirectory. Conversely, when these are `false`, it prevents the subdirectories from redirecting back to the root. Default is `false`. + +`disableHugoGeneratorInject` +: (`bool`) Whether to disable injection of a `` tag into the home page. Default is `false`. + +`disableKinds` +: (`[]string`) A slice of page [kinds](g) to disable during the build process, any of `404`, `home`, `page`, `robotstxt`, `rss`, `section`, `sitemap`, `taxonomy`, or `term`. + +`disableLanguages` +: (`[]string`) A slice of language keys representing the languages to disable during the build process. Although this is functional, consider using the [`disabled`][] key under each language instead. + +`disableLiveReload` +: (`bool`) Whether to disable automatic live reloading of the browser window. Default is `false`. + +`disablePathToLower` +: (`bool`) Whether to disable transformation of page URLs to lower case. Default is `false`. + +`enableEmoji` +: (`bool`) Whether to allow emoji in Markdown. Default is `false`. + +`enableGitInfo` +: (`bool`) Whether to retrieve commit metadata from the Git history of your local project and any [modules](g). This enables the [`GitInfo`][] method on a `Page` object. With the default front matter configuration, the [`Lastmod`][] method on a `Page` object returns the Git author date of the last commit for that file. Default is `false`. + +`enableMissingTranslationPlaceholders` +: (`bool`) Whether to show a placeholder instead of the default value or an empty string if a translation is missing. Default is `false`. + +`enableRobotsTXT` +: (`bool`) Whether to enable generation of a `robots.txt` file. Default is `false`. + +`frontmatter` +: See [configure front matter][]. + +`hasCJKLanguage` +: (`bool`) Whether to automatically detect [CJK](g) languages in content. Affects the values returned by the [`WordCount`][] and [`FuzzyWordCount`][] methods. Default is `false`. + +`HTTPCache` +: See [configure HTTP cache][]. + +`i18nDir` +: (`string`) The designated directory for translation tables. Default is `i18n`. {{% module-mounts-note %}} + +`ignoreCache` +: (`bool`) Whether to ignore the configured file caches. Default is `false`. + +`ignoreFiles` +: (`[]string`) A slice of [regular expressions](g) used to exclude specific files from a build. These expressions are matched against the absolute file path and apply to files within the `content`, `data`, and `i18n` directories. For more advanced file exclusion options, see the section on [module mounts][]. + +`ignoreLogs` +: (`[]string`) A slice of message identifiers corresponding to warnings and errors you wish to suppress. See [`erroridf`][] and [`warnidf`][]. + +`ignoreVendorPaths` +: (`string`) A [glob pattern](g) matching the module paths to exclude from the `_vendor` directory. + +`imaging` +: See [configure imaging][]. + +`languageCode` +: {{}} +: Use [`locale`](#locale) instead. + +`languages` +: See [configure languages][]. + +`layoutDir` +: (`string`) The designated directory for templates. Default is `layouts`. {{% module-mounts-note %}} + +{{% include "/_common/configuration/locale.md" %}} + + For a multilingual project, specify this value independently for each language key. See [configure languages][]. + +`mainSections` +: (`string` or `[]string`) The main sections of a site. If set, the [`MainSections`][] method on the `Site` object returns the given sections, otherwise it returns the section with the most pages. + +`markup` +: See [configure markup][]. + +`mediaTypes` +: See [configure media types][]. + +`menus` +: See [configure menus][]. + +`minify` +: See [configure minify][]. + +`module` +: See [configure modules][]. + +`newContentEditor` +: (`string`) The editor to use when creating new content. + +`noBuildLock` +: (`bool`) Whether to disable creation of the `.hugo_build.lock` file. Default is `false`. + +`noChmod` +: (`bool`) Whether to disable synchronization of file permission modes. Default is `false`. + +`noTimes` +: (`bool`) Whether to disable synchronization of file modification times. Default is `false`. + +`outputFormats` +: See [configure output formats][]. + +`outputs` +: See [configure outputs][]. + +`page` +: See [configure page][]. + +`pagination` +: See [configure pagination][]. + +`panicOnWarning` +: (`bool`) Whether to panic on the first WARNING. Default is `false`. + +`params` +: See [configure params][]. + +`permalinks` +: See [configure permalinks][]. + +`pluralizeListTitles` +: (`bool`) Whether to pluralize automatic list titles. Applicable to section pages. Default is `true`. + +`printI18nWarnings` +: (`bool`) Whether to log WARNINGs for each missing translation. Default is `false`. + +`printPathWarnings` +: (`bool`) Whether to log WARNINGs when Hugo publishes two or more files to the same path. Default is `false`. + +`printUnusedTemplates` +: (`bool`) Whether to log WARNINGs for each unused template. Default is `false`. + +`privacy` +: See [configure privacy][]. + +`publishDir` +: (`string`) The designated directory for publishing the site. Default is `public`. + +`refLinksErrorLevel` +: (`string`) The logging error level to use when the `ref` and `relref` functions, methods, and shortcodes are unable to resolve a reference to a page. Either `ERROR` or `WARNING`. Any `ERROR` will fail the build. Default is `ERROR`. + +`refLinksNotFoundURL` +: (`string`) The URL to return when the `ref` and `relref` functions, methods, and shortcodes are unable to resolve a reference to a page. + +`related` +: See [configure related content][]. + +`relativeURLs` +: (`bool`) See [details][relative-urls] before enabling this feature. Default is `false`. + +`removePathAccents` +: (`bool`) Whether to remove [non-spacing marks][] from [composite characters][] in content paths. Default is `false`. + +`renderSegments` +: (`[]string`) A slice of [segments](g) to render. If omitted, all segments are rendered. This option is typically set via a command-line flag, such as `hugo build --renderSegments segment1,segment2`. The provided segment names must correspond to those defined in the [`segments`][] configuration. + +`resourceDir` +: (`string`) The designated directory for caching output from [asset pipelines](g). Default is `resources`. + +`roles` +: See [configure roles][]. + +`security` +: See [configure security][]. + +`sectionPagesMenu` +: (`string`) When set, each top-level section will be added to the menu identified by the provided value. See [details][define-automatically]. + +`segments` +: See [configure segments][]. + +`server` +: See [configure server][]. + +`services` +: See [configure services][]. + +`sitemap` +: See [configure sitemap][]. + +`staticDir` +: (`string`) The designated directory for static files. Default is `static`. {{% module-mounts-note %}} + +`summaryLength` +: (`int`) Applicable to [automatic summaries][], the minimum number of words returned by the [`Summary`][] method on a `Page` object. The `Summary` method will return content truncated at the paragraph boundary closest to the specified `summaryLength`, but at least this minimum number of words. Default is `70`. + +`taxonomies` +: See [configure taxonomies][]. + +`templateMetrics` +: (`bool`) Whether to print template execution metrics to the console. Default is `false`. See [details][template-metrics]. + +`templateMetricsHints` +: (`bool`) Whether to print template execution improvement hints to the console. Applicable when `templateMetrics` is `true`. Default is `false`. See [details][template-metrics]. + +`theme` +: (`string` or `[]string`) The [theme](g) to use. Multiple themes can be listed, with precedence given from left to right. See [details][]. + +`themesDir` +: (`string`) The designated directory for themes. Default is `themes`. + +`timeout` +: (`string`) The timeout for generating page content, either as a [duration][] or in seconds. This timeout is used to prevent infinite recursion during content generation. You may need to increase this value if your pages take a long time to generate, for example, due to extensive image processing or reliance on remote content. Default is `60s`. + +`timeZone` +: (`string`) The time zone used to parse dates without time zone offsets, including front matter date fields and values passed to the [`time.AsTime`][] and [`time.Format`][] functions. The list of valid values may be system dependent, but should include `UTC`, `Local`, and any location in the [IANA Time Zone Database][]. For example, `America/Los_Angeles` and `Europe/Oslo` are valid time zones. + +`title` +: (`string`) The site title. + +`titleCaseStyle` +: (`string`) The capitalization rules to follow when Hugo automatically generates a section title, or when using the [`strings.Title`][] function. One of `ap`, `chicago`, `go`, `firstupper`, or `none`. Default is `ap`. See [details](#title-case-style). + +`uglyurls` +: See [configure ugly URLs][]. + +`versions` +: See [configure versions][]. + +## Cache directory + +Hugo's file cache directory is configurable via the [`cacheDir`](#cachedir) setting or the `HUGO_CACHEDIR` environment variable. If neither is set, Hugo will use, in order of preference: + +1. If running on Netlify: `/opt/build/cache/hugo_cache/`. This means that if you run your builds on Netlify, all caches configured with `:cacheDir` will be saved and restored on the next build. For other [CI/CD](g) platforms, please read their documentation. For a CircleCI example, see [this configuration][]. +1. In a `hugo_cache` directory below the OS user cache directory as defined by Go's [`os.UserCacheDir`][] function. On Unix systems, per the [XDG base directory specification][], this is `$XDG_CACHE_HOME` if non-empty, else `$HOME/.cache`. On MacOS, this is `$HOME/Library/Caches`. On Windows, this is`%LocalAppData%`. On Plan 9, this is `$home/lib/cache`. +1. In a `hugo_cache_$USER` directory below the OS temp dir. + +To determine the current `cacheDir`: + +```sh +hugo config | grep cachedir +``` + +## Title case style + +Hugo's [`titleCaseStyle`](#titlecasestyle) setting governs capitalization for automatically generated section titles and the [`strings.Title`][] function. By default, it follows the capitalization rules published in the Associated Press Stylebook. Change this setting to use other capitalization rules. + +`ap` +: Use the capitalization rules published in the [Associated Press Stylebook][]. This is the default. + +`chicago` +: Use the capitalization rules published in the [Chicago Manual of Style][]. + +`go` +: Capitalize the first letter of every word. + +`firstupper` +: Capitalize the first letter of the first word. + +`none` +: Disable transformation of automatic section titles, and disable the transformation performed by the `strings.Title` function. This is useful if you would prefer to manually capitalize section titles as needed, and to bypass opinionated theme usage of the `strings.Title` function. + +## Localized settings + +Some configuration settings, such as menus and custom parameters, can be defined separately for each language. See [configure languages][]. + +[Associated Press Stylebook]: https://www.apstylebook.com/ +[Chicago Manual of Style]: https://www.chicagomanualofstyle.org/home.html +[IANA Time Zone Database]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +[RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 +[XDG base directory specification]: https://specifications.freedesktop.org/basedir-spec/latest/ +[`FuzzyWordCount`]: /methods/page/fuzzywordcount/ +[`GitInfo`]: /methods/page/gitinfo/ +[`Lastmod`]: /methods/page/lastmod/ +[`MainSections`]: /methods/site/mainsections/ +[`Summary`]: /methods/page/summary/ +[`WordCount`]: /methods/page/wordcount/ +[`disabled`]: /configuration/languages/#disabled +[`erroridf`]: /functions/fmt/erroridf/ +[`os.UserCacheDir`]: https://pkg.go.dev/os#UserCacheDir +[`segments`]: /configuration/segments/ +[`strings.Title`]: /functions/strings/title/ +[`time.AsTime`]: /functions/time/astime/ +[`time.Format`]: /functions/time/format/ +[`warnidf`]: /functions/fmt/warnidf/ +[aliases_front_matter]: /content-management/front-matter/#aliases +[aliases_page_method]: /methods/page/aliases/ +[automatic summaries]: /content-management/summaries/#automatic-summary +[canonical-urls]: /content-management/urls/#canonical-urls +[client-side redirection]: /content-management/urls/#client-side-redirection +[composite characters]: https://en.wikipedia.org/wiki/Precomposed_character +[configure HTTP cache]: /configuration/http-cache/ +[configure build]: /configuration/build/ +[configure cascade]: /configuration/cascade/ +[configure deployment]: /configuration/deployment/ +[configure file caches]: /configuration/caches/ +[configure front matter]: /configuration/front-matter/ +[configure imaging]: /configuration/imaging/ +[configure languages]: /configuration/languages/ +[configure markup]: /configuration/markup/ +[configure media types]: /configuration/media-types/ +[configure menus]: /configuration/menus/ +[configure minify]: /configuration/minify/ +[configure modules]: /configuration/module/ +[configure output formats]: /configuration/output-formats/ +[configure outputs]: /configuration/outputs/ +[configure page]: /configuration/page/ +[configure pagination]: /configuration/pagination/ +[configure params]: /configuration/params/ +[configure permalinks]: /configuration/permalinks/ +[configure privacy]: /configuration/privacy/ +[configure related content]: /configuration/related-content/ +[configure roles]: /configuration/roles/ +[configure security]: /configuration/security/ +[configure segments]: /configuration/segments/ +[configure server]: /configuration/server/ +[configure services]: /configuration/services/ +[configure sitemap]: /configuration/sitemap/ +[configure taxonomies]: /configuration/taxonomies/ +[configure ugly URLs]: /configuration/ugly-urls/ +[configure versions]: /configuration/versions/ +[define-automatically]: /content-management/menus/#define-automatically +[details]: /hugo-modules/theme-components/ +[duration]: https://pkg.go.dev/time#Duration +[module mounts]: /configuration/module/#mounts +[non-spacing marks]: https://www.compart.com/en/unicode/category/Mn +[relative-urls]: /content-management/urls/#relative-urls +[template-metrics]: /troubleshooting/performance/#template-metrics +[this configuration]: https://github.com/bep/hugo-sass-test/blob/6c3960a8f4b90e8938228688bc49bdcdd6b2d99e/.circleci/config.yml diff --git a/docs/content/en/configuration/build.md b/docs/content/en/configuration/build.md new file mode 100644 index 0000000..595b0e8 --- /dev/null +++ b/docs/content/en/configuration/build.md @@ -0,0 +1,85 @@ +--- +title: Configure build +linkTitle: Build +description: Configure global build options. +categories: [] +keywords: [] +aliases: [/getting-started/configuration-build/] +--- + +This is the default configuration: + +{{< code-toggle config=build />}} + +`buildStats` +: See the [build stats](#build-stats) section below. + +`cachebusters` +: See the [cache busters](#cache-busters) section below. + +`noJSConfigInAssets` +: (`bool`) Whether to disable writing a `jsconfig.json` in your `assets` directory with mapping of imports from running [js.Build][]. This file is intended to help with intellisense/navigation inside code editors such as [VS Code][]. Note that if you do not use `js.Build`, no file will be written. + +`useResourceCacheWhen` +: (`string`) When to use the resource file cache, one of `never`, `fallback`, or `always`. Applicable when transpiling Sass to CSS. Default is `fallback`. + +## Cache busters + +The `build.cachebusters` setting was added to support development using Tailwind 3.x's JIT compiler where a `build` configuration may look like this: + + +{{< code-toggle file=hugo >}} +[build] + [build.buildStats] + enable = true + [[build.cachebusters]] + source = 'assets/watching/hugo_stats\.json' + target = 'styles\.css' + [[build.cachebusters]] + source = '(postcss|tailwind)\.config\.js' + target = 'css' + [[build.cachebusters]] + source = 'assets/.*\.(js|ts|jsx|tsx)' + target = 'js' + [[build.cachebusters]] + source = 'assets/.*\.(.*)$' + target = '$1' +{{< /code-toggle >}} + + +When `buildStats` is enabled, Hugo writes a `hugo_stats.json` file on each build with HTML classes etc. that's used in the rendered output. Changes to this file will trigger a rebuild of the `styles.css` file. You also need to add `hugo_stats.json` to Hugo's server watcher. See [Hugo Starter Tailwind Basic][] for a running example. + +`source` +: (`string`) A [regular expression](g) matching file(s) relative to one of the virtual component directories in Hugo, typically `assets/...`. + +`target` +: (`string`) A [regular expression](g) matching the keys in the resource cache that should be expired when `source` changes. You can use the matching regexp groups from `source` in the expression, e.g. `$1`. + +## Build stats + +{{< code-toggle config=build.buildStats />}} + +`enable` +: (`bool`) Whether to create a `hugo_stats.json` file in the root of your project. This file contains arrays of the `class` attributes, `id` attributes, and tags of every HTML element within your published site. Use this file as data source when [removing unused CSS][] from your site. This process is also known as pruning, purging, or tree shaking. Default is `false`. + +`disableIDs` +: (`bool`) Whether to exclude `id` attributes. Default is `false`. + +`disableTags` +: (`bool`) Whether to exclude element tags. Default is `false`. + +`disableClasses` +: (`bool`) Whether to exclude `class` attributes. Default is `false`. + +> [!NOTE] +> Given that CSS purging is typically limited to production builds, place the `buildStats` object below [`config/production`][]. +> +> Built for speed, there may be "false positive" detections (e.g., HTML elements that are not HTML elements) while parsing the published site. These "false positives" are infrequent and inconsequential. + +Due to the nature of partial server builds, new HTML entities are added while the server is running, but old values will not be removed until you restart the server or run `hugo build`. + +[Hugo Starter Tailwind Basic]: https://github.com/bep/hugo-starter-tailwind-basic +[VS Code]: https://code.visualstudio.com/ +[`config/production`]: /configuration/introduction/#configuration-directory +[js.Build]: /functions/js/build/ +[removing unused CSS]: /functions/resources/postprocess/ diff --git a/docs/content/en/configuration/caches.md b/docs/content/en/configuration/caches.md new file mode 100644 index 0000000..a83d55a --- /dev/null +++ b/docs/content/en/configuration/caches.md @@ -0,0 +1,63 @@ +--- +title: Configure file caches +linkTitle: Caches +description: Configure file caches. +categories: [] +keywords: [] +--- + +This is the default configuration: + +{{< code-toggle config=caches />}} + +## Purpose + +Hugo uses file caches to store data on disk, avoiding repeated operations within the same build and persisting data from one build to the next. + +`assets` +: Caches processed CSS and Sass resources. + +`getresource` +: Caches files fetched from remote URLs via the [`resources.GetRemote`][] function. + +`images` +: Caches processed images. + +`misc` +: Caches miscellaneous data. + +`modulegitinfo` +: Caches Git information for modules. + +`modulequeries` +: Caches the results of module resolution queries. + +`modules` +: Caches downloaded modules. + +## Keys + +`dir` +: (`string`) The absolute file system path where Hugo stores the cached files. You can begin the path with the `:cacheDir` or `:resourceDir` [tokens](#tokens) to anchor the cache to specific system or project locations. + +`maxAge` +: (`string`) The duration a cached entry remains valid before being evicted, expressed as a [duration](g). A value of `0` disables the cache for that key, and a value of `-1` means the cache entry never expires. Default is `-1`. + +## Tokens + +`:cacheDir` +: (`string`) The designated cache directory. See [details][cachedir]. + +`:project` +: (`string`) The base directory name of the current Hugo project. This ensures isolated file caches for each project, preventing the `hugo build --gc` command from affecting other projects on the same machine. + +`:resourceDir` +: (`string`) The designated directory for caching output from [asset pipelines](g). See [details][resourcedir]. + +## Garbage collection + +As you modify your site or change your configuration, cached files from previous builds may remain on disk, consuming unnecessary space. Use the `hugo build --gc` command to remove these expired or unused entries from the file cache. + +[`resources.GetRemote`]: /functions/resources/getremote/ +[cachedir]: /configuration/all/#cachedir +[resourcedir]: /configuration/all/#resourcedir diff --git a/docs/content/en/configuration/cascade.md b/docs/content/en/configuration/cascade.md new file mode 100644 index 0000000..6ef4b71 --- /dev/null +++ b/docs/content/en/configuration/cascade.md @@ -0,0 +1,62 @@ +--- +title: Configure cascade +linkTitle: Cascade +description: Configure cascade. +categories: [] +keywords: [] +--- + +You can configure your site to cascade front matter values to the home page and any of its descendants. However, this cascading will be prevented if the descendant already defines the field, or if a closer ancestor [branch](g) has already cascaded a value for the same field through its front matter's `cascade` key. + +> [!NOTE] +> You can also configure cascading behavior within a page's front matter. See [details][]. + +For example, to cascade the `color` page parameter to all pages: + +{{< code-toggle file=hugo >}} +[cascade.params] +color = 'red' +{{< /code-toggle >}} + +## Target + + + +The `target` key accepts a [page matcher](g) to limit cascaded values to a subset of pages.[^1] If a target is omitted, values cascade to all pages. + +{{% include "/_common/configuration/page-matcher.md" %}} + +For example, to cascade the `color` page parameter to the `articles` section and its descendants, but only for the English (`en`) and German (`de`) language sites: + +{{< code-toggle file=hugo >}} +[cascade.params] +color = 'red' +[cascade.target] +path = '{/articles,/articles/**}' +[cascade.target.sites.matrix] +languages = '{en,de}' +{{< /code-toggle >}} + +## Array + +Define an array of cascade maps to apply different values to different targets. For example: + +{{< code-toggle file=hugo >}} +[[cascade]] +[cascade.params] +color = 'red' +[cascade.target] +path = '{/articles,/articles/**}' +[[cascade]] +[cascade.params] +color = 'blue' +[cascade.target] +path = '{/tutorials,/tutorials/**}' +{{< /code-toggle >}} + +[^1]: The `_target` alias for `target` is deprecated and will be removed in a future release. + +[details]: /content-management/front-matter/#cascade-1 diff --git a/docs/content/en/configuration/content-types.md b/docs/content/en/configuration/content-types.md new file mode 100644 index 0000000..568b969 --- /dev/null +++ b/docs/content/en/configuration/content-types.md @@ -0,0 +1,63 @@ +--- +title: Configure content types +linkTitle: Content types +description: Configure content types. +categories: [] +keywords: [] +--- + +{{< new-in 0.144.0 />}} + +Hugo supports six [content formats](g): + +{{% include "/_common/content-format-table.md" %}} + +These can be used as either page content or [page resources](g). When used as page resources, their [resource type](g) is `page`. + +Consider this example of a [page bundle](g): + +```tree +content/ +└── example/ + ├── index.md <-- content + ├── a.adoc <-- resource (resource type: page) + ├── b.html <-- resource (resource type: page) + ├── c.md <-- resource (resource type: page) + ├── d.org <-- resource (resource type: page) + ├── e.pdc <-- resource (resource type: page) + ├── f.rst <-- resource (resource type: page) + ├── g.jpg <-- resource (resource type: image) + └── h.png <-- resource (resource type: image) +``` + +The `index.md` file is the page's content, while the other files are page resources. Files `a` through `f` are of resource type `page`, while `g` and `h` are of resource type `image`. + +When you build a site, Hugo does not publish page resources having a resource type of `page`. For example, this is the result of building the site above: + +```tree +public/ +├── example/ +│ ├── g.jpg +│ ├── h.png +│ └── index.html +└── index.html +``` + +The default behavior is appropriate in most cases. Given that page resources containing markup are typically intended for inclusion in the main content, publishing them independently is generally undesirable. + +The default behavior is determined by the `contentTypes` configuration: + +{{< code-toggle config=contentTypes />}} + +In this default configuration, page resources with those media types will have a resource type of `page`, and will not be automatically published. To change the resource type assignment from `page` to `text` for a given media type, remove the corresponding entry from the list. + +For example, to set the resource type of `text/html` files to `text`, thereby enabling automatic publication, remove the `text/html` entry: + +{{< code-toggle file=hugo >}} +contentTypes: + text/asciidoc: {} + text/markdown: {} + text/org: {} + text/pandoc: {} + text/rst: {} +{{< /code-toggle >}} diff --git a/docs/content/en/configuration/deployment.md b/docs/content/en/configuration/deployment.md new file mode 100644 index 0000000..eebbcaf --- /dev/null +++ b/docs/content/en/configuration/deployment.md @@ -0,0 +1,159 @@ +--- +title: Configure deployment +linkTitle: Deployment +description: Configure deployments to Amazon S3, Azure Blob Storage, or Google Cloud Storage. +categories: [] +keywords: [] +--- + +> [!NOTE] +> This configuration is only relevant when running `hugo deploy`. See [details][hugo deploy]. + +## Top-level settings + +These settings control the overall behavior of the deployment process. This is the default configuration: + +{{< code-toggle file=hugo config=deployment />}} + +`confirm` +: (`bool`) Whether to prompt for confirmation before deploying. Default is `false`. + +`dryRun` +: (`bool`) Whether to simulate the deployment without any remote changes. Default is `false`. + +`force` +: (`bool`) Whether to re-upload all files. Default is `false`. + +`invalidateCDN` +: (`bool`) Whether to invalidate the CDN cache listed in the deployment target. Default is `true`. + +`maxDeletes` +: (`int`) The maximum number of files to delete, or `-1` to disable. Default is `256`. + +`matchers` +: (`[]*Matcher`) A slice of [matchers](#matchers-1). + +`order` +: (`[]string`) An ordered slice of [regular expressions](g) that determines upload priority (left to right). Files not matching any expression are uploaded last in an arbitrary order. + +`target` +: (`string`) The target deployment [`name`](#name). Defaults to the first target. + +`targets` +: (`[]*Target`) A slice of [targets](#targets-1). + +`workers` +: (`int`) The number of concurrent workers to use when uploading files. Default is `10`. + +## Targets + +A target represents a deployment target such as "staging" or "production". + +`cloudFrontDistributionID` +: (`string`) The CloudFront Distribution ID, applicable if you are using the Amazon Web Services CloudFront CDN. Hugo will invalidate the CDN when deploying this target. + +`exclude` +: (`string`) A [glob pattern](g) matching files to exclude when deploying to this target. Local files failing the include/exclude filters are not uploaded, and remote files failing these filters are not deleted. + +`googleCloudCDNOrigin` +: (`string`) The Google Cloud project and CDN origin to invalidate when deploying this target, specified as `/`. + +`include` +: (`string`) A [glob pattern](g) matching files to include when deploying to this target. Local files failing the include/exclude filters are not uploaded, and remote files failing these filters are not deleted. + +`name` +: (`string`) An arbitrary name for this target. + +`stripIndexHTML` +: (`bool`) Whether to map files named `
    /index.html` to `` on the remote (except for the root `index.html`). This is useful for key-value cloud storage (e.g., Amazon S3, Google Cloud Storage, Azure Blob Storage) to align canonical URLs with object keys. Default is `false`. + +`url` +: (`string`) The [destination URL](#destination-urls) for deployment. + +## Matchers + +A Matcher represents a configuration to be applied to files whose paths match +the specified pattern. + +`cacheControl` +: (`string`) The caching attributes to use when serving the blob. See [details][cacheControl]. + +`contentEncoding` +: (`string`) The encoding used for the blob's content, if any. See [details][contentEncoding]. + +`contentType` +: (`string`) The media type of the blob being written. See [details][contentType]. + +`force` +: (`bool`) Whether matching files should be re-uploaded. Useful when other route-determined metadata (e.g., `contentType`) has changed. Default is `false`. + +`gzip` +: (`bool`) Whether the file should be gzipped before upload. If so, the `ContentEncoding` field will automatically be set to `gzip`. Default is `false`. + +`pattern` +: (`string`) A [regular expression](g) used to match paths. Paths are converted to use forward slashes (`/`) before matching. + +## Destination URLs + +Service|URL example +:--|:-- +Amazon Simple Storage Service (S3)|`s3://my-bucket?region=us-west-1` +Azure Blob Storage|`azblob://my-container` +Google Cloud Storage (GCS)|`gs://my-bucket` + +With Google Cloud Storage you can target a subdirectory: + +```text +gs://my-bucket?prefix=a/subdirectory +``` + +You can also to deploy to storage servers compatible with Amazon S3 such as: + +- [Ceph][] +- [MinIO][] +- [SeaweedFS][] + +For example, the `url` for a MinIO deployment target might resemble this: + +```text +s3://my-bucket?endpoint=https://my.minio.instance&awssdk=v2&use_path_style=true&disable_https=false +``` + +## Example + +{{< code-toggle file=hugo >}} +[deployment] + order = ['.jpg$', '.gif$'] + [[deployment.matchers]] + cacheControl = 'max-age=31536000, no-transform, public' + gzip = true + pattern = '^.+\.(js|css|svg|ttf)$' + [[deployment.matchers]] + cacheControl = 'max-age=31536000, no-transform, public' + gzip = false + pattern = '^.+\.(png|jpg)$' + [[deployment.matchers]] + contentType = 'application/xml' + gzip = true + pattern = '^sitemap\.xml$' + [[deployment.matchers]] + gzip = true + pattern = '^.+\.(html|xml|json)$' + [[deployment.targets]] + url = 's3://my_production_bucket?region=us-west-1' + cloudFrontDistributionID = 'E1234567890ABCDEF0' + exclude = '**.{heic,psd}' + name = 'production' + [[deployment.targets]] + url = 's3://my_staging_bucket?region=us-west-1' + exclude = '**.{heic,psd}' + name = 'staging' +{{< /code-toggle >}} + +[Ceph]: https://ceph.com/ +[MinIO]: https://www.minio.io/ +[SeaweedFS]: https://github.com/chrislusf/seaweedfs +[cacheControl]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control +[contentEncoding]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding +[contentType]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type +[hugo deploy]: /host-and-deploy/deploy-with-hugo-deploy/ diff --git a/docs/content/en/configuration/front-matter.md b/docs/content/en/configuration/front-matter.md new file mode 100644 index 0000000..1e85c62 --- /dev/null +++ b/docs/content/en/configuration/front-matter.md @@ -0,0 +1,104 @@ +--- +title: Configure front matter +linkTitle: Front matter +description: Configure front matter. +categories: [] +keywords: [] +--- + +## Dates + +There are four methods on a `Page` object that return a date. + +Method|Description +:--|:-- +[`Date`][]|Returns the date of the given page. +[`ExpiryDate`][]|Returns the expiry date of the given page. +[`Lastmod`][]|Returns the last modification date of the given page. +[`PublishDate`][]|Returns the publish date of the given page. + +Hugo determines the values to return based on this configuration: + +{{< code-toggle config=frontmatter />}} + +The `ExpiryDate` method, for example, returns the `expirydate` value if it exists, otherwise it returns `unpublishdate`. + +You can also use custom date parameters: + +{{< code-toggle file=hugo >}} +[frontmatter] +date = ["myDate", "date"] +{{< /code-toggle >}} + +In the example above, the `Date` method returns the `myDate` value if it exists, otherwise it returns `date`. + +To fall back to the default sequence of dates, use the `:default` token: + +{{< code-toggle file=hugo >}} +[frontmatter] +date = ["myDate", ":default"] +{{< /code-toggle >}} + +In the example above, the `Date` method returns the `myDate` value if it exists, otherwise it returns the first valid date from `date`, `publishdate`, `pubdate`, `published`, `lastmod`, and `modified`. + +## Aliases + +Some of the front matter fields have aliases. + +Front matter field|Aliases +:--|:-- +`expiryDate`|`unpublishdate` +`lastmod`|`modified` +`publishDate`|`pubdate`, `published` + +The default front matter configuration includes these aliases. + +## Tokens + +Hugo provides the following [tokens](g) to help you configure your front matter: + +`:default` +: The default ordered sequence of date fields. + +`:fileModTime` +: The file's last modification timestamp. + +`:filename` +: Extracts the date from the file name, provided the file name begins with a date in one of the following formats: + + - `YYYY-MM-DD` + - `YYYY-MM-DD-HH-MM-SS` {{< new-in 0.148.0 />}} + + Within the `YYYY-MM-DD-HH-MM-SS` format, the date and time values may be separated by any character including a space (e.g., `2025-02-01T14-30-00`). + + Hugo resolves the extracted date to the [`timeZone`][] defined in your project configuration, falling back to the system time zone. Hugo also derives the page [`slug`][] from the remaining file name, unless the page already defines a `slug` in its front matter. + + Slug inference only occurs when `:filename` is the winning date source. If an earlier entry in the list provides a valid date, Hugo skips `:filename` entirely. For example, with `date = ["date", ":filename"]`, a page that defines `date` in its front matter will use that value, and the slug will not be inferred from the file name. + + For example, if you name your file `2025-02-01-article.md`, Hugo will set the date to `2025-02-01` and the slug to `article`. + +`:git` +: The Git author date for the file's last revision. To enable access to the Git author date, set [`enableGitInfo`][] to `true`. + +## Example + +Consider this project configuration: + +{{< code-toggle file=hugo >}} +[frontmatter] +date = [':filename', ':default'] +publishDate = [':filename', ':default'] +lastmod = ['lastmod', ':fileModTime'] +{{< /code-toggle >}} + +To determine `date` and `publishDate`, Hugo tries to extract the value from the file name, falling back to the default ordered sequence of date fields. + +To determine `lastmod`, Hugo looks for a `lastmod` field in front matter, falling back to the file's last modification timestamp. + +[`Date`]: /methods/page/date/ +[`ExpiryDate`]: /methods/page/expirydate/ +[`Lastmod`]: /methods/page/lastmod/ +[`PublishDate`]: /methods/page/publishdate/ +[`enableGitInfo`]: /configuration/all/#enablegitinfo +[`slug`]: /content-management/front-matter/#slug +[`timeZone`]: /configuration/all/#timezone diff --git a/docs/content/en/configuration/http-cache.md b/docs/content/en/configuration/http-cache.md new file mode 100644 index 0000000..de7f1b0 --- /dev/null +++ b/docs/content/en/configuration/http-cache.md @@ -0,0 +1,129 @@ +--- +title: Configure the HTTP cache +linkTitle: HTTP cache +description: Configure the HTTP cache. +categories: [] +keywords: [] +--- + +> [!NOTE] +> This configuration is only relevant when using the [`resources.GetRemote`][] function. + +## Layered caching + +Hugo employs a layered caching system. + +```goat {.w-40} + .-----------. +| dynacache | + '-----+-----' + | + v + .----------. +| HTTP cache | + '-----+----' + | + v + .----------. +| file cache | + '-----+----' +``` + +Dynacache +: An in-memory cache employing a Least Recently Used (LRU) eviction policy. Entries are removed from the cache when changes occur, when they match [cache-busting][] patterns, or under low-memory conditions. + +HTTP Cache +: An HTTP cache for remote resources as specified in [RFC 9111][]. Optimal performance is achieved when resources include appropriate HTTP cache headers. The HTTP cache utilizes the file cache for storage and retrieval of cached resources. + +File cache +: See [configure file caches][]. + +The HTTP cache involves two key aspects: determining which content to cache (the caching process itself) and defining the frequency with which to check for updates (the polling strategy). + +## HTTP caching + +The HTTP cache behavior is defined for a configured set of resources. Stale resources will be refreshed from the file cache, even if their configured Time-To-Live (TTL) has not expired. If HTTP caching is disabled for a resource, Hugo will bypass the cache and access the file directly. + +This is the default configuration for HTTP caching: + +{{< code-toggle config=HTTPCache />}} + +`respectCacheControlNoStoreInRequest` +: {{< new-in 0.151.0 />}} +: (`bool`) Whether to respect the `no-store` directive in the server's `Cache-Control` request header when fetching remote resources via the [`resources.GetRemote`][] function. Default is `true`. + +`respectCacheControlNoStoreInResponse` +: {{< new-in 0.151.0 />}} +: (`bool`) Whether to respect the `no-store` directive in the server's `Cache-Control` response header when fetching remote resources via the [`resources.GetRemote`][] function. Default is `false`. + +`cache.for.excludes` +: (`[]string`) A slice of [glob patterns](g) to exclude from caching. In its default configuration HTTP caching excludes all files. + +`cache.for.includes` +: (`[]string`) A slice of [glob patterns](g) to cache. + +`polls` +: (`[]PollConfig`) A slice of polling configurations. + +`polls.disable` +: (`bool`) Whether to disable polling for this configuration. Default is `true`. + +`polls.high` +: (`string`) The maximum polling interval expressed as a [duration](g). This is used when the resource is considered stable. Default is `0s`. + +`polls.low` +: (`string`) The minimum polling interval expressed as a [duration](g). This is used after a recent change and gradually increases towards `polls.high`. Default is `0s`. + +`polls.for.excludes` +: (`[]string`) A slice of [glob patterns](g) to exclude from polling for this configuration. + +`polls.for.includes` +: (`[]string`) A slice of [glob patterns](g) to include in polling for this configuration. + +## HTTP polling + +Polling is used in watch mode (e.g., `hugo server`) to detect changes in remote resources. Polling can be enabled even if HTTP caching is disabled. Detected changes trigger a rebuild of pages using the affected resource. Polling can be disabled for specific resources, typically those known to be static. + +The default configuration disables everything: + +{{< code-toggle file=hugo >}} +[[HTTPCache.polls]] +disable = true +high = '0s' +low = '0s' +[HTTPCache.polls.for] +includes = ['**'] +excludes = [] +{{< /code-toggle >}} + +`polls` +: (`[]PollConfig`) A slice of polling configurations. + +`polls.disable` +: (`bool`) Whether to disable polling for this configuration. Default is `true`. + +`polls.high` +: (`string`) The maximum polling interval expressed as a [duration](g). This is used when the resource is considered stable. Default is `0s`. + +`polls.low` +: (`string`) The minimum polling interval expressed as a [duration](g). This is used after a recent change and gradually increases towards `polls.high`. Default is `0s`. + +`polls.for.excludes` +: (`[]string`) A list of [glob patterns](g) to exclude from polling for this configuration. + +`polls.for.includes` +: (`[]string`) A list of [glob patterns](g) to include in polling for this configuration. + +## Behavior + +Polling and HTTP caching interact as follows: + +- With polling enabled, rebuilds are triggered only by actual changes, detected via `eTag` changes (Hugo generates an MD5 hash if the server doesn't provide one). +- If polling is enabled but HTTP caching is disabled, the remote is checked for changes only after the file cache's TTL expires (e.g., a `maxAge` of `10h` with a `1s` polling interval is inefficient). +- If both polling and HTTP caching are enabled, changes are checked for even before the file cache's TTL expires. Cached `eTag` and `last-modified` values are sent in `if-none-match` and `if-modified-since` headers, respectively, and a cached response is returned on HTTP [304][]. + +[304]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304 +[RFC 9111]: https://datatracker.ietf.org/doc/html/rfc9111 +[`resources.GetRemote`]: /functions/resources/getremote/ +[cache-busting]: /configuration/build/#cache-busters +[configure file caches]: /configuration/caches/ diff --git a/docs/content/en/configuration/imaging.md b/docs/content/en/configuration/imaging.md new file mode 100644 index 0000000..1675541 --- /dev/null +++ b/docs/content/en/configuration/imaging.md @@ -0,0 +1,155 @@ +--- +title: Configure imaging +linkTitle: Imaging +description: Configure imaging. +categories: [] +keywords: [] +--- + +These are the default settings for processing images: + +{{< code-toggle config=imaging />}} + +## Top-level settings + +These settings apply to all image formats. + +`anchor` +: (`string`) The focal point used when cropping or filling an image. Valid case-insensitive options include `TopLeft`, `Top`, `TopRight`, `Left`, `Center`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`, or `Smart`. The `Smart` option utilizes the [`muesli/smartcrop`][] package to identify the most interesting area of the image. Default is `smart`. + +`bgColor` +: (`string`) The background color used when converting transparent images to formats that do not support transparency, such as PNG to JPEG. This color also fills the empty space created when rotating an image by a non-orthogonal angle if the space is not transparent and a background color is not specified in the processing specification. The value must be an RGB [hexadecimal color][]. Default is `#ffffff`. + +`compression` +: {{< deprecated-in 0.163.0 />}} +: Use the format-specific `compression` setting instead, applicable to [AVIF](#avif) and [WebP](#webp) images. + +`hint` +: {{< deprecated-in 0.163.0 />}} +: Use the format-specific `hint` setting instead, applicable to [AVIF](#avif) and [WebP](#webp) images. + +`quality` +: {{< deprecated-in 0.163.0 />}} +: Use the format-specific `quality` setting instead, applicable to [AVIF](#avif), [JPEG](#jpeg), and [WebP](#webp) images. + +`resampleFilter` +: (`string`) The algorithm used to calculate new pixels when resizing, fitting, or filling an image. Common case-insensitive options include `box`, `lanczos`, `catmullRom`, `mitchellNetravali`, `linear`, or `nearestNeighbor`. Default is `box`. + + Filter|Description + :--|:-- + `box`|Simple and fast averaging filter appropriate for downscaling + `lanczos`|High-quality resampling filter for photographic images yielding sharp results + `catmullRom`|Sharp cubic filter that is faster than the Lanczos filter while providing similar results + `mitchellNetravali`|Cubic filter that produces smoother results with less ringing artifacts than CatmullRom + `linear`|Bilinear resampling filter, produces smooth output, faster than cubic filters + `nearestNeighbor`|Fastest resampling filter, no antialiasing + + Refer to the [source documentation][] for a complete list of available resampling filters. If you wish to improve image quality at the expense of performance, you may wish to experiment with the alternative filters. + +## AVIF + +{{< new-in 0.162.0 />}} + +These settings apply when encoding AVIF images. + +> [!NOTE] +> When exporting HDR AVIF images from Lightroom, in the Export dialog under File Settings, uncheck Maximize Compatibility to improve Hugo's AVIF decoding speed. + +> [!NOTE] +> Encoding animated images to AVIF produces a single-frame (static) image. Converting an animated AVIF to another format such as GIF works as expected. + +{{< code-toggle config=imaging.avif />}} + +`compression` +: {{< new-in 0.163.0 />}} +: (`string`) The encoding strategy. Options are `lossy` or `lossless`. Default is `lossy`. + +`encoderSpeed` +: (`int`) The encoder speed. Expressed as a whole number from `1` to `10`, inclusive, equivalent to the `-s` flag for the [`avifenc`][] CLI. Lower numbers reduce file size at the cost of build time. At typical web image sizes, quality is indistinguishable across settings. Values below `5` may cause significantly longer build times. Default is `10`. + +`hint` +: {{< new-in 0.163.0 />}} +: (`string`) The content hint. Valid options include `drawing`, `icon`, `photo`, `picture`, or `text`. Hugo uses the `4:2:0` chroma subsampling format with `photo` and `picture`, and `4:4:4` with the remaining options. Default is `photo`. + + Value|Example + :--|:-- + `drawing`|Hand or line drawing with high-contrast details + `icon`|Small colorful image + `photo`|Outdoor photograph with natural lighting + `picture`|Indoor photograph such as a portrait + `text`|Image that is primarily text + +`quality` +: {{< new-in 0.163.0 />}} +: (`int`) The visual fidelity when using `lossy` compression. Expressed as a whole number from `1` to `100`, inclusive. Lower numbers prioritize smaller file size, while higher numbers prioritize visual clarity. Default is `60`. Quality values are encoder-specific and not directly comparable across formats; a value of `60` for AVIF is perceptually similar to `75` for JPEG. + +## JPEG + +{{< new-in 0.163.0 />}} + +These settings apply when encoding JPEG images. + +{{< code-toggle config=imaging.jpeg />}} + +`quality` +: (`int`) The visual fidelity. Expressed as a whole number from `1` to `100`, inclusive. Lower numbers prioritize smaller file size, while higher numbers prioritize visual clarity. Default is `75`. + +## WebP + +{{< new-in 0.155.0 />}} + +These settings apply when encoding WebP images. + +{{< code-toggle config=imaging.webp />}} + +`compression` +: {{< new-in 0.163.0 />}} +: (`string`) The encoding strategy. Options are `lossy` or `lossless`. Default is `lossy`. + +`hint` +: (`string`) The content hint, equivalent to the `-preset` flag for the [`cwebp`][] CLI. Valid options include `drawing`, `icon`, `photo`, `picture`, or `text`. Default is `photo`. + + Value|Example + :--|:-- + `drawing`|Hand or line drawing with high-contrast details + `icon`|Small colorful image + `photo`|Outdoor photograph with natural lighting + `picture`|Indoor photograph such as a portrait + `text`|Image that is primarily text + +`method` +: (`int`) The effort level of the compression algorithm. Expressed as a whole number from `0` to `6`, inclusive, equivalent to the `-m` flag for the [`cwebp`][] CLI. Lower numbers prioritize processing speed, while higher numbers prioritize compression efficiency and image quality. Default is `2`. + +`quality` +: {{< new-in 0.163.0 />}} +: (`int`) The visual fidelity when using `lossy` compression. Expressed as a whole number from `1` to `100`, inclusive. Lower numbers prioritize smaller file size, while higher numbers prioritize visual clarity. Default is `75`. + +`useSharpYuv` +: (`bool`) The conversion method used for RGB-to-YUV encoding, equivalent to the `-sharp_yuv` flag for the [`cwebp`][] CLI. Enabling this prioritizes image sharpness at the expense of processing speed. Default is `false`. + +## Exif method + +{{< deprecated-in 0.155.0 >}} +Use the [`Meta`](#meta-method) method instead. +{{< /deprecated-in >}} + +## Meta method + +{{< new-in 0.155.0 />}} + +The following parameters allow you to control how Hugo extracts and filters metadata when using the [`Meta`][] method, helping you balance data granularity with build performance. + +`fields` +: (`[]string`) A [glob slice](g) matching the fields to include when extracting metadata. If empty, a default set excluding technical metadata is used. Set to `['**']` to include all fields. + + > [!NOTE] + > By default, to improve performance and decrease cache size, Hugo excludes the following fields: `ColorSpace`, `Contrast`, `Exif`, `ExposureBias`, `ExposureMode`, `ExposureProgram`, `Flash`, `GPS`, `JPEG`, `Metering`, `Resolution`, `Saturation`, `Sensing`, `Sharp`, and `WhiteBalance`. + +`sources` +: (`[]string`) The metadata sources to include, one or more of `exif`, `iptc`, or `xmp`. Default is `['exif', 'iptc']`. The XMP metadata is excluded by default to improve performance. + +[`avifenc`]: https://github.com/aomediacodec/libavif +[`cwebp`]: https://developers.google.com/speed/webp/docs/cwebp +[`muesli/smartcrop`]: https://github.com/muesli/smartcrop +[hexadecimal color]: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color +[source documentation]: https://github.com/disintegration/imaging#image-resizing diff --git a/docs/content/en/configuration/introduction.md b/docs/content/en/configuration/introduction.md new file mode 100644 index 0000000..dc31229 --- /dev/null +++ b/docs/content/en/configuration/introduction.md @@ -0,0 +1,315 @@ +--- +title: Introduction +description: Configure your site using files, directories, and environment variables. +categories: [] +keywords: [] +weight: 10 +--- + +## Sensible defaults + +Hugo offers many configuration settings, but its defaults are often sufficient. A new project requires only these settings: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +locale = 'en-us' +title = 'My New Hugo Site' +{{< /code-toggle >}} + +Only define settings that deviate from the defaults. A smaller configuration file is easier to read, understand, and debug. Keep your configuration concise. + +> [!NOTE] +> The best configuration file is a short configuration file. + +## Configuration file + +Create a project configuration file in the root of your project directory, naming it `hugo.toml`, `hugo.yaml`, or `hugo.json`, with that order of precedence. + +```tree +my-project/ +└── hugo.toml +``` + +A simple example: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +locale = 'en-us' +title = 'ABC Widgets, Inc.' +[params] +subtitle = 'The Best Widgets on Earth' +[params.contact] +email = 'info@example.org' +phone = '+1 202-555-1212' +{{< /code-toggle >}} + +To use a different configuration file when building your project, use the `--config` flag: + +```sh +hugo build --config other.toml +``` + +Combine two or more configuration files, with left-to-right precedence: + +```sh +hugo build --config a.toml,b.yaml,c.json +``` + +> [!NOTE] +> See the specifications for each file format: [TOML][], [YAML][], and [JSON][]. + +## Configuration directory + +Instead of a single project configuration file, split your configuration by [environment](g), root configuration key, and language. For example: + +```tree +my-project/ +└── config/ + ├── _default/ + │ ├── hugo.toml + │ ├── menus.en.toml + │ ├── menus.de.toml + │ └── params.toml + └── production/ + └── params.toml +``` + +The root configuration keys are {{< root-configuration-keys >}}. + +### Root key + +{{< new-in 0.162.0 />}} + +When splitting the configuration by root key, you may omit or include the root key in the component file. For example, these are equivalent: + +{{< code-toggle file=config/_default/hugo >}} +[params] +foo = 'bar' +{{< /code-toggle >}} + +{{< code-toggle file=config/_default/params >}} +foo = 'bar' +{{< /code-toggle >}} + +This also applies to keys whose values are maps of slices, such as `menus`. For example, these are equivalent: + +{{< code-toggle file=config/_default/menus >}} +[[main]] +name = 'Home' +pageRef = '/' +weight = 10 +{{< /code-toggle >}} + +{{< code-toggle file=config/_default/menus >}} +[[menus.main]] +name = 'Home' +pageRef = '/' +weight = 10 +{{< /code-toggle >}} + +For pure slice-typed keys such as `cascade` and `permalinks`, including the root key is required. For example: + +{{< code-toggle file=config/_default/cascade >}} +[[cascade]] +[cascade.params] +color = 'red' +[cascade.target] +path = '/articles/**' +{{< /code-toggle >}} + +> [!NOTE] +> Hugo unwraps the root key only when it is the sole top-level key in the file and matches the file's basename. + +### Recursive parsing + +Hugo parses the `config` directory recursively, allowing you to organize the files into subdirectories. For example: + +```tree +my-project/ +└── config/ + └── _default/ + ├── navigation/ + │ ├── menus.de.toml + │ └── menus.en.toml + └── hugo.toml +``` + +### Example + +```tree +my-project/ +└── config/ + ├── _default/ + │ ├── hugo.toml + │ ├── menus.en.toml + │ ├── menus.de.toml + │ └── params.toml + ├── production/ + │ ├── hugo.toml + │ └── params.toml + └── staging/ + ├── hugo.toml + └── params.toml +``` + +Considering the structure above, when running `hugo build --environment staging`, Hugo will use every setting from `config/_default` and merge `staging`'s on top of those. + +Let's take an example to understand this better. Let's say you are using Google Analytics for your website. This requires you to specify a [Google tag ID][] in your project configuration: + +{{< code-toggle file=hugo >}} +[services.googleAnalytics] +ID = 'G-XXXXXXXXX' +{{< /code-toggle >}} + +Now consider the following scenario: + +1. You don't want to load the analytics code when running `hugo server`. +1. You want to use different Google tag IDs for your production and staging environments. For example: + - `G-PPPPPPPPP` for production + - `G-SSSSSSSSS` for staging + +To satisfy these requirements, configure your site as follows: + +1. `config/_default/hugo.toml` + - Exclude the `services.googleAnalytics` section. This will prevent loading of the analytics code when you run `hugo server`. + - By default, Hugo sets its `environment` to `development` when running `hugo server`. In the absence of a `config/development` directory, Hugo uses the `config/_default` directory. +1. `config/production/hugo.toml` + - Include this section only: + + {{< code-toggle file=hugo >}} + [services.googleAnalytics] + ID = 'G-PPPPPPPPP' + {{< /code-toggle >}} + + - You do not need to include other parameters in this file. Include only those parameters that are specific to your production environment. Hugo will merge these parameters with the default configuration. + - By default, Hugo sets its `environment` to `production` when running `hugo build`. The analytics code will use the `G-PPPPPPPPP` tag ID. + +1. `config/staging/hugo.toml` + + - Include this section only: + + {{< code-toggle file=hugo >}} + [services.googleAnalytics] + ID = 'G-SSSSSSSSS' + {{< /code-toggle >}} + + - You do not need to include other parameters in this file. Include only those parameters that are specific to your staging environment. Hugo will merge these parameters with the default configuration. + - To build your staging site, run `hugo build --environment staging`. The analytics code will use the `G-SSSSSSSSS` tag ID. + +## Merge configuration settings + +Hugo merges configuration settings from themes and modules, prioritizing the project's own settings. Given this simplified project structure with two themes: + +```tree +project/ +├── themes/ +│ ├── theme-a/ +│ │ └── hugo.toml +│ └── theme-b/ +│ └── hugo.toml +└── hugo.toml +``` + +and this project-level configuration: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +locale = 'en-us' +title = 'My New Hugo Site' +theme = ['theme-a','theme-b'] +{{< /code-toggle >}} + +Hugo merges settings in this order: + +1. Project configuration (`hugo.toml` in the project root) +1. `theme-a` configuration +1. `theme-b` configuration + +The `_merge` setting within each top-level configuration key controls _which_ settings are merged and _how_ they are merged. + +The value for `_merge` can be one of: + +`none` +: No merge. + +`shallow` +: Only add values for new keys. + +`deep` +: Add values for new keys, merge existing. + +Note that you don't need to be so verbose as in the default setup below; a `_merge` value higher up will be inherited if not set. + +{{< code-toggle file=hugo dataKey="config_helpers.mergeStrategy" skipHeader=true />}} + +> [!NOTE] +> Hugo can merge map configuration values from modules and themes into the project configuration, but cannot merge slice values. This applies to top-level slice keys such as `menus`, as well as to map keys whose values are slices, such as the per-kind format lists in `outputs`. + +## Environment variables + +You can also configure settings using operating system environment variables: + +```sh +export HUGO_BASEURL=https://example.org/ +export HUGO_ENABLEGITINFO=true +hugo +``` + +The above configures the [`baseURL`][] and [`enableGitInfo`][] settings and then builds your site. + +> [!NOTE] +> An environment variable takes precedence over the values set in the configuration file. This means that if you set a configuration value with both an environment variable and in the configuration file, the value in the environment variable will be used. + +Environment variables simplify configuration for [CI/CD](g) platforms by allowing you to set values directly within their respective configuration and workflow files. + +> [!NOTE] +> Environment variable names must be prefixed with `HUGO_`. +> +> To set custom site parameters, prefix the name with `HUGO_PARAMS_`. + +For snake_case variable names, the standard `HUGO_` prefix won't work. Hugo infers the delimiter from the first character following `HUGO`. This allows for variations like `HUGOxPARAMSxAPI_KEY=abcdefgh` using any [permitted delimiter][]. + +In addition to configuring standard settings, environment variables may be used to override default values for certain internal settings: + +`DART_SASS_BINARY` +: (`string`) The absolute path to the Dart Sass executable. By default, Hugo searches for the executable in each of the paths in the `PATH` environment variable. + +`HUGO_ENVIRONMENT` +: (`string`) The build environment. Default is `production` when running `hugo build` and `development` when running `hugo server`. + +`HUGO_FILE_LOG_FORMAT` +: (`string`) A format string for the file path, line number, and column number displayed when reporting errors, or when calling the `Position` method from a shortcode or Markdown render hook. Valid tokens are `:file`, `:line`, and `:col`. Default is `:file::line::col`. + +`HUGO_MEMORYLIMIT` +: (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. Note that `HUGO_MEMORYLIMIT` is a "best effort" setting. Don't expect Hugo to build a million pages with only 1 GB of memory. You can get more information about how this behaves during the build by running `hugo build --logLevel info` and look for the `dynacache` label. + +`HUGO_NUMWORKERMULTIPLIER` +: (`int`) The number of workers used in parallel processing. Default is the number of logical CPUs. + +## Current configuration + +Display the complete project configuration with: + +```sh +hugo config +``` + +Display a specific configuration setting with: + +```sh +hugo config | grep [key] +``` + +Display the configured file mounts with: + +```sh +hugo config mounts +``` + +[Google tag ID]: https://support.google.com/tagmanager/answer/12326985?hl=en +[JSON]: https://datatracker.ietf.org/doc/html/rfc7159 +[TOML]: https://toml.io/en/latest +[YAML]: https://yaml.org/spec/ +[`baseURL`]: /configuration/all#baseurl +[`enableGitInfo`]: /configuration/all#enablegitinfo +[permitted delimiter]: https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html diff --git a/docs/content/en/configuration/languages.md b/docs/content/en/configuration/languages.md new file mode 100644 index 0000000..6313509 --- /dev/null +++ b/docs/content/en/configuration/languages.md @@ -0,0 +1,198 @@ +--- +title: Configure languages +linkTitle: Languages +description: Configure the languages in your multilingual project. +categories: [] +keywords: [] +--- + +## Base settings + +Configure the following base settings: + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'en' +defaultContentLanguageInSubdir = false +disableDefaultLanguageRedirect = false +disableLanguages = [] +{{< /code-toggle >}} + +`defaultContentLanguage` +: (`string`) The projects's default content language, conforming to the syntax described in [RFC 5646][]. This value must match one of the defined [language keys][]. Default is `en`. + +`defaultContentLanguageInSubdir` +: (`bool`) Whether to publish the default content language to a subdirectory matching the [`defaultContentLanguage`](#defaultcontentlanguage). Default is `false`. + +`disableDefaultLanguageRedirect` +: {{< new-in 0.140.0 />}} +: (`bool`) Whether to disable generation of the alias redirect for the default content language. When [`defaultContentLanguageInSubdir`](#defaultcontentlanguageinsubdir) is `true`, this setting prevents the root directory from redirecting to the language subdirectory. Conversely, when `defaultContentLanguageInSubdir` is `false`, this setting prevents the language subdirectory from redirecting to the root directory. This is superseded by the more general [`disableDefaultSiteRedirect`][] setting. Default is `false`. + +`disableLanguages` +: (`[]string]`) A slice of language keys representing the languages to disable during the build process. Although this is functional, consider using the [`disabled`](#disabled) key under each language instead. + +## Language settings + +Configure each language under the `languages` key: + +{{< code-toggle config=languages />}} + +In the above, `en` is the [language key](#language-keys). + +`direction` +: (`string`) The language direction, either left-to-right (`ltr`) or right-to-left (`rtl`). Use this value in your templates with the global [`dir`][] HTML attribute. Access this value from a template using the [`Language.Direction`][] method on a `Site` or `Page` object. Default is `ltr`. + +`disabled` +: (`bool`) Whether to disable this language when building the site. Default is `false`. + +`label` +: (`string`) The language name, typically used when rendering a language switcher. Access this value from a template using the [`Language.Label`][] method on a `Site` or `Page` object. + +`languageCode` +: {{}} +: Use [`locale`](#locale) instead. + +`languageDirection` +: {{}} +: Use [`direction`](#direction) instead. + +`languageName` +: {{}} +: Use [`label`](#label) instead. + +{{% include "/_common/configuration/locale.md" %}} + +`title` +: (`string`) The site title for this language. Access this value from a template using the [`Title`][] method on a `Site` object. + +`weight` +: (`int`) The language [weight](g). When set to a non-zero value, this is the primary sort criteria for this language. + +## Sort order + +Hugo sorts languages by weight in ascending order, then lexicographically in ascending order. This affects build order and complement selection. + +## Localized settings + +Some configuration settings can be defined separately for each language. For example: + +{{< code-toggle file=hugo >}} +[languages.en] +label = 'English' +locale = 'en-US' +timeZone = 'America/New_York' +title = 'Project Documentation' +weight = 1 +[languages.en.pagination] +path = 'page' +[languages.en.params] +subtitle = 'Reference, Tutorials, and Explanations' +{{< /code-toggle >}} + +The following configuration keys can be defined separately for each language: + +{{< per-lang-config-keys >}} + +Any key not defined in a `languages` object will fall back to the global value in the root of your project configuration. + +## Language keys + +Language keys must conform to the syntax described in [RFC 5646][]. For example: + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'de' +[languages.de] +weight = 1 +[languages.en-US] +weight = 2 +[languages.pt-BR] +weight = 3 +{{< /code-toggle >}} + +Artificial languages with private use subtags as defined in [RFC 5646 § 2.2.7][] are also supported. Omit the `art-x-` prefix from the language key. For example: + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'en' +[languages.en] +weight = 1 +[languages.hugolang] +weight = 2 +{{< /code-toggle >}} + +> [!NOTE] +> Private use subtags must not exceed 8 alphanumeric characters. + +## Example + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'de' +defaultContentLanguageInSubdir = true +disableDefaultLanguageRedirect = false + +[languages.de] +contentDir = 'content/de' +direction = 'ltr' +disabled = false +label = 'Deutsch' +locale = 'de-DE' +title = 'Projekt Dokumentation' +weight = 1 + +[languages.de.params] +subtitle = 'Referenz, Tutorials und Erklärungen' + +[languages.en] +contentDir = 'content/en' +direction = 'ltr' +disabled = false +label = 'English' +locale = 'en-US' +title = 'Project Documentation' +weight = 2 + +[languages.en.params] +subtitle = 'Reference, Tutorials, and Explanations' +{{< /code-toggle >}} + +> [!NOTE] +> In the example above, omit `contentDir` if [translating by file name][]. + +## Multihost + +Hugo supports multiple languages in a multihost configuration. This means you can configure a `baseURL` per `language`. + +> [!NOTE] +> If you define a `baseURL` for one language, you must define a unique `baseURL` for all languages. + +For example: + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'fr' +[languages.en] +baseURL = 'https://en.example.org/' +label = 'English' +title = 'In English' +weight = 2 +[languages.fr] +baseURL = 'https://fr.example.org' +label = 'Français' +title = 'En Français' +weight = 1 +{{}} + +With the above, Hugo publishes two sites, each with their own root: + +```tree +public +├── en +└── fr +``` + +[RFC 5646 § 2.2.7]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.7 +[RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 +[`Language.Direction`]: /methods/site/language/#direction +[`Language.Label`]: /methods/site/language/#label +[`Title`]: /methods/site/title/ +[`dir`]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir +[`disableDefaultSiteRedirect`]: /configuration/all/#disabledefaultsiteredirect +[language keys]: /configuration/languages/#language-keys +[translating by file name]: /content-management/multilingual/#translation-by-file-name diff --git a/docs/content/en/configuration/markup.md b/docs/content/en/configuration/markup.md new file mode 100644 index 0000000..95bdd00 --- /dev/null +++ b/docs/content/en/configuration/markup.md @@ -0,0 +1,365 @@ +--- +title: Configure markup +linkTitle: Markup +description: Configure markup. +categories: [] +keywords: [] +aliases: [/getting-started/configuration-markup/] +--- + +## Default handler + +In its default configuration, Hugo uses [Goldmark][] to render Markdown to HTML. + +{{< code-toggle file=hugo >}} +[markup] +defaultMarkdownHandler = 'goldmark' +{{< /code-toggle >}} + +Files with ending with `.md`, `.mdown`, or `.markdown` are processed as Markdown, unless you've explicitly set a different format using the `markup` field in your front matter. + +To use a different renderer for Markdown files, specify one of `asciidocext`, `org`, `pandoc`, or `rst` in your project configuration. + +`defaultMarkdownHandler` | Renderer +:------------------------|:-------------------- +`asciidocext` | [AsciiDoc][] +`goldmark` | [Goldmark][] +`org` | [Emacs Org Mode][] +`pandoc` | [Pandoc][] +`rst` | [reStructuredText][] + +To use AsciiDoc, Pandoc, or reStructuredText you must install the relevant renderer and update your [security policy][]. + +> [!NOTE] +> Unless you need a unique capability provided by one of the alternative Markdown handlers, we strongly recommend that you use the default setting. Goldmark is fast, well maintained, conforms to the [CommonMark][] specification, and is compatible with [GitHub Flavored Markdown][] (GFM). + +## Goldmark + +This is the default configuration for the Goldmark Markdown renderer: + +{{< code-toggle config=markup.goldmark />}} + +### Extensions + +The extensions below, excluding Extras and Passthrough, are enabled by default. + +Extension | Documentation | Enabled +:----------------|:----------------------------------------------|:-----------------: +`cjk` | [Goldmark Extensions: CJK][] | :heavy_check_mark: +`definitionList` | [PHP Markdown Extra: Definition lists][] | :heavy_check_mark: +`extras` | [Hugo Goldmark Extensions: Extras][] |   +`footnote` | [PHP Markdown Extra: Footnotes][] | :heavy_check_mark: +`linkify` | [GitHub Flavored Markdown: Autolinks][] | :heavy_check_mark: +`passthrough` | [Hugo Goldmark Extensions: Passthrough][] |   +`strikethrough` | [GitHub Flavored Markdown: Strikethrough][] | :heavy_check_mark: +`table` | [GitHub Flavored Markdown: Tables][] | :heavy_check_mark: +`taskList` | [GitHub Flavored Markdown: Task list items][] | :heavy_check_mark: +`typographer` | [Goldmark Extensions: Typographer][] | :heavy_check_mark: + +#### Extras + +Enable [deleted text][], [inserted text][], [mark text][], [subscript][], and [superscript][] elements in Markdown. + +Element | Markdown | Rendered +:-------------|:----------|:------------------ +Deleted text | `~~foo~~` | `foo` +Inserted text | `++bar++` | `bar` +Mark text | `==baz==` | `baz` +Subscript | `H~2~O` | `H2O` +Superscript | `1^st^` | `1st` + +To avoid a conflict[^1], if you enable the "subscript" feature of the Extras extension, you must disable the Strikethrough extension: + +{{< code-toggle file=hugo >}} +[markup.goldmark.extensions] +strikethrough = false + +[markup.goldmark.extensions.extras.subscript] +enable = true +{{< /code-toggle >}} + +If you still need to show deleted text after disabling the Strikethrough extension, enable the "deleted text" feature of the Extras extension: + +{{< code-toggle file=hugo >}} +[markup.goldmark.extensions] +strikethrough = false + +[markup.goldmark.extensions.extras.delete] +enable = true +{{< /code-toggle >}} + +With this configuration, to format text as deleted, wrap it with double-tildes. + +#### Footnote + +Enabled by default, the Footnote extension enables inclusion of footnotes in Markdown. + +`enable` +: {{< new-in 0.151.0 />}} +: (`bool`) Whether to enable the Footnotes extension. Default is `true`. + +`backlinkHTML` +: {{< new-in 0.151.0 />}} +: (`string`) The HTML to be displayed at the end of a footnote that links the user back to the corresponding reference in the main text. The default is ↩︎ (a return arrow symbol). + +`enableAutoIDPrefix` +: {{< new-in 0.151.0 />}} +: (`bool`) Whether to prepend a unique prefix to footnote IDs, preventing clashes when multiple documents are rendered together. This prefix is unique to each logical path, which means that the prefix is not unique across content dimensions such as language. Default is `false`. + +#### Passthrough + +Enable the Passthrough extension to include mathematical equations and expressions in Markdown using LaTeX markup. See [mathematics in Markdown][] for details. + +#### Typographer + +The Typographer extension replaces certain character combinations with HTML entities as specified below: + +Markdown|Replaced by|Description +:--|:--|:-- +`...`|`…`|horizontal ellipsis +`'`|`’`|apostrophe +`--`|`–`|en dash +`---`|`—`|em dash +`«`|`«`|left angle quote +`“`|`“`|left double quote +`‘`|`‘`|left single quote +`»`|`»`|right angle quote +`”`|`”`|right double quote +`’`|`’`|right single quote + +### Goldmark settings explained + +Most of the Goldmark settings above are self-explanatory, but some require explanation. + +`duplicateResourceFiles` +: (`bool`) Whether to duplicate shared page resources for each language on multilingual single-host projects. See [multilingual page resources][] for details. Default is `false`. + + > [!NOTE] + > With multilingual single-host projects, setting this parameter to `false` will enable Hugo's [embedded link render hook][] and [embedded image render hook][]. This is the default configuration for multilingual single-host projects. + +`parser.wrapStandAloneImageWithinParagraph` +: (`bool`) Whether to wrap image elements without adjacent content within a `p` element when rendered. This is the default Markdown behavior. Set to `false` when using an [image render hook][] to render standalone images as `figure` elements. Default is `true`. + +`parser.autoDefinitionTermID` +: {{< new-in 0.144.0 />}} +: (`bool`) Whether to automatically add `id` attributes to description list terms (i.e., `dt` elements). When `true`, the `id` attribute of each `dt` element is accessible through the [`Fragments.Identifiers`][] method on a `Page` object. + +`parser.autoHeadingID` +: (`bool`) Whether to automatically add `id` attributes to headings (i.e., `h1`, `h2`, `h3`, `h4`, `h5`, and `h6` elements). + +`parser.autoIDType` +: (`string`) The strategy used to automatically generate `id` attributes, one of `github`, `github-ascii` or `blackfriday`. Default is `github`. + + - `github`: Generate GitHub-compatible `id` attributes + - `github-ascii`: Drop any non-ASCII characters after accent normalization + - `blackfriday`: Generate `id` attributes compatible with the Blackfriday Markdown renderer + + This is also the strategy used by the [`urls.Anchorize`][] function. + +`parser.attribute.block` +: (`bool`) Whether to enable [Markdown attributes][] for block elements. Default is `false`. + +`parser.attribute.title` +: (`bool`) Whether to enable [Markdown attributes][] for headings. Default is `true`. + +`renderHooks.image.enableDefault` +: {{< deprecated-in 0.148.0 />}} +: Use the `renderHooks.image.useEmbedded` setting instead. + +`renderHooks.image.useEmbedded` +: {{< new-in 0.148.0 />}} +: (`string`) When to use the [embedded image render hook][]. One of `auto`, `never`, `always`, or `fallback`. Default is `auto`. + + - `auto`: Use the embedded image render hook only for multilingual single-host projects where the [duplication of shared page resources][] feature is disabled. If custom image render hooks are defined by your project, modules, or themes, these will be used instead. + - `never`: Never use the embedded image render hook. If custom image render hooks are defined by your project, modules, or themes, these will be used instead. + - `always`: Always use the embedded image render hook, even if custom image render hooks are provided by your project, modules, or themes. + - `fallback`: Use the embedded image render hook only if custom image render hooks are not provided by your project, modules, or themes. If custom image render hooks exist, these will be used instead. + +`renderHooks.link.enableDefault` +: {{< deprecated-in 0.148.0 />}} +: Use the `renderHooks.link.useEmbedded` setting instead. + +`renderHooks.link.useEmbedded` +: (`string`) When to use the [embedded link render hook][]. One of `auto`, `never`, `always`, or `fallback`. Default is `auto`. + + - `auto`: Use the embedded link render hook only for multilingual single-host projects where the [duplication of shared page resources][] feature is disabled. If custom link render hooks are defined by your project, modules, or themes, these will be used instead. + - `never`: Never use the embedded link render hook. If custom link render hooks are defined by your project, modules, or themes, these will be used instead. + - `always`: Always use the embedded link render hook, even if custom link render hooks are provided by your project, modules, or themes. + - `fallback`: Use the embedded link render hook only if custom link render hooks are not provided by your project, modules, or themes. If custom link render hooks exist, these will be used instead. + +`renderer.hardWraps` +: (`bool`) Whether to replace newline characters within a paragraph with `br` elements. Default is `false`. + +`renderer.unsafe` +: (`bool`) Whether to render raw HTML mixed within Markdown. This is unsafe unless the content is under your control. Default is `false`. + +## AsciiDoc + +This is the default configuration for the AsciiDoc renderer: + +{{< code-toggle config=markup.asciidocExt />}} + +### AsciiDoc settings explained + +`attributes` +: (`map`) A map of key-value pairs, each a document attribute. See Asciidoctor's [attributes][]. + +`backend` +: (`string`) The backend output file format. Default is `html5`. + +`extensions` +: (`[]string`) An array of enabled extensions, such as `asciidoctor-html5s`, `asciidoctor-bibtex`, or `asciidoctor-diagram`. + + > [!NOTE] + > To mitigate security risks, entries in the extension array may not contain forward slashes (`/`), backslashes (`\`), or periods. Due to this restriction, extensions must be in Ruby's `$LOAD_PATH`. + +`failureLevel` +: (`string`) The minimum logging level that triggers a non-zero exit code (failure). Default is `fatal`. + +`noHeaderOrFooter` +: (`bool`) Whether to output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. Default is `true`. + +`preserveTOC` +: (`bool`) Whether to preserve the table of contents (TOC) rendered by Asciidoctor. By default, to make the TOC compatible with existing themes, Hugo removes the TOC rendered by Asciidoctor. To render the TOC, use the [`TableOfContents`][] method on a `Page` object in your templates. Default is `false`. + +`safeMode` +: (`string`) The safe mode level, one of `unsafe`, `safe`, `server`, or `secure`. Default is `unsafe`. + +`sectionNumbers` +: (`bool`) Whether to number each section title. Default is `false`. + +`trace` +: (`bool`) Whether to include backtrace information on errors. Default is `false`. + +`verbose` +: (`bool`) Whether to verbosely print processing information and configuration file checks to stderr. Default is `false`. + +`workingFolderCurrent` +: (`bool`) Whether to set the working directory to be the same as that of the AsciiDoc file being processed, allowing [includes][] to work with relative paths. Set to `true` to render diagrams with the [asciidoctor-diagram][] extension. Default is `false`. + +### Configuration example + +{{< code-toggle file=hugo >}} +[markup.asciidocExt] +extensions = ['asciidoctor-html5s','asciidoctor-diagram'] +workingFolderCurrent = true +[markup.asciidocExt.attributes] +my-base-url = 'https://example.com/' +my-attribute-name = 'my value' +{{< /code-toggle >}} + +### Syntax highlighting + +Follow the steps below to enable syntax highlighting. + +Step 1 +: Set the `source-highlighter` attribute in your project configuration. For example: + + {{< code-toggle file=hugo >}} + [markup.asciidocExt.attributes] + source-highlighter = 'rouge' + {{< /code-toggle >}} + +Step 2 +: Generate the highlighter CSS. For example: + + ```sh + rougify style monokai.sublime > assets/css/syntax.css + ``` + +Step 3 +: In your base template add a link to the CSS file: + + ```go-html-template {file="layouts/baseof.html"} + + ... + {{ with resources.Get "css/syntax.css" }} + + {{ end }} + ... + + ``` + +Step 4 +: Add the code to be highlighted to your markup: + + ```text + [#hello,ruby] + ---- + require 'sinatra' + + get '/hi' do + "Hello World!" + end + ---- + ``` + +### Troubleshooting + +Run `hugo build --logLevel debug` to examine Hugo's call to the Asciidoctor executable: + +```txt +INFO 2019/12/22 09:08:48 Rendering book-as-pdf.adoc with C:\Ruby26-x64\bin\asciidoctor.bat using asciidoc args [--no-header-footer -r asciidoctor-html5s -b html5s -r asciidoctor-diagram --base-dir D:\prototypes\hugo_asciidoc_ddd\docs -a outdir=D:\prototypes\hugo_asciidoc_ddd\build -] ... +``` + +## Highlight + +This is the default configuration. + +{{< code-toggle config=markup.highlight />}} + +{{% include "/_common/syntax-highlighting-options.md" %}} + +## Table of contents + +This is the default configuration for the table of contents, applicable to Goldmark and Asciidoctor: + +{{< code-toggle config=markup.tableOfContents />}} + +`startLevel` +: (`int`) Heading levels less than this value will be excluded from the table of contents. For example, to exclude `h1` elements from the table of contents, set this value to `2`. Default is `2`. + +`endLevel` +: (`int`) Heading levels greater than this value will be excluded from the table of contents. For example, to exclude `h4`, `h5`, and `h6` elements from the table of contents, set this value to `3`. Default is `3`. + +`ordered` +: (`bool`) Whether to generates an ordered list instead of an unordered list. Default is `false`. + +[^1]: See [details](https://github.com/gohugoio/hugo-goldmark-extensions/commit/4d4fcd022fe45a9b51483df001c9e5f4e632d5a9). + +[AsciiDoc]: https://asciidoc.org/ +[CommonMark]: https://spec.commonmark.org/current/ +[Emacs Org Mode]: https://orgmode.org/ +[GitHub Flavored Markdown: Autolinks]: https://github.github.com/gfm/#autolinks-extension- +[GitHub Flavored Markdown: Strikethrough]: https://github.github.com/gfm/#strikethrough-extension- +[GitHub Flavored Markdown: Tables]: https://github.github.com/gfm/#tables-extension- +[GitHub Flavored Markdown: Task list items]: https://github.github.com/gfm/#task-list-items-extension- +[GitHub Flavored Markdown]: https://github.github.com/gfm/ +[Goldmark Extensions: CJK]: https://github.com/yuin/goldmark?tab=readme-ov-file#cjk-extension +[Goldmark Extensions: Typographer]: https://github.com/yuin/goldmark?tab=readme-ov-file#typographer-extension +[Goldmark]: https://github.com/yuin/goldmark/ +[Hugo Goldmark Extensions: Extras]: https://github.com/gohugoio/hugo-goldmark-extensions?tab=readme-ov-file#extras-extension +[Hugo Goldmark Extensions: Passthrough]: https://github.com/gohugoio/hugo-goldmark-extensions?tab=readme-ov-file#passthrough-extension +[Markdown attributes]: /content-management/markdown-attributes/ +[PHP Markdown Extra: Definition lists]: https://michelf.ca/projects/php-markdown/extra/#def-list +[PHP Markdown Extra: Footnotes]: https://michelf.ca/projects/php-markdown/extra/#footnotes +[Pandoc]: https://pandoc.org/ +[`Fragments.Identifiers`]: /methods/page/fragments/#identifiers +[`TableOfContents`]: /methods/page/tableofcontents/ +[`urls.Anchorize`]: /functions/urls/anchorize/ +[asciidoctor-diagram]: https://asciidoctor.org/docs/asciidoctor-diagram/ +[attributes]: https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#attributes-and-substitutions +[deleted text]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del +[duplication of shared page resources]: /configuration/markup/#duplicateresourcefiles +[embedded image render hook]: /render-hooks/images/#embedded +[embedded link render hook]: /render-hooks/links/#embedded +[image render hook]: /render-hooks/images/ +[includes]: https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/#includes +[inserted text]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins +[mark text]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark +[mathematics in Markdown]: /content-management/mathematics/ +[multilingual page resources]: /content-management/page-resources/#multilingual +[reStructuredText]: https://docutils.sourceforge.io/rst.html +[security policy]: /configuration/security/ +[subscript]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub +[superscript]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup diff --git a/docs/content/en/configuration/media-types.md b/docs/content/en/configuration/media-types.md new file mode 100644 index 0000000..546761f --- /dev/null +++ b/docs/content/en/configuration/media-types.md @@ -0,0 +1,82 @@ +--- +title: Configure media types +linkTitle: Media types +description: Configure media types. +categories: [] +keywords: [] +--- + +{{% glossary-term "media type" %}} + +Configured media types serve multiple purposes in Hugo, including the definition of [output formats](g). This is the default media type configuration in tabular form: + +{{< datatable "config" "mediaTypes" "_key" "suffixes" >}} + +The `suffixes` column in the table above shows the suffixes associated with each media type. For example, Hugo associates `.html` and `.htm` files with the `text/html` media type. + +> [!NOTE] +> The first suffix is the primary suffix. Use the primary suffix when naming template files. For example, when creating a template for an RSS feed, use the `xml` suffix. + +## Default configuration + +The following is the default configuration that matches the table above: + +{{< code-toggle file=hugo config=mediaTypes />}} + +`delimiter` +: (`string`) The delimiter between the file name and the suffix. The delimiter, in conjunction with the suffix, forms the file extension. Default is `"."`. + +`suffixes` +: (`[]string`) The suffixes associated with this media type. The first suffix is the primary suffix. + +## Modify a media type + +You can modify any of the default media types. For example, to switch the primary suffix for `text/html` from `html` to `htm`: + +{{< code-toggle file=hugo >}} +[mediaTypes.'text/html'] +suffixes = ['htm','html'] +{{< /code-toggle >}} + +If you alter a default media type, you must also explicitly redefine all output formats that utilize that media type. For example, to ensure the changes above affect the `html` output format, redefine the `html` output format: + +{{< code-toggle file=hugo >}} +[outputFormats.html] +mediaType = 'text/html' +{{< /code-toggle >}} + +## Create a media type + +You can create new media types as needed. For example, to create a media type for an Atom feed: + +{{< code-toggle file=hugo >}} +[mediaTypes.'application/atom+xml'] +suffixes = ['atom'] +{{< /code-toggle >}} + +## Media types without suffixes + +Occasionally, you may need to create a media type without a suffix or delimiter. For example, [Netlify][] recognizes configuration files named `_redirects` and `_headers`, which Hugo can generate using custom [output formats](g). + +To support these custom output formats, register a custom media type with no suffix or delimiter: + +{{< code-toggle file=hugo >}} +[mediaTypes.'text/netlify'] +delimiter = '' +{{< /code-toggle >}} + +The custom output format definitions would look something like this: + +{{< code-toggle file=hugo >}} +[outputFormats.redir] +baseName = '_redirects' +isPlainText = true +mediatype = 'text/netlify' +[outputFormats.headers] +baseName = '_headers' +isPlainText = true +mediatype = 'text/netlify' +notAlternative = true +{{< /code-toggle >}} + +[Netlify]: https://www.netlify.com/ diff --git a/docs/content/en/configuration/menus.md b/docs/content/en/configuration/menus.md new file mode 100644 index 0000000..a88ec7b --- /dev/null +++ b/docs/content/en/configuration/menus.md @@ -0,0 +1,137 @@ +--- +title: Configure menus +linkTitle: Menus +description: Centrally define menu entries for one or more menus. +categories: [] +keywords: [] +--- + +> [!NOTE] +> To understand Hugo's menu system, please refer to the [menus][] page. + +There are three ways to define menu entries: + +1. [Automatically][] +1. In [front matter][] +1. In your project configuration + +This page covers the project configuration method. + +## Example + +To define entries for a "main" menu: + +{{< code-toggle file=hugo >}} +[[menus.main]] +name = 'Home' +pageRef = '/' +weight = 10 + +[[menus.main]] +name = 'Products' +pageRef = '/products' +weight = 20 + +[[menus.main]] +name = 'Services' +pageRef = '/services' +weight = 30 +{{< /code-toggle >}} + +This creates a menu structure that you can access with [`Menus`][] method on a `Site` object: + +```go-html-template +{{ range .Site.Menus.main }} + ... +{{ end }} +``` + +See [menu templates][] for a detailed example. + +To define entries for a "footer" menu: + +{{< code-toggle file=hugo >}} +[[menus.footer]] +name = 'Terms' +pageRef = '/terms' +weight = 10 + +[[menus.footer]] +name = 'Privacy' +pageRef = '/privacy' +weight = 20 +{{< /code-toggle >}} + +Access this menu structure in the same way: + +```go-html-template +{{ range .Site.Menus.footer }} + ... +{{ end }} +``` + +## Properties + +Menu entries usually include at least three properties: `name`, `weight`, and either `pageRef` or `url`. Use `pageRef` for internal page destinations and `url` for external destinations. + +These are the available menu entry properties: + +{{% include "/_common/menu-entry-properties.md" %}} + +`pageRef` +: (`string`) The [logical path](g) of the target page. For example: + + page kind|pageRef + :--|:-- + home|`/` + page|`/books/book-1` + section|`/books` + taxonomy|`/tags` + term|`/tags/foo` + +`url` +: (`string`) The destination URL. Use this for external destinations only. + +## Nested menu + +This nested menu demonstrates some of the available properties: + + +{{< code-toggle file=hugo >}} +[[menus.main]] +name = 'Products' +pageRef = '/products' +weight = 10 + +[[menus.main]] +name = 'Hardware' +pageRef = '/products/hardware' +parent = 'Products' +weight = 1 + +[[menus.main]] +name = 'Software' +pageRef = '/products/software' +parent = 'Products' +weight = 2 + +[[menus.main]] +name = 'Services' +pageRef = '/services' +weight = 20 + +[[menus.main]] +name = 'Hugo' +pre = '' +url = 'https://gohugo.io/' +weight = 30 +[menus.main.params] +rel = 'external' +{{< /code-toggle >}} + + +[Automatically]: /content-management/menus/#define-automatically +[`Menus`]: /methods/site/menus/ +[front matter]: /content-management/menus/#define-in-front-matter +[menu templates]: /templates/menu/ +[menus]: /content-management/menus/ diff --git a/docs/content/en/configuration/minify.md b/docs/content/en/configuration/minify.md new file mode 100644 index 0000000..9329879 --- /dev/null +++ b/docs/content/en/configuration/minify.md @@ -0,0 +1,18 @@ +--- +title: Configure minify +linkTitle: Minify +description: Configure minify. +categories: [] +keywords: [] +--- + +This is the default configuration: + +{{< code-toggle config=minify />}} + +See the [`tdewolff/minify`][] project page for details, but note the following: + +- `css.inline` is for internal use. Changing this setting has no effect. +- `html.keepConditionalComments` has been deprecated. Use `html.keepSpecialComments` instead. + +[`tdewolff/minify`]: https://github.com/tdewolff/minify diff --git a/docs/content/en/configuration/module.md b/docs/content/en/configuration/module.md new file mode 100644 index 0000000..114d0eb --- /dev/null +++ b/docs/content/en/configuration/module.md @@ -0,0 +1,208 @@ +--- +title: Configure modules +linkTitle: Modules +description: Configure modules. +categories: [] +keywords: [] +aliases: [/hugo-modules/configuration/] +--- + +{{% include "/_common/gomodules-info.md" %}} + +## Top-level settings + +This is the default configuration: + + +{{< code-toggle file=hugo >}} +[module] +noProxy = 'none' +noVendor = '' +private = '*.*' +proxy = 'direct' +vendorClosest = false +workspace = 'off' +{{< /code-toggle >}} + + +`auth` +: {{< new-in 0.144.0 />}} +: (`string`) Configures `GOAUTH` when running the Go command for module operations. This is a semicolon-separated list of authentication commands for go-import and HTTPS module mirror interactions. This is useful for private repositories. See `go help goauth` for more information. + +`noProxy` +: (`string`) A comma-separated list of [glob patterns](g), matching paths that should not use the [configured proxy server](#proxy). + +`noVendor` +: (`string`) A [glob pattern](g) matching module paths to skip when vendoring. + +`private` +: (`string`) A comma-separated list of [glob patterns](g), matching paths that should be treated as private. + +`proxy` +: (`string`) The proxy server to use to download remote modules. Default is `direct`, which means `git clone` and similar. + +`replacements` +: (`string`) Primarily useful for local module development, a comma-separated list of mappings from module paths to directories. Paths may be absolute or relative to the [`themesDir`][]. + + {{< code-toggle file=hugo >}} + [module] + replacements = 'github.com/bep/my-theme -> ../..,github.com/bep/shortcodes -> /some/path' + {{< /code-toggle >}} + +`vendorClosest` +: (`bool`) Whether to pick the vendored module closest to the module using it. The default behavior is to pick the first. Note that there can still be only one dependency of a given module path, so once it is in use it cannot be redefined. Default is `false`. + +`workspace` +: (`string`) The Go workspace file to use, either as an absolute path or a path relative to the current working directory. Enabling this activates Go workspace mode and requires Go 1.18 or later. The default is `off`. + +You may also use environment variables to set any of the above. For example: + +```sh +export HUGO_MODULE_PROXY="https://proxy.example.org" +export HUGO_MODULE_REPLACEMENTS="github.com/bep/my-theme -> ../.." +export HUGO_MODULE_WORKSPACE="/my/hugo.work" +``` + +## Hugo version + +You can specify a required Hugo version for your module in the `module` section. Users will then receive a warning if their Hugo version is incompatible. + +This is the default configuration: + +{{< code-toggle config=module.hugoVersion />}} + +You can omit any of the settings above. + +`extended` +: {{< deprecated-in v0.153.0 />}} +: (`bool`) Whether the extended edition of Hugo is required, satisfied by installing either the extended or extended/deploy edition. + + > [!NOTE] + > The extended version check is disabled in v0.153.2 and later. + > + > Historically, certain features—specifically WebP encoding and LibSass—required the Hugo Extended binary. However, as of v0.153.0: + > + > - WebP encoding is now supported in all Hugo editions. + > - LibSass has been deprecated in favor of [Dart Sass][], which is compatible with any Hugo edition. + > + > Because these dependencies no longer require a specialized binary, the internal enforcement check for the extended version has been removed. Site and theme authors are encouraged to use Dart Sass to ensure cross-edition compatibility. + +`max` +: (`string`) The maximum Hugo version supported, for example `0.153.0`. + +`min` +: (`string`) The minimum Hugo version supported, for example `0.102.0`. + +## Imports + +{{< code-toggle file=hugo >}} +[[module.imports]] +disable = false +ignoreConfig = false +ignoreImports = false +path = 'github.com/gohugoio/hugoTestModules1_linux/modh1_2_1v' +[[module.imports]] +path = 'my-shortcodes' +{{< /code-toggle >}} + +`disable` +: (`bool`) Whether to disable the module but keep version information in the `go.*` files. Default is `false`. + +`ignoreConfig` +: (`bool`) Whether to ignore module configuration files, for example, `hugo.toml`. This will also prevent loading of any transitive module dependencies. Default is `false`. + +`ignoreImports` +: (`bool`) Whether to ignore module imports. Default is `false`. + +`noMounts` +: (`bool`) Whether to disable directory mounting for this import. Default is `false`. + +`noVendor` +: (`bool`) Whether to disable vendoring for this import. This setting is restricted to the main project. Default is `false`. + +`usePackageJSON` +: {{< new-in 0.159.0 />}} +: (`string`) Whether to use the import's npm dependencies in [hugo mod npm pack](commands/hugo_mod_npm_pack/). One of `auto` (default), `always` or `never`. When set to `auto`, Hugo will enable this if either there is a Hugo config file (e.g. `hugo.toml`) or a `package.hugo.json` file in the module root. + +`path` +: (`string`) The module path, either a valid Go module path (e.g., `github.com/gohugoio/myShortcodes`) or the directory name if stored in the [`themesDir`][]. + +`version` +: {{< new-in 0.150.0 />}} +: (`string`) If set to a [version query][], this import becomes a direct dependency, in contrast to dependencies managed by Go modules. See [this issue][] for more information. + +## Mounts + +{{% glossary-term mount %}} + +> [!IMPORTANT] +> If you define one or more mounts to map a file system path to a component path, do not use these legacy configuration settings: [`archetypeDir`][], [`assetDir`][], [`contentDir`][], [`dataDir`][], [`i18nDir`][], [`layoutDir`][], or [`staticDir`][]. + +### Default mounts + +Defining a mount for a component within a project configuration removes the default mount for that component. + +Defining a mount for a component within a module configuration removes all default mounts for that module. + +If you still need any of the default mounts, you must explicitly add them along with the new mount. + +These are the default mounts: + +{{< code-toggle config=module.mounts />}} + +`source` +: (`string`) The source directory of the mount. For the main project, this can be either project-relative or absolute. For other modules it must be project-relative. + +`target` +: (`string`) Where the mount will reside within Hugo's [unified file system](g). It must begin with one of Hugo's [component](g) directories: archetypes, assets, content, data, i18n, layouts, or static. For example, content/blog. + +`disableWatch` +: (`bool`) Whether to disable watching in watch mode for this mount. Default is `false`. + +`excludeFiles` +: {{< deprecated-in 0.153.0 />}} +: Use the [`files`](#files) setting instead. + +`files` +: {{< new-in 0.153.0 />}} +: (`[]string`) A [glob slice](g) defining the files to include or exclude. + +`includeFiles` +: {{< deprecated-in 0.153.0 />}} +: Use the [`files`](#files) setting instead. + +`lang` +: {{< deprecated-in 0.153.0 />}} +: Use the [`sites`](#sites) setting instead. + +`sites` +: {{< new-in 0.153.0 />}} +: (`map`) A map to define [sites matrix](g) and [sites complements](g) for the mount. Relevant for `content` and `layouts` mounts, and `static` mounts when in multihost mode. For `static` and `layouts`, only the `matrix` keyword is supported. + +### Example + +{{< code-toggle file=hugo >}} +[module] +[[module.mounts]] +source = 'content' +target = 'content' +files = ['! docs/*'] +[[module.mounts]] +source = 'node_modules' +target = 'assets' +[[module.mounts]] +source = 'assets' +target = 'assets' +{{< /code-toggle >}} + +[Dart Sass]: /functions/css/sass/#dart-sass +[`archetypeDir`]: /configuration/all/#archetypedir +[`assetDir`]: /configuration/all/#assetdir +[`contentDir`]: /configuration/all/#contentdir +[`dataDir`]: /configuration/all/#datadir +[`i18nDir`]: /configuration/all/#i18ndir +[`layoutDir`]: /configuration/all/#layoutdir +[`staticDir`]: /configuration/all/#staticdir +[`themesDir`]: /configuration/all/#themesdir +[this issue]: https://github.com/gohugoio/hugo/pull/13966 +[version query]: https://go.dev/ref/mod#version-queries diff --git a/docs/content/en/configuration/output-formats.md b/docs/content/en/configuration/output-formats.md new file mode 100644 index 0000000..99ca158 --- /dev/null +++ b/docs/content/en/configuration/output-formats.md @@ -0,0 +1,206 @@ +--- +title: Configure output formats +linkTitle: Output formats +description: Configure output formats. +categories: [] +keywords: [] +--- + +{{% glossary-term "output format" %}} + +You can output a page in as many formats as you want. Define an infinite number of output formats, provided they each resolve to a unique file system path. + +This is the default output format configuration in tabular form: + +{{< datatable + "config" + "outputFormats" + "_key" + "mediaType" + "weight" + "baseName" + "isHTML" + "isPlainText" + "noUgly" + "notAlternative" + "path" + "permalinkable" + "protocol" + "rel" + "root" + "ugly" +>}} + +## Default configuration + +The following is the default configuration that matches the table above: + +{{< code-toggle config=outputFormats />}} + +`baseName` +: (`string`) The base name of the published file. Default is `index`. + +`isHTML` +: (`bool`) Whether to classify the output format as HTML. This value determines when the LiveReload script is injected and, in conjunction with [`permalinkable`](#permalinkable), whether [alias redirects][] are generated. Default is `false`. + +`isPlainText` +: (`bool`) Whether to parse templates for this output format with Go's [`text/template`][] package instead of the [`html/template`][] package. Default is `false`. + +`mediaType` +: (`string`) The [media type](g) of the published file. This must match one of the [configured media types][]. + +`notAlternative` +: (`bool`) Whether to exclude this output format from the values returned by the [`AlternativeOutputFormats`][] method on a `Page` object. Default is `false`. + +`noUgly` +: (`bool`) Whether to disable ugly URLs for this output format when [`uglyURLs`][] are enabled in your project configuration. Default is `false`. + +`path` +: (`string`) The first segment of the publication path for this output format. This path segment is relative to the root of your [`publishDir`][]. If omitted, Hugo will use the file's original content path for publishing. + +`permalinkable` +: (`bool`) Whether to return the rendering output format rather than the main output format when invoking the [`Permalink`][] and [`RelPermalink`][] methods on a `Page` object. Along with [`isHTML`](#ishtml), this must be `true` to create [alias redirects][]. Enabled by default for the `html` and `amp` output formats. Default is `false`. + +`protocol` +: (`string`) The protocol (scheme) of the URL for this output format. For example, `https://` or `webcal://`. Default is the scheme of the [`baseURL`][] parameter in your project configuration, typically `https://`. + +`rel` +: (`string`) The relationship of the output format to the current page. Hugo uses this property to determine the [canonical output format](g) of the current page. For the predefined `html` output format, the default value is `canonical`; for all other predefined output formats, the default value is `alternate`. + +`root` +: (`bool`) Whether to publish files to the root of the publish directory. Default is `false`. + +`ugly` +: (`bool`) Whether to enable uglyURLs for this output format when `uglyURLs` is `false` in your project configuration. Default is `false`. + +`weight` +: (`int`) When set to a non-zero value, Hugo uses the `weight` as the first criteria when sorting output formats, falling back to the name of the output format. Lighter items float to the top, while heavier items sink to the bottom. Hugo renders output formats sequentially based on the sort order. Default is `0`, except for the `html` output format, which has a default weight of `10`. + +## Modify an output format + +You can modify any of the default output formats. For example, to prioritize `json` rendering over `html` rendering, when both are generated, adjust the [`weight`](#weight): + +{{< code-toggle file=hugo >}} +[outputFormats.json] +weight = 1 +[outputFormats.html] +weight = 2 +{{< /code-toggle >}} + +The example above shows that when you modify a default content format, you only need to define the properties that differ from their default values. + +## Create an output format + +You can create new output formats as needed. For example, you may wish to create an output format to support Atom feeds. + +Step 1 +: Output formats require a specified media type. Because Atom feeds use `application/atom+xml`, which is not one of the [default media types][], you must create it first. + + {{< code-toggle file=hugo >}} + [mediaTypes.'application/atom+xml'] + suffixes = ['atom'] + {{< /code-toggle >}} + + See [configure media types][] for more information. + +Step 2 +: Create a new output format: + + {{< code-toggle file=hugo >}} + [outputFormats.atom] + mediaType = 'application/atom+xml' + noUgly = true + {{< /code-toggle >}} + + Note that we use the default settings for all other output format properties. + +Step 3 +: Specify the page [kinds](g) for which to render this output format: + + {{< code-toggle file=hugo >}} + [outputs] + home = ['html', 'rss', 'atom'] + section = ['html', 'rss', 'atom'] + taxonomy = ['html', 'rss', 'atom'] + term = ['html', 'rss', 'atom'] + {{< /code-toggle >}} + + See [configure outputs][] for more information. + +Step 4 +: Create a template to render the output format. Since Atom feeds are lists, you need to create a list template. Consult the [template lookup order][] to find the correct template path: + + ```text + layouts/list.atom.atom + ``` + + We leave writing the template code as an exercise for you. Aim for a result similar to the [embedded RSS template][]. + +## List output formats + +To access output formats, each `Page` object provides two methods: [`OutputFormats`][] (for all formats, including the current one) and [`AlternativeOutputFormats`][]. Use `AlternativeOutputFormats` to create a link `rel` list within a `head` element, as shown below: + +```go-html-template +{{ range .AlternativeOutputFormats }} + +{{ end }} +``` + +## Link to output formats + +By default, a `Page` object's [`Permalink`][] and [`RelPermalink`][] methods return the URL of the [primary output format](g), typically `html`. This behavior remains consistent regardless of the template used. + +For example, in `page.json.json`, you'll see: + +```go-html-template +{{ .RelPermalink }} → /that-page/ +{{ with .OutputFormats.Get "json" }} + {{ .RelPermalink }} → /that-page/index.json +{{ end }} +``` + +To make these methods return the URL of the _current_ template's output format, you must set the [`permalinkable`](#permalinkable) setting to `true` for that format. + +With `permalinkable` set to true for `json` in the same `page.json.json` template: + +```go-html-template +{{ .RelPermalink }} → /that-page/index.json +{{ with .OutputFormats.Get "html" }} + {{ .RelPermalink }} → /that-page/ +{{ end }} +``` + +## Template lookup order + +Each output format requires a template conforming to the [template lookup order][]. + +For the highest specificity in the template lookup order, include the page kind, output format, and suffix in the file name: + +```text +[page kind].[output format].[suffix] +``` + +For example, for section pages: + +Output format|Template path +:--|:-- +`html`|`layouts/section.html.html` +`json`|`layouts/section.json.json` +`rss`|`layouts/section.rss.xml` + +[`AlternativeOutputFormats`]: /methods/page/alternativeoutputformats/ +[`OutputFormats`]: /methods/page/outputformats/ +[`Permalink`]: /methods/page/permalink/ +[`RelPermalink`]: /methods/page/relpermalink/ +[`baseURL`]: /configuration/all/#baseurl +[`html/template`]: https://pkg.go.dev/html/template +[`publishDir`]: /configuration/all/#publishdir +[`text/template`]: https://pkg.go.dev/text/template +[`uglyURLs`]: /configuration/ugly-urls/ +[alias redirects]: /content-management/urls/#aliases +[configure media types]: /configuration/media-types/ +[configure outputs]: /configuration/outputs/ +[configured media types]: /configuration/media-types/ +[default media types]: /configuration/media-types/ +[embedded RSS template]: <{{% eturl rss %}}> +[template lookup order]: /templates/lookup-order/ diff --git a/docs/content/en/configuration/outputs.md b/docs/content/en/configuration/outputs.md new file mode 100644 index 0000000..ff8c58f --- /dev/null +++ b/docs/content/en/configuration/outputs.md @@ -0,0 +1,49 @@ +--- +title: Configure outputs +linkTitle: Outputs +description: Configure which output formats to render for each page kind. +categories: [] +keywords: [] +--- + +{{% glossary-term "output format" %}} + +Learn more about creating and configuring output formats in the [configure output formats][] section. + +## Outputs per page kind + +The following default configuration determines the output formats generated for each page kind: + +{{< code-toggle config=outputs />}} + +To render the built-in `json` output format for the `home` page kind, assuming you've already created the necessary template, add the following to your configuration: + +{{< code-toggle file=hugo >}} +[outputs] +home = ['html','rss','json'] +{{< /code-toggle >}} + +Notice in this example that we only specified the `home` page kind. You don't need to include entries for other page kinds unless you intend to modify their default output formats. + +> [!NOTE] +> The order of the output formats in the arrays above is important. The first element will be the _primary output format_ for that page kind, and in most cases that should be `html` as shown in the default configuration. +> +> The primary output format for a given page kind determines the value returned by the [`Permalink`][] and [`RelPermalink`][] methods on a `Page` object. +> +> See the [link to output formats][] section for details. + +## Outputs per page + +Add output formats to a page's rendering using the `outputs` field in its front matter. For example, to include `json` in the output formats rendered for a specific page: + +{{< code-toggle file=content/example.md fm=true >}} +title = 'Example' +outputs = ['json'] +{{< /code-toggle >}} + +In its default configuration, Hugo will render both the `html` and `json` output formats for this page. The `outputs` field appends to, rather than replaces, the project's configured outputs. + +[`Permalink`]: /methods/page/permalink/ +[`RelPermalink`]: /methods/page/relpermalink/ +[configure output formats]: /configuration/output-formats/ +[link to output formats]: configuration/output-formats/#link-to-output-formats diff --git a/docs/content/en/configuration/page.md b/docs/content/en/configuration/page.md new file mode 100644 index 0000000..d5ed72a --- /dev/null +++ b/docs/content/en/configuration/page.md @@ -0,0 +1,42 @@ +--- +title: Configure page +linkTitle: Page +description: Configure page behavior. +categories: [] +keywords: [] +--- + +{{% glossary-term "default sort order" %}} + +Hugo uses the default sort order to determine the _next_ and _previous_ page relative to the current page when calling these methods on a `Page` object: + +- [`Next`][] and [`Prev`][] +- [`NextInSection`][] and [`PrevInSection`][] + +This is based on this default project configuration: + +{{< code-toggle config=page />}} + +`nextPrevInSectionSortOrder` +: (`string`) The sort order used to determine the _next_ and _previous_ page within the same section when calling [`NextInSection`][] or [`PrevInSection`][] on a `Page` object. Valid values are `asc` (ascending) or `desc` (descending). Default is `desc`. + +`nextPrevSortOrder` +: (`string`) The sort order used to determine the _next_ and _previous_ page when calling [`Next`][] or [`Prev`][] on a `Page` object. Valid values are `asc` (ascending) or `desc` (descending). Default is `desc`. + +To reverse the meaning of _next_ and _previous_: + +{{< code-toggle file=hugo >}} +[page] + nextPrevInSectionSortOrder = 'asc' + nextPrevSortOrder = 'asc' +{{< /code-toggle >}} + +> [!NOTE] +> These settings do not apply to the [`Next`][next-pages] or [`Prev`][prev-pages] methods on a `Pages` object. + +[`NextInSection`]: /methods/page/nextinsection/ +[`Next`]: /methods/page/next/ +[`PrevInSection`]: /methods/page/previnsection/ +[`Prev`]: /methods/page/prev/ +[next-pages]: /methods/pages/next/ +[prev-pages]: /methods/pages/prev/ diff --git a/docs/content/en/configuration/pagination.md b/docs/content/en/configuration/pagination.md new file mode 100644 index 0000000..ded0b99 --- /dev/null +++ b/docs/content/en/configuration/pagination.md @@ -0,0 +1,45 @@ +--- +title: Configure pagination +linkTitle: Pagination +description: Configure pagination. +categories: [] +keywords: [] +--- + +This is the default configuration: + +{{< code-toggle config=pagination />}} + +`disableAliases` +: (`bool`) Whether to disable alias generation for the first pager. Default is `false`. + +`pagerSize` +: (`int`) The number of pages per pager. Default is `10`. + +`path` +: (`string`) The segment of each pager URL indicating that the target page is a pager. Default is `page`. + +With multilingual projects you can define the pagination behavior for each language: + +{{< code-toggle file=hugo >}} +[languages.en] +contentDir = 'content/en' +direction = 'ltr' +label = 'English' +locale = 'en-US' +weight = 1 +[languages.en.pagination] +disableAliases = true +pagerSize = 10 +path = 'page' +[languages.de] +contentDir = 'content/de' +direction = 'ltr' +label = 'Deutsch' +locale = 'de-DE' +weight = 2 +[languages.de.pagination] +disableAliases = true +pagerSize = 20 +path = 'blatt' +{{< /code-toggle >}} diff --git a/docs/content/en/configuration/params.md b/docs/content/en/configuration/params.md new file mode 100644 index 0000000..b30ad98 --- /dev/null +++ b/docs/content/en/configuration/params.md @@ -0,0 +1,100 @@ +--- +title: Configure params +linkTitle: Params +description: Create custom site parameters. +categories: [] +keywords: [] +--- + +Use the `params` key for custom parameters: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +locale = 'en-US' +title = 'Project Documentation' +[params] +subtitle = 'Reference, Tutorials, and Explanations' +[params.contact] +email = 'info@example.org' +phone = '+1 206-555-1212' +{{< /code-toggle >}} + +Access the custom parameters from your templates using the [`Params`][] method on a `Site` object: + +```go-html-template +{{ .Site.Params.subtitle }} → Reference, Tutorials, and Explanations +{{ .Site.Params.contact.email }} → info@example.org +``` + +Key names should use camelCase or snake_case. While TOML, YAML, and JSON allow kebab-case keys, they are not valid [identifiers](g) and cannot be used when [chaining](g) identifiers. + +For example, you can do either of these: + +```go-html-template +{{ .Site.params.camelCase.foo }} +{{ .Site.params.snake_case.foo }} +``` + +But you cannot do this: + +```go-html-template +{{ .Site.params.kebab-case.foo }} +``` + +## Multilingual projects + +For multilingual projects, create a `params` key under each language: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +defaultContentLanguage = 'en' + +[languages.de] +direction = 'ltr' +label = 'Deutsch' +locale = 'de-DE' +title = 'Projekt Dokumentation' +weight = 1 + +[languages.de.params] +subtitle = 'Referenz, Tutorials und Erklärungen' + +[languages.de.params.contact] +email = 'info@de.example.org' +phone = '+49 30 1234567' + +[languages.en] +direction = 'ltr' +label = 'English' +locale = 'en-US' +title = 'Project Documentation' +weight = 2 + +[languages.en.params] +subtitle = 'Reference, Tutorials, and Explanations' + +[languages.en.params.contact] +email = 'info@example.org' +phone = '+1 206-555-1212' +{{< /code-toggle >}} + +## Namespacing + +To prevent naming conflicts, module and theme developers should namespace any custom parameters specific to their module or theme. + +{{< code-toggle file=hugo >}} +[params.modules.myModule.colors] +background = '#efefef' +font = '#222222' +{{< /code-toggle >}} + +To access the module/theme settings: + +```go-html-template +{{ $cfg := .Site.Params.module.mymodule }} + +{{ $cfg.colors.background }} → #efefef +{{ $cfg.colors.font }} → #222222 +``` + +[`Params`]: /methods/site/params/ diff --git a/docs/content/en/configuration/permalinks.md b/docs/content/en/configuration/permalinks.md new file mode 100644 index 0000000..7e50278 --- /dev/null +++ b/docs/content/en/configuration/permalinks.md @@ -0,0 +1,105 @@ +--- +title: Configure permalinks +linkTitle: Permalinks +description: Configure permalinks. +categories: [] +keywords: [] +--- + +Use the `permalinks` configuration to define custom URL patterns for your pages. Hugo supports two forms: a map form for simple section-based patterns, and an array form that supports [page matchers](g) for more precise targeting. + +> [!NOTE] +> The [`url`][] front matter field overrides any matching permalink pattern. + +## Map form + +Define URL patterns for each top-level [section](g), keyed by [page kind](g). For example, to configure URL patterns for the `articles` section: + +{{< code-toggle file=hugo >}} +[permalinks.page] +articles = '/blog/:year/:month/:slug/' +[permalinks.section] +articles = '/blog/' +{{< /code-toggle >}} + +To configure permalinks per language, nest the `permalinks` key under the language key: + +{{< code-toggle file=hugo >}} +[languages] + [languages.de] + label = 'Deutsch' + locale = 'de-DE' + weight = 1 + [languages.de.permalinks] + [languages.de.permalinks.page] + articles = '/artikel/:year/:month/:slug/' + [languages.de.permalinks.section] + articles = '/artikel/' + [languages.en] + label = 'English' + locale = 'en-US' + weight = 2 + [languages.en.permalinks] + [languages.en.permalinks.page] + articles = '/blog/:year/:month/:slug/' + [languages.en.permalinks.section] + articles = '/blog/' +{{< /code-toggle >}} + +## Array form + +{{< new-in 0.161.0 />}} + +Define an array of permalink entries to apply different URL patterns to different subsets of pages. Each entry requires a `pattern` key. Hugo applies the first matching pattern. + +The optional `target` key accepts a [page matcher](g). If `target` is omitted, the pattern applies to all pages. + +{{% include "/_common/configuration/page-matcher.md" %}} + +For example, to apply language-specific URL patterns to the `articles` section page and its leaf pages separately: + +{{< code-toggle file=hugo >}} +[[permalinks]] + pattern = '/artikel/' + [permalinks.target] + path = '{/articles}' + [permalinks.target.sites] + [permalinks.target.sites.matrix] + languages = ['de'] +[[permalinks]] + pattern = '/artikel/:year/:month/:slug/' + [permalinks.target] + path = '{/articles/**}' + [permalinks.target.sites] + [permalinks.target.sites.matrix] + languages = ['de'] +[[permalinks]] + pattern = '/blog/' + [permalinks.target] + path = '{/articles}' + [permalinks.target.sites] + [permalinks.target.sites.matrix] + languages = ['en'] +[[permalinks]] + pattern = '/blog/:year/:month/:slug/' + [permalinks.target] + path = '{/articles/**}' + [permalinks.target.sites] + [permalinks.target.sites.matrix] + languages = ['en'] +{{< /code-toggle >}} + +To define a fallback that matches any page not already matched by a preceding entry, place a pattern without a `target` key at the end: + +{{< code-toggle file=hugo >}} +[[permalinks]] +pattern = '/:section/:slug/' +{{< /code-toggle >}} + +## Tokens + +Use these tokens when defining a URL pattern. + +{{% include "/_common/permalink-tokens.md" %}} + +[`url`]: /content-management/front-matter/#url diff --git a/docs/content/en/configuration/privacy.md b/docs/content/en/configuration/privacy.md new file mode 100644 index 0000000..65b5c75 --- /dev/null +++ b/docs/content/en/configuration/privacy.md @@ -0,0 +1,50 @@ +--- +title: Configure privacy +linkTitle: Privacy +description: Configure your site to help comply with regional privacy regulations. +categories: [] +keywords: [] +aliases: [/about/privacy/] +--- + +## Responsibility + +Site authors are responsible for ensuring compliance with regional privacy regulations, including but not limited to: + +- GDPR (General Data Protection Regulation): Applies to individuals within the European Union and the European Economic Area. +- CCPA (California Consumer Privacy Act): Applies to California residents. +- CPRA (California Privacy Rights Act): Expands upon the CCPA with stronger consumer privacy protections. +- Virginia Consumer Data Protection Act (CDPA): Applies to businesses that collect, process, or sell the personal data of Virginia residents. + +Hugo's privacy settings can assist in compliance efforts. + +## Embedded templates + +Hugo provides [embedded templates](g) to simplify project and content creation. Some of these templates interact with external services. For example, the `youtube` shortcode connects with YouTube's servers to embed videos. + +Some of these templates include settings to enhance privacy. + +## Configuration + +> [!NOTE] +> These settings affect the behavior of some of Hugo's embedded templates. These settings may or may not affect the behavior of templates provided by third parties in their modules or themes. + +These are the default privacy settings for Hugo's embedded templates: + +{{< code-toggle config=privacy />}} + +See each template's documentation for a description of its privacy settings: + +- [Disqus partial][] +- [Google Analytics partial][] +- [Instagram shortcode][] +- [Vimeo shortcode][] +- [X shortcode][] +- [YouTube shortcode][] + +[Disqus partial]: /templates/embedded/#privacy-disqus +[Google Analytics partial]: /templates/embedded/#privacy-google-analytics +[Instagram shortcode]: /shortcodes/instagram/#privacy +[Vimeo shortcode]: /shortcodes/vimeo/#privacy +[X shortcode]: /shortcodes/x/#privacy +[YouTube shortcode]: /shortcodes/youtube/#privacy diff --git a/docs/content/en/configuration/related-content.md b/docs/content/en/configuration/related-content.md new file mode 100644 index 0000000..92cf92e --- /dev/null +++ b/docs/content/en/configuration/related-content.md @@ -0,0 +1,111 @@ +--- +title: Configure related content +linkTitle: Related content +description: Configure related content. +categories: [] +keywords: [] +--- + +> [!NOTE] +> To understand Hugo's related content identification, please refer to the [related content][] page. + +Hugo provides a sensible default configuration for identifying related content, but you can customize it in your project configuration, either globally or per language. + +## Default configuration + +This is the default configuration: + +{{< code-toggle config=related />}} + +> [!NOTE] +> Adding a `related` section to your project configuration requires you to provide a full configuration. You cannot override individual default values without specifying all related settings. + +## Top-level settings + +`threshold` +: (`int`) A value between 0-100, inclusive. A lower value will return more, but maybe not so relevant, matches. + +`includeNewer` +: (`bool`) Whether to include pages newer than the current page in the related content listing. This will mean that the output for older posts may change as new related content gets added. Default is `false`. + +`toLower` +: (`bool`) Whether to transform keywords in both the indexes and the queries to lower case. This may give more accurate results at a slight performance penalty. Default is `false`. + +## Per-index settings + +`applyFilter` +: (`string`) Apply a `type` specific filter to the result of a search. This is only used for the `fragments` type. + +`cardinalityThreshold` +: (`int`) If between `1` and `100`, this is a percentage. All keywords that are used in more than this percentage of documents are removed. For example, setting this to `60` will remove all keywords that are used in more than 60% of the documents in the index. If `0`, no keyword is removed from the index. Default is `0`. + +`name` +: (`string`) The index name. This value maps directly to a page parameter. Hugo supports string values (`author` in the example) and lists (`tags`, `keywords` etc.) and time and date objects. + +`pattern` +: (`string`) This is currently only relevant for dates. When listing related content, we may want to list content that is also close in time. Setting "2006" (default value for date indexes) as the pattern for a date index will add weight to pages published in the same year. For busier blogs, "200601" (year and month) may be a better default. + +`toLower` +: (`bool`) Whether to transform keywords in both the indexes and the queries to lower case. This may give more accurate results at a slight performance penalty. Default is `false`. + +`type` +: (`string`) One of `basic` or `fragments`. Default is `basic`. + +`weight` +: (`int`) An integer weight that indicates how important this parameter is relative to the other parameters. It can be `0`, which has the effect of turning this index off, or even negative. Test with different values to see what fits your content best. Default is `0`. + +## Example + +Imagine we're building a book review site. Our main content will be book reviews, and we'll use genres and authors as taxonomies. When someone views a book review, we want to show a short list of related reviews based on shared authors and genres. + +Create the content: + +```tree +content/ +└── book-reviews/ + ├── book-review-1.md + ├── book-review-2.md + ├── book-review-3.md + ├── book-review-4.md + └── book-review-5.md +``` + +Configure the taxonomies: + +{{< code-toggle file=hugo >}} +[taxonomies] +author = 'authors' +genre = 'genres' +{{< /code-toggle >}} + +Configure the related content identification: + +{{< code-toggle file=hugo >}} +[related] +includeNewer = true +threshold = 80 +toLower = true +[[related.indices]] +name = 'authors' +weight = 2 +[[related.indices]] +name = 'genres' +weight = 1 +{{< /code-toggle >}} + +We've configured the `authors` index with a weight of `2` and the `genres` index with a weight of `1`. This means Hugo prioritizes shared `authors` as twice as significant as shared `genres`. + +Then render a list of 5 related reviews with a _partial_ template like this: + +```go-html-template {file="layouts/_partials/related.html" copy=true} +{{ with site.RegularPages.Related . | first 5 }} +

    Related content:

    + +{{ end }} +``` + +[related content]: /content-management/related-content/ diff --git a/docs/content/en/configuration/roles.md b/docs/content/en/configuration/roles.md new file mode 100644 index 0000000..2d2c49c --- /dev/null +++ b/docs/content/en/configuration/roles.md @@ -0,0 +1,35 @@ +--- +title: Configure roles +linkTitle: Roles +description: Configure roles. +categories: [] +keywords: [] +--- + +{{< new-in 0.153.0 />}} + +This is the default configuration: + +{{< code-toggle config=roles />}} + +## Settings + +Use the following setting to define how Hugo orders roles. + +`weight` +: (`int`) The role [weight](g). + +## Sort order + +Hugo sorts roles by weight in ascending order, then lexicographically in ascending order. This affects build order and complement selection. + +## Example + +The following configuration demonstrates how to define multiple roles with specific weights. + +{{< code-toggle >}} +[roles.guest] +weight = 20 +[roles.member] +weight = 10 +{{< /code-toggle >}} diff --git a/docs/content/en/configuration/security.md b/docs/content/en/configuration/security.md new file mode 100644 index 0000000..9efbc01 --- /dev/null +++ b/docs/content/en/configuration/security.md @@ -0,0 +1,94 @@ +--- +title: Configure security +linkTitle: Security +description: Configure security. +categories: [] +keywords: [] +--- + +Hugo's built-in security policy, which restricts access to `os/exec`, remote communication, and similar operations, is configured via allowlists. By default, access is restricted. If a build attempts to use a feature not included in the allowlist, it will fail, providing a detailed message. + +This is the default security configuration: + +{{< code-toggle config=security />}} + +`allowContent` +: {{< new-in 0.162.0 />}} +: (`[]string`) A slice of [regular expressions](g) matching the [media type](g) of [content formats](g) allowed in the `content` directory. By default, the HTML content format (media type `text/html`) is denied. Hugo emits HTML file content verbatim, which could allow arbitrary JavaScript execution. See the [classification][] table for a mapping of content formats to media types. + +`enableInlineShortcodes` +: (`bool`) Whether to enable [inline shortcodes][]. Default is `false`. + +`exec.allow` +: (`[]string`) A slice of [regular expressions](g) matching the names of external executables that Hugo is allowed to run. + +`exec.osEnv` +: (`[]string`) A slice of [regular expressions](g) matching the names of operating system environment variables that Hugo is allowed to access. + +`funcs.getenv` +: (`[]string`) A slice of [regular expressions](g) matching the names of operating system environment variables that Hugo is allowed to access with the [`os.Getenv`][] function. + +`http.methods` +: (`[]string`) A slice of [regular expressions](g) matching the HTTP methods that the [`resources.GetRemote`][] function is allowed to use. + +`http.mediaTypes` +: (`[]string`) Applicable to the `resources.GetRemote` function, a slice of [regular expressions](g) matching the `Content-Type` in HTTP responses that Hugo trusts, bypassing file content analysis for media type detection. + +`http.urls` +: (`[]string`) A slice of [regular expressions](g) matching the URLs that the `resources.GetRemote` function is allowed to access. + +`node.permissions.disable` +: {{< new-in 0.161.0 />}} +: (`bool`) Whether to disable the Node.js [permission model][]. When `false`, Hugo runs Node.js tools with the `--permission` flag, restricting their file system and resource access to what is explicitly allowed below. Default is `false`. + +`node.permissions.allowAddons` +: {{< new-in 0.161.0 />}} +: (`[]string`) A slice of Node.js tool names permitted to load native addons (`--allow-addons`). + +`node.permissions.allowChildProcess` +: {{< new-in 0.161.0 />}} +: (`[]string`) A slice of Node.js tool names permitted to spawn child processes (`--allow-child-process`). + +`node.permissions.allowRead` +: {{< new-in 0.161.0 />}} +: (`[]string`) A slice of file system paths that Node.js tools are allowed to read (`--allow-fs-read`). Paths are relative to the working directory; `"."` means the working directory itself. Use `"*"` to allow all paths. + +`node.permissions.allowWorker` +: {{< new-in 0.161.0 />}} +: (`[]string`) A slice of Node.js tool names permitted to spawn worker threads (`--allow-worker`). + +`node.permissions.allowWrite` +: {{< new-in 0.161.0 />}} +: (`[]string`) A slice of file system paths that Node.js tools are allowed to write (`--allow-fs-write`). Paths are relative to the working directory; `"."` means the working directory itself. Use `"*"` to allow all paths. + +## Negation rules + +{{< new-in 0.161.0 />}} + +Any pattern in an allowlist can be negated by prefixing it with an exclamation mark (`!`) and one space to turn it into a deny rule. Deny rules take precedence over allow rules. An allowlist composed entirely of deny rules implicitly allows everything it does not deny. An empty allowlist rejects everything. + +For example, to allow all URLs except those pointing to `evil.example.com`: + +```toml +[security.http] +urls = ['.*', '! ^https?://evil\.example\.com'] +``` + +Setting an allowlist to the string `none` will completely disable the associated feature. + +## Environment variables + +You can also override your project configuration with environment variables. For example, to block `resources.GetRemote` from accessing any URL: + +```txt +export HUGO_SECURITY_HTTP_URLS=none +``` + +Learn more about [using environment variables][] to configure your site. + +[`os.Getenv`]: /functions/os/getenv/ +[`resources.GetRemote`]: /functions/resources/getremote/ +[classification]: /content-management/formats/#classification +[inline shortcodes]: /content-management/shortcodes/#inline +[permission model]: https://nodejs.org/api/permissions.html#permission-model +[using environment variables]: /configuration/introduction/#environment-variables diff --git a/docs/content/en/configuration/segments.md b/docs/content/en/configuration/segments.md new file mode 100644 index 0000000..0b124ae --- /dev/null +++ b/docs/content/en/configuration/segments.md @@ -0,0 +1,196 @@ +--- +title: Configure segments +linkTitle: Segments +description: Configure your site for segmented rendering. +categories: [] +keywords: [] +--- + +> [!NOTE] +> The `segments` configuration applies only to segmented rendering. While it controls when content is rendered, it doesn't restrict access to Hugo's complete object graph (sites and pages), which remains fully available. + +Segmented rendering offers several advantages: + +- Faster builds: Process large sites more efficiently. +- Rapid development: Render only a subset of your site for quicker iteration. +- Scheduled rebuilds: Rebuild specific sections at different frequencies (e.g., home page and news hourly, full site weekly). +- Targeted output: Generate specific output formats (like JSON for search indexes). + +## Segment definition + +Each segment is defined by an `includes` key and an `excludes` key, both of which accept an array of filters. + +A _filter_ is a collection of one or more conditions, represented as an item in the configuration array. A _condition_ compares a specific page [field](#fields) to a given [glob pattern](g). + +### Evaluation rules + +The evaluation logic adheres to three rules: + +- All conditions within a single filter item must match for that filter to evaluate as true, creating an AND relationship. +- If the `includes` or `excludes` array contains multiple filters, only one filter needs to evaluate as true for the entire array to match, creating an OR relationship. +- The `excludes` array takes absolute precedence. If a page matches any filter in the `excludes` array, Hugo omits it from the segment regardless of whether it matches the `includes` array. + +### Performance optimization + +Using the `excludes` array to target sites or output formats allows Hugo to skip entire groups of pages during evaluation instead of checking every page. This optimization helps with performance in larger setups. + +For example, excluding unwanted output formats is faster: + +{{< code-toggle file=hugo >}} +[segments] + [segments.segment1] + [[segments.segment1.excludes]] + output = '! json' +{{< /code-toggle >}} + +Including only the desired output format is slower: + +{{< code-toggle file=hugo >}} +[segments] + [segments.segment1] + [[segments.segment1.includes]] + output = 'json' +{{< /code-toggle >}} + +## Fields + +`kind` +: (`string`) A [glob pattern](g) matching the [page kind](g). For example: `{taxonomy,term}`. + +`lang` +: {{< deprecated-in 0.153.0 />}} +: Use [`sites`](#sites) instead. + +`output` +: (`string`) A [glob pattern](g) matching the [output format](g) of the page. For example: `{html,json}`. + +`path` +: (`string`) A [glob pattern](g) matching the page's [logical path](g). For example: `{/books,/books/**}`. + +`sites` +: {{< new-in 0.153.0 />}} +: (`map`) A map to define [sites matrix](g). + +## Targeting segments + +To specify which segments Hugo builds, add the [`renderSegments`][] setting to your project configuration: + +{{< code-toggle file=hugo >}} +renderSegments = ['segment1','segment2'] +{{< /code-toggle >}} + +Alternatively, pass the segment names directly to the `--renderSegments` command-line flag during a build: + +```sh +hugo build --renderSegments segment1 +``` + +You can target multiple segments by providing a comma-separated list: + +```sh +hugo build --renderSegments segment1,segment2 +``` + +## Example + + + +Consider a project with this content structure: + +```tree +content/ +├── books/ +│ ├── _index.en.md +│ ├── _index.nb.md +│ ├── _index.nn.md +│ ├── book-1.en.md +│ ├── book-1.nb.md +│ └── book-1.nn.md +├── films/ +│ ├── _index.en.md +│ ├── _index.nb.md +│ ├── _index.nn.md +│ ├── film-1.en.md +│ ├── film-1.nb.md +│ └── film-1.nn.md +├── _index.en.md +├── _index.nb.md +└── _index.nn.md +``` + +And this project configuration: + +{{< code-toggle file=hugo >}} +baseURL = 'https://example.org/' +title = 'Segmentation' +defaultContentLanguage = 'en' +defaultContentLanguageInSubdir = true + +[languages.en] + direction = 'ltr' + label = 'English' + locale = 'en-US' + weight = 1 + +[languages.nb] + locale = 'nb-NO' + direction = 'ltr' + label = 'Bokmål' + weight = 2 + +[languages.nn] + locale = 'nn-NO' + direction = 'ltr' + label = 'Norsk' + weight = 3 + +[segments] + [segments.segment1] + [[segments.segment1.excludes]] + [segments.segment1.excludes.sites.matrix] + languages = ['n*'] + [[segments.segment1.excludes]] + output = 'rss' + [segments.segment1.excludes.sites.matrix] + languages = ['en'] + [[segments.segment1.includes]] + kind = '{home,term,taxonomy}' + [[segments.segment1.includes]] + path = '{/books,/books/**}' + +[taxonomies] + tag = 'tags' +{{< /code-toggle >}} + +When you run this command: + +```sh +hugo build --renderSegments segment1 +``` + +The published project has this structure: + +```tree +public/ +├── en/ +│ ├── books/ +│ │ ├── book-1/ +│ │ │ └── index.html +│ │ └── index.html +│ ├── tags/ +│ │ ├── tag-a/ +│ │ │ └── index.html +│ │ ├── tag-b/ +│ │ │ └── index.html +│ │ └── index.html +│ └── index.html +└── index.html +``` + +[`renderSegments`]: /configuration/all/#rendersegments diff --git a/docs/content/en/configuration/server.md b/docs/content/en/configuration/server.md new file mode 100644 index 0000000..3bccdce --- /dev/null +++ b/docs/content/en/configuration/server.md @@ -0,0 +1,126 @@ +--- +title: Configure server +linkTitle: Server +description: Configure the development server. +categories: [] +keywords: [] +--- + +These settings are exclusive to Hugo's development server, so a dedicated [configuration directory][] for development, where the server is configured accordingly, is the recommended approach. + +```tree +project/ +└── config/ + ├── _default/ + │ └── hugo.toml + └── development/ + └── server.toml +``` + +## Default settings + +The development server defaults to redirecting to `/404.html` for any requests to URLs that don't exist. See the [404 errors](#404-errors) section below for details. + +{{< code-toggle config=server />}} + +`force` +: (`bool`) Whether to force a redirect even if there is existing content in the path. + +`from` +: (`string`) A [glob pattern](g) matching the requested URL. Either `from` or `fromRE` must be set. If both `from` and `fromRe` are specified, the URL must match both patterns. + +`fromHeaders` +: {{< new-in 0.144.0 />}} +: (`map[string][string]`) Headers to match for the redirect. This maps the HTTP header name to a [glob pattern](g) with values to match. If the map is empty, the redirect will always be triggered. + +`fromRe` +: {{< new-in 0.144.0 />}} +: (`string`) A [regular expression](g) used to match the requested URL. Either `from` or `fromRE` must be set. If both `from` and `fromRe` are specified, the URL must match both patterns. Capture groups from the regular expression are accessible in the `to` field as `$1`, `$2`, and so on. + +`status` +: (`string`) The HTTP status code to use for the redirect. A status code of 200 will trigger a URL rewrite. + +`to` +: (`string`) The URL to forward the request to. + +## Headers + +Include headers in every server response to facilitate testing, particularly for features like [Content Security Policies][]. + +{{< code-toggle file=config/development/server >}} +[[headers]] +for = '/**' + +[headers.values] +X-Frame-Options = 'DENY' +X-XSS-Protection = '1; mode=block' +X-Content-Type-Options = 'nosniff' +Referrer-Policy = 'strict-origin-when-cross-origin' +Content-Security-Policy = 'script-src localhost:1313' +{{< /code-toggle >}} + +## Redirects + +You can define simple redirect rules. + +{{< code-toggle file=config/development/server >}} +[[redirects]] +from = '/myspa/**' +to = '/myspa/' +status = 200 +force = false +{{< /code-toggle >}} + +The `200` status code in this example triggers a URL rewrite, which is typically the desired behavior for [single-page applications][]. + +## 404 errors + +The development server defaults to redirecting to /404.html for any requests to URLs that don't exist. + +{{< code-toggle config=server />}} + +If you've already defined other redirects, you must explicitly add the 404 redirect. + +{{< code-toggle file=config/development/server >}} +[[redirects]] +force = false +from = '/**' +to = '/404.html' +status = 404 +{{< /code-toggle >}} + +For multilingual projects, ensure the default language 404 redirect is defined last: + +{{< code-toggle file=config/development/server >}} +defaultContentLanguage = 'en' +defaultContentLanguageInSubdir = false +[[redirects]] +from = '/fr/**' +to = '/fr/404.html' +status = 404 + +[[redirects]] # Default language must be last. +from = '/**' +to = '/404.html' +status = 404 +{{< /code-toggle >}} + +When the default language is served from a subdirectory: + +{{< code-toggle file=config/development/server >}} +defaultContentLanguage = 'en' +defaultContentLanguageInSubdir = true +[[redirects]] +from = '/fr/**' +to = '/fr/404.html' +status = 404 + +[[redirects]] # Default language must be last. +from = '/**' +to = '/en/404.html' +status = 404 +{{< /code-toggle >}} + +[Content Security Policies]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP +[configuration directory]: /configuration/introduction/#configuration-directory +[single-page applications]: https://en.wikipedia.org/wiki/Single-page_application diff --git a/docs/content/en/configuration/services.md b/docs/content/en/configuration/services.md new file mode 100644 index 0000000..50ff525 --- /dev/null +++ b/docs/content/en/configuration/services.md @@ -0,0 +1,46 @@ +--- +title: Configure services +linkTitle: Services +description: Configure embedded templates. +categories: [] +keywords: [] +--- + +Hugo provides [embedded templates](g) to simplify site and content creation. Some of these templates are configurable. For example, the embedded Google Analytics template requires a Google tag ID. + +This is the default configuration: + +{{< code-toggle config=services />}} + +`disqus.shortname` +: (`string`) The `shortname` used with the Disqus commenting system. See [details][disqus]. To access this value from a template: + + ```go-html-template + {{ .Site.Config.Services.Disqus.Shortname }} + ``` + +`googleAnalytics.id` +: (`string`) The Google tag ID for Google Analytics 4 properties. See [details][google-analytics]. To access this value from a template: + + ```go-html-template + {{ .Site.Config.Services.GoogleAnalytics.ID }} + ``` + +`rss.limit` +: (`int`) The maximum number of items to include in an RSS feed. Set to `-1` for no limit. Default is `-1`. See [details][rss]. To access this value from a template: + + ```go-html-template + {{ .Site.Config.Services.RSS.Limit }} + ``` + +`x.disableInlineCSS` +: (`bool`) Whether to disable the inline CSS rendered by the embedded `x` shortode. See [details][privacy]. Default is `false`. To access this value from a template: + + ```go-html-template + {{ .Site.Config.Services.X.DisableInlineCSS }} + ``` + +[disqus]: /templates/embedded/#disqus +[google-analytics]: /templates/embedded/#google-analytics +[privacy]: /shortcodes/x/#privacy +[rss]: /templates/rss/ diff --git a/docs/content/en/configuration/sitemap.md b/docs/content/en/configuration/sitemap.md new file mode 100644 index 0000000..7f45a25 --- /dev/null +++ b/docs/content/en/configuration/sitemap.md @@ -0,0 +1,26 @@ +--- +title: Configure sitemap +linkTitle: Sitemap +description: Configure the sitemap. +categories: [] +keywords: [] +--- + +These are the default sitemap configuration values. They apply to all pages unless overridden in front matter. + +{{< code-toggle config=sitemap />}} + +`changefreq` +: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details][changefreqdef]. + +`disable` +: (`bool`) Whether to disable page inclusion. Default is `false`. Set to `true` in front matter to exclude the page. + +`filename` +: (`string`) The name of the generated file. Default is `sitemap.xml`. + +`priority` +: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details][prioritydef]. + +[changefreqdef]: https://www.sitemaps.org/protocol.html#changefreqdef +[prioritydef]: https://www.sitemaps.org/protocol.html#prioritydef diff --git a/docs/content/en/configuration/taxonomies.md b/docs/content/en/configuration/taxonomies.md new file mode 100644 index 0000000..5bada37 --- /dev/null +++ b/docs/content/en/configuration/taxonomies.md @@ -0,0 +1,68 @@ +--- +title: Configure taxonomies +linkTitle: Taxonomies +description: Configure taxonomies. +categories: [] +keywords: [] +--- + +The default configuration defines two [taxonomies](g), `categories` and `tags`. + +{{< code-toggle config=taxonomies />}} + +When creating a taxonomy: + +- Use the singular form for the key (e.g., `category`). +- Use the plural form for the value (e.g., `categories`). + +Then use the value as the key in front matter: + + +{{< code-toggle file=content/example.md fm=true >}} +title: Example +categories: + - vegetarian + - gluten-free +tags: + - appetizer + - main course +{{< /code-toggle >}} + +If you do not expect to assign more than one [term](g) from a given taxonomy to a content page, you may use the singular form for both key and value: + +{{< code-toggle file=hugo >}} +taxonomies: + author: author +{{< /code-toggle >}} + +Then in front matter: + + +{{< code-toggle file=content/example.md fm=true >}} +title: Example +author: + - Robert Smith +{{< /code-toggle >}} + + +The example above illustrates that even with a single term, the value is still provided as an array. + +You must explicitly define the default taxonomies to maintain them when adding a new one: + +{{< code-toggle file=hugo >}} +taxonomies: + author: author + category: categories + tag: tags +{{< /code-toggle >}} + +To disable the taxonomy system, use the [`disableKinds`][] setting in the root of your project configuration to disable the `taxonomy` and `term` page [kinds](g). + +{{< code-toggle file=hugo >}} +disableKinds = ['taxonomy','term'] +{{< /code-toggle >}} + +See the [taxonomies][] section for more information. + +[`disableKinds`]: /configuration/all/#disablekinds +[taxonomies]: /content-management/taxonomies/ diff --git a/docs/content/en/configuration/ugly-urls.md b/docs/content/en/configuration/ugly-urls.md new file mode 100644 index 0000000..17b110a --- /dev/null +++ b/docs/content/en/configuration/ugly-urls.md @@ -0,0 +1,37 @@ +--- +title: Configure ugly URLs +linkTitle: Ugly URLs +description: Configure ugly URLs. +categories: [] +keywords: [] +--- + +{{% glossary-term "ugly url" %}} For example: + +```text +https://example.org/section/article.html +``` + +In its default configuration, Hugo generates [pretty URLs](g). For example: + +```text +https://example.org/section/article/ +``` + +This is the default configuration: + +{{< code-toggle config=uglyURLs />}} + +To generate ugly URLs for the entire site: + +{{< code-toggle file=hugo >}} +uglyURLs = true +{{< /code-toggle >}} + +To generate ugly URLs for specific sections of your site: + +{{< code-toggle file=hugo >}} +[uglyURLs] +books = true +films = false +{{< /code-toggle >}} diff --git a/docs/content/en/configuration/versions.md b/docs/content/en/configuration/versions.md new file mode 100644 index 0000000..b796908 --- /dev/null +++ b/docs/content/en/configuration/versions.md @@ -0,0 +1,37 @@ +--- +title: Configure versions +linkTitle: Versions +description: Configure versions. +categories: [] +keywords: [] +--- + +{{< new-in 0.153.0 />}} + +This is the default configuration: + +{{< code-toggle config=versions />}} + +## Settings + +Use the following setting to define how Hugo orders versions. + +`weight` +: (`int`) The language [weight](g). + +## Sort order + +Hugo sorts versions by weight in ascending order, then by their [semantic version][] in descending order. This affects build order and complement selection. + +## Example + +The following configuration demonstrates how to define multiple versions with specific weights. + +{{< code-toggle >}} +[versions."v1.0.0"] +weight = 20 +[versions."v2.0.0"] +weight = 10 +{{< /code-toggle >}} + +[semantic version]: https://semver.org/ diff --git a/docs/content/en/content-management/_index.md b/docs/content/en/content-management/_index.md new file mode 100644 index 0000000..4e20607 --- /dev/null +++ b/docs/content/en/content-management/_index.md @@ -0,0 +1,8 @@ +--- +title: Content management +description: Hugo makes managing large static sites easy with support for archetypes, content types, menus, cross references, summaries, and more. +categories: [] +keywords: [] +weight: 10 +aliases: [/content/,/content/organization] +--- diff --git a/docs/content/en/content-management/archetypes.md b/docs/content/en/content-management/archetypes.md new file mode 100644 index 0000000..95351e9 --- /dev/null +++ b/docs/content/en/content-management/archetypes.md @@ -0,0 +1,188 @@ +--- +title: Archetypes +description: An archetype is a template for new content. +categories: [] +keywords: [] +aliases: [/content/archetypes/] +--- + +## Overview + +A content file consists of [front matter](g) and markup. The markup is typically Markdown, but Hugo also supports other [content formats](g). Front matter can be TOML, YAML, or JSON. + +The `hugo new content` command creates a new file in the `content` directory, using an archetype as a template. This is the default archetype: + +{{< code-toggle file=archetypes/default.md fm=true >}} +title = '{{ replace .File.ContentBaseName `-` ` ` | title }}' +date = '{{ .Date }}' +draft = true +{{< /code-toggle >}} + +When you create new content, Hugo evaluates the [template actions](g) within the archetype. For example: + +```sh +hugo new content posts/my-first-post.md +``` + +With the default archetype shown above, Hugo creates this content file: + +{{< code-toggle file=content/posts/my-first-post.md fm=true >}} +title = 'My First Post' +date = '2023-08-24T11:49:46-07:00' +draft = true +{{< /code-toggle >}} + +You can create an archetype for one or more [content types](g). For example, use one archetype for posts, and use the default archetype for everything else: + +```tree +archetypes/ +├── default.md +└── posts.md +``` + +## Lookup order + +Hugo looks for archetypes in the `archetypes` directory in the root of your project, falling back to the `archetypes` directory in themes or installed modules. An archetype for a specific content type takes precedence over the default archetype. + +For example, if you have enabled a theme named `my-theme` and you run this command: + +```sh +hugo new content posts/my-first-post.md +``` + +The archetype lookup order is: + +1. `archetypes/posts.md` +1. `themes/my-theme/archetypes/posts.md` +1. `archetypes/default.md` +1. `themes/my-theme/archetypes/default.md` + +If none of these exists, Hugo uses a built-in default archetype. + +## Functions and context + +You can use any template [function](g) within an archetype. As shown above, the default archetype uses the [`strings.Replace`][] function to replace hyphens with spaces when populating the title in front matter. + +Archetypes receive the following [context](g): + +`Date` +: (`string`) The current date and time, formatted in compliance with RFC3339. + +`File` +: (`hugolib.fileInfo`) Returns [file information][] for the current page. + +`Type` +: (`string`) The [content type](g) inferred from the top-level directory name, or as specified by the `--kind` flag passed to the `hugo new content` command. + +`Site` +: (`page.Site`) The current `Site` object. + +## Date format + +To insert date and time with a different format, use the [`time.Now`][] function: + +{{< code-toggle file=archetypes/default.md fm=true >}} +title = '{{ replace .File.ContentBaseName `-` ` ` | title }}' +date = '{{ time.Now.Format "2006-01-02" }}' +draft = true +{{< /code-toggle >}} + +## Include content + +Although typically used as a front matter template, you can also use an archetype to populate content. + +For example, in a documentation site you might have a section (content type) for functions. Every page within this section should follow the same format: a brief description, the function signature, examples, and notes. We can pre-populate the page to remind content authors of the standard format. + +````md {file="archetypes/functions.md"} +--- +date: '{{ .Date }}' +draft: true +title: '{{ replace .File.ContentBaseName `-` ` ` | title }}' +--- + +A brief description of what the function does, using simple present tense in the third person singular form. For example: + +`someFunction` returns the string `s` repeated `n` times. + +## Signature + +```text +func someFunction(s string, n int) string +``` + +## Examples + +One or more practical examples, each within a fenced code block. + +## Notes + +Additional information to clarify as needed. +```` + +Although you can include [template actions](g) within the content body, remember that Hugo evaluates these once---at the time of content creation. In most cases, place template actions in a [template](g) where Hugo evaluates the actions every time you [build](g) the site. + +## Leaf bundles + +You can also create archetypes for [leaf bundles](g). + +For example, in a photography site you might have a section (content type) for galleries. Each gallery is leaf bundle with content and images. + +Create an archetype for galleries: + +```tree +archetypes/ +├── galleries/ +│ ├── images/ +│ │ └── .gitkeep +│ └── index.md <-- same format as default.md +└── default.md +``` + +Subdirectories within an archetype must contain at least one file. Without a file, Hugo will not create the subdirectory when you create new content. The name and size of the file are irrelevant. The example above includes a `.gitkeep` file, an empty file commonly used to preserve otherwise empty directories in a Git repository. + +To create a new gallery: + +```sh +hugo new galleries/bryce-canyon +``` + +This produces: + +```tree +content/ +├── galleries/ +│ └── bryce-canyon/ +│ ├── images/ +│ │ └── .gitkeep +│ └── index.md +└── _index.md +``` + +## Specify archetype + +Use the `--kind` command line flag to specify an archetype when creating content. + +For example, let's say your site has two sections: articles and tutorials. Create an archetype for each content type: + +```tree +archetypes/ +├── articles.md +├── default.md +└── tutorials.md +``` + +To create an article using the articles archetype: + +```sh +hugo new content articles/something.md +``` + +To create an article using the tutorials archetype: + +```sh +hugo new content --kind tutorials articles/something.md +``` + +[`strings.Replace`]: /functions/strings/replace/ +[`time.Now`]: /functions/time/now/ +[file information]: /methods/page/file/ diff --git a/docs/content/en/content-management/build-options.md b/docs/content/en/content-management/build-options.md new file mode 100644 index 0000000..d3799e7 --- /dev/null +++ b/docs/content/en/content-management/build-options.md @@ -0,0 +1,297 @@ +--- +title: Build options +description: Build options help define how Hugo must treat a given page when building the site. +categories: [] +keywords: [] +aliases: [/content/build-options/] +--- + +Build options are stored in a reserved front matter object named `build` with these defaults: + +{{< code-toggle file=content/example/index.md fm=true >}} +[build] +list = 'always' +publishResources = true +render = 'always' +{{< /code-toggle >}} + +`list` +: When to include the page within page collections. Specify one of: + + - `always`: Include the page in _all_ page collections. For example, `site.RegularPages`, `.Pages`, etc. This is the default value. + - `local`: Include the page in _local_ page collections. For example, `.RegularPages`, `.Pages`, etc. Use this option to create fully navigable but headless content sections. + - `never`: Do not include the page in _any_ page collection. + +`publishResources` +: Applicable to [page bundles][], determines whether to publish the associated [page resources][]. Specify one of: + + - `true`: Always publish resources. This is the default value. + - `false`: Only publish a resource when invoking its [`Permalink`][], [`RelPermalink`][], or [`Publish`][] method within a template. + +`render` +: When to render the page. Specify one of: + + - `always`: Always render the page to disk. This is the default value. + - `link`: Do not render the page to disk, but assign `Permalink` and `RelPermalink` values. + - `never`: Never render the page to disk, and exclude it from all page collections. + +> [!NOTE] +> Any page, regardless of its build options, will always be available by using the [`.Page.GetPage`][] or [`.Site.GetPage`][] method. + +## Example -- headless page + +Create a unpublished page whose content and resources can be included in other pages. + +```tree +content/ +├── headless/ +│ ├── a.jpg +│ ├── b.jpg +│ └── index.md <-- leaf bundle +└── _index.md <-- home page +``` + +Set the build options in front matter: + +{{< code-toggle file=content/headless/index.md fm=true >}} +title = 'Headless page' +[build] + list = 'never' + publishResources = false + render = 'never' +{{< /code-toggle >}} + +To include the content and images on the home page: + +```go-html-template {file="layouts/home.html"} +{{ with .Site.GetPage "/headless" }} + {{ .Content }} + {{ range .Resources.ByType "image" }} + + {{ end }} +{{ end }} +``` + +The published site will have this structure: + +```tree +public/ +├── headless/ +│ ├── a.jpg +│ └── b.jpg +└── index.html +``` + +In the example above, note that: + +1. Hugo did not publish an HTML file for the page. +1. Despite setting `publishResources` to `false` in front matter, Hugo published the [page resources][] because we invoked the [`RelPermalink`][] method on each resource. This is the expected behavior. + +## Example -- headless section + +Create a unpublished section whose content and resources can be included in other pages. + +```tree +content/ +├── headless/ +│ ├── note-1/ +│ │ ├── a.jpg +│ │ ├── b.jpg +│ │ └── index.md <-- leaf bundle +│ ├── note-2/ +│ │ ├── c.jpg +│ │ ├── d.jpg +│ │ └── index.md <-- leaf bundle +│ └── _index.md <-- branch bundle +└── _index.md <-- home page +``` + +Set the build options in front matter, using the `cascade` keyword to "cascade" the values down to descendant pages. + +{{< code-toggle file=content/headless/_index.md fm=true >}} +title = 'Headless section' +[[cascade]] +[cascade.build] + list = 'local' + publishResources = false + render = 'never' +{{< /code-toggle >}} + +In the front matter above, note that we have set `list` to `local` to include the descendant pages in local page collections. + +To include the content and images on the home page: + +```go-html-template {file="layouts/home.html"} +{{ with .Site.GetPage "/headless" }} + {{ range .Pages }} + {{ .Content }} + {{ range .Resources.ByType "image" }} + + {{ end }} + {{ end }} +{{ end }} +``` + +The published site will have this structure: + +```tree +public/ +├── headless/ +│ ├── note-1/ +│ │ ├── a.jpg +│ │ └── b.jpg +│ └── note-2/ +│ ├── c.jpg +│ └── d.jpg +└── index.html +``` + +In the example above, note that: + +1. Hugo did not publish an HTML file for the page. +1. Despite setting `publishResources` to `false` in front matter, Hugo correctly published the [page resources][] because we invoked the [`RelPermalink`][] method on each resource. This is the expected behavior. + +## Example -- list without publishing + +Publish a section page without publishing the descendant pages. For example, to create a glossary: + +```tree +content/ +├── glossary/ +│ ├── _index.md +│ ├── bar.md +│ ├── baz.md +│ └── foo.md +└── _index.md +``` + +Set the build options in front matter, using the `cascade` keyword to "cascade" the values down to descendant pages. + +{{< code-toggle file=content/glossary/_index.md fm=true >}} +title = 'Glossary' +[build] +render = 'always' +[[cascade]] +[cascade.build] + list = 'local' + publishResources = false + render = 'never' +{{< /code-toggle >}} + +To render the glossary: + +```go-html-template {file="layouts/glossary/section.html"} +
    + {{ range .Pages }} +
    {{ .Title }}
    +
    {{ .Content }}
    + {{ end }} +
    +``` + +The published site will have this structure: + +```tree +public/ +├── glossary/ +│ └── index.html +└── index.html +``` + +## Example -- publish without listing + +Publish a section's descendant pages without publishing the section page itself. + +```tree +content/ +├── books/ +│ ├── _index.md +│ ├── book-1.md +│ └── book-2.md +└── _index.md +``` + +Set the build options in front matter: + +{{< code-toggle file=content/books/_index.md fm=true >}} +title = 'Books' +[build] +render = 'never' +list = 'never' +{{< /code-toggle >}} + +The published site will have this structure: + +```tree +public/ +├── books/ +│ ├── book-1/ +│ │ └── index.html +│ └── book-2/ +│ └── index.html +└── index.html +``` + +## Example -- conditionally hide section + +Consider this example. A documentation site has a team of contributors with access to 20 custom shortcodes. Each shortcode takes several arguments, and requires documentation for the contributors to reference when using them. + +Instead of external documentation for the shortcodes, include an `internal` section that is hidden when building the production site. + +```tree +content/ +├── internal/ +│ ├── shortcodes/ +│ │ ├── _index.md +│ │ ├── shortcode-1.md +│ │ └── shortcode-2.md +│ └── _index.md +├── reference/ +│ ├── _index.md +│ ├── reference-1.md +│ └── reference-2.md +├── tutorials/ +│ ├── _index.md +│ ├── tutorial-1.md +│ └── tutorial-2.md +└── _index.md +``` + +Set the build options in front matter, using the `cascade` keyword to "cascade" the values down to descendant pages, and use the `target` keyword to target the production environment. + +{{< code-toggle file=content/internal/_index.md >}} +title = 'Internal' +[[cascade]] +[cascade.build] +render = 'never' +list = 'never' +[cascade.target] +environment = 'production' +{{< /code-toggle >}} + +The production site will have this structure: + +```tree +public/ +├── reference/ +│ ├── reference-1/ +│ │ └── index.html +│ ├── reference-2/ +│ │ └── index.html +│ └── index.html +├── tutorials/ +│ ├── tutorial-1/ +│ │ └── index.html +│ ├── tutorial-2/ +│ │ └── index.html +│ └── index.html +└── index.html +``` + +[`.Page.GetPage`]: /methods/page/getpage/ +[`.Site.GetPage`]: /methods/site/getpage/ +[`Permalink`]: /methods/resource/permalink/ +[`Publish`]: /methods/resource/publish/ +[`RelPermalink`]: /methods/resource/relpermalink/ +[page bundles]: /content-management/page-bundles/ +[page resources]: /content-management/page-resources/ diff --git a/docs/content/en/content-management/comments.md b/docs/content/en/content-management/comments.md new file mode 100644 index 0000000..ed11cfa --- /dev/null +++ b/docs/content/en/content-management/comments.md @@ -0,0 +1,86 @@ +--- +title: Comments +description: Hugo ships with an embedded Disqus partial, but this isn't the only commenting system that will work with your new Hugo website. +categories: [] +keywords: [] +aliases: [/extras/comments/] +--- + +Hugo ships with support for [Disqus][], a third-party service that provides comment and community capabilities to websites via JavaScript. + +Your theme may already support Disqus, but if not, it is easy to add to your templates via Hugo's [embedded partial][]. + +## Add Disqus + +Hugo comes with all the code you need to load Disqus into your templates. Before adding Disqus to your site, you'll need to [set up an account][]. + +### Configure Disqus + +Disqus comments require you set a single value in your project configuration: + +{{< code-toggle file=hugo >}} +[services.disqus] +shortname = 'your-disqus-shortname' +{{}} + +For many websites, this is enough configuration. However, you also have the option to set the following in the front matter of a single content file: + +- `params.disqus_identifier` +- `params.disqus_title` +- `params.disqus_url` + +### Render Hugo's embedded Disqus partial + +To render it, add the following code where you want comments to appear: + +```go-html-template +{{ partial "disqus.html" . }} +``` + +## Alternatives + +Commercial commenting systems: + +- [Commentix][] +- [Emote][] +- [FastComments][] +- [Graph Comment][] +- [Hyvor Talk][] +- [IntenseDebate][] +- [ReplyBox][] + +Open-source commenting systems: + +- [Cactus Comments][] +- [Comentario][] +- [Comma][] +- [Discourse][] +- [Giscus][] +- [Isso][] +- [Remark42][] +- [Staticman][] +- [Talkyard][] +- [Utterances][] +- [Zoomment][] + +[Cactus Comments]: https://cactus.chat/docs/integrations/hugo/ +[Comentario]: https://gitlab.com/comentario/comentario/ +[Comma]: https://github.com/Dieterbe/comma/ +[Commentix]: https://www.commentix.com/ +[Discourse]: https://meta.discourse.org/t/embed-discourse-comments-on-another-website-via-javascript/31963 +[Disqus]: https://disqus.com/ +[Emote]: https://emote.com/ +[FastComments]: https://fastcomments.com/commenting-system-for-hugo +[Giscus]: https://giscus.app/ +[Graph Comment]: https://graphcomment.com/ +[Hyvor Talk]: https://talk.hyvor.com/ +[IntenseDebate]: https://intensedebate.com/ +[Isso]: https://isso-comments.de/ +[Remark42]: https://remark42.com/ +[ReplyBox]: https://getreplybox.com/ +[Staticman]: https://staticman.net/ +[Talkyard]: https://blog-comments.talkyard.io/ +[Utterances]: https://utteranc.es/ +[Zoomment]: https://zoomment.com/ +[embedded partial]: /templates/embedded/#disqus +[set up an account]: https://disqus.com/profile/signup/ diff --git a/docs/content/en/content-management/content-adapters.md b/docs/content/en/content-management/content-adapters.md new file mode 100644 index 0000000..413a90a --- /dev/null +++ b/docs/content/en/content-management/content-adapters.md @@ -0,0 +1,348 @@ +--- +title: Content adapters +description: Create content adapters to dynamically add content when building your project. +categories: [] +keywords: [] +--- + +## Overview + +A content adapter is a template that dynamically creates pages when building a site. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML. + +Unlike templates that reside in the `layouts` directory, content adapters reside in the `content` directory, no more than one per directory per language. When a content adapter creates a page, the page's [logical path](g) will be relative to the content adapter. + +```tree +content/ +├── articles/ +│ ├── _index.md +│ ├── article-1.md +│ └── article-2.md +├── books/ +│ ├── _content.gotmpl <-- content adapter +│ └── _index.md +└── films/ + ├── _content.gotmpl <-- content adapter + └── _index.md +``` + +Each content adapter is named `_content.gotmpl` and uses the same [syntax][] as templates in the `layouts` directory. You can use any of the [template functions][] within a content adapter, as well as the methods described below. + +## Methods + +Use these methods within a content adapter. + +`AddPage` +: Adds a page to the site. + + ```go-html-template {file="content/books/_content.gotmpl"} + {{ $content := dict + "mediaType" "text/markdown" + "value" "The _Hunchback of Notre Dame_ was written by Victor Hugo." + }} + {{ $page := dict + "content" $content + "kind" "page" + "path" "the-hunchback-of-notre-dame" + "title" "The Hunchback of Notre Dame" + }} + {{ .AddPage $page }} + ``` + +`AddResource` +: Adds a page resource to the site. + + ```go-html-template {file="content/books/_content.gotmpl"} + {{ with resources.Get "images/a.jpg" }} + {{ $content := dict + "mediaType" .MediaType.Type + "value" . + }} + {{ $resource := dict + "content" $content + "path" "the-hunchback-of-notre-dame/cover.jpg" + }} + {{ $.AddResource $resource }} + {{ end }} + ``` + + Then retrieve the new page resource with something like: + + ```go-html-template {file="layouts/page.html"} + {{ with .Resources.Get "cover.jpg" }} + + {{ end }} + ``` + +`Site` +: (`Site`) Returns the site to which the pages will be added. + + ```go-html-template {file="content/books/_content.gotmpl"} + {{ .Site.Title }} + ``` + + > [!NOTE] + > The `Site` object is not fully initialized while Hugo executes a content adapter. + > Methods that depend on built pages, such as `Site.Pages`, are unavailable at this stage and return an error. + +`Store` +: (`maps.Scratch`) Returns a persistent data structure for storing and manipulating keyed values. The main use case for this is to transfer values between executions when [EnableAllLanguages](#enablealllanguages) is set. See [examples][]. + + ```go-html-template {file="content/books/_content.gotmpl"} + {{ .Store.Set "key" "value" }} + {{ .Store.Get "key" }} + ``` + +`EnableAllLanguages` +: By default, Hugo executes the content adapter only once for the first matching site in the [sites matrix](g). Use this method to expand execution to all languages while maintaining the current role and version. + + For more fine-grained control, define a `sites.matrix` in front matter or in a content mount. + + ```go-html-template {file="content/books/_content.gotmpl"} + {{ .EnableAllLanguages }} + {{ $content := dict + "mediaType" "text/markdown" + "value" "The _Hunchback of Notre Dame_ was written by Victor Hugo." + }} + {{ $page := dict + "content" $content + "kind" "page" + "path" "the-hunchback-of-notre-dame" + "title" "The Hunchback of Notre Dame" + }} + {{ .AddPage $page }} + ``` + +`EnableAllDimensions` +: By default, Hugo executes the content adapter only once for the first matching site in the [sites matrix](g). Use this method to expand execution to every possible combination of language, version, and role. + + For more fine-grained control, define a `sites.matrix` in front matter or in a content mount. + +## Page map + +Set any [front matter field][] in the map passed to the [`AddPage`](#addpage) method, excluding `markup`. Instead of setting the `markup` field, specify the `content.mediaType` as described below. + +This table describes the fields most commonly passed to the `AddPage` method. + +Key|Description|Required +:--|:--|:-: +`content.mediaType`|The content [media type][]. Default is `text/markdown`. See [content formats][] for examples.|  +`content.value`|The content value as a string.|  +`dates.date`|The page creation date as a `time.Time` value.|  +`dates.expiryDate`|The page expiry date as a `time.Time` value.|  +`dates.lastmod`|The page last modification date as a `time.Time` value.|  +`dates.publishDate`|The page publication date as a `time.Time` value.|  +`params`|A map of page parameters.|  +`path`|The page's [logical path](g) relative to the content adapter. Do not include a leading slash or file extension.|:heavy_check_mark: +`title`|The page title.|  + +> [!NOTE] +> While `path` is the only required field, we recommend setting `title` as well. +> +> When setting the `path`, Hugo transforms the given string to a logical path. For example, setting `path` to `A B C` produces a logical path of `/section/a-b-c`. + +## Resource map + +Construct the map passed to the [`AddResource`](#addresource) method using the fields below. + +Key|Description|Required +:--|:--|:-: +`content.mediaType`|The content [media type][].|:heavy_check_mark: +`content.value`|The content value as a string or resource.|:heavy_check_mark: +`name`|The resource name.|  +`params`|A map of resource parameters.|  +`path`|The resources's [logical path](g) relative to the content adapter. Do not include a leading slash.|:heavy_check_mark: +`title`|The resource title.|  + +> [!NOTE] +> When `content.value` is a string, Hugo generates a new resource with a publication path relative to the page. However, if `content.value` is already a resource, Hugo directly uses its value and publishes it relative to the site root. This latter method is more efficient. +> +> When setting the `path`, Hugo transforms the given string to a logical path. For example, setting `path` to `A B C/cover.jpg` produces a logical path of `/section/a-b-c/cover.jpg`. + +## Example + +Create pages from remote data, where each page represents a book review. + +Step 1 +: Create the content structure. + + ```tree + content/ + └── books/ + ├── _content.gotmpl <-- content adapter + └── _index.md + ``` + +Step 2 +: Inspect the remote data to determine how to map key-value pairs to front matter fields.\ + + +Step 3 +: Create the content adapter. + + ```go-html-template {file="content/books/_content.gotmpl" copy=true} + {{/* Get remote data. */}} + {{ $data := dict }} + {{ $url := "https://gohugo.io/shared/examples/data/books.json" }} + {{ with try (resources.GetRemote $url) }} + {{ with .Err }} + {{ errorf "Unable to get remote resource %s: %s" $url . }} + {{ else with .Value }} + {{ $data = . | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %s" $url }} + {{ end }} + {{ end }} + + {{/* Add pages and page resources. */}} + {{ range $data }} + + {{/* Add page. */}} + {{ $content := dict "mediaType" "text/markdown" "value" .summary }} + {{ $dates := dict "date" (time.AsTime .date) }} + {{ $params := dict "author" .author "isbn" .isbn "rating" .rating "tags" .tags }} + {{ $page := dict + "content" $content + "dates" $dates + "kind" "page" + "params" $params + "path" .title + "title" .title + }} + {{ $.AddPage $page }} + + {{/* Add page resource. */}} + {{ $item := . }} + {{ with $url := $item.cover }} + {{ with try (resources.GetRemote $url) }} + {{ with .Err }} + {{ errorf "Unable to get remote resource %s: %s" $url . }} + {{ else with .Value }} + {{ $content := dict "mediaType" .MediaType.Type "value" .Content }} + {{ $params := dict "alt" $item.title }} + {{ $resource := dict + "content" $content + "params" $params + "path" (printf "%s/cover.%s" $item.title .MediaType.SubType) + }} + {{ $.AddResource $resource }} + {{ else }} + {{ errorf "Unable to get remote resource %s" $url }} + {{ end }} + {{ end }} + {{ end }} + + {{ end }} + ``` + +Step 4 +: Create a _page_ template to render each book review. + + ```go-html-template {file="layouts/books/page.html" copy=true} + {{ define "main" }} +

    {{ .Title }}

    + + {{ with .Resources.GetMatch "cover.*" }} + {{ .Params.alt }} + {{ end }} + +

    Author: {{ .Params.author }}

    + +

    + ISBN: {{ .Params.isbn }}
    + Rating: {{ .Params.rating }}
    + Review date: {{ .Date | time.Format ":date_long" }} +

    + + {{ with .GetTerms "tags" }} +

    Tags:

    + + {{ end }} + + {{ .Content }} + {{ end }} + ``` + +## Multilingual projects + +With multilingual projects you can: + +1. Create one content adapter for all languages using the [`EnableAllLanguages`](#enablealllanguages) method as described above. +1. Create content adapters unique to each language. See the examples below. + +### Translations by file name + +With this project configuration: + +{{< code-toggle file=hugo >}} +[languages.en] +weight = 1 + +[languages.de] +weight = 2 +{{< /code-toggle >}} + +Include a language designator in the content adapter's file name. + +```tree +content/ +└── books/ + ├── _content.de.gotmpl + ├── _content.en.gotmpl + ├── _index.de.md + └── _index.en.md +``` + +### Translations by content directory + +With this project configuration: + +{{< code-toggle file=hugo >}} +[languages.en] +contentDir = 'content/en' +weight = 1 + +[languages.de] +contentDir = 'content/de' +weight = 2 +{{< /code-toggle >}} + +Create a single content adapter in each directory: + +```tree +content/ +├── de/ +│ └── books/ +│ ├── _content.gotmpl +│ └── _index.md +└── en/ + └── books/ + ├── _content.gotmpl + └── _index.md +``` + +## Page collisions + +Two or more pages collide when they have the same publication path. Due to concurrency, the content of the published page is indeterminate. Consider this example: + +```tree +content/ +└── books/ + ├── _content.gotmpl <-- content adapter + ├── _index.md + └── the-hunchback-of-notre-dame.md +``` + +If the content adapter also creates `books/the-hunchback-of-notre-dame`, the content of the published page is indeterminate. You can not define the processing order. + +To detect page collisions, use the `--printPathWarnings` flag when building your project. + +[content formats]: /content-management/formats/#classification +[examples]: /methods/page/store/ +[front matter field]: /content-management/front-matter/#fields +[media type]: https://en.wikipedia.org/wiki/Media_type +[syntax]: /templates/introduction/ +[template functions]: /functions/ diff --git a/docs/content/en/content-management/data-sources.md b/docs/content/en/content-management/data-sources.md new file mode 100644 index 0000000..21b3ec8 --- /dev/null +++ b/docs/content/en/content-management/data-sources.md @@ -0,0 +1,114 @@ +--- +title: Data sources +description: Use local and remote data sources to augment or create content. +categories: [] +keywords: [] +aliases: [/extras/datafiles/,/extras/datadrivencontent/,/doc/datafiles/,/templates/data-templates/] +--- + +Hugo can access and [unmarshal](g) local and remote data sources including CSV, JSON, TOML, YAML, and XML. Use this data to augment existing content or to create new content. + +A data source might be a file in the `data` directory, a [global resource](g), a [page resource](g), or a [remote resource](g). + +## Data directory + +The `data` directory in the root of your project may contain one or more data files, in either a flat or nested tree. Hugo merges the data files to create a single data structure, accessible with the `Data` method on a `Site` object. + +Hugo also merges data directories from themes and modules into this single data structure, where the `data` directory in the root of your project takes precedence. + +> [!NOTE] +> Hugo reads the combined data structure into memory and keeps it there for the entire build. For data that is infrequently accessed, use global or page resources instead. + +Theme and module authors may wish to namespace their data files to prevent collisions. For example: + +```tree +project/ +└── data/ + └── mytheme/ + └── foo.json +``` + +> [!NOTE] +> Do not place CSV files in the `data` directory. Access CSV files as page, global, or remote resources. + +See the documentation for the [`Data`][] method on a `Site` object for details and examples. + +## Global resources + +Use the `resources.Get` and `transform.Unmarshal` functions to access data files that exist as global resources. + +See the [`transform.Unmarshal`][global-resource] documentation for details and examples. + +## Page resources + +Use the `Resources.Get` method on a `Page` object combined with the `transform.Unmarshal` function to access data files that exist as page resources. + +See the [`transform.Unmarshal`][page-resource] documentation for details and examples. + +## Remote resources + +Use the `resources.GetRemote` and `transform.Unmarshal` functions to access remote data. + +See the [`transform.Unmarshal`][remote-resource] documentation for details and examples. + +## Augment existing content + +Use data sources to augment existing content. For example, create a shortcode to render an HTML table from a global CSV resource. + +```csv {file="assets/pets.csv"} +"name","type","breed","age" +"Spot","dog","Collie","3" +"Felix","cat","Malicious","7" +``` + +```md {file="content/example.md"} +{{}} +``` + +```go-html-template {file="layouts/_shortcodes/csv-to-table.html"} +{{ with $file := .Get 0 }} + {{ with resources.Get $file }} + {{ with . | transform.Unmarshal }} + + + + {{ range index . 0 }} + + {{ end }} + + + + {{ range after 1 . }} + + {{ range . }} + + {{ end }} + + {{ end }} + +
    {{ . }}
    {{ . }}
    + {{ end }} + {{ else }} + {{ errorf "The %q shortcode was unable to find %s. See %s" $.Name $file $.Position }} + {{ end }} +{{ else }} + {{ errorf "The %q shortcode requires one positional argument, the path to the CSV file relative to the assets directory. See %s" .Name .Position }} +{{ end }} +``` + +Hugo renders this to: + +name|type|breed|age +:--|:--|:--|:-- +Spot|dog|Collie|3 +Felix|cat|Malicious|7 + +## Create new content + +Use [content adapters][] to create new content. + +[`Data`]: /methods/site/data/ +[content adapters]: /content-management/content-adapters/ +[global-resource]: /functions/transform/unmarshal/#global-resource +[page-resource]: /functions/transform/unmarshal/#page-resource +[remote-resource]: /functions/transform/unmarshal/#remote-resource diff --git a/docs/content/en/content-management/diagrams.md b/docs/content/en/content-management/diagrams.md new file mode 100644 index 0000000..26df451 --- /dev/null +++ b/docs/content/en/content-management/diagrams.md @@ -0,0 +1,260 @@ +--- +title: Diagrams +description: Use fenced code blocks and Markdown render hooks to include diagrams in your content. +categories: [] +keywords: [] +--- + +## GoAT diagrams (ASCII) + +Hugo natively supports [GoAT][] diagrams with an [embedded code block render hook][]. This means that this code block: + +````txt +```goat + . . . .--- 1 .-- 1 / 1 + / \ | | .---+ .-+ + + / \ .---+---. .--+--. | '--- 2 | '-- 2 / \ 2 + + + | | | | ---+ ---+ + + / \ / \ .-+-. .-+-. .+. .+. | .--- 3 | .-- 3 \ / 3 + / \ / \ | | | | | | | | '---+ '-+ + + 1 2 3 4 1 2 3 4 1 2 3 4 '--- 4 '-- 4 \ 4 + +``` +```` + +Will be rendered as: + +```goat + + . . . .--- 1 .-- 1 / 1 + / \ | | .---+ .-+ + + / \ .---+---. .--+--. | '--- 2 | '-- 2 / \ 2 + + + | | | | ---+ ---+ + + / \ / \ .-+-. .-+-. .+. .+. | .--- 3 | .-- 3 \ / 3 + / \ / \ | | | | | | | | '---+ '-+ + + 1 2 3 4 1 2 3 4 1 2 3 4 '--- 4 '-- 4 \ 4 +``` + +## Mermaid diagrams + +Hugo does not provide a built-in template for Mermaid diagrams. Create your own using a [code block render hook][]: + +```go-html-template {file="layouts/_markup/render-codeblock-mermaid.html" copy=true} +
    +  {{ .Inner | htmlEscape | safeHTML }}
    +
    +{{ .Page.Store.Set "hasMermaid" true }} +``` + +Then include this snippet at the _bottom_ of your base template, before the closing `body` tag: + +```go-html-template {file="layouts/baseof.html" copy=true} +{{ if .Store.Get "hasMermaid" }} + +{{ end }} +``` + +With that you can use the `mermaid` language in Markdown code blocks: + +````md {file="content/example.md" copy=true} +```mermaid +sequenceDiagram + participant Alice + participant Bob + Alice->>John: Hello John, how are you? + loop Healthcheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts
    prevail! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! +``` +```` + +## Goat ASCII diagram examples + +### Graphics + +```goat + . + 0 3 P * Eye / ^ / + *-------* +y \ +) \ / Reflection + 1 /| 2 /| ^ \ \ \ v + *-------* | | v0 \ v3 --------*-------- + | |4 | |7 | *----\-----* + | *-----|-* +-----> +x / v X \ .-.<-------- o + |/ |/ / / o \ | / | Refraction / \ + *-------* v / \ +-' / \ + 5 6 +z v1 *------------------* v2 | o-----o + v + +``` + +### Complex + +```goat ++-------------------+ ^ .---. +| A Box |__.--.__ __.--> | .-. | | +| | '--' v | * |<--- | | ++-------------------+ '-' | | + Round *---(-. | + .-----------------. .-------. .----------. .-------. | | | + | Mixed Rounded | | | / Diagonals \ | | | | | | + | & Square Corners | '--. .--' / \ |---+---| '-)-' .--------. + '--+------------+-' .--. | '-------+--------' | | | | / Search / + | | | | '---. | '-------' | '-+------' + |<---------->| | | | v Interior | ^ + ' <---' '----' .-----------. ---. .--- v | + .------------------. Diag line | .-------. +---. \ / . | + | if (a > b) +---. .--->| | | | | Curved line \ / / \ | + | obj->fcn() | \ / | '-------' |<--' + / \ | + '------------------' '--' '--+--------' .--. .--. | .-. +Done?+-' + .---+-----. | ^ |\ | | /| .--+ | | \ / + | | | Join \|/ | | Curved | \| |/ | | \ | \ / + | | +----> o --o-- '-' Vertical '--' '--' '-- '--' + .---. + <--+---+-----' | /|\ | | 3 | + v not:line 'quotes' .-' '---' + .-. .---+--------. / A || B *bold* | ^ + | | | Not a dot | <---+---<-- A dash--is not a line v | + '-' '---------+--' / Nor/is this. --- + +``` + +### Process + +```goat + . + .---------. / \ + | START | / \ .-+-------+-. ___________ + '----+----' .-------. A / \ B | |COMPLEX| | / \ .-. + | | END |<-----+CHOICE +----->| | | +--->+ PREPARATION +--->| X | + v '-------' \ / | |PROCESS| | \___________/ '-' + .---------. \ / '-+---+---+-' + / INPUT / \ / + '-----+---' ' + | ^ + v | + .-----------. .-----+-----. .-. + | PROCESS +---------------->| PROCESS |<------+ X | + '-----------' '-----------' '-' +``` + +### File tree + +Created from + +```goat {width=300 color="orange"} +───Linux─┬─Android + ├─Debian─┬─Ubuntu─┬─Lubuntu + │ │ ├─Kubuntu + │ │ ├─Xubuntu + │ │ └─Xubuntu + │ └─Mint + ├─Centos + └─Fedora +``` + +### Sequence diagram + + + +```goat {class="w-40"} +┌─────┐ ┌───┐ +│Alice│ │Bob│ +└──┬──┘ └─┬─┘ + │ │ + │ Hello Bob! │ + │───────────>│ + │ │ + │Hello Alice!│ + │<───────────│ +┌──┴──┐ ┌─┴─┐ +│Alice│ │Bob│ +└─────┘ └───┘ + +``` + +### Flowchart + + + +```goat + _________________ + ╱ ╲ ┌─────┐ + ╱ DO YOU UNDERSTAND ╲____________________________________________________│GOOD!│ + ╲ FLOW CHARTS? ╱yes └──┬──┘ + ╲_________________╱ │ + │no │ + _________▽_________ ______________________ │ + ╱ ╲ ╱ ╲ ┌────┐ │ +╱ OKAY, YOU SEE THE ╲________________╱ ... AND YOU CAN SEE ╲___│GOOD│ │ +╲ LINE LABELED 'YES'? ╱yes ╲ THE ONES LABELED 'NO'? ╱yes└──┬─┘ │ + ╲___________________╱ ╲______________________╱ │ │ + │no │no │ │ + ________▽_________ _________▽__________ │ │ + ╱ ╲ ┌───────────┐ ╱ ╲ │ │ + ╱ BUT YOU SEE THE ╲___│WAIT, WHAT?│ ╱ BUT YOU JUST ╲___ │ │ + ╲ ONES LABELED 'NO'? ╱yes└───────────┘ ╲ FOLLOWED THEM TWICE? ╱yes│ │ │ + ╲__________________╱ ╲____________________╱ │ │ │ + │no │no │ │ │ + ┌───▽───┐ │ │ │ │ + │LISTEN.│ └───────┬───────┘ │ │ + └───┬───┘ ┌──────▽─────┐ │ │ + ┌─────▽────┐ │(THAT WASN'T│ │ │ + │I HATE YOU│ │A QUESTION) │ │ │ + └──────────┘ └──────┬─────┘ │ │ + ┌────▽───┐ │ │ + │SCREW IT│ │ │ + └────┬───┘ │ │ + └─────┬─────┘ │ + │ │ + └─────┬─────┘ + ┌───────▽──────┐ + │LET'S GO DRING│ + └───────┬──────┘ + ┌─────────▽─────────┐ + │HEY, I SHOULD TRY │ + │INSTALLING FREEBSD!│ + └───────────────────┘ + +``` + +### Table + + + +```goat {class="w-80 dark-blue"} +┌────────────────────────────────────────────────┐ +│ │ +├────────────────────────────────────────────────┤ +│SYNTAX = { PRODUCTION } . │ +├────────────────────────────────────────────────┤ +│PRODUCTION = IDENTIFIER "=" EXPRESSION "." . │ +├────────────────────────────────────────────────┤ +│EXPRESSION = TERM { "|" TERM } . │ +├────────────────────────────────────────────────┤ +│TERM = FACTOR { FACTOR } . │ +├────────────────────────────────────────────────┤ +│FACTOR = IDENTIFIER │ +├────────────────────────────────────────────────┤ +│ | LITERAL │ +├────────────────────────────────────────────────┤ +│ | "[" EXPRESSION "]" │ +├────────────────────────────────────────────────┤ +│ | "(" EXPRESSION ")" │ +├────────────────────────────────────────────────┤ +│ | "{" EXPRESSION "}" . │ +├────────────────────────────────────────────────┤ +│IDENTIFIER = letter { letter } . │ +├────────────────────────────────────────────────┤ +│LITERAL = """" character { character } """" .│ +└────────────────────────────────────────────────┘ +``` + +[GoAT]: https://github.com/bep/goat +[code block render hook]: /render-hooks/code-blocks/ +[embedded code block render hook]: <{{% eturl render-codeblock-goat %}}> diff --git a/docs/content/en/content-management/formats.md b/docs/content/en/content-management/formats.md new file mode 100644 index 0000000..86cb5ad --- /dev/null +++ b/docs/content/en/content-management/formats.md @@ -0,0 +1,128 @@ +--- +title: Content formats +description: Create your content using Markdown, HTML, Emacs Org Mode, AsciiDoc, Pandoc, or reStructuredText. +categories: [] +keywords: [] +aliases: [/content/markdown-extras/,/content/supported-formats/,/doc/supported-formats/] +--- + +## Introduction + +You may mix content formats throughout your site. For example: + +```tree +content/ +└── posts/ + ├── post-1.md + ├── post-2.adoc + ├── post-3.org + ├── post-4.pandoc + ├── post-5.rst + └── post-6.html +``` + +Regardless of content format, all content must have [front matter][], preferably including both `title` and `date`. + +Hugo selects the content renderer based on the `markup` identifier in front matter, falling back to the file extension. See the [classification](#classification) table below for a list of markup identifiers and recognized file extensions. + +## Formats + +### Markdown + +Create your content in [Markdown][] preceded by front matter. + +Markdown is Hugo's default content format. Hugo natively renders Markdown to HTML using [Goldmark][]. Goldmark is fast and conforms to the [CommonMark][] and [GitHub Flavored Markdown][] specifications. You can configure Goldmark in your [project configuration][configure goldmark]. + +Hugo provides custom Markdown features including: + +[Attributes][] +: Apply HTML attributes such as `class` and `id` to Markdown images and block elements including blockquotes, fenced code blocks, headings, horizontal rules, lists, paragraphs, and tables. + +[Extensions][] +: Leverage the embedded Markdown extensions to create tables, definition lists, footnotes, task lists, inserted text, mark text, subscripts, superscripts, and more. + +[Mathematics][] +: Include mathematical equations and expressions in Markdown using LaTeX markup. + +[Render hooks][] +: Override the conversion of Markdown to HTML when rendering fenced code blocks, headings, images, and links. For example, render every standalone image as an HTML `figure` element. + +### HTML + +Create your content in [HTML][] preceded by front matter. The content is typically what you would place within an HTML document's `body` or `main` element. + +> [!NOTE] +> The HTML content format is denied by default. See [`security.allowContent`][]. + +### Emacs Org Mode + +Create your content in the [Emacs Org Mode][] format preceded by front matter. You can use Org Mode keywords for front matter. See [details][]. + +### AsciiDoc + +Create your content in the [AsciiDoc][] format preceded by front matter. Hugo renders AsciiDoc content to HTML using the Asciidoctor executable. You must install Asciidoctor and its dependencies (Ruby) to render the AsciiDoc content format. + +You can configure the AsciiDoc renderer in your [project configuration][configure asciidoc]. + +In its default configuration, Hugo passes these CLI flags when calling the Asciidoctor executable: + +```sh +--no-header-footer +``` + +The CLI flags passed to the Asciidoctor executable depend on configuration. You may inspect the flags when building your project: + +```sh +hugo build --logLevel info +``` + +### Pandoc + +Create your content in the [Pandoc][] format preceded by front matter. Hugo renders Pandoc content to HTML using the Pandoc executable. You must install Pandoc to render the Pandoc content format. + +Hugo passes these CLI flags when calling the Pandoc executable: + +```sh +--mathjax +``` + +### reStructuredText + +Create your content in the [reStructuredText][] format preceded by front matter. Hugo renders reStructuredText content to HTML using [Docutils][], specifically rst2html. You must install Docutils and its dependencies (Python) to render the reStructuredText content format. + +Hugo passes these CLI flags when calling the rst2html executable: + +```sh +--leave-comments --initial-header-level=2 +``` + +## Classification + +{{% include "/_common/content-format-table.md" %}} + +When converting content to HTML, Hugo uses: + +- Native renderers for Markdown, HTML, and Emacs Org mode +- External renderers for AsciiDoc, Pandoc, and reStructuredText + +Native renderers are faster than external renderers. + +[AsciiDoc]: https://asciidoc.org/ +[Attributes]: /content-management/markdown-attributes/ +[CommonMark]: https://spec.commonmark.org/current/ +[Docutils]: https://docutils.sourceforge.io/ +[Emacs Org Mode]: https://orgmode.org/ +[Extensions]: /configuration/markup/#extensions +[GitHub Flavored Markdown]: https://github.github.com/gfm/ +[Goldmark]: https://github.com/yuin/goldmark +[HTML]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content +[Markdown]: https://daringfireball.net/projects/markdown/ +[Mathematics]: /content-management/mathematics/ +[Pandoc]: https://pandoc.org/MANUAL.html#pandocs-markdown +[Render hooks]: /render-hooks/introduction/ +[`security.allowContent`]: /configuration/security/#allowcontent +[configure asciidoc]: /configuration/markup/#asciidoc +[configure goldmark]: /configuration/markup/#goldmark +[details]: /content-management/front-matter/#emacs-org-mode +[front matter]: /content-management/front-matter/ +[reStructuredText]: https://docutils.sourceforge.io/rst.html diff --git a/docs/content/en/content-management/front-matter.md b/docs/content/en/content-management/front-matter.md new file mode 100644 index 0000000..c207037 --- /dev/null +++ b/docs/content/en/content-management/front-matter.md @@ -0,0 +1,341 @@ +--- +title: Front matter +description: Use front matter to add metadata to your content. +categories: [] +keywords: [] +aliases: [/content/front-matter/] +--- + +## Overview + +The front matter at the top of each content file is metadata that: + +- Describes the content +- Augments the content +- Establishes relationships with other content +- Controls the published structure of your site +- Determines template selection + +Provide front matter using a serialization format, one of [JSON][], [TOML][], or [YAML][]. Hugo determines the front matter format by examining the delimiters that separate the front matter from the page content. + +See examples of front matter delimiters by toggling between the serialization formats below. + +{{< code-toggle file=content/example.md fm=true >}} +title = 'Example' +date = 2024-02-02T04:14:54-08:00 +draft = false +weight = 10 +[params] +author = 'John Smith' +{{< /code-toggle >}} + +Front matter fields may be [boolean](g), [integer](g), [float](g), [string](g), [arrays](g), or [maps](g). Note that the TOML format also supports unquoted date/time values. + +## Fields + +The most common front matter fields are `date`, `draft`, `title`, and `weight`, but you can specify metadata using any of fields below. + +> [!NOTE] +> The field names below are reserved. For example, you cannot create a custom field named `type`. Create custom fields under the `params` key. See the [parameters](#parameters) section for details. + +`aliases` +: (`[]string`) An array of one or more [page-relative](g) or [site-relative](g) paths that should redirect to the current page. Hugo resolves these to [server-relative](g) URLs during the build process. Access these values from a template using the [`Aliases`][] method on a `Page` object. See the [aliases][] section for details. + +`build` +: (`map`) A map of [build options][]. + +`cascade` +: (`map`) A map (or array of maps) of front matter keys whose values are passed down to the page's descendants unless overwritten by self or a closer ancestor's cascade. See the [cascade](#cascade-1) section for details. + +`date` +: (`string`) The date associated with the page, typically the creation date. Note that the TOML format also supports unquoted date/time values. See the [dates](#dates) section for examples. Access this value from a template using the [`Date`][] method on a `Page` object. + +`description` +: (`string`) Conceptually different than the page `summary`, the description is typically rendered within a `meta` element within the `head` element of the published HTML file. Access this value from a template using the [`Description`][] method on a `Page` object. + +`draft` +: (`bool`) Whether to disable rendering unless you pass the `--buildDrafts` flag to the `hugo` command. Access this value from a template using the [`Draft`][] method on a `Page` object. + +`expiryDate` +: (`string`) The page expiration date. On or after the expiration date, the page will not be rendered unless you pass the `--buildExpired` flag to the `hugo` command. Note that the TOML format also supports unquoted date/time values. See the [dates](#dates) section for examples. Access this value from a template using the [`ExpiryDate`][] method on a `Page` object. + +`headless` +: (`bool`) Applicable to [leaf bundles][], whether to set the `render` and `list` [build options][] to `never`, creating a headless bundle of [page resources][]. + +`isCJKLanguage` +: (`bool`) Whether the content language is in the [CJK](g) family. This value determines how Hugo calculates word count, and affects the values returned by the [`WordCount`][], [`FuzzyWordCount`][], [`ReadingTime`][], and [`Summary`][] methods on a `Page` object. + +`keywords` +: (`[]string`) An array of keywords, typically rendered within a `meta` element within the `head` element of the published HTML file, or used as a [taxonomy](g) to classify content. Access these values from a template using the [`Keywords`][] method on a `Page` object. + +`lastmod` +: (`string`) The date that the page was last modified. Note that the TOML format also supports unquoted date/time values. See the [dates](#dates) section for examples. Access this value from a template using the [`Lastmod`][] method on a `Page` object. + +`layout` +: (`string`) Provide a template name to [target a specific template][], overriding the default [template lookup order][]. Set the value to the base file name of the template, excluding its extension. Access this value from a template using the [`Layout`][] method on a `Page` object. + +`linkTitle` +: (`string`) Typically a shorter version of the `title`. Access this value from a template using the [`LinkTitle`][] method on a `Page` object. + +`markup` +: (`string`) An identifier corresponding to one of the supported [content formats][]. If not provided, Hugo determines the content renderer based on the file extension. + +`menus` +: (`string`, `[]string`, or `map`) If set, Hugo adds the page to the given menu or menus. See the [menus][] page for details. + +`modified` +: Alias to [lastmod](#lastmod). + +`outputs` +: (`[]string`) The [output formats][] to render. See [configure outputs][] for more information. + +`params` +: (`map`) A map of custom [page parameters](#parameters). + +`pubdate` +: Alias to [publishDate](#publishdate). + +`publishDate` +: (`string`) The page publication date. Before the publication date, the page will not be rendered unless you pass the `--buildFuture` flag to the `hugo` command. Note that the TOML format also supports unquoted date/time values. See the [dates](#dates) section for examples. Access this value from a template using the [`PublishDate`][] method on a `Page` object. + +`published` +: Alias to [publishDate](#publishdate). + +`resources` +: (`map array`) An array of maps to provide metadata for [page resources][]. Each element supports the `src`, `name`, `title`, and `params` keys. + +`sitemap` +: (`map`) A map of sitemap options. See the [sitemap templates][] page for details. Access these values from a template using the [`Sitemap`][] method on a `Page` object. + +`sites` +: {{< new-in 0.153.0 />}} +: (`map`) A map to define [sites matrix](g) and [sites complements](g) for the page. + + + + {{< code-toggle file=content/_index.md fm=true >}} + title = 'Home' + [sites.matrix] + languages = ["en","fr"] + versions = ["v1.2.*","v2.*.*"] + roles = ["**"] + [sites.complements] + versions = ["v3.*.*"] + {{< /code-toggle >}} + + + +`slug` +: (`string`) Overrides the last segment of the URL path. Not applicable to `home`, `section`, `taxonomy`, or `term` pages. See the [URL management][] page for details. Access this value from a template using the [`Slug`][] method on a `Page` object. + +`summary` +: (`string`) Conceptually different than the page `description`, the summary either summarizes the content or serves as a teaser to encourage readers to visit the page. Access this value from a template using the [`Summary`][] method on a `Page` object. + +`title` +: (`string`) The page title. Access this value from a template using the [`Title`][] method on a `Page` object. + +`translationKey` +: (`string`) An arbitrary value used to relate two or more translations of the same page, useful when the translated pages do not share a common path. Access this value from a template using the [`TranslationKey`][] method on a `Page` object. + +`type` +: (`string`) The [content type](g), overriding the value derived from the top-level section in which the page resides. Access this value from a template using the [`Type`][] method on a `Page` object. + +`unpublishdate` +: Alias to [expirydate](#expirydate). + +`url` +: (`string`) Overrides the entire URL path. Applicable to regular pages and section pages. See the [URL management][] page for details. + +`weight` +: (`int`) The page [weight](g), used to order the page within a [page collection](g). Access this value from a template using the [`Weight`][] method on a `Page` object. + +## Parameters + +Specify custom page parameters under the `params` key in front matter: + +{{< code-toggle file=content/example.md fm=true >}} +title = 'Example' +date = 2024-02-02T04:14:54-08:00 +draft = false +weight = 10 +[params] +author = 'John Smith' +{{< /code-toggle >}} + +Access these values from a template using the [`Params`][] or [`Param`][] method on a `Page` object. + +## Taxonomies + +Classify content by adding taxonomy terms to front matter. For example, with this project configuration: + +{{< code-toggle file=hugo >}} +[taxonomies] +tag = 'tags' +genre = 'genres' +{{< /code-toggle >}} + +Add taxonomy terms as shown below: + +{{< code-toggle file=content/example.md fm=true >}} +title = 'Example' +date = 2024-02-02T04:14:54-08:00 +draft = false +weight = 10 +tags = ['red','blue'] +genres = ['mystery','romance'] +[params] +author = 'John Smith' +{{< /code-toggle >}} + +You can add taxonomy terms to the front matter of any these [page kinds](g): + +- `home` +- `page` +- `section` +- `taxonomy` +- `term` + +Access taxonomy terms from a template using the [`Params`][] or [`GetTerms`][] method on a `Page` object. For example: + +```go-html-template {file="layouts/page.html"} +{{ with .GetTerms "tags" }} +

    Tags

    + +{{ end }} +``` + +## Cascade + +> [!NOTE] + > For multilingual projects, defining cascade values in your project configuration is often more efficient. This avoids repeating the same cascade values for each language. See [details][]. + +A [branch](g) can cascade front matter values to its descendants. However, this cascading will be prevented if the descendant already defines the field, or if a closer ancestor branch has already cascaded a value for that same field. + +For example, to cascade the `color` page parameter from the home page to all its descendants: + +{{< code-toggle file=content/_index.md fm=true >}} +title = 'Home' +[cascade.params] +color = 'red' +{{< /code-toggle >}} + +### Target + + + +The `target` key accepts a [page matcher](g) to limit cascaded values to a subset of pages.[^1] If a target is not specified, values cascade to all descendant pages. + +{{% include "/_common/configuration/page-matcher.md" %}} + +For example, to cascade the `color` page parameter from the home page to the `articles` section and its descendants: + +{{< code-toggle file=hugo >}} +[cascade.params] +color = 'red' +[cascade.target] +path = '{/articles,/articles/**}' +{{< /code-toggle >}} + +### Array + +Define an array of cascade maps to apply different values to different targets. For example: + +{{< code-toggle file=content/_index.md fm=true >}} +title = 'Home' +[[cascade]] +[cascade.params] +color = 'red' +[cascade.target] +path = '{/articles,/articles/**}' +[[cascade]] +[cascade.params] +color = 'blue' +[cascade.target] +path = '{/tutorials,/tutorials/**}' +{{< /code-toggle >}} + +## Emacs Org Mode + +If your [content format][] is [Emacs Org Mode][], you may provide front matter using Org Mode keywords. For example: + +```text {file="content/example.org"} +#+TITLE: Example +#+DATE: 2024-02-02T04:14:54-08:00 +#+DRAFT: false +#+AUTHOR: John Smith +#+GENRES: mystery +#+GENRES: romance +#+TAGS: red +#+TAGS: blue +#+WEIGHT: 10 +``` + +Note that you can also specify array elements on a single line: + +```text {file="content/example.org"} +#+TAGS[]: red blue +``` + +## Dates + +When populating a date field, whether a [custom page parameter](#parameters) or one of the four predefined fields ([`date`](#date), [`expiryDate`](#expirydate), [`lastmod`](#lastmod), [`publishDate`](#publishdate)), use one of these parsable formats: + +{{% include "/_common/parsable-date-time-strings.md" %}} + +To override the default time zone, set the [`timeZone`][] in your project configuration. The order of precedence for determining the time zone is: + +1. The time zone offset in the date/time string +1. The time zone specified in your project configuration +1. The `Etc/UTC` time zone + +[^1]: The `_target` alias for `target` is deprecated and will be removed in a future release. + +[Emacs Org Mode]: https://orgmode.org/ +[JSON]: https://www.json.org/ +[TOML]: https://toml.io/ +[URL management]: /content-management/urls/#slug +[YAML]: https://yaml.org/ +[`Aliases`]: /methods/page/aliases/ +[`Date`]: /methods/page/date/ +[`Description`]: /methods/page/description/ +[`Draft`]: /methods/page/draft/ +[`ExpiryDate`]: /methods/page/expirydate/ +[`FuzzyWordCount`]: /methods/page/wordcount/ +[`GetTerms`]: /methods/page/getterms/ +[`Keywords`]: /methods/page/keywords/ +[`Lastmod`]: /methods/page/date/ +[`Layout`]: /methods/page/layout/ +[`LinkTitle`]: /methods/page/linktitle/ +[`Param`]: /methods/page/param/ +[`Params`]: /methods/page/params/ +[`PublishDate`]: /methods/page/publishdate/ +[`ReadingTime`]: /methods/page/readingtime/ +[`Sitemap`]: /methods/page/sitemap/ +[`Slug`]: /methods/page/slug/ +[`Summary`]: /methods/page/summary/ +[`Title`]: /methods/page/title/ +[`TranslationKey`]: /methods/page/translationkey/ +[`Type`]: /methods/page/type/ +[`Weight`]: /methods/page/weight/ +[`WordCount`]: /methods/page/wordcount/ +[`timeZone`]: /configuration/all/#timezone +[aliases]: /content-management/urls/#aliases +[build options]: /content-management/build-options/ +[configure outputs]: /configuration/outputs/#outputs-per-page +[content format]: /content-management/formats/ +[content formats]: /content-management/formats/#classification +[details]: /configuration/cascade/ +[leaf bundles]: /content-management/page-bundles/#leaf-bundles +[menus]: /content-management/menus/#define-in-front-matter +[output formats]: /configuration/output-formats/ +[page resources]: /content-management/page-resources/#metadata +[sitemap templates]: /templates/sitemap/ +[target a specific template]: /templates/lookup-order/#target-a-template +[template lookup order]: /templates/lookup-order/ diff --git a/docs/content/en/content-management/image-processing/index.md b/docs/content/en/content-management/image-processing/index.md new file mode 100644 index 0000000..9e8fa95 --- /dev/null +++ b/docs/content/en/content-management/image-processing/index.md @@ -0,0 +1,170 @@ +--- +title: Image processing +description: Transform images to change their size, shape, and appearance. +categories: [] +keywords: [] +--- + +Hugo provides methods to transform and analyze images during the build process. While Hugo can manage any image format as a resource, only [processable images](g) can be transformed using the methods below. The results are cached to ensure subsequent builds remain fast. + +> [!NOTE] +> Use the [`reflect.IsImageResourceProcessable`][] function to verify that an image can be processed. + +## Resources + +To process an image you must capture the file as a page resource, a global resource, or a remote resource. + +### Page + +{{% glossary-term "page resource" %}} + +```tree +content/ +└── posts/ + └── post-1/ <-- page bundle + ├── index.md + └── sunset.jpg <-- page resource +``` + +To capture an image as a page resource: + +```go-html-template +{{ $image := .Resources.Get "sunset.jpg" }} +``` + +### Global + +{{% glossary-term "global resource" %}} + +```tree +assets/ +└── images/ + └── sunset.jpg <-- global resource +``` + +To capture an image as a global resource: + +```go-html-template +{{ $image := resources.Get "images/sunset.jpg" }} +``` + +### Remote + +{{% glossary-term "remote resource" %}} + +To capture an image as a remote resource: + +```go-html-template +{{ $image := resources.GetRemote "https://gohugo.io/img/hugo-logo.png" }} +``` + +## Rendering + +Once you have captured an image as a resource, render it in your templates using the [`Permalink`][], [`RelPermalink`][], [`Width`][], and [`Height`][] methods. + +Example 1: Throw an error if the resource is not found. + +```go-html-template +{{ $image := .Resources.GetMatch "sunset.jpg" }} + +``` + +Example 2: Skip image rendering if the resource is not found. + +```go-html-template +{{ $image := .Resources.GetMatch "sunset.jpg" }} +{{ with $image }} + +{{ end }} +``` + +Example 3: A more concise way to skip image rendering if the resource is not found. + +```go-html-template +{{ with .Resources.GetMatch "sunset.jpg" }} + +{{ end }} +``` + +Example 4: Skip rendering if there's problem accessing a remote resource. + +```go-html-template +{{ $url := "https://gohugo.io/img/hugo-logo.png" }} +{{ with try (resources.GetRemote $url) }} + {{ with .Err }} + {{ errorf "%s" . }} + {{ else with .Value }} + + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} + {{ end }} +{{ end }} +``` + +{{% include "/_common/functions/reflect/image-reflection-functions.md" %}} + +## Processing + +To transform an image, apply a processing method to the image resource. Hugo generates the processed image on demand, caches the result, and returns a new resource object. + +```go-html-template +{{ with .Resources.Get "sunset.jpg" }} + {{ with .Resize "400x" }} + + {{ end }} +{{ end }} +``` + +> [!NOTE] +> Metadata is not preserved during image transformation. Use the [`Meta`][] method with the original image resource to extract metadata from supported formats. + +Select a method from the table below for syntax and usage examples, depending on your specific transformation or metadata requirements: + +{{% render-table-of-pages-in-section + path=/methods/resource + filter=methods_resource_image_processing + filterType=include + headingColumn1=Method + headingColumn2=Description +%}}{class="!mt-0"} + +## Performance + +### Caching + +Hugo processes images on demand and returns a new resource object. To ensure subsequent builds remain fast, Hugo caches the results in the directory specified in the [file cache][] section of your project configuration. + +If you host your site with Netlify, include the following in your project configuration to persist the image cache between builds: + +```toml +[caches] + [caches.images] + dir = ':cacheDir/images' +``` + +### Garbage collection + +If you change image processing methods, or rename/remove images, the cache will eventually contain unused files. To remove them and reclaim disk space, run Hugo's garbage collection: + +```sh +hugo build --gc +``` + +### Resource usage + +The time and memory required to process an image increase with the image's dimensions. For example, a `4032x2268` image requires significantly more memory and processing time than a `1920x1080` image. + +If your source images are much larger than the maximum size you intend to publish, consider scaling them down before the build to optimize performance. + +## Configuration + +See [configure imaging][]. + +[`Height`]: /methods/resource/height/ +[`Meta`]: /methods/resource/meta/ +[`Permalink`]: /methods/resource/permalink/ +[`RelPermalink`]: /methods/resource/relpermalink/ +[`Width`]: /methods/resource/width/ +[`reflect.IsImageResourceProcessable`]: /functions/reflect/isimageresourceprocessable/ +[configure imaging]: /configuration/imaging/ +[file cache]: /configuration/caches/ diff --git a/docs/content/en/content-management/image-processing/sunset.jpg b/docs/content/en/content-management/image-processing/sunset.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4dbcc08368a65f26a0da6cbb68428a3a49a5d480 GIT binary patch literal 34584 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

    fhJg;A67nEQ85q$^>hl*UFrwAP$ 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 literal 0 HcmV?d00001 diff --git a/docs/content/en/content-management/markdown-attributes.md b/docs/content/en/content-management/markdown-attributes.md new file mode 100644 index 0000000..c9ff839 --- /dev/null +++ b/docs/content/en/content-management/markdown-attributes.md @@ -0,0 +1,119 @@ +--- +title: Markdown attributes +description: Use Markdown attributes to add HTML attributes when rendering Markdown to HTML. +categories: [] +keywords: [] +--- + +## Overview + +Hugo supports Markdown attributes on images and block elements including blockquotes, fenced code blocks, headings, horizontal rules, lists, paragraphs, and tables. + +For example: + +```md +This is a paragraph. +{class="foo bar" id="baz"} +``` + +With `class` and `id` attributes you can also use short-form notation: + +```md +This is a paragraph. +{.foo .bar #baz} +``` + +Hugo renders both of the examples above to: + +```html +

    This is a paragraph.

    +``` + +With `class` and `id` attributes, whether you use long-form or short-form notation, the resulting values are available in [render hook templates][] via the `Attributes` method. For example: + +```go-html-template +{{ .Attributes.class }} → foo bar +{{ .Attributes.id }} → baz +``` + +## Block elements + +Update your project configuration to enable Markdown attributes for block-level elements. + +{{< code-toggle file=hugo >}} +[markup.goldmark.parser.attribute] +title = true # default is true +block = true # default is false +{{< /code-toggle >}} + +## Standalone images + +By default, when the [Goldmark][] Markdown renderer encounters a standalone image element (no other elements or text on the same line), it wraps the image element within a paragraph element per the [CommonMark][] specification. + +If you were to place an attribute list beneath an image element, Hugo would apply the attributes to the surrounding paragraph, not the image. + +To apply attributes to a standalone image element, you must disable the default wrapping behavior: + +{{< code-toggle file=hugo >}} +[markup.goldmark.parser] +wrapStandAloneImageWithinParagraph = false # default is true +{{< /code-toggle >}} + +## Usage + +You may add [global HTML attributes][], or HTML attributes specific to the current element type. Consistent with its content security model, Hugo removes HTML event attributes such as `onclick` and `onmouseover`. + +> [!NOTE] +> Within fenced code blocks, Hugo interprets the `style` attribute as a syntax highlighting [option][option] rather than a global HTML attribute. + +The attribute list consists of one or more key-value pairs, separated by spaces or commas, wrapped by braces. You must quote string values that contain spaces. Unlike HTML, boolean attributes must have both key and value. + +For example: + +```md +> This is a blockquote. +{class="foo bar" hidden=hidden} +``` + +Hugo renders this to: + +```html + +``` + +In most cases, place the attribute list beneath the markup element. For headings and fenced code blocks, place the attribute list on the right. + +Element | Position of attribute list +:-----------------|:-------------------------- +blockquote | bottom +fenced code block | right +heading | right +horizontal rule | bottom +image | bottom +list | bottom +paragraph | bottom +table | bottom + +For example: + +````md +## Section 1 {class=foo} + +```sh {class=foo linenos=inline} +declare a=1 +echo "${a}" +``` + +This is a paragraph. +{class=foo} +```` + +As shown above, the attribute list for fenced code blocks is not limited to HTML attributes. You can also configure syntax highlighting by passing one or more of [these options][option]. + +[CommonMark]: https://spec.commonmark.org/current/ +[Goldmark]: https://github.com/yuin/goldmark +[global HTML attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes +[option]: /functions/transform/highlight/#options +[render hook templates]: /render-hooks/introduction/ diff --git a/docs/content/en/content-management/mathematics.md b/docs/content/en/content-management/mathematics.md new file mode 100644 index 0000000..529d669 --- /dev/null +++ b/docs/content/en/content-management/mathematics.md @@ -0,0 +1,223 @@ +--- +title: Mathematics in Markdown +linkTitle: Mathematics +description: Include mathematical equations and expressions in Markdown using LaTeX markup. +categories: [] +keywords: [] +--- + +## Overview + +Mathematical equations and expressions written in [LaTeX][] are common in academic and scientific publications. Your browser typically renders this mathematical markup using an open-source JavaScript display engine such as [MathJax][] or [KaTeX][]. + +For example, this LaTeX markup: + +```md +\[ +\begin{aligned} +KL(\hat{y} || y) &= \sum_{c=1}^{M}\hat{y}_c \log{\frac{\hat{y}_c}{y_c}} \\ +JS(\hat{y} || y) &= \frac{1}{2}(KL(y||\frac{y+\hat{y}}{2}) + KL(\hat{y}||\frac{y+\hat{y}}{2})) +\end{aligned} +\] +``` + +Is rendered to: + +\[ +\begin{aligned} +KL(\hat{y} || y) &= \sum_{c=1}^{M}\hat{y}_c \log{\frac{\hat{y}_c}{y_c}} \\ +JS(\hat{y} || y) &= \frac{1}{2}(KL(y||\frac{y+\hat{y}}{2}) + KL(\hat{y}||\frac{y+\hat{y}}{2})) +\end{aligned} +\] + +Equations and expressions can be displayed inline with other text, or as standalone blocks. Block presentation is also known as "display" mode. + +Whether an equation or expression appears inline, or as a block, depends on the delimiters that surround the mathematical markup. Delimiters are defined in pairs, where each pair consists of an opening and closing delimiter. The opening and closing delimiters may be the same, or different. + +> [!NOTE] +> You can configure Hugo to render mathematical markup on the client side using the MathJax or KaTeX display engine, or you can render the markup with the [`transform.ToMath`][] function while building your project. +> +> The first approach is described below. + +## Setup + +Follow these instructions to include mathematical equations and expressions in your Markdown using LaTeX markup. + +Step 1 +: Enable and configure the Goldmark [passthrough extension][] in your project configuration. The passthrough extension preserves raw Markdown within delimited snippets of text, including the delimiters themselves. + + {{< code-toggle file=hugo copy=true >}} + [markup.goldmark.extensions.passthrough] + enable = true + + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + inline = [['\(', '\)']] + + [params] + math = true + {{< /code-toggle >}} + + The configuration above enables mathematical rendering on every page unless you set the `math` parameter to `false` in front matter. To enable mathematical rendering as needed, set the `math` parameter to `false` in your project configuration, and set the `math` parameter to `true` in front matter. Use this parameter in your base template as shown in [Step 3](#step-3). + + > [!NOTE] + > The configuration above precludes the use of the `$...$` delimiter pair for inline equations. Although you can add this delimiter pair to the configuration and JavaScript, you must double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting. + > + > See the [inline delimiters](#inline-delimiters) section for details. + + To disable passthrough of inline snippets, omit the `inline` key from the configuration: + + {{< code-toggle file=hugo >}} + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + {{< /code-toggle >}} + + You can define your own opening and closing delimiters, provided they match the delimiters that you set in [Step 2](#step-2). + + {{< code-toggle file=hugo >}} + [markup.goldmark.extensions.passthrough.delimiters] + block = [['@@', '@@']] + inline = [['@', '@']] + {{< /code-toggle >}} + +Step 2 +: Create a _partial_ template to load MathJax or KaTeX. The example below loads MathJax, or you can use KaTeX as described in the [engines](#engines) section. + + ```go-html-template {file="layouts/_partials/math.html" copy=true} + + + + ``` + + The delimiters above must match the delimiters in your project configuration. + +Step 3 +: Conditionally call the _partial_ template from the base template. + + ```go-html-template {file="layouts/baseof.html"} + + ... + {{ if .Param "math" }} + {{ partialCached "math.html" . }} + {{ end }} + ... + + ``` + + The example above loads the _partial_ template if you have set the `math` parameter in front matter to `true`. If you have not set the `math` parameter in front matter, the conditional statement falls back to the `math` parameter in your project configuration. + +Step 4 +: If you set the `math` parameter to `false` in your project configuration, you must set the `math` parameter to `true` in front matter. For example: + + {{< code-toggle file=content/math-examples.md fm=true >}} + title = 'Math examples' + date = 2024-01-24T18:09:49-08:00 + [params] + math = true + {{< /code-toggle >}} + +Step 5 +: Include mathematical equations and expressions in Markdown using LaTeX markup. + + ```md {file="content/math-examples.md" copy=true} + This is an inline \(a^*=x-b^*\) equation. + + These are block equations: + + \[a^*=x-b^*\] + + \[ a^*=x-b^* \] + + \[ + a^*=x-b^* + \] + + These are also block equations: + + $$a^*=x-b^*$$ + + $$ a^*=x-b^* $$ + + $$ + a^*=x-b^* + $$ + ``` + +## Inline delimiters + +The configuration, JavaScript, and examples above use the `\(...\)` delimiter pair for inline equations. The `$...$` delimiter pair is a common alternative, but using it may result in unintended formatting if you use the `$` symbol outside of math contexts. + +If you add the `$...$` delimiter pair to your configuration and JavaScript, you must double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting. For example: + +```md +I will give you \\$2 if you can solve $y = x^2$. +``` + +> [!NOTE] +> If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation][]. + +## Engines + +MathJax and KaTeX are open-source JavaScript display engines. + +> [!NOTE] +> If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation][]. +> +>See the [inline delimiters](#inline-delimiters) section for details. + +To use KaTeX instead of MathJax, replace the _partial_ template from [Step 2](#step-2) with this: + +```go-html-template {file="layouts/_partials/math.html" copy=true} + + + + + + + +``` + +The delimiters above must match the delimiters in your project configuration. + +## Chemistry + +Both MathJax and KaTeX provide support for chemical equations. For example: + +```md +$$C_p[\ce{H2O(l)}] = \pu{75.3 J // mol K}$$ +``` + +$$C_p[\ce{H2O(l)}] = \pu{75.3 J // mol K}$$ + +As shown in [Step 2](#step-2) above, MathJax supports chemical equations without additional configuration. To add chemistry support to KaTeX, enable the mhchem extension as described in the KaTeX [documentation][]. + +[KaTeX]: https://katex.org/ +[LaTeX]: https://www.latex-project.org/ +[MathJax]: https://www.mathjax.org/ +[`transform.ToMath`]: /functions/transform/tomath/ +[documentation]: https://katex.org/docs/libs +[passthrough extension]: /configuration/markup/#passthrough +[this KaTeX limitation]: https://github.com/KaTeX/KaTeX/issues/437 diff --git a/docs/content/en/content-management/menus.md b/docs/content/en/content-management/menus.md new file mode 100644 index 0000000..1590366 --- /dev/null +++ b/docs/content/en/content-management/menus.md @@ -0,0 +1,100 @@ +--- +title: Menus +description: Create menus by defining entries, localizing each entry, and rendering the resulting data structure. +categories: [] +keywords: [] +aliases: [/extras/menus/] +--- + +## Overview + +To create a menu for your site: + +1. Define the menu entries +1. [Localize](multilingual/#menus) each entry +1. Render the menu with a [template][] + +Create multiple menus, either flat or nested. For example, create a main menu for the header, and a separate menu for the footer. + +There are three ways to define menu entries: + +1. Automatically +1. In front matter +1. In your project configuration + +> [!NOTE] +> Although you can use these methods in combination when defining a menu, the menu will be easier to conceptualize and maintain if you use one method throughout the site. + +## Define automatically + +To automatically define a menu entry for each top-level [section](g) of your site, enable the section pages menu in your project configuration. + +{{< code-toggle file=hugo >}} +sectionPagesMenu = 'main' +{{< /code-toggle >}} + +This creates a menu structure that you can access with `site.Menus.main` in your templates. See [menu templates][] for details. + +## Define in front matter + +To add a page to the "main" menu: + +{{< code-toggle file=content/about.md fm=true >}} +title = 'About' +menus = 'main' +{{< /code-toggle >}} + +Access the entry with `site.Menus.main` in your templates. See [menu templates][] for details. + +To add a page to the "main" and "footer" menus: + +{{< code-toggle file=content/contact.md fm=true >}} +title = 'Contact' +menus = ['main','footer'] +{{< /code-toggle >}} + +Access the entry with `site.Menus.main` and `site.Menus.footer` in your templates. See [menu templates][] for details. + +> [!NOTE] +> The configuration key in the examples above is `menus`. The `menu` (singular) configuration key is an alias for `menus`. + +### Properties + +Use these properties when defining menu entries in front matter: + +{{% include "/_common/menu-entry-properties.md" %}} + +### Example + +This front matter menu entry demonstrates some of the available properties: + + +{{< code-toggle file=content/products/software.md fm=true >}} +title = 'Software' +[menus.main] +parent = 'Products' +weight = 20 +pre = '' +[menus.main.params] +class = 'center' +{{< /code-toggle >}} + + +Access the entry with `site.Menus.main` in your templates. See [menu templates][] for details. + +## Define in project configuration + +See [configure menus][]. + +## Localize + +Hugo provides two methods to localize your menu entries. See [multilingual][]. + +## Render + +See [menu templates][]. + +[configure menus]: /configuration/menus/ +[menu templates]: /templates/menu/ +[multilingual]: /content-management/multilingual/#menus +[template]: /templates/menu/ diff --git a/docs/content/en/content-management/multilingual.md b/docs/content/en/content-management/multilingual.md new file mode 100644 index 0000000..d652a8c --- /dev/null +++ b/docs/content/en/content-management/multilingual.md @@ -0,0 +1,399 @@ +--- +title: Multilingual mode +linkTitle: Multilingual +description: Localize your project for each language and region, including translations, images, dates, currencies, numbers, percentages, and collation sequence. Hugo's multilingual framework supports single-host and multihost configurations. +categories: [] +keywords: [] +aliases: [/content/multilingual/,/tutorials/create-a-multilingual-site/] +--- + +## Configuration + +See [configure languages][]. + +## Translate your content + +There are two ways to manage your content translations. Both ensure each page is assigned a language and is linked to its counterpart translations. + +### Translation by file name + +Considering the following example: + +1. `/content/about.en.md` +1. `/content/about.fr.md` + +The first file is assigned the English language and is linked to the second. +The second file is assigned the French language and is linked to the first. + +Their language is assigned according to the language code added as a suffix to the file name. + +By having the same path and base file name, the content pieces are linked together as translated pages. + +> [!NOTE] +> The language code in a file name must be lowercase. For example, use `about.en-us.md` instead of `about.en-US.md`. + +> [!NOTE] +> If a file has no language code, it will be assigned the default language. + +### Translation by content directory + +This system uses different content directories for each of the languages. Each language's `content` directory is set using the `contentDir` parameter. + +{{< code-toggle file=hugo >}} +[languages.en] +contentDir = 'content/english' +label = "English" +weight = 10 + +[languages.fr] +contentDir = 'content/french' +label = "Français" +weight = 20 +{{< /code-toggle >}} + +The value of `contentDir` can be any valid path -- even absolute path references. The only restriction is that the content directories cannot overlap. + +Considering the following example in conjunction with the configuration above: + +1. `/content/english/about.md` +1. `/content/french/about.md` + +The first file is assigned the English language and is linked to the second. +The second file is assigned the French language and is linked to the first. + +Their language is assigned according to the `content` directory they are placed in. + +By having the same path and basename (relative to their language `content` directory), the content pieces are linked together as translated pages. + +### Bypassing default linking + +Any pages sharing the same `translationKey` set in front matter will be linked as translated pages regardless of basename or location. + +Considering the following example: + +1. `/content/about-us.en.md` +1. `/content/om.nn.md` +1. `/content/presentation/a-propos.fr.md` + +{{< code-toggle file=hugo >}} +translationKey: "about" +{{< /code-toggle >}} + +By setting the `translationKey` front matter parameter to `about` in all three pages, they will be linked as translated pages. + +### Localizing permalinks + +Because paths and file names are used to handle linking, all translated pages will share the same URL (apart from the language subdirectory). + +To localize URLs: + +- For a regular page, set either [`slug`][] or [`url`][] in front matter +- For a section page, set [`url`][] in front matter + +For example, a French translation can have its own localized slug. + +{{< code-toggle file=content/about.fr.md fm=true >}} +title: A Propos +slug: "a-propos" +{{< /code-toggle >}} + +At render, Hugo will build both `/about/` and `/fr/a-propos/` without affecting the translation link. + +### Page bundles + +To avoid the burden of having to duplicate files, each Page Bundle inherits the resources of its linked translated pages' bundles except for the content files (Markdown files, HTML files etc.). + +Therefore, from within a template, the page will have access to the files from all linked pages' bundles. + +If, across the linked bundles, two or more files share the same basename, only one will be included and chosen as follows: + +- File from current language bundle, if present. +- First file found across bundles by order of language `Weight`. + +> [!NOTE] +> Page Bundle resources follow the same language assignment logic as content files, both by file name (`image.jpg`, `image.fr.jpg`) and by directory (`english/about/header.jpg`, `french/about/header.jpg`). + +## Translation of strings + +See the [`lang.Translate`][] function. + +## Localization + +The following localization examples assume your project's primary language is English, with translations to French and German. + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'en' + +[languages] +[languages.en] +contentDir = 'content/en' +label = 'English' +weight = 1 +[languages.fr] +contentDir = 'content/fr' +label = 'Français' +weight = 2 +[languages.de] +contentDir = 'content/de' +label = 'Deutsch' +weight = 3 + +{{< /code-toggle >}} + +### Dates + +With this front matter: + +{{< code-toggle file=hugo >}} +date = 2021-11-03T12:34:56+01:00 +{{< /code-toggle >}} + +And this template code: + +```go-html-template +{{ .Date | time.Format ":date_full" }} +``` + +The rendered page displays: + +Language|Value +:--|:-- +English|Wednesday, November 3, 2021 +Français|mercredi 3 novembre 2021 +Deutsch|Mittwoch, 3. November 2021 + +See [`time.Format`][] for details. + +### Currency + +With this template code: + +```go-html-template +{{ 512.5032 | lang.FormatCurrency 2 "USD" }} +``` + +The rendered page displays: + +Language|Value +:--|:-- +English|$512.50 +Français|512,50 $US +Deutsch|512,50 $ + +See [lang.FormatCurrency][] and [lang.FormatAccounting][] for details. + +### Numbers + +With this template code: + +```go-html-template +{{ 512.5032 | lang.FormatNumber 2 }} +``` + +The rendered page displays: + +Language|Value +:--|:-- +English|512.50 +Français|512,50 +Deutsch|512,50 + +See [lang.FormatNumber][] and [lang.FormatNumberCustom][] for details. + +### Percentages + +With this template code: + +```go-html-template +{{ 512.5032 | lang.FormatPercent 2 }} +``` + +The rendered page displays: + +Language|Value +:--|:-- +English|512.50% +Français|512,50 % +Deutsch|512,50 % + +See [lang.FormatPercent][] for details. + +## Menus + +Localization of menu entries depends on how you define them: + +- When you define menu entries [automatically][] using the section pages menu, you must use translation tables to localize each entry. +- When you define menu entries in [front matter][], they are already localized based on the front matter itself. If the front matter values are insufficient, use translation tables to localize each entry. +- When you define menu entries in your [project configuration][], you must create language-specific menu entries under each language key. If the names of the menu entries are insufficient, use translation tables to localize each entry. + +### Create language-specific menu entries + +#### Method 1 -- Use a single configuration file + +For a simple menu with a small number of entries, use a single configuration file. For example: + +{{< code-toggle file=hugo >}} +[languages.de] +label = 'Deutsch' +locale = 'de-DE' +weight = 1 + +[[languages.de.menus.main]] +name = 'Produkte' +pageRef = '/products' +weight = 10 + +[[languages.de.menus.main]] +name = 'Leistungen' +pageRef = '/services' +weight = 20 + +[languages.en] +label = 'English' +locale = 'en-US' +weight = 2 + +[[languages.en.menus.main]] +name = 'Products' +pageRef = '/products' +weight = 10 + +[[languages.en.menus.main]] +name = 'Services' +pageRef = '/services' +weight = 20 +{{< /code-toggle >}} + +#### Method 2 -- Use a configuration directory + +With a more complex menu structure, create a [configuration directory][] and split the menu entries into multiple files, one file per language. For example: + +```tree +config/ +└── _default/ + ├── menus.de.toml + ├── menus.en.toml + └── hugo.toml +``` + +{{< code-toggle file=config/_default/menus.de >}} +[[main]] +name = 'Produkte' +pageRef = '/products' +weight = 10 +[[main]] +name = 'Leistungen' +pageRef = '/services' +weight = 20 +{{< /code-toggle >}} + +{{< code-toggle file=config/_default/menus.en >}} +[[main]] +name = 'Products' +pageRef = '/products' +weight = 10 +[[main]] +name = 'Services' +pageRef = '/services' +weight = 20 +{{< /code-toggle >}} + +### Use translation tables + +When rendering the text that appears in menu each entry, the [example menu template][] does this: + +```go-html-template +{{ or (T .Identifier) .Name | safeHTML }} +``` + +It queries the translation table for the current language using the menu entry's `identifier` and returns the translated string. If the translation table does not exist, or if the `identifier` key is not present in the translation table, it falls back to `name`. + +The `identifier` depends on how you define menu entries: + +- If you define the menu entry [automatically][] using the section pages menu, the `identifier` is the page's `.Section`. +- If you define the menu entry in your [project configuration][] or in [front matter][], set the `identifier` property to the desired value. + +For example, if you define menu entries in project configuration: + +{{< code-toggle file=hugo >}} +[[menus.main]] + identifier = 'products' + name = 'Products' + pageRef = '/products' + weight = 10 +[[menus.main]] + identifier = 'services' + name = 'Services' + pageRef = '/services' + weight = 20 +{{< / code-toggle >}} + +Create corresponding entries in the translation tables: + +{{< code-toggle file=i18n/de >}} +products = 'Produkte' +services = 'Leistungen' +{{< / code-toggle >}} + +## Missing translations + +If a string does not have a translation for the current language, Hugo will use the value from the default language. If no default value is set, an empty string will be shown. + +While translating a Hugo website, it can be helpful to have a visual indicator of missing translations. The [`enableMissingTranslationPlaceholders`][] configuration setting will flag all untranslated strings with the placeholder `[i18n] identifier`, where `identifier` is the id of the missing translation. + +> [!NOTE] +> Hugo will generate your website with these missing translation placeholders. It might not be suitable for production environments. + +For merging of content from other languages (i.e. missing content translations), see [lang.Merge]. + +To track down missing translation strings, run Hugo with the `--printI18nWarnings` flag: + +```sh +hugo build --printI18nWarnings | grep i18n +i18n|MISSING_TRANSLATION|en|wordCount +``` + +## Multilingual themes support + +To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there is more than one language, URLs must meet the following criteria: + +- Come from the built-in `.Permalink` or `.RelPermalink` +- Be constructed with the [`urls.RelLangURL`][] or [`urls.AbsLangURL`][] function, or be prefixed with `{{ .LanguagePrefix }}` + +If there is more than one language defined, the `LanguagePrefix` method will return `/en` (or whatever the current language is). If not enabled, it will be an empty string (and is therefore harmless for single-language Hugo websites). + +## Generate multilingual content with `hugo new content` + +If you organize content with translations in the same directory: + +```sh +hugo new content post/test.en.md +hugo new content post/test.de.md +``` + +If you organize content with translations in different directories: + +```sh +hugo new content content/en/post/test.md +hugo new content content/de/post/test.md +``` + +[`enableMissingTranslationPlaceholders`]: /configuration/all/#enablemissingtranslationplaceholders +[`lang.Translate`]: /functions/lang/translate/ +[`slug`]: /content-management/urls/#slug +[`time.Format`]: /functions/time/format/ +[`url`]: /content-management/urls/#url +[`urls.AbsLangURL`]: /functions/urls/abslangurl/ +[`urls.RelLangURL`]: /functions/urls/rellangurl/ +[automatically]: /content-management/menus/#define-automatically +[configuration directory]: /configuration/introduction/#configuration-directory +[configure languages]: /configuration/languages/ +[example menu template]: /templates/menu/#example +[front matter]: /content-management/menus/#define-in-front-matter +[lang.FormatAccounting]: /functions/lang/formataccounting/ +[lang.FormatCurrency]: /functions/lang/formatcurrency/ +[lang.FormatNumberCustom]: /functions/lang/formatnumbercustom/ +[lang.FormatNumber]: /functions/lang/formatnumber/ +[lang.FormatPercent]: /functions/lang/formatpercent/ +[lang.Merge]: /functions/lang/merge/ +[project configuration]: /content-management/menus/#define-in-project-configuration diff --git a/docs/content/en/content-management/organization/index.md b/docs/content/en/content-management/organization/index.md new file mode 100644 index 0000000..b559876 --- /dev/null +++ b/docs/content/en/content-management/organization/index.md @@ -0,0 +1,155 @@ +--- +title: Content organization +linkTitle: Organization +description: Hugo assumes that the same structure that works to organize your source content is used to organize the rendered site. +categories: [] +keywords: [] +aliases: [/content/sections/] +--- + +## Page bundles + +Hugo supports page-relative images and other resources packaged into `Page Bundles`. + +These terms are connected, and you also need to read about [page resources][] and [image processing][] to get the full picture. + +```tree +content/ +├── blog/ +│ ├── hugo-is-cool/ +│ │ ├── images/ +│ │ │ ├── funnier-cat.jpg +│ │ │ └── funny-cat.jpg +│ │ ├── cats-info.md +│ │ └── index.md +│ ├── posts/ +│ │ ├── post1.md +│ │ └── post2.md +│ ├── 1-landscape.jpg +│ ├── 2-sunset.jpg +│ ├── _index.md +│ ├── content-1.md +│ └── content-2.md +├── 1-logo.png +└── _index.md +``` + +The file tree above shows three bundles. Note that the home page bundle cannot contain other content pages, although other files (images etc.) are allowed. + +## Organization of content source + +In Hugo, your content should be organized in a manner that reflects the rendered website. + +While Hugo supports content nested at any level, the top levels (i.e. `content/`) are special in Hugo and are considered the content type used to determine layouts etc. To read more about sections, including how to nest them, see [sections][]. + +Without any additional configuration, the following will automatically work: + +```txt +. +└── content + └── about + | └── index.md // <- https://example.org/about/ + ├── posts + | ├── firstpost.md // <- https://example.org/posts/firstpost/ + | ├── happy + | | └── ness.md // <- https://example.org/posts/happy/ness/ + | └── secondpost.md // <- https://example.org/posts/secondpost/ + └── quote + ├── first.md // <- https://example.org/quote/first/ + └── second.md // <- https://example.org/quote/second/ +``` + +## Path breakdown in Hugo + +The following demonstrates the relationships between your content organization and the output URL structure for your Hugo website when it renders. These examples assume you are [using pretty URLs][pretty], which is the default behavior for Hugo. The examples also assume a key-value of `baseURL = "https://example.org/"` in your [project configuration][]. + +### Index pages: `_index.md` + +`_index.md` has a special role in Hugo. It allows you to add front matter and content to `home`, `section`, `taxonomy`, and `term` pages. + +> [!NOTE] +> Access the content and metadata within an `_index.md` file by invoking the `GetPage` method on a `Site` or `Page` object. + +You can create one `_index.md` for your home page and one in each of your content sections, taxonomies, and terms. The following shows typical placement of an `_index.md` that would contain content and front matter for a `posts` section list page on a Hugo website: + +```txt +. url +. ⊢--^-⊣ +. path slug +. ⊢--^-⊣⊢---^---⊣ +. file path +. ⊢------^------⊣ +content/posts/_index.md +``` + +At build, this will output to the following destination with the associated values: + +```txt + + url ("/posts/") + ⊢-^-⊣ + baseurl section ("posts") +⊢--------^---------⊣⊢-^-⊣ + permalink +⊢----------^-------------⊣ +https://example.org/posts/index.html +``` + +The [sections][] can be nested as deeply as you want. The important thing to understand is that to make the section tree fully navigational, at least the lower-most section must include a content file. (i.e. `_index.md`). + +### Single pages in sections + +Single content files in each of your sections will be rendered by a [page template][]. Here is an example of a single `post` within `posts`: + +```txt + path ("posts/my-first-hugo-post.md") +. ⊢-----------^------------⊣ +. section slug +. ⊢-^-⊣⊢--------^----------⊣ +content/posts/my-first-hugo-post.md +``` + +When Hugo builds your site, the content will be output to the following destination: + +```txt + + url ("/posts/my-first-hugo-post/") + ⊢------------^----------⊣ + baseurl section slug +⊢--------^--------⊣⊢-^--⊣⊢-------^---------⊣ + permalink +⊢--------------------^---------------------⊣ +https://example.org/posts/my-first-hugo-post/index.html +``` + +## Paths explained + +The following concepts provide more insight into the relationship between your project's organization and the default Hugo behavior when building output for the website. + +### `section` + +A default content type is determined by the section in which a content item is stored. `section` is determined by the location within the project's `content` directory. `section` cannot be specified or overridden in front matter. + +### `slug` + +The `slug` is the last segment of the URL path, defined by the file name and optionally overridden by a `slug` value in front matter. See [URL management][slug] for details. + +### `path` + +A content's `path` is determined by the section's path to the file. The file `path`: + +- Is based on the path to the content's location AND +- Does not include the slug + +### `url` + +The `url` is the entire URL path, defined by the file path and optionally overridden by a `url` value in front matter. See [URL management][url] for details. + +[image processing]: /content-management/image-processing/ +[page resources]: /content-management/page-resources/ +[page template]: /templates/types/#page +[pretty]: /content-management/urls/#appearance +[project configuration]: /configuration/ +[sections]: /content-management/sections/ +[slug]: /content-management/urls/#slug +[url]: /content-management/urls/#url diff --git a/docs/content/en/content-management/page-bundles.md b/docs/content/en/content-management/page-bundles.md new file mode 100644 index 0000000..ced0758 --- /dev/null +++ b/docs/content/en/content-management/page-bundles.md @@ -0,0 +1,145 @@ +--- +title: Page bundles +description: Use page bundles to logically associate one or more resources with content. +categories: [] +keywords: [] +--- + +## Introduction + +A page bundle is a directory that encapsulates both content and associated resources. + +By way of example, this site has an `about` page and a `privacy` page: + +```tree +content/ +├── about/ +│ ├── index.md +│ └── welcome.jpg +└── privacy.md +``` + +The `about` page is a page bundle. It logically associates a resource with content by bundling them together. Resources within a page bundle are [page resources][], accessible with the [`Resources`][] method on the `Page` object. + +Page bundles are either _leaf bundles_ or _branch bundles_. + +leaf bundle +: A _leaf bundle_ is a directory that contains an `index.md` file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. + +branch bundle +: A _branch bundle_ is a directory that contains an `_index.md` file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top-level directories with or without `_index.md` files are also branch bundles. This includes the home page. + +> [!NOTE] +> In the definitions above and the examples below, the extension of the index file depends on the [content format](g). For example, use `index.md` for Markdown content, `index.html` for HTML content, `index.adoc` for AsciiDoc content, etc. + +## Comparison + +Page bundle characteristics vary by bundle type. + +| | Leaf bundle | Branch bundle | +|---------------------|--------------------------------------------------------|---------------------------------------------------------| +| Index file | `index.md` | `_index.md` | +| Example | `content/about/index.md` | `content/posts/_index.md` | +| [Page kinds](g) | `page` | `home`, `section`, `taxonomy`, or `term` | +| Template types | [single][] | [home][], [section][], [taxonomy][], or [term][] | +| Descendant pages | None | Zero or more | +| Resource location | Adjacent to the index file or in a nested subdirectory | Same as a leaf bundles, but excludes descendant bundles | +| [Resource types](g) | `page`, `image`, `video`, etc. | all but `page` | + +Files with [resource type](g) `page` include content written in Markdown, HTML, AsciiDoc, Pandoc, reStructuredText, and Emacs Org Mode. In a leaf bundle, excluding the index file, these files are only accessible as page resources. In a branch bundle, these files are only accessible as content pages. + +## Leaf bundles + +A _leaf bundle_ is a directory that contains an `index.md` file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. + +```tree +content/ +├── about +│ └── index.md +├── posts +│ ├── my-post +│ │ ├── content-1.md +│ │ ├── content-2.md +│ │ ├── image-1.jpg +│ │ ├── image-2.png +│ │ └── index.md +│ └── my-other-post +│ └── index.md +└── another-section + ├── foo.md + └── not-a-leaf-bundle + ├── bar.md + └── another-leaf-bundle + └── index.md +``` + +There are four leaf bundles in the example above: + +about +: This leaf bundle does not contain any page resources. + +my-post +: This leaf bundle contains an index file, two resources of [resource type](g) `page`, and two resources of resource type `image`. + + - content-1, content-2 + + These are resources of resource type `page`, accessible via the [`Resources`][] method on the `Page` object. Hugo will not render these as individual pages. + + - image-1, image-2 + + These are resources of resource type `image`, accessible via the `Resources` method on the `Page` object + +my-other-post +: This leaf bundle does not contain any page resources. + +another-leaf-bundle +: This leaf bundle does not contain any page resources. + +> [!NOTE] +> Create leaf bundles at any depth within the `content` directory, but a leaf bundle may not contain another bundle. Leaf bundles do not have descendants. + +## Branch bundles + +A _branch bundle_ is a directory that contains an `_index.md` file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top-level directories with or without `_index.md` files are also branch bundles. This includes the home page. + +```tree +content/ +├── branch-bundle-1/ +│ ├── _index.md +│ ├── content-1.md +│ ├── content-2.md +│ ├── image-1.jpg +│ └── image-2.png +├── branch-bundle-2/ +│ ├── a-leaf-bundle/ +│ │ └── index.md +│ └── _index.md +└── _index.md +``` + +There are three branch bundles in the example above: + +home page +: This branch bundle contains an index file, two descendant branch bundles, and no resources. + +branch-bundle-1 +: This branch bundle contains an index file, two resources of [resource type](g) `page`, and two resources of resource type `image`. + +branch-bundle-2 +: This branch bundle contains an index file and a leaf bundle. + +> [!NOTE] +> Create branch bundles at any depth within the `content` directory. Branch bundles may have descendants. + +## Headless bundles + +Use [build options][] in front matter to create an unpublished leaf or branch bundle whose content and resources you can include in other pages. + +[`Resources`]: /methods/page/resources/ +[build options]: /content-management/build-options/ +[home]: /templates/types/#home +[page resources]: /content-management/page-resources/ +[section]: /templates/types/#section +[single]: /templates/types/#single +[taxonomy]: /templates/types/#taxonomy +[term]: /templates/types/#term diff --git a/docs/content/en/content-management/page-resources.md b/docs/content/en/content-management/page-resources.md new file mode 100644 index 0000000..bef99ec --- /dev/null +++ b/docs/content/en/content-management/page-resources.md @@ -0,0 +1,273 @@ +--- +title: Page resources +description: Use page resources to logically associate assets with a page. +categories: [] +keywords: [] +--- + +Page resources are only accessible from [page bundles][], those directories with `index.md` or`_index.md` files at their root. Page resources are only available to the page with which they are bundled. + +In this example, `first-post` is a page bundle with access to 10 page resources including audio, data, documents, images, and video. Although `second-post` is also a page bundle, it has no page resources and is unable to directly access the page resources associated with `first-post`. + +```tree +content +└── post + ├── first-post + │ ├── images + │ │ ├── a.jpg + │ │ ├── b.jpg + │ │ └── c.jpg + │ ├── index.md (root of page bundle) + │ ├── latest.html + │ ├── manual.json + │ ├── notice.md + │ ├── office.mp3 + │ ├── pocket.mp4 + │ ├── rating.pdf + │ └── safety.txt + └── second-post + └── index.md (root of page bundle) +``` + +## Examples + +Use any of these methods on a `Page` object to capture page resources: + +- [`Resources.ByType`][] +- [`Resources.Get`][] +- [`Resources.GetMatch`][] +- [`Resources.Match`][] + + Once you have captured a resource, use any of the applicable [`Resource`][] methods to return a value or perform an action. + +The following examples assume this content structure: + +```tree +content/ +└── example/ + ├── data/ + │ └── books.json <-- page resource + ├── images/ + │ ├── a.jpg <-- page resource + │ └── b.jpg <-- page resource + ├── snippets/ + │ └── text.md <-- page resource + └── index.md +``` + +Render a single image, and throw an error if the file does not exist: + +```go-html-template +{{ $path := "images/a.jpg" }} +{{ with .Resources.Get $path }} + +{{ else }} + {{ errorf "Unable to get page resource %q" $path }} +{{ end }} +``` + +Render all images, resized to 300 px wide: + +```go-html-template +{{ range .Resources.ByType "image" }} + {{ with .Resize "300x" }} + + {{ end }} +{{ end }} +``` + +Render the markdown snippet: + +```go-html-template +{{ with .Resources.Get "snippets/text.md" }} + {{ .Content }} +{{ end }} +``` + +List the titles in the data file, and throw an error if the file does not exist. + +```go-html-template +{{ $path := "data/books.json" }} +{{ with .Resources.Get $path }} + {{ with . | transform.Unmarshal }} +

    Books:

    +
      + {{ range . }} +
    • {{ .title }}
    • + {{ end }} +
    + {{ end }} +{{ else }} + {{ errorf "Unable to get page resource %q" $path }} +{{ end }} +``` + +## Metadata + +The page resources' metadata is managed from the corresponding page's front matter with an array parameter named `resources`. + +> [!NOTE] +> Resources of type `page` get `Title` etc. from their own front matter. + +`src` +: (`string`) Required. A [glob pattern](g) matching one or more page resources by file path, relative to the page bundle. Matching is case-insensitive. When the pattern matches multiple resources, the same metadata is applied to each. + +`name` +: (`string`) Sets the value returned by [`Name`][]. Supports the [`:counter`](#the-counter-placeholder-in-name-and-title) placeholder. After assignment, use `name`, not the original file path, with [`Resources.Get`][], [`Resources.Match`][], and [`Resources.GetMatch`][]. + +`title` +: (`string`) Sets the value returned by [`Title`][]. Supports the [`:counter`](#the-counter-placeholder-in-name-and-title) placeholder. + +`params` +: (`map`) A map of custom key-value pairs. When multiple array entries match the same resource, their `params` maps are merged; later entries take precedence for duplicate keys. + +### Resources metadata example + + +{{< code-toggle file=content/example.md fm=true >}} +title: Application +date: 2018-01-25 +resources: + - src: images/sunset.jpg + name: header + - src: documents/photo_specs.pdf + title: Photo Specifications + - src: documents/guide.pdf + title: Instruction Guide + - src: documents/checklist.pdf + title: Document Checklist + - src: documents/payment.docx + title: Proof of Payment + - src: "**.pdf" + name: pdf-file-:counter + params: + icon: pdf + - src: "**.docx" + params: + icon: word +{{}} + + +From the example above: + +- `sunset.jpg` will receive a new `Name` and can now be found with `.GetMatch "header"`. +- `documents/photo_specs.pdf`, `documents/guide.pdf`, `documents/checklist.pdf`, and `documents/payment.docx` will get `Title` as set by `title`. +- All `PDF` files will get the `pdf` icon and a new `Name`. The `name` parameter contains a special placeholder [`:counter`](#the-counter-placeholder-in-name-and-title), so the `Name` will be `pdf-file-1`, `pdf-file-2`, `pdf-file-3`. +- All `.docx` files will get the `word` icon. + +> [!NOTE] +> For `name` and `title`, the first matching array entry wins; later matches are ignored. For `params`, all matching entries contribute; later entries take precedence for duplicate keys. Place more specific `src` patterns before broader wildcards to control which `name` and `title` values are applied. + +### The `:counter` placeholder in `name` and `title` + +The `:counter` is a special placeholder recognized in `name` and `title` parameters `resources`. + +Each unique `src` pattern maintains independent counters for `name` and `title`, each starting at 1 with the first matching resource. + +For example, if a bundle has the resources `photo_specs.pdf`, `other_specs.pdf`, `guide.pdf` and `checklist.pdf`, and the front matter has specified the `resources` as: + +{{< code-toggle file=content/inspections/engine/index.md fm=true >}} +title = 'Engine inspections' +[[resources]] + src = '*specs.pdf' + title = 'Specification #:counter' +[[resources]] + src = '**.pdf' + name = 'pdf-file-:counter.pdf' +{{}} + +the `Name` and `Title` will be assigned to the resource files as follows: + +| Resource file | `Name` | `Title` | +|------------------|--------------------|----------------------| +| checklist.pdf | `"pdf-file-1.pdf"` | `"checklist.pdf"` | +| guide.pdf | `"pdf-file-2.pdf"` | `"guide.pdf"` | +| other\_specs.pdf | `"pdf-file-3.pdf"` | `"Specification #1"` | +| photo\_specs.pdf | `"pdf-file-4.pdf"` | `"Specification #2"` | + +## Multilingual + +By default, with a multilingual single-host project, Hugo does not duplicate shared page during the build. + +> [!NOTE] +> This behavior is limited to Markdown content. Shared page resources for other [content formats][] are copied into each language bundle. + +Consider this project configuration: + +{{< code-toggle file=hugo >}} +defaultContentLanguage = 'de' +defaultContentLanguageInSubdir = true + +[languages.de] +label = 'Deutsch' +locale = 'de-DE' +weight = 1 + +[languages.en] +label = 'English' +locale = 'en-US' +weight = 2 +{{< /code-toggle >}} + +And this content: + +```tree +content/ +└── my-bundle/ + ├── a.jpg <-- shared page resource + ├── b.jpg <-- shared page resource + ├── c.de.jpg + ├── c.en.jpg + ├── index.de.md + └── index.en.md +``` + +Hugo places the shared resources in the page bundle for the default content language: + +```tree +public/ +├── de/ +│ ├── my-bundle/ +│ │ ├── a.jpg <-- shared page resource +│ │ ├── b.jpg <-- shared page resource +│ │ ├── c.de.jpg +│ │ └── index.html +│ └── index.html +├── en/ +│ ├── my-bundle/ +│ │ ├── c.en.jpg +│ │ └── index.html +│ └── index.html +└── index.html +``` + +This approach reduces build times, storage requirements, bandwidth consumption, and deployment times, ultimately reducing cost. + +> [!IMPORTANT] +> To resolve Markdown link and image destinations to the correct location, you must use link and image render hooks that capture the page resource with the [`Resources.Get`][] method, and then invoke its [`RelPermalink`][] method. +> +> In its default configuration, Hugo automatically uses the [embedded link render hook][] and the [embedded image render hook][] for multilingual single-host projects, specifically when the [duplication of shared page resources][] feature is disabled. This is the default behavior for such projects. If custom link or image render hooks are defined by your project, modules, or themes, these will be used instead. +> +> You can also configure Hugo to `always` use the embedded link or image render hook, use it only as a `fallback`, or `never` use it. See [details][]. + +Although duplicating shared page resources is inefficient, you can enable this feature in your project configuration if desired: + +{{< code-toggle file=hugo >}} +[markup.goldmark] +duplicateResourceFiles = true +{{< /code-toggle >}} + +[`Name`]: /methods/resource/name/ +[`RelPermalink`]: /methods/resource/relpermalink/ +[`Resource`]: /methods/resource/ +[`Resources.ByType`]: /methods/page/resources#bytype +[`Resources.GetMatch`]: /methods/page/resources#getmatch +[`Resources.Get`]: /methods/page/resources/#get +[`Resources.Match`]: /methods/page/resources#match +[`Title`]: /methods/resource/title/ +[content formats]: /content-management/formats/ +[details]: /configuration/markup/#renderhookslinkuseembedded +[duplication of shared page resources]: /configuration/markup/#duplicateresourcefiles +[embedded image render hook]: /render-hooks/images/#embedded +[embedded link render hook]: /render-hooks/links/#embedded +[page bundles]: /content-management/page-bundles/ diff --git a/docs/content/en/content-management/related-content.md b/docs/content/en/content-management/related-content.md new file mode 100644 index 0000000..3b132a6 --- /dev/null +++ b/docs/content/en/content-management/related-content.md @@ -0,0 +1,104 @@ +--- +title: Related content +description: List related content in "See Also" sections. +categories: [] +keywords: [] +aliases: [/content/related/,/related/,/content-management/related/] +--- + +Hugo uses a set of factors to identify a page's related content based on front matter parameters. This can be tuned to the desired set of indices and parameters. + +## List related content + +To list up to 5 related pages (which share the same _date_ or _keyword_ parameters) is as simple as including something similar to this partial in your template: + +```go-html-template {file="layouts/_partials/related.html" copy=true} +{{ with site.RegularPages.Related . | first 5 }} +

    Related content:

    + +{{ end }} +``` + +The `Related` method takes one argument which may be a `Page` or an options map. The options map has these options: + +`indices` +: (`slice`) The indices to search within. + +`document` +: (`page`) The page for which to find related content. Required when specifying an options map. + +`namedSlices` +: (`slice`) The keywords to search for, expressed as a slice of `KeyValues` using the [`keyVals`][] function. + +`fragments` +: (`slice`) A list of special keywords that is used for indices configured as type "fragments". This will match the [fragment](g) identifiers of the documents. + +A fictional example using all of the above options: + +```go-html-template +{{ $page := . }} +{{ $opts := dict + "indices" (slice "tags" "keywords") + "document" $page + "namedSlices" (slice (keyVals "tags" "hugo" "rocks") (keyVals "date" $page.Date)) + "fragments" (slice "heading-1" "heading-2") +}} +``` + +> [!NOTE] +> We improved and simplified this feature in Hugo 0.111.0. Before this we had 3 different methods: `Related`, `RelatedTo` and `RelatedIndices`. Now we have only one method: `Related`. The old methods are still available but deprecated. Also see [this blog article][] for a great explanation of more advanced usage of this feature. + +## Index content headings + +Hugo can index the headings in your content and use this to find related content. You can enable this by adding a index of type `fragments` to your `related` configuration: + +{{< code-toggle file=hugo >}} +[related] +threshold = 20 +includeNewer = true +toLower = false +[[related.indices]] +name = 'fragmentrefs' +type = 'fragments' +applyFilter = true +weight = 80 +{{< /code-toggle >}} + +- The `name` maps to a optional front matter slice attribute that can be used to link from the page level down to the fragment/heading level. +- If `applyFilter` is enabled, the `.HeadingsFiltered` on each page in the result will reflect the filtered headings. This is useful if you want to show the headings in the related content listing: + +```go-html-template +{{ $related := .Site.RegularPages.Related . | first 5 }} +{{ with $related }} +

    See Also

    +
      + {{ range $i, $p := . }} +
    • + {{ .LinkTitle }} + {{ with .HeadingsFiltered }} +
        + {{ range . }} + {{ $link := printf "%s#%s" $p.RelPermalink .ID | safeURL }} +
      • + {{ .Title }} +
      • + {{ end }} +
      + {{ end }} +
    • + {{ end }} +
    +{{ end }} +``` + +## Configuration + +See [configure related content][]. + +[`keyVals`]: /functions/collections/keyvals/ +[configure related content]: /configuration/related-content/ +[this blog article]: https://regisphilibert.com/blog/2018/04/hugo-optmized-relashionships-with-related-content/ diff --git a/docs/content/en/content-management/sections.md b/docs/content/en/content-management/sections.md new file mode 100644 index 0000000..f4194ea --- /dev/null +++ b/docs/content/en/content-management/sections.md @@ -0,0 +1,139 @@ +--- +title: Sections +description: Organize content into sections. + +categories: [] +keywords: [] +aliases: [/content/sections/] +--- + +## Overview + +{{% glossary-term "section" %}} + +```tree +content/ +├── articles/ <-- section (top-level directory) +│ ├── 2022/ +│ │ ├── article-1/ +│ │ │ ├── cover.jpg +│ │ │ └── index.md +│ │ └── article-2.md +│ └── 2023/ +│ ├── article-3.md +│ └── article-4.md +├── products/ <-- section (top-level directory) +│ ├── product-1/ <-- section (has _index.md file) +│ │ ├── benefits/ <-- section (has _index.md file) +│ │ │ ├── _index.md +│ │ │ ├── benefit-1.md +│ │ │ └── benefit-2.md +│ │ ├── features/ <-- section (has _index.md file) +│ │ │ ├── _index.md +│ │ │ ├── feature-1.md +│ │ │ └── feature-2.md +│ │ └── _index.md +│ └── product-2/ <-- section (has _index.md file) +│ ├── benefits/ <-- section (has _index.md file) +│ │ ├── _index.md +│ │ ├── benefit-1.md +│ │ └── benefit-2.md +│ ├── features/ <-- section (has _index.md file) +│ │ ├── _index.md +│ │ ├── feature-1.md +│ │ └── feature-2.md +│ └── _index.md +├── _index.md +└── about.md +``` + +The example above has two top-level sections: articles and products. None of the directories under articles are sections, while all of the directories under products are sections. A section within a section is a known as a nested section or subsection. + +## Explanation + +Sections and non-sections behave differently. + + |Sections|Non-sections +:--|:-:|:-: +Directory names become URL segments|:heavy_check_mark:|:heavy_check_mark: +Have logical ancestors and descendants|:heavy_check_mark:|:x: +Have list pages|:heavy_check_mark:|:x: + +With the file structure from the [example above](#overview): + +1. The list page for the articles section includes all articles, regardless of directory structure; none of the subdirectories are sections. +1. The articles/2022 and articles/2023 directories do not have list pages; they are not sections. +1. The list page for the products section, by default, includes product-1 and product-2, but not their descendant pages. To include descendant pages, use the `RegularPagesRecursive` method instead of the `Pages` method in the _section_ template. +1. All directories in the products section have list pages; each directory is a section. + +## Template selection + +Hugo has a defined [lookup order][] to determine which template to use when rendering a page. The [lookup rules][] consider the top-level section name; subsection names are not considered when selecting a template. + +With the file structure from the [example above](#overview): + +Content directory|Section template +:--|:-- +`content/products`|`layouts/products/section.html` +`content/products/product-1`|`layouts/products/section.html` +`content/products/product-1/benefits`|`layouts/products/section.html` + +Content directory|Page template +:--|:-- +`content/products`|`layouts/products/page.html` +`content/products/product-1`|`layouts/products/page.html` +`content/products/product-1/benefits`|`layouts/products/page.html` + +If you need to use a different template for a subsection, specify `type` and/or `layout` in front matter. + +## Ancestors and descendants + +A section has one or more ancestors (including the home page), and zero or more descendants. With the file structure from the [example above](#overview): + +```text +content/products/product-1/benefits/benefit-1.md +``` + +The content file (benefit-1.md) has four ancestors: benefits, product-1, products, and the home page. This logical relationship allows us to use the `.Parent` and `.Ancestors` methods to traverse the site structure. + +For example, use the `.Ancestors` method to render breadcrumb navigation. + +```go-html-template {file="layouts/_partials/breadcrumb.html"} + +``` + +With this CSS: + +```css +.breadcrumb ol { + padding-left: 0; +} + +.breadcrumb li { + display: inline; +} + +.breadcrumb li:not(:last-child)::after { + content: "»"; +} +``` + +Hugo renders this, where each breadcrumb is a link to the corresponding page: + +```text +Home » Products » Product 1 » Benefits » Benefit 1 +``` + +[lookup order]: /templates/lookup-order/ +[lookup rules]: /templates/lookup-order/#lookup-rules diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md new file mode 100644 index 0000000..613e24f --- /dev/null +++ b/docs/content/en/content-management/shortcodes.md @@ -0,0 +1,232 @@ +--- +title: Shortcodes +description: Use embedded, custom, or inline shortcodes to insert elements such as videos, images, and social media embeds into your content. +categories: [] +keywords: [] +aliases: [/extras/shortcodes/] +--- + +## Introduction + +{{% glossary-term shortcode %}} + +There are three types of shortcodes: embedded, custom, and inline. + +## Embedded + +Hugo's embedded shortcodes are pre-defined templates within the application. Refer to each shortcode's documentation for specific usage instructions and available arguments. + +{{% render-list-of-pages-in-section path=/shortcodes %}} + +## Custom + +Create custom shortcodes to simplify and standardize content creation. For example, the following _shortcode_ template generates an audio player using a [global resource](g): + +```go-html-template {file="layouts/_shortcodes/audio.html"} +{{ with resources.Get (.Get "src") }} + +{{ end }} +``` + +Then call the shortcode from within markup: + +```md {file="content/example.md"} +{{}} +``` + +Learn more about creating shortcodes in the [shortcode templates][] section. + +## Inline + +An inline shortcode is a _shortcode_ template defined within content. + +Hugo's security model is based on the premise that template and configuration authors are trusted, but content authors are not. This model enables generation of HTML output safe against code injection. + +To conform with this security model, creating _shortcode_ templates within content is disabled by default. If you trust your content authors, you can enable this functionality in your project configuration: + +{{< code-toggle file=hugo >}} +[security] +enableInlineShortcodes = true +{{< /code-toggle >}} + +For more information see [configure security][]. + +The following example demonstrates an inline shortcode, `date.inline`, that accepts a single positional argument: a date/time [layout string][]. + +```md {file="content/example.md"} +Today is +{{}} + {{- now | time.Format (.Get 0) -}} +{{}}. + +Today is {{}}. +``` + +In the example above, the inline shortcode is executed twice: once upon definition and again when subsequently called. Hugo renders this to: + +```html +

    Today is Jan 30, 2025.

    +

    Today is Thursday, January 30, 2025

    +``` + +Inline shortcodes process their inner content within the same context as regular _shortcode_ templates, allowing you to use any available [shortcode method][]. + +> [!NOTE] +> You cannot [nest](#nesting) inline shortcodes. + +Learn more about creating shortcodes in the [shortcode templates][] section. + +## Calling + +Shortcode calls involve three syntactical elements: tags, arguments, and notation. + +### Tags + +Some shortcodes expect content between opening and closing tags. For example, the embedded [`details`][] shortcode requires an opening and closing tag: + +```md +{{}} +This is a **bold** word. +{{}} +``` + +Some shortcodes do not accept content. For example, the embedded [`instagram`][] shortcode requires a single _positional_ argument: + +```md +{{}} +``` + +Some shortcodes optionally accept content. For example, you can call the embedded [`qr`][] shortcode with content: + +```md +{{}} +https://gohugo.io +{{}} +``` + +Or use the self-closing syntax with a trailing slash to pass the text as an argument: + +```md +{{}} +``` + +Refer to each shortcode's documentation for specific usage instructions and available arguments. + +### Arguments + +Shortcode arguments can be either _named_ or _positional_. + +Named arguments are passed as case-sensitive key-value pairs, as seen in this example with the embedded [`figure`][] shortcode. The `src` argument, for instance, is required. + +```md +{{}} +``` + +Positional arguments, on the other hand, are determined by their position. The embedded `instagram` shortcode, for example, expects the first argument to be the Instagram post ID. + +```md +{{}} +``` + +Shortcode arguments are space-delimited, and arguments with internal spaces must be quoted. + +```md +{{}} +``` + +Shortcodes accept [scalar](g) arguments, one of [string](g), [integer](g), [floating point](g), or [boolean](g). + +```md +{{}} +``` + +You can optionally use multiple lines when providing several arguments to a shortcode for better readability: + +```md +{{}} +``` + +Use a [raw string literal](g) if you need to pass a multiline string: + +```md +{{HTML
    , +and a new line with a "quoted string".` */>}} +``` + +Shortcodes can accept named arguments, positional arguments, or both, but you must use either named or positional arguments exclusively within a single shortcode call; mixing them is not allowed. + +Refer to each shortcode's documentation for specific usage instructions and available arguments. + +### Notation + +Shortcodes can be called using two different notations, distinguished by their tag delimiters. + +Notation|Example +:--|:-- +Markdown|`{{%/* foo */%}} ## Section 1 {{%/* /foo */%}}` +Standard|`{{}} ## Section 2 {{}}` + +#### Markdown notation + +Hugo processes the shortcode before the page content is rendered by the Markdown renderer. This means, for instance, that Markdown headings inside a Markdown-notation shortcode will be included when invoking the [`TableOfContents`][] method on the `Page` object. + +#### Standard notation + +With standard notation, Hugo processes the shortcode separately, merging the output into the page content after Markdown rendering. This means, for instance, that Markdown headings inside a standard-notation shortcode will be excluded when invoking the `TableOfContents` method on the `Page` object. + +By way of example, with this _shortcode_ template: + +```go-html-template {file="layouts/_shortcodes/foo.html"} +{{ .Inner }} +``` + +And this markdown: + +```md {file="content/example.md"} +{{%/* foo */%}} ## Section 1 {{%/* /foo */%}} + +{{}} ## Section 2 {{}} +``` + +Hugo renders this HTML: + +```html +

    Section 1

    + +## Section 2 +``` + +In the above, "Section 1" will be included when invoking the `TableOfContents` method, while "Section 2" will not. + +> [!NOTE] +> The shortcode author determines which notation to use. Consult each shortcode's documentation for specific usage instructions and available arguments. + +## Nesting + +Shortcodes (excluding [inline](#inline) shortcodes) can be nested, creating parent-child relationships. For example, a gallery shortcode might contain several image shortcodes: + +```md {file="content/example.md"} +{{}} + {{}} + {{}} + {{}} +{{}} +``` + +The [shortcode templates][nesting] section provides a detailed explanation and examples. + +[`TableOfContents`]: /methods/page/tableofcontents/ +[`details`]: /shortcodes/details/ +[`figure`]: /shortcodes/figure/ +[`instagram`]: /shortcodes/instagram/ +[`qr`]: /shortcodes/qr/ +[configure security]: /configuration/security/ +[layout string]: /functions/time/format/#layout-string +[nesting]: /templates/shortcode/#nesting +[shortcode method]: /templates/shortcode/#methods +[shortcode templates]: /templates/shortcode/ diff --git a/docs/content/en/content-management/summaries.md b/docs/content/en/content-management/summaries.md new file mode 100644 index 0000000..1534835 --- /dev/null +++ b/docs/content/en/content-management/summaries.md @@ -0,0 +1,153 @@ +--- +title: Content summaries +linkTitle: Summaries +description: Create and render content summaries. +categories: [] +keywords: [] +aliases: [/content/summaries/,/content-management/content-summaries/] +--- + + + + + + +You can define a summary manually, in front matter, or automatically. A manual summary takes precedence over a front matter summary, and a front matter summary takes precedence over an automatic summary. + +Review the [comparison table](#comparison) below to understand the characteristics of each summary type. + +## Manual summary + +Use a `` divider to indicate the end of the summary. Hugo will not render the summary divider itself. + +```md {file="content/example.md"} ++++ +title: 'Example' +date: 2024-05-26T09:10:33-07:00 ++++ + +This is the first paragraph. + + + +This is the second paragraph. +``` + +> [!NOTE] +> Place the summary divider on its own line. Do not place it inline with other content. + +Correct placement: + +```md {file="content/example.md"} +--- +title: 'Example' +--- + +This is an example of **strong text** in a sentence. This is another sentence. + + + +This is another paragraph. +``` + +Incorrect placement: + +```md {file="content/example.md"} +--- +title: 'Example' +--- + +This is an example of **strong text** in a sentence. This is another sentence. + +This is another paragraph. +``` + +When using the Emacs Org Mode [content format][], use a `# more` divider to indicate the end of the summary. + +## Front matter summary + +Use front matter to define a summary independent of content. + +```md {file="content/example.md"} ++++ +title: 'Example' +date: 2024-05-26T09:10:33-07:00 +summary: 'This summary is independent of the content.' ++++ + +This is the first paragraph. + +This is the second paragraph. +``` + +## Automatic summary + +If you do not define the summary manually or in front matter, Hugo automatically defines the summary based on the [`summaryLength`][] in your project configuration. + +```md {file="content/example.md"} ++++ +title: 'Example' +date: 2024-05-26T09:10:33-07:00 ++++ + +This is the first paragraph. + +This is the second paragraph. + +This is the third paragraph. +``` + +For example, with a `summaryLength` of 7, the automatic summary will be: + +```html +

    This is the first paragraph.

    +

    This is the second paragraph.

    +``` + +> [!WARNING] +> Automatic `.Summary` may cut block tags (e.g., `blockquote`) in the middle when `summaryLength` is reached, causing the browser to recover the end tag (the end tag will be inserted before the parent's end tag), resulting in unexpected rendering behavior. To avoid this, wrap `.Summary` in a `
    `; alternatively, wrap it together with the heading tag using `
    `. You can avoid this entirely by using a manual summary. See issue [#14044][] for details. + +## Comparison + +Each summary type has different characteristics: + +Type|Precedence|Renders markdown|Renders shortcodes|Wraps single lines with `

    ` +:--|:-:|:-:|:-:|:-: +Manual|1|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark: +Front matter|2|:heavy_check_mark:|:x:|:x: +Automatic|3|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark: + +## Rendering + +Render the summary in a template by calling the [`Summary`][] method on a `Page` object. + +```go-html-template +{{ range site.RegularPages }} +

    {{ .LinkTitle }}

    +
    + {{ .Summary }} + {{ if .Truncated }} + More ... + {{ end }} +
    +{{ end }} +``` + +## Alternative + +Instead of calling the `Summary` method on a `Page` object, use the [`strings.Truncate`][] function for granular control of the summary length. For example: + +```go-html-template +{{ range site.RegularPages }} +

    {{ .LinkTitle }}

    +
    + {{ .Content | strings.Truncate 42 }} +
    +{{ end }} +``` + +[#14044]: https://github.com/gohugoio/hugo/issues/14044 +[`Summary`]: /methods/page/summary/ +[`strings.Truncate`]: /functions/strings/truncate/ +[`summaryLength`]: /configuration/all/#summarylength +[content format]: /content-management/formats/ diff --git a/docs/content/en/content-management/syntax-highlighting.md b/docs/content/en/content-management/syntax-highlighting.md new file mode 100644 index 0000000..32abb77 --- /dev/null +++ b/docs/content/en/content-management/syntax-highlighting.md @@ -0,0 +1,101 @@ +--- +title: Syntax highlighting +description: Add syntax highlighting to code examples. +categories: [] +keywords: [highlight] +aliases: [/extras/highlighting/,/extras/highlight/,/tools/syntax-highlighting/] +--- + +Hugo provides several methods to add syntax highlighting to code examples: + +- Use the [`transform.Highlight`][] function within your templates +- Use the [`highlight`][] shortcode with any [content format](g) +- Use fenced code blocks with the Markdown content format + +## Fenced code blocks + +In its default configuration, Hugo highlights code examples within fenced code blocks, following this form: + +````md {file="content/example.md"} +```LANG [OPTIONS] +CODE +``` +```` + +`CODE` +: The code to highlight. + +`LANG` +: The language of the code to highlight. Choose from one of the [supported languages](#languages). This value is case-insensitive. If omitted or unsupported, Hugo renders the text as a plain text block without syntax highlighting. Consistent with the [CommonMark][] specification, fenced code blocks require a known language identifier to trigger semantic syntax highlighting. + +`OPTIONS` +: One or more space-separated or comma-separated key-value pairs wrapped in braces. Set default values for each option in your [project configuration][]. The key names are case-insensitive. + +For example, with this Markdown: + +````md {file="content/example.md"} +```go {linenos=inline hl_lines=[3,"6-8"] style=emacs} +package main + +import "fmt" + +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +``` +```` + +Hugo renders this: + +```go {linenos=inline, hl_lines=[3, "6-8"], style=emacs} +package main + +import "fmt" + +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +``` + +## Options + +{{% include "_common/syntax-highlighting-options.md" %}} + +## Escaping + +When documenting shortcode usage, escape the tag delimiters: + +````md {file="content/example.md"} +```text {linenos=inline} +{{}} + +{{%/*/* shortcode-2 */*/%}} +``` +```` + +Hugo renders this to: + +```text {linenos=inline} +{{}} + +{{%/* shortcode-2 */%}} +``` + +## Languages + +These are the supported languages. Use one of the identifiers, not the language name, when specifying a language for: + +- The [`transform.Highlight`][] function +- The [`highlight`][] shortcode +- Fenced code blocks + +{{< chroma-lexers >}} + +[CommonMark]: https://spec.commonmark.org/current/#indented-code-blocks +[`highlight`]: /shortcodes/highlight/ +[`transform.Highlight`]: /functions/transform/highlight/ +[project configuration]: /configuration/markup/#highlight diff --git a/docs/content/en/content-management/taxonomies.md b/docs/content/en/content-management/taxonomies.md new file mode 100644 index 0000000..be26fb0 --- /dev/null +++ b/docs/content/en/content-management/taxonomies.md @@ -0,0 +1,179 @@ +--- +title: Taxonomies +description: Hugo includes support for user-defined taxonomies. +categories: [] +keywords: [] +aliases: [/taxonomies/overview/,/taxonomies/usage/,/indexes/overview/,/doc/indexes/,/extras/indexes] +--- + +## What is a taxonomy? + +Hugo includes support for user-defined groupings of content called **taxonomies**. Taxonomies are classifications of logical relationships between content. + +### Definitions + +Taxonomy +: A categorization that can be used to classify content + +Term +: A key within the taxonomy + +Value +: A piece of content assigned to a term + +## Example taxonomy: movie website + +Let's assume you are making a website about movies. You may want to include the following taxonomies: + +- Actors +- Directors +- Studios +- Genre +- Year +- Awards + +Then, in each of the movies, you would specify terms for each of these taxonomies (i.e., in the front matter of each of your movie content files). From these terms, Hugo would automatically create pages for each Actor, Director, Studio, Genre, Year, and Award, with each listing all of the Movies that matched that specific Actor, Director, Studio, Genre, Year, and Award. + +### Movie taxonomy organization + +To continue with the example of a movie site, the following demonstrates content relationships from the perspective of the taxonomy: + +```txt +Actor <- Taxonomy + Bruce Willis <- Term + The Sixth Sense <- Value + Unbreakable <- Value + Moonrise Kingdom <- Value + Samuel L. Jackson <- Term + Unbreakable <- Value + The Avengers <- Value + xXx <- Value +``` + +From the perspective of the content, the relationships would appear differently, although the data and labels used are the same: + +```txt +Unbreakable <- Value + Actors <- Taxonomy + Bruce Willis <- Term + Samuel L. Jackson <- Term + Director <- Taxonomy + M. Night Shyamalan <- Term + ... +Moonrise Kingdom <- Value + Actors <- Taxonomy + Bruce Willis <- Term + Bill Murray <- Term + Director <- Taxonomy + Wes Anderson <- Term + ... +``` + +### Default destinations + +When taxonomies are used Hugo will automatically create both a page listing all the taxonomy's terms and individual pages with lists of content associated with each term. For example, a `categories` taxonomy declared in your configuration and used in your content front matter will create the following pages: + +- A single page at `example.com/categories/` that lists all the terms within the taxonomy +- Individual taxonomy list pages (e.g., `/categories/development/`) for each of the terms that shows a listing of all pages marked as part of that taxonomy within any content file's front matter + +## Configuration + +See [configure taxonomies][]. + +## Assign terms to content + +To assign one or more terms to a page, create a front matter field using the plural name of the taxonomy, then add terms to the corresponding array. For example: + +{{< code-toggle file=content/example.md fm=true >}} +title = 'Example' +tags = ['Tag A','Tag B'] +categories = ['Category A','Category B'] +{{< /code-toggle >}} + +## Taxonomic weight + +{{% glossary-term "taxonomic weight" %}} + +Assign a taxonomic weight using a front matter key named `[taxonomy_name]_weight`. + +{{< code-toggle file="content/courses/organic-chemistry.md" fm=true >}} +title = 'Organic Chemistry' +weight = 10 +tags_weight = 1000 +tags = ['chemistry','science'] +{{}} + +With the front matter above, the `organic-chemistry` page will float towards the top of the list on section and home pages, and it will sink towards the bottom of the list on the `chemistry` and `science` term pages. + +## Metadata + +Display metadata about each term by creating a corresponding branch bundle in the `content` directory. + +For example, create an `authors` taxonomy: + +{{< code-toggle file=hugo >}} +[taxonomies] +author = 'authors' +{{< /code-toggle >}} + +Then create content with one [branch bundle](g) for each term: + +```tree +content/ +└── authors/ + ├── jsmith/ + │ ├── _index.md + │ └── portrait.jpg + └── rjones/ + ├── _index.md + └── portrait.jpg +``` + +Then add front matter to each term page: + +{{< code-toggle file=content/authors/jsmith/_index.md fm=true >}} +title = 'John Smith' +affiliation = 'University of Chicago' +{{< /code-toggle >}} + +Then create a _taxonomy_ template specific to the `authors` taxonomy: + +```go-html-template {file="layouts/authors/taxonomy.html"} +{{ define "main" }} +

    {{ .Title }}

    + {{ .Content }} + {{ range .Data.Terms.Alphabetical }} +

    {{ .Page.LinkTitle }}

    +

    Affiliation: {{ .Page.Params.Affiliation }}

    + {{ with .Page.Resources.Get "portrait.jpg" }} + {{ with .Fill "100x100" }} + portrait + {{ end }} + {{ end }} + {{ end }} +{{ end }} +``` + +In the example above we list each author including their affiliation and portrait. + +Or create a _term_ template specific to the `authors` taxonomy: + +```go-html-template {file="layouts/authors/term.html"} +{{ define "main" }} +

    {{ .Title }}

    +

    Affiliation: {{ .Params.affiliation }}

    + {{ with .Resources.Get "portrait.jpg" }} + {{ with .Fill "100x100" }} + portrait + {{ end }} + {{ end }} + {{ .Content }} + {{ range .Pages }} +

    {{ .LinkTitle }}

    + {{ end }} +{{ end }} +``` + +In the example above we display the author including their affiliation and portrait, then a list of associated content. + +[configure taxonomies]: /configuration/taxonomies/ diff --git a/docs/content/en/content-management/urls.md b/docs/content/en/content-management/urls.md new file mode 100644 index 0000000..a30361f --- /dev/null +++ b/docs/content/en/content-management/urls.md @@ -0,0 +1,259 @@ +--- +title: URL management +description: Control the structure and appearance of URLs through front matter entries and settings in your project configuration. +categories: [] +keywords: [] +aliases: [/extras/permalinks/,/extras/aliases/,/extras/urls/,/doc/redirects/,/doc/alias/,/doc/aliases/] +--- + +## Overview + +By default, when Hugo renders a page, the resulting URL matches the file path within the `content` directory. For example: + +```text +content/posts/post-1.md → https://example.org/posts/post-1/ +``` + +You can change the structure and appearance of URLs with front matter values and configuration settings. + +## Front matter + +### `slug` + +Set the `slug` in front matter to override the last segment of the path. This front matter field is not applicable to `home`, `section`, `taxonomy`, or `term` pages. + +{{< code-toggle file=content/posts/post-1.md fm=true >}} +title = 'My First Post' +slug = 'my-first-post' +{{< /code-toggle >}} + +The resulting URL will be: + +```text +https://example.org/posts/my-first-post/ +``` + +### `url` + +Set the `url` in front matter to override the entire path. Use this with either regular pages or section pages. + +> [!NOTE] +> Hugo does not sanitize the `url` front matter field, allowing you to generate: +> +> - File paths that contain characters reserved by the operating system. For example, file paths on Windows may not contain any of these [reserved characters][]. Hugo throws an error if a file path includes a character reserved by the current operating system. +> - URLs that contain disallowed characters. For example, the less than sign (`<`) is not allowed in a URL. + +If you set both `slug` and `url` in front matter, the `url` value takes precedence. + +#### Include a colon + +{{< new-in 0.136.0 />}} + +If you need to include a colon in the `url` front matter field, escape it with backslash characters. Use one backslash if you wrap the string within single quotes, or use two backslashes if you wrap the string within double quotes. With YAML front matter, use a single backslash if you omit quotation marks. + +For example, with this front matter: + +{{< code-toggle file=content/example.md fm=true >}} +title: Example +url: "my\\:example" +{{< /code-toggle >}} + +The resulting URL will be: + +```text +https://example.org/my:example/ +``` + +As described above, this will fail on Windows because the colon (`:`) is a reserved character. + +#### File extensions + +With this front matter: + +{{< code-toggle file=content/posts/post-1.md fm=true >}} +title = 'My First Article' +url = 'articles/my-first-article' +{{< /code-toggle >}} + +The resulting URL will be: + +```text +https://example.org/articles/my-first-article/ +``` + +If you include a file extension: + +{{< code-toggle file=content/posts/post-1.md fm=true >}} +title = 'My First Article' +url = 'articles/my-first-article.html' +{{< /code-toggle >}} + +The resulting URL will be: + +```text +https://example.org/articles/my-first-article.html +``` + +#### Leading slashes + +With monolingual projects, `url` values with or without a leading slash are relative to the [`baseURL`][]. With multilingual projects, `url` values with a leading slash are relative to the `baseURL`, and `url` values without a leading slash are relative to the `baseURL` plus the language prefix. + +Site type|Front matter `url`|Resulting URL +:--|:--|:-- +monolingual|`/about`|`https://example.org/about/` +monolingual|`about`|`https://example.org/about/` +multilingual|`/about`|`https://example.org/about/` +multilingual|`about`|`https://example.org/de/about/` + +#### Tokens + +You can also use tokens when setting the `url` value. This is typically used in `cascade` sections: + +{{< code-toggle file=content/foo/bar/_index.md fm=true >}} +title ='Bar' +[[cascade]] + url = '/:sections[last]/:slug' +{{< /code-toggle >}} + +Use any of these tokens: + +{{% include "/_common/permalink-tokens.md" %}} + +## Project configuration + +### Permalinks + +See [configure permalinks][]. + +### Appearance + +See [configure ugly URLs](/configuration/ugly-urls/). + +### Post-processing + +Hugo provides two mutually exclusive configuration settings to alter URLs _after_ it renders a page. + +#### Canonical URLs + +> [!CAUTION] +> This is a legacy configuration setting, superseded by template functions and Markdown render hooks, and will likely be [removed in a future release][]. +{class="!mt-6"} + +If enabled, Hugo performs a search and replace _after_ it renders the page. It searches for site-relative URLs (those with a leading slash) associated with `action`, `href`, `src`, `srcset`, and `url` attributes. It then prepends the `baseURL` to create absolute URLs. + +```html + + +``` + +This is an imperfect, brute force approach that can affect content as well as HTML attributes. As noted above, this is a legacy configuration setting that will likely be removed in a future release. + +To enable: + +{{< code-toggle file=hugo >}} +canonifyURLs = true +{{< /code-toggle >}} + +#### Relative URLs + +> [!CAUTION] +> Do not enable this option unless you are creating a serverless site, navigable via the file system. +{class="!mt-6"} + +If enabled, Hugo performs a search and replace _after_ it renders the page. It searches for site-relative URLs (those with a leading slash) associated with `action`, `href`, `src`, `srcset`, and `url` attributes. It then transforms the URL to be relative to the current page. + +For example, when rendering `content/posts/post-1`: + +```html + + +``` + +This is an imperfect, brute force approach that can affect content as well as HTML attributes. As noted above, do not enable this option unless you are creating a serverless site. + +To enable: + +{{< code-toggle file=hugo >}} +relativeURLs = true +{{< /code-toggle >}} + +## Aliases + +Aliases allow you to redirect old URLs to new URLs. This is essential for preventing broken links and ensuring that existing bookmarks or external links continue to function when you rename or move content. + +### Defining aliases + +To add redirects to a page, list the previous paths in the [`aliases`][aliases_field] field in your front matter. Hugo resolves these to [server-relative](g) paths during the build process, accounting for the [`baseURL`][] and [content dimension](g) prefixes such as language, version, or role. + +{{< code-toggle file=content/examples/example-1.en.md fm=true >}} +title = 'Example 1' +date = 2025-02-02 +aliases = ['/old-url', 'old-name', '../old/path'] +{{< /code-toggle >}} + +As shown in the example above, you can use [site-relative](g) paths or [page-relative](g) paths. Page-relative paths can also include directory traversal. Using the file `content/examples/example-1.en.md` as a reference point, here is how Hugo interprets those different path types: + +Path type|Alias|Server-relative path +:--|:--|:-- +site-relative|`/old-url`|`/en/old-url/` +page-relative|`old-name`|`/en/examples/old-name/` +page-relative|`../old/path`|`/en/old/path/` + +### Redirection methods + +There are two ways to implement aliases depending on your hosting environment and preferences: client-side redirection and server-side redirection. + +> [!NOTE] +> Alias data is only generated for [output formats](g) where both [`isHTML`][] and [`permalinkable`][] are `true`. This affects both the creation of client-side redirect files and the results returned by the [`Aliases`][aliases_method] method used in server-side redirection. + +#### Client-side redirection + +By default, Hugo uses client-side redirection, generating a small HTML file for every alias. This file contains a `meta http-equiv="refresh"` tag that instructs the browser to navigate to the new URL. This approach is portable across all hosting providers. + +When using this method, Hugo creates a physical directory and an `index.html` file at each alias location. For example, if a page at `content/posts/new.md` has a page-relative alias of `old-path`, a file is generated at `public/posts/old-path/index.html`. + +Unless you provide a custom layout, Hugo uses its [embedded alias template][] to generate the redirect files: + +```go-html-template + + + + {{ .Permalink }} + {{ with .OutputFormats.Canonical }}{{ end }} + + + + +``` + +To override this, create a file named `alias.html` in your `layouts` directory. This custom template has access to the following context: + +`Permalink` +: (`string`) The absolute URL of the destination page. + +`Page` +: (`page.Page`) The full `Page` object of the destination. + +#### Server-side redirection + +Alternatively, you can implement server-side redirection by using the [`Aliases`][aliases_method] method on a `Page` object to generate a single configuration file that the web server processes. This method is more efficient because the redirect happens at the HTTP header level before any page content is processed, whereas a meta refresh requires the browser to download and parse the HTML body before acting. Additionally, server-side redirection improves build and deployment times because Hugo doesn't need to write a physical directory and HTML file for every alias. + +To implement this, you typically create a single template to generate the necessary rules for your specific host or server. Common examples include: + +- A `_redirects` file for hosting services such as Cloudflare, GitLab Pages, and Netlify. +- An `.htaccess` file for web servers such as Apache and LiteSpeed. + +See the [`Aliases`][aliases_method] method page for a complete example of how to iterate through pages to generate these rules. + +If you implement server-side redirects, you should disable the generation of individual HTML files by setting [`disableAliases`][] to `true` in your project configuration. This setting only prevents the generation of the physical HTML files; the `Aliases` method on a `Page` object remains available for use in your configuration templates. + +[`baseURL`]: /configuration/all/#baseurl +[`disableAliases`]: /configuration/all/#disablealiases +[`isHTML`]: /configuration/output-formats/#ishtml +[`permalinkable`]: /configuration/output-formats/#permalinkable +[aliases_field]: /content-management/front-matter/#aliases +[aliases_method]: /methods/page/aliases/ +[configure permalinks]: /configuration/permalinks/ +[embedded alias template]: <{{% eturl alias %}}> +[removed in a future release]: https://github.com/gohugoio/hugo/issues/4733 +[reserved characters]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions diff --git a/docs/content/en/contribute/_index.md b/docs/content/en/contribute/_index.md new file mode 100644 index 0000000..d0ae954 --- /dev/null +++ b/docs/content/en/contribute/_index.md @@ -0,0 +1,9 @@ +--- +title: Contribute to the Hugo project +linkTitle: Contribute +description: Contribute to development, documentation, and themes. +categories: [] +keywords: [] +weight: 10 +aliases: [/tutorials/how-to-contribute-to-hugo/,/community/contributing/] +--- diff --git a/docs/content/en/contribute/development.md b/docs/content/en/contribute/development.md new file mode 100644 index 0000000..59dfca2 --- /dev/null +++ b/docs/content/en/contribute/development.md @@ -0,0 +1,148 @@ +--- +title: Development +description: Contribute to the development of Hugo. +categories: [] +keywords: [] +--- + +## Introduction + +You can contribute to the Hugo project by: + +- Answering questions on the [forum][] +- Improving the [documentation][] +- Monitoring the [issue queue][] +- Creating or improving [themes][] +- Squashing [bugs][] + +Please submit documentation issues and pull requests to the [documentation repository][]. + +If you have an idea for an enhancement or new feature, create a new topic on the [forum][] in the "Feature" category. This will help you to: + +- Determine if the capability already exists +- Measure interest +- Refine the concept + +If there is sufficient interest, [create a proposal][]. Do not submit a pull request until the project lead accepts the proposal. + +For a complete guide to contributing to Hugo, see the [Contribution Guide][]. + +## Prerequisites + +To build Hugo from source you must install: + +1. Install [Git][] +1. Install [Go][] version {{% current-go-version %}} or later + +## GitHub workflow + +> [!NOTE] +> This section assumes that you have a working knowledge of Go, Git and GitHub, and are comfortable working on the command line. + +Use this workflow to create and submit pull requests. + +Step 1 +: Fork the [project repository][]. + +Step 2 +: Clone your fork. + +Step 3 +: Create a new branch with a descriptive name that includes the corresponding issue number. + + For a new feature: + + ```sh + git checkout -b feat/implement-some-feature-99999 + ``` + + For a bug fix: + + ```sh + git checkout -b fix/fix-some-bug-99999 + ``` + +Step 4 +: Make changes. + +Step 5 +: Build and install. + + To build and install the standard edition: + + ```sh + CGO_ENABLED=0 go install + ``` + + {{< new-in v0.159.2 />}} To build and install the deploy edition: + + ```sh + CGO_ENABLED=0 go install -tags withdeploy + ``` + + To build and install the extended edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command: + + ```sh + CGO_ENABLED=1 go install -tags extended + ``` + + To build and install the extended/deploy edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command: + + ```sh + CGO_ENABLED=1 go install -tags extended,withdeploy + ``` + +Step 6 +: Test your changes: + + ```sh + go test ./... + ``` + +Step 7 +: Commit your changes with a descriptive commit message: + + - Provide a summary on the first line, typically 50 characters or less, followed by a blank line. + - Begin the summary with the name of the package, followed by a colon, a space, and a brief description of the change beginning with a capital letter + - Use imperative present tense + - See the [commit message guidelines][] for requirements + - Optionally, provide a detailed description where each line is 72 characters or less, followed by a blank line. + - Add one or more "Fixes" or "Closes" keywords, each on its own line, referencing the [issues][] addressed by this change. + + For example: + + ```sh + git commit -m "tpl/strings: Create wrap function + + The strings.Wrap function wraps a string into one or more lines, + splitting the string after the given number of characters, but not + splitting in the middle of a word. + + Fixes #99998 + Closes #99999" + ``` + +Step 8 +: Push the new branch to your fork of the documentation repository. + +Step 9 +: Visit the [project repository][] and create a pull request (PR). + +Step 10 +: A project maintainer will review your PR and may request changes. You may delete your branch after the maintainer merges your PR. + +[Clang]: https://clang.llvm.org/ +[Contribution Guide]: https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md +[GCC]: https://gcc.gnu.org/ +[Git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git +[Go]: https://go.dev/doc/install +[bugs]: https://github.com/gohugoio/hugo/issues?q=is%3Aopen+is%3Aissue+label%3ABug +[commit message guidelines]: https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md#git-commit-message-guidelines +[create a proposal]: https://github.com/gohugoio/hugo/issues/new?labels=Proposal%2C+NeedsTriage&template=feature_request.md +[documentation repository]: https://github.com/gohugoio/hugoDocs +[documentation]: /documentation +[forum]: https://discourse.gohugo.io +[issue queue]: https://github.com/gohugoio/hugo/issues +[issues]: https://github.com/gohugoio/hugo/issues +[project repository]: https://github.com/gohugoio/hugo/ +[themes]: https://themes.gohugo.io/ diff --git a/docs/content/en/contribute/documentation/index.md b/docs/content/en/contribute/documentation/index.md new file mode 100644 index 0000000..d79ebdb --- /dev/null +++ b/docs/content/en/contribute/documentation/index.md @@ -0,0 +1,593 @@ +--- +title: Documentation +description: Help us to improve the documentation by identifying issues and suggesting changes. +categories: [] +keywords: [] +aliases: [/contribute/docs/] +--- + +## Introduction + +We welcome corrections and improvements to the documentation. The documentation lives in a separate repository from the main project. To contribute: + +- For corrections and improvements to existing documentation, submit issues and pull requests to the [documentation repository][]. +- For documentation of new features, include the documentation changes in your pull request to the [project repository][]. + +## Guidelines + +### Style + +Follow Google's [developer documentation style guide][] where practical. + +### Markdown + +Adhere to these Markdown conventions: + +- Use [ATX][] headings (levels 2-4), not [setext][] headings. +- Use [collapsed link references][] instead of full or shortcut references. For example: + + ```md {file="content/example.md"} + This is a [link][]. + + [link]: https://example.org + ``` + +- Use [inline links][] to link to fragments on the same page. For example: + + ```md {file="content/example.md"} + This is a link to a [fragment][#fragment]. + ``` + +- Use [fenced code blocks][] instead of [indented code blocks][]. +- Use hyphens, not asterisks, for unordered [list items][]. +- Prefix each ordered list item with `1.` instead of numbering the items sequentially. +- Use [callouts](#callouts) instead of bold text for emphasis. +- Do not mix [raw HTML][] within Markdown. +- Do not use bold text in place of a heading or description term (`dt`). +- Remove consecutive blank lines. +- Remove trailing spaces. + +### Glossary + +[Glossary][] terms are defined on individual pages, providing a central repository for definitions, though these pages are not directly linked from the site. + +Definitions must be complete sentences, with the first sentence defining the term. Italicize the first occurrence of the term and any referenced glossary terms for consistency. + +Link to glossary terms using this syntax: `[term](g)` + +Term lookups are case-insensitive, ignore formatting, and support singular and plural forms. For example, all of these variations will link to the same glossary term: + +```md {file="content/example.md"} +[global resource](g) +[Global Resource](g) +[Global Resources](g) +[`Global Resources`](g) +``` + +Use the [glossary-term shortcode](#glossary-term) to insert a term definition: + +```md {file="content/example.md"} +{{%/* glossary-term "global resource" */%}} +``` + +### Terminology + +Link to the [glossary][] as needed and use terms consistently. Pay particular attention to: + +- "client side" (noun), "client-side" (adjective) +- "file name" (two words) +- "flag" (instead of "option" for command-line flags) +- "front matter" (two words, except when referring to the configuration key) +- "home page" (two words) +- "map" (instead of "dictionary") +- "Markdown" (capitalized) +- "Node.js" (first mention per page), "Node" (subsequent mentions), "node" (for the executable), "npm" (always lowercase) +- "open source" (noun), "open-source" (adjective) +- "server side" (noun), "server-side" (adjective) +- "standalone" (noun or adjective) +- "stylesheet" (one word) +- "website" (one word) + +### Template types + +When you refer to a template type, italicize it: + +```md {file="content/example.md"} +When creating a _taxonomy_ template, do this... +``` + +However, if the template type is also a link, do not italicize it to avoid distracting formatting: + +```md {file="content/example.md"} +When creating a [taxonomy][] template, do this... +``` + +Do not italicize the template type in a title, heading, or front matter description. + +### Titles and headings + +- Use sentence-style capitalization. +- Avoid formatted strings. +- Keep them concise. + +### Page descriptions + +When writing the page `description` use imperative present tense when possible. For example: + +{{< code-toggle file=content/en/functions/data/_index.md fm=true >}} +title: Data functions +linkTitle: data +description: Use these functions to read local or remote data files. +{{< /code-toggle >}} + +### Writing style + +Prefer active voice and present tense. + +No → With Hugo you can build a static site.\ +Yes → Build a static site with Hugo. + +No → This will cause Hugo to generate HTML files in the `public` directory.\ +Yes → Hugo generates HTML files in the `public` directory. + +Use second person instead of third person. + +No → Users should exercise caution when deleting files.\ +Better → You must be cautious when deleting files.\ +Best → Be cautious when deleting files. + +Minimize adverbs. + +No → Hugo is extremely fast.\ +Yes → Hugo is fast. + +> [!NOTE] +> "It's an adverb, Sam. It's a lazy tool of a weak mind." (Outbreak, 1995). + +### Function and method descriptions + +Start descriptions in the functions and methods sections with "Returns", or for booelan values, "Reports whether". + +### File paths and names + +Enclose directory names, file names, and file paths in backticks, except when used in: + +- Page titles +- Section headings (`h1`-`h6`) +- The `description` field in front matter + +### Miscellaneous + +Other best practices: + +- Introduce lists with a sentence or phrase, not directly under a heading. +- Do not use Hugo's `ref` or `relref` shortcodes. +- Prioritize current best practices over multiple options or historical information. +- Use short, focused code examples. +- Use [basic english][] where possible for a global audience. + +## Front matter fields + +This site uses the front matter fields listed in the table below. + +Of the four required fields, only `title` and `description` require data. + +{{< code-toggle file=content/example.md fm=true >}} +title: The title +description: The description +categories: [] +keywords: [] +{{< /code-toggle >}} + +If quotation marks are required, prefer single quotes to double quotes when possible. + +Field|Description|Required +:--|:--|:-- +`title`|The page title|:heavy_check_mark: +`linkTitle`|A short version of the page title|  +`description`|A complete sentence describing the page|:heavy_check_mark: +`categories`|An array of terms in the categories taxonomy|:heavy_check_mark: [^1] +`keywords`|An array of keywords used to identify related content|:heavy_check_mark: [^1] +`publishDate`|Applicable to news items: the publication date|  +`params.alt_title`|An alternate title: used in the "see also" panel if provided|  +`params.functions_and_methods.aliases`|Applicable to function and method pages: an array of alias names|  +`params.functions_and_methods.returnType`|Applicable to function and method pages: the data type returned|  +`params.functions_and_methods.signatures`|Applicable to function and method pages: an array of signatures|  +`params.hide_in_this_section`|Whether to hide the "in this section" panel|  +`params.label`|Applicable to second-level section pages such as concepts, guides, references, and tutorials: a lowercase 3-letter code to identify the section, used in the `related` partial|  +`params.minversion`|Applicable to the quick start page: the minimum Hugo version required|  +`params.permalink`|Reserved for use by the news content adapter|  +`params.reference`|Applicable to glossary entries: a URL for additional information|  +`params.searchable`|Whether to add the content of this page to the search index. The default value is cascaded down from the project configuration; `true` if the page kind is `page`, and `false` if the page kind is one of `home`, `section`, `taxonomy`, or `term`. Add this field to override the default value.|  +`params.show_publish_date`|Whether to show the `publishDate` when rendering the page|  +`weight`|The page weight|  +`aliases`|Previous URLs used to access this page|  +`expirydate`|The expiration date|  + +## Related content + +When available, the "See also" sidebar displays related pages using Hugo's [related content][] feature, based on front matter keywords. We ensure consistent keyword usage by validating them against `data/keywords.yaml` during the build. If a keyword is not found, you'll be alerted and must either modify the keyword or update the data file. This validation process helps to refine the related content for better results. + +If the title in the "See also" sidebar is ambiguous or the same as another page, you can define an alternate title in the front matter: + +{{< code-toggle file=hugo >}} +title = 'Long descriptive title' +linkTitle = 'Short title' +[params] +alt_title = 'Whatever you want' +{{< /code-toggle >}} + +Use of the alternate title is limited to the "See also" sidebar. + +> [!NOTE] +> Think carefully before setting the `alt_title`. Use it only when necessary. + +## Code examples + +With examples of template code: + +- Indent with two spaces. +- Insert a space after an opening action delimiter. +- Insert a space before a closing action delimiter. +- Do not add white space removal syntax to action delimiters unless required. For example, inline elements like `img` and `a` require whitespace removal on both sides. + +````md {file="content/example.md"} +```go-html-template +{{ if eq $foo $bar }} + {{ fmt.Printf "%s is %s" $foo $bar }} +{{ end }} +``` +```` + +### Fenced code blocks + +The code block render hook performs several language substitutions: + +Given language|Substitution|Reason +:--|:--|:-- +`html`, `gotmpl`|`go-html-template`|Consistent rendering +`bash`, `ksh`, `sh`, `shell`, `zsh`|`text`|Avoid highlighting quirks +`md`, `mkd`|`text`|Avoid highlighting quirks +`tree`|`text`|There isn't a `tree` lexer + +Set the language to `go-html-template` when including template examples: + +````md {file="content/example.md"} +```go-html-template +{{ if eq $foo "bar" }} + {{ print "foo is bar" }} +{{ end }} +``` +```` + +Set the language to `md` when including Markdown examples: + +````md {file="content/example.md"} +```md +This is **bold** text. +``` +```` + +Set the language to `sh` when including command line examples: + +````md {file="content/example.md"} +```sh +hugo server -D +``` +```` + +Set the language to `tree` when documenting the output of the `tree` command: + +````md {file="content/example.md"} +```tree +assets/ +└── images/ + └── sunset.jpg +``` +```` + +To include a file name header and copy-to-clipboard button: + +````md {file="content/example.md"} +```go-html-template {file="layouts/_partials/foo.html" copy=true} +{{ if eq $foo "bar" }} + {{ print "foo is bar" }} +{{ end }} +``` +```` + +To wrap the code block within an initially-opened `details` element using a non-default summary: + +````md {file="content/example.md"} +```go-html-template {details=true open=true summary="layouts/_partials/foo.html" copy=true} +{{ if eq $foo "bar" }} + {{ print "foo is bar" }} +{{ end }} +``` +```` + +Whitespace trimming is enabled by default. To override this behavior and preserve leading and trailing whitespace: + +````md {file="content/example.md"} +```go-html-template {trim=false} + +{{ if eq $foo "bar" }} + {{ print "foo is bar" }} +{{ end }} + +``` +```` + +### Shortcode calls + +Use this syntax to escape the call within examples: + +```md {file="content/example.md"} +{{}} +{{%/*/* foo */*/%}} +``` + +### Project configuration + +Use the [code-toggle shortcode](#code-toggle) to include project configuration examples: + +```md {file="content/example.md"} +{{}} +baseURL = 'https://example.org/' +locale = 'en-US' +title = 'My Site' +{{}} +``` + +### Front matter + +Use the [code-toggle shortcode](#code-toggle) to include front matter example, setting the `fm` attribute to `true`: + +```md {file="content/example.md"} +{{}} +title = 'My first post' +date = 2023-11-09T12:56:07-08:00 +draft = false +{{}} +``` + +## Callouts + +Use callouts (admonitions) to visually emphasize important information. + +There are five callout types: `note`, `important`, `tip`, `warning`, and `caution`. These are the usage statistic as of 10 Jun 2026, including usage on this page: + +- note (302) +- tip (5) +- important (7) +- warning (7) +- caution (5) + +The predominant use of the `note` callout type is a historical artifact; the previous theme only provided a `note` shortcode, so the callouts were generic. + +In the examples below, the callout type (e.g., `note`, `warning`) is case-insensitive. + +```md {file="content/example.md"} +> [!NOTE] +> Useful information that users should know, even when skimming content. +``` + +> [!NOTE] +> Useful information that users should know, even when skimming content. + +```md {file="content/example.md"} +> [!IMPORTANT] +> Key information users need to know to achieve their goal. +``` + +> [!IMPORTANT] +> Key information users need to know to achieve their goal. + +```md {file="content/example.md"} +> [!TIP] +> Helpful advice for doing things better or more easily. +``` + +> [!TIP] +> Helpful advice for doing things better or more easily. + +```md {file="content/example.md"} +> [!WARNING] +> Urgent info that needs immediate user attention to avoid problems. +``` + +> [!WARNING] +> Urgent info that needs immediate user attention to avoid problems. + +```md {file="content/example.md"} +> [!CAUTION] +> Advises about risks or negative outcomes of certain actions. +``` + +> [!CAUTION] +> Advises about risks or negative outcomes of certain actions. + +## Shortcodes + +These shortcodes are commonly used throughout the documentation. Other shortcodes are available for specialized use. + +### code-toggle + +Use the `code-toggle` shortcode to display examples of project configuration, front matter, or data files. This shortcode takes these arguments: + +`config` +: (`string`) The section of `hugo.Data.docs.config` to render. + +`copy` +: (`bool`) Whether to display a copy-to-clipboard button. Default is `false`. + +`datakey` +: (`string`) The section of `hugo.Data.docs` to render. + +`file` +: (`string`) The file name to display above the rendered code. Omit the file extension for project configuration examples. + +`fm` +: (`bool`) Whether to render the code as front matter. Default is `false`. + +`skipHeader` +: (`bool`) Whether to omit top-level key(s) when rendering a section of `hugo.Data.docs.config`. + +```md {file="content/example.md"} +{{}} +baseURL = 'https://example.org/' +locale = 'en-US' +title = 'My Site' +{{}} +``` + +### deprecated-in + +Use the `deprecated-in` shortcode to indicate that a feature is deprecated: + +```md +{{}} +``` + +You can also include details: + +```md +{{}} +Use the [`hugo.IsServer`](/docs/reference/functions/hugo/isserver/) function instead. +{{}} +``` + +### eturl + +Use the embedded template URL (`eturl`) shortcode to insert an absolute URL to the source code for an embedded template. The shortcode takes a single argument, the base file name of the template (omit the file extension). + +```md {file="content/example.md"} +This is a link to the [embedded alias template][]. + +[embedded alias template]: <{{%/* eturl alias */%}}> +``` + +### glossary-term + +Use the `glossary-term` shortcode to insert the definition of the given glossary term. + +```md {file="content/example.md"} +{{%/* glossary-term scalar */%}} +``` + +### include + +Use the `include` shortcode to include content from another page. + +```md {file="content/example.md"} +{{%/* include "_common/glob-patterns.md" */%}} +``` + +### new-in + +Use the `new-in` shortcode to indicate a new feature: + +```md {file="content/example.md"} +{{}} +``` + +You can also include details: + +```md {file="content/example.md"} +{{}} +This is a new feature. +{{}} +``` + +## New features + +Use the [new-in](#new-in) shortcode to indicate a new feature. + +The new-in shortcode will trigger a build warning if the specified version is older than a predefined threshold, based on differences in major and minor versions. This serves as a reminder to remove this shortcode call. See [details][]. + +## Deprecated features + +Use the [deprecated-in](#deprecated-in) shortcode to indicate that a feature is deprecated. + +The deprecated-in shortcode will trigger a build warning if the specified version is older than a predefined threshold, based on differences in major and minor versions. This serves as a reminder to remove this shortcode call and the associated content. See [details][]. + +When deprecating a feature that has its own page, also set the `expiryDate` in front matter to two years from the date of deprecation. Include a brief comment to explain the setting: + +```yaml +expiryDate: 2028-03-03 # deprecated 2026-03-03 in v0.157.0 +``` + +## GitHub workflow + +> [!NOTE] +> This section assumes that you have a working knowledge of Git and GitHub, and are comfortable working on the command line. + +Use this workflow to create and submit pull requests. + +Step 1 +: Fork the [documentation repository][]. + +Step 2 +: Clone your fork. + +Step 3 +: Create a new branch with a descriptive name that includes the corresponding issue number, if any: + + ```sh + git checkout -b restructure-foo-page-99999 + ``` + +Step 4 +: Make changes. + +Step 5 +: Build the site locally to preview your changes. + +Step 6 +: Commit your changes with a descriptive commit message: + + - Provide a summary on the first line, typically 50 characters or less, followed by a blank line. + - Begin the summary with one of `content`, `theme`, `config`, `all`, or `misc`, followed by a colon, a space, and a brief description of the change beginning with a capital letter + - Use imperative present tense + - Optionally, provide a detailed description where each line is 72 characters or less, followed by a blank line. + - Optionally, add one or more "Fixes" or "Closes" keywords, each on its own line, referencing the [issues][] addressed by this change. + + For example: + + ```sh + git commit -m "content: Restructure the taxonomy page + + This restructures the taxonomy page by splitting topics into logical + sections, each with one or more examples. + + Fixes #9999 + Closes #9998" + ``` + +Step 7 +: Push the new branch to your fork of the documentation repository. + +Step 8 +: Visit the [documentation repository][] and create a pull request (PR). + +Step 9 +: A project maintainer will review your PR and may request changes. You may delete your branch after the maintainer merges your PR. + +[^1]: The field is required, but its data is not. + +[ATX]: https://spec.commonmark.org/current/#atx-headings +[Glossary]: /quick-reference/glossary/ +[basic english]: https://simple.wikipedia.org/wiki/Basic_English +[collapsed link references]: https://discourse.gohugo.io/t/55714 +[details]: https://github.com/gohugoio/hugoDocs/blob/master/layouts/_partials/layouts/blocks/feature-state.html +[developer documentation style guide]: https://developers.google.com/style +[documentation repository]: https://github.com/gohugoio/hugoDocs/ +[fenced code blocks]: https://spec.commonmark.org/current/#fenced-code-blocks +[indented code blocks]: https://spec.commonmark.org/current/#indented-code-blocks +[inline links]: https://discourse.gohugo.io/t/55714 +[issues]: https://github.com/gohugoio/hugoDocs/issues +[list items]: https://spec.commonmark.org/current/#list-items +[project repository]: https://github.com/gohugoio/hugo +[raw HTML]: https://spec.commonmark.org/current/#raw-html +[related content]: /content-management/related-content/ +[setext]: https://spec.commonmark.org/current/#setext-heading diff --git a/docs/content/en/contribute/themes.md b/docs/content/en/contribute/themes.md new file mode 100644 index 0000000..796b5cf --- /dev/null +++ b/docs/content/en/contribute/themes.md @@ -0,0 +1,25 @@ +--- +title: Themes +description: If you've built a Hugo theme and want to contribute back to the Hugo Community, please share it with us. +categories: [] +keywords: [] +aliases: [/contribute/theme/] +--- + +Visit [themes.gohugo.io][] to browse a collection of themes created by the Hugo community. + +To submit your theme: + +1. Read the [submission guidelines][] +1. Open a pull request in the [themes repository][] + +Other useful theme directories: + +- [jamstack.club][] +- [jamstackthemes.dev][] + +[jamstack.club]: https://jamstack.club/#ssg=hugo +[jamstackthemes.dev]: https://jamstackthemes.dev/ssg/hugo +[submission guidelines]: https://github.com/gohugoio/hugoThemesSiteBuilder/tree/main#readme +[themes repository]: https://github.com/gohugoio/hugoThemesSiteBuilder +[themes.gohugo.io]: https://themes.gohugo.io/ diff --git a/docs/content/en/documentation.md b/docs/content/en/documentation.md new file mode 100644 index 0000000..81ed045 --- /dev/null +++ b/docs/content/en/documentation.md @@ -0,0 +1,22 @@ +--- +title: Hugo Documentation +linkTitle: Docs +description: Hugo is the world's fastest static website engine. It's written in Go (aka Golang) and developed by bep, spf13 and friends. +layout: list +params: + searchable: false +--- + + diff --git a/docs/content/en/featured.png b/docs/content/en/featured.png new file mode 100644 index 0000000000000000000000000000000000000000..09953aed96230c387c48bee4ba12001896dbe10f GIT binary patch literal 73881 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