diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 0aad99cda..000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-defaults: &defaults
- docker:
- - image: bepsays/ci-goreleaser:1.13-1
- environment:
- CGO_ENABLED: "0"
-
-version: 2
-jobs:
- build:
- <<: *defaults
- steps:
- - checkout:
- path: hugo
- - run:
- command: |
- git clone git@github.com:gohugoio/hugoDocs.git
- cd hugo
- go mod download
- sleep 5
- go test -p 1 ./...
- - persist_to_workspace:
- root: .
- paths: .
- release:
- <<: *defaults
- steps:
- - attach_workspace:
- at: /root/project
- - run:
- command: |
- cd hugo
- git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
- git config --global user.name "hugoreleaser"
- go run -tags release main.go release -r ${CIRCLE_BRANCH}
-
-workflows:
- version: 2
- release:
- jobs:
- - build:
- filters:
- branches:
- only: /release-.*/
- - hold:
- type: approval
- requires:
- - build
- - release:
- context: org-global
- requires:
- - hold
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index a183f6fcf..000000000
--- a/.dockerignore
+++ /dev/null
@@ -1,9 +0,0 @@
-*.md
-*.log
-*.txt
-.git
-.github
-.circleci
-docs
-examples
-Dockerfile
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..dd2a0096f
--- /dev/null
+++ b/.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/.gitattributes b/.gitattributes
deleted file mode 100644
index 6994810cf..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,8 +0,0 @@
-# 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
deleted file mode 100644
index 12e4b5541..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-name: 'Bug report'
-labels: ''
-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/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index da14802fb..000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: Proposal
-about: Suggest an idea for Hugo
-title: ''
-labels: 'Proposal'
-assignees: ''
-
----
diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md
deleted file mode 100644
index 31837330e..000000000
--- a/.github/ISSUE_TEMPLATE/support.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: Support (Do not use)
-about: Please do not use Github for support requests. Visit https://discourse.gohugo.io for support
-title: ''
-labels: support
-assignees: ''
-
----
-
-Issues created with this template will be automatically closed. Please visit https://discourse.gohugo.io for the support you really, really, want!
\ No newline at end of file
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
deleted file mode 100644
index cc9de09ff..000000000
--- a/.github/SUPPORT.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### 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/calibre/image-actions.yml b/.github/calibre/image-actions.yml
new file mode 100644
index 000000000..938660243
--- /dev/null
+++ b/.github/calibre/image-actions.yml
@@ -0,0 +1,2 @@
+ignorePaths:
+ - "resources/**"
\ No newline at end of file
diff --git a/.github/stale.yml b/.github/stale.yml
index 692c59659..389205294 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -6,6 +6,7 @@ daysUntilClose: 30
exemptLabels:
- Keep
- Security
+ - UndocumentedFeature
# Label to use when marking an issue as stale
staleLabel: Stale
# Comment to post when marking an issue as stale. Set to `false` to disable
@@ -13,9 +14,7 @@ markComment: >
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.
+ If you still think this is important, 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.
diff --git a/.github/workflows/auto_close_support.yml b/.github/workflows/auto_close_support.yml
deleted file mode 100644
index 28a0e695f..000000000
--- a/.github/workflows/auto_close_support.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-on:
- schedule:
- - cron: 0 5 * * 3
-name: Weekly Issue Closure
-jobs:
- cycle-weekly-close:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@master
- - name: weekly-issue-closure
- uses: bdougie/close-issues-based-on-label@master
- env:
- LABEL: support
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/calibreapp-image-actions.yml b/.github/workflows/calibreapp-image-actions.yml
new file mode 100644
index 000000000..3d95fd0c8
--- /dev/null
+++ b/.github/workflows/calibreapp-image-actions.yml
@@ -0,0 +1,12 @@
+name: Compress images
+on: pull_request
+jobs:
+ build:
+ name: calibreapp/image-actions
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - name: calibreapp/image-actions
+ uses: docker://calibreapp/github-image-actions
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index e71fe6c00..b203a37cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,5 @@
-/hugo
-docs/public*
/.idea
-hugo.exe
-*.test
-*.prof
+/public
nohup.out
-cover.out
-*.swp
-*.swo
.DS_Store
-*~
-vendor/*/
-*.bench
-*.debug
-coverage*.out
-
-dock.sh
-
-GoBuilds
-dist
-
-resources/sunset.jpg
-
-vendor
\ No newline at end of file
+trace.out
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e69de29bb..000000000
diff --git a/.mailmap b/.mailmap
deleted file mode 100644
index e93adabc1..000000000
--- a/.mailmap
+++ /dev/null
@@ -1,3 +0,0 @@
-spf13 Steve Francia
-bep Bjørn Erik Pedersen
-
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e614099d0..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-language: go
-sudo: false
-dist: xenial
-env:
- global:
- - GOPROXY="https://proxy.golang.org"
- - HUGO_BUILD_TAGS="extended"
-git:
- depth: false
-go:
- - "1.12.10"
- - "1.13.1"
- - tip
-os:
- - linux
- - osx
- - windows
-matrix:
- allow_failures:
- - go: tip
- fast_finish: true
- exclude:
- - os: windows
- go: tip
-
-install:
- - mkdir -p $HOME/src
- - mv $TRAVIS_BUILD_DIR $HOME/src
- - export TRAVIS_BUILD_DIR=$HOME/src/hugo
- - cd $HOME/src/hugo
- - go get github.com/magefile/mage
-script:
- - go mod download || true
- - mage -v test
- - mage -v check
- - mage -v hugo
- - ./hugo -s docs/
- - ./hugo --renderToMemory -s docs/
- - df -h
-
-before_install:
- - df -h
- # https://travis-ci.community/t/go-cant-find-gcc-with-go1-11-1-on-windows/293/5
- - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install mingw -y; export PATH=/c/tools/mingw64/bin:"$PATH"; fi
- - gem install asciidoctor
- - type asciidoctor
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 124e5b754..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,191 +0,0 @@
-# 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)
- * [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](https://github.com/gohugoio/hugo/issues) 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.
-
-## 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 future (answer questions on the forum, fix any bugs etc.).
-
-It is recommended to open up a discussion on the [Hugo Forum](https://discourse.gohugo.io/) to get feedback on your idea before you begin. If you are submitting a complex feature, create a small design proposal on the [Hugo issue tracker](https://github.com/gohugoio/hugo/issues) before you start.
-
-
-**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.
- * 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 `mage check` succeeds. [Travis CI](https://travis-ci.org/gohugoio/hugo) (Windows, Linux and macOS) will fail the build if `mage check` fails.
- * Follow the **Git Commit Message Guidelines** below.
-
-### Git Commit Message Guidelines
-
-This [blog article](http://chris.beams.io/posts/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:
-*"Return error on wrong use of the Paginator"*, **NOT** *"returning some error."*
-
-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*.
-
-Sometimes it makes sense to prefix the commit message with the package name (or docs folder) all lowercased ending with a colon.
-That is fine, but the rest of the rules above apply.
-So it is "tpl: Add emojify template func", not "tpl: add emojify template func.", and "docs: Document emoji", not "doc: document emoji."
-
-Please use a short and descriptive branch name, e.g. **NOT** "patch-1". It's very common but creates a naming conflict each time when a submission is pulled for a review.
-
-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
-```
-
->Note: Some Go tools may not be fully updated to support Go Modules yet. One example would be LiteIDE. Follow [this workaround](https://github.com/visualfc/liteide/issues/986#issuecomment-428117702) for how to continue to work with Hugo below `GOPATH`.
-
-For some convenient build and test targets, you also will want to install Mage:
-
-```bash
-go get 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
deleted file mode 100755
index 4728a0f2e..000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,33 +0,0 @@
-# GitHub: https://github.com/gohugoio
-# Twitter: https://twitter.com/gohugoio
-# Website: https://gohugo.io/
-
-FROM golang:1.11-stretch AS build
-
-
-WORKDIR /go/src/github.com/gohugoio/hugo
-RUN apt-get install \
- git gcc g++ binutils
-COPY . /go/src/github.com/gohugoio/hugo/
-ENV GO111MODULE=on
-RUN go get -d .
-
-ARG CGO=0
-ENV CGO_ENABLED=${CGO}
-ENV GOOS=linux
-
-# default non-existent build tag so -tags always has an arg
-ARG BUILD_TAGS="99notag"
-RUN go install -ldflags '-w -extldflags "-static"' -tags ${BUILD_TAGS}
-
-# ---
-
-FROM alpine:3.9
-RUN apk add --no-cache ca-certificates
-COPY --from=build /go/bin/hugo /hugo
-ARG WORKDIR="/site"
-WORKDIR ${WORKDIR}
-VOLUME ${WORKDIR}
-EXPOSE 1313
-ENTRYPOINT [ "/hugo" ]
-CMD [ "--help" ]
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 261eeb9e9..000000000
--- a/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- 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/LICENSE.md b/LICENSE.md
new file mode 100644
index 000000000..b62a9b5ff
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,194 @@
+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.
+
+_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/README.md b/README.md
index cef49b617..7ca9d734c 100644
--- a/README.md
+++ b/README.md
@@ -1,187 +1,48 @@
-
+[](https://app.netlify.com/sites/gohugoio/deploys)
+[](https://gohugo.io/contribute/documentation/)
-A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](http://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
+# Hugo Docs
-[Website](https://gohugo.io) |
-[Forum](https://discourse.gohugo.io) |
-[Documentation](https://gohugo.io/getting-started/) |
-[Installation Guide](https://gohugo.io/getting-started/installing/) |
-[Contribution Guide](CONTRIBUTING.md) |
-[Twitter](https://twitter.com/gohugoio)
+Documentation site for [Hugo](https://github.com/gohugoio/hugo), the very fast and flexible static site generator built with love in Go.
-[](https://godoc.org/github.com/gohugoio/hugo)
-[](https://travis-ci.org/gohugoio/hugo)
-[](https://goreportcard.com/report/github.com/gohugoio/hugo)
+## Contributing
-## Overview
+We welcome contributions to Hugo of any kind including documentation, suggestions, bug reports, pull requests etc. Also check out our [contribution guide](https://gohugo.io/contribute/documentation/). We would love to hear from you.
-Hugo is a static HTML and CSS website generator written in [Go][].
-It is optimized for speed, ease of use, and configurability.
-Hugo takes a directory with content and templates and renders them into a full HTML website.
+Note that this repository contains solely the documentation for Hugo. For contributions that aren't documentation-related please refer to the [hugo](https://github.com/gohugoio/hugo) repository.
-Hugo relies on Markdown files with front matter for metadata, and you can run Hugo from any directory.
-This works well for shared hosts and other systems where you don’t have a privileged account.
+*Pull requests shall **only** contain changes to the actual documentation. However, changes on the code base of Hugo **and** the documentation shall be a single, atomic pull request in the [hugo](https://github.com/gohugoio/hugo) repository.*
-Hugo renders a typical website of moderate size in a fraction of a second.
-A good rule of thumb is that each piece of content renders in around 1 millisecond.
+Spelling fixes are most welcomed, and if you want to contribute longer sections to the documentation, it would be great if you had these in mind when writing:
-Hugo is designed to work well for any kind of website including blogs, tumbles, and docs.
+* Short is good. People go to the library to read novels. If there is more than one way to _do a thing_ in Hugo, describe the current _best practice_ (avoid "… but you can also do …" and "… in older versions of Hugo you had to …".
+* For examples, try to find short snippets that teaches people about the concept. If the example is also useful as-is (copy and paste), then great, but don't list long and similar examples just so people can use them on their sites.
+* Hugo has users from all over the world, so an easy to understand and [simple English](https://simple.wikipedia.org/wiki/Basic_English) is good.
-#### Supported Architectures
+## Branches
-Currently, we provide pre-built Hugo binaries for Windows, Linux, FreeBSD, NetBSD, DragonFly BSD, Open BSD, macOS (Darwin), and [Android](https://gist.github.com/bep/a0d8a26cf6b4f8bc992729b8e50b480b) for x64, i386 and ARM architectures.
+* The `master` branch is where the site is automatically built from, and is the place to put changes relevant to the current Hugo version.
+* The `next` branch is where we store changes that is related to the next Hugo release. This can be previewed here: https://next--gohugoio.netlify.com/
-Hugo may also be compiled from source wherever the Go compiler tool chain can run, e.g. for other operating systems including Plan 9 and Solaris.
+## Build
-**Complete documentation is available at [Hugo Documentation](https://gohugo.io/getting-started/).**
-
-## Choose How to Install
-
-If you want to use Hugo as your site generator, simply install the Hugo binaries.
-The Hugo binaries have no external dependencies.
-
-To contribute to the Hugo source code or documentation, you should [fork the Hugo GitHub project](https://github.com/gohugoio/hugo#fork-destination-box) and clone it to your local machine.
-
-Finally, you can install the Hugo source code with `go`, build the binaries yourself, and run Hugo that way.
-Building the binaries is an easy task for an experienced `go` getter.
-
-### Install Hugo as Your Site Generator (Binary Install)
-
-Use the [installation instructions in the Hugo documentation](https://gohugo.io/getting-started/installing/).
-
-### Build and Install the Binaries from Source (Advanced Install)
-
-#### Prerequisite Tools
-
-* [Git](https://git-scm.com/)
-* [Go (at least Go 1.11)](https://golang.org/dl/)
-
-#### Fetch 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:
+To view the documentation site locally, you need to clone this repository:
```bash
-mkdir $HOME/src
-cd $HOME/src
-git clone https://github.com/gohugoio/hugo.git
-cd hugo
-go install
+git clone https://github.com/gohugoio/hugoDocs.git
```
-**If you are a Windows user, substitute the `$HOME` environment variable above with `%USERPROFILE%`.**
-
-## The Hugo Documentation
+Also note that the documentation version for a given version of Hugo can also be found in the `/docs` sub-folder of the [Hugo source repository](https://github.com/gohugoio/hugo).
-The Hugo documentation now lives in its own repository, see https://github.com/gohugoio/hugoDocs. But we do keep a version of that documentation as a `git subtree` in this repository. To build the sub folder `/docs` as a Hugo site, you need to clone this repo:
+Then to view the docs in your browser, run Hugo and open up the link:
```bash
-git clone git@github.com:gohugoio/hugo.git
+▶ hugo server
+
+Started building sites ...
+.
+.
+Serving pages from memory
+Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
+Press Ctrl+C to stop
```
-## Contributing to Hugo
-
-For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
-
-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.
-
-### 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`).
-
-### 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.
-Hugo is a very active project with many contributions happening daily.
-
-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.
-
-For a complete guide to contributing code to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
-
-[](https://github.com/igrigorik/ga-beacon)
-
-[Go]: https://golang.org/
-[Hugo Documentation]: https://gohugo.io/overview/introduction/
-
-## Dependencies
-
-Hugo stands on the shoulder of many great open source libraries, in lexical order:
-
- | Dependency | License |
- | :------------- | :------------- |
- | [github.com/BurntSushi/locker](https://github.com/BurntSushi/locker) | The Unlicense |
- | [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) | MIT License |
- | [github.com/PuerkitoBio/purell](https://github.com/PuerkitoBio/purell) | BSD 3-Clause "New" or "Revised" License |
- | [github.com/PuerkitoBio/urlesc](https://github.com/PuerkitoBio/urlesc) | BSD 3-Clause "New" or "Revised" License |
- | [github.com/alecthomas/chroma](https://github.com/alecthomas/chroma) | MIT License |
- | [github.com/bep/debounce](https://github.com/bep/debounce) | MIT License |
- | [github.com/bep/gitmap](https://github.com/bep/gitmap) | MIT License |
- | [github.com/bep/go-tocss](https://github.com/bep/go-tocss) | MIT License |
- | [github.com/niklasfasching/go-org](https://github.com/niklasfasching/go-org) | MIT License |
- | [github.com/cpuguy83/go-md2man](https://github.com/cpuguy83/go-md2man) | MIT License |
- | [github.com/danwakefield/fnmatch](https://github.com/danwakefield/fnmatch) | BSD 2-Clause "Simplified" License |
- | [github.com/disintegration/imaging](https://github.com/disintegration/imaging) | MIT License |
- | [github.com/dlclark/regexp2](https://github.com/dlclark/regexp2) | MIT License |
- | [github.com/eknkc/amber](https://github.com/eknkc/amber) | MIT License |
- | [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) | BSD 3-Clause "New" or "Revised" License |
- | [github.com/gobwas/glob](https://github.com/gobwas/glob) | MIT License |
- | [github.com/gorilla/websocket](https://github.com/gorilla/websocket) | BSD 2-Clause "Simplified" License |
- | [github.com/hashicorp/go-immutable-radix](https://github.com/hashicorp/go-immutable-radix) | Mozilla Public License 2.0 |
- | [github.com/hashicorp/golang-lru](https://github.com/hashicorp/golang-lru) | Mozilla Public License 2.0 |
- | [github.com/hashicorp/hcl](https://github.com/hashicorp/hcl) | Mozilla Public License 2.0 |
- | [github.com/jdkato/prose](https://github.com/jdkato/prose) | MIT License |
- | [github.com/kyokomi/emoji](https://github.com/kyokomi/emoji) | MIT License |
- | [github.com/magiconair/properties](https://github.com/magiconair/properties) | BSD 2-Clause "Simplified" License |
- | [github.com/markbates/inflect](https://github.com/markbates/inflect) | MIT License |
- | [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) | MIT License |
- | [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth) | MIT License |
- | [github.com/miekg/mmark](https://github.com/miekg/mmark) | Simplified BSD License |
- | [github.com/mitchellh/hashstructure](https://github.com/mitchellh/hashstructure) | MIT License |
- | [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) | MIT License |
- | [github.com/muesli/smartcrop](https://github.com/muesli/smartcrop) | MIT License |
- | [github.com/nicksnyder/go-i18n](https://github.com/nicksnyder/go-i18n) | MIT License |
- | [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) | MIT License |
- | [github.com/pelletier/go-toml](https://github.com/pelletier/go-toml) | MIT License |
- | [github.com/pkg/errors](https://github.com/pkg/errors) | BSD 2-Clause "Simplified" License |
- | [github.com/russross/blackfriday](https://github.com/russross/blackfriday) | Simplified BSD License |
- | [github.com/shurcooL/sanitized_anchor_name](https://github.com/shurcooL/sanitized_anchor_name) | MIT License |
- | [github.com/spf13/afero](https://github.com/spf13/afero) | Apache License 2.0 |
- | [github.com/spf13/cast](https://github.com/spf13/cast) | MIT License |
- | [github.com/spf13/cobra](https://github.com/spf13/cobra) | Apache License 2.0 |
- | [github.com/spf13/fsync](https://github.com/spf13/fsync) | MIT License |
- | [github.com/spf13/jwalterweatherman](https://github.com/spf13/jwalterweatherman) | MIT License |
- | [github.com/spf13/nitro](https://github.com/spf13/nitro) | Apache License 2.0 |
- | [github.com/spf13/pflag](https://github.com/spf13/pflag) | BSD 3-Clause "New" or "Revised" License |
- | [github.com/spf13/viper](https://github.com/spf13/viper) | MIT License |
- | [github.com/tdewolff/minify](https://github.com/tdewolff/minify) | MIT License |
- | [github.com/tdewolff/parse](https://github.com/tdewolff/parse) | MIT License |
- | [github.com/wellington/go-libsass](https://github.com/wellington/go-libsass) | Apache License 2.0 |
- | [github.com/yosssi/ace](https://github.com/yosssi/ace) | MIT License |
- | [golang.org/x/image](https://golang.org/x/image) | BSD 3-Clause "New" or "Revised" License |
- | [golang.org/x/net](https://golang.org/x/net) | BSD 3-Clause "New" or "Revised" License |
- | [golang.org/x/sync](https://golang.org/x/sync) | BSD 3-Clause "New" or "Revised" License |
- | [golang.org/x/sys](https://golang.org/x/sys) | BSD 3-Clause "New" or "Revised" License |
- | [golang.org/x/text](https://golang.org/x/text) | BSD 3-Clause "New" or "Revised" License
- | [gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) | Apache License 2.0 |
-
-
-
-
-
-
-
diff --git a/archetypes/default.md b/archetypes/default.md
new file mode 100644
index 000000000..f30f01f74
--- /dev/null
+++ b/archetypes/default.md
@@ -0,0 +1,13 @@
+---
+linktitle: ""
+description: ""
+godocref: ""
+publishdate: ""
+lastmod: ""
+categories: []
+tags: []
+weight: 00
+slug: ""
+aliases: []
+toc: false
+---
\ No newline at end of file
diff --git a/archetypes/functions.md b/archetypes/functions.md
new file mode 100644
index 000000000..0a5dd344f
--- /dev/null
+++ b/archetypes/functions.md
@@ -0,0 +1,17 @@
+---
+linktitle: ""
+description: ""
+godocref: ""
+publishdate: ""
+lastmod: ""
+categories: [functions]
+tags: []
+ns: ""
+signature: []
+workson: []
+hugoversion: ""
+aliases: []
+relatedfuncs: []
+toc: false
+deprecated: false
+---
\ No newline at end of file
diff --git a/archetypes/showcase/bio.md b/archetypes/showcase/bio.md
new file mode 100644
index 000000000..2443c2f35
--- /dev/null
+++ b/archetypes/showcase/bio.md
@@ -0,0 +1,8 @@
+
+Add some **general info** about {{ replace .Name "-" " " | title }} here.
+
+The site is built by:
+
+* [Person 1](https://example.com)
+* [Person 1](https://example.com)
+
diff --git a/archetypes/showcase/featured.png b/archetypes/showcase/featured.png
new file mode 100644
index 000000000..4f390132e
Binary files /dev/null and b/archetypes/showcase/featured.png differ
diff --git a/archetypes/showcase/index.md b/archetypes/showcase/index.md
new file mode 100644
index 000000000..a21bb9726
--- /dev/null
+++ b/archetypes/showcase/index.md
@@ -0,0 +1,37 @@
+---
+
+title: {{ replace .Name "-" " " | title }}
+date: {{ now.Format "2006-01-02" }}
+
+description: "A short description of this page."
+
+# The URL to the site on the internet.
+siteURL: https://gohugo.io/
+
+# Link to the site's Hugo source code if public and you can/want to share.
+# Remove or leave blank if not needed/wanted.
+siteSource: https://github.com/gohugoio/hugoDocs
+
+# Add credit to the article author. Leave blank or remove if not needed/wanted.
+byline: "[bep](https://github.com/bep), Hugo Lead"
+
+---
+
+To complete this showcase:
+
+1. Write the story about your site in this file.
+2. Add a summary to the `bio.md` file in this folder.
+3. Replace the `featured-template.png` with a screenshot of your site. You can rename it, but it must contain the word `featured`.
+4. Create a new pull request in https://github.com/gohugoio/hugoDocs/pulls
+
+The content of this bundle explained:
+
+index.md
+: The main content file. Fill in required front matter metadata and write your story. I does not have to be a novel. It can even be self-promotional, but it should include Hugo in some form.
+
+bio.md
+: A short summary of the website. Site credits (who built it) fits nicely here.
+
+featured.png
+: A reasonably sized screenshot of your website. It can be named anything, but the name must start with "featured". The sample image is `1500x750` (2:1 aspect ratio).
+
diff --git a/bench.sh b/bench.sh
deleted file mode 100755
index c6a20a7e3..000000000
--- a/bench.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-# allow user to override go executable by running as GOEXE=xxx make ...
-GOEXE="${GOEXE-go}"
-
-# Convenience script to
-# - For a given branch
-# - Run benchmark tests for a given package
-# - Do the same for master
-# - then compare the two runs with benchcmp
-
-benchFilter=".*"
-
-if (( $# < 2 ));
- then
- echo "USAGE: ./bench.sh (and (regexp, optional))"
- exit 1
-fi
-
-
-
-if [ $# -eq 3 ]; then
- benchFilter=$3
-fi
-
-
-BRANCH=$1
-PACKAGE=$2
-
-git checkout $BRANCH
-"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-$BRANCH.txt
-
-git checkout master
-"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-master.txt
-
-
-benchcmp /tmp/bench-$PACKAGE-master.txt /tmp/bench-$PACKAGE-$BRANCH.txt
diff --git a/benchSite.sh b/benchSite.sh
deleted file mode 100755
index 623086708..000000000
--- a/benchSite.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-# allow user to override go executable by running as GOEXE=xxx make ...
-GOEXE="${GOEXE-go}"
-
-# Send in a regexp mathing the benchmarks you want to run, i.e. './benchSite.sh "YAML"'.
-# Note the quotes, which will be needed for more complex expressions.
-# The above will run all variations, but only for front matter YAML.
-
-echo "Running with BenchmarkSiteBuilding/${1}"
-
-"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -count 3 -cpuprofile cpu.prof
diff --git a/benchbep.sh b/benchbep.sh
deleted file mode 100755
index efd616c88..000000000
--- a/benchbep.sh
+++ /dev/null
@@ -1 +0,0 @@
-gobench -package=./hugolib -bench="BenchmarkSiteNew/Deep_content_tree"
\ No newline at end of file
diff --git a/bepdock.sh b/bepdock.sh
deleted file mode 100755
index a7ac0c639..000000000
--- a/bepdock.sh
+++ /dev/null
@@ -1 +0,0 @@
-docker run --rm --mount type=bind,source="$(pwd)",target=/hugo -w /hugo -i -t bepsays/ci-goreleaser:1.11-2 /bin/bash
\ No newline at end of file
diff --git a/bufferpool/bufpool.go b/bufferpool/bufpool.go
deleted file mode 100644
index c1e4105d0..000000000
--- a/bufferpool/bufpool.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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() interface{} {
- 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
deleted file mode 100644
index 023724b97..000000000
--- a/bufferpool/bufpool_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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/filecache/filecache.go b/cache/filecache/filecache.go
deleted file mode 100644
index 3628300fb..000000000
--- a/cache/filecache/filecache.go
+++ /dev/null
@@ -1,376 +0,0 @@
-// 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 (
- "bytes"
- "errors"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "time"
-
- "github.com/gohugoio/hugo/common/hugio"
-
- "github.com/gohugoio/hugo/helpers"
-
- "github.com/BurntSushi/locker"
- "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
-
- // Max age for items in this cache. Negative duration means forever,
- // 0 is effectively turning this cache off.
- maxAge time.Duration
-
- // When set, we just remove this entire root directory on expiration.
- pruneAllRootDir string
-
- nlocker *lockTracker
-}
-
-type lockTracker struct {
- seenMu sync.RWMutex
- seen map[string]struct{}
-
- *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.seenMu.RLock()
- if _, seen := l.seen[id]; !seen {
- l.seenMu.RUnlock()
- l.seenMu.Lock()
- l.seen[id] = struct{}{}
- l.seenMu.Unlock()
- } else {
- l.seenMu.RUnlock()
- }
-
- 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, maxAge time.Duration, pruneAllRootDir string) *Cache {
- return &Cache{
- Fs: fs,
- nlocker: &lockTracker{Locker: locker.NewLocker(), seen: make(map[string]struct{})},
- maxAge: maxAge,
- pruneAllRootDir: pruneAllRootDir,
- }
-}
-
-// 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()
-}
-
-// 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) {
- id = cleanID(id)
- c.nlocker.Lock(id)
-
- info := ItemInfo{Name: id}
-
- f, err := helpers.OpenFileForWriting(c.Fs, id)
- if err != nil {
- c.nlocker.Unlock(id)
- return info, nil, err
- }
-
- return info, &lockedFile{
- File: f,
- unlock: func() { c.nlocker.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.Reader) error,
- create func(info ItemInfo, w io.WriteCloser) error) (info ItemInfo, err error) {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.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
-
-}
-
-// 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) {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.Unlock(id)
-
- info := ItemInfo{Name: id}
-
- if r := c.getOrRemove(id); r != nil {
- return info, r, nil
- }
-
- r, err := create()
- if err != nil {
- return info, nil, err
- }
-
- if c.maxAge == 0 {
- // No caching.
- return info, hugio.ToReadCloser(r), nil
- }
-
- var buff bytes.Buffer
- return info,
- hugio.ToReadCloser(&buff),
- afero.WriteReader(c.Fs, id, io.TeeReader(r, &buff))
-}
-
-// GetOrCreateBytes is the same as GetOrCreate, but produces a byte slice.
-func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (ItemInfo, []byte, error) {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.Unlock(id)
-
- info := ItemInfo{Name: id}
-
- if r := c.getOrRemove(id); r != nil {
- defer r.Close()
- b, err := ioutil.ReadAll(r)
- return info, b, err
- }
-
- b, err := create()
- if err != nil {
- return info, nil, err
- }
-
- if c.maxAge == 0 {
- return info, b, nil
- }
-
- if err := afero.WriteReader(c.Fs, id, bytes.NewReader(b)); err != nil {
- return info, nil, err
- }
- return info, b, nil
-
-}
-
-// GetBytes gets the file content with the given id from the cahce, nil if none found.
-func (c *Cache) GetBytes(id string) (ItemInfo, []byte, error) {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.Unlock(id)
-
- info := ItemInfo{Name: id}
-
- if r := c.getOrRemove(id); r != nil {
- defer r.Close()
- b, err := ioutil.ReadAll(r)
- return info, b, err
- }
-
- return info, nil, nil
-}
-
-// Get gets the file with the given id from the cahce, nil if none found.
-func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.Unlock(id)
-
- info := ItemInfo{Name: id}
-
- r := c.getOrRemove(id)
-
- return info, r, nil
-}
-
-// 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.maxAge == 0 {
- // No caching.
- return nil
- }
-
- if c.maxAge > 0 {
- fi, err := c.Fs.Stat(id)
- if err != nil {
- return nil
- }
-
- if c.isExpired(fi.ModTime()) {
- c.Fs.Remove(id)
- return nil
- }
- }
-
- f, err := c.Fs.Open(id)
-
- if err != nil {
- return nil
- }
-
- return f
-}
-
-func (c *Cache) isExpired(modTime time.Time) bool {
- if c.maxAge < 0 {
- return false
- }
- return c.maxAge == 0 || time.Since(modTime) > c.maxAge
-}
-
-// For testing
-func (c *Cache) getString(id string) string {
- id = cleanID(id)
-
- c.nlocker.Lock(id)
- defer c.nlocker.Unlock(id)
-
- f, err := c.Fs.Open(id)
-
- if err != nil {
- return ""
- }
- defer f.Close()
-
- b, _ := ioutil.ReadAll(f)
- return string(b)
-
-}
-
-// Caches is a named set of caches.
-type Caches map[string]*Cache
-
-// 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(p *helpers.PathSpec) (Caches, error) {
- var dcfg Configs
- if c, ok := p.Cfg.Get("filecacheConfigs").(Configs); ok {
- dcfg = c
- } else {
- var err error
- dcfg, err = DecodeConfig(p.Fs.Source, p.Cfg)
- if err != nil {
- return nil, err
- }
- }
-
- fs := p.Fs.Source
-
- m := make(Caches)
- for k, v := range dcfg {
- var cfs afero.Fs
-
- if v.isResourceDir {
- cfs = p.BaseFs.ResourcesCache
- } else {
- cfs = fs
- }
-
- if cfs == nil {
- // TODO(bep) we still have some places that do not initialize the
- // full dependencies of a site, e.g. the import Jekyll command.
- // That command does not need these caches, so let us just continue
- // for now.
- continue
- }
-
- baseDir := v.Dir
-
- if err := cfs.MkdirAll(baseDir, 0777); err != nil && !os.IsExist(err) {
- return nil, err
- }
-
- bfs := afero.NewBasePathFs(cfs, baseDir)
-
- var pruneAllRootDir string
- if k == cacheKeyModules {
- pruneAllRootDir = "pkg"
- }
-
- m[k] = NewCache(bfs, v.MaxAge, pruneAllRootDir)
- }
-
- return m, nil
-}
-
-func cleanID(name string) string {
- return strings.TrimPrefix(filepath.Clean(name), helpers.FilePathSeparator)
-}
diff --git a/cache/filecache/filecache_config.go b/cache/filecache/filecache_config.go
deleted file mode 100644
index 0c6b569c1..000000000
--- a/cache/filecache/filecache_config.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// 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 (
- "path"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/config"
-
- "github.com/gohugoio/hugo/helpers"
-
- "github.com/mitchellh/mapstructure"
- "github.com/pkg/errors"
- "github.com/spf13/afero"
-)
-
-const (
- cachesConfigKey = "caches"
-
- resourcesGenDir = ":resourceDir/_gen"
-)
-
-var defaultCacheConfig = Config{
- MaxAge: -1, // Never expire
- Dir: ":cacheDir/:project",
-}
-
-const (
- cacheKeyGetJSON = "getjson"
- cacheKeyGetCSV = "getcsv"
- cacheKeyImages = "images"
- cacheKeyAssets = "assets"
- cacheKeyModules = "modules"
-)
-
-type Configs map[string]Config
-
-func (c Configs) CacheDirModules() string {
- return c[cacheKeyModules].Dir
-}
-
-var defaultCacheConfigs = Configs{
- cacheKeyModules: {
- MaxAge: -1,
- Dir: ":cacheDir/modules",
- },
- cacheKeyGetJSON: defaultCacheConfig,
- cacheKeyGetCSV: defaultCacheConfig,
- cacheKeyImages: {
- MaxAge: -1,
- Dir: resourcesGenDir,
- },
- cacheKeyAssets: {
- MaxAge: -1,
- Dir: resourcesGenDir,
- },
-}
-
-type Config 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.
- MaxAge time.Duration
-
- // The directory where files are stored.
- Dir string
-
- // Will resources/_gen will get its own composite filesystem that
- // also checks any theme.
- isResourceDir bool
-}
-
-// GetJSONCache gets the file cache for getJSON.
-func (f Caches) GetJSONCache() *Cache {
- return f[cacheKeyGetJSON]
-}
-
-// GetCSVCache gets the file cache for getCSV.
-func (f Caches) GetCSVCache() *Cache {
- return f[cacheKeyGetCSV]
-}
-
-// 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]
-}
-
-// AssetsCache gets the file cache for assets (processed resources, SCSS etc.).
-func (f Caches) AssetsCache() *Cache {
- return f[cacheKeyAssets]
-}
-
-func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
- c := make(Configs)
- valid := make(map[string]bool)
- // Add defaults
- for k, v := range defaultCacheConfigs {
- c[k] = v
- valid[k] = true
- }
-
- m := cfg.GetStringMap(cachesConfigKey)
-
- _, isOsFs := fs.(*afero.OsFs)
-
- for k, v := range m {
- cc := defaultCacheConfig
-
- 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, err
- }
-
- if cc.Dir == "" {
- return c, errors.New("must provide cache Dir")
- }
-
- name := strings.ToLower(k)
- if !valid[name] {
- return nil, errors.Errorf("%q is not a valid cache name", name)
- }
-
- c[name] = cc
- }
-
- // This is a very old flag in Hugo, but we need to respect it.
- disabled := cfg.GetBool("ignoreCache")
-
- 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, cfg, part)
- if err != nil {
- return c, err
- }
- if isResource {
- v.isResourceDir = true
- }
- parts[i] = resolved
- }
- }
-
- dir = path.Join(parts...)
- if hadSlash {
- dir = "/" + dir
- }
- v.Dir = filepath.Clean(filepath.FromSlash(dir))
-
- if !v.isResourceDir {
- if isOsFs && !filepath.IsAbs(v.Dir) {
- return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
- }
-
- // Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
- if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
- return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
- }
- }
-
- if !strings.HasPrefix(v.Dir, "_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.Dir = filepath.Join(v.Dir, filecacheRootDirname, k)
- } else {
- v.Dir = filepath.Join(v.Dir, k)
- }
-
- if disabled {
- v.MaxAge = 0
- }
-
- c[k] = v
- }
-
- return c, nil
-}
-
-// Resolves :resourceDir => /myproject/resources etc., :cacheDir => ...
-func resolveDirPlaceholder(fs afero.Fs, cfg config.Provider, placeholder string) (cacheDir string, isResource bool, err error) {
- workingDir := cfg.GetString("workingDir")
-
- switch strings.ToLower(placeholder) {
- case ":resourcedir":
- return "", true, nil
- case ":cachedir":
- d, err := helpers.GetCacheDir(fs, cfg)
- return d, false, err
- case ":project":
- return filepath.Base(workingDir), false, nil
- }
-
- return "", false, errors.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
deleted file mode 100644
index 9f80a4f90..000000000
--- a/cache/filecache/filecache_config_test.go
+++ /dev/null
@@ -1,196 +0,0 @@
-// 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 (
- "path/filepath"
- "runtime"
- "strings"
- "testing"
- "time"
-
- "github.com/spf13/afero"
-
- "github.com/gohugoio/hugo/config"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/viper"
-)
-
-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.getJSON]
-maxAge = "10m"
-dir = "/path/to/c1"
-[caches.getCSV]
-maxAge = "11h"
-dir = "/path/to/c2"
-[caches.images]
-dir = "/path/to/c3"
-
-`
-
- cfg, err := config.FromConfigString(configStr, "toml")
- c.Assert(err, qt.IsNil)
- fs := afero.NewMemMapFs()
- decoded, err := DecodeConfig(fs, cfg)
- c.Assert(err, qt.IsNil)
-
- c.Assert(len(decoded), qt.Equals, 5)
-
- c2 := decoded["getcsv"]
- c.Assert(c2.MaxAge.String(), qt.Equals, "11h0m0s")
- c.Assert(c2.Dir, qt.Equals, filepath.FromSlash("/path/to/c2/filecache/getcsv"))
-
- c3 := decoded["images"]
- c.Assert(c3.MaxAge, qt.Equals, time.Duration(-1))
- c.Assert(c3.Dir, qt.Equals, filepath.FromSlash("/path/to/c3/filecache/images"))
-
-}
-
-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.getJSON]
-maxAge = 1234
-dir = "/path/to/c1"
-[caches.getCSV]
-maxAge = 3456
-dir = "/path/to/c2"
-[caches.images]
-dir = "/path/to/c3"
-
-`
-
- cfg, err := config.FromConfigString(configStr, "toml")
- c.Assert(err, qt.IsNil)
- fs := afero.NewMemMapFs()
- decoded, err := DecodeConfig(fs, cfg)
- c.Assert(err, qt.IsNil)
-
- c.Assert(len(decoded), qt.Equals, 5)
-
- for _, v := range decoded {
- c.Assert(v.MaxAge, qt.Equals, time.Duration(0))
- }
-
-}
-
-func TestDecodeConfigDefault(t *testing.T) {
- c := qt.New(t)
- cfg := newTestConfig()
-
- 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")
- }
-
- fs := afero.NewMemMapFs()
-
- decoded, err := DecodeConfig(fs, cfg)
-
- c.Assert(err, qt.IsNil)
-
- c.Assert(len(decoded), qt.Equals, 5)
-
- imgConfig := decoded[cacheKeyImages]
- jsonConfig := decoded[cacheKeyGetJSON]
-
- if runtime.GOOS == "windows" {
- c.Assert(imgConfig.Dir, qt.Equals, filepath.FromSlash("_gen/images"))
- } else {
- c.Assert(imgConfig.Dir, qt.Equals, "_gen/images")
- c.Assert(jsonConfig.Dir, qt.Equals, "/cache/thecache/hugoproject/filecache/getjson")
- }
-
- c.Assert(imgConfig.isResourceDir, qt.Equals, true)
- c.Assert(jsonConfig.isResourceDir, qt.Equals, false)
-}
-
-func TestDecodeConfigInvalidDir(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.getJSON]
-maxAge = "10m"
-dir = "/"
-
-`
- if runtime.GOOS == "windows" {
- configStr = strings.Replace(configStr, "/", "c:\\\\", 1)
- }
-
- cfg, err := config.FromConfigString(configStr, "toml")
- c.Assert(err, qt.IsNil)
- fs := afero.NewMemMapFs()
-
- _, err = DecodeConfig(fs, cfg)
- c.Assert(err, qt.Not(qt.IsNil))
-
-}
-
-func newTestConfig() *viper.Viper {
- cfg := viper.New()
- cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
- cfg.Set("contentDir", "content")
- cfg.Set("dataDir", "data")
- cfg.Set("resourceDir", "resources")
- cfg.Set("i18nDir", "i18n")
- cfg.Set("layoutDir", "layouts")
- cfg.Set("archetypeDir", "archetypes")
- cfg.Set("assetDir", "assets")
-
- return cfg
-}
diff --git a/cache/filecache/filecache_pruner.go b/cache/filecache/filecache_pruner.go
deleted file mode 100644
index 7f68c8b82..000000000
--- a/cache/filecache/filecache_pruner.go
+++ /dev/null
@@ -1,138 +0,0 @@
-// 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 (
- "io"
- "os"
-
- "github.com/pkg/errors"
- "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 os.IsNotExist(err) {
- continue
- }
- return counter, errors.Wrapf(err, "failed to prune cache %q", k)
- }
-
- }
-
- 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.pruneAllRootDir != "" {
- return c.pruneRootDir(force)
- }
-
- 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
- }
- defer f.Close()
- _, err = f.Readdirnames(1)
- if err == io.EOF {
- // Empty dir.
- err = c.Fs.Remove(name)
- }
-
- if err != nil && !os.IsNotExist(err) {
- return err
- }
-
- return nil
- }
-
- shouldRemove := force || c.isExpired(info.ModTime())
-
- if !shouldRemove && len(c.nlocker.seen) > 0 {
- // Remove it if it's not been touched/used in the last build.
- _, seen := c.nlocker.seen[name]
- shouldRemove = !seen
- }
-
- if shouldRemove {
- err := c.Fs.Remove(name)
- if err == nil {
- counter++
- }
-
- if err != nil && !os.IsNotExist(err) {
- return err
- }
-
- }
-
- return nil
- })
-
- return counter, err
-}
-
-func (c *Cache) pruneRootDir(force bool) (int, error) {
-
- info, err := c.Fs.Stat(c.pruneAllRootDir)
- if err != nil {
- if os.IsNotExist(err) {
- return 0, nil
- }
- return 0, err
- }
-
- if !force && !c.isExpired(info.ModTime()) {
- return 0, nil
- }
-
- counter := 0
- // Module cache has 0555 directories; make them writable in order to remove content.
- afero.Walk(c.Fs, c.pruneAllRootDir, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return nil
- }
- if info.IsDir() {
- counter++
- c.Fs.Chmod(path, 0777)
- }
- return nil
- })
- return 1, c.Fs.RemoveAll(c.pruneAllRootDir)
-
-}
diff --git a/cache/filecache/filecache_pruner_test.go b/cache/filecache/filecache_pruner_test.go
deleted file mode 100644
index 48bce723e..000000000
--- a/cache/filecache/filecache_pruner_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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"
- "testing"
- "time"
-
- "github.com/spf13/afero"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestPrune(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.getjson]
-maxAge = "200ms"
-dir = "/cache/c"
-[caches.getcsv]
-maxAge = "200ms"
-dir = "/cache/d"
-[caches.assets]
-maxAge = "200ms"
-dir = ":resourceDir/_gen"
-[caches.images]
-maxAge = "200ms"
-dir = ":resourceDir/_gen"
-`
-
- for _, name := range []string{cacheKeyGetCSV, cacheKeyGetJSON, cacheKeyAssets, cacheKeyImages} {
- msg := qt.Commentf("cache: %s", name)
- p := newPathsSpec(t, afero.NewMemMapFs(), configStr)
- caches, err := NewCaches(p)
- c.Assert(err, qt.IsNil)
- cache := caches[name]
- for i := 0; i < 10; i++ {
- 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 := 0; i < 10; i++ {
- 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 = NewCaches(p)
- c.Assert(err, qt.IsNil)
- 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 := 0; i < 10; i++ {
- 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
deleted file mode 100644
index a4bf45fe0..000000000
--- a/cache/filecache/filecache_test.go
+++ /dev/null
@@ -1,348 +0,0 @@
-// 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 (
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "testing"
- "time"
-
- "github.com/gohugoio/hugo/langs"
- "github.com/gohugoio/hugo/modules"
-
- "github.com/gohugoio/hugo/common/hugio"
- "github.com/gohugoio/hugo/config"
- "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, err := ioutil.TempDir("", "hugo_filecache_test_work")
- c.Assert(err, qt.IsNil)
- defer os.Remove(tempWorkingDir)
-
- tempCacheDir, err := ioutil.TempDir("", "hugo_filecache_test_cache")
- c.Assert(err, qt.IsNil)
- defer os.Remove(tempCacheDir)
-
- 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.getJSON]
-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)
-
- caches, err := NewCaches(p)
- c.Assert(err, qt.IsNil)
-
- cache := caches.Get("GetJSON")
- c.Assert(cache, qt.Not(qt.IsNil))
- c.Assert(cache.maxAge.String(), qt.Equals, "10h0m0s")
-
- bfs, ok := cache.Fs.(*afero.BasePathFs)
- c.Assert(ok, qt.Equals, true)
- filename, err := bfs.RealPath("key")
- c.Assert(err, qt.IsNil)
- if test.cacheDir != "" {
- c.Assert(filename, qt.Equals, filepath.Join(test.cacheDir, "c/"+filecacheRootDirname+"/getjson/key"))
- } else {
- // Temp dir.
- c.Assert(filename, qt.Matches, ".*hugo_cache.*"+filecacheRootDirname+".*key")
- }
-
- cache = caches.Get("Images")
- c.Assert(cache, qt.Not(qt.IsNil))
- c.Assert(cache.maxAge, qt.Equals, time.Duration(-1))
- bfs, ok = cache.Fs.(*afero.BasePathFs)
- c.Assert(ok, qt.Equals, true)
- filename, _ = bfs.RealPath("key")
- c.Assert(filename, qt.Equals, filepath.FromSlash("_gen/images/key"))
-
- rf := func(s string) func() (io.ReadCloser, error) {
- return func() (io.ReadCloser, error) {
- return struct {
- io.ReadSeeker
- io.Closer
- }{
- strings.NewReader(s),
- ioutil.NopCloser(nil),
- }, nil
- }
- }
-
- bf := func() ([]byte, error) {
- return []byte("bcd"), nil
- }
-
- for _, ca := range []*Cache{caches.ImageCache(), caches.AssetsCache(), caches.GetJSONCache(), caches.GetCSVCache()} {
- for i := 0; i < 2; i++ {
- 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, _ := ioutil.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, _ = ioutil.ReadAll(r)
- r.Close()
- c.Assert(string(b), qt.Equals, "abc")
- }
- }
-
- c.Assert(caches.Get("getJSON"), qt.Not(qt.IsNil))
-
- 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, _ := ioutil.ReadAll(r)
- r.Close()
- c.Assert(string(b), qt.Equals, "Hugo is great!")
-
- info, b, err = caches.ImageCache().GetBytes("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) {
- t.Parallel()
-
- c := qt.New(t)
-
- configStr := `
-resourceDir = "myresources"
-contentDir = "content"
-dataDir = "data"
-i18nDir = "i18n"
-layoutDir = "layouts"
-assetDir = "assets"
-archeTypedir = "archetypes"
-
-[caches]
-[caches.getjson]
-maxAge = "1s"
-dir = "/cache/c"
-
-`
-
- p := newPathsSpec(t, afero.NewMemMapFs(), configStr)
-
- caches, err := NewCaches(p)
- c.Assert(err, qt.IsNil)
-
- const cacheName = "getjson"
-
- 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 := 0; i < 50; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
- for j := 0; j < 20; j++ {
- 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, _ := ioutil.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 ItemInfo, r io.Reader) error {
-
- return func(info ItemInfo, r io.Reader) error {
- if failLevel > 0 {
- if failLevel > 1 {
- return ErrFatal
- }
- return errors.New("fail")
- }
-
- b, _ := ioutil.ReadAll(r)
- result = string(b)
-
- return nil
- }
- }
-
- bf := func(s string) func(info ItemInfo, w io.WriteCloser) error {
- return func(info ItemInfo, w io.WriteCloser) error {
- defer w.Close()
- result = s
- _, err := w.Write([]byte(s))
- return err
- }
- }
-
- cache := NewCache(afero.NewMemMapFs(), 100*time.Hour, "")
-
- 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, ErrFatal)
-}
-
-func TestCleanID(t *testing.T) {
- c := qt.New(t)
- c.Assert(cleanID(filepath.FromSlash("/a/b//c.txt")), qt.Equals, filepath.FromSlash("a/b/c.txt"))
- c.Assert(cleanID(filepath.FromSlash("a/b//c.txt")), qt.Equals, filepath.FromSlash("a/b/c.txt"))
-}
-
-func initConfig(fs afero.Fs, cfg config.Provider) error {
- if _, err := langs.LoadLanguageSettings(cfg, nil); err != nil {
- return err
- }
-
- modConfig, err := modules.DecodeConfig(cfg)
- if err != nil {
- return err
- }
-
- workingDir := cfg.GetString("workingDir")
- themesDir := cfg.GetString("themesDir")
- if !filepath.IsAbs(themesDir) {
- themesDir = filepath.Join(workingDir, themesDir)
- }
- modulesClient := modules.NewClient(modules.ClientConfig{
- Fs: fs,
- WorkingDir: workingDir,
- ThemesDir: themesDir,
- ModuleConfig: modConfig,
- IgnoreVendor: true,
- })
-
- moduleConfig, err := modulesClient.Collect()
- if err != nil {
- return err
- }
-
- if err := modules.ApplyProjectConfigDefaults(cfg, moduleConfig.ActiveModules[len(moduleConfig.ActiveModules)-1]); err != nil {
- return err
- }
-
- cfg.Set("allModules", moduleConfig.ActiveModules)
-
- return nil
-}
-
-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)
- initConfig(fs, cfg)
- p, err := helpers.NewPathSpec(hugofs.NewFrom(fs, cfg), cfg, nil)
- c.Assert(err, qt.IsNil)
- return p
-
-}
diff --git a/cache/namedmemcache/named_cache.go b/cache/namedmemcache/named_cache.go
deleted file mode 100644
index d8c229a01..000000000
--- a/cache/namedmemcache/named_cache.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 namedmemcache provides a memory cache with a named lock. This is suitable
-// for situations where creating the cached resource can be time consuming or otherwise
-// resource hungry, or in situations where a "once only per key" is a requirement.
-package namedmemcache
-
-import (
- "sync"
-
- "github.com/BurntSushi/locker"
-)
-
-// Cache holds the cached values.
-type Cache struct {
- nlocker *locker.Locker
- cache map[string]cacheEntry
- mu sync.RWMutex
-}
-
-type cacheEntry struct {
- value interface{}
- err error
-}
-
-// New creates a new cache.
-func New() *Cache {
- return &Cache{
- nlocker: locker.NewLocker(),
- cache: make(map[string]cacheEntry),
- }
-}
-
-// Clear clears the cache state.
-func (c *Cache) Clear() {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- c.cache = make(map[string]cacheEntry)
- c.nlocker = locker.NewLocker()
-
-}
-
-// GetOrCreate tries to get the value with the given cache key, if not found
-// create will be called and cached.
-// This method is thread safe. It also guarantees that the create func for a given
-// key is invoced only once for this cache.
-func (c *Cache) GetOrCreate(key string, create func() (interface{}, error)) (interface{}, error) {
- c.mu.RLock()
- entry, found := c.cache[key]
- c.mu.RUnlock()
-
- if found {
- return entry.value, entry.err
- }
-
- c.nlocker.Lock(key)
- defer c.nlocker.Unlock(key)
-
- // Create it.
- value, err := create()
-
- c.mu.Lock()
- c.cache[key] = cacheEntry{value: value, err: err}
- c.mu.Unlock()
-
- return value, err
-}
diff --git a/cache/namedmemcache/named_cache_test.go b/cache/namedmemcache/named_cache_test.go
deleted file mode 100644
index 9feddb11f..000000000
--- a/cache/namedmemcache/named_cache_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 namedmemcache
-
-import (
- "fmt"
- "sync"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestNamedCache(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- cache := New()
-
- counter := 0
- create := func() (interface{}, error) {
- counter++
- return counter, nil
- }
-
- for i := 0; i < 5; i++ {
- v1, err := cache.GetOrCreate("a1", create)
- c.Assert(err, qt.IsNil)
- c.Assert(v1, qt.Equals, 1)
- v2, err := cache.GetOrCreate("a2", create)
- c.Assert(err, qt.IsNil)
- c.Assert(v2, qt.Equals, 2)
- }
-
- cache.Clear()
-
- v3, err := cache.GetOrCreate("a2", create)
- c.Assert(err, qt.IsNil)
- c.Assert(v3, qt.Equals, 3)
-}
-
-func TestNamedCacheConcurrent(t *testing.T) {
- t.Parallel()
-
- c := qt.New(t)
-
- var wg sync.WaitGroup
-
- cache := New()
-
- create := func(i int) func() (interface{}, error) {
- return func() (interface{}, error) {
- return i, nil
- }
- }
-
- for i := 0; i < 10; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- for j := 0; j < 100; j++ {
- id := fmt.Sprintf("id%d", j)
- v, err := cache.GetOrCreate(id, create(j))
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.Equals, j)
- }
- }()
- }
- wg.Wait()
-}
diff --git a/cache/partitioned_lazy_cache.go b/cache/partitioned_lazy_cache.go
deleted file mode 100644
index 31e66e127..000000000
--- a/cache/partitioned_lazy_cache.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017-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 cache
-
-import (
- "sync"
-)
-
-// Partition represents a cache partition where Load is the callback
-// for when the partition is needed.
-type Partition struct {
- Key string
- Load func() (map[string]interface{}, error)
-}
-
-// Lazy represents a lazily loaded cache.
-type Lazy struct {
- initSync sync.Once
- initErr error
- cache map[string]interface{}
- load func() (map[string]interface{}, error)
-}
-
-// NewLazy creates a lazy cache with the given load func.
-func NewLazy(load func() (map[string]interface{}, error)) *Lazy {
- return &Lazy{load: load}
-}
-
-func (l *Lazy) init() error {
- l.initSync.Do(func() {
- c, err := l.load()
- l.cache = c
- l.initErr = err
-
- })
-
- return l.initErr
-}
-
-// Get initializes the cache if not already initialized, then looks up the
-// given key.
-func (l *Lazy) Get(key string) (interface{}, bool, error) {
- l.init()
- if l.initErr != nil {
- return nil, false, l.initErr
- }
- v, found := l.cache[key]
- return v, found, nil
-}
-
-// PartitionedLazyCache is a lazily loaded cache paritioned by a supplied string key.
-type PartitionedLazyCache struct {
- partitions map[string]*Lazy
-}
-
-// NewPartitionedLazyCache creates a new NewPartitionedLazyCache with the supplied
-// partitions.
-func NewPartitionedLazyCache(partitions ...Partition) *PartitionedLazyCache {
- lazyPartitions := make(map[string]*Lazy, len(partitions))
- for _, partition := range partitions {
- lazyPartitions[partition.Key] = NewLazy(partition.Load)
- }
- cache := &PartitionedLazyCache{partitions: lazyPartitions}
-
- return cache
-}
-
-// Get initializes the partition if not already done so, then looks up the given
-// key in the given partition, returns nil if no value found.
-func (c *PartitionedLazyCache) Get(partition, key string) (interface{}, error) {
- p, found := c.partitions[partition]
-
- if !found {
- return nil, nil
- }
-
- v, found, err := p.Get(key)
- if err != nil {
- return nil, err
- }
-
- if found {
- return v, nil
- }
-
- return nil, nil
-
-}
diff --git a/cache/partitioned_lazy_cache_test.go b/cache/partitioned_lazy_cache_test.go
deleted file mode 100644
index 2c61a6560..000000000
--- a/cache/partitioned_lazy_cache_test.go
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2017-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 cache
-
-import (
- "errors"
- "sync"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestNewPartitionedLazyCache(t *testing.T) {
- t.Parallel()
-
- c := qt.New(t)
-
- p1 := Partition{
- Key: "p1",
- Load: func() (map[string]interface{}, error) {
- return map[string]interface{}{
- "p1_1": "p1v1",
- "p1_2": "p1v2",
- "p1_nil": nil,
- }, nil
- },
- }
-
- p2 := Partition{
- Key: "p2",
- Load: func() (map[string]interface{}, error) {
- return map[string]interface{}{
- "p2_1": "p2v1",
- "p2_2": "p2v2",
- "p2_3": "p2v3",
- }, nil
- },
- }
-
- cache := NewPartitionedLazyCache(p1, p2)
-
- v, err := cache.Get("p1", "p1_1")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.Equals, "p1v1")
-
- v, err = cache.Get("p1", "p2_1")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.IsNil)
-
- v, err = cache.Get("p1", "p1_nil")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.IsNil)
-
- v, err = cache.Get("p2", "p2_3")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.Equals, "p2v3")
-
- v, err = cache.Get("doesnotexist", "p1_1")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.IsNil)
-
- v, err = cache.Get("p1", "doesnotexist")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.IsNil)
-
- errorP := Partition{
- Key: "p3",
- Load: func() (map[string]interface{}, error) {
- return nil, errors.New("Failed")
- },
- }
-
- cache = NewPartitionedLazyCache(errorP)
-
- v, err = cache.Get("p1", "doesnotexist")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.IsNil)
-
- _, err = cache.Get("p3", "doesnotexist")
- c.Assert(err, qt.Not(qt.IsNil))
-
-}
-
-func TestConcurrentPartitionedLazyCache(t *testing.T) {
- t.Parallel()
-
- c := qt.New(t)
-
- var wg sync.WaitGroup
-
- p1 := Partition{
- Key: "p1",
- Load: func() (map[string]interface{}, error) {
- return map[string]interface{}{
- "p1_1": "p1v1",
- "p1_2": "p1v2",
- "p1_nil": nil,
- }, nil
- },
- }
-
- p2 := Partition{
- Key: "p2",
- Load: func() (map[string]interface{}, error) {
- return map[string]interface{}{
- "p2_1": "p2v1",
- "p2_2": "p2v2",
- "p2_3": "p2v3",
- }, nil
- },
- }
-
- cache := NewPartitionedLazyCache(p1, p2)
-
- for i := 0; i < 100; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- for j := 0; j < 10; j++ {
- v, err := cache.Get("p1", "p1_1")
- c.Assert(err, qt.IsNil)
- c.Assert(v, qt.Equals, "p1v1")
- }
- }()
- }
- wg.Wait()
-}
diff --git a/codegen/methods.go b/codegen/methods.go
deleted file mode 100644
index ed8dba923..000000000
--- a/codegen/methods.go
+++ /dev/null
@@ -1,548 +0,0 @@
-// 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"
- "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
-
- var 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.Ptr
-
- 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 i := 0; i < t.NumMethod(); i++ {
-
- m := t.Method(i)
- 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 := 0; i < numIn; i++ {
- 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 := 0; i < numOut; i++ {
- 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 := 0; i < len(args); i++ {
- 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 := 0; i < len(args); i++ {
- 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 := 0; i < len(outs); i++ {
- 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
-// matchin 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
- var 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 = append(pkgImports[:i], pkgImports[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 {
- // Embedded interface
- methodNames = append(
- methodNames,
- collectMethodsRecursive(
- pkg,
- ident.Obj.Decl.(*ast.TypeSpec).Type.(*ast.InterfaceType).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]interface{}{}
- 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
deleted file mode 100644
index bd36b5e80..000000000
--- a/codegen/methods2_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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
deleted file mode 100644
index 77399f4e4..000000000
--- a/codegen/methods_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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.TypeOf((*IEmbed)(nil)).Elem()
- zeroIEOnly = reflect.TypeOf((*IEOnly)(nil)).Elem()
- zeroI = reflect.TypeOf((*I)(nil)).Elem()
- )
-
- 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() interface{}
- Method8() herrors.ErrorContext
- method2()
- method9() os.FileInfo
-}
-
-type IEOnly interface {
- IEmbed
-}
diff --git a/commands/check.go b/commands/check.go
deleted file mode 100644
index f36f23969..000000000
--- a/commands/check.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.
-
-// +build !darwin
-
-package commands
-
-import (
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*checkCmd)(nil)
-
-type checkCmd struct {
- *baseCmd
-}
-
-func newCheckCmd() *checkCmd {
- return &checkCmd{baseCmd: &baseCmd{cmd: &cobra.Command{
- Use: "check",
- Short: "Contains some verification checks",
- },
- }}
-}
diff --git a/commands/check_darwin.go b/commands/check_darwin.go
deleted file mode 100644
index 9291be84c..000000000
--- a/commands/check_darwin.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 commands
-
-import (
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*checkCmd)(nil)
-
-type checkCmd struct {
- *baseCmd
-}
-
-func newCheckCmd() *checkCmd {
- cc := &checkCmd{baseCmd: &baseCmd{cmd: &cobra.Command{
- Use: "check",
- Short: "Contains some verification checks",
- },
- }}
-
- cc.cmd.AddCommand(newLimitCmd().getCommand())
-
- return cc
-}
diff --git a/commands/commandeer.go b/commands/commandeer.go
deleted file mode 100644
index 55526a857..000000000
--- a/commands/commandeer.go
+++ /dev/null
@@ -1,414 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "errors"
- "sync"
-
- "golang.org/x/sync/semaphore"
-
- "github.com/gohugoio/hugo/modules"
-
- "io/ioutil"
-
- "github.com/gohugoio/hugo/common/herrors"
- "github.com/gohugoio/hugo/common/hugo"
-
- jww "github.com/spf13/jwalterweatherman"
-
- "os"
- "path/filepath"
- "regexp"
- "time"
-
- "github.com/gohugoio/hugo/common/loggers"
- "github.com/gohugoio/hugo/config"
-
- "github.com/spf13/cobra"
-
- "github.com/gohugoio/hugo/hugolib"
- "github.com/spf13/afero"
-
- "github.com/bep/debounce"
- "github.com/gohugoio/hugo/common/types"
- "github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/langs"
-)
-
-type commandeerHugoState struct {
- *deps.DepsCfg
- hugoSites *hugolib.HugoSites
- fsCreate sync.Once
- created chan struct{}
-}
-
-type commandeer struct {
- *commandeerHugoState
-
- logger *loggers.Logger
-
- // Currently only set when in "fast render mode". But it seems to
- // be fast enough that we could maybe just add it for all server modes.
- changeDetector *fileChangeDetector
-
- // We need to reuse this on server rebuilds.
- destinationFs afero.Fs
-
- h *hugoBuilderCommon
- ftch flagsToConfigHandler
-
- visitedURLs *types.EvictingStringQueue
-
- doWithCommandeer func(c *commandeer) error
-
- // We watch these for changes.
- configFiles []string
-
- // Used in cases where we get flooded with events in server mode.
- debounce func(f func())
-
- serverPorts []int
- languagesConfigured bool
- languages langs.Languages
- doLiveReload bool
- fastRenderMode bool
- showErrorInBrowser bool
-
- configured bool
- paused bool
-
- fullRebuildSem *semaphore.Weighted
-
- // Any error from the last build.
- buildErr error
-}
-
-func newCommandeerHugoState() *commandeerHugoState {
- return &commandeerHugoState{
- created: make(chan struct{}),
- }
-}
-
-func (c *commandeerHugoState) hugo() *hugolib.HugoSites {
- <-c.created
- return c.hugoSites
-}
-
-func (c *commandeer) errCount() int {
- return int(c.logger.ErrorCounter.Count())
-}
-
-func (c *commandeer) getErrorWithContext() interface{} {
- errCount := c.errCount()
-
- if errCount == 0 {
- return nil
- }
-
- m := make(map[string]interface{})
-
- m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors()))
- m["Version"] = hugo.BuildVersionString()
-
- fe := herrors.UnwrapErrorWithFileContext(c.buildErr)
- if fe != nil {
- m["File"] = fe
- }
-
- if c.h.verbose {
- var b bytes.Buffer
- herrors.FprintStackTrace(&b, c.buildErr)
- m["StackTrace"] = b.String()
- }
-
- return m
-}
-
-func (c *commandeer) Set(key string, value interface{}) {
- if c.configured {
- panic("commandeer cannot be changed")
- }
- c.Cfg.Set(key, value)
-}
-
-func (c *commandeer) initFs(fs *hugofs.Fs) error {
- c.destinationFs = fs.Destination
- c.DepsCfg.Fs = fs
-
- return nil
-}
-
-func newCommandeer(mustHaveConfigFile, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, doWithCommandeer func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
-
- var rebuildDebouncer func(f func())
- if running {
- // The time value used is tested with mass content replacements in a fairly big Hugo site.
- // It is better to wait for some seconds in those cases rather than get flooded
- // with rebuilds.
- rebuildDebouncer = debounce.New(4 * time.Second)
- }
-
- c := &commandeer{
- h: h,
- ftch: f,
- commandeerHugoState: newCommandeerHugoState(),
- doWithCommandeer: doWithCommandeer,
- visitedURLs: types.NewEvictingStringQueue(10),
- debounce: rebuildDebouncer,
- fullRebuildSem: semaphore.NewWeighted(1),
- // This will be replaced later, but we need something to log to before the configuration is read.
- logger: loggers.NewLogger(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, running),
- }
-
- return c, c.loadConfig(mustHaveConfigFile, running)
-}
-
-type fileChangeDetector struct {
- sync.Mutex
- current map[string]string
- prev map[string]string
-
- irrelevantRe *regexp.Regexp
-}
-
-func (f *fileChangeDetector) OnFileClose(name, md5sum string) {
- f.Lock()
- defer f.Unlock()
- f.current[name] = md5sum
-}
-
-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.filterIrrelevant(c)
-}
-
-func (f *fileChangeDetector) filterIrrelevant(in []string) []string {
- var filtered []string
- for _, v := range in {
- if !f.irrelevantRe.MatchString(v) {
- filtered = append(filtered, v)
- }
- }
- return filtered
-}
-
-func (f *fileChangeDetector) PrepareNew() {
- if f == nil {
- return
- }
-
- f.Lock()
- defer f.Unlock()
-
- if f.current == nil {
- f.current = make(map[string]string)
- f.prev = make(map[string]string)
- return
- }
-
- f.prev = make(map[string]string)
- for k, v := range f.current {
- f.prev[k] = v
- }
- f.current = make(map[string]string)
-}
-
-func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error {
-
- if c.DepsCfg == nil {
- c.DepsCfg = &deps.DepsCfg{}
- }
-
- if c.logger != nil {
- // Truncate the error log if this is a reload.
- c.logger.Reset()
- }
-
- cfg := c.DepsCfg
- c.configured = false
- cfg.Running = running
-
- var dir string
- if c.h.source != "" {
- dir, _ = filepath.Abs(c.h.source)
- } else {
- dir, _ = os.Getwd()
- }
-
- var sourceFs afero.Fs = hugofs.Os
- if c.DepsCfg.Fs != nil {
- sourceFs = c.DepsCfg.Fs.Source
- }
-
- environment := c.h.getEnvironment(running)
-
- doWithConfig := func(cfg config.Provider) error {
-
- if c.ftch != nil {
- c.ftch.flagsToConfig(cfg)
- }
-
- cfg.Set("workingDir", dir)
- cfg.Set("environment", environment)
- return nil
- }
-
- doWithCommandeer := func(cfg config.Provider) error {
- c.Cfg = cfg
- if c.doWithCommandeer == nil {
- return nil
- }
- err := c.doWithCommandeer(c)
- return err
- }
-
- configPath := c.h.source
- if configPath == "" {
- configPath = dir
- }
- config, configFiles, err := hugolib.LoadConfig(
- hugolib.ConfigSourceDescriptor{
- Fs: sourceFs,
- Path: configPath,
- WorkingDir: dir,
- Filename: c.h.cfgFile,
- AbsConfigDir: c.h.getConfigDir(dir),
- Environ: os.Environ(),
- Environment: environment},
- doWithCommandeer,
- doWithConfig)
-
- if err != nil {
- if mustHaveConfigFile {
- return err
- }
- if err != hugolib.ErrNoConfigFile && !modules.IsNotExist(err) {
- return err
- }
-
- }
-
- c.configFiles = configFiles
-
- if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok {
- c.languagesConfigured = true
- c.languages = l
- }
-
- // Set some commonly used flags
- c.doLiveReload = running && !c.Cfg.GetBool("disableLiveReload")
- c.fastRenderMode = c.doLiveReload && !c.Cfg.GetBool("disableFastRender")
- c.showErrorInBrowser = c.doLiveReload && !c.Cfg.GetBool("disableBrowserError")
-
- // This is potentially double work, but we need to do this one more time now
- // that all the languages have been configured.
- if c.doWithCommandeer != nil {
- if err := c.doWithCommandeer(c); err != nil {
- return err
- }
- }
-
- logger, err := c.createLogger(config, running)
- if err != nil {
- return err
- }
-
- cfg.Logger = logger
- c.logger = logger
-
- createMemFs := config.GetBool("renderToMemory")
-
- if createMemFs {
- // Rendering to memoryFS, publish to Root regardless of publishDir.
- config.Set("publishDir", "/")
- }
-
- c.fsCreate.Do(func() {
- fs := hugofs.NewFrom(sourceFs, config)
-
- if c.destinationFs != nil {
- // Need to reuse the destination on server rebuilds.
- fs.Destination = c.destinationFs
- } else if createMemFs {
- // Hugo writes the output to memory instead of the disk.
- fs.Destination = new(afero.MemMapFs)
- }
-
- if c.fastRenderMode {
- // For now, fast render mode only. It should, however, be fast enough
- // for the full variant, too.
- 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$`),
- }
-
- changeDetector.PrepareNew()
- fs.Destination = hugofs.NewHashingFs(fs.Destination, changeDetector)
- c.changeDetector = changeDetector
- }
-
- if c.Cfg.GetBool("logPathWarnings") {
- fs.Destination = hugofs.NewCreateCountingFs(fs.Destination)
- }
-
- // To debug hard-to-find path issues.
- //fs.Destination = hugofs.NewStacktracerFs(fs.Destination, `fr/fr`)
-
- err = c.initFs(fs)
- if err != nil {
- close(c.created)
- return
- }
-
- var h *hugolib.HugoSites
-
- h, err = hugolib.NewHugoSites(*c.DepsCfg)
- c.hugoSites = h
- close(c.created)
-
- })
-
- if err != nil {
- return err
- }
-
- cacheDir, err := helpers.GetCacheDir(sourceFs, config)
- if err != nil {
- return err
- }
- config.Set("cacheDir", cacheDir)
-
- cfg.Logger.INFO.Println("Using config file:", config.ConfigFileUsed())
-
- return nil
-
-}
diff --git a/commands/commands.go b/commands/commands.go
deleted file mode 100644
index 3096e1fe0..000000000
--- a/commands/commands.go
+++ /dev/null
@@ -1,312 +0,0 @@
-// 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 commands
-
-import (
- "os"
-
- "github.com/gohugoio/hugo/hugolib/paths"
-
- "github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/common/loggers"
- "github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/helpers"
- "github.com/spf13/cobra"
-)
-
-type commandsBuilder struct {
- hugoBuilderCommon
-
- commands []cmder
-}
-
-func newCommandsBuilder() *commandsBuilder {
- return &commandsBuilder{}
-}
-
-func (b *commandsBuilder) addCommands(commands ...cmder) *commandsBuilder {
- b.commands = append(b.commands, commands...)
- return b
-}
-
-func (b *commandsBuilder) addAll() *commandsBuilder {
- b.addCommands(
- b.newServerCmd(),
- newVersionCmd(),
- newEnvCmd(),
- newConfigCmd(),
- newCheckCmd(),
- newDeployCmd(),
- newConvertCmd(),
- b.newNewCmd(),
- newListCmd(),
- newImportCmd(),
- newGenCmd(),
- createReleaser(),
- b.newModCmd(),
- )
-
- return b
-}
-
-func (b *commandsBuilder) build() *hugoCmd {
- h := b.newHugoCmd()
- addCommands(h.getCommand(), b.commands...)
- return h
-}
-
-func addCommands(root *cobra.Command, commands ...cmder) {
- for _, command := range commands {
- cmd := command.getCommand()
- if cmd == nil {
- continue
- }
- root.AddCommand(cmd)
- }
-}
-
-type baseCmd struct {
- cmd *cobra.Command
-}
-
-var _ commandsBuilderGetter = (*baseBuilderCmd)(nil)
-
-// Used in tests.
-type commandsBuilderGetter interface {
- getCommandsBuilder() *commandsBuilder
-}
-type baseBuilderCmd struct {
- *baseCmd
- *commandsBuilder
-}
-
-func (b *baseBuilderCmd) getCommandsBuilder() *commandsBuilder {
- return b.commandsBuilder
-}
-
-func (c *baseCmd) getCommand() *cobra.Command {
- return c.cmd
-}
-
-func newBaseCmd(cmd *cobra.Command) *baseCmd {
- return &baseCmd{cmd: cmd}
-}
-
-func (b *commandsBuilder) newBuilderCmd(cmd *cobra.Command) *baseBuilderCmd {
- bcmd := &baseBuilderCmd{commandsBuilder: b, baseCmd: &baseCmd{cmd: cmd}}
- bcmd.hugoBuilderCommon.handleFlags(cmd)
- return bcmd
-}
-
-func (c *baseCmd) flagsToConfig(cfg config.Provider) {
- initializeFlags(c.cmd, cfg)
-}
-
-type hugoCmd struct {
- *baseBuilderCmd
-
- // Need to get the sites once built.
- c *commandeer
-}
-
-var _ cmder = (*nilCommand)(nil)
-
-type nilCommand struct {
-}
-
-func (c *nilCommand) getCommand() *cobra.Command {
- return nil
-}
-
-func (c *nilCommand) flagsToConfig(cfg config.Provider) {
-
-}
-
-func (b *commandsBuilder) newHugoCmd() *hugoCmd {
- cc := &hugoCmd{}
-
- cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{
- Use: "hugo",
- Short: "hugo builds your site",
- Long: `hugo is the main command, used to build your Hugo site.
-
-Hugo is a Fast and Flexible Static Site Generator
-built with love by spf13 and friends in Go.
-
-Complete documentation is available at http://gohugo.io/.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- cfgInit := func(c *commandeer) error {
- if cc.buildWatch {
- c.Set("disableLiveReload", true)
- }
- return nil
- }
-
- c, err := initializeConfig(true, cc.buildWatch, &cc.hugoBuilderCommon, cc, cfgInit)
- if err != nil {
- return err
- }
- cc.c = c
-
- return c.build()
- },
- })
-
- cc.cmd.PersistentFlags().StringVar(&cc.cfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
- cc.cmd.PersistentFlags().StringVar(&cc.cfgDir, "configDir", "config", "config dir")
- cc.cmd.PersistentFlags().BoolVar(&cc.quiet, "quiet", false, "build in quiet mode")
-
- // Set bash-completion
- _ = cc.cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, config.ValidConfigFileExtensions)
-
- cc.cmd.PersistentFlags().BoolVarP(&cc.verbose, "verbose", "v", false, "verbose output")
- cc.cmd.PersistentFlags().BoolVarP(&cc.debug, "debug", "", false, "debug output")
- cc.cmd.PersistentFlags().BoolVar(&cc.logging, "log", false, "enable Logging")
- cc.cmd.PersistentFlags().StringVar(&cc.logFile, "logFile", "", "log File path (if set, logging enabled automatically)")
- cc.cmd.PersistentFlags().BoolVar(&cc.verboseLog, "verboseLog", false, "verbose logging")
-
- cc.cmd.Flags().BoolVarP(&cc.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
-
- cc.cmd.Flags().Bool("renderToMemory", false, "render to memory (only useful for benchmark testing)")
-
- // Set bash-completion
- _ = cc.cmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{})
-
- cc.cmd.SetGlobalNormalizationFunc(helpers.NormalizeHugoFlags)
- cc.cmd.SilenceUsage = true
-
- return cc
-}
-
-type hugoBuilderCommon struct {
- source string
- baseURL string
- environment string
-
- buildWatch bool
-
- gc bool
-
- // Profile flags (for debugging of performance problems)
- cpuprofile string
- memprofile string
- mutexprofile string
- traceprofile string
-
- // TODO(bep) var vs string
- logging bool
- verbose bool
- verboseLog bool
- debug bool
- quiet bool
-
- cfgFile string
- cfgDir string
- logFile string
-}
-
-func (cc *hugoBuilderCommon) getConfigDir(baseDir string) string {
- if cc.cfgDir != "" {
- return paths.AbsPathify(baseDir, cc.cfgDir)
- }
-
- if v, found := os.LookupEnv("HUGO_CONFIGDIR"); found {
- return paths.AbsPathify(baseDir, v)
- }
-
- return paths.AbsPathify(baseDir, "config")
-}
-
-func (cc *hugoBuilderCommon) getEnvironment(isServer bool) string {
- if cc.environment != "" {
- return cc.environment
- }
-
- if v, found := os.LookupEnv("HUGO_ENVIRONMENT"); found {
- return v
- }
-
- if isServer {
- return hugo.EnvironmentDevelopment
- }
-
- return hugo.EnvironmentProduction
-}
-
-func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
- cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
- cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
- cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
- cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
- cmd.PersistentFlags().BoolP("ignoreVendor", "", false, "ignores any _vendor directory")
-}
-
-func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
- cc.handleCommonBuilderFlags(cmd)
- 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().StringP("contentDir", "c", "", "filesystem path to content directory")
- cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
- cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/")
- cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
- cmd.Flags().StringP("destination", "d", "", "filesystem path to write files to")
- cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
- cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
- cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date and author info to the pages")
- cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
-
- 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().BoolP("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("i18n-warnings", "", false, "print missing translations")
- cmd.Flags().BoolP("path-warnings", "", false, "print warnings on duplicate target paths etc.")
- cmd.Flags().StringVarP(&cc.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`")
- cmd.Flags().StringVarP(&cc.memprofile, "profile-mem", "", "", "write memory profile to `file`")
- cmd.Flags().StringVarP(&cc.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`")
- cmd.Flags().StringVarP(&cc.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.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)")
-
- // Set bash-completion.
- // Each flag must first be defined before using the SetAnnotation() call.
- _ = cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
- _ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
- _ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
- _ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
-}
-
-func checkErr(logger *loggers.Logger, err error, s ...string) {
- if err == nil {
- return
- }
- if len(s) == 0 {
- logger.CRITICAL.Println(err)
- return
- }
- for _, message := range s {
- logger.ERROR.Println(message)
- }
- logger.ERROR.Println(err)
-}
diff --git a/commands/commands_test.go b/commands/commands_test.go
deleted file mode 100644
index 565736793..000000000
--- a/commands/commands_test.go
+++ /dev/null
@@ -1,286 +0,0 @@
-// 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 commands
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/gohugoio/hugo/common/types"
-
- "github.com/spf13/cobra"
- "github.com/spf13/viper"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestExecute(t *testing.T) {
-
- c := qt.New(t)
-
- dir, err := createSimpleTestSite(t, testSiteConfig{})
- c.Assert(err, qt.IsNil)
-
- defer func() {
- os.RemoveAll(dir)
- }()
-
- resp := Execute([]string{"-s=" + dir})
- c.Assert(resp.Err, qt.IsNil)
- result := resp.Result
- c.Assert(len(result.Sites) == 1, qt.Equals, true)
- c.Assert(len(result.Sites[0].RegularPages()) == 1, qt.Equals, true)
-}
-
-func TestCommandsPersistentFlags(t *testing.T) {
- c := qt.New(t)
-
- noOpRunE := func(cmd *cobra.Command, args []string) error {
- return nil
- }
-
- tests := []struct {
- args []string
- check func(command []cmder)
- }{{[]string{"server",
- "--config=myconfig.toml",
- "--configDir=myconfigdir",
- "--contentDir=mycontent",
- "--disableKinds=page,home",
- "--environment=testing",
- "--configDir=myconfigdir",
- "--layoutDir=mylayouts",
- "--theme=mytheme",
- "--gc",
- "--themesDir=mythemes",
- "--cleanDestinationDir",
- "--navigateToChanged",
- "--disableLiveReload",
- "--noHTTPCache",
- "--i18n-warnings",
- "--destination=/tmp/mydestination",
- "-b=https://example.com/b/",
- "--port=1366",
- "--renderToDisk",
- "--source=mysource",
- "--path-warnings",
- }, func(commands []cmder) {
- var sc *serverCmd
- for _, command := range commands {
- if b, ok := command.(commandsBuilderGetter); ok {
- v := b.getCommandsBuilder().hugoBuilderCommon
- c.Assert(v.cfgFile, qt.Equals, "myconfig.toml")
- c.Assert(v.cfgDir, qt.Equals, "myconfigdir")
- c.Assert(v.source, qt.Equals, "mysource")
- c.Assert(v.baseURL, qt.Equals, "https://example.com/b/")
- }
-
- if srvCmd, ok := command.(*serverCmd); ok {
- sc = srvCmd
- }
- }
-
- c.Assert(sc, qt.Not(qt.IsNil))
- c.Assert(sc.navigateToChanged, qt.Equals, true)
- c.Assert(sc.disableLiveReload, qt.Equals, true)
- c.Assert(sc.noHTTPCache, qt.Equals, true)
- c.Assert(sc.renderToDisk, qt.Equals, true)
- c.Assert(sc.serverPort, qt.Equals, 1366)
- c.Assert(sc.environment, qt.Equals, "testing")
-
- cfg := viper.New()
- sc.flagsToConfig(cfg)
- c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
- c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
- c.Assert(cfg.GetString("layoutDir"), qt.Equals, "mylayouts")
- c.Assert(cfg.GetStringSlice("theme"), qt.DeepEquals, []string{"mytheme"})
- c.Assert(cfg.GetString("themesDir"), qt.Equals, "mythemes")
- c.Assert(cfg.GetString("baseURL"), qt.Equals, "https://example.com/b/")
-
- c.Assert(cfg.Get("disableKinds"), qt.DeepEquals, []string{"page", "home"})
-
- c.Assert(cfg.GetBool("gc"), qt.Equals, true)
-
- // The flag is named path-warnings
- c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true)
-
- // The flag is named i18n-warnings
- c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true)
-
- }}}
-
- for _, test := range tests {
- b := newCommandsBuilder()
- root := b.addAll().build()
-
- for _, c := range b.commands {
- if c.getCommand() == nil {
- continue
- }
- // We are only intereseted in the flag handling here.
- c.getCommand().RunE = noOpRunE
- }
- rootCmd := root.getCommand()
- rootCmd.SetArgs(test.args)
- c.Assert(rootCmd.Execute(), qt.IsNil)
- test.check(b.commands)
- }
-
-}
-
-func TestCommandsExecute(t *testing.T) {
-
- c := qt.New(t)
-
- dir, err := createSimpleTestSite(t, testSiteConfig{})
- c.Assert(err, qt.IsNil)
-
- dirOut, err := ioutil.TempDir("", "hugo-cli-out")
- c.Assert(err, qt.IsNil)
-
- defer func() {
- os.RemoveAll(dir)
- os.RemoveAll(dirOut)
- }()
-
- sourceFlag := fmt.Sprintf("-s=%s", dir)
-
- tests := []struct {
- commands []string
- flags []string
- expectErrToContain string
- }{
- // TODO(bep) permission issue on my OSX? "operation not permitted" {[]string{"check", "ulimit"}, nil, false},
- {[]string{"env"}, nil, ""},
- {[]string{"version"}, nil, ""},
- // no args = hugo build
- {nil, []string{sourceFlag}, ""},
- {nil, []string{sourceFlag, "--renderToMemory"}, ""},
- {[]string{"config"}, []string{sourceFlag}, ""},
- {[]string{"convert", "toTOML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "toml")}, ""},
- {[]string{"convert", "toYAML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "yaml")}, ""},
- {[]string{"convert", "toJSON"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "json")}, ""},
- {[]string{"gen", "autocomplete"}, []string{"--completionfile=" + filepath.Join(dirOut, "autocomplete.txt")}, ""},
- {[]string{"gen", "chromastyles"}, []string{"--style=manni"}, ""},
- {[]string{"gen", "doc"}, []string{"--dir=" + filepath.Join(dirOut, "doc")}, ""},
- {[]string{"gen", "man"}, []string{"--dir=" + filepath.Join(dirOut, "man")}, ""},
- {[]string{"list", "drafts"}, []string{sourceFlag}, ""},
- {[]string{"list", "expired"}, []string{sourceFlag}, ""},
- {[]string{"list", "future"}, []string{sourceFlag}, ""},
- {[]string{"new", "new-page.md"}, []string{sourceFlag}, ""},
- {[]string{"new", "site", filepath.Join(dirOut, "new-site")}, nil, ""},
- {[]string{"unknowncommand"}, nil, "unknown command"},
- // TODO(bep) cli refactor fix https://github.com/gohugoio/hugo/issues/4450
- //{[]string{"new", "theme", filepath.Join(dirOut, "new-theme")}, nil,false},
- }
-
- for _, test := range tests {
- b := newCommandsBuilder().addAll().build()
- hugoCmd := b.getCommand()
- test.flags = append(test.flags, "--quiet")
- hugoCmd.SetArgs(append(test.commands, test.flags...))
-
- // TODO(bep) capture output and add some simple asserts
- // TODO(bep) misspelled subcommands does not return an error. We should investigate this
- // but before that, check for "Error: unknown command".
-
- _, err := hugoCmd.ExecuteC()
- if test.expectErrToContain != "" {
- c.Assert(err, qt.Not(qt.IsNil))
- c.Assert(err.Error(), qt.Contains, test.expectErrToContain)
- } else {
- c.Assert(err, qt.IsNil)
- }
-
- // Assert that we have not left any development debug artifacts in
- // the code.
- if b.c != nil {
- _, ok := b.c.destinationFs.(types.DevMarker)
- c.Assert(ok, qt.Equals, false)
- }
-
- }
-
-}
-
-type testSiteConfig struct {
- configTOML string
- contentDir string
-}
-
-func createSimpleTestSite(t *testing.T, cfg testSiteConfig) (string, error) {
- d, e := ioutil.TempDir("", "hugo-cli")
- if e != nil {
- return "", e
- }
-
- cfgStr := `
-
-baseURL = "https://example.org"
-title = "Hugo Commands"
-
-`
-
- contentDir := "content"
-
- if cfg.configTOML != "" {
- cfgStr = cfg.configTOML
- }
- if cfg.contentDir != "" {
- contentDir = cfg.contentDir
- }
-
- // Just the basic. These are for CLI tests, not site testing.
- writeFile(t, filepath.Join(d, "config.toml"), cfgStr)
-
- writeFile(t, filepath.Join(d, contentDir, "p1.md"), `
----
-title: "P1"
-weight: 1
----
-
-Content
-
-`)
-
- writeFile(t, filepath.Join(d, "layouts", "_default", "single.html"), `
-
-Single: {{ .Title }}
-
-`)
-
- writeFile(t, filepath.Join(d, "layouts", "_default", "list.html"), `
-
-List: {{ .Title }}
-Environment: {{ hugo.Environment }}
-
-`)
-
- return d, nil
-
-}
-
-func writeFile(t *testing.T, filename, content string) {
- must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755)))
- must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755)))
-}
-
-func must(t *testing.T, err error) {
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/commands/config.go b/commands/config.go
deleted file mode 100644
index 72c2a0d97..000000000
--- a/commands/config.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// 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.Print the version number of Hug
-
-package commands
-
-import (
- "encoding/json"
- "os"
- "reflect"
- "regexp"
- "sort"
- "strings"
-
- "github.com/gohugoio/hugo/parser"
- "github.com/gohugoio/hugo/parser/metadecoders"
-
- "github.com/gohugoio/hugo/modules"
-
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
-)
-
-var _ cmder = (*configCmd)(nil)
-
-type configCmd struct {
- hugoBuilderCommon
- *baseCmd
-}
-
-func newConfigCmd() *configCmd {
- cc := &configCmd{}
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "config",
- Short: "Print the site configuration",
- Long: `Print the site configuration, both default and custom settings.`,
- RunE: cc.printConfig,
- })
-
- cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
-
- printMountsCmd := &cobra.Command{
- Use: "mounts",
- Short: "Print the configured file mounts",
- RunE: cc.printMounts,
- }
-
- cc.cmd.AddCommand(printMountsCmd)
-
- return cc
-}
-
-func (c *configCmd) printMounts(cmd *cobra.Command, args []string) error {
- cfg, err := initializeConfig(true, false, &c.hugoBuilderCommon, c, nil)
- if err != nil {
- return err
- }
-
- allModules := cfg.Cfg.Get("allmodules").(modules.Modules)
-
- for _, m := range allModules {
- if err := parser.InterfaceToConfig(&modMounts{m: m}, metadecoders.JSON, os.Stdout); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (c *configCmd) printConfig(cmd *cobra.Command, args []string) error {
- cfg, err := initializeConfig(true, false, &c.hugoBuilderCommon, c, nil)
- if err != nil {
- return err
- }
-
- allSettings := cfg.Cfg.(*viper.Viper).AllSettings()
-
- // We need to clean up this, but we store objects in the config that
- // isn't really interesting to the end user, so filter these.
- ignoreKeysRe := regexp.MustCompile("client|sorted|filecacheconfigs|allmodules|multilingual")
-
- separator := ": "
-
- if len(cfg.configFiles) > 0 && strings.HasSuffix(cfg.configFiles[0], ".toml") {
- separator = " = "
- }
-
- var keys []string
- for k := range allSettings {
- if ignoreKeysRe.MatchString(k) {
- continue
- }
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- kv := reflect.ValueOf(allSettings[k])
- if kv.Kind() == reflect.String {
- jww.FEEDBACK.Printf("%s%s\"%+v\"\n", k, separator, allSettings[k])
- } else {
- jww.FEEDBACK.Printf("%s%s%+v\n", k, separator, allSettings[k])
- }
- }
-
- return nil
-}
-
-type modMounts struct {
- m modules.Module
-}
-
-type modMount struct {
- Source string `json:"source"`
- Target string `json:"target"`
- Lang string `json:"lang,omitempty"`
-}
-
-func (m *modMounts) MarshalJSON() ([]byte, error) {
- var mounts []modMount
-
- for _, mount := range m.m.Mounts() {
- mounts = append(mounts, modMount{
- Source: mount.Source,
- Target: mount.Target,
- Lang: mount.Lang,
- })
- }
-
- return json.Marshal(&struct {
- Path string `json:"path"`
- Dir string `json:"dir"`
- Mounts []modMount `json:"mounts"`
- }{
- Path: m.m.Path(),
- Dir: m.m.Dir(),
- Mounts: mounts,
- })
-}
diff --git a/commands/convert.go b/commands/convert.go
deleted file mode 100644
index e4ff1ac61..000000000
--- a/commands/convert.go
+++ /dev/null
@@ -1,250 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "fmt"
- "io"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/resources/page"
-
- "github.com/gohugoio/hugo/hugofs"
-
- "github.com/gohugoio/hugo/helpers"
-
- "github.com/gohugoio/hugo/parser"
- "github.com/gohugoio/hugo/parser/metadecoders"
- "github.com/gohugoio/hugo/parser/pageparser"
-
- "github.com/pkg/errors"
-
- "github.com/gohugoio/hugo/hugolib"
-
- "path/filepath"
-
- "github.com/spf13/cobra"
-)
-
-var (
- _ cmder = (*convertCmd)(nil)
-)
-
-type convertCmd struct {
- hugoBuilderCommon
-
- outputDir string
- unsafe bool
-
- *baseCmd
-}
-
-func newConvertCmd() *convertCmd {
- cc := &convertCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "convert",
- Short: "Convert your content to different formats",
- Long: `Convert your content (e.g. front matter) to different formats.
-
-See convert's subcommands toJSON, toTOML and toYAML for more information.`,
- RunE: nil,
- })
-
- cc.cmd.AddCommand(
- &cobra.Command{
- Use: "toJSON",
- Short: "Convert front matter to JSON",
- Long: `toJSON converts all front matter in the content directory
-to use JSON for the front matter.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- return cc.convertContents(metadecoders.JSON)
- },
- },
- &cobra.Command{
- Use: "toTOML",
- Short: "Convert front matter to TOML",
- Long: `toTOML converts all front matter in the content directory
-to use TOML for the front matter.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- return cc.convertContents(metadecoders.TOML)
- },
- },
- &cobra.Command{
- Use: "toYAML",
- Short: "Convert front matter to YAML",
- Long: `toYAML converts all front matter in the content directory
-to use YAML for the front matter.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- return cc.convertContents(metadecoders.YAML)
- },
- },
- )
-
- cc.cmd.PersistentFlags().StringVarP(&cc.outputDir, "output", "o", "", "filesystem path to write files to")
- cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
- cc.cmd.PersistentFlags().BoolVar(&cc.unsafe, "unsafe", false, "enable less safe operations, please backup first")
- cc.cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
-
- return cc
-}
-
-func (cc *convertCmd) convertContents(format metadecoders.Format) error {
- if cc.outputDir == "" && !cc.unsafe {
- return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
- }
-
- c, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, nil)
- if err != nil {
- return err
- }
-
- c.Cfg.Set("buildDrafts", true)
-
- h, err := hugolib.NewHugoSites(*c.DepsCfg)
- if err != nil {
- return err
- }
-
- if err := h.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
- return err
- }
-
- site := h.Sites[0]
-
- site.Log.FEEDBACK.Println("processing", len(site.AllPages()), "content files")
- for _, p := range site.AllPages() {
- if err := cc.convertAndSavePage(p, site, format); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (cc *convertCmd) 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 := cc.convertAndSavePage(r.(page.Page), site, targetFormat); err != nil {
- return err
- }
- }
-
- if p.File().IsZero() {
- // No content file.
- return nil
- }
-
- errMsg := fmt.Errorf("Error processing file %q", p.Path())
-
- site.Log.INFO.Println("Attempting to convert", p.File().Filename())
-
- f := p.File()
- file, err := f.FileInfo().Meta().Open()
- if err != nil {
- site.Log.ERROR.Println(errMsg)
- file.Close()
- return nil
- }
-
- pf, err := parseContentFile(file)
- if err != nil {
- site.Log.ERROR.Println(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.ERROR.Println(errMsg)
- return err
- }
-
- newContent.Write(pf.content)
-
- newFilename := p.File().Filename()
-
- if cc.outputDir != "" {
- contentDir := strings.TrimSuffix(newFilename, p.Path())
- contentDir = filepath.Base(contentDir)
-
- newFilename = filepath.Join(cc.outputDir, contentDir, p.Path())
- }
-
- fs := hugofs.Os
- if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil {
- return errors.Wrapf(err, "Failed to save file %q:", newFilename)
- }
-
- return nil
-}
-
-type parsedFile struct {
- frontMatterFormat metadecoders.Format
- frontMatterSource []byte
- frontMatter map[string]interface{}
-
- // Everything after Front Matter
- content []byte
-}
-
-func parseContentFile(r io.Reader) (parsedFile, error) {
- var pf parsedFile
-
- psr, err := pageparser.Parse(r, pageparser.Config{})
- if err != nil {
- return pf, err
- }
-
- iter := psr.Iterator()
-
- walkFn := func(item pageparser.Item) bool {
- if pf.frontMatterSource != nil {
- // The rest is content.
- pf.content = psr.Input()[item.Pos:]
- // Done
- return false
- } else if item.IsFrontMatter() {
- pf.frontMatterFormat = metadecoders.FormatFromFrontMatterType(item.Type)
- pf.frontMatterSource = item.Val
- }
- return true
-
- }
-
- iter.PeekWalk(walkFn)
-
- metadata, err := metadecoders.Default.UnmarshalToMap(pf.frontMatterSource, pf.frontMatterFormat)
- if err != nil {
- return pf, err
- }
- pf.frontMatter = metadata
-
- return pf, nil
-
-}
diff --git a/commands/deploy.go b/commands/deploy.go
deleted file mode 100644
index d74d93709..000000000
--- a/commands/deploy.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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 commands
-
-import (
- "context"
-
- "github.com/gohugoio/hugo/deploy"
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*deployCmd)(nil)
-
-// deployCmd supports deploying sites to Cloud providers.
-type deployCmd struct {
- hugoBuilderCommon
- *baseCmd
-}
-
-// TODO: In addition to the "deploy" command, consider adding a "--deploy"
-// flag for the default command; this would build the site and then deploy it.
-// It's not obvious how to do this; would all of the deploy-specific flags
-// have to exist at the top level as well?
-
-// TODO: The output files change every time "hugo" is executed, it looks
-// like because of map order randomization. This means that you can
-// run "hugo && hugo deploy" again and again and upload new stuff every time. Is
-// this intended?
-
-func newDeployCmd() *deployCmd {
- cc := &deployCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "deploy",
- Short: "Deploy your site to a Cloud provider.",
- Long: `Deploy your site to a Cloud provider.
-
-See https://gohugo.io/hosting-and-deployment/hugo-deploy/ for detailed
-documentation.
-`,
-
- RunE: func(cmd *cobra.Command, args []string) error {
- cfgInit := func(c *commandeer) error {
- return nil
- }
- comm, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, cfgInit)
- if err != nil {
- return err
- }
- deployer, err := deploy.New(comm.Cfg, comm.hugo().PathSpec.PublishFs)
- if err != nil {
- return err
- }
- return deployer.Deploy(context.Background())
- },
- })
-
- cc.cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
- cc.cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
- cc.cmd.Flags().Bool("dryRun", false, "dry run")
- cc.cmd.Flags().Bool("force", false, "force upload of all files")
- cc.cmd.Flags().Bool("invalidateCDN", true, "invalidate the CDN cache listed in the deployment target")
- cc.cmd.Flags().Int("maxDeletes", 256, "maximum # of files to delete, or -1 to disable")
-
- return cc
-}
diff --git a/commands/env.go b/commands/env.go
deleted file mode 100644
index 76c16b93b..000000000
--- a/commands/env.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 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 (
- "runtime"
-
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*envCmd)(nil)
-
-type envCmd struct {
- *baseCmd
-}
-
-func newEnvCmd() *envCmd {
- return &envCmd{baseCmd: newBaseCmd(&cobra.Command{
- Use: "env",
- Short: "Print Hugo version and environment info",
- Long: `Print Hugo version and environment info. This is useful in Hugo bug reports.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- printHugoVersion()
- jww.FEEDBACK.Printf("GOOS=%q\n", runtime.GOOS)
- jww.FEEDBACK.Printf("GOARCH=%q\n", runtime.GOARCH)
- jww.FEEDBACK.Printf("GOVERSION=%q\n", runtime.Version())
-
- return nil
- },
- }),
- }
-}
diff --git a/commands/gen.go b/commands/gen.go
deleted file mode 100644
index 6878cfe70..000000000
--- a/commands/gen.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 commands
-
-import (
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*genCmd)(nil)
-
-type genCmd struct {
- *baseCmd
-}
-
-func newGenCmd() *genCmd {
- cc := &genCmd{}
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "gen",
- Short: "A collection of several useful generators.",
- })
-
- cc.cmd.AddCommand(
- newGenautocompleteCmd().getCommand(),
- newGenDocCmd().getCommand(),
- newGenManCmd().getCommand(),
- createGenDocsHelper().getCommand(),
- createGenChromaStyles().getCommand())
-
- return cc
-}
diff --git a/commands/genautocomplete.go b/commands/genautocomplete.go
deleted file mode 100644
index b0b98abb4..000000000
--- a/commands/genautocomplete.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 commands
-
-import (
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*genautocompleteCmd)(nil)
-
-type genautocompleteCmd struct {
- autocompleteTarget string
-
- // bash for now (zsh and others will come)
- autocompleteType string
-
- *baseCmd
-}
-
-func newGenautocompleteCmd() *genautocompleteCmd {
- cc := &genautocompleteCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "autocomplete",
- Short: "Generate shell autocompletion script for Hugo",
- Long: `Generates a shell autocompletion script for Hugo.
-
-NOTE: The current version supports Bash only.
- This should work for *nix systems with Bash installed.
-
-By default, the file is written directly to /etc/bash_completion.d
-for convenience, and the command may need superuser rights, e.g.:
-
- $ sudo hugo gen autocomplete
-
-Add ` + "`--completionfile=/path/to/file`" + ` flag to set alternative
-file-path and name.
-
-Logout and in again to reload the completion scripts,
-or just source them in directly:
-
- $ . /etc/bash_completion`,
-
- RunE: func(cmd *cobra.Command, args []string) error {
- if cc.autocompleteType != "bash" {
- return newUserError("Only Bash is supported for now")
- }
-
- err := cmd.Root().GenBashCompletionFile(cc.autocompleteTarget)
-
- if err != nil {
- return err
- }
-
- jww.FEEDBACK.Println("Bash completion file for Hugo saved to", cc.autocompleteTarget)
-
- return nil
- },
- })
-
- cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteTarget, "completionfile", "", "/etc/bash_completion.d/hugo.sh", "autocompletion file")
- cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteType, "type", "", "bash", "autocompletion type (currently only bash supported)")
-
- // For bash-completion
- cc.cmd.PersistentFlags().SetAnnotation("completionfile", cobra.BashCompFilenameExt, []string{})
-
- return cc
-}
diff --git a/commands/genchromastyles.go b/commands/genchromastyles.go
deleted file mode 100644
index a2231e56e..000000000
--- a/commands/genchromastyles.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017-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 commands
-
-import (
- "os"
-
- "github.com/alecthomas/chroma"
- "github.com/alecthomas/chroma/formatters/html"
- "github.com/alecthomas/chroma/styles"
- "github.com/spf13/cobra"
-)
-
-var (
- _ cmder = (*genChromaStyles)(nil)
-)
-
-type genChromaStyles struct {
- style string
- highlightStyle string
- linesStyle string
- *baseCmd
-}
-
-// TODO(bep) highlight
-func createGenChromaStyles() *genChromaStyles {
- g := &genChromaStyles{
- baseCmd: newBaseCmd(&cobra.Command{
- Use: "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 pygmentsUseClasses is enabled in config.
-
-See https://help.farbox.com/pygments.html for preview of available styles`,
- }),
- }
-
- g.cmd.RunE = func(cmd *cobra.Command, args []string) error {
- return g.generate()
- }
-
- g.cmd.PersistentFlags().StringVar(&g.style, "style", "friendly", "highlighter style (see https://help.farbox.com/pygments.html)")
- g.cmd.PersistentFlags().StringVar(&g.highlightStyle, "highlightStyle", "bg:#ffffcc", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
- g.cmd.PersistentFlags().StringVar(&g.linesStyle, "linesStyle", "", "style used for line numbers (see https://github.com/alecthomas/chroma)")
-
- return g
-}
-
-func (g *genChromaStyles) generate() error {
- builder := styles.Get(g.style).Builder()
- if g.highlightStyle != "" {
- builder.Add(chroma.LineHighlight, g.highlightStyle)
- }
- if g.linesStyle != "" {
- builder.Add(chroma.LineNumbers, g.linesStyle)
- }
- style, err := builder.Build()
- if err != nil {
- return err
- }
- formatter := html.New(html.WithClasses())
- formatter.WriteCSS(os.Stdout, style)
- return nil
-}
diff --git a/commands/gendoc.go b/commands/gendoc.go
deleted file mode 100644
index 8312191f2..000000000
--- a/commands/gendoc.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2016 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 (
- "fmt"
- "path"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/spf13/cobra"
- "github.com/spf13/cobra/doc"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*genDocCmd)(nil)
-
-type genDocCmd struct {
- gendocdir string
- *baseCmd
-}
-
-func newGenDocCmd() *genDocCmd {
- const gendocFrontmatterTemplate = `---
-date: %s
-title: "%s"
-slug: %s
-url: %s
----
-`
-
- cc := &genDocCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "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 http://gohugo.io/.
-
-It creates one Markdown file per command with front matter suitable
-for rendering in Hugo.`,
-
- RunE: func(cmd *cobra.Command, args []string) error {
- if !strings.HasSuffix(cc.gendocdir, helpers.FilePathSeparator) {
- cc.gendocdir += helpers.FilePathSeparator
- }
- if found, _ := helpers.Exists(cc.gendocdir, hugofs.Os); !found {
- jww.FEEDBACK.Println("Directory", cc.gendocdir, "does not exist, creating...")
- if err := hugofs.Os.MkdirAll(cc.gendocdir, 0777); err != nil {
- return err
- }
- }
- now := time.Now().Format("2006-01-02")
- prepender := func(filename string) string {
- name := filepath.Base(filename)
- base := strings.TrimSuffix(name, path.Ext(name))
- url := "/commands/" + strings.ToLower(base) + "/"
- return fmt.Sprintf(gendocFrontmatterTemplate, now, strings.Replace(base, "_", " ", -1), base, url)
- }
-
- linkHandler := func(name string) string {
- base := strings.TrimSuffix(name, path.Ext(name))
- return "/commands/" + strings.ToLower(base) + "/"
- }
-
- jww.FEEDBACK.Println("Generating Hugo command-line documentation in", cc.gendocdir, "...")
- doc.GenMarkdownTreeCustom(cmd.Root(), cc.gendocdir, prepender, linkHandler)
- jww.FEEDBACK.Println("Done.")
-
- return nil
- },
- })
-
- cc.cmd.PersistentFlags().StringVar(&cc.gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
-
- // For bash-completion
- cc.cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
-
- return cc
-}
diff --git a/commands/gendocshelper.go b/commands/gendocshelper.go
deleted file mode 100644
index c243581f6..000000000
--- a/commands/gendocshelper.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017-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 commands
-
-import (
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
-
- "github.com/gohugoio/hugo/docshelper"
- "github.com/spf13/cobra"
-)
-
-var (
- _ cmder = (*genDocsHelper)(nil)
-)
-
-type genDocsHelper struct {
- target string
- *baseCmd
-}
-
-func createGenDocsHelper() *genDocsHelper {
- g := &genDocsHelper{
- baseCmd: newBaseCmd(&cobra.Command{
- Use: "docshelper",
- Short: "Generate some data files for the Hugo docs.",
- Hidden: true,
- }),
- }
-
- g.cmd.RunE = func(cmd *cobra.Command, args []string) error {
- return g.generate()
- }
-
- g.cmd.PersistentFlags().StringVarP(&g.target, "dir", "", "docs/data", "data dir")
-
- return g
-}
-
-func (g *genDocsHelper) generate() error {
- fmt.Println("Generate docs data to", g.target)
-
- targetFile := filepath.Join(g.target, "docs.json")
-
- f, err := os.Create(targetFile)
- if err != nil {
- return err
- }
- defer f.Close()
-
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
-
- if err := enc.Encode(docshelper.DocProviders); err != nil {
- return err
- }
-
- fmt.Println("Done!")
- return nil
-
-}
diff --git a/commands/genman.go b/commands/genman.go
deleted file mode 100644
index 720046289..000000000
--- a/commands/genman.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 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 (
- "fmt"
- "strings"
-
- "github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/spf13/cobra"
- "github.com/spf13/cobra/doc"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*genManCmd)(nil)
-
-type genManCmd struct {
- genmandir string
- *baseCmd
-}
-
-func newGenManCmd() *genManCmd {
- cc := &genManCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "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.`,
-
- RunE: func(cmd *cobra.Command, args []string) error {
- header := &doc.GenManHeader{
- Section: "1",
- Manual: "Hugo Manual",
- Source: fmt.Sprintf("Hugo %s", hugo.CurrentVersion),
- }
- if !strings.HasSuffix(cc.genmandir, helpers.FilePathSeparator) {
- cc.genmandir += helpers.FilePathSeparator
- }
- if found, _ := helpers.Exists(cc.genmandir, hugofs.Os); !found {
- jww.FEEDBACK.Println("Directory", cc.genmandir, "does not exist, creating...")
- if err := hugofs.Os.MkdirAll(cc.genmandir, 0777); err != nil {
- return err
- }
- }
- cmd.Root().DisableAutoGenTag = true
-
- jww.FEEDBACK.Println("Generating Hugo man pages in", cc.genmandir, "...")
- doc.GenManTree(cmd.Root(), header, cc.genmandir)
-
- jww.FEEDBACK.Println("Done.")
-
- return nil
- },
- })
-
- cc.cmd.PersistentFlags().StringVar(&cc.genmandir, "dir", "man/", "the directory to write the man pages.")
-
- // For bash-completion
- cc.cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
-
- return cc
-}
diff --git a/commands/helpers.go b/commands/helpers.go
deleted file mode 100644
index 1386e425f..000000000
--- a/commands/helpers.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 commands defines and implements command-line commands and flags
-// used by Hugo. Commands and flags are implemented using Cobra.
-package commands
-
-import (
- "fmt"
- "regexp"
-
- "github.com/gohugoio/hugo/config"
- "github.com/spf13/cobra"
-)
-
-const (
- ansiEsc = "\u001B"
- clearLine = "\r\033[K"
- hideCursor = ansiEsc + "[?25l"
- showCursor = ansiEsc + "[?25h"
-)
-
-type flagsToConfigHandler interface {
- flagsToConfig(cfg config.Provider)
-}
-
-type cmder interface {
- flagsToConfigHandler
- getCommand() *cobra.Command
-}
-
-// commandError is an error used to signal different error situations in command handling.
-type commandError struct {
- s string
- userError bool
-}
-
-func (c commandError) Error() string {
- return c.s
-}
-
-func (c commandError) isUserError() bool {
- return c.userError
-}
-
-func newUserError(a ...interface{}) commandError {
- return commandError{s: fmt.Sprintln(a...), userError: true}
-}
-
-func newSystemError(a ...interface{}) commandError {
- return commandError{s: fmt.Sprintln(a...), userError: false}
-}
-
-func newSystemErrorF(format string, a ...interface{}) commandError {
- return commandError{s: fmt.Sprintf(format, a...), userError: false}
-}
-
-// Catch some of the obvious user errors from Cobra.
-// We don't want to show the usage message for every error.
-// The below may be to generic. Time will show.
-var userErrorRegexp = regexp.MustCompile("argument|flag|shorthand")
-
-func isUserError(err error) bool {
- if cErr, ok := err.(commandError); ok && cErr.isUserError() {
- return true
- }
-
- return userErrorRegexp.MatchString(err.Error())
-}
diff --git a/commands/hugo.go b/commands/hugo.go
deleted file mode 100644
index 3da059cc5..000000000
--- a/commands/hugo.go
+++ /dev/null
@@ -1,1169 +0,0 @@
-// 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 commands defines and implements command-line commands and flags
-// used by Hugo. Commands and flags are implemented using Cobra.
-package commands
-
-import (
- "context"
- "fmt"
- "io/ioutil"
- "os/signal"
- "runtime/pprof"
- "runtime/trace"
- "sync/atomic"
-
- "github.com/gohugoio/hugo/hugofs"
-
- "github.com/gohugoio/hugo/resources/page"
-
- "github.com/pkg/errors"
-
- "github.com/gohugoio/hugo/common/herrors"
- "github.com/gohugoio/hugo/common/loggers"
- "github.com/gohugoio/hugo/common/terminal"
-
- "syscall"
-
- "github.com/gohugoio/hugo/hugolib/filesystems"
-
- "golang.org/x/sync/errgroup"
-
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/config"
-
- flag "github.com/spf13/pflag"
-
- "github.com/fsnotify/fsnotify"
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugolib"
- "github.com/gohugoio/hugo/livereload"
- "github.com/gohugoio/hugo/watcher"
- "github.com/spf13/afero"
- "github.com/spf13/cobra"
- "github.com/spf13/fsync"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-// The Response value from Execute.
-type Response struct {
- // The build Result will only be set in the hugo build command.
- Result *hugolib.HugoSites
-
- // Err is set when the command failed to execute.
- Err error
-
- // The command that was executed.
- Cmd *cobra.Command
-}
-
-// IsUserError returns true is the Response error is a user error rather than a
-// system error.
-func (r Response) IsUserError() bool {
- return r.Err != nil && isUserError(r.Err)
-}
-
-// Execute adds all child commands to the root command HugoCmd and sets flags appropriately.
-// The args are usually filled with os.Args[1:].
-func Execute(args []string) Response {
- hugoCmd := newCommandsBuilder().addAll().build()
- cmd := hugoCmd.getCommand()
- cmd.SetArgs(args)
-
- c, err := cmd.ExecuteC()
-
- var resp Response
-
- if c == cmd && hugoCmd.c != nil {
- // Root command executed
- resp.Result = hugoCmd.c.hugo()
- }
-
- if err == nil {
- errCount := int(loggers.GlobalErrorCounter.Count())
- if errCount > 0 {
- err = fmt.Errorf("logged %d errors", errCount)
- } else if resp.Result != nil {
- errCount = resp.Result.NumLogErrors()
- if errCount > 0 {
- err = fmt.Errorf("logged %d errors", errCount)
- }
- }
-
- }
-
- resp.Err = err
- resp.Cmd = c
-
- return resp
-}
-
-// InitializeConfig initializes a config file with sensible default configuration flags.
-func initializeConfig(mustHaveConfigFile, running bool,
- h *hugoBuilderCommon,
- f flagsToConfigHandler,
- doWithCommandeer func(c *commandeer) error) (*commandeer, error) {
-
- c, err := newCommandeer(mustHaveConfigFile, running, h, f, doWithCommandeer)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-
-}
-
-func (c *commandeer) createLogger(cfg config.Provider, running bool) (*loggers.Logger, error) {
- var (
- logHandle = ioutil.Discard
- logThreshold = jww.LevelWarn
- logFile = cfg.GetString("logFile")
- outHandle = os.Stdout
- stdoutThreshold = jww.LevelWarn
- )
-
- if c.h.verboseLog || c.h.logging || (c.h.logFile != "") {
- var err error
- if logFile != "" {
- logHandle, err = os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
- if err != nil {
- return nil, newSystemError("Failed to open log file:", logFile, err)
- }
- } else {
- logHandle, err = ioutil.TempFile("", "hugo")
- if err != nil {
- return nil, newSystemError(err)
- }
- }
- } else if !c.h.quiet && cfg.GetBool("verbose") {
- stdoutThreshold = jww.LevelInfo
- }
-
- if cfg.GetBool("debug") {
- stdoutThreshold = jww.LevelDebug
- }
-
- if c.h.verboseLog {
- logThreshold = jww.LevelInfo
- if cfg.GetBool("debug") {
- logThreshold = jww.LevelDebug
- }
- }
-
- loggers.InitGlobalLogger(stdoutThreshold, logThreshold, outHandle, logHandle)
- helpers.InitLoggers()
-
- return loggers.NewLogger(stdoutThreshold, logThreshold, outHandle, logHandle, running), nil
-}
-
-func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
- persFlagKeys := []string{
- "debug",
- "verbose",
- "logFile",
- // Moved from vars
- }
- flagKeys := []string{
- "cleanDestinationDir",
- "buildDrafts",
- "buildFuture",
- "buildExpired",
- "uglyURLs",
- "canonifyURLs",
- "enableRobotsTXT",
- "enableGitInfo",
- "pluralizeListTitles",
- "preserveTaxonomyNames",
- "ignoreCache",
- "forceSyncStatic",
- "noTimes",
- "noChmod",
- "ignoreVendor",
- "templateMetrics",
- "templateMetricsHints",
-
- // Moved from vars.
- "baseURL",
- "buildWatch",
- "cacheDir",
- "cfgFile",
- "confirm",
- "contentDir",
- "debug",
- "destination",
- "disableKinds",
- "dryRun",
- "force",
- "gc",
- "i18n-warnings",
- "invalidateCDN",
- "layoutDir",
- "logFile",
- "maxDeletes",
- "quiet",
- "renderToMemory",
- "source",
- "target",
- "theme",
- "themesDir",
- "verbose",
- "verboseLog",
- "duplicateTargetPaths",
- }
-
- // Will set a value even if it is the default.
- flagKeysForced := []string{
- "minify",
- }
-
- for _, key := range persFlagKeys {
- setValueFromFlag(cmd.PersistentFlags(), key, cfg, "", false)
- }
- for _, key := range flagKeys {
- setValueFromFlag(cmd.Flags(), key, cfg, "", false)
- }
-
- for _, key := range flagKeysForced {
- setValueFromFlag(cmd.Flags(), key, cfg, "", true)
- }
-
- // Set some "config aliases"
- setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false)
- setValueFromFlag(cmd.Flags(), "i18n-warnings", cfg, "logI18nWarnings", false)
- setValueFromFlag(cmd.Flags(), "path-warnings", cfg, "logPathWarnings", false)
-
-}
-
-func setValueFromFlag(flags *flag.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 isTerminal() bool {
- return terminal.IsTerminal(os.Stdout)
-
-}
-func ifTerminal(s string) string {
- if !isTerminal() {
- return ""
- }
- return s
-}
-
-func (c *commandeer) fullBuild() error {
-
- var (
- g errgroup.Group
- langCount map[string]uint64
- )
-
- if !c.h.quiet {
- fmt.Print(ifTerminal(hideCursor) + "Building sites … ")
- if isTerminal() {
- defer func() {
- fmt.Print(showCursor + clearLine)
- }()
- }
- }
-
- copyStaticFunc := func() error {
-
- cnt, err := c.copyStatic()
- if err != nil {
- return errors.Wrap(err, "Error copying static files")
- }
- langCount = cnt
- return nil
- }
- buildSitesFunc := func() error {
- if err := c.buildSites(); err != nil {
- return errors.Wrap(err, "Error building site")
- }
- 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.
- if c.Cfg.GetBool("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
- }
- }
-
- for _, s := range c.hugo().Sites {
- s.ProcessingStats.Static = langCount[s.Language().Lang]
- }
-
- if c.h.gc {
- count, err := c.hugo().GC()
- if err != nil {
- return err
- }
- for _, s := range c.hugo().Sites {
- // We have no way of knowing what site the garbage belonged to.
- s.ProcessingStats.Cleaned = uint64(count)
- }
- }
-
- return nil
-
-}
-
-func (c *commandeer) initCPUProfile() (func(), error) {
- if c.h.cpuprofile == "" {
- return nil, nil
- }
-
- f, err := os.Create(c.h.cpuprofile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create CPU profile")
- }
- if err := pprof.StartCPUProfile(f); err != nil {
- return nil, errors.Wrap(err, "failed to start CPU profile")
- }
- return func() {
- pprof.StopCPUProfile()
- f.Close()
- }, nil
-}
-
-func (c *commandeer) initMemProfile() {
- if c.h.memprofile == "" {
- return
- }
-
- f, err := os.Create(c.h.memprofile)
- if err != nil {
- c.logger.ERROR.Println("could not create memory profile: ", err)
- }
- defer f.Close()
- runtime.GC() // get up-to-date statistics
- if err := pprof.WriteHeapProfile(f); err != nil {
- c.logger.ERROR.Println("could not write memory profile: ", err)
- }
-}
-
-func (c *commandeer) initTraceProfile() (func(), error) {
- if c.h.traceprofile == "" {
- return nil, nil
- }
-
- f, err := os.Create(c.h.traceprofile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create trace file")
- }
-
- if err := trace.Start(f); err != nil {
- return nil, errors.Wrap(err, "failed to start trace")
- }
-
- return func() {
- trace.Stop()
- f.Close()
- }, nil
-}
-
-func (c *commandeer) initMutexProfile() (func(), error) {
- if c.h.mutexprofile == "" {
- return nil, nil
- }
-
- f, err := os.Create(c.h.mutexprofile)
- if err != nil {
- return nil, err
- }
-
- runtime.SetMutexProfileFraction(1)
-
- return func() {
- pprof.Lookup("mutex").WriteTo(f, 0)
- f.Close()
- }, nil
-
-}
-
-func (c *commandeer) 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
- }
-
- return func() {
- c.initMemProfile()
-
- if stopCPUProf != nil {
- stopCPUProf()
- }
- if stopMutexProf != nil {
- stopMutexProf()
- }
-
- if stopTraceProf != nil {
- stopTraceProf()
- }
- }, nil
-}
-
-func (c *commandeer) build() error {
- defer c.timeTrack(time.Now(), "Total")
-
- stopProfiling, err := c.initProfiling()
- if err != nil {
- return err
- }
-
- defer func() {
- if stopProfiling != nil {
- stopProfiling()
- }
- }()
-
- if err := c.fullBuild(); err != nil {
- return err
- }
-
- // TODO(bep) Feedback?
- if !c.h.quiet {
- fmt.Println()
- c.hugo().PrintProcessingStats(os.Stdout)
- fmt.Println()
-
- if createCounter, ok := c.destinationFs.(hugofs.DuplicatesReporter); ok {
- dupes := createCounter.ReportDuplicates()
- if dupes != "" {
- c.logger.WARN.Println("Duplicate target paths:", dupes)
- }
- }
- }
-
- if c.h.buildWatch {
- watchDirs, err := c.getDirList()
- if err != nil {
- return err
- }
-
- baseWatchDir := c.Cfg.GetString("workingDir")
- rootWatchDirs := getRootWatchDirsStr(baseWatchDir, watchDirs)
-
- c.logger.FEEDBACK.Printf("Watching for changes in %s%s{%s}\n", baseWatchDir, helpers.FilePathSeparator, rootWatchDirs)
- c.logger.FEEDBACK.Println("Press Ctrl+C to stop")
- watcher, err := c.newWatcher(watchDirs...)
- checkErr(c.Logger, err)
- defer watcher.Close()
-
- var sigs = make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
-
- <-sigs
- }
-
- return nil
-}
-
-func (c *commandeer) serverBuild() error {
- defer c.timeTrack(time.Now(), "Total")
-
- stopProfiling, err := c.initProfiling()
- if err != nil {
- return err
- }
-
- defer func() {
- if stopProfiling != nil {
- stopProfiling()
- }
- }()
-
- if err := c.fullBuild(); err != nil {
- return err
- }
-
- // TODO(bep) Feedback?
- if !c.h.quiet {
- fmt.Println()
- c.hugo().PrintProcessingStats(os.Stdout)
- fmt.Println()
- }
-
- return nil
-}
-
-func (c *commandeer) copyStatic() (map[string]uint64, error) {
- m, err := c.doWithPublishDirs(c.copyStaticTo)
- if err == nil || os.IsNotExist(err) {
- return m, nil
- }
- return m, err
-}
-
-func (c *commandeer) doWithPublishDirs(f func(sourceFs *filesystems.SourceFilesystem) (uint64, error)) (map[string]uint64, error) {
-
- langCount := make(map[string]uint64)
-
- staticFilesystems := c.hugo().BaseFs.SourceFilesystems.Static
-
- if len(staticFilesystems) == 0 {
- c.logger.INFO.Println("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
- for _, l := range c.languages {
- langCount[l.Lang] = cnt
- }
- } else {
- langCount[lang] = cnt
- }
- }
-
- return langCount, nil
-}
-
-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
-}
-
-func chmodFilter(dst, src os.FileInfo) bool {
- // Hugo publishes data from multiple sources, potentially
- // with overlapping directory structures. We cannot sync permissions
- // for directories as that would mean that we might end up with write-protected
- // directories inside /public.
- // One example of this would be syncing from the Go Module cache,
- // which have 0555 directories.
- return src.IsDir()
-}
-
-func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
- publishDir := c.hugo().PathSpec.PublishDir
- // If root, remove the second '/'
- if publishDir == "//" {
- publishDir = helpers.FilePathSeparator
- }
-
- if sourceFs.PublishFolder != "" {
- publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
- }
-
- fs := &countingStatFs{Fs: sourceFs.Fs}
-
- syncer := fsync.NewSyncer()
- syncer.NoTimes = c.Cfg.GetBool("noTimes")
- syncer.NoChmod = c.Cfg.GetBool("noChmod")
- syncer.ChmodFilter = chmodFilter
- syncer.SrcFs = fs
- syncer.DestFs = c.Fs.Destination
- // Now that we are using a unionFs for the static directories
- // We can effectively clean the publishDir on initial sync
- syncer.Delete = c.Cfg.GetBool("cleanDestinationDir")
-
- if syncer.Delete {
- c.logger.INFO.Println("removing all files from destination that don't exist in static dirs")
-
- syncer.DeleteFilter = func(f os.FileInfo) bool {
- return f.IsDir() && strings.HasPrefix(f.Name(), ".")
- }
- }
- c.logger.INFO.Println("syncing static files to", publishDir)
-
- // 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
- }
-
- // Sync runs Stat 3 times for every source file (which sounds much)
- numFiles := fs.statCounter / 3
-
- return numFiles, err
-}
-
-func (c *commandeer) firstPathSpec() *helpers.PathSpec {
- return c.hugo().Sites[0].PathSpec
-}
-
-func (c *commandeer) timeTrack(start time.Time, name string) {
- if c.h.quiet {
- return
- }
- elapsed := time.Since(start)
- c.logger.FEEDBACK.Printf("%s in %v ms", name, int(1000*elapsed.Seconds()))
-}
-
-// getDirList provides NewWatcher() with a list of directories to watch for changes.
-func (c *commandeer) getDirList() ([]string, error) {
- var dirnames []string
-
- walkFn := func(path string, fi hugofs.FileMetaInfo, err error) error {
- if err != nil {
- c.logger.ERROR.Println("walker: ", err)
- return nil
- }
-
- if fi.IsDir() {
- if fi.Name() == ".git" ||
- fi.Name() == "node_modules" || fi.Name() == "bower_components" {
- return filepath.SkipDir
- }
-
- dirnames = append(dirnames, fi.Meta().Filename())
- }
-
- return nil
-
- }
-
- watchDirs := c.hugo().PathSpec.BaseFs.WatchDirs()
- for _, watchDir := range watchDirs {
-
- w := hugofs.NewWalkway(hugofs.WalkwayConfig{Logger: c.logger, Info: watchDir, WalkFn: walkFn})
- if err := w.Walk(); err != nil {
- c.logger.ERROR.Println("walker: ", err)
- }
- }
-
- dirnames = helpers.UniqueStringsSorted(dirnames)
-
- return dirnames, nil
-}
-
-func (c *commandeer) buildSites() (err error) {
- return c.hugo().Build(hugolib.BuildCfg{})
-}
-
-func (c *commandeer) handleBuildErr(err error, msg string) {
- c.buildErr = err
-
- c.logger.ERROR.Print(msg + ":\n\n")
- c.logger.ERROR.Println(helpers.FirstUpper(err.Error()))
- if !c.h.quiet && c.h.verbose {
- herrors.PrintStackTrace(err)
- }
-}
-
-func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
- defer c.timeTrack(time.Now(), "Total")
-
- c.buildErr = nil
- visited := c.visitedURLs.PeekAllSet()
- if c.fastRenderMode {
-
- // Make sure we always render the home pages
- for _, l := range c.languages {
- langPath := c.hugo().PathSpec.GetLangSubDir(l.Lang)
- if langPath != "" {
- langPath = langPath + "/"
- }
- home := c.hugo().PathSpec.PrependBasePath("/"+langPath, false)
- visited[home] = true
- }
-
- }
- return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited}, events...)
-}
-
-func (c *commandeer) partialReRender(urls ...string) error {
- c.buildErr = nil
- visited := make(map[string]bool)
- for _, url := range urls {
- visited[url] = true
- }
- return c.hugo().Build(hugolib.BuildCfg{RecentlyVisited: visited, PartialReRender: true})
-}
-
-func (c *commandeer) 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 back.
- // This will block any rebuild on config changes for the
- // duration of the sleep.
- time.Sleep(2 * time.Second)
- }()
-
- defer c.timeTrack(time.Now(), "Total")
-
- c.commandeerHugoState = newCommandeerHugoState()
- err := c.loadConfig(true, true)
- if err != nil {
- // Set the processing on pause until the state is recovered.
- c.paused = true
- c.handleBuildErr(err, "Failed to reload config")
-
- } else {
- c.paused = false
- }
-
- if !c.paused {
- _, err := c.copyStatic()
- if err != nil {
- c.logger.ERROR.Println(err)
- return
- }
-
- err = c.buildSites()
- if err != nil {
- c.logger.ERROR.Println(err)
- } else if !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload") {
- livereload.ForceRefresh()
- }
- }
- }()
-}
-
-// newWatcher creates a new watcher to watch filesystem events.
-func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
- if runtime.GOOS == "darwin" {
- tweakLimit()
- }
-
- staticSyncer, err := newStaticSyncer(c)
- if err != nil {
- return nil, err
- }
-
- watcher, err := watcher.New(1 * time.Second)
-
- if err != nil {
- return nil, err
- }
-
- for _, d := range dirList {
- if d != "" {
- _ = watcher.Add(d)
- }
- }
-
- // Identifies changes to config (config.toml) files.
- configSet := make(map[string]bool)
-
- c.logger.FEEDBACK.Println("Watching for config changes in", strings.Join(c.configFiles, ", "))
- for _, configFile := range c.configFiles {
- watcher.Add(configFile)
- configSet[configFile] = true
- }
-
- go func() {
- for {
- select {
- case evs := <-watcher.Events:
- c.handleEvents(watcher, staticSyncer, evs, configSet)
- if c.showErrorInBrowser && c.errCount() > 0 {
- // Need to reload browser to show the error
- livereload.ForceRefresh()
- }
- case err := <-watcher.Errors:
- if err != nil {
- c.logger.ERROR.Println("Error while watching:", err)
- }
- }
- }
- }()
-
- return watcher, nil
-}
-
-func (c *commandeer) printChangeDetected(typ string) {
- msg := "\nChange"
- if typ != "" {
- msg += " of " + typ
- }
- msg += " detected, rebuilding site."
-
- c.logger.FEEDBACK.Println(msg)
- const layout = "2006-01-02 15:04:05.000 -0700"
- c.logger.FEEDBACK.Println(time.Now().Format(layout))
-}
-
-const (
- configChangeConfig = "config file"
- configChangeGoMod = "go.mod file"
-)
-
-func (c *commandeer) handleEvents(watcher *watcher.Batcher,
- staticSyncer *staticSyncer,
- evs []fsnotify.Event,
- configSet map[string]bool) {
-
- var isHandled bool
-
- for _, ev := range evs {
- isConfig := configSet[ev.Name]
- configChangeType := configChangeConfig
- if isConfig {
- if strings.Contains(ev.Name, "go.mod") {
- configChangeType = configChangeGoMod
- }
- }
- 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 {
- for _, configFile := range c.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.paused {
- // 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.logger.INFO.Println("Received System Events:", evs)
-
- staticEvents := []fsnotify.Event{}
- dynamicEvents := []fsnotify.Event{}
-
- // Special handling for symbolic links inside /content.
- filtered := []fsnotify.Event{}
- for _, ev := range evs {
- // Check the most specific first, i.e. files.
- contentMapped := c.hugo().ContentChanges.GetSymbolicLinkMappings(ev.Name)
- if len(contentMapped) > 0 {
- for _, mapped := range contentMapped {
- filtered = append(filtered, fsnotify.Event{Name: mapped, Op: ev.Op})
- }
- continue
- }
-
- // Check for any symbolic directory mapping.
-
- dir, name := filepath.Split(ev.Name)
-
- contentMapped = c.hugo().ContentChanges.GetSymbolicLinkMappings(dir)
-
- if len(contentMapped) == 0 {
- filtered = append(filtered, ev)
- continue
- }
-
- for _, mapped := range contentMapped {
- mappedFilename := filepath.Join(mapped, name)
- filtered = append(filtered, fsnotify.Event{Name: mappedFilename, Op: ev.Op})
- }
- }
-
- evs = filtered
-
- 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 == ".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 c.hugo().Deps.SourceSpec.IgnoreFile(ev.Name) {
- continue
- }
- // Sometimes during rm -rf operations a '"": REMOVE' is triggered. Just ignore these
- if ev.Name == "" {
- continue
- }
-
- // 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|fsnotify.Write|fsnotify.Create) == fsnotify.Chmod {
- continue
- }
-
- walkAdder := func(path string, f hugofs.FileMetaInfo, err error) error {
- if f.IsDir() {
- c.logger.FEEDBACK.Println("adding created directory to watchlist", path)
- if err := watcher.Add(path); err != nil {
- return err
- }
- } else if !staticSyncer.isStatic(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
- // When mkdir -p is used, only the top directory triggers an event (at least on OSX)
- if ev.Op&fsnotify.Create == fsnotify.Create {
- if s, err := c.Fs.Source.Stat(ev.Name); err == nil && s.Mode().IsDir() {
- _ = helpers.SymbolicWalk(c.Fs.Source, ev.Name, walkAdder)
- }
- }
-
- if staticSyncer.isStatic(ev.Name) {
- staticEvents = append(staticEvents, ev)
- } else {
- dynamicEvents = append(dynamicEvents, ev)
- }
- }
-
- if len(staticEvents) > 0 {
- c.printChangeDetected("Static files")
-
- if c.Cfg.GetBool("forceSyncStatic") {
- c.logger.FEEDBACK.Printf("Syncing all static files\n")
- _, err := c.copyStatic()
- if err != nil {
- c.logger.ERROR.Println("Error copying static files to publish dir:", err)
- return
- }
- } else {
- if err := staticSyncer.syncsStaticEvents(staticEvents); err != nil {
- c.logger.ERROR.Println("Error syncing static files to publish dir:", err)
- return
- }
- }
-
- if !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload") {
- // Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
-
- // force refresh when more than one file
- if len(staticEvents) == 1 {
- ev := staticEvents[0]
- path := c.hugo().BaseFs.SourceFilesystems.MakeStaticPathRelative(ev.Name)
- path = c.firstPathSpec().RelURL(helpers.ToSlashTrimLeading(path), false)
- livereload.RefreshPath(path)
- } else {
- livereload.ForceRefresh()
- }
- }
- }
-
- if len(dynamicEvents) > 0 {
- partitionedEvents := partitionDynamicEvents(
- c.firstPathSpec().BaseFs.SourceFilesystems,
- dynamicEvents)
-
- doLiveReload := !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload")
- onePageName := pickOneWriteOrCreatePath(partitionedEvents.ContentEvents)
-
- c.printChangeDetected("")
- c.changeDetector.PrepareNew()
- if err := c.rebuildSites(dynamicEvents); err != nil {
- c.handleBuildErr(err, "Rebuild failed")
- }
-
- if doLiveReload {
- if len(partitionedEvents.ContentEvents) == 0 && len(partitionedEvents.AssetEvents) > 0 {
- changed := c.changeDetector.changed()
- if c.changeDetector != nil && len(changed) == 0 {
- // Nothing has changed.
- return
- } else if len(changed) == 1 {
- pathToRefresh := c.firstPathSpec().RelURL(helpers.ToSlashTrimLeading(changed[0]), false)
- livereload.RefreshPath(pathToRefresh)
- } else {
- livereload.ForceRefresh()
- }
- }
-
- if len(partitionedEvents.ContentEvents) > 0 {
-
- navigate := c.Cfg.GetBool("navigateToChanged")
- // We have fetched the same page above, but it may have
- // changed.
- var p page.Page
-
- if navigate {
- if onePageName != "" {
- p = c.hugo().GetContentPage(onePageName)
- }
- }
-
- if p != nil {
- livereload.NavigateToPathForPort(p.RelPermalink(), p.Site().ServerPort())
- } else {
- livereload.ForceRefresh()
- }
- }
- }
- }
-}
-
-// 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
-}
-
-func partitionDynamicEvents(sourceFs *filesystems.SourceFilesystems, events []fsnotify.Event) (de dynamicEvents) {
- for _, e := range events {
- if sourceFs.IsAsset(e.Name) {
- de.AssetEvents = append(de.AssetEvents, e)
- } else {
- de.ContentEvents = append(de.ContentEvents, e)
- }
- }
- return
-
-}
-
-func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
- name := ""
-
- // Some editors (for example notepad.exe on Windows) triggers a change
- // both for directory and file. So we pick the longest path, which should
- // be the file itself.
- for _, ev := range events {
- if (ev.Op&fsnotify.Write == fsnotify.Write || ev.Op&fsnotify.Create == fsnotify.Create) && len(ev.Name) > len(name) {
- name = ev.Name
- }
- }
-
- return name
-}
diff --git a/commands/hugo_test.go b/commands/hugo_test.go
deleted file mode 100644
index 6a666ff4a..000000000
--- a/commands/hugo_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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 commands
-
-import (
- "os"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-// Issue #5662
-func TestHugoWithContentDirOverride(t *testing.T) {
- c := qt.New(t)
-
- hugoCmd := newCommandsBuilder().addAll().build()
- cmd := hugoCmd.getCommand()
-
- contentDir := "contentOverride"
-
- cfgStr := `
-
-baseURL = "https://example.org"
-title = "Hugo Commands"
-
-contentDir = "thisdoesnotexist"
-
-`
- dir, err := createSimpleTestSite(t, testSiteConfig{configTOML: cfgStr, contentDir: contentDir})
- c.Assert(err, qt.IsNil)
-
- defer func() {
- os.RemoveAll(dir)
- }()
-
- cmd.SetArgs([]string{"-s=" + dir, "-c=" + contentDir})
-
- _, err = cmd.ExecuteC()
- c.Assert(err, qt.IsNil)
-
-}
diff --git a/commands/hugo_windows.go b/commands/hugo_windows.go
deleted file mode 100644
index 106c0cc71..000000000
--- a/commands/hugo_windows.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 commands
-
-import "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 website.
-
- You need to open cmd.exe and run Hugo from there.
-
- Visit https://gohugo.io/ for more information.`
-}
diff --git a/commands/import_jekyll.go b/commands/import_jekyll.go
deleted file mode 100644
index e5c39dc34..000000000
--- a/commands/import_jekyll.go
+++ /dev/null
@@ -1,596 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "time"
- "unicode"
-
- "github.com/gohugoio/hugo/common/hugio"
-
- "github.com/gohugoio/hugo/parser/metadecoders"
-
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/hugolib"
- "github.com/gohugoio/hugo/parser"
- "github.com/spf13/afero"
- "github.com/spf13/cast"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*importCmd)(nil)
-
-type importCmd struct {
- *baseCmd
-}
-
-func newImportCmd() *importCmd {
- cc := &importCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "import",
- Short: "Import your site from others.",
- Long: `Import your site from other web site generators like Jekyll.
-
-Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
- RunE: nil,
- })
-
- importJekyllCmd := &cobra.Command{
- Use: "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`.",
- RunE: cc.importFromJekyll,
- }
-
- importJekyllCmd.Flags().Bool("force", false, "allow import into non-empty target directory")
-
- cc.cmd.AddCommand(importJekyllCmd)
-
- return cc
-
-}
-
-func (i *importCmd) importFromJekyll(cmd *cobra.Command, 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`.")
- }
-
- 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])
- }
-
- jww.INFO.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")
- }
-
- forceImport, _ := cmd.Flags().GetBool("force")
-
- fs := afero.NewOsFs()
- jekyllPostDirs, hasAnyPost := i.getJekyllDirInfo(fs, jekyllRoot)
- if !hasAnyPost {
- return errors.New("abort: jekyll root contains neither posts nor drafts")
- }
-
- site, err := i.createSiteFromJekyll(jekyllRoot, targetDir, jekyllPostDirs, forceImport)
-
- if err != nil {
- return newUserError(err)
- }
-
- jww.FEEDBACK.Println("Importing...")
-
- fileCount := 0
- callback := func(path string, fi hugofs.FileMetaInfo, err error) error {
- if err != nil {
- return err
- }
-
- 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 convertJekyllPost(site, path, relPath, targetDir, draft)
- }
-
- for jekyllPostDir, hasAnyPostInDir := range jekyllPostDirs {
- if hasAnyPostInDir {
- if err = helpers.SymbolicWalk(hugofs.Os, filepath.Join(jekyllRoot, jekyllPostDir), callback); err != nil {
- return err
- }
- }
- }
-
- jww.FEEDBACK.Println("Congratulations!", fileCount, "post(s) imported!")
- jww.FEEDBACK.Println("Now, start Hugo by yourself:\n" +
- "$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
- jww.FEEDBACK.Println("$ cd " + args[1] + "\n$ hugo server --theme=herring-cove")
-
- return nil
-}
-
-func (i *importCmd) getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[string]bool, bool) {
- postDirs := make(map[string]bool)
- hasAnyPost := false
- if entries, err := ioutil.ReadDir(jekyllRoot); err == nil {
- for _, entry := range entries {
- if entry.IsDir() {
- subDir := filepath.Join(jekyllRoot, entry.Name())
- if isPostDir, hasAnyPostInDir := i.retrieveJekyllPostDir(fs, subDir); isPostDir {
- postDirs[entry.Name()] = hasAnyPostInDir
- if hasAnyPostInDir {
- hasAnyPost = true
- }
- }
- }
- }
- }
- return postDirs, hasAnyPost
-}
-
-func (i *importCmd) 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 := ioutil.ReadDir(dir); err == nil {
- for _, entry := range entries {
- if entry.IsDir() {
- subDir := filepath.Join(dir, entry.Name())
- if isPostDir, hasAnyPost := i.retrieveJekyllPostDir(fs, subDir); isPostDir {
- return isPostDir, hasAnyPost
- }
- }
- }
- }
-
- return false, true
-}
-
-func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool, force bool) (*hugolib.Site, error) {
- s, err := hugolib.NewSiteDefaultLang()
- if err != nil {
- return nil, err
- }
-
- fs := s.Fs.Source
- if exists, _ := helpers.Exists(targetDir, fs); exists {
- if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
- return nil, errors.New("target path \"" + targetDir + "\" exists but is not a directory")
- }
-
- isEmpty, _ := helpers.IsEmpty(targetDir, fs)
-
- if !isEmpty && !force {
- return nil, errors.New("target path \"" + targetDir + "\" exists and is not empty")
- }
- }
-
- jekyllConfig := i.loadJekyllConfig(fs, jekyllRoot)
-
- mkdir(targetDir, "layouts")
- mkdir(targetDir, "content")
- mkdir(targetDir, "archetypes")
- mkdir(targetDir, "static")
- mkdir(targetDir, "data")
- mkdir(targetDir, "themes")
-
- i.createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig)
-
- i.copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"), jekyllPostDirs)
-
- return s, nil
-}
-
-func (i *importCmd) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
- path := filepath.Join(jekyllRoot, "_config.yml")
-
- exists, err := helpers.Exists(path, fs)
-
- if err != nil || !exists {
- jww.WARN.Println("_config.yaml not found: Is the specified Jekyll root correct?")
- return nil
- }
-
- f, err := fs.Open(path)
- if err != nil {
- return nil
- }
-
- defer f.Close()
-
- b, err := ioutil.ReadAll(f)
-
- if err != nil {
- return nil
- }
-
- c, err := metadecoders.Default.UnmarshalToMap(b, metadecoders.YAML)
-
- if err != nil {
- return nil
- }
-
- return c
-}
-
-func (i *importCmd) createConfigFromJekyll(fs afero.Fs, inpath string, kind metadecoders.Format, jekyllConfig map[string]interface{}) (err error) {
- title := "My New Hugo Site"
- 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]interface{}{
- "baseURL": baseURL,
- "title": title,
- "languageCode": "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, "config."+string(kind)), &buf, fs)
-}
-
-func (i *importCmd) 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 := ioutil.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 {
- jww.ERROR.Println(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 {
- jww.ERROR.Println(err)
- }
- }
- }
-
- }
- return nil
-}
-
-func parseJekyllFilename(filename string) (time.Time, string, error) {
- re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
- r := re.FindAllStringSubmatch(filename, -1)
- if len(r) == 0 {
- return time.Now(), "", errors.New("filename not match")
- }
-
- postDate, err := time.Parse("2006-1-2", r[0][1])
- if err != nil {
- return time.Now(), "", err
- }
-
- postName := r[0][2]
-
- return postDate, postName, nil
-}
-
-func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft bool) error {
- jww.TRACE.Println("Converting", path)
-
- filename := filepath.Base(path)
- postDate, postName, err := parseJekyllFilename(filename)
- if err != nil {
- jww.WARN.Printf("Failed to parse filename '%s': %s. Skipping.", filename, err)
- return nil
- }
-
- jww.TRACE.Println(filename, postDate, postName)
-
- targetFile := filepath.Join(targetDir, relPath)
- targetParentDir := filepath.Dir(targetFile)
- os.MkdirAll(targetParentDir, 0777)
-
- contentBytes, err := ioutil.ReadFile(path)
- if err != nil {
- jww.ERROR.Println("Read file error:", path)
- return err
- }
-
- pf, err := parseContentFile(bytes.NewReader(contentBytes))
- if err != nil {
- jww.ERROR.Println("Parse file error:", path)
- return err
- }
-
- newmetadata, err := convertJekyllMetaData(pf.frontMatter, postName, postDate, draft)
- if err != nil {
- jww.ERROR.Println("Convert metadata error:", path)
- return err
- }
-
- content := convertJekyllContent(newmetadata, string(pf.content))
-
- 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 convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
- metadata, err := cast.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 convertJekyllContent(m interface{}, content string) string {
- metadata, _ := cast.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*%}`), replaceImageTag},
- {regexp.MustCompile(`{%\s*highlight\s*(.*?)\s*%}`), replaceHighlightTag},
- }
-
- for _, replace := range replaceListFunc {
- content = replace.re.ReplaceAllStringFunc(content, replace.replace)
- }
-
- return content
-}
-
-func 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 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
- replaceOptionalPart(result, "class", parts[1])
- replaceOptionalPart(result, "src", parts[2])
- replaceOptionalPart(result, "width", parts[3])
- replaceOptionalPart(result, "height", parts[4])
- // title + alt
- part := parts[5]
- if len(part) > 0 {
- splits := strings.Split(part, "'")
- lenSplits := len(splits)
- if lenSplits == 1 {
- replaceOptionalPart(result, "title", splits[0])
- } else if lenSplits == 3 {
- replaceOptionalPart(result, "title", splits[1])
- } else if lenSplits == 5 {
- replaceOptionalPart(result, "title", splits[1])
- replaceOptionalPart(result, "alt", splits[3])
- }
- }
- result.WriteString(">}}")
- return result.String()
-
-}
-func replaceOptionalPart(buffer *bytes.Buffer, partName string, part string) {
- if len(part) > 0 {
- buffer.WriteString(partName + "=\"" + part + "\" ")
- }
-}
diff --git a/commands/import_jekyll_test.go b/commands/import_jekyll_test.go
deleted file mode 100644
index 4ae26b95c..000000000
--- a/commands/import_jekyll_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// 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 commands
-
-import (
- "encoding/json"
- "testing"
- "time"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestParseJekyllFilename(t *testing.T) {
- c := qt.New(t)
- filenameArray := []string{
- "2015-01-02-test.md",
- "2012-03-15-中文.markup",
- }
-
- expectResult := []struct {
- postDate time.Time
- postName string
- }{
- {time.Date(2015, time.January, 2, 0, 0, 0, 0, time.UTC), "test"},
- {time.Date(2012, time.March, 15, 0, 0, 0, 0, time.UTC), "中文"},
- }
-
- for i, filename := range filenameArray {
- postDate, postName, err := parseJekyllFilename(filename)
- c.Assert(err, qt.IsNil)
- c.Assert(expectResult[i].postDate.Format("2006-01-02"), qt.Equals, postDate.Format("2006-01-02"))
- c.Assert(expectResult[i].postName, qt.Equals, postName)
- }
-}
-
-func TestConvertJekyllMetadata(t *testing.T) {
- c := qt.New(t)
- testDataList := []struct {
- metadata interface{}
- postName string
- postDate time.Time
- draft bool
- expect string
- }{
- {map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"date":"2015-10-01T00:00:00Z"}`},
- {map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), true,
- `{"date":"2015-10-01T00:00:00Z","draft":true}`},
- {map[interface{}]interface{}{"Permalink": "/permalink.html", "layout": "post"},
- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},
- {map[interface{}]interface{}{"permalink": "/permalink.html"},
- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},
- {map[interface{}]interface{}{"category": nil, "permalink": 123},
- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"date":"2015-10-01T00:00:00Z"}`},
- {map[interface{}]interface{}{"Excerpt_Separator": "sep"},
- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep"}`},
- {map[interface{}]interface{}{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},
- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
- `{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z"}`},
- }
-
- for _, data := range testDataList {
- result, err := convertJekyllMetaData(data.metadata, data.postName, data.postDate, data.draft)
- c.Assert(err, qt.IsNil)
- jsonResult, err := json.Marshal(result)
- c.Assert(err, qt.IsNil)
- c.Assert(string(jsonResult), qt.Equals, data.expect)
- }
-}
-
-func TestConvertJekyllContent(t *testing.T) {
- c := qt.New(t)
- testDataList := []struct {
- metadata interface{}
- content string
- expect string
- }{
- {map[interface{}]interface{}{},
- `Test content\n\npart2 content`, `Test content\n\npart2 content`},
- {map[interface{}]interface{}{},
- `Test content\n\npart2 content`, `Test content\n\npart2 content`},
- {map[interface{}]interface{}{"excerpt_separator": ""},
- `Test content\n\npart2 content`, `Test content\n\npart2 content`},
- {map[interface{}]interface{}{}, "{% raw %}text{% endraw %}", "text"},
- {map[interface{}]interface{}{}, "{%raw%} text2 {%endraw %}", "text2"},
- {map[interface{}]interface{}{},
- "{% highlight go %}\nvar s int\n{% endhighlight %}",
- "{{< highlight go >}}\nvar s int\n{{< / highlight >}}"},
- {map[interface{}]interface{}{},
- "{% highlight go linenos hl_lines=\"1 2\" %}\nvar s string\nvar i int\n{% endhighlight %}",
- "{{< highlight go \"linenos=table,hl_lines=1 2\" >}}\nvar s string\nvar i int\n{{< / highlight >}}"},
-
- // Octopress image tag
- {map[interface{}]interface{}{},
- "{% img http://placekitten.com/890/280 %}",
- "{{< figure src=\"http://placekitten.com/890/280\" >}}"},
- {map[interface{}]interface{}{},
- "{% img left http://placekitten.com/320/250 Place Kitten #2 %}",
- "{{< figure class=\"left\" src=\"http://placekitten.com/320/250\" title=\"Place Kitten #2\" >}}"},
- {map[interface{}]interface{}{},
- "{% img right http://placekitten.com/300/500 150 250 'Place Kitten #3' %}",
- "{{< figure class=\"right\" src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #3\" >}}"},
- {map[interface{}]interface{}{},
- "{% img right http://placekitten.com/300/500 150 250 'Place Kitten #4' 'An image of a very cute kitten' %}",
- "{{< figure class=\"right\" src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}"},
- {map[interface{}]interface{}{},
- "{% img http://placekitten.com/300/500 150 250 'Place Kitten #4' 'An image of a very cute kitten' %}",
- "{{< figure src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}"},
- {map[interface{}]interface{}{},
- "{% img right /placekitten/300/500 'Place Kitten #4' 'An image of a very cute kitten' %}",
- "{{< figure class=\"right\" src=\"/placekitten/300/500\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}"},
- }
-
- for _, data := range testDataList {
- result := convertJekyllContent(data.metadata, data.content)
- c.Assert(data.expect, qt.Equals, result)
- }
-}
diff --git a/commands/limit_darwin.go b/commands/limit_darwin.go
deleted file mode 100644
index 6799f37b1..000000000
--- a/commands/limit_darwin.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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 commands
-
-import (
- "syscall"
-
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*limitCmd)(nil)
-
-type limitCmd struct {
- *baseCmd
-}
-
-func newLimitCmd() *limitCmd {
- ccmd := &cobra.Command{
- Use: "ulimit",
- Short: "Check system ulimit settings",
- Long: `Hugo will inspect the current ulimit settings on the system.
-This is primarily to ensure that Hugo can watch enough files on some OSs`,
- RunE: func(cmd *cobra.Command, args []string) error {
- var rLimit syscall.Rlimit
- err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- if err != nil {
- return newSystemError("Error Getting rlimit ", err)
- }
-
- jww.FEEDBACK.Println("Current rLimit:", rLimit)
-
- if rLimit.Cur >= newRlimit {
- return nil
- }
-
- jww.FEEDBACK.Println("Attempting to increase limit")
- rLimit.Cur = newRlimit
- err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- if err != nil {
- return newSystemError("Error Setting rLimit ", err)
- }
- err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- if err != nil {
- return newSystemError("Error Getting rLimit ", err)
- }
- jww.FEEDBACK.Println("rLimit after change:", rLimit)
-
- return nil
- },
- }
-
- return &limitCmd{baseCmd: newBaseCmd(ccmd)}
-}
-
-const newRlimit = 10240
-
-func tweakLimit() {
- var rLimit syscall.Rlimit
- err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- if err != nil {
- jww.WARN.Println("Unable to get rlimit:", err)
- return
- }
- if rLimit.Cur < newRlimit {
- rLimit.Cur = newRlimit
- err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- if err != nil {
- // This may not succeed, see https://github.com/golang/go/issues/30401
- jww.INFO.Println("Unable to increase number of open files limit:", err)
- }
- }
-}
diff --git a/commands/limit_others.go b/commands/limit_others.go
deleted file mode 100644
index 8d3e6ad70..000000000
--- a/commands/limit_others.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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.
-
-// +build !darwin
-
-package commands
-
-func tweakLimit() {
- // nothing to do
-}
diff --git a/commands/list.go b/commands/list.go
deleted file mode 100644
index f233ce62c..000000000
--- a/commands/list.go
+++ /dev/null
@@ -1,209 +0,0 @@
-// 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 commands
-
-import (
- "encoding/csv"
- "os"
- "strconv"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/hugolib"
- "github.com/gohugoio/hugo/resources/resource"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*listCmd)(nil)
-
-type listCmd struct {
- hugoBuilderCommon
- *baseCmd
-}
-
-func (lc *listCmd) buildSites(config map[string]interface{}) (*hugolib.HugoSites, error) {
- cfgInit := func(c *commandeer) error {
- for key, value := range config {
- c.Set(key, value)
- }
- return nil
- }
-
- c, err := initializeConfig(true, false, &lc.hugoBuilderCommon, lc, cfgInit)
- if err != nil {
- return nil, err
- }
-
- sites, err := hugolib.NewHugoSites(*c.DepsCfg)
-
- if err != nil {
- return nil, newSystemError("Error creating sites", err)
- }
-
- if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
- return nil, newSystemError("Error Processing Source Content", err)
- }
-
- return sites, nil
-}
-
-func newListCmd() *listCmd {
- cc := &listCmd{}
-
- cc.baseCmd = newBaseCmd(&cobra.Command{
- Use: "list",
- Short: "Listing out various types of content",
- Long: `Listing out various types of content.
-
-List requires a subcommand, e.g. ` + "`hugo list drafts`.",
- RunE: nil,
- })
-
- cc.cmd.AddCommand(
- &cobra.Command{
- Use: "drafts",
- Short: "List all drafts",
- Long: `List all of the drafts in your content directory.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- sites, err := cc.buildSites(map[string]interface{}{"buildDrafts": true})
-
- if err != nil {
- return newSystemError("Error building sites", err)
- }
-
- for _, p := range sites.Pages() {
- if p.Draft() {
- jww.FEEDBACK.Println(strings.TrimPrefix(p.File().Filename(), sites.WorkingDir+string(os.PathSeparator)))
- }
- }
-
- return nil
- },
- },
- &cobra.Command{
- Use: "future",
- Short: "List all posts dated in the future",
- Long: `List all of the posts in your content directory which will be posted in the future.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- sites, err := cc.buildSites(map[string]interface{}{"buildFuture": true})
-
- if err != nil {
- return newSystemError("Error building sites", err)
- }
-
- writer := csv.NewWriter(os.Stdout)
- defer writer.Flush()
-
- for _, p := range sites.Pages() {
- if resource.IsFuture(p) {
- err := writer.Write([]string{
- strings.TrimPrefix(p.File().Filename(), sites.WorkingDir+string(os.PathSeparator)),
- p.PublishDate().Format(time.RFC3339),
- })
- if err != nil {
- return newSystemError("Error writing future posts to stdout", err)
- }
- }
- }
-
- return nil
- },
- },
- &cobra.Command{
- Use: "expired",
- Short: "List all posts already expired",
- Long: `List all of the posts in your content directory which has already expired.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- sites, err := cc.buildSites(map[string]interface{}{"buildExpired": true})
-
- if err != nil {
- return newSystemError("Error building sites", err)
- }
-
- writer := csv.NewWriter(os.Stdout)
- defer writer.Flush()
-
- for _, p := range sites.Pages() {
- if resource.IsExpired(p) {
- err := writer.Write([]string{
- strings.TrimPrefix(p.File().Filename(), sites.WorkingDir+string(os.PathSeparator)),
- p.ExpiryDate().Format(time.RFC3339),
- })
- if err != nil {
- return newSystemError("Error writing expired posts to stdout", err)
- }
- }
- }
-
- return nil
- },
- },
- &cobra.Command{
- Use: "all",
- Short: "List all posts",
- Long: `List all of the posts in your content directory, include drafts, future and expired pages.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- sites, err := cc.buildSites(map[string]interface{}{
- "buildExpired": true,
- "buildDrafts": true,
- "buildFuture": true,
- })
-
- if err != nil {
- return newSystemError("Error building sites", err)
- }
-
- writer := csv.NewWriter(os.Stdout)
- defer writer.Flush()
-
- writer.Write([]string{
- "path",
- "slug",
- "title",
- "date",
- "expiryDate",
- "publishDate",
- "draft",
- "permalink",
- })
- for _, p := range sites.Pages() {
- if !p.IsPage() {
- continue
- }
- err := writer.Write([]string{
- strings.TrimPrefix(p.File().Filename(), sites.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(),
- })
- if err != nil {
- return newSystemError("Error writing posts to stdout", err)
- }
- }
-
- return nil
- },
- },
- )
-
- cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
- cc.cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
-
- return cc
-}
diff --git a/commands/list_test.go b/commands/list_test.go
deleted file mode 100644
index bfc280679..000000000
--- a/commands/list_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package commands
-
-import (
- "bytes"
- "encoding/csv"
- "io"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/cobra"
-)
-
-func captureStdout(f func() (*cobra.Command, error)) (string, error) {
- old := os.Stdout
- r, w, _ := os.Pipe()
- os.Stdout = w
-
- _, err := f()
-
- if err != nil {
- return "", err
- }
-
- w.Close()
- os.Stdout = old
-
- var buf bytes.Buffer
- io.Copy(&buf, r)
- return buf.String(), nil
-}
-
-func TestListAll(t *testing.T) {
- c := qt.New(t)
- dir, err := createSimpleTestSite(t, testSiteConfig{})
-
- c.Assert(err, qt.IsNil)
-
- hugoCmd := newCommandsBuilder().addAll().build()
- cmd := hugoCmd.getCommand()
-
- defer func() {
- os.RemoveAll(dir)
- }()
-
- cmd.SetArgs([]string{"-s=" + dir, "list", "all"})
-
- out, err := captureStdout(cmd.ExecuteC)
- c.Assert(err, qt.IsNil)
-
- r := csv.NewReader(strings.NewReader(out))
-
- header, err := r.Read()
-
- c.Assert(err, qt.IsNil)
- c.Assert(header, qt.DeepEquals, []string{
- "path", "slug", "title",
- "date", "expiryDate", "publishDate",
- "draft", "permalink",
- })
-
- record, err := r.Read()
-
- c.Assert(err, qt.IsNil)
- c.Assert(record, qt.DeepEquals, []string{
- filepath.Join("content", "p1.md"), "", "P1",
- "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z",
- "false", "https://example.org/p1/",
- })
-}
diff --git a/commands/mod.go b/commands/mod.go
deleted file mode 100644
index 5fbd93ecb..000000000
--- a/commands/mod.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// 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 commands
-
-import (
- "os"
-
- "github.com/gohugoio/hugo/modules"
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*modCmd)(nil)
-
-type modCmd struct {
- *baseBuilderCmd
-}
-
-func (b *commandsBuilder) newModCmd() *modCmd {
- c := &modCmd{}
-
- const commonUsage = `
-Note that Hugo will always start out by resolving the components defined in the site
-configuration, provided by a _vendor directory (if no --ignoreVendor flag provided),
-Go Modules, or a folder inside the themes directory, in that order.
-
-See https://gohugo.io/hugo-modules/ for more information.
-
-`
-
- cmd := &cobra.Command{
- Use: "mod",
- Short: "Various Hugo Modules helpers.",
- 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".
-
-` + commonUsage,
-
- RunE: nil,
- }
-
- cmd.AddCommand(
- &cobra.Command{
- Use: "get",
- DisableFlagParsing: true,
- 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 module dependencies:
-
- hugo mod get -u
-
-Run "go help get" for more information. All flags available for "go get" is also relevant here.
-` + commonUsage,
- RunE: func(cmd *cobra.Command, args []string) error {
- return c.withModsClient(false, func(c *modules.Client) 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" {
- return cmd.Help()
- }
- return c.Get(args...)
- })
- },
- },
- &cobra.Command{
- Use: "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.
-`,
- RunE: func(cmd *cobra.Command, args []string) error {
- return c.withModsClient(true, func(c *modules.Client) error {
- return c.Graph(os.Stdout)
- })
- },
- },
- &cobra.Command{
- Use: "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.
-`,
- RunE: func(cmd *cobra.Command, args []string) error {
- var path string
- if len(args) >= 1 {
- path = args[0]
- }
- return c.withModsClient(false, func(c *modules.Client) error {
- return c.Init(path)
- })
- },
- },
- &cobra.Command{
- Use: "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.
-`,
- RunE: func(cmd *cobra.Command, args []string) error {
- return c.withModsClient(true, func(c *modules.Client) error {
- return c.Vendor()
- })
- },
- },
- &cobra.Command{
- Use: "tidy",
- Short: "Remove unused entries in go.mod and go.sum.",
- RunE: func(cmd *cobra.Command, args []string) error {
- return c.withModsClient(true, func(c *modules.Client) error {
- return c.Tidy()
- })
- },
- },
- &cobra.Command{
- Use: "clean",
- Short: "Delete the entire Hugo Module cache.",
- Long: `Delete the entire Hugo Module cache.
-
-Note that after you run this command, all of your dependencies will be re-downloaded next time you run "hugo".
-
-Also note that if you configure a positive maxAge for the "modules" file cache, it will also be cleaned as part of "hugo --gc".
-
-`,
- RunE: func(cmd *cobra.Command, args []string) error {
- com, err := c.initConfig(true)
- if err != nil {
- return err
- }
-
- _, err = com.hugo().FileCaches.ModulesCache().Prune(true)
- return err
-
- },
- },
- )
-
- c.baseBuilderCmd = b.newBuilderCmd(cmd)
-
- return c
-
-}
-
-func (c *modCmd) withModsClient(failOnMissingConfig bool, f func(*modules.Client) error) error {
- com, err := c.initConfig(failOnMissingConfig)
- if err != nil {
- return err
- }
-
- return f(com.hugo().ModulesClient)
-}
-
-func (c *modCmd) initConfig(failOnNoConfig bool) (*commandeer, error) {
- com, err := initializeConfig(failOnNoConfig, false, &c.hugoBuilderCommon, c, nil)
- if err != nil {
- return nil, err
- }
- return com, nil
-}
diff --git a/commands/new.go b/commands/new.go
deleted file mode 100644
index 4fc0d4ed4..000000000
--- a/commands/new.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/gohugoio/hugo/create"
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugolib"
- "github.com/spf13/afero"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*newCmd)(nil)
-
-type newCmd struct {
- contentEditor string
- contentType string
-
- *baseBuilderCmd
-}
-
-func (b *commandsBuilder) newNewCmd() *newCmd {
- cmd := &cobra.Command{
- Use: "new [path]",
- Short: "Create new content for your site",
- 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 site, they will be used.
-
-Ensure you run this within the root directory of your site.`,
- }
-
- cc := &newCmd{baseBuilderCmd: b.newBuilderCmd(cmd)}
-
- cmd.Flags().StringVarP(&cc.contentType, "kind", "k", "", "content type to create")
- cmd.Flags().StringVar(&cc.contentEditor, "editor", "", "edit new content with this editor, if provided")
-
- cmd.AddCommand(newNewSiteCmd().getCommand())
- cmd.AddCommand(newNewThemeCmd().getCommand())
-
- cmd.RunE = cc.newContent
-
- return cc
-}
-
-func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
- cfgInit := func(c *commandeer) error {
- if cmd.Flags().Changed("editor") {
- c.Set("newContentEditor", n.contentEditor)
- }
- return nil
- }
-
- c, err := initializeConfig(true, false, &n.hugoBuilderCommon, n, cfgInit)
-
- if err != nil {
- return err
- }
-
- if len(args) < 1 {
- return newUserError("path needs to be provided")
- }
-
- createPath := args[0]
-
- var kind string
-
- createPath, kind = newContentPathSection(c.hugo(), createPath)
-
- if n.contentType != "" {
- kind = n.contentType
- }
-
- return create.NewContent(c.hugo(), kind, createPath)
-}
-
-func mkdir(x ...string) {
- p := filepath.Join(x...)
-
- err := os.MkdirAll(p, 0777) // before umask
- if err != nil {
- jww.FATAL.Fatalln(err)
- }
-}
-
-func touchFile(fs afero.Fs, x ...string) {
- inpath := filepath.Join(x...)
- mkdir(filepath.Dir(inpath))
- err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), fs)
- if err != nil {
- jww.FATAL.Fatalln(err)
- }
-}
-
-func newContentPathSection(h *hugolib.HugoSites, path string) (string, string) {
- // Forward slashes is used in all examples. Convert if needed.
- // Issue #1133
- createpath := filepath.FromSlash(path)
-
- if h != nil {
- for _, dir := range h.BaseFs.Content.Dirs {
- createpath = strings.TrimPrefix(createpath, dir.Meta().Filename())
- }
- }
-
- var section string
- // assume the first directory is the section (kind)
- if strings.Contains(createpath[1:], helpers.FilePathSeparator) {
- parts := strings.Split(strings.TrimPrefix(createpath, helpers.FilePathSeparator), helpers.FilePathSeparator)
- if len(parts) > 0 {
- section = parts[0]
- }
-
- }
-
- return createpath, section
-}
diff --git a/commands/new_content_test.go b/commands/new_content_test.go
deleted file mode 100644
index 36726e37a..000000000
--- a/commands/new_content_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 commands
-
-import (
- "path/filepath"
- "testing"
-
- "github.com/gohugoio/hugo/hugofs"
- "github.com/spf13/viper"
-
- qt "github.com/frankban/quicktest"
-)
-
-// Issue #1133
-func TestNewContentPathSectionWithForwardSlashes(t *testing.T) {
- c := qt.New(t)
- p, s := newContentPathSection(nil, "/post/new.md")
- c.Assert(p, qt.Equals, filepath.FromSlash("/post/new.md"))
- c.Assert(s, qt.Equals, "post")
-}
-
-func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
- c := qt.New(t)
- paths := []string{
- filepath.Join(basepath, "layouts"),
- filepath.Join(basepath, "content"),
- filepath.Join(basepath, "archetypes"),
- filepath.Join(basepath, "static"),
- filepath.Join(basepath, "data"),
- filepath.Join(basepath, "config.toml"),
- }
-
- for _, path := range paths {
- _, err := fs.Source.Stat(path)
- c.Assert(err, qt.IsNil)
- }
-}
-
-func TestDoNewSite(t *testing.T) {
- c := qt.New(t)
- n := newNewSiteCmd()
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
-
- c.Assert(n.doNewSite(fs, basepath, false), qt.IsNil)
-
- checkNewSiteInited(fs, basepath, t)
-}
-
-func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
- c := qt.New(t)
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
- n := newNewSiteCmd()
-
- c.Assert(fs.Source.MkdirAll(basepath, 0777), qt.IsNil)
-
- c.Assert(n.doNewSite(fs, basepath, false), qt.IsNil)
-}
-
-func TestDoNewSite_error_base_exists(t *testing.T) {
- c := qt.New(t)
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
- n := newNewSiteCmd()
-
- c.Assert(fs.Source.MkdirAll(basepath, 0777), qt.IsNil)
- _, err := fs.Source.Create(filepath.Join(basepath, "foo"))
- c.Assert(err, qt.IsNil)
- // Since the directory already exists and isn't empty, expect an error
- c.Assert(n.doNewSite(fs, basepath, false), qt.Not(qt.IsNil))
-
-}
-
-func TestDoNewSite_force_empty_dir(t *testing.T) {
- c := qt.New(t)
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
- n := newNewSiteCmd()
-
- c.Assert(fs.Source.MkdirAll(basepath, 0777), qt.IsNil)
- c.Assert(n.doNewSite(fs, basepath, true), qt.IsNil)
-
- checkNewSiteInited(fs, basepath, t)
-}
-
-func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
- c := qt.New(t)
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
- n := newNewSiteCmd()
-
- contentPath := filepath.Join(basepath, "content")
-
- c.Assert(fs.Source.MkdirAll(contentPath, 0777), qt.IsNil)
- c.Assert(n.doNewSite(fs, basepath, true), qt.Not(qt.IsNil))
-}
-
-func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
- c := qt.New(t)
- basepath := filepath.Join("base", "blog")
- _, fs := newTestCfg()
- n := newNewSiteCmd()
-
- configPath := filepath.Join(basepath, "config.toml")
- c.Assert(fs.Source.MkdirAll(basepath, 0777), qt.IsNil)
- _, err := fs.Source.Create(configPath)
- c.Assert(err, qt.IsNil)
-
- c.Assert(n.doNewSite(fs, basepath, true), qt.Not(qt.IsNil))
-}
-
-func newTestCfg() (*viper.Viper, *hugofs.Fs) {
-
- v := viper.New()
- fs := hugofs.NewMem(v)
-
- v.SetFs(fs.Source)
-
- return v, fs
-
-}
diff --git a/commands/new_site.go b/commands/new_site.go
deleted file mode 100644
index 6d1677e22..000000000
--- a/commands/new_site.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "errors"
- "path/filepath"
- "strings"
-
- "github.com/gohugoio/hugo/parser/metadecoders"
-
- _errors "github.com/pkg/errors"
-
- "github.com/gohugoio/hugo/create"
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/parser"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
-)
-
-var _ cmder = (*newSiteCmd)(nil)
-
-type newSiteCmd struct {
- configFormat string
-
- *baseCmd
-}
-
-func newNewSiteCmd() *newSiteCmd {
- ccmd := &newSiteCmd{}
-
- cmd := &cobra.Command{
- Use: "site [path]",
- Short: "Create a new site (skeleton)",
- Long: `Create a new site in the provided directory.
-The new site will have the correct structure, but no content or theme yet.
-Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
- RunE: ccmd.newSite,
- }
-
- cmd.Flags().StringVarP(&ccmd.configFormat, "format", "f", "toml", "config & frontmatter format")
- cmd.Flags().Bool("force", false, "init inside non-empty directory")
-
- ccmd.baseCmd = newBaseCmd(cmd)
-
- return ccmd
-
-}
-
-func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
- archeTypePath := filepath.Join(basepath, "archetypes")
- dirs := []string{
- filepath.Join(basepath, "layouts"),
- filepath.Join(basepath, "content"),
- archeTypePath,
- filepath.Join(basepath, "static"),
- filepath.Join(basepath, "data"),
- filepath.Join(basepath, "themes"),
- }
-
- if exists, _ := helpers.Exists(basepath, fs.Source); exists {
- if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
- return errors.New(basepath + " already exists but not a directory")
- }
-
- isEmpty, _ := helpers.IsEmpty(basepath, fs.Source)
-
- switch {
- case !isEmpty && !force:
- return errors.New(basepath + " already exists and is not empty")
-
- case !isEmpty && force:
- all := append(dirs, filepath.Join(basepath, "config."+n.configFormat))
- for _, path := range all {
- if exists, _ := helpers.Exists(path, fs.Source); exists {
- return errors.New(path + " already exists")
- }
- }
- }
- }
-
- for _, dir := range dirs {
- if err := fs.Source.MkdirAll(dir, 0777); err != nil {
- return _errors.Wrap(err, "Failed to create dir")
- }
- }
-
- createConfig(fs, basepath, n.configFormat)
-
- // Create a default archetype file.
- helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
- strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
-
- jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
- jww.FEEDBACK.Println(nextStepsText())
-
- return nil
-}
-
-// newSite creates a new Hugo site and initializes a structured Hugo directory.
-func (n *newSiteCmd) newSite(cmd *cobra.Command, 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 newUserError(err)
- }
-
- forceNew, _ := cmd.Flags().GetBool("force")
-
- return n.doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
-}
-
-func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
- in := map[string]string{
- "baseURL": "http://example.org/",
- "title": "My New Hugo Site",
- "languageCode": "en-us",
- }
-
- var buf bytes.Buffer
- err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(kind), &buf)
- if err != nil {
- return err
- }
-
- return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
-}
-
-func nextStepsText() string {
- var nextStepsText bytes.Buffer
-
- nextStepsText.WriteString(`Just a few more steps and you're ready to go:
-
-1. Download a theme into the same-named folder.
- Choose a theme from https://themes.gohugo.io/ or
- create your own with the "hugo new theme " command.
-2. Perhaps you want to add some content. You can add single files
- with "hugo new `)
-
- nextStepsText.WriteString(filepath.Join("", "."))
-
- nextStepsText.WriteString(`".
-3. Start the built-in live server via "hugo server".
-
-Visit https://gohugo.io/ for quickstart guide and full documentation.`)
-
- return nextStepsText.String()
-}
diff --git a/commands/new_theme.go b/commands/new_theme.go
deleted file mode 100644
index a0a4e89e3..000000000
--- a/commands/new_theme.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "errors"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*newThemeCmd)(nil)
-
-type newThemeCmd struct {
- *baseCmd
- hugoBuilderCommon
-}
-
-func newNewThemeCmd() *newThemeCmd {
- ccmd := &newThemeCmd{baseCmd: newBaseCmd(nil)}
-
- cmd := &cobra.Command{
- Use: "theme [name]",
- Short: "Create a new theme",
- Long: `Create a new theme (skeleton) called [name] in the current directory.
-New theme is a skeleton. Please add content to the touched files. Add your
-name to the copyright line in the license and adjust the theme.toml file
-as you see fit.`,
- RunE: ccmd.newTheme,
- }
-
- ccmd.cmd = cmd
-
- return ccmd
-}
-
-// newTheme creates a new Hugo theme template
-func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
- c, err := initializeConfig(false, false, &n.hugoBuilderCommon, n, nil)
-
- if err != nil {
- return err
- }
-
- if len(args) < 1 {
- return newUserError("theme name needs to be provided")
- }
-
- createpath := c.hugo().PathSpec.AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
- jww.FEEDBACK.Println("Creating theme at", createpath)
-
- cfg := c.DepsCfg
-
- if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
- return errors.New(createpath + " already exists")
- }
-
- mkdir(createpath, "layouts", "_default")
- mkdir(createpath, "layouts", "partials")
-
- touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
- touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
- touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
- touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
-
- baseofDefault := []byte(`
-
- {{- partial "head.html" . -}}
-
- {{- partial "header.html" . -}}
-
- {{- block "main" . }}{{- end }}
-
- {{- partial "footer.html" . -}}
-
-
-`)
- err = helpers.WriteToDisk(filepath.Join(createpath, "layouts", "_default", "baseof.html"), bytes.NewReader(baseofDefault), cfg.Fs.Source)
- if err != nil {
- return err
- }
-
- touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "head.html")
- touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
- touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
-
- mkdir(createpath, "archetypes")
-
- archDefault := []byte("+++\n+++\n")
-
- err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
- if err != nil {
- return err
- }
-
- mkdir(createpath, "static", "js")
- mkdir(createpath, "static", "css")
-
- by := []byte(`The MIT License (MIT)
-
-Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-`)
-
- err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE"), bytes.NewReader(by), cfg.Fs.Source)
- if err != nil {
- return err
- }
-
- n.createThemeMD(cfg.Fs, createpath)
-
- return nil
-}
-
-func (n *newThemeCmd) createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
-
- by := []byte(`# theme.toml template for a Hugo theme
-# See https://github.com/gohugoio/hugoThemes#themetoml for an example
-
-name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
-license = "MIT"
-licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
-description = ""
-homepage = "http://example.com/"
-tags = []
-features = []
-min_version = "0.41"
-
-[author]
- name = ""
- homepage = ""
-
-# If porting an existing theme
-[original]
- name = ""
- homepage = ""
- repo = ""
-`)
-
- err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
- if err != nil {
- return
- }
-
- return nil
-}
diff --git a/commands/release.go b/commands/release.go
deleted file mode 100644
index 4de165f35..000000000
--- a/commands/release.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// +build release
-
-// Copyright 2017-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 commands
-
-import (
- "errors"
-
- "github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/releaser"
- "github.com/spf13/cobra"
-)
-
-var _ cmder = (*releaseCommandeer)(nil)
-
-type releaseCommandeer struct {
- cmd *cobra.Command
-
- version string
-
- skipPublish bool
- try bool
-}
-
-func createReleaser() cmder {
- // 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.
- r := &releaseCommandeer{
- cmd: &cobra.Command{
- Use: "release",
- Short: "Release a new version of Hugo.",
- Hidden: true,
- },
- }
-
- r.cmd.RunE = func(cmd *cobra.Command, args []string) error {
- return r.release()
- }
-
- r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
- r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
- r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
-
- return r
-}
-
-func (c *releaseCommandeer) getCommand() *cobra.Command {
- return c.cmd
-}
-
-func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
-
-}
-
-func (r *releaseCommandeer) release() error {
- if r.version == "" {
- return errors.New("must set the --rel flag to the relevant version number")
- }
- return releaser.New(r.version, r.skipPublish, r.try).Run()
-}
diff --git a/commands/release_noop.go b/commands/release_noop.go
deleted file mode 100644
index ccf34b68e..000000000
--- a/commands/release_noop.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// +build !release
-
-// 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 commands
-
-func createReleaser() cmder {
- return &nilCommand{}
-}
diff --git a/commands/server.go b/commands/server.go
deleted file mode 100644
index 709181507..000000000
--- a/commands/server.go
+++ /dev/null
@@ -1,557 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "fmt"
- "net"
- "net/http"
- "net/url"
- "os"
- "os/signal"
- "path/filepath"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/pkg/errors"
-
- "github.com/gohugoio/hugo/livereload"
- "github.com/gohugoio/hugo/tpl"
-
- "github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/helpers"
- "github.com/spf13/afero"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-type serverCmd struct {
- // Can be used to stop the server. Useful in tests
- stop <-chan bool
-
- disableLiveReload bool
- navigateToChanged bool
- renderToDisk bool
- serverAppend bool
- serverInterface string
- serverPort int
- liveReloadPort int
- serverWatch bool
- noHTTPCache bool
-
- disableFastRender bool
- disableBrowserError bool
-
- *baseBuilderCmd
-}
-
-func (b *commandsBuilder) newServerCmd() *serverCmd {
- return b.newServerCmdSignaled(nil)
-}
-
-func (b *commandsBuilder) newServerCmdSignaled(stop <-chan bool) *serverCmd {
- cc := &serverCmd{stop: stop}
-
- cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{
- Use: "server",
- Aliases: []string{"serve"},
- Short: "A high performance webserver",
- Long: `Hugo provides its own webserver which builds and serves the site.
-While hugo server is high performance, it is a webserver with limited options.
-Many run it in production, but the standard behavior is for people to use it
-in development and use a more full featured server such as Nginx or Caddy.
-
-'hugo server' will avoid writing the rendered and served content to disk,
-preferring to store it in memory.
-
-By default hugo will also watch your files for any changes you make and
-automatically rebuild the site. It will then live reload any open browser pages
-and push the latest content to them. As most Hugo sites are built in a fraction
-of a second, you will be able to save and see your changes nearly instantly.`,
- RunE: cc.server,
- })
-
- cc.cmd.Flags().IntVarP(&cc.serverPort, "port", "p", 1313, "port on which the server will listen")
- cc.cmd.Flags().IntVar(&cc.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
- cc.cmd.Flags().StringVarP(&cc.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
- cc.cmd.Flags().BoolVarP(&cc.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
- cc.cmd.Flags().BoolVar(&cc.noHTTPCache, "noHTTPCache", false, "prevent HTTP caching")
- cc.cmd.Flags().BoolVarP(&cc.serverAppend, "appendPort", "", true, "append port to baseURL")
- cc.cmd.Flags().BoolVar(&cc.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
- cc.cmd.Flags().BoolVar(&cc.navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload")
- cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)")
- cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
- cc.cmd.Flags().BoolVar(&cc.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
-
- cc.cmd.Flags().String("memstats", "", "log memory usage to this file")
- cc.cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
-
- return cc
-}
-
-type filesOnlyFs struct {
- fs http.FileSystem
-}
-
-type noDirFile struct {
- http.File
-}
-
-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
-}
-
-func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) {
- return nil, nil
-}
-
-var serverPorts []int
-
-func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
- // If a Destination is provided via flag write to disk
- destination, _ := cmd.Flags().GetString("destination")
- if destination != "" {
- sc.renderToDisk = true
- }
-
- var serverCfgInit sync.Once
-
- cfgInit := func(c *commandeer) error {
- c.Set("renderToMemory", !sc.renderToDisk)
- if cmd.Flags().Changed("navigateToChanged") {
- c.Set("navigateToChanged", sc.navigateToChanged)
- }
- if cmd.Flags().Changed("disableLiveReload") {
- c.Set("disableLiveReload", sc.disableLiveReload)
- }
- if cmd.Flags().Changed("disableFastRender") {
- c.Set("disableFastRender", sc.disableFastRender)
- }
- if cmd.Flags().Changed("disableBrowserError") {
- c.Set("disableBrowserError", sc.disableBrowserError)
- }
- if sc.serverWatch {
- c.Set("watch", true)
- }
-
- // TODO(bep) yes, we should fix.
- if !c.languagesConfigured {
- return nil
- }
-
- var err error
-
- // We can only do this once.
- serverCfgInit.Do(func() {
- serverPorts = make([]int, 1)
-
- if c.languages.IsMultihost() {
- if !sc.serverAppend {
- err = newSystemError("--appendPort=false not supported when in multihost mode")
- }
- serverPorts = make([]int, len(c.languages))
- }
-
- currentServerPort := sc.serverPort
-
- for i := 0; i < len(serverPorts); i++ {
- l, err := net.Listen("tcp", net.JoinHostPort(sc.serverInterface, strconv.Itoa(currentServerPort)))
- if err == nil {
- l.Close()
- serverPorts[i] = currentServerPort
- } else {
- if i == 0 && sc.cmd.Flags().Changed("port") {
- // port set explicitly by user -- he/she probably meant it!
- err = newSystemErrorF("Server startup failed: %s", err)
- }
- c.logger.FEEDBACK.Println("port", sc.serverPort, "already in use, attempting to use an available port")
- sp, err := helpers.FindAvailablePort()
- if err != nil {
- err = newSystemError("Unable to find alternative port to use:", err)
- }
- serverPorts[i] = sp.Port
- }
-
- currentServerPort = serverPorts[i] + 1
- }
- })
-
- c.serverPorts = serverPorts
-
- c.Set("port", sc.serverPort)
- if sc.liveReloadPort != -1 {
- c.Set("liveReloadPort", sc.liveReloadPort)
- } else {
- c.Set("liveReloadPort", serverPorts[0])
- }
-
- isMultiHost := c.languages.IsMultihost()
- for i, language := range c.languages {
- var serverPort int
- if isMultiHost {
- serverPort = serverPorts[i]
- } else {
- serverPort = serverPorts[0]
- }
-
- baseURL, err := sc.fixURL(language, sc.baseURL, serverPort)
- if err != nil {
- return nil
- }
- if isMultiHost {
- language.Set("baseURL", baseURL)
- }
- if i == 0 {
- c.Set("baseURL", baseURL)
- }
- }
-
- return err
-
- }
-
- if err := memStats(); err != nil {
- jww.WARN.Println("memstats error:", err)
- }
-
- c, err := initializeConfig(true, true, &sc.hugoBuilderCommon, sc, cfgInit)
- if err != nil {
- return err
- }
-
- if err := c.serverBuild(); err != nil {
- return err
- }
-
- for _, s := range c.hugo().Sites {
- s.RegisterMediaTypes()
- }
-
- // Watch runs its own server as part of the routine
- if sc.serverWatch {
-
- watchDirs, err := c.getDirList()
- if err != nil {
- return err
- }
-
- watchGroups := helpers.ExtractAndGroupRootPaths(watchDirs)
-
- for _, group := range watchGroups {
- jww.FEEDBACK.Printf("Watching for changes in %s\n", group)
- }
- watcher, err := c.newWatcher(watchDirs...)
-
- if err != nil {
- return err
- }
-
- defer watcher.Close()
-
- }
-
- return c.serve(sc)
-
-}
-
-func getRootWatchDirsStr(baseDir string, watchDirs []string) string {
- relWatchDirs := make([]string, len(watchDirs))
- for i, dir := range watchDirs {
- relWatchDirs[i], _ = helpers.GetRelativePath(dir, baseDir)
- }
-
- return strings.Join(helpers.UniqueStringsSorted(helpers.ExtractRootPaths(relWatchDirs)), ",")
-}
-
-type fileServer struct {
- baseURLs []string
- roots []string
- errorTemplate tpl.Template
- c *commandeer
- s *serverCmd
-}
-
-func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, error) {
- baseURL := f.baseURLs[i]
- root := f.roots[i]
- port := f.c.serverPorts[i]
-
- publishDir := f.c.Cfg.GetString("publishDir")
-
- if root != "" {
- publishDir = filepath.Join(publishDir, root)
- }
-
- absPublishDir := f.c.hugo().PathSpec.AbsPathify(publishDir)
-
- jww.FEEDBACK.Printf("Environment: %q", f.c.hugo().Deps.Site.Hugo().Environment)
-
- if i == 0 {
- if f.s.renderToDisk {
- jww.FEEDBACK.Println("Serving pages from " + absPublishDir)
- } else {
- jww.FEEDBACK.Println("Serving pages from memory")
- }
- }
-
- httpFs := afero.NewHttpFs(f.c.destinationFs)
- fs := filesOnlyFs{httpFs.Dir(absPublishDir)}
-
- if i == 0 && f.c.fastRenderMode {
- jww.FEEDBACK.Println("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender")
- }
-
- // We're only interested in the path
- u, err := url.Parse(baseURL)
- if err != nil {
- return nil, "", "", errors.Wrap(err, "Invalid baseURL")
- }
-
- 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 {
- w.WriteHeader(500)
- var b bytes.Buffer
- err := f.errorTemplate.Execute(&b, err)
- if err != nil {
- f.c.logger.ERROR.Println(err)
- }
- port = 1313
- if !f.c.paused {
- port = f.c.Cfg.GetInt("liveReloadPort")
- }
- fmt.Fprint(w, injectLiveReloadScript(&b, port))
-
- return
- }
- }
-
- if f.s.noHTTPCache {
- w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
- w.Header().Set("Pragma", "no-cache")
- }
-
- if f.c.fastRenderMode && f.c.buildErr == nil {
- p := r.RequestURI
- if strings.HasSuffix(p, "/") || strings.HasSuffix(p, "html") || strings.HasSuffix(p, "htm") {
- if !f.c.visitedURLs.Contains(p) {
- // If not already on stack, re-render that single page.
- if err := f.c.partialReRender(p); err != nil {
- f.c.handleBuildErr(err, fmt.Sprintf("Failed to render %q", p))
- if f.c.showErrorInBrowser {
- http.Redirect(w, r, p, http.StatusMovedPermanently)
- return
- }
- }
- }
-
- f.c.visitedURLs.Add(p)
-
- }
- }
- h.ServeHTTP(w, r)
- })
- }
-
- fileserver := decorate(http.FileServer(fs))
- mu := http.NewServeMux()
-
- if u.Path == "" || u.Path == "/" {
- mu.Handle("/", fileserver)
- } else {
- mu.Handle(u.Path, http.StripPrefix(u.Path, fileserver))
- }
-
- endpoint := net.JoinHostPort(f.s.serverInterface, strconv.Itoa(port))
-
- return mu, u.String(), endpoint, nil
-}
-
-var logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
-
-func removeErrorPrefixFromLog(content string) string {
- return logErrorRe.ReplaceAllLiteralString(content, "")
-}
-func (c *commandeer) serve(s *serverCmd) error {
-
- isMultiHost := c.hugo().IsMultihost()
-
- var (
- baseURLs []string
- roots []string
- )
-
- if isMultiHost {
- for _, s := range c.hugo().Sites {
- baseURLs = append(baseURLs, s.BaseURL.String())
- roots = append(roots, s.Language().Lang)
- }
- } else {
- s := c.hugo().Sites[0]
- baseURLs = []string{s.BaseURL.String()}
- roots = []string{""}
- }
-
- templ, err := c.hugo().TextTmpl.Parse("__default_server_error", buildErrorTemplate)
- if err != nil {
- return err
- }
-
- srv := &fileServer{
- baseURLs: baseURLs,
- roots: roots,
- c: c,
- s: s,
- errorTemplate: templ,
- }
-
- doLiveReload := !c.Cfg.GetBool("disableLiveReload")
-
- if doLiveReload {
- livereload.Initialize()
- }
-
- var sigs = make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
-
- for i := range baseURLs {
- mu, serverURL, endpoint, err := srv.createEndpoint(i)
-
- if doLiveReload {
- mu.HandleFunc("/livereload.js", livereload.ServeJS)
- mu.HandleFunc("/livereload", livereload.Handler)
- }
- jww.FEEDBACK.Printf("Web Server is available at %s (bind address %s)\n", serverURL, s.serverInterface)
- go func() {
- err = http.ListenAndServe(endpoint, mu)
- if err != nil {
- c.logger.ERROR.Printf("Error: %s\n", err.Error())
- os.Exit(1)
- }
- }()
- }
-
- jww.FEEDBACK.Println("Press Ctrl+C to stop")
-
- if s.stop != nil {
- select {
- case <-sigs:
- case <-s.stop:
- }
- } else {
- <-sigs
- }
-
- return nil
-}
-
-// fixURL massages the baseURL into a form needed for serving
-// all pages correctly.
-func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, error) {
- useLocalhost := false
- if s == "" {
- s = cfg.GetString("baseURL")
- useLocalhost = true
- }
-
- if !strings.HasSuffix(s, "/") {
- s = s + "/"
- }
-
- // do an initial parse of the input string
- u, err := url.Parse(s)
- 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 == "" && s != "/" {
- s = "//" + s
-
- u, err = url.Parse(s)
- if err != nil {
- return "", err
- }
- }
-
- if useLocalhost {
- if u.Scheme == "https" {
- u.Scheme = "http"
- }
- u.Host = "localhost"
- }
-
- if sc.serverAppend {
- if strings.Contains(u.Host, ":") {
- u.Host, _, err = net.SplitHostPort(u.Host)
- if err != nil {
- return "", errors.Wrap(err, "Failed to split baseURL hostpost")
- }
- }
- u.Host += fmt.Sprintf(":%d", port)
- }
-
- return u.String(), nil
-}
-
-func memStats() error {
- b := newCommandsBuilder()
- sc := b.newServerCmd().getCommand()
- memstats := sc.Flags().Lookup("memstats").Value.String()
- if memstats != "" {
- interval, err := time.ParseDuration(sc.Flags().Lookup("meminterval").Value.String())
- if err != nil {
- interval, _ = time.ParseDuration("100ms")
- }
-
- fileMemStats, err := os.Create(memstats)
- if err != nil {
- return err
- }
-
- fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
-
- go func() {
- var stats runtime.MemStats
-
- start := time.Now().UnixNano()
-
- for {
- runtime.ReadMemStats(&stats)
- if fileMemStats != nil {
- fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
- (time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
- time.Sleep(interval)
- } else {
- break
- }
- }
- }()
- }
- return nil
-}
diff --git a/commands/server_errors.go b/commands/server_errors.go
deleted file mode 100644
index 9f13c9d8c..000000000
--- a/commands/server_errors.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// 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 commands
-
-import (
- "bytes"
- "io"
-
- "github.com/gohugoio/hugo/transform"
- "github.com/gohugoio/hugo/transform/livereloadinject"
-)
-
-var buildErrorTemplate = `
-
-
-
- Hugo Server: Error
-
-
-
-
- {{ highlight .Error "apl" "noclasses=true,style=monokai" }}
- {{ with .File }}
- {{ $params := printf "noclasses=true,style=monokai,linenos=table,hl_lines=%d,linenostart=%d" (add .LinesPos 1) (sub .Position.LineNumber .LinesPos) }}
- {{ $lexer := .ChromaLexer | default "go-html-template" }}
- {{ highlight (delimit .Lines "\n") $lexer $params }}
- {{ end }}
- {{ with .StackTrace }}
- {{ highlight . "apl" "noclasses=true,style=monokai" }}
- {{ end }}
-
{{ .Version }}
- Reload Page
-
-
-
-`
-
-func injectLiveReloadScript(src io.Reader, port int) string {
- var b bytes.Buffer
- chain := transform.Chain{livereloadinject.New(port)}
- chain.Apply(&b, src)
-
- return b.String()
-}
diff --git a/commands/server_test.go b/commands/server_test.go
deleted file mode 100644
index 8bd96c6ec..000000000
--- a/commands/server_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 commands
-
-import (
- "fmt"
- "net/http"
- "os"
- "runtime"
- "strings"
- "testing"
- "time"
-
- "github.com/gohugoio/hugo/helpers"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/viper"
-)
-
-func TestServer(t *testing.T) {
- if isWindowsCI() {
- // TODO(bep) not sure why server tests have started to fail on the Windows CI server.
- t.Skip("Skip server test on appveyor")
- }
- c := qt.New(t)
- dir, err := createSimpleTestSite(t, testSiteConfig{})
- c.Assert(err, qt.IsNil)
-
- // Let us hope that this port is available on all systems ...
- port := 1331
-
- defer func() {
- os.RemoveAll(dir)
- }()
-
- stop := make(chan bool)
-
- b := newCommandsBuilder()
- scmd := b.newServerCmdSignaled(stop)
-
- cmd := scmd.getCommand()
- cmd.SetArgs([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)})
-
- go func() {
- _, err = cmd.ExecuteC()
- c.Assert(err, qt.IsNil)
- }()
-
- // There is no way to know exactly when the server is ready for connections.
- // We could improve by something like https://golang.org/pkg/net/http/httptest/#Server
- // But for now, let us sleep and pray!
- time.Sleep(2 * time.Second)
-
- resp, err := http.Get("http://localhost:1331/")
- c.Assert(err, qt.IsNil)
- defer resp.Body.Close()
- homeContent := helpers.ReaderToString(resp.Body)
-
- c.Assert(homeContent, qt.Contains, "List: Hugo Commands")
- c.Assert(homeContent, qt.Contains, "Environment: development")
-
- // Stop the server.
- stop <- true
-
-}
-
-func TestFixURL(t *testing.T) {
- type data struct {
- TestName string
- CLIBaseURL string
- CfgBaseURL string
- AppendPort bool
- Port int
- Result string
- }
- tests := []data{
- {"Basic http localhost", "", "http://foo.com", true, 1313, "http://localhost:1313/"},
- {"Basic https production, http localhost", "", "https://foo.com", true, 1313, "http://localhost:1313/"},
- {"Basic subdir", "", "http://foo.com/bar", true, 1313, "http://localhost:1313/bar/"},
- {"Basic production", "http://foo.com", "http://foo.com", false, 80, "http://foo.com/"},
- {"Production subdir", "http://foo.com/bar", "http://foo.com/bar", false, 80, "http://foo.com/bar/"},
- {"No http", "", "foo.com", true, 1313, "//localhost:1313/"},
- {"Override configured port", "", "foo.com:2020", true, 1313, "//localhost:1313/"},
- {"No http production", "foo.com", "foo.com", false, 80, "//foo.com/"},
- {"No http production with port", "foo.com", "foo.com", true, 2020, "//foo.com:2020/"},
- {"No config", "", "", true, 1313, "//localhost:1313/"},
- }
-
- for _, test := range tests {
- t.Run(test.TestName, func(t *testing.T) {
- b := newCommandsBuilder()
- s := b.newServerCmd()
- v := viper.New()
- baseURL := test.CLIBaseURL
- v.Set("baseURL", test.CfgBaseURL)
- s.serverAppend = test.AppendPort
- s.serverPort = test.Port
- result, err := s.fixURL(v, baseURL, s.serverPort)
- if err != nil {
- t.Errorf("Unexpected error %s", err)
- }
- if result != test.Result {
- t.Errorf("Expected %q, got %q", test.Result, result)
- }
- })
- }
-}
-
-func TestRemoveErrorPrefixFromLog(t *testing.T) {
- c := qt.New(t)
- content := `ERROR 2018/10/07 13:11:12 Error while rendering "home": template: _default/baseof.html:4:3: executing "main" at : error calling partial: template: partials/logo.html:5:84: executing "partials/logo.html" at <$resized.AHeight>: can't evaluate field AHeight in type *resource.Image
-ERROR 2018/10/07 13:11:12 Rebuild failed: logged 1 error(s)
-`
-
- withoutError := removeErrorPrefixFromLog(content)
-
- c.Assert(strings.Contains(withoutError, "ERROR"), qt.Equals, false)
-
-}
-
-func isWindowsCI() bool {
- return runtime.GOOS == "windows" && os.Getenv("CI") != ""
-}
diff --git a/commands/static_syncer.go b/commands/static_syncer.go
deleted file mode 100644
index 62ef28b2c..000000000
--- a/commands/static_syncer.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2017 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 (
- "os"
- "path/filepath"
-
- "github.com/gohugoio/hugo/hugolib/filesystems"
-
- "github.com/fsnotify/fsnotify"
- "github.com/gohugoio/hugo/helpers"
- "github.com/spf13/fsync"
-)
-
-type staticSyncer struct {
- c *commandeer
-}
-
-func newStaticSyncer(c *commandeer) (*staticSyncer, error) {
- return &staticSyncer{c: c}, nil
-}
-
-func (s *staticSyncer) isStatic(filename string) bool {
- return s.c.hugo().BaseFs.SourceFilesystems.IsStatic(filename)
-}
-
-func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
- c := s.c
-
- syncFn := func(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
- publishDir := c.hugo().PathSpec.PublishDir
- // If root, remove the second '/'
- if publishDir == "//" {
- publishDir = helpers.FilePathSeparator
- }
-
- if sourceFs.PublishFolder != "" {
- publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
- }
-
- syncer := fsync.NewSyncer()
- syncer.NoTimes = c.Cfg.GetBool("noTimes")
- syncer.NoChmod = c.Cfg.GetBool("noChmod")
- syncer.ChmodFilter = chmodFilter
- syncer.SrcFs = sourceFs.Fs
- syncer.DestFs = c.Fs.Destination
-
- // prevent spamming the log on changes
- logger := helpers.NewDistinctFeedbackLogger()
-
- 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 := sourceFs.MakePathRelative(fromPath)
-
- if relPath == "" {
- // 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); os.IsNotExist(err) {
- // If file doesn't exist in any static dir, remove it
- toRemove := filepath.Join(publishDir, relPath)
-
- logger.Println("File no longer exists in static dir, removing", toRemove)
- _ = c.Fs.Destination.RemoveAll(toRemove)
- } else if err == nil {
- // If file still exists, sync it
- logger.Println("Syncing", relPath, "to", publishDir)
-
- if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil {
- c.logger.ERROR.Println(err)
- }
- } else {
- c.logger.ERROR.Println(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.logger.ERROR.Println(err)
- }
- }
-
- return 0, nil
- }
-
- _, err := c.doWithPublishDirs(syncFn)
- return err
-
-}
diff --git a/commands/version.go b/commands/version.go
deleted file mode 100644
index 287950a2d..000000000
--- a/commands/version.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 commands
-
-import (
- "github.com/gohugoio/hugo/common/hugo"
- "github.com/spf13/cobra"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var _ cmder = (*versionCmd)(nil)
-
-type versionCmd struct {
- *baseCmd
-}
-
-func newVersionCmd() *versionCmd {
- return &versionCmd{
- newBaseCmd(&cobra.Command{
- Use: "version",
- Short: "Print the version number of Hugo",
- Long: `All software has versions. This is Hugo's.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- printHugoVersion()
- return nil
- },
- }),
- }
-}
-
-func printHugoVersion() {
- jww.FEEDBACK.Println(hugo.BuildVersionString())
-}
diff --git a/common/collections/append.go b/common/collections/append.go
deleted file mode 100644
index b56455bc9..000000000
--- a/common/collections/append.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// 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"
-)
-
-// 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 interface{}, from ...interface{}) (interface{}, error) {
- tov, toIsNil := indirect(reflect.ValueOf(to))
-
- toIsNil = toIsNil || to == nil
- var tot reflect.Type
-
- if !toIsNil {
- if tov.Kind() != reflect.Slice {
- return nil, fmt.Errorf("expected a slice, got %T", to)
- }
-
- tot = tov.Type().Elem()
- toIsNil = tov.Len() == 0
-
- if len(from) == 1 {
- fromv := reflect.ValueOf(from[0])
- if fromv.Kind() == reflect.Slice {
- if toIsNil {
- // If we get nil []string, we just return the []string
- return from[0], nil
- }
-
- fromt := reflect.TypeOf(from[0]).Elem()
-
- // 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.Type().AssignableTo(tot) {
- // Fall back to a []interface{} slice.
- tov, _ := indirect(reflect.ValueOf(to))
- return appendToInterfaceSlice(tov, from...)
- }
- tov = reflect.Append(tov, fv)
- }
-
- return tov.Interface(), nil
-}
-
-func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]interface{}, error) {
- var tos []interface{}
-
- for _, slice := range []reflect.Value{slice1, slice2} {
- for i := 0; i < slice.Len(); i++ {
- tos = append(tos, slice.Index(i).Interface())
- }
- }
-
- return tos, nil
-}
-
-func appendToInterfaceSlice(tov reflect.Value, from ...interface{}) ([]interface{}, error) {
- var tos []interface{}
-
- for i := 0; i < tov.Len(); i++ {
- tos = append(tos, tov.Index(i).Interface())
- }
-
- tos = append(tos, from...)
-
- return tos, nil
-}
-
-// indirect is borrowed from the Go stdlib: 'text/template/exec.go'
-// TODO(bep) consolidate
-func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
- for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
- if v.IsNil() {
- return v, true
- }
- if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
- break
- }
- }
- return v, false
-}
diff --git a/common/collections/append_test.go b/common/collections/append_test.go
deleted file mode 100644
index 4086570b8..000000000
--- a/common/collections/append_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 _, test := range []struct {
- start interface{}
- addend []interface{}
- expected interface{}
- }{
- {[]string{"a", "b"}, []interface{}{"c"}, []string{"a", "b", "c"}},
- {[]string{"a", "b"}, []interface{}{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}},
- {[]string{"a", "b"}, []interface{}{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}},
- {[]string{"a"}, []interface{}{"b", template.HTML("c")}, []interface{}{"a", "b", template.HTML("c")}},
- {nil, []interface{}{"a", "b"}, []string{"a", "b"}},
- {nil, []interface{}{nil}, []interface{}{nil}},
- {[]interface{}{}, []interface{}{[]string{"c", "d", "e"}}, []string{"c", "d", "e"}},
- {tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}},
- []interface{}{&tstSlicer{"c"}},
- tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}, &tstSlicer{"c"}}},
- {&tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}},
- []interface{}{&tstSlicer{"c"}},
- tstSlicers{&tstSlicer{"a"},
- &tstSlicer{"b"},
- &tstSlicer{"c"}}},
- {testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}},
- []interface{}{&tstSlicerIn1{"c"}},
- testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}, &tstSlicerIn1{"c"}}},
- //https://github.com/gohugoio/hugo/issues/5361
- {[]string{"a", "b"}, []interface{}{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
- []interface{}{"a", "b", &tstSlicer{"a"}, &tstSlicer{"b"}}},
- {[]string{"a", "b"}, []interface{}{&tstSlicer{"a"}},
- []interface{}{"a", "b", &tstSlicer{"a"}}},
- // Errors
- {"", []interface{}{[]string{"a", "b"}}, false},
- // No string concatenation.
- {"ab",
- []interface{}{"c"},
- false},
- } {
-
- 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)
- }
-
-}
diff --git a/common/collections/collections.go b/common/collections/collections.go
deleted file mode 100644
index bb47c8acc..000000000
--- a/common/collections/collections.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 interface{}, items interface{}) (interface{}, error)
-}
diff --git a/common/collections/slice.go b/common/collections/slice.go
deleted file mode 100644
index 38ca86b08..000000000
--- a/common/collections/slice.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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"
-)
-
-// 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 interface{}) (interface{}, error)
-}
-
-// Slice returns a slice of all passed arguments.
-func Slice(args ...interface{}) interface{} {
- 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()
-}
diff --git a/common/collections/slice_test.go b/common/collections/slice_test.go
deleted file mode 100644
index 3ebfe6d11..000000000
--- a/common/collections/slice_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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)
-var _ Slicer = (*tstSlicerIn1)(nil)
-var _ Slicer = (*tstSlicerIn2)(nil)
-var _ testSlicerInterface = (*tstSlicerIn1)(nil)
-var _ 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 interface{}) (interface{}, error) {
- items := in.([]interface{})
- 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 interface{}) (interface{}, error) {
- items := in.([]interface{})
- 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 interface{}) (interface{}, error) {
- items := in.([]interface{})
- 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 []interface{}
- expected interface{}
- }{
- {[]interface{}{"a", "b"}, []string{"a", "b"}},
- {[]interface{}{&tstSlicer{"a"}, &tstSlicer{"b"}}, tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
- {[]interface{}{&tstSlicer{"a"}, "b"}, []interface{}{&tstSlicer{"a"}, "b"}},
- {[]interface{}{}, []interface{}{}},
- {[]interface{}{nil}, []interface{}{nil}},
- {[]interface{}{5, "b"}, []interface{}{5, "b"}},
- {[]interface{}{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}, testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}},
- {[]interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}, []interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}},
- } {
- errMsg := qt.Commentf("[%d] %v", i, test.args)
-
- result := Slice(test.args...)
-
- c.Assert(test.expected, qt.DeepEquals, result, errMsg)
- }
-
-}
diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go
deleted file mode 100644
index 3778a3729..000000000
--- a/common/herrors/error_locator.go
+++ /dev/null
@@ -1,255 +0,0 @@
-// 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 errors contains common Hugo errors and error related utilities.
-package herrors
-
-import (
- "io"
- "io/ioutil"
- "path/filepath"
- "strings"
-
- "github.com/gohugoio/hugo/common/text"
-
- "github.com/spf13/afero"
-)
-
-// 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.
-type LineMatcherFn func(m LineMatcher) bool
-
-// SimpleLineMatcher simply matches by line number.
-var SimpleLineMatcher = func(m LineMatcher) bool {
- return m.Position.LineNumber == m.LineNumber
-}
-
-var _ text.Positioner = ErrorContext{}
-
-// 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
-
- position text.Position
-
- // The lexer to use for syntax highlighting.
- // https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages
- ChromaLexer string
-}
-
-// Position returns the text position of this error.
-func (e ErrorContext) Position() text.Position {
- return e.position
-}
-
-var _ causer = (*ErrorWithFileContext)(nil)
-
-// ErrorWithFileContext is an error with some additional file context related
-// to that error.
-type ErrorWithFileContext struct {
- cause error
- ErrorContext
-}
-
-func (e *ErrorWithFileContext) Error() string {
- pos := e.Position()
- if pos.IsValid() {
- return pos.String() + ": " + e.cause.Error()
- }
- return e.cause.Error()
-}
-
-func (e *ErrorWithFileContext) Cause() error {
- return e.cause
-}
-
-// WithFileContextForFile will try to add a file context with lines matching the given matcher.
-// If no match could be found, the original error is returned with false as the second return value.
-func WithFileContextForFile(e error, realFilename, filename string, fs afero.Fs, matcher LineMatcherFn) (error, bool) {
- f, err := fs.Open(filename)
- if err != nil {
- return e, false
- }
- defer f.Close()
- return WithFileContext(e, realFilename, f, matcher)
-}
-
-// WithFileContextForFile will try to add a file context with lines matching the given matcher.
-// If no match could be found, the original error is returned with false as the second return value.
-func WithFileContext(e error, realFilename string, r io.Reader, matcher LineMatcherFn) (error, bool) {
- if e == nil {
- panic("error missing")
- }
- le := UnwrapFileError(e)
-
- if le == nil {
- var ok bool
- if le, ok = ToFileError("", e).(FileError); !ok {
- return e, false
- }
- }
-
- var errCtx ErrorContext
-
- posle := le.Position()
-
- if posle.Offset != -1 {
- errCtx = locateError(r, le, func(m LineMatcher) bool {
- 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 matcher(m)
- })
- } else {
- errCtx = locateError(r, le, matcher)
- }
-
- pos := &errCtx.position
-
- if pos.LineNumber == -1 {
- return e, false
- }
-
- pos.Filename = realFilename
-
- if le.Type() != "" {
- errCtx.ChromaLexer = chromaLexerFromType(le.Type())
- } else {
- errCtx.ChromaLexer = chromaLexerFromFilename(realFilename)
- }
-
- return &ErrorWithFileContext{cause: e, ErrorContext: errCtx}, true
-}
-
-// UnwrapErrorWithFileContext tries to unwrap an ErrorWithFileContext from err.
-// It returns nil if this is not possible.
-func UnwrapErrorWithFileContext(err error) *ErrorWithFileContext {
- for err != nil {
- switch v := err.(type) {
- case *ErrorWithFileContext:
- return v
- case causer:
- err = v.Cause()
- default:
- return nil
- }
- }
- return nil
-}
-
-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")
- }
-
- errCtx := ErrorContext{position: text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}, LinesPos: -1}
-
- b, err := ioutil.ReadAll(r)
- if err != nil {
- return errCtx
- }
-
- pos := &errCtx.position
- lepos := le.Position()
-
- lines := strings.Split(string(b), "\n")
-
- if lepos.ColumnNumber >= 0 {
- pos.ColumnNumber = lepos.ColumnNumber
- }
-
- 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,
- }
- if errCtx.LinesPos == -1 && matches(m) {
- pos.LineNumber = lineNo
- break
- }
-
- posBytes += len(line)
- }
-
- if pos.LineNumber != -1 {
- low := pos.LineNumber - 3
- if low < 0 {
- low = 0
- }
-
- if pos.LineNumber > 2 {
- errCtx.LinesPos = 2
- } else {
- errCtx.LinesPos = pos.LineNumber - 1
- }
-
- high := pos.LineNumber + 2
- if high > len(lines) {
- high = len(lines)
- }
-
- errCtx.Lines = lines[low:high]
-
- }
-
- return errCtx
-}
diff --git a/common/herrors/error_locator_test.go b/common/herrors/error_locator_test.go
deleted file mode 100644
index cce94166f..000000000
--- a/common/herrors/error_locator_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// 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 errors 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) bool {
- return strings.Contains(m.Line, "THEONE")
- }
-
- lines := `LINE 1
-LINE 2
-LINE 3
-LINE 4
-This is THEONE
-LINE 6
-LINE 7
-LINE 8
-`
-
- location := locateErrorInString(lines, lineMatcher)
- c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"})
-
- pos := location.Position()
- c.Assert(pos.LineNumber, qt.Equals, 5)
- c.Assert(location.LinesPos, qt.Equals, 2)
-
- c.Assert(locateErrorInString(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
-
- location = locateErrorInString(`L1
-This is THEONE
-L2
-`, lineMatcher)
- c.Assert(location.Position().LineNumber, qt.Equals, 2)
- c.Assert(location.LinesPos, qt.Equals, 1)
- c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""})
-
- location = locateErrorInString(`This is THEONE
-L2
-`, lineMatcher)
- c.Assert(location.LinesPos, qt.Equals, 0)
- c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""})
-
- location = locateErrorInString(`L1
-This THEONE
-`, lineMatcher)
- c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""})
- c.Assert(location.LinesPos, qt.Equals, 1)
-
- location = locateErrorInString(`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)
- c.Assert(location.Position().LineNumber, qt.Equals, -1)
- c.Assert(location.LinesPos, qt.Equals, -1)
- c.Assert(len(location.Lines), qt.Equals, 0)
-
- lineMatcher = func(m LineMatcher) bool {
- return m.LineNumber == 6
- }
-
- location = locateErrorInString(`A
-B
-C
-D
-E
-F
-G
-H
-I
-J`, lineMatcher)
-
- c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"})
- c.Assert(location.Position().LineNumber, qt.Equals, 6)
- c.Assert(location.LinesPos, qt.Equals, 2)
-
- // Test match EOF
- lineMatcher = func(m LineMatcher) bool {
- return m.LineNumber == 4
- }
-
- location = locateErrorInString(`A
-B
-C
-`, lineMatcher)
-
- c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""})
- c.Assert(location.Position().LineNumber, qt.Equals, 4)
- c.Assert(location.LinesPos, qt.Equals, 2)
-
- offsetMatcher := func(m LineMatcher) bool {
- return m.Offset == 1
- }
-
- location = locateErrorInString(`A
-B
-C
-D
-E`, offsetMatcher)
-
- c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"})
- c.Assert(location.Position().LineNumber, qt.Equals, 2)
- c.Assert(location.LinesPos, qt.Equals, 1)
-
-}
diff --git a/common/herrors/errors.go b/common/herrors/errors.go
deleted file mode 100644
index ff8eab116..000000000
--- a/common/herrors/errors.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 contains common Hugo errors and error related utilities.
-package herrors
-
-import (
- "errors"
- "fmt"
- "io"
- "os"
- "runtime/debug"
-
- _errors "github.com/pkg/errors"
-)
-
-// As defined in https://godoc.org/github.com/pkg/errors
-type causer interface {
- Cause() error
-}
-
-type stackTracer interface {
- StackTrace() _errors.StackTrace
-}
-
-// PrintStackTrace prints the error's stack trace to stdoud.
-func PrintStackTrace(err error) {
- FprintStackTrace(os.Stdout, err)
-}
-
-// FprintStackTrace prints the error's stack trace to w.
-func FprintStackTrace(w io.Writer, err error) {
- if err, ok := err.(stackTracer); ok {
- for _, f := range err.StackTrace() {
- fmt.Fprintf(w, "%+s:%d\n", f, f)
- }
- }
-}
-
-// 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 ...interface{}) {
- if r := recover(); r != nil {
- fmt.Println("ERR:", r)
- args = append(args, "stacktrace from panic: \n"+string(debug.Stack()), "\n")
- fmt.Println(args...)
- }
-
-}
-
-// 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 = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")
diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go
deleted file mode 100644
index 5af84adf5..000000000
--- a/common/herrors/file_error.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// 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
-// limitatio ns under the License.
-
-package herrors
-
-import (
- "encoding/json"
-
- "github.com/gohugoio/hugo/common/text"
-
- "github.com/pkg/errors"
-)
-
-var (
- _ causer = (*fileError)(nil)
-)
-
-// FileError represents an error when handling a file: Parsing a config file,
-// execute a template etc.
-type FileError interface {
- error
-
- text.Positioner
-
- // A string identifying the type of file, e.g. JSON, TOML, markdown etc.
- Type() string
-}
-
-var _ FileError = (*fileError)(nil)
-
-type fileError struct {
- position text.Position
-
- fileType string
-
- cause error
-}
-
-// Position returns the text position of this error.
-func (e fileError) Position() text.Position {
- return e.position
-}
-
-func (e *fileError) Type() string {
- return e.fileType
-}
-
-func (e *fileError) Error() string {
- if e.cause == nil {
- return ""
- }
- return e.cause.Error()
-}
-
-func (f *fileError) Cause() error {
- return f.cause
-}
-
-// NewFileError creates a new FileError.
-func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError {
- pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber}
- return &fileError{cause: err, fileType: fileType, position: 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
- case causer:
- err = v.Cause()
- default:
- return nil
- }
- }
- return nil
-}
-
-// ToFileErrorWithOffset will return a new FileError with a line number
-// with the given offset from the original.
-func ToFileErrorWithOffset(fe FileError, offset int) FileError {
- pos := fe.Position()
- return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset)
-}
-
-// ToFileErrorWithOffset will return a new FileError with the given line number.
-func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError {
- pos := fe.Position()
- pos.LineNumber = lineNumber
- return &fileError{cause: fe, fileType: fe.Type(), position: pos}
-}
-
-// ToFileError will convert the given error to an error supporting
-// the FileError interface.
-func ToFileError(fileType string, err error) FileError {
- for _, handle := range lineNumberExtractors {
- lno, col := handle(err)
- offset, typ := extractOffsetAndType(err)
- if fileType == "" {
- fileType = typ
- }
-
- if lno > 0 || offset != -1 {
- return NewFileError(fileType, offset, lno, col, err)
- }
- }
- // Fall back to the pointing to line number 1.
- return NewFileError(fileType, -1, 1, 1, err)
-}
-
-func extractOffsetAndType(e error) (int, string) {
- e = errors.Cause(e)
- switch v := e.(type) {
- case *json.UnmarshalTypeError:
- return int(v.Offset), "json"
- case *json.SyntaxError:
- return int(v.Offset), "json"
- default:
- return -1, ""
- }
-}
diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go
deleted file mode 100644
index b1b5c5a02..000000000
--- a/common/herrors/file_error_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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 (
- "testing"
-
- "github.com/pkg/errors"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestToLineNumberError(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("error in front matter: Near line 32 (last key parsed 'title')"), 0, 32, 1},
- {errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7},
- } {
-
- got := ToFileError("template", test.in)
-
- errMsg := qt.Commentf("[%d][%T]", i, got)
- le, ok := got.(FileError)
- c.Assert(ok, qt.Equals, true)
-
- c.Assert(ok, qt.Equals, true, errMsg)
- pos := le.Position()
- c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg)
- c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg)
- c.Assert(errors.Cause(got), qt.Not(qt.IsNil))
- }
-
-}
diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go
deleted file mode 100644
index 93969b967..000000000
--- a/common/herrors/line_number_extractors.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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
-// limitatio ns under the License.
-
-package herrors
-
-import (
- "regexp"
- "strconv"
-)
-
-var lineNumberExtractors = []lineNumberExtractor{
- // Template/shortcode parse errors
- newLineNumberErrHandlerFromRegexp(".*:(\\d+):(\\d*):"),
- newLineNumberErrHandlerFromRegexp(".*:(\\d+):"),
-
- // TOML parse errors
- newLineNumberErrHandlerFromRegexp(".*Near line (\\d+)(\\s.*)"),
-
- // YAML parse errors
- newLineNumberErrHandlerFromRegexp("line (\\d+):"),
-
- // i18n bundle errors
- newLineNumberErrHandlerFromRegexp("\\((\\d+),\\s(\\d*)"),
-}
-
-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 -1, col
- }
-}
diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go
deleted file mode 100644
index db7b208b5..000000000
--- a/common/hreflect/helpers.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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 hreflect contains reflect helpers.
-package hreflect
-
-import (
- "reflect"
-
- "github.com/gohugoio/hugo/common/types"
-)
-
-// IsTruthful returns whether in represents a truthful value.
-// See IsTruthfulValue
-func IsTruthful(in interface{}) bool {
- switch v := in.(type) {
- case reflect.Value:
- return IsTruthfulValue(v)
- default:
- return IsTruthfulValue(reflect.ValueOf(in))
- }
-
-}
-
-var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem()
-
-// IsTruthfulValue returns whether the given value has a meaningful truth value.
-// This is based on template.IsTrue in Go's stdlib, but also considers
-// IsZero and any interface value will be unwrapped before it's considered
-// for truthfulness.
-//
-// Based on:
-// https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L306
-func IsTruthfulValue(val reflect.Value) (truth bool) {
- val = indirectInterface(val)
-
- if !val.IsValid() {
- // Something like var x interface{}, never set. It's a form of nil.
- return
- }
-
- if val.Type().Implements(zeroType) {
- return !val.Interface().(types.Zeroer).IsZero()
- }
-
- switch val.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- truth = val.Len() > 0
- case reflect.Bool:
- truth = val.Bool()
- case reflect.Complex64, reflect.Complex128:
- truth = val.Complex() != 0
- case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
- truth = !val.IsNil()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- truth = val.Int() != 0
- case reflect.Float32, reflect.Float64:
- truth = val.Float() != 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- truth = val.Uint() != 0
- case reflect.Struct:
- truth = true // Struct values are always true.
- default:
- return
- }
-
- return
-}
-
-// Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
-func indirectInterface(v reflect.Value) reflect.Value {
- if v.Kind() != reflect.Interface {
- return v
- }
- if v.IsNil() {
- return reflect.Value{}
- }
- return v.Elem()
-}
diff --git a/common/hreflect/helpers_test.go b/common/hreflect/helpers_test.go
deleted file mode 100644
index 480ccb27a..000000000
--- a/common/hreflect/helpers_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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 hreflect
-
-import (
- "reflect"
- "testing"
- "time"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestIsTruthful(t *testing.T) {
- c := qt.New(t)
-
- c.Assert(IsTruthful(true), qt.Equals, true)
- c.Assert(IsTruthful(false), qt.Equals, false)
- c.Assert(IsTruthful(time.Now()), qt.Equals, true)
- c.Assert(IsTruthful(time.Time{}), qt.Equals, false)
-}
-
-func BenchmarkIsTruthFul(b *testing.B) {
- v := reflect.ValueOf("Hugo")
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- if !IsTruthfulValue(v) {
- b.Fatal("not truthful")
- }
- }
-}
diff --git a/common/hugio/copy.go b/common/hugio/copy.go
deleted file mode 100644
index 2b756cb44..000000000
--- a/common/hugio/copy.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 hugio
-
-import (
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
-
- "github.com/pkg/errors"
-
- "github.com/spf13/afero"
-)
-
-// CopyFile copies a file.
-func CopyFile(fs afero.Fs, from, to string) error {
- sf, err := os.Open(from)
- if err != nil {
- return err
- }
- defer sf.Close()
- df, err := os.Create(to)
- if err != nil {
- return err
- }
- defer df.Close()
- _, err = io.Copy(df, sf)
- if err == nil {
- si, err := os.Stat(from)
- if err != nil {
- err = os.Chmod(to, si.Mode())
-
- if err != nil {
- return err
- }
- }
-
- }
- return nil
-}
-
-// CopyDir copies a directory.
-func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
- fi, err := os.Stat(from)
- if err != nil {
- return err
- }
-
- if !fi.IsDir() {
- return errors.Errorf("%q is not a directory", from)
- }
-
- err = fs.MkdirAll(to, 0777) // before umask
- if err != nil {
- return err
- }
-
- entries, _ := ioutil.ReadDir(from)
- for _, entry := range entries {
- fromFilename := filepath.Join(from, entry.Name())
- toFilename := filepath.Join(to, entry.Name())
- if entry.IsDir() {
- if shouldCopy != nil && !shouldCopy(fromFilename) {
- continue
- }
- if err := CopyDir(fs, fromFilename, toFilename, shouldCopy); err != nil {
- return err
- }
- } else {
- if err := CopyFile(fs, fromFilename, toFilename); err != nil {
- return err
- }
- }
-
- }
-
- return nil
-}
diff --git a/common/hugio/readers.go b/common/hugio/readers.go
deleted file mode 100644
index 8c901dd24..000000000
--- a/common/hugio/readers.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 hugio
-
-import (
- "io"
- "strings"
-)
-
-// ReadSeeker wraps io.Reader and io.Seeker.
-type ReadSeeker interface {
- io.Reader
- io.Seeker
-}
-
-// ReadSeekCloser is implemented by afero.File. We use this as the common type for
-// content in Resource objects, even for strings.
-type ReadSeekCloser interface {
- ReadSeeker
- io.Closer
-}
-
-// ReadSeekerNoOpCloser implements ReadSeekCloser by doing nothing in Close.
-// TODO(bep) rename this and simila to ReadSeekerNopCloser, naming used in stdlib, which kind of makes sense.
-type ReadSeekerNoOpCloser struct {
- ReadSeeker
-}
-
-// Close does nothing.
-func (r ReadSeekerNoOpCloser) Close() error {
- return nil
-}
-
-// NewReadSeekerNoOpCloser creates a new ReadSeekerNoOpCloser with the given ReadSeeker.
-func NewReadSeekerNoOpCloser(r ReadSeeker) ReadSeekerNoOpCloser {
- return ReadSeekerNoOpCloser{r}
-}
-
-// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser
-// from the given string.
-func NewReadSeekerNoOpCloserFromString(content string) ReadSeekerNoOpCloser {
- return ReadSeekerNoOpCloser{strings.NewReader(content)}
-}
diff --git a/common/hugio/writers.go b/common/hugio/writers.go
deleted file mode 100644
index 82c4dca52..000000000
--- a/common/hugio/writers.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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 hugio
-
-import (
- "io"
- "io/ioutil"
-)
-
-type multiWriteCloser struct {
- io.Writer
- closers []io.WriteCloser
-}
-
-func (m multiWriteCloser) Close() error {
- var err error
- for _, c := range m.closers {
- if closeErr := c.Close(); err != nil {
- err = closeErr
- }
- }
- return err
-}
-
-// NewMultiWriteCloser creates a new io.WriteCloser that duplicates its writes to all the
-// provided writers.
-func NewMultiWriteCloser(writeClosers ...io.WriteCloser) io.WriteCloser {
- writers := make([]io.Writer, len(writeClosers))
- for i, w := range writeClosers {
- writers[i] = w
- }
- return multiWriteCloser{Writer: io.MultiWriter(writers...), closers: writeClosers}
-}
-
-// ToWriteCloser creates an io.WriteCloser from the given io.Writer.
-// If it's not already, one will be created with a Close method that does nothing.
-func ToWriteCloser(w io.Writer) io.WriteCloser {
- if rw, ok := w.(io.WriteCloser); ok {
- return rw
- }
-
- return struct {
- io.Writer
- io.Closer
- }{
- w,
- ioutil.NopCloser(nil),
- }
-}
-
-// ToReadCloser creates an io.ReadCloser from the given io.Reader.
-// If it's not already, one will be created with a Close method that does nothing.
-func ToReadCloser(r io.Reader) io.ReadCloser {
- if rc, ok := r.(io.ReadCloser); ok {
- return rc
- }
-
- return struct {
- io.Reader
- io.Closer
- }{
- r,
- ioutil.NopCloser(nil),
- }
-}
diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go
deleted file mode 100644
index 62d923bf0..000000000
--- a/common/hugo/hugo.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 hugo
-
-import (
- "fmt"
- "html/template"
-)
-
-const (
- EnvironmentDevelopment = "development"
- EnvironmentProduction = "production"
-)
-
-var (
- // commitHash contains the current Git revision. Use make to build to make
- // sure this gets set.
- commitHash string
-
- // buildDate contains the date of the current build.
- buildDate string
-)
-
-// Info contains information about the current Hugo environment
-type Info struct {
- CommitHash string
- BuildDate string
-
- // The build environment.
- // Defaults are "production" (hugo) and "development" (hugo server).
- // This can also be set by the user.
- // It can be any string, but it will be all lower case.
- Environment string
-}
-
-// Version returns the current version as a comparable version string.
-func (i Info) Version() VersionString {
- return CurrentVersion.Version()
-}
-
-// Generator a Hugo meta generator HTML tag.
-func (i Info) Generator() template.HTML {
- return template.HTML(fmt.Sprintf(``, CurrentVersion.String()))
-}
-
-// NewInfo creates a new Hugo Info object.
-func NewInfo(environment string) Info {
- if environment == "" {
- environment = EnvironmentProduction
- }
- return Info{
- CommitHash: commitHash,
- BuildDate: buildDate,
- Environment: environment,
- }
-}
diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go
deleted file mode 100644
index 5be575b62..000000000
--- a/common/hugo/hugo_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 hugo
-
-import (
- "fmt"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestHugoInfo(t *testing.T) {
- c := qt.New(t)
-
- hugoInfo := NewInfo("")
-
- c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
- c.Assert(fmt.Sprintf("%T", VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
- c.Assert(hugoInfo.CommitHash, qt.Equals, commitHash)
- c.Assert(hugoInfo.BuildDate, qt.Equals, buildDate)
- c.Assert(hugoInfo.Environment, qt.Equals, "production")
- c.Assert(string(hugoInfo.Generator()), qt.Contains, fmt.Sprintf("Hugo %s", hugoInfo.Version()))
-
-}
diff --git a/common/hugo/vars_extended.go b/common/hugo/vars_extended.go
deleted file mode 100644
index bb96bade6..000000000
--- a/common/hugo/vars_extended.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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.
-
-// +build extended
-
-package hugo
-
-var IsExtended = true
diff --git a/common/hugo/vars_regular.go b/common/hugo/vars_regular.go
deleted file mode 100644
index fae18df14..000000000
--- a/common/hugo/vars_regular.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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.
-
-// +build !extended
-
-package hugo
-
-var IsExtended = false
diff --git a/common/hugo/version.go b/common/hugo/version.go
deleted file mode 100644
index 848393f97..000000000
--- a/common/hugo/version.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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 hugo
-
-import (
- "fmt"
- "strconv"
-
- "runtime"
- "strings"
-
- "github.com/gohugoio/hugo/compare"
- "github.com/spf13/cast"
-)
-
-// Version represents the Hugo build version.
-type Version struct {
- // Major and minor version.
- Number float32
-
- // Increment this for bug releases
- PatchLevel int
-
- // HugoVersionSuffix is the suffix used in the Hugo version string.
- // It will be blank for release versions.
- Suffix string
-}
-
-var (
- _ compare.Eqer = (*VersionString)(nil)
- _ compare.Comparer = (*VersionString)(nil)
-)
-
-func (v Version) String() string {
- return version(v.Number, v.PatchLevel, v.Suffix)
-}
-
-// Version returns the Hugo version.
-func (v Version) Version() VersionString {
- return VersionString(v.String())
-}
-
-// VersionString represents a Hugo version string.
-type VersionString string
-
-func (h VersionString) String() string {
- return string(h)
-}
-
-// Compare implements the compare.Comparer interface.
-func (h VersionString) Compare(other interface{}) int {
- v := MustParseVersion(h.String())
- return compareVersionsWithSuffix(v.Number, v.PatchLevel, v.Suffix, other)
-}
-
-// Eq implements the compare.Eqer interface.
-func (h VersionString) Eq(other interface{}) bool {
- s, err := cast.ToStringE(other)
- if err != nil {
- return false
- }
- return s == h.String()
-}
-
-var versionSuffixes = []string{"-test", "-DEV"}
-
-// ParseVersion parses a version string.
-func ParseVersion(s string) (Version, error) {
- var vv Version
- for _, suffix := range versionSuffixes {
- if strings.HasSuffix(s, suffix) {
- vv.Suffix = suffix
- s = strings.TrimSuffix(s, suffix)
- }
- }
-
- v, p := parseVersion(s)
-
- vv.Number = v
- vv.PatchLevel = p
-
- return vv, nil
-}
-
-// MustParseVersion parses a version string
-// and panics if any error occurs.
-func MustParseVersion(s string) Version {
- vv, err := ParseVersion(s)
- if err != nil {
- panic(err)
- }
- return vv
-}
-
-// ReleaseVersion represents the release version.
-func (v Version) ReleaseVersion() Version {
- v.Suffix = ""
- return v
-}
-
-// Next returns the next Hugo release version.
-func (v Version) Next() Version {
- return Version{Number: v.Number + 0.01}
-}
-
-// Prev returns the previous Hugo release version.
-func (v Version) Prev() Version {
- return Version{Number: v.Number - 0.01}
-}
-
-// NextPatchLevel returns the next patch/bugfix Hugo version.
-// This will be a patch increment on the previous Hugo version.
-func (v Version) NextPatchLevel(level int) Version {
- return Version{Number: v.Number - 0.01, PatchLevel: level}
-}
-
-// BuildVersionString creates a version string. This is what you see when
-// running "hugo version".
-func BuildVersionString() string {
- program := "Hugo Static Site Generator"
-
- version := "v" + CurrentVersion.String()
- if commitHash != "" {
- version += "-" + strings.ToUpper(commitHash)
- }
- if IsExtended {
- version += "/extended"
- }
-
- osArch := runtime.GOOS + "/" + runtime.GOARCH
-
- date := buildDate
- if date == "" {
- date = "unknown"
- }
-
- return fmt.Sprintf("%s %s %s BuildDate: %s", program, version, osArch, date)
-
-}
-
-func version(version float32, patchVersion int, suffix string) string {
- if patchVersion > 0 || version > 0.53 {
- return fmt.Sprintf("%.2f.%d%s", version, patchVersion, suffix)
- }
- return fmt.Sprintf("%.2f%s", version, suffix)
-}
-
-// CompareVersion compares the given version string or number against the
-// running Hugo version.
-// It returns -1 if the given version is less than, 0 if equal and 1 if greater than
-// the running version.
-func CompareVersion(version interface{}) int {
- return compareVersionsWithSuffix(CurrentVersion.Number, CurrentVersion.PatchLevel, CurrentVersion.Suffix, version)
-}
-
-func compareVersions(inVersion float32, inPatchVersion int, in interface{}) int {
- return compareVersionsWithSuffix(inVersion, inPatchVersion, "", in)
-}
-
-func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix string, in interface{}) int {
- var c int
- switch d := in.(type) {
- case float64:
- c = compareFloatVersions(inVersion, float32(d))
- case float32:
- c = compareFloatVersions(inVersion, d)
- case int:
- c = compareFloatVersions(inVersion, float32(d))
- case int32:
- c = compareFloatVersions(inVersion, float32(d))
- case int64:
- c = compareFloatVersions(inVersion, float32(d))
- default:
- s, err := cast.ToStringE(in)
- if err != nil {
- return -1
- }
-
- v, err := ParseVersion(s)
- if err != nil {
- return -1
- }
-
- if v.Number == inVersion && v.PatchLevel == inPatchVersion {
- return strings.Compare(suffix, v.Suffix)
- }
-
- if v.Number < inVersion || (v.Number == inVersion && v.PatchLevel < inPatchVersion) {
- return -1
- }
-
- return 1
- }
-
- if c == 0 && suffix != "" {
- return 1
- }
-
- return c
-}
-
-func parseVersion(s string) (float32, int) {
- var (
- v float32
- p int
- )
-
- if strings.Count(s, ".") == 2 {
- li := strings.LastIndex(s, ".")
- p = cast.ToInt(s[li+1:])
- s = s[:li]
- }
-
- v = float32(cast.ToFloat64(s))
-
- return v, p
-}
-
-func compareFloatVersions(version float32, v float32) int {
- if v == version {
- return 0
- }
- if v < version {
- return -1
- }
- return 1
-}
-
-func GoMinorVersion() int {
- return goMinorVersion(runtime.Version())
-}
-
-func goMinorVersion(version string) int {
- if strings.HasPrefix(version, "devel") {
- return 9999 // magic
- }
- i, _ := strconv.Atoi(strings.Split(version, ".")[1])
- return i
-
-}
diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go
deleted file mode 100644
index 8e023e264..000000000
--- a/common/hugo/version_current.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 hugo
-
-// CurrentVersion represents the current build version.
-// This should be the only one.
-var CurrentVersion = Version{
- Number: 0.59,
- PatchLevel: 0,
- Suffix: "-DEV",
-}
diff --git a/common/hugo/version_test.go b/common/hugo/version_test.go
deleted file mode 100644
index e0cd0e6e8..000000000
--- a/common/hugo/version_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 hugo
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestHugoVersion(t *testing.T) {
- c := qt.New(t)
-
- c.Assert(version(0.15, 0, "-DEV"), qt.Equals, "0.15-DEV")
- c.Assert(version(0.15, 2, "-DEV"), qt.Equals, "0.15.2-DEV")
-
- v := Version{Number: 0.21, PatchLevel: 0, Suffix: "-DEV"}
-
- c.Assert(v.ReleaseVersion().String(), qt.Equals, "0.21")
- c.Assert(v.String(), qt.Equals, "0.21-DEV")
- c.Assert(v.Next().String(), qt.Equals, "0.22")
- nextVersionString := v.Next().Version()
- c.Assert(nextVersionString.String(), qt.Equals, "0.22")
- c.Assert(nextVersionString.Eq("0.22"), qt.Equals, true)
- c.Assert(nextVersionString.Eq("0.21"), qt.Equals, false)
- c.Assert(nextVersionString.Eq(nextVersionString), qt.Equals, true)
- c.Assert(v.NextPatchLevel(3).String(), qt.Equals, "0.20.3")
-
- // We started to use full semver versions even for main
- // releases in v0.54.0
- v = Version{Number: 0.53, PatchLevel: 0}
- c.Assert(v.String(), qt.Equals, "0.53")
- c.Assert(v.Next().String(), qt.Equals, "0.54.0")
- c.Assert(v.Next().Next().String(), qt.Equals, "0.55.0")
- v = Version{Number: 0.54, PatchLevel: 0, Suffix: "-DEV"}
- c.Assert(v.String(), qt.Equals, "0.54.0-DEV")
-}
-
-func TestCompareVersions(t *testing.T) {
- c := qt.New(t)
-
- c.Assert(compareVersions(0.20, 0, 0.20), qt.Equals, 0)
- c.Assert(compareVersions(0.20, 0, float32(0.20)), qt.Equals, 0)
- c.Assert(compareVersions(0.20, 0, float64(0.20)), qt.Equals, 0)
- c.Assert(compareVersions(0.19, 1, 0.20), qt.Equals, 1)
- c.Assert(compareVersions(0.19, 3, "0.20.2"), qt.Equals, 1)
- c.Assert(compareVersions(0.19, 1, 0.01), qt.Equals, -1)
- c.Assert(compareVersions(0, 1, 3), qt.Equals, 1)
- c.Assert(compareVersions(0, 1, int32(3)), qt.Equals, 1)
- c.Assert(compareVersions(0, 1, int64(3)), qt.Equals, 1)
- c.Assert(compareVersions(0.20, 0, "0.20"), qt.Equals, 0)
- c.Assert(compareVersions(0.20, 1, "0.20.1"), qt.Equals, 0)
- c.Assert(compareVersions(0.20, 1, "0.20"), qt.Equals, -1)
- c.Assert(compareVersions(0.20, 0, "0.20.1"), qt.Equals, 1)
- c.Assert(compareVersions(0.20, 1, "0.20.2"), qt.Equals, 1)
- c.Assert(compareVersions(0.21, 1, "0.22.1"), qt.Equals, 1)
- c.Assert(compareVersions(0.22, 0, "0.22-DEV"), qt.Equals, -1)
- c.Assert(compareVersions(0.22, 0, "0.22.1-DEV"), qt.Equals, 1)
- c.Assert(compareVersionsWithSuffix(0.22, 0, "-DEV", "0.22"), qt.Equals, 1)
- c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22"), qt.Equals, -1)
- c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22.1-DEV"), qt.Equals, 0)
-}
-
-func TestParseHugoVersion(t *testing.T) {
- c := qt.New(t)
-
- c.Assert(MustParseVersion("0.25").String(), qt.Equals, "0.25")
- c.Assert(MustParseVersion("0.25.2").String(), qt.Equals, "0.25.2")
- c.Assert(MustParseVersion("0.25-test").String(), qt.Equals, "0.25-test")
- c.Assert(MustParseVersion("0.25-DEV").String(), qt.Equals, "0.25-DEV")
-}
-
-func TestGoMinorVersion(t *testing.T) {
- c := qt.New(t)
- c.Assert(goMinorVersion("go1.12.5"), qt.Equals, 12)
- c.Assert(GoMinorVersion() >= 11, qt.Equals, true)
-}
diff --git a/common/loggers/loggers.go b/common/loggers/loggers.go
deleted file mode 100644
index 082fd1487..000000000
--- a/common/loggers/loggers.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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 loggers
-
-import (
- "bytes"
- "io"
- "io/ioutil"
- "log"
- "os"
- "regexp"
-
- "github.com/gohugoio/hugo/common/terminal"
-
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var (
- // Counts ERROR logs to the global jww logger.
- GlobalErrorCounter *jww.Counter
-)
-
-func init() {
- GlobalErrorCounter = &jww.Counter{}
- jww.SetLogListeners(jww.LogCounter(GlobalErrorCounter, jww.LevelError))
-}
-
-// Logger wraps a *loggers.Logger and some other related logging state.
-type Logger struct {
- *jww.Notepad
- ErrorCounter *jww.Counter
- WarnCounter *jww.Counter
-
- // This is only set in server mode.
- errors *bytes.Buffer
-}
-
-func (l *Logger) Errors() string {
- if l.errors == nil {
- return ""
- }
- return ansiColorRe.ReplaceAllString(l.errors.String(), "")
-}
-
-// Reset resets the logger's internal state.
-func (l *Logger) Reset() {
- l.ErrorCounter.Reset()
- if l.errors != nil {
- l.errors.Reset()
- }
-}
-
-// NewLogger creates a new Logger for the given thresholds
-func NewLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) *Logger {
- return newLogger(stdoutThreshold, logThreshold, outHandle, logHandle, saveErrors)
-}
-
-// NewDebugLogger is a convenience function to create a debug logger.
-func NewDebugLogger() *Logger {
- return newBasicLogger(jww.LevelDebug)
-}
-
-// NewWarningLogger is a convenience function to create a warning logger.
-func NewWarningLogger() *Logger {
- return newBasicLogger(jww.LevelWarn)
-}
-
-// NewErrorLogger is a convenience function to create an error logger.
-func NewErrorLogger() *Logger {
- return newBasicLogger(jww.LevelError)
-}
-
-var (
- ansiColorRe = regexp.MustCompile("(?s)\\033\\[\\d*(;\\d*)*m")
- errorRe = regexp.MustCompile("^(ERROR|FATAL|WARN)")
-)
-
-type ansiCleaner struct {
- w io.Writer
-}
-
-func (a ansiCleaner) Write(p []byte) (n int, err error) {
- return a.w.Write(ansiColorRe.ReplaceAll(p, []byte("")))
-}
-
-type labelColorizer struct {
- w io.Writer
-}
-
-func (a labelColorizer) Write(p []byte) (n int, err error) {
- replaced := errorRe.ReplaceAllStringFunc(string(p), func(m string) string {
- switch m {
- case "ERROR", "FATAL":
- return terminal.Error(m)
- case "WARN":
- return terminal.Warning(m)
- default:
- return m
- }
- })
- // io.MultiWriter will abort if we return a bigger write count than input
- // bytes, so we lie a little.
- _, err = a.w.Write([]byte(replaced))
- return len(p), err
-
-}
-
-// InitGlobalLogger initializes the global logger, used in some rare cases.
-func InitGlobalLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer) {
- outHandle, logHandle = getLogWriters(outHandle, logHandle)
-
- jww.SetStdoutOutput(outHandle)
- jww.SetLogOutput(logHandle)
- jww.SetLogThreshold(logThreshold)
- jww.SetStdoutThreshold(stdoutThreshold)
-
-}
-
-func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
- isTerm := terminal.IsTerminal(os.Stdout)
- if logHandle != ioutil.Discard && isTerm {
- // Remove any Ansi coloring from log output
- logHandle = ansiCleaner{w: logHandle}
- }
-
- if isTerm {
- outHandle = labelColorizer{w: outHandle}
- }
-
- return outHandle, logHandle
-
-}
-
-func newLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) *Logger {
- errorCounter := &jww.Counter{}
- warnCounter := &jww.Counter{}
- outHandle, logHandle = getLogWriters(outHandle, logHandle)
-
- listeners := []jww.LogListener{jww.LogCounter(errorCounter, jww.LevelError), jww.LogCounter(warnCounter, jww.LevelWarn)}
- var errorBuff *bytes.Buffer
- if saveErrors {
- errorBuff = new(bytes.Buffer)
- errorCapture := func(t jww.Threshold) io.Writer {
- if t != jww.LevelError {
- // Only interested in ERROR
- return nil
- }
-
- return errorBuff
- }
-
- listeners = append(listeners, errorCapture)
- }
-
- return &Logger{
- Notepad: jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime, listeners...),
- ErrorCounter: errorCounter,
- WarnCounter: warnCounter,
- errors: errorBuff,
- }
-}
-
-func newBasicLogger(t jww.Threshold) *Logger {
- return newLogger(t, jww.LevelError, os.Stdout, ioutil.Discard, false)
-}
diff --git a/common/loggers/loggers_test.go b/common/loggers/loggers_test.go
deleted file mode 100644
index f572ba170..000000000
--- a/common/loggers/loggers_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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 loggers
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestLogger(t *testing.T) {
- c := qt.New(t)
- l := NewWarningLogger()
-
- l.ERROR.Println("One error")
- l.ERROR.Println("Two error")
- l.WARN.Println("A warning")
-
- c.Assert(l.ErrorCounter.Count(), qt.Equals, uint64(2))
-
-}
diff --git a/common/maps/maps.go b/common/maps/maps.go
deleted file mode 100644
index e0d4f964d..000000000
--- a/common/maps/maps.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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 maps
-
-import (
- "strings"
-
- "github.com/gobwas/glob"
-
- "github.com/spf13/cast"
-)
-
-// ToLower makes all the keys in the given map lower cased and will do so
-// recursively.
-// Notes:
-// * This will modify the map given.
-// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}.
-func ToLower(m map[string]interface{}) {
- for k, v := range m {
- switch v.(type) {
- case map[interface{}]interface{}:
- v = cast.ToStringMap(v)
- ToLower(v.(map[string]interface{}))
- case map[string]interface{}:
- ToLower(v.(map[string]interface{}))
- }
-
- lKey := strings.ToLower(k)
- if k != lKey {
- delete(m, k)
- m[lKey] = v
- }
-
- }
-}
-
-type keyRename struct {
- pattern glob.Glob
- newKey string
-}
-
-// KeyRenamer supports renaming of keys in a map.
-type KeyRenamer struct {
- renames []keyRename
-}
-
-// NewKeyRenamer creates a new KeyRenamer given a list of pattern and new key
-// value pairs.
-func NewKeyRenamer(patternKeys ...string) (KeyRenamer, error) {
- var renames []keyRename
- for i := 0; i < len(patternKeys); i += 2 {
- g, err := glob.Compile(strings.ToLower(patternKeys[i]), '/')
- if err != nil {
- return KeyRenamer{}, err
- }
- renames = append(renames, keyRename{pattern: g, newKey: patternKeys[i+1]})
- }
-
- return KeyRenamer{renames: renames}, nil
-}
-
-func (r KeyRenamer) getNewKey(keyPath string) string {
- for _, matcher := range r.renames {
- if matcher.pattern.Match(keyPath) {
- return matcher.newKey
- }
- }
-
- return ""
-}
-
-// Rename renames the keys in the given map according
-// to the patterns in the current KeyRenamer.
-func (r KeyRenamer) Rename(m map[string]interface{}) {
- r.renamePath("", m)
-}
-
-func (KeyRenamer) keyPath(k1, k2 string) string {
- k1, k2 = strings.ToLower(k1), strings.ToLower(k2)
- if k1 == "" {
- return k2
- } else {
- return k1 + "/" + k2
- }
-}
-
-func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) {
- for key, val := range m {
- keyPath := r.keyPath(parentKeyPath, key)
- switch val.(type) {
- case map[interface{}]interface{}:
- val = cast.ToStringMap(val)
- r.renamePath(keyPath, val.(map[string]interface{}))
- case map[string]interface{}:
- r.renamePath(keyPath, val.(map[string]interface{}))
- }
-
- newKey := r.getNewKey(keyPath)
-
- if newKey != "" {
- delete(m, key)
- m[newKey] = val
- }
- }
-}
diff --git a/common/maps/maps_get.go b/common/maps/maps_get.go
deleted file mode 100644
index 9289991ae..000000000
--- a/common/maps/maps_get.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 maps
-
-import (
- "github.com/spf13/cast"
-)
-
-// GetString tries to get a value with key from map m and convert it to a string.
-// It will return an empty string if not found or if it cannot be convertd to a string.
-func GetString(m map[string]interface{}, key string) string {
- if m == nil {
- return ""
- }
- v, found := m[key]
- if !found {
- return ""
- }
- return cast.ToString(v)
-}
diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go
deleted file mode 100644
index 8b0aa5eb9..000000000
--- a/common/maps/maps_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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 maps
-
-import (
- "reflect"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestToLower(t *testing.T) {
-
- tests := []struct {
- input map[string]interface{}
- expected map[string]interface{}
- }{
- {
- map[string]interface{}{
- "abC": 32,
- },
- map[string]interface{}{
- "abc": 32,
- },
- },
- {
- map[string]interface{}{
- "abC": 32,
- "deF": map[interface{}]interface{}{
- 23: "A value",
- 24: map[string]interface{}{
- "AbCDe": "A value",
- "eFgHi": "Another value",
- },
- },
- "gHi": map[string]interface{}{
- "J": 25,
- },
- },
- map[string]interface{}{
- "abc": 32,
- "def": map[string]interface{}{
- "23": "A value",
- "24": map[string]interface{}{
- "abcde": "A value",
- "efghi": "Another value",
- },
- },
- "ghi": map[string]interface{}{
- "j": 25,
- },
- },
- },
- }
-
- for i, test := range tests {
- // ToLower modifies input.
- ToLower(test.input)
- if !reflect.DeepEqual(test.expected, test.input) {
- t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
- }
- }
-}
-
-func TestRenameKeys(t *testing.T) {
- c := qt.New(t)
-
- m := map[string]interface{}{
- "a": 32,
- "ren1": "m1",
- "ren2": "m1_2",
- "sub": map[string]interface{}{
- "subsub": map[string]interface{}{
- "REN1": "m2",
- "ren2": "m2_2",
- },
- },
- "no": map[string]interface{}{
- "ren1": "m2",
- "ren2": "m2_2",
- },
- }
-
- expected := map[string]interface{}{
- "a": 32,
- "new1": "m1",
- "new2": "m1_2",
- "sub": map[string]interface{}{
- "subsub": map[string]interface{}{
- "new1": "m2",
- "ren2": "m2_2",
- },
- },
- "no": map[string]interface{}{
- "ren1": "m2",
- "ren2": "m2_2",
- },
- }
-
- renamer, err := NewKeyRenamer(
- "{ren1,sub/*/ren1}", "new1",
- "{Ren2,sub/ren2}", "new2",
- )
- c.Assert(err, qt.IsNil)
-
- renamer.Rename(m)
-
- if !reflect.DeepEqual(expected, m) {
- t.Errorf("Expected\n%#v, got\n%#v\n", expected, m)
- }
-
-}
diff --git a/common/maps/params.go b/common/maps/params.go
deleted file mode 100644
index 2d62ad752..000000000
--- a/common/maps/params.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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 maps
-
-import (
- "strings"
-
- "github.com/spf13/cast"
-)
-
-// GetNestedParam gets the first match of the keyStr in the candidates given.
-// It will first try the exact match and then try to find it as a nested map value,
-// using the given separator, e.g. "mymap.name".
-// It assumes that all the maps given have lower cased keys.
-func GetNestedParam(keyStr, separator string, candidates ...map[string]interface{}) (interface{}, error) {
- keyStr = strings.ToLower(keyStr)
-
- lookupFn := func(key string) interface{} {
- for _, m := range candidates {
- if v, ok := m[key]; ok {
- return v
- }
- }
-
- return nil
- }
-
- v, _, _, err := GetNestedParamFn(keyStr, separator, lookupFn)
- return v, err
-}
-
-func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
- result, _ := traverseDirectParams(keyStr, lookupFn)
- if result != nil {
- return result, keyStr, nil, nil
- }
-
- keySegments := strings.Split(keyStr, separator)
- if len(keySegments) == 1 {
- return nil, keyStr, nil, nil
- }
-
- return traverseNestedParams(keySegments, lookupFn)
-}
-
-func traverseDirectParams(keyStr string, lookupFn func(key string) interface{}) (interface{}, error) {
- return lookupFn(keyStr), nil
-}
-
-func traverseNestedParams(keySegments []string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
- firstKey, rest := keySegments[0], keySegments[1:]
- result := lookupFn(firstKey)
- if result == nil || len(rest) == 0 {
- return result, firstKey, nil, nil
- }
-
- switch m := result.(type) {
- case map[string]interface{}:
- v, key, owner := traverseParams(rest, m)
- return v, key, owner, nil
- default:
- return nil, "", nil, nil
- }
-}
-
-func traverseParams(keys []string, m map[string]interface{}) (interface{}, string, map[string]interface{}) {
- // Shift first element off.
- firstKey, rest := keys[0], keys[1:]
- result := m[firstKey]
-
- // No point in continuing here.
- if result == nil {
- return result, "", nil
- }
-
- if len(rest) == 0 {
- // That was the last key.
- return result, firstKey, m
- }
-
- // That was not the last key.
- return traverseParams(rest, cast.ToStringMap(result))
-}
diff --git a/common/maps/params_test.go b/common/maps/params_test.go
deleted file mode 100644
index 6477de6f4..000000000
--- a/common/maps/params_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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 maps
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestGetNestedParam(t *testing.T) {
-
- m := map[string]interface{}{
- "string": "value",
- "first": 1,
- "with_underscore": 2,
- "nested": map[string]interface{}{
- "color": "blue",
- "nestednested": map[string]interface{}{
- "color": "green",
- },
- },
- }
-
- c := qt.New(t)
-
- must := func(keyStr, separator string, candidates ...map[string]interface{}) interface{} {
- v, err := GetNestedParam(keyStr, separator, candidates...)
- c.Assert(err, qt.IsNil)
- return v
- }
-
- c.Assert(must("first", "_", m), qt.Equals, 1)
- c.Assert(must("First", "_", m), qt.Equals, 1)
- c.Assert(must("with_underscore", "_", m), qt.Equals, 2)
- c.Assert(must("nested_color", "_", m), qt.Equals, "blue")
- c.Assert(must("nested.nestednested.color", ".", m), qt.Equals, "green")
- c.Assert(must("string.name", ".", m), qt.IsNil)
-
-}
diff --git a/common/maps/scratch.go b/common/maps/scratch.go
deleted file mode 100644
index 4acd10c6c..000000000
--- a/common/maps/scratch.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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 maps
-
-import (
- "reflect"
- "sort"
- "sync"
-
- "github.com/gohugoio/hugo/common/collections"
- "github.com/gohugoio/hugo/common/math"
-)
-
-// Scratch is a writable context used for stateful operations in Page/Node rendering.
-type Scratch struct {
- values map[string]interface{}
- mu sync.RWMutex
-}
-
-// Scratcher provides a scratching service.
-type Scratcher interface {
- Scratch() *Scratch
-}
-
-type scratcher struct {
- s *Scratch
-}
-
-func (s scratcher) Scratch() *Scratch {
- return s.s
-}
-
-// NewScratcher creates a new Scratcher.
-func NewScratcher() Scratcher {
- return scratcher{s: NewScratch()}
-}
-
-// Add will, for single values, add (using the + operator) the addend to the existing addend (if found).
-// Supports numeric values and strings.
-//
-// If the first add for a key is an array or slice, then the next value(s) will be appended.
-func (c *Scratch) Add(key string, newAddend interface{}) (string, error) {
-
- var newVal interface{}
- c.mu.RLock()
- existingAddend, found := c.values[key]
- c.mu.RUnlock()
- if found {
- var err error
-
- addendV := reflect.TypeOf(existingAddend)
-
- if addendV.Kind() == reflect.Slice || addendV.Kind() == reflect.Array {
- newVal, err = collections.Append(existingAddend, newAddend)
- if err != nil {
- return "", err
- }
- } else {
- newVal, err = math.DoArithmetic(existingAddend, newAddend, '+')
- if err != nil {
- return "", err
- }
- }
- } else {
- newVal = newAddend
- }
- c.mu.Lock()
- c.values[key] = newVal
- c.mu.Unlock()
- return "", nil // have to return something to make it work with the Go templates
-}
-
-// Set stores a value with the given key in the Node context.
-// This value can later be retrieved with Get.
-func (c *Scratch) Set(key string, value interface{}) string {
- c.mu.Lock()
- c.values[key] = value
- c.mu.Unlock()
- return ""
-}
-
-// Delete deletes the given key.
-func (c *Scratch) Delete(key string) string {
- c.mu.Lock()
- delete(c.values, key)
- c.mu.Unlock()
- return ""
-}
-
-// Get returns a value previously set by Add or Set.
-func (c *Scratch) Get(key string) interface{} {
- c.mu.RLock()
- val := c.values[key]
- c.mu.RUnlock()
-
- return val
-}
-
-// SetInMap stores a value to a map with the given key in the Node context.
-// This map can later be retrieved with GetSortedMapValues.
-func (c *Scratch) SetInMap(key string, mapKey string, value interface{}) string {
- c.mu.Lock()
- _, found := c.values[key]
- if !found {
- c.values[key] = make(map[string]interface{})
- }
-
- c.values[key].(map[string]interface{})[mapKey] = value
- c.mu.Unlock()
- return ""
-}
-
-// GetSortedMapValues returns a sorted map previously filled with SetInMap.
-func (c *Scratch) GetSortedMapValues(key string) interface{} {
- c.mu.RLock()
-
- if c.values[key] == nil {
- c.mu.RUnlock()
- return nil
- }
-
- unsortedMap := c.values[key].(map[string]interface{})
- c.mu.RUnlock()
- var keys []string
- for mapKey := range unsortedMap {
- keys = append(keys, mapKey)
- }
-
- sort.Strings(keys)
-
- sortedArray := make([]interface{}, len(unsortedMap))
- for i, mapKey := range keys {
- sortedArray[i] = unsortedMap[mapKey]
- }
-
- return sortedArray
-}
-
-// NewScratch returns a new instance Scratch.
-func NewScratch() *Scratch {
- return &Scratch{values: make(map[string]interface{})}
-}
diff --git a/common/maps/scratch_test.go b/common/maps/scratch_test.go
deleted file mode 100644
index c2c436e40..000000000
--- a/common/maps/scratch_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-// 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 maps
-
-import (
- "reflect"
- "sync"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestScratchAdd(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.Add("int1", 10)
- scratch.Add("int1", 20)
- scratch.Add("int2", 20)
-
- c.Assert(scratch.Get("int1"), qt.Equals, int64(30))
- c.Assert(scratch.Get("int2"), qt.Equals, 20)
-
- scratch.Add("float1", float64(10.5))
- scratch.Add("float1", float64(20.1))
-
- c.Assert(scratch.Get("float1"), qt.Equals, float64(30.6))
-
- scratch.Add("string1", "Hello ")
- scratch.Add("string1", "big ")
- scratch.Add("string1", "World!")
-
- c.Assert(scratch.Get("string1"), qt.Equals, "Hello big World!")
-
- scratch.Add("scratch", scratch)
- _, err := scratch.Add("scratch", scratch)
-
- if err == nil {
- t.Errorf("Expected error from invalid arithmetic")
- }
-
-}
-
-func TestScratchAddSlice(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
-
- _, err := scratch.Add("intSlice", []int{1, 2})
- c.Assert(err, qt.IsNil)
- _, err = scratch.Add("intSlice", 3)
- c.Assert(err, qt.IsNil)
-
- sl := scratch.Get("intSlice")
- expected := []int{1, 2, 3}
-
- if !reflect.DeepEqual(expected, sl) {
- t.Errorf("Slice difference, go %q expected %q", sl, expected)
- }
- _, err = scratch.Add("intSlice", []int{4, 5})
-
- c.Assert(err, qt.IsNil)
-
- sl = scratch.Get("intSlice")
- expected = []int{1, 2, 3, 4, 5}
-
- if !reflect.DeepEqual(expected, sl) {
- t.Errorf("Slice difference, go %q expected %q", sl, expected)
- }
-}
-
-// https://github.com/gohugoio/hugo/issues/5275
-func TestScratchAddTypedSliceToInterfaceSlice(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.Set("slice", []interface{}{})
-
- _, err := scratch.Add("slice", []int{1, 2})
- c.Assert(err, qt.IsNil)
- c.Assert(scratch.Get("slice"), qt.DeepEquals, []int{1, 2})
-
-}
-
-// https://github.com/gohugoio/hugo/issues/5361
-func TestScratchAddDifferentTypedSliceToInterfaceSlice(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.Set("slice", []string{"foo"})
-
- _, err := scratch.Add("slice", []int{1, 2})
- c.Assert(err, qt.IsNil)
- c.Assert(scratch.Get("slice"), qt.DeepEquals, []interface{}{"foo", 1, 2})
-
-}
-
-func TestScratchSet(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.Set("key", "val")
- c.Assert(scratch.Get("key"), qt.Equals, "val")
-}
-
-func TestScratchDelete(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.Set("key", "val")
- scratch.Delete("key")
- scratch.Add("key", "Lucy Parsons")
- c.Assert(scratch.Get("key"), qt.Equals, "Lucy Parsons")
-}
-
-// Issue #2005
-func TestScratchInParallel(t *testing.T) {
- var wg sync.WaitGroup
- scratch := NewScratch()
-
- key := "counter"
- scratch.Set(key, int64(1))
- for i := 1; i <= 10; i++ {
- wg.Add(1)
- go func(j int) {
- for k := 0; k < 10; k++ {
- newVal := int64(k + j)
-
- _, err := scratch.Add(key, newVal)
- if err != nil {
- t.Errorf("Got err %s", err)
- }
-
- scratch.Set(key, newVal)
-
- val := scratch.Get(key)
-
- if counter, ok := val.(int64); ok {
- if counter < 1 {
- t.Errorf("Got %d", counter)
- }
- } else {
- t.Errorf("Got %T", val)
- }
- }
- wg.Done()
- }(i)
- }
- wg.Wait()
-}
-
-func TestScratchGet(t *testing.T) {
- t.Parallel()
- scratch := NewScratch()
- nothing := scratch.Get("nothing")
- if nothing != nil {
- t.Errorf("Should not return anything, but got %v", nothing)
- }
-}
-
-func TestScratchSetInMap(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- scratch := NewScratch()
- scratch.SetInMap("key", "lux", "Lux")
- scratch.SetInMap("key", "abc", "Abc")
- scratch.SetInMap("key", "zyx", "Zyx")
- scratch.SetInMap("key", "abc", "Abc (updated)")
- scratch.SetInMap("key", "def", "Def")
- c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []interface{}{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"})
-}
-
-func TestScratchGetSortedMapValues(t *testing.T) {
- t.Parallel()
- scratch := NewScratch()
- nothing := scratch.GetSortedMapValues("nothing")
- if nothing != nil {
- t.Errorf("Should not return anything, but got %v", nothing)
- }
-}
-
-func BenchmarkScratchGet(b *testing.B) {
- scratch := NewScratch()
- scratch.Add("A", 1)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- scratch.Get("A")
- }
-}
diff --git a/common/math/math.go b/common/math/math.go
deleted file mode 100644
index cd06379aa..000000000
--- a/common/math/math.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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 math
-
-import (
- "errors"
- "reflect"
-)
-
-// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
-// determine the type of the two terms.
-func DoArithmetic(a, b interface{}, op rune) (interface{}, error) {
- av := reflect.ValueOf(a)
- bv := reflect.ValueOf(b)
- var ai, bi int64
- var af, bf float64
- var au, bu uint64
- switch av.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- ai = av.Int()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- case reflect.Float32, reflect.Float64:
- af = float64(ai) // may overflow
- ai = 0
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bu = bv.Uint()
- if ai >= 0 {
- au = uint64(ai)
- ai = 0
- } else {
- bi = int64(bu) // may overflow
- bu = 0
- }
- default:
- return nil, errors.New("can't apply the operator to the values")
- }
- case reflect.Float32, reflect.Float64:
- af = av.Float()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bf = float64(bv.Int()) // may overflow
- case reflect.Float32, reflect.Float64:
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bf = float64(bv.Uint()) // may overflow
- default:
- return nil, errors.New("can't apply the operator to the values")
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- au = av.Uint()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- if bi >= 0 {
- bu = uint64(bi)
- bi = 0
- } else {
- ai = int64(au) // may overflow
- au = 0
- }
- case reflect.Float32, reflect.Float64:
- af = float64(au) // may overflow
- au = 0
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bu = bv.Uint()
- default:
- return nil, errors.New("can't apply the operator to the values")
- }
- case reflect.String:
- as := av.String()
- if bv.Kind() == reflect.String && op == '+' {
- bs := bv.String()
- return as + bs, nil
- }
- return nil, errors.New("can't apply the operator to the values")
- default:
- return nil, errors.New("can't apply the operator to the values")
- }
-
- switch op {
- case '+':
- if ai != 0 || bi != 0 {
- return ai + bi, nil
- } else if af != 0 || bf != 0 {
- return af + bf, nil
- } else if au != 0 || bu != 0 {
- return au + bu, nil
- }
- return 0, nil
- case '-':
- if ai != 0 || bi != 0 {
- return ai - bi, nil
- } else if af != 0 || bf != 0 {
- return af - bf, nil
- } else if au != 0 || bu != 0 {
- return au - bu, nil
- }
- return 0, nil
- case '*':
- if ai != 0 || bi != 0 {
- return ai * bi, nil
- } else if af != 0 || bf != 0 {
- return af * bf, nil
- } else if au != 0 || bu != 0 {
- return au * bu, nil
- }
- return 0, nil
- case '/':
- if bi != 0 {
- return ai / bi, nil
- } else if bf != 0 {
- return af / bf, nil
- } else if bu != 0 {
- return au / bu, nil
- }
- return nil, errors.New("can't divide the value by 0")
- default:
- return nil, errors.New("there is no such an operation")
- }
-}
diff --git a/common/math/math_test.go b/common/math/math_test.go
deleted file mode 100644
index a11701862..000000000
--- a/common/math/math_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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 math
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestDoArithmetic(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
-
- for _, test := range []struct {
- a interface{}
- b interface{}
- op rune
- expect interface{}
- }{
- {3, 2, '+', int64(5)},
- {3, 2, '-', int64(1)},
- {3, 2, '*', int64(6)},
- {3, 2, '/', int64(1)},
- {3.0, 2, '+', float64(5)},
- {3.0, 2, '-', float64(1)},
- {3.0, 2, '*', float64(6)},
- {3.0, 2, '/', float64(1.5)},
- {3, 2.0, '+', float64(5)},
- {3, 2.0, '-', float64(1)},
- {3, 2.0, '*', float64(6)},
- {3, 2.0, '/', float64(1.5)},
- {3.0, 2.0, '+', float64(5)},
- {3.0, 2.0, '-', float64(1)},
- {3.0, 2.0, '*', float64(6)},
- {3.0, 2.0, '/', float64(1.5)},
- {uint(3), uint(2), '+', uint64(5)},
- {uint(3), uint(2), '-', uint64(1)},
- {uint(3), uint(2), '*', uint64(6)},
- {uint(3), uint(2), '/', uint64(1)},
- {uint(3), 2, '+', uint64(5)},
- {uint(3), 2, '-', uint64(1)},
- {uint(3), 2, '*', uint64(6)},
- {uint(3), 2, '/', uint64(1)},
- {3, uint(2), '+', uint64(5)},
- {3, uint(2), '-', uint64(1)},
- {3, uint(2), '*', uint64(6)},
- {3, uint(2), '/', uint64(1)},
- {uint(3), -2, '+', int64(1)},
- {uint(3), -2, '-', int64(5)},
- {uint(3), -2, '*', int64(-6)},
- {uint(3), -2, '/', int64(-1)},
- {-3, uint(2), '+', int64(-1)},
- {-3, uint(2), '-', int64(-5)},
- {-3, uint(2), '*', int64(-6)},
- {-3, uint(2), '/', int64(-1)},
- {uint(3), 2.0, '+', float64(5)},
- {uint(3), 2.0, '-', float64(1)},
- {uint(3), 2.0, '*', float64(6)},
- {uint(3), 2.0, '/', float64(1.5)},
- {3.0, uint(2), '+', float64(5)},
- {3.0, uint(2), '-', float64(1)},
- {3.0, uint(2), '*', float64(6)},
- {3.0, uint(2), '/', float64(1.5)},
- {0, 0, '+', 0},
- {0, 0, '-', 0},
- {0, 0, '*', 0},
- {"foo", "bar", '+', "foobar"},
- {3, 0, '/', false},
- {3.0, 0, '/', false},
- {3, 0.0, '/', false},
- {uint(3), uint(0), '/', false},
- {3, uint(0), '/', false},
- {-3, uint(0), '/', false},
- {uint(3), 0, '/', false},
- {3.0, uint(0), '/', false},
- {uint(3), 0.0, '/', false},
- {3, "foo", '+', false},
- {3.0, "foo", '+', false},
- {uint(3), "foo", '+', false},
- {"foo", 3, '+', false},
- {"foo", "bar", '-', false},
- {3, 2, '%', false},
- } {
- result, err := DoArithmetic(test.a, test.b, test.op)
-
- if b, ok := test.expect.(bool); ok && !b {
- c.Assert(err, qt.Not(qt.IsNil))
- continue
- }
-
- c.Assert(err, qt.IsNil)
- c.Assert(test.expect, qt.Equals, result)
- }
-}
diff --git a/common/terminal/colors.go b/common/terminal/colors.go
deleted file mode 100644
index 334b82fae..000000000
--- a/common/terminal/colors.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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 terminal contains helper for the terminal, such as coloring output.
-package terminal
-
-import (
- "fmt"
- "os"
- "runtime"
- "strings"
-
- isatty "github.com/mattn/go-isatty"
-)
-
-const (
- errorColor = "\033[1;31m%s\033[0m"
- warningColor = "\033[0;33m%s\033[0m"
- noticeColor = "\033[1;36m%s\033[0m"
-)
-
-// IsTerminal return true if the file descriptor is terminal and the TERM
-// environment variable isn't a dumb one.
-func IsTerminal(f *os.File) bool {
- if runtime.GOOS == "windows" {
- return false
- }
-
- fd := f.Fd()
- return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd))
-}
-
-// Notice colorizes the string in a noticeable color.
-func Notice(s string) string {
- return colorize(s, noticeColor)
-}
-
-// Error colorizes the string in a colour that grabs attention.
-func Error(s string) string {
- return colorize(s, errorColor)
-}
-
-// Warning colorizes the string in a colour that warns.
-func Warning(s string) string {
- return colorize(s, warningColor)
-}
-
-// colorize s in color.
-func colorize(s, color string) string {
- s = fmt.Sprintf(color, doublePercent(s))
- return singlePercent(s)
-}
-
-func doublePercent(str string) string {
- return strings.Replace(str, "%", "%%", -1)
-}
-
-func singlePercent(str string) string {
- return strings.Replace(str, "%%", "%", -1)
-}
diff --git a/common/text/position.go b/common/text/position.go
deleted file mode 100644
index 0c43c5ae7..000000000
--- a/common/text/position.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 text
-
-import (
- "fmt"
- "os"
- "strings"
-
- "github.com/gohugoio/hugo/common/terminal"
-)
-
-// Positioner represents a thing that knows its position in a text file or stream,
-// typically an error.
-type Positioner interface {
- Position() Position
-}
-
-// Position holds a source position in a text file or stream.
-type Position struct {
- Filename string // filename, if any
- Offset int // byte offset, starting at 0. It's set to -1 if not provided.
- LineNumber int // line number, starting at 1
- ColumnNumber int // column number, starting at 1 (character count per line)
-}
-
-func (pos Position) String() string {
- if pos.Filename == "" {
- pos.Filename = ""
- }
- return positionStringFormatfunc(pos)
-}
-
-// IsValid returns true if line number is > 0.
-func (pos Position) IsValid() bool {
- return pos.LineNumber > 0
-}
-
-var positionStringFormatfunc func(p Position) string
-
-func createPositionStringFormatter(formatStr string) func(p Position) string {
-
- if formatStr == "" {
- formatStr = "\":file::line::col\""
- }
-
- var identifiers = []string{":file", ":line", ":col"}
- var identifiersFound []string
-
- for i := range formatStr {
- for _, id := range identifiers {
- if strings.HasPrefix(formatStr[i:], id) {
- identifiersFound = append(identifiersFound, id)
- }
- }
- }
-
- replacer := strings.NewReplacer(":file", "%s", ":line", "%d", ":col", "%d")
- format := replacer.Replace(formatStr)
-
- f := func(pos Position) string {
- args := make([]interface{}, len(identifiersFound))
- for i, id := range identifiersFound {
- switch id {
- case ":file":
- args[i] = pos.Filename
- case ":line":
- args[i] = pos.LineNumber
- case ":col":
- args[i] = pos.ColumnNumber
- }
- }
-
- msg := fmt.Sprintf(format, args...)
-
- if terminal.IsTerminal(os.Stdout) {
- return terminal.Notice(msg)
- }
-
- return msg
- }
-
- return f
-}
-
-func init() {
- positionStringFormatfunc = createPositionStringFormatter(os.Getenv("HUGO_FILE_LOG_FORMAT"))
-}
diff --git a/common/text/position_test.go b/common/text/position_test.go
deleted file mode 100644
index ba4824344..000000000
--- a/common/text/position_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 text
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestPositionStringFormatter(t *testing.T) {
- c := qt.New(t)
-
- pos := Position{Filename: "/my/file.txt", LineNumber: 12, ColumnNumber: 13, Offset: 14}
-
- c.Assert(createPositionStringFormatter(":file|:col|:line")(pos), qt.Equals, "/my/file.txt|13|12")
- c.Assert(createPositionStringFormatter(":col|:file|:line")(pos), qt.Equals, "13|/my/file.txt|12")
- c.Assert(createPositionStringFormatter("好::col")(pos), qt.Equals, "好:13")
- c.Assert(createPositionStringFormatter("")(pos), qt.Equals, "\"/my/file.txt:12:13\"")
- c.Assert(pos.String(), qt.Equals, "\"/my/file.txt:12:13\"")
-
-}
diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go
deleted file mode 100644
index 884762426..000000000
--- a/common/types/evictingqueue.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017-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 types contains types shared between packages in Hugo.
-package types
-
-import (
- "sync"
-)
-
-// EvictingStringQueue is a queue which automatically evicts elements from the head of
-// the queue when attempting to add new elements onto the queue and it is full.
-// This queue orders elements LIFO (last-in-first-out). It throws away duplicates.
-// Note: This queue currently does not contain any remove (poll etc.) methods.
-type EvictingStringQueue struct {
- size int
- vals []string
- set map[string]bool
- mu sync.Mutex
-}
-
-// NewEvictingStringQueue creates a new queue with the given size.
-func NewEvictingStringQueue(size int) *EvictingStringQueue {
- return &EvictingStringQueue{size: size, set: make(map[string]bool)}
-}
-
-// Add adds a new string to the tail of the queue if it's not already there.
-func (q *EvictingStringQueue) Add(v string) {
- q.mu.Lock()
- if q.set[v] {
- q.mu.Unlock()
- return
- }
-
- if len(q.set) == q.size {
- // Full
- delete(q.set, q.vals[0])
- q.vals = append(q.vals[:0], q.vals[1:]...)
- }
- q.set[v] = true
- q.vals = append(q.vals, v)
- q.mu.Unlock()
-}
-
-// Contains returns whether the queue contains v.
-func (q *EvictingStringQueue) Contains(v string) bool {
- q.mu.Lock()
- defer q.mu.Unlock()
- return q.set[v]
-}
-
-// Peek looks at the last element added to the queue.
-func (q *EvictingStringQueue) Peek() string {
- q.mu.Lock()
- l := len(q.vals)
- if l == 0 {
- q.mu.Unlock()
- return ""
- }
- elem := q.vals[l-1]
- q.mu.Unlock()
- return elem
-}
-
-// PeekAll looks at all the elements in the queue, with the newest first.
-func (q *EvictingStringQueue) PeekAll() []string {
- q.mu.Lock()
- vals := make([]string, len(q.vals))
- copy(vals, q.vals)
- q.mu.Unlock()
- for i, j := 0, len(vals)-1; i < j; i, j = i+1, j-1 {
- vals[i], vals[j] = vals[j], vals[i]
- }
- return vals
-}
-
-// PeekAllSet returns PeekAll as a set.
-func (q *EvictingStringQueue) PeekAllSet() map[string]bool {
- all := q.PeekAll()
- set := make(map[string]bool)
- for _, v := range all {
- set[v] = true
- }
-
- return set
-}
diff --git a/common/types/evictingqueue_test.go b/common/types/evictingqueue_test.go
deleted file mode 100644
index 7489ba88d..000000000
--- a/common/types/evictingqueue_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017-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 types
-
-import (
- "sync"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestEvictingStringQueue(t *testing.T) {
- c := qt.New(t)
-
- queue := NewEvictingStringQueue(3)
-
- c.Assert(queue.Peek(), qt.Equals, "")
- queue.Add("a")
- queue.Add("b")
- queue.Add("a")
- c.Assert(queue.Peek(), qt.Equals, "b")
- queue.Add("b")
- c.Assert(queue.Peek(), qt.Equals, "b")
-
- queue.Add("a")
- queue.Add("b")
-
- c.Assert(queue.Contains("a"), qt.Equals, true)
- c.Assert(queue.Contains("foo"), qt.Equals, false)
-
- c.Assert(queue.PeekAll(), qt.DeepEquals, []string{"b", "a"})
- c.Assert(queue.Peek(), qt.Equals, "b")
- queue.Add("c")
- queue.Add("d")
- // Overflowed, a should now be removed.
- c.Assert(queue.PeekAll(), qt.DeepEquals, []string{"d", "c", "b"})
- c.Assert(len(queue.PeekAllSet()), qt.Equals, 3)
- c.Assert(queue.PeekAllSet()["c"], qt.Equals, true)
-}
-
-func TestEvictingStringQueueConcurrent(t *testing.T) {
- var wg sync.WaitGroup
- val := "someval"
-
- queue := NewEvictingStringQueue(3)
-
- for j := 0; j < 100; j++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- queue.Add(val)
- v := queue.Peek()
- if v != val {
- t.Error("wrong val")
- }
- vals := queue.PeekAll()
- if len(vals) != 1 || vals[0] != val {
- t.Error("wrong val")
- }
- }()
- }
- wg.Wait()
-}
diff --git a/common/types/types.go b/common/types/types.go
deleted file mode 100644
index f03031439..000000000
--- a/common/types/types.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 types contains types shared between packages in Hugo.
-package types
-
-import (
- "fmt"
- "reflect"
-
- "github.com/spf13/cast"
-)
-
-// KeyValueStr is a string tuple.
-type KeyValueStr struct {
- Key string
- Value string
-}
-
-// KeyValues holds an key and a slice of values.
-type KeyValues struct {
- Key interface{}
- Values []interface{}
-}
-
-// KeyString returns the key as a string, an empty string if conversion fails.
-func (k KeyValues) KeyString() string {
- return cast.ToString(k.Key)
-}
-
-func (k KeyValues) String() string {
- return fmt.Sprintf("%v: %v", k.Key, k.Values)
-}
-
-// NewKeyValuesStrings takes a given key and slice of values and returns a new
-// KeyValues struct.
-func NewKeyValuesStrings(key string, values ...string) KeyValues {
- iv := make([]interface{}, len(values))
- for i := 0; i < len(values); i++ {
- iv[i] = values[i]
- }
- return KeyValues{Key: key, Values: iv}
-}
-
-// Zeroer, as implemented by time.Time, will be used by the truth template
-// funcs in Hugo (if, with, not, and, or).
-type Zeroer interface {
- IsZero() bool
-}
-
-// IsNil reports whether v is nil.
-func IsNil(v interface{}) bool {
- if v == nil {
- return true
- }
-
- value := reflect.ValueOf(v)
- switch value.Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
- return value.IsNil()
- }
-
- return false
-}
-
-// DevMarker is a marker interface for types that should only be used during
-// development.
-type DevMarker interface {
- DevOnly()
-}
diff --git a/common/types/types_test.go b/common/types/types_test.go
deleted file mode 100644
index 7c5cba659..000000000
--- a/common/types/types_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017-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 types
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestKeyValues(t *testing.T) {
- c := qt.New(t)
-
- kv := NewKeyValuesStrings("key", "a1", "a2")
-
- c.Assert(kv.KeyString(), qt.Equals, "key")
- c.Assert(kv.Values, qt.DeepEquals, []interface{}{"a1", "a2"})
-}
diff --git a/common/urls/ref.go b/common/urls/ref.go
deleted file mode 100644
index 71b00b71d..000000000
--- a/common/urls/ref.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 urls
-
-// RefLinker is implemented by those who support reference linking.
-// args must contain a path, but can also point to the target
-// language or output format.
-type RefLinker interface {
- Ref(args map[string]interface{}) (string, error)
- RelRef(args map[string]interface{}) (string, error)
-}
diff --git a/compare/compare.go b/compare/compare.go
deleted file mode 100644
index 537a66676..000000000
--- a/compare/compare.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 compare
-
-// Eqer can be used to determine if this value is equal to the other.
-// The semantics of equals is that the two value are interchangeable
-// in the Hugo templates.
-type Eqer interface {
- Eq(other interface{}) bool
-}
-
-// ProbablyEqer is an equal check that may return false positives, but never
-// a false negative.
-type ProbablyEqer interface {
- ProbablyEq(other interface{}) bool
-}
-
-// Comparer can be used to compare two values.
-// This will be used when using the le, ge etc. operators in the templates.
-// Compare returns -1 if the given version is less than, 0 if equal and 1 if greater than
-// the running version.
-type Comparer interface {
- Compare(other interface{}) int
-}
diff --git a/compare/compare_strings.go b/compare/compare_strings.go
deleted file mode 100644
index 1fd954081..000000000
--- a/compare/compare_strings.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// 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 compare
-
-import (
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-// Strings returns an integer comparing two strings lexicographically.
-func Strings(s, t string) int {
- c := compareFold(s, t)
-
- if c == 0 {
- // "B" and "b" would be the same so we need a tiebreaker.
- return strings.Compare(s, t)
- }
-
- return c
-}
-
-// This function is derived from strings.EqualFold in Go's stdlib.
-// https://github.com/golang/go/blob/ad4a58e31501bce5de2aad90a620eaecdc1eecb8/src/strings/strings.go#L893
-func compareFold(s, t string) int {
- for s != "" && t != "" {
- var sr, tr rune
- if s[0] < utf8.RuneSelf {
- sr, s = rune(s[0]), s[1:]
- } else {
- r, size := utf8.DecodeRuneInString(s)
- sr, s = r, s[size:]
- }
- if t[0] < utf8.RuneSelf {
- tr, t = rune(t[0]), t[1:]
- } else {
- r, size := utf8.DecodeRuneInString(t)
- tr, t = r, t[size:]
- }
-
- if tr == sr {
- continue
- }
-
- c := 1
- if tr < sr {
- tr, sr = sr, tr
- c = -c
- }
-
- // ASCII only.
- if tr < utf8.RuneSelf {
- if sr >= 'A' && sr <= 'Z' {
- if tr <= 'Z' {
- // Same case.
- return -c
- }
-
- diff := tr - (sr + 'a' - 'A')
-
- if diff == 0 {
- continue
- }
-
- if diff < 0 {
- return c
- }
-
- if diff > 0 {
- return -c
- }
- }
- }
-
- // Unicode.
- r := unicode.SimpleFold(sr)
- for r != sr && r < tr {
- r = unicode.SimpleFold(r)
- }
-
- if r == tr {
- continue
- }
-
- return -c
- }
-
- if s == "" && t == "" {
- return 0
- }
-
- if s == "" {
- return -1
- }
-
- return 1
-}
-
-// LessStrings returns whether s is less than t lexicographically.
-func LessStrings(s, t string) bool {
- return Strings(s, t) < 0
-}
diff --git a/compare/compare_strings_test.go b/compare/compare_strings_test.go
deleted file mode 100644
index db286c2c5..000000000
--- a/compare/compare_strings_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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 compare
-
-import (
- "sort"
- "strings"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestCompare(t *testing.T) {
- c := qt.New(t)
- for _, test := range []struct {
- a string
- b string
- }{
- {"a", "a"},
- {"A", "a"},
- {"Ab", "Ac"},
- {"az", "Za"},
- {"C", "D"},
- {"B", "a"},
- {"C", ""},
- {"", ""},
- {"αβδC", "ΑΒΔD"},
- {"αβδC", "ΑΒΔ"},
- {"αβδ", "ΑΒΔD"},
- {"αβδ", "ΑΒΔ"},
- {"β", "δ"},
- {"好", strings.ToLower("好")},
- } {
-
- expect := strings.Compare(strings.ToLower(test.a), strings.ToLower(test.b))
- got := compareFold(test.a, test.b)
-
- c.Assert(got, qt.Equals, expect)
-
- }
-}
-
-func TestLexicographicSort(t *testing.T) {
- c := qt.New(t)
-
- s := []string{"b", "Bz", "ba", "A", "Ba", "ba"}
-
- sort.Slice(s, func(i, j int) bool {
- return LessStrings(s[i], s[j])
- })
-
- c.Assert(s, qt.DeepEquals, []string{"A", "b", "Ba", "ba", "ba", "Bz"})
-
-}
diff --git a/config.toml b/config.toml
new file mode 100644
index 000000000..6f66955ba
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,118 @@
+baseURL = "https://gohugo.io/"
+paginate = 100
+defaultContentLanguage = "en"
+enableEmoji = true
+# Set the unicode character used for the "return" link in page footnotes.
+footnotereturnlinkcontents = "↩"
+languageCode = "en-us"
+title = "Hugo"
+
+
+googleAnalytics = "UA-7131036-4"
+
+pluralizeListTitles = false
+
+# We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below).
+disableAliases = true
+
+# Highlighting config (Pygments)
+# It is (currently) not in use, but you can do ```go in a content file if you want to.
+pygmentsCodeFences = true
+
+pygmentsOptions = ""
+# Use the Chroma stylesheet
+pygmentsUseClasses = true
+pygmentsUseClassic = false
+
+# See https://help.farbox.com/pygments.html
+pygmentsStyle = "trac"
+
+[module]
+ [module.hugoVersion]
+ min = "0.56.0"
+ [[module.imports]]
+ path = "github.com/gohugoio/gohugoioTheme"
+
+[outputs]
+home = [ "HTML", "RSS", "REDIR", "HEADERS" ]
+section = [ "HTML", "RSS"]
+
+[mediaTypes]
+[mediaTypes."text/netlify"]
+delimiter = ""
+
+[outputFormats]
+[outputFormats.REDIR]
+mediatype = "text/netlify"
+baseName = "_redirects"
+isPlainText = true
+notAlternative = true
+[outputFormats.HEADERS]
+mediatype = "text/netlify"
+baseName = "_headers"
+isPlainText = true
+notAlternative = true
+
+[related]
+
+threshold = 80
+includeNewer = true
+toLower = false
+
+[[related.indices]]
+name = "keywords"
+weight = 100
+[[related.indices]]
+name = "date"
+weight = 10
+pattern = "2006"
+
+[social]
+twitter = "GoHugoIO"
+
+#CUSTOM PARAMS
+[params]
+ description = "The world’s fastest framework for building websites"
+ ## Used for views in rendered HTML (i.e., rather than using the .Hugo variable)
+ release = "0.55.0-DEV"
+ ## Setting this to true will add a "noindex" to *EVERY* page on the site
+ removefromexternalsearch = false
+ ## Gh repo for site footer (include trailing slash)
+ ghrepo = "https://github.com/gohugoio/hugoDocs/"
+ ## GH Repo for filing a new issue
+ github_repo = "https://github.com/gohugoio/hugo/issues/new"
+ ### Edit content repo (set to automatically enter "edit" mode; this is good for "improve this page" links)
+ ghdocsrepo = "https://github.com/gohugoio/hugoDocs/tree/master/docs"
+ ## Gitter URL
+ gitter = "https://gitter.im/spf13/hugo"
+ ## Discuss Forum URL
+ forum = "https://discourse.gohugo.io/"
+ ## Google Tag Manager
+ gtmid = ""
+
+ # First one is picked as the Twitter card image if not set on page.
+ images = ["images/gohugoio-card.png"]
+
+ flex_box_interior_classes = "flex-auto w-100 w-40-l mr3 mb3 bg-white ba b--moon-gray nested-copy-line-height"
+
+ #sidebar_direction = "sidebar_left"
+
+# MARKDOWN
+## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
+[blackfriday]
+ plainIDAnchors = true
+ # See https://github.com/gohugoio/hugo/issues/2424
+ hrefTargetBlank = false
+ angledQuotes = false
+ latexDashes = true
+
+[imaging]
+# See https://github.com/disintegration/imaging
+# CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots.
+# Note that you can also set this per image processing.
+resampleFilter = "CatmullRom"
+
+# Default JPEG quality setting. Default is 75.
+quality = 75
+
+anchor = "smart"
diff --git a/config/_default/config.toml b/config/_default/config.toml
new file mode 100644
index 000000000..9f908c9fd
--- /dev/null
+++ b/config/_default/config.toml
@@ -0,0 +1,103 @@
+baseURL = "https://gohugo.io/"
+paginate = 100
+defaultContentLanguage = "en"
+enableEmoji = true
+# Set the unicode character used for the "return" link in page footnotes.
+footnotereturnlinkcontents = "↩"
+languageCode = "en-us"
+title = "Hugo"
+
+googleAnalytics = "UA-7131036-4"
+
+pluralizeListTitles = false
+
+# We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below).
+disableAliases = true
+
+# Highlighting config (Pygments)
+# It is (currently) not in use, but you can do ```go in a content file if you want to.
+pygmentsCodeFences = true
+
+pygmentsOptions = ""
+# Use the Chroma stylesheet
+pygmentsUseClasses = true
+pygmentsUseClassic = false
+
+# See https://help.farbox.com/pygments.html
+pygmentsStyle = "trac"
+
+[outputs]
+home = [ "HTML", "RSS", "REDIR", "HEADERS" ]
+section = [ "HTML", "RSS"]
+
+[mediaTypes]
+[mediaTypes."text/netlify"]
+delimiter = ""
+
+[outputFormats]
+[outputFormats.REDIR]
+mediatype = "text/netlify"
+baseName = "_redirects"
+isPlainText = true
+notAlternative = true
+[outputFormats.HEADERS]
+mediatype = "text/netlify"
+baseName = "_headers"
+isPlainText = true
+notAlternative = true
+
+[caches]
+[caches.getjson]
+dir = ":cacheDir/:project"
+maxAge = -1
+[caches.getcsv]
+dir = ":cacheDir/:project"
+maxAge = -1
+[caches.images]
+dir = ":resourceDir/_gen"
+maxAge = -1
+[caches.assets]
+dir = ":resourceDir/_gen"
+maxAge = -1
+
+
+[related]
+
+threshold = 80
+includeNewer = true
+toLower = false
+
+[[related.indices]]
+name = "keywords"
+weight = 100
+[[related.indices]]
+name = "date"
+weight = 10
+pattern = "2006"
+
+[social]
+twitter = "GoHugoIO"
+
+
+# MARKDOWN
+## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
+[blackfriday]
+plainIDAnchors = true
+# See https://github.com/gohugoio/hugo/issues/2424
+hrefTargetBlank = false
+angledQuotes = false
+latexDashes = true
+
+[imaging]
+# See https://github.com/disintegration/imaging
+# CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots.
+# Note that you can also set this per image processing.
+resampleFilter = "CatmullRom"
+
+# Default JPEG quality setting. Default is 75.
+quality = 75
+
+anchor = "smart"
+
+[taxonomies]
+category = "categories"
diff --git a/config/_default/languages.toml b/config/_default/languages.toml
new file mode 100644
index 000000000..c9914d84d
--- /dev/null
+++ b/config/_default/languages.toml
@@ -0,0 +1,10 @@
+
+ [en]
+ contentDir = "content/en"
+ languageName = "English"
+ weight = 1
+
+ [zh]
+ contentDir = "content/zh"
+ languageName = "中文"
+ weight = 2
diff --git a/config/_default/menus/menus.en.toml b/config/_default/menus/menus.en.toml
new file mode 100644
index 000000000..a31218a4f
--- /dev/null
+++ b/config/_default/menus/menus.en.toml
@@ -0,0 +1,154 @@
+[[docs]]
+ name = "About Hugo"
+ weight = 1
+ identifier = "about"
+ url = "/about/"
+
+[[docs]]
+ name = "Getting Started"
+ weight = 5
+ identifier = "getting-started"
+ url = "/getting-started/"
+
+[[docs]]
+ name = "Hugo Modules"
+ weight = 15
+ identifier = "modules"
+ post = "break"
+ url = "/hugo-modules/"
+
+# Core Menus
+
+[[docs]]
+ name = "Content Management"
+ weight = 20
+ identifier = "content-management"
+ post = "expanded"
+ url = "/content-management/"
+
+[[docs]]
+ name = "Templates"
+ weight = 25
+ identifier = "templates"
+
+ url = "/templates/"
+
+[[docs]]
+ name = "Functions"
+ weight = 30
+ identifier = "functions"
+ url = "/functions/"
+
+[[docs]]
+ name = "Variables"
+ weight = 35
+ identifier = "variables"
+ url = "/variables/"
+[[docs]]
+ name = "Hugo Pipes"
+ weight = 36
+ identifier = "pipes"
+ url = "/hugo-pipes/"
+[[docs]]
+ name = "CLI"
+ weight = 40
+ post = "break"
+ identifier = "commands"
+ url = "/commands/"
+
+
+
+# LOW LEVEL ITEMS
+
+
+[[docs]]
+ name = "Troubleshooting"
+ weight = 60
+ identifier = "troubleshooting"
+ url = "/troubleshooting/"
+
+[[docs]]
+ name = "Tools"
+ weight = 70
+ identifier = "tools"
+ url = "/tools/"
+
+[[docs]]
+ name = "Hosting & Deployment"
+ weight = 80
+ identifier = "hosting-and-deployment"
+ url = "/hosting-and-deployment/"
+
+[[docs]]
+ name = "Contribute"
+ weight = 100
+ post = "break"
+ identifier = "contribute"
+ url = "/contribute/"
+
+#[[docs]]
+# name = "Tags"
+# weight = 120
+# identifier = "tags"
+# url = "/tags/"
+
+
+# [[docs]]
+# name = "Categories"
+# weight = 140
+# identifier = "categories"
+# url = "/categories/"
+
+######## QUICKLINKS
+
+ [[quicklinks]]
+ name = "Fundamentals"
+ weight = 1
+ identifier = "fundamentals"
+ url = "/tags/fundamentals/"
+
+
+
+
+######## GLOBAL ITEMS TO BE SHARED WITH THE HUGO SITES
+
+[[global]]
+ name = "News"
+ weight = 1
+ identifier = "news"
+ url = "/news/"
+
+ [[global]]
+ name = "Docs"
+ weight = 5
+ identifier = "docs"
+ url = "/documentation/"
+
+ [[global]]
+ name = "Themes"
+ weight = 10
+ identifier = "themes"
+ url = "https://themes.gohugo.io/"
+
+ [[global]]
+ name = "Showcase"
+ weight = 20
+ identifier = "showcase"
+ url = "/showcase/"
+
+ # Anything with a weight > 100 gets an external icon
+ [[global]]
+ name = "Community"
+ weight = 150
+ icon = true
+ identifier = "community"
+ post = "external"
+ url = "https://discourse.gohugo.io/"
+
+
+ [[global]]
+ name = "GitHub"
+ weight = 200
+ identifier = "github"
+ post = "external"
+ url = "https://github.com/gohugoio/hugo"
\ No newline at end of file
diff --git a/config/_default/menus/menus.zh.toml b/config/_default/menus/menus.zh.toml
new file mode 100644
index 000000000..2f68be67b
--- /dev/null
+++ b/config/_default/menus/menus.zh.toml
@@ -0,0 +1,121 @@
+
+# Chinese menus
+
+[[docs]]
+ name = "关于 Hugo"
+ weight = 1
+ identifier = "about"
+ url = "/zh/about/"
+
+[[docs]]
+ name = "入门"
+ weight = 5
+ identifier = "getting-started"
+ url = "/zh/getting-started/"
+
+[[docs]]
+ name = "主题"
+ weight = 15
+ identifier = "themes"
+ post = "break"
+ url = "/zh/themes/"
+
+# Core languages.zh.menus
+
+[[docs]]
+ name = "内容管理"
+ weight = 20
+ identifier = "content-management"
+ post = "expanded"
+ url = "/zh/content-management/"
+
+[[docs]]
+ name = "模板"
+ weight = 25
+ identifier = "templates"
+ url = "/zh/templates/"
+
+[[docs]]
+ name = "函数"
+ weight = 30
+ identifier = "functions"
+ url = "/zh/functions/"
+
+[[docs]]
+ name = "变量"
+ weight = 35
+ identifier = "variables"
+ url = "/zh/variables/"
+
+[[docs]]
+ name = "CLI"
+ weight = 40
+ post = "break"
+ identifier = "commands"
+ url = "/commands/"
+
+# LOW LEVEL ITEMS
+[[docs]]
+ name = "故障排除"
+ weight = 60
+ identifier = "troubleshooting"
+ url = "/zh/troubleshooting/"
+
+[[docs]]
+ name = "工具"
+ weight = 70
+ identifier = "tools"
+ url = "/zh/tools/"
+
+[[docs]]
+ name = "托管与部署"
+ weight = 80
+ identifier = "hosting-and-deployment"
+ url = "/zh/hosting-and-deployment/"
+
+[[docs]]
+ name = "贡献"
+ weight = 100
+ post = "break"
+ identifier = "contribute"
+ url = "/zh/contribute/"
+
+[[global]]
+ name = "新闻"
+ weight = 1
+ identifier = "news"
+ url = "/zh/news/"
+
+[[global]]
+ name = "文档"
+ weight = 5
+ identifier = "docs"
+ url = "/zh/documentation/"
+
+[[global]]
+ name = "主题"
+ weight = 10
+ identifier = "themes"
+ url = "https://themes.gohugo.io/"
+
+[[global]]
+ name = "作品展示"
+ weight = 20
+ identifier = "showcase"
+ url = "/zh/showcase/"
+
+# Anything with a weight > 100 gets an external icon
+[[global]]
+ name = "社区"
+ weight = 150
+ icon = true
+ identifier = "community"
+ post = "external"
+ url = "https://discourse.gohugo.io/"
+
+[[global]]
+ name = "GitHub"
+ weight = 200
+ identifier = "github"
+ post = "external"
+ url = "https://github.com/gohugoio/hugo"
diff --git a/config/_default/params.toml b/config/_default/params.toml
new file mode 100644
index 000000000..6ddf97e56
--- /dev/null
+++ b/config/_default/params.toml
@@ -0,0 +1,25 @@
+
+description = "The world’s fastest framework for building websites"
+## Used for views in rendered HTML (i.e., rather than using the .Hugo variable)
+release = "0.52"
+## Setting this to true will add a "noindex" to *EVERY* page on the site..
+removefromexternalsearch = false
+## Gh repo for site footer (include trailing slash)
+ghrepo = "https://github.com/gohugoio/hugoDocs/"
+## GH Repo for filing a new issue
+github_repo = "https://github.com/gohugoio/hugo/issues/new"
+### Edit content repo (set to automatically enter "edit" mode; this is good for "improve this page" links)
+ghdocsrepo = "https://github.com/gohugoio/hugoDocs/tree/master/docs"
+## Gitter URL
+gitter = "https://gitter.im/spf13/hugo"
+## Discuss Forum URL
+forum = "https://discourse.gohugo.io/"
+## Google Tag Manager
+gtmid = ""
+
+# First one is picked as the Twitter card image if not set on page.
+images = ["images/gohugoio-card.png"]
+
+flex_box_interior_classes = "flex-auto w-100 w-40-l mr3 mb3 bg-white ba b--moon-gray nested-copy-line-height"
+
+#sidebar_direction = "sidebar_left"
\ No newline at end of file
diff --git a/config/configLoader.go b/config/configLoader.go
deleted file mode 100644
index 2e37a5b35..000000000
--- a/config/configLoader.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// 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 config
-
-import (
- "path/filepath"
- "strings"
-
- "github.com/gohugoio/hugo/common/maps"
- "github.com/gohugoio/hugo/parser/metadecoders"
- "github.com/spf13/afero"
- "github.com/spf13/viper"
-)
-
-var (
- ValidConfigFileExtensions = []string{"toml", "yaml", "yml", "json"}
- validConfigFileExtensionsMap map[string]bool = make(map[string]bool)
-)
-
-func init() {
- for _, ext := range ValidConfigFileExtensions {
- validConfigFileExtensionsMap[ext] = true
- }
-}
-
-// IsValidConfigFilename returns whether filename is one of the supported
-// config formats in Hugo.
-func IsValidConfigFilename(filename string) bool {
- ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
- return validConfigFileExtensionsMap[ext]
-}
-
-// FromConfigString creates a config from the given YAML, JSON or TOML config. This is useful in tests.
-func FromConfigString(config, configType string) (Provider, error) {
- v := newViper()
- m, err := readConfig(metadecoders.FormatFromString(configType), []byte(config))
- if err != nil {
- return nil, err
- }
-
- v.MergeConfigMap(m)
-
- return v, nil
-}
-
-// FromFile loads the configuration from the given filename.
-func FromFile(fs afero.Fs, filename string) (Provider, error) {
- m, err := loadConfigFromFile(fs, filename)
- if err != nil {
- return nil, err
- }
-
- v := newViper()
-
- err = v.MergeConfigMap(m)
- if err != nil {
- return nil, err
- }
-
- return v, nil
-}
-
-// FromFileToMap is the same as FromFile, but it returns the config values
-// as a simple map.
-func FromFileToMap(fs afero.Fs, filename string) (map[string]interface{}, error) {
- return loadConfigFromFile(fs, filename)
-}
-
-func readConfig(format metadecoders.Format, data []byte) (map[string]interface{}, error) {
- m, err := metadecoders.Default.UnmarshalToMap(data, format)
- if err != nil {
- return nil, err
- }
-
- RenameKeys(m)
-
- return m, nil
-
-}
-
-func loadConfigFromFile(fs afero.Fs, filename string) (map[string]interface{}, error) {
- m, err := metadecoders.Default.UnmarshalFileToMap(fs, filename)
- if err != nil {
- return nil, err
- }
- RenameKeys(m)
- return m, nil
-}
-
-var keyAliases maps.KeyRenamer
-
-func init() {
- var err error
- keyAliases, err = maps.NewKeyRenamer(
- // Before 0.53 we used singular for "menu".
- "{menu,languages/*/menu}", "menus",
- )
-
- if err != nil {
- panic(err)
- }
-}
-
-// RenameKeys renames config keys in m recursively according to a global Hugo
-// alias definition.
-func RenameKeys(m map[string]interface{}) {
- keyAliases.Rename(m)
-}
-
-func newViper() *viper.Viper {
- v := viper.New()
-
- return v
-}
diff --git a/config/configLoader_test.go b/config/configLoader_test.go
deleted file mode 100644
index 546031334..000000000
--- a/config/configLoader_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 config
-
-import (
- "strings"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestIsValidConfigFileName(t *testing.T) {
- c := qt.New(t)
-
- for _, ext := range ValidConfigFileExtensions {
- filename := "config." + ext
- c.Assert(IsValidConfigFilename(filename), qt.Equals, true)
- c.Assert(IsValidConfigFilename(strings.ToUpper(filename)), qt.Equals, true)
- }
-
- c.Assert(IsValidConfigFilename(""), qt.Equals, false)
- c.Assert(IsValidConfigFilename("config.toml.swp"), qt.Equals, false)
-}
diff --git a/config/configProvider.go b/config/configProvider.go
deleted file mode 100644
index 187fb7b10..000000000
--- a/config/configProvider.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 config
-
-import (
- "github.com/spf13/cast"
-)
-
-// Provider provides the configuration settings for Hugo.
-type Provider interface {
- GetString(key string) string
- GetInt(key string) int
- GetBool(key string) bool
- GetStringMap(key string) map[string]interface{}
- GetStringMapString(key string) map[string]string
- GetStringSlice(key string) []string
- Get(key string) interface{}
- Set(key string, value interface{})
- IsSet(key string) bool
-}
-
-// GetStringSlicePreserveString returns a string slice from the given config and key.
-// It differs from the GetStringSlice method in that if the config value is a string,
-// we do not attempt to split it into fields.
-func GetStringSlicePreserveString(cfg Provider, key string) []string {
- sd := cfg.Get(key)
- return toStringSlicePreserveString(sd)
-}
-
-func toStringSlicePreserveString(v interface{}) []string {
- if sds, ok := v.(string); ok {
- return []string{sds}
- }
- return cast.ToStringSlice(v)
-}
-
-// SetBaseTestDefaults provides some common config defaults used in tests.
-func SetBaseTestDefaults(cfg Provider) {
- cfg.Set("resourceDir", "resources")
- cfg.Set("contentDir", "content")
- cfg.Set("dataDir", "data")
- cfg.Set("i18nDir", "i18n")
- cfg.Set("layoutDir", "layouts")
- cfg.Set("assetDir", "assets")
- cfg.Set("archetypeDir", "archetypes")
- cfg.Set("publishDir", "public")
-}
diff --git a/config/configProvider_test.go b/config/configProvider_test.go
deleted file mode 100644
index d9fff56b6..000000000
--- a/config/configProvider_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 config
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/viper"
-)
-
-func TestGetStringSlicePreserveString(t *testing.T) {
- c := qt.New(t)
- cfg := viper.New()
-
- s := "This is a string"
- sSlice := []string{"This", "is", "a", "slice"}
-
- cfg.Set("s1", s)
- cfg.Set("s2", sSlice)
-
- c.Assert(GetStringSlicePreserveString(cfg, "s1"), qt.DeepEquals, []string{s})
- c.Assert(GetStringSlicePreserveString(cfg, "s2"), qt.DeepEquals, sSlice)
- c.Assert(GetStringSlicePreserveString(cfg, "s3"), qt.IsNil)
-}
diff --git a/config/development/params.toml b/config/development/params.toml
new file mode 100644
index 000000000..4cd7314ab
--- /dev/null
+++ b/config/development/params.toml
@@ -0,0 +1 @@
+# Params for development (server mode)
diff --git a/config/env.go b/config/env.go
deleted file mode 100644
index f482cd247..000000000
--- a/config/env.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 config
-
-import (
- "os"
- "runtime"
- "strconv"
- "strings"
-)
-
-// GetNumWorkerMultiplier returns the base value used to calculate the number
-// of workers to use for Hugo's parallel execution.
-// It returns the value in HUGO_NUMWORKERMULTIPLIER OS env variable if set to a
-// positive integer, else the number of logical CPUs.
-func GetNumWorkerMultiplier() int {
- if gmp := os.Getenv("HUGO_NUMWORKERMULTIPLIER"); gmp != "" {
- if p, err := strconv.Atoi(gmp); err == nil && p > 0 {
- return p
- }
- }
- return runtime.NumCPU()
-}
-
-// SetEnvVars sets vars on the form key=value in the oldVars slice.
-func SetEnvVars(oldVars *[]string, keyValues ...string) {
- for i := 0; i < len(keyValues); i += 2 {
- setEnvVar(oldVars, keyValues[i], keyValues[i+1])
- }
-}
-
-func SplitEnvVar(v string) (string, string) {
- parts := strings.Split(v, "=")
- return parts[0], parts[1]
-}
-
-func setEnvVar(vars *[]string, key, value string) {
- for i := range *vars {
- if strings.HasPrefix((*vars)[i], key+"=") {
- (*vars)[i] = key + "=" + value
- return
- }
- }
- // New var.
- *vars = append(*vars, key+"="+value)
-}
diff --git a/config/env_test.go b/config/env_test.go
deleted file mode 100644
index 3c402b9ef..000000000
--- a/config/env_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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 config
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestSetEnvVars(t *testing.T) {
- t.Parallel()
- c := qt.New(t)
- vars := []string{"FOO=bar", "HUGO=cool", "BAR=foo"}
- SetEnvVars(&vars, "HUGO", "rocking!", "NEW", "bar")
- c.Assert(vars, qt.DeepEquals, []string{"FOO=bar", "HUGO=rocking!", "BAR=foo", "NEW=bar"})
-
- key, val := SplitEnvVar("HUGO=rocks")
- c.Assert(key, qt.Equals, "HUGO")
- c.Assert(val, qt.Equals, "rocks")
-}
diff --git a/config/privacy/privacyConfig.go b/config/privacy/privacyConfig.go
deleted file mode 100644
index ea34563eb..000000000
--- a/config/privacy/privacyConfig.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// 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 privacy
-
-import (
- "github.com/gohugoio/hugo/config"
- "github.com/mitchellh/mapstructure"
-)
-
-const privacyConfigKey = "privacy"
-
-// Service is the common values for a service in a policy definition.
-type Service struct {
- Disable bool
-}
-
-// Config is a privacy configuration for all the relevant services in Hugo.
-type Config struct {
- Disqus Disqus
- GoogleAnalytics GoogleAnalytics
- Instagram Instagram
- Twitter Twitter
- Vimeo Vimeo
- YouTube YouTube
-}
-
-// Disqus holds the privacy configuration settings related to the Disqus template.
-type Disqus struct {
- Service `mapstructure:",squash"`
-}
-
-// GoogleAnalytics holds the privacy configuration settings related to the Google Analytics template.
-type GoogleAnalytics struct {
- Service `mapstructure:",squash"`
-
- // Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
- UseSessionStorage bool
-
- // Enabling this will make the GA templates respect the
- // "Do Not Track" HTTP header. See https://www.paulfurley.com/google-analytics-dnt/.
- RespectDoNotTrack bool
-
- // Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
- AnonymizeIP bool
-}
-
-// Instagram holds the privacy configuration settings related to the Instagram shortcode.
-type Instagram struct {
- Service `mapstructure:",squash"`
-
- // If simple mode is enabled, a static and no-JS version of the Instagram
- // image card will be built.
- Simple bool
-}
-
-// Twitter holds the privacy configuration settingsrelated to the Twitter shortcode.
-type Twitter struct {
- Service `mapstructure:",squash"`
-
- // When set to true, the Tweet and its embedded page on your site are not used
- // for purposes that include personalized suggestions and personalized ads.
- EnableDNT bool
-
- // If simple mode is enabled, a static and no-JS version of the Tweet will be built.
- Simple bool
-}
-
-// Vimeo holds the privacy configuration settingsrelated to the Vimeo shortcode.
-type Vimeo struct {
- Service `mapstructure:",squash"`
-
- // If simple mode is enabled, only a thumbnail is fetched from i.vimeocdn.com and
- // shown with a play button overlaid. If a user clicks the button, he/she will
- // be taken to the video page on vimeo.com in a new browser tab.
- Simple bool
-}
-
-// YouTube holds the privacy configuration settingsrelated to the YouTube shortcode.
-type YouTube struct {
- Service `mapstructure:",squash"`
-
- // When you turn on privacy-enhanced mode,
- // YouTube won’t store information about visitors on your website
- // unless the user plays the embedded video.
- PrivacyEnhanced bool
-}
-
-// DecodeConfig creates a privacy Config from a given Hugo configuration.
-func DecodeConfig(cfg config.Provider) (pc Config, err error) {
- if !cfg.IsSet(privacyConfigKey) {
- return
- }
-
- m := cfg.GetStringMap(privacyConfigKey)
-
- err = mapstructure.WeakDecode(m, &pc)
-
- return
-}
diff --git a/config/privacy/privacyConfig_test.go b/config/privacy/privacyConfig_test.go
deleted file mode 100644
index d798721e1..000000000
--- a/config/privacy/privacyConfig_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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 privacy
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/gohugoio/hugo/config"
- "github.com/spf13/viper"
-)
-
-func TestDecodeConfigFromTOML(t *testing.T) {
- c := qt.New(t)
-
- tomlConfig := `
-
-someOtherValue = "foo"
-
-[privacy]
-[privacy.disqus]
-disable = true
-[privacy.googleAnalytics]
-disable = true
-respectDoNotTrack = true
-anonymizeIP = true
-useSessionStorage = true
-[privacy.instagram]
-disable = true
-simple = true
-[privacy.twitter]
-disable = true
-enableDNT = true
-simple = true
-[privacy.vimeo]
-disable = true
-simple = true
-[privacy.youtube]
-disable = true
-privacyEnhanced = true
-simple = true
-`
- cfg, err := config.FromConfigString(tomlConfig, "toml")
- c.Assert(err, qt.IsNil)
-
- pc, err := DecodeConfig(cfg)
- c.Assert(err, qt.IsNil)
- c.Assert(pc, qt.Not(qt.IsNil))
-
- got := []bool{
- pc.Disqus.Disable, pc.GoogleAnalytics.Disable,
- pc.GoogleAnalytics.RespectDoNotTrack, pc.GoogleAnalytics.AnonymizeIP,
- pc.GoogleAnalytics.UseSessionStorage, pc.Instagram.Disable,
- pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT,
- pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.Simple,
- pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable,
- }
-
- c.Assert(got, qt.All(qt.Equals), true)
-
-}
-
-func TestDecodeConfigFromTOMLCaseInsensitive(t *testing.T) {
- c := qt.New(t)
-
- tomlConfig := `
-
-someOtherValue = "foo"
-
-[Privacy]
-[Privacy.YouTube]
-PrivacyENhanced = true
-`
- cfg, err := config.FromConfigString(tomlConfig, "toml")
- c.Assert(err, qt.IsNil)
-
- pc, err := DecodeConfig(cfg)
- c.Assert(err, qt.IsNil)
- c.Assert(pc, qt.Not(qt.IsNil))
- c.Assert(pc.YouTube.PrivacyEnhanced, qt.Equals, true)
-}
-
-func TestDecodeConfigDefault(t *testing.T) {
- c := qt.New(t)
-
- pc, err := DecodeConfig(viper.New())
- c.Assert(err, qt.IsNil)
- c.Assert(pc, qt.Not(qt.IsNil))
- c.Assert(pc.YouTube.PrivacyEnhanced, qt.Equals, false)
-}
diff --git a/config/production/config.toml b/config/production/config.toml
new file mode 100644
index 000000000..961f04d35
--- /dev/null
+++ b/config/production/config.toml
@@ -0,0 +1,6 @@
+# Config for production
+
+# This is turned off in development as it is relatively slow.
+# This is needed to get accurate lastMod and Git commit info
+# on the docs pages.
+enableGitInfo = true
\ No newline at end of file
diff --git a/config/production/params.toml b/config/production/params.toml
new file mode 100644
index 000000000..d0071fe65
--- /dev/null
+++ b/config/production/params.toml
@@ -0,0 +1,2 @@
+# Params for production
+
diff --git a/config/services/servicesConfig.go b/config/services/servicesConfig.go
deleted file mode 100644
index 559848f5c..000000000
--- a/config/services/servicesConfig.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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 services
-
-import (
- "github.com/gohugoio/hugo/config"
- "github.com/mitchellh/mapstructure"
-)
-
-const (
- servicesConfigKey = "services"
-
- disqusShortnameKey = "disqusshortname"
- googleAnalyticsKey = "googleanalytics"
- rssLimitKey = "rssLimit"
-)
-
-// Config is a privacy configuration for all the relevant services in Hugo.
-type Config struct {
- Disqus Disqus
- GoogleAnalytics GoogleAnalytics
- Instagram Instagram
- Twitter Twitter
- RSS RSS
-}
-
-// Disqus holds the functional configuration settings related to the Disqus template.
-type Disqus struct {
- // A Shortname is the unique identifier assigned to a Disqus site.
- Shortname string
-}
-
-// GoogleAnalytics holds the functional configuration settings related to the Google Analytics template.
-type GoogleAnalytics struct {
- // The GA tracking ID.
- ID string
-}
-
-// Instagram holds the functional configuration settings related to the Instagram shortcodes.
-type Instagram struct {
- // The Simple variant of the Instagram is decorated with Bootstrap 4 card classes.
- // This means that if you use Bootstrap 4 or want to provide your own CSS, you want
- // to disable the inline CSS provided by Hugo.
- DisableInlineCSS bool
-}
-
-// Twitter holds the functional configuration settings related to the Twitter shortcodes.
-type Twitter struct {
- // The Simple variant of Twitter is decorated with a basic set of inline styles.
- // This means that if you want to provide your own CSS, you want
- // to disable the inline CSS provided by Hugo.
- DisableInlineCSS bool
-}
-
-// RSS holds the functional configuration settings related to the RSS feeds.
-type RSS struct {
- // Limit the number of pages.
- Limit int
-}
-
-// DecodeConfig creates a services Config from a given Hugo configuration.
-func DecodeConfig(cfg config.Provider) (c Config, err error) {
- m := cfg.GetStringMap(servicesConfigKey)
-
- err = mapstructure.WeakDecode(m, &c)
-
- // Keep backwards compatibility.
- if c.GoogleAnalytics.ID == "" {
- // Try the global config
- c.GoogleAnalytics.ID = cfg.GetString(googleAnalyticsKey)
- }
- if c.Disqus.Shortname == "" {
- c.Disqus.Shortname = cfg.GetString(disqusShortnameKey)
- }
-
- if c.RSS.Limit == 0 {
- c.RSS.Limit = cfg.GetInt(rssLimitKey)
- }
-
- return
-}
diff --git a/config/services/servicesConfig_test.go b/config/services/servicesConfig_test.go
deleted file mode 100644
index ed3038159..000000000
--- a/config/services/servicesConfig_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 services
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/gohugoio/hugo/config"
- "github.com/spf13/viper"
-)
-
-func TestDecodeConfigFromTOML(t *testing.T) {
- c := qt.New(t)
-
- tomlConfig := `
-
-someOtherValue = "foo"
-
-[services]
-[services.disqus]
-shortname = "DS"
-[services.googleAnalytics]
-id = "ga_id"
-[services.instagram]
-disableInlineCSS = true
-[services.twitter]
-disableInlineCSS = true
-`
- cfg, err := config.FromConfigString(tomlConfig, "toml")
- c.Assert(err, qt.IsNil)
-
- config, err := DecodeConfig(cfg)
- c.Assert(err, qt.IsNil)
- c.Assert(config, qt.Not(qt.IsNil))
-
- c.Assert(config.Disqus.Shortname, qt.Equals, "DS")
- c.Assert(config.GoogleAnalytics.ID, qt.Equals, "ga_id")
-
- c.Assert(config.Instagram.DisableInlineCSS, qt.Equals, true)
-}
-
-// Support old root-level GA settings etc.
-func TestUseSettingsFromRootIfSet(t *testing.T) {
- c := qt.New(t)
-
- cfg := viper.New()
- cfg.Set("disqusShortname", "root_short")
- cfg.Set("googleAnalytics", "ga_root")
-
- config, err := DecodeConfig(cfg)
- c.Assert(err, qt.IsNil)
- c.Assert(config, qt.Not(qt.IsNil))
-
- c.Assert(config.Disqus.Shortname, qt.Equals, "root_short")
- c.Assert(config.GoogleAnalytics.ID, qt.Equals, "ga_root")
-
-}
diff --git a/config/sitemap.go b/config/sitemap.go
deleted file mode 100644
index 4031b7ec1..000000000
--- a/config/sitemap.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 config
-
-import (
- "github.com/spf13/cast"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-// Sitemap configures the sitemap to be generated.
-type Sitemap struct {
- ChangeFreq string
- Priority float64
- Filename string
-}
-
-func DecodeSitemap(prototype Sitemap, input map[string]interface{}) Sitemap {
-
- for key, value := range input {
- switch key {
- case "changefreq":
- prototype.ChangeFreq = cast.ToString(value)
- case "priority":
- prototype.Priority = cast.ToFloat64(value)
- case "filename":
- prototype.Filename = cast.ToString(value)
- default:
- jww.WARN.Printf("Unknown Sitemap field: %s\n", key)
- }
- }
-
- return prototype
-}
diff --git a/content/en/_index.md b/content/en/_index.md
new file mode 100644
index 000000000..bfebd8b2f
--- /dev/null
+++ b/content/en/_index.md
@@ -0,0 +1,49 @@
+---
+title: "The world’s fastest framework for building websites"
+date: 2017-03-02T12:00:00-05:00
+features:
+ - heading: Blistering Speed
+ image_path: /images/icon-fast.svg
+ tagline: What's modern about waiting for your site to build?
+ copy: Hugo is the fastest tool of its kind. At <1 ms per page, the average site builds in less than a second.
+
+ - heading: Robust Content Management
+ image_path: /images/icon-content-management.svg
+ tagline: Flexibility rules. Hugo is a content strategist's dream.
+ copy: Hugo supports unlimited content types, taxonomies, menus, dynamic API-driven content, and more, all without plugins.
+
+ - heading: Shortcodes
+ image_path: /images/icon-shortcodes.svg
+ tagline: Hugo's shortcodes are Markdown's hidden superpower.
+ copy: We love the beautiful simplicity of markdown’s syntax, but there are times when we want more flexibility. Hugo shortcodes allow for both beauty and flexibility.
+
+ - heading: Built-in Templates
+ image_path: /images/icon-built-in-templates.svg
+ tagline: Hugo has common patterns to get your work done quickly.
+ copy: Hugo ships with pre-made templates to make quick work of SEO, commenting, analytics and other functions. One line of code, and you're done.
+
+ - heading: Multilingual and i18n
+ image_path: /images/icon-multilingual2.svg
+ tagline: Polyglot baked in.
+ copy: Hugo provides full i18n support for multi-language sites with the same straightforward development experience Hugo users love in single-language sites.
+
+ - heading: Custom Outputs
+ image_path: /images/icon-custom-outputs.svg
+ tagline: HTML not enough?
+ copy: Hugo allows you to output your content in multiple formats, including JSON or AMP, and makes it easy to create your own.
+sections:
+ - heading: "300+ Themes"
+ cta: Check out the Hugo themes.
+ link: https://themes.gohugo.io/
+ color_classes: bg-accent-color white
+ image: /images/homepage-screenshot-hugo-themes.jpg
+ copy: "Hugo provides a robust theming system that is easy to implement but capable of producing even the most complicated websites."
+ - heading: "Capable Templating"
+ cta: Get Started.
+ link: templates/
+ color_classes: bg-primary-color-light black
+ image: /images/home-page-templating-example.png
+ copy: "Hugo's Go-based templating provides just the right amount of logic to build anything from the simple to complex. If you prefer Jade/Pug-like syntax, you can also use Amber, Ace, or any combination of the three."
+---
+
+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/content/en/about/_index.md b/content/en/about/_index.md
new file mode 100644
index 000000000..8ed441b61
--- /dev/null
+++ b/content/en/about/_index.md
@@ -0,0 +1,20 @@
+---
+title: About Hugo
+linktitle: Overview
+description: Hugo's features, roadmap, license, and motivation.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+categories: []
+keywords: []
+menu:
+ docs:
+ parent: "about"
+ weight: 1
+weight: 1
+draft: false
+aliases: [/about-hugo/,/docs/]
+toc: false
+---
+
+Hugo is not your average static site generator.
diff --git a/content/en/about/benefits.md b/content/en/about/benefits.md
new file mode 100644
index 000000000..16d43e48f
--- /dev/null
+++ b/content/en/about/benefits.md
@@ -0,0 +1,43 @@
+---
+title: The Benefits of Static Site Generators
+linktitle: The Benefits of Static
+description: Improved performance, security and ease of use are just a few of the reasons static site generators are so appealing.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+keywords: [ssg,static,performance,security]
+menu:
+ docs:
+ parent: "about"
+ weight: 30
+weight: 30
+sections_weight: 30
+draft: false
+aliases: []
+toc: false
+---
+
+The purpose of website generators is to render content into HTML files. Most are "dynamic site generators." That means the HTTP server---i.e., the program that sends files to the browser to be viewed---runs the generator to create a new HTML file every time an end user requests a page.
+
+Over time, dynamic site generators were programmed to cache their HTML files to prevent unnecessary delays in delivering pages to end users. A cached page is a static version of a web page.
+
+Hugo takes caching a step further and all HTML files are rendered on your computer. You can review the files locally before copying them to the computer hosting the HTTP server. Since the HTML files aren't generated dynamically, we say that Hugo is a *static site generator*.
+
+This has many benefits. The most noticeable is performance. HTTP servers are *very* good at sending files---so good, in fact, that you can effectively serve the same number of pages with a fraction of the memory and CPU needed for a dynamic site.
+
+## More on Static Site Generators
+
+* ["An Introduction to Static Site Generators", David Walsh][]
+* ["Hugo vs. Wordpress page load speed comparison: Hugo leaves WordPress in its dust", GettingThingsTech][hugovwordpress]
+* ["Static Site Generators", O'Reilly][]
+* [StaticGen: Top Open-Source Static Site Generators (GitHub Stars)][]
+* ["Top 10 Static Website Generators", Netlify blog][]
+* ["The Resurgence of Static", dotCMS][dotcms]
+
+
+["An Introduction to Static Site Generators", David Walsh]: https://davidwalsh.name/introduction-static-site-generators
+["Static Site Generators", O'Reilly]: https://www.oreilly.com/web-platform/free/files/static-site-generators.pdf
+["Top 10 Static Website Generators", Netlify blog]: https://www.netlify.com/blog/2016/05/02/top-ten-static-website-generators/
+[hugovwordpress]: https://gettingthingstech.com/hugo-vs.-wordpress-page-load-speed-comparison-hugo-leaves-wordpress-in-its-dust/
+[StaticGen: Top Open-Source Static Site Generators (GitHub Stars)]: https://www.staticgen.com/
+[dotcms]: https://dotcms.com/blog/post/the-resurgence-of-static
diff --git a/content/en/about/features.md b/content/en/about/features.md
new file mode 100644
index 000000000..cadcc6b0f
--- /dev/null
+++ b/content/en/about/features.md
@@ -0,0 +1,87 @@
+---
+title: Hugo Features
+linktitle: Hugo Features
+description: Hugo boasts blistering speed, robust content management, and a powerful templating language making it a great fit for all kinds of static websites.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+menu:
+ docs:
+ parent: "about"
+ weight: 20
+weight: 20
+sections_weight: 20
+draft: false
+toc: true
+---
+
+## General
+
+* [Extremely fast][] build times (< 1 ms per page)
+* Completely cross platform, with [easy installation][install] on macOS, Linux, Windows, and more
+* Renders changes on the fly with [LiveReload][] as you develop
+* [Powerful theming][]
+* [Host your site anywhere][hostanywhere]
+
+## Organization
+
+* Straightforward [organization for your projects][], including website sections
+* Customizable [URLs][]
+* Support for configurable [taxonomies][], including categories and tags
+* [Sort content][] as you desire through powerful template [functions][]
+* Automatic [table of contents][] generation
+* [Dynamic menu][] creation
+* [Pretty URLs][] support
+* [Permalink][] pattern support
+* Redirects via [aliases][]
+
+## Content
+
+* Native Markdown and Emacs Org-Mode support, as well as other languages via *external helpers* (see [supported formats][])
+* TOML, YAML, and JSON metadata support in [front matter][]
+* Customizable [homepage][]
+* Multiple [content types][]
+* Automatic and user defined [content summaries][]
+* [Shortcodes][] to enable rich content inside of Markdown
+* ["Minutes to Read"][pagevars] functionality
+* ["Wordcount"][pagevars] functionality
+
+## Additional Features
+
+* Integrated [Disqus][] comment support
+* Integrated [Google Analytics][] support
+* Automatic [RSS][] creation
+* Support for [Go][], [Amber], and [Ace][] HTML templates
+* [Syntax highlighting][] powered by [Chroma][] (partly compatible with Pygments)
+
+
+[Ace]: /templates/alternatives/
+[aliases]: /content-management/urls/#aliases
+[Amber]: https://github.com/eknkc/amber
+[Chroma]: https://github.com/alecthomas/chroma
+[content summaries]: /content-management/summaries/
+[content types]: /content-management/types/
+[Disqus]: https://disqus.com/
+[Dynamic menu]: /templates/menus/
+[Extremely fast]: https://github.com/bep/hugo-benchmark
+[front matter]: /content-management/front-matter/
+[functions]: /functions/
+[Go]: https://golang.org/pkg/html/template/
+[Google Analytics]: https://google-analytics.com/
+[homepage]: /templates/homepage/
+[hostanywhere]: /hosting-and-deployment/
+[install]: /getting-started/installing/
+[LiveReload]: /getting-started/usage/
+[organization for your projects]: /getting-started/directory-structure/
+[pagevars]: /variables/page/
+[Permalink]: /content-management/urls/#permalinks
+[Powerful theming]: /themes/
+[Pretty URLs]: /content-management/urls/
+[RSS]: /templates/rss/
+[Shortcodes]: /content-management/shortcodes/
+[sort content]: /templates/
+[supported formats]: /content-management/formats/
+[Syntax highlighting]: /tools/syntax-highlighting/
+[table of contents]: /content-management/toc/
+[taxonomies]: /content-management/taxonomies/
+[URLs]: /content-management/urls/
diff --git a/content/en/about/hugo-and-gdpr.md b/content/en/about/hugo-and-gdpr.md
new file mode 100644
index 000000000..e193e1838
--- /dev/null
+++ b/content/en/about/hugo-and-gdpr.md
@@ -0,0 +1,135 @@
+
+
+---
+title: Hugo and the General Data Protection Regulation (GDPR)
+linktitle: Hugo and GDPR
+description: About how to configure your Hugo site to meet the new regulations.
+date: 2018-05-25
+layout: single
+keywords: ["GDPR", "Privacy", "Data Protection"]
+menu:
+ docs:
+ parent: "about"
+ weight: 5
+weight: 5
+sections_weight: 5
+draft: false
+aliases: [/privacy/,/gdpr/]
+toc: true
+---
+
+
+ General Data Protection Regulation ([GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation)) is a regulation in EU law on data protection and privacy for all individuals within the European Union and the European Economic Area. It became enforceable on 25 May 2018.
+
+ **Hugo is a static site generator. By using Hugo you are already standing on very solid ground. Static HTML files on disk are much easier to reason about compared to server and database driven web sites.**
+
+ But even static websites can integrate with external services, so from version `0.41`, Hugo provides a **Privacy Config** that covers the relevant built-in templates.
+
+ Note that:
+
+ * These settings have their defaults setting set to _off_, i.e. how it worked before Hugo `0.41`. You must do your own evaluation of your site and apply the appropriate settings.
+ * These settings work with the [internal templates](/templates/internal/). Some theme may contain custom templates for embedding services like Google Analytics. In that case these options have no effect.
+ * We will continue this work and improve this further in future Hugo versions.
+
+## All Privacy Settings
+
+Below are all privacy settings and their default value. These settings need to be put in your site config (e.g. `config.toml`).
+
+ {{< code-toggle file="config">}}
+[privacy]
+[privacy.disqus]
+disable = false
+[privacy.googleAnalytics]
+disable = false
+respectDoNotTrack = false
+anonymizeIP = false
+useSessionStorage = false
+[privacy.instagram]
+disable = false
+simple = false
+[privacy.twitter]
+disable = false
+enableDNT = false
+simple = false
+[privacy.vimeo]
+disable = false
+simple = false
+[privacy.youtube]
+disable = false
+privacyEnhanced = false
+{{< /code-toggle >}}
+
+
+## Disable All Services
+
+An example Privacy Config that disables all the relevant services in Hugo. With this configuration, the other settings will not matter.
+
+ {{< code-toggle file="config">}}
+[privacy]
+[privacy.disqus]
+disable = true
+[privacy.googleAnalytics]
+disable = true
+[privacy.instagram]
+disable = true
+[privacy.twitter]
+disable = true
+[privacy.vimeo]
+disable = true
+[privacy.youtube]
+disable = true
+{{< /code-toggle >}}
+
+## The Privacy Settings Explained
+
+### GoogleAnalytics
+
+anonymizeIP
+: Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
+
+respectDoNotTrack
+: Enabling this will make the GA templates respect the "Do Not Track" HTTP header.
+
+useSessionStorage
+: Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
+
+### Instagram
+
+simple
+: If simple mode is enabled, a static and no-JS version of the Instagram image card will be built. Note that this only supports image cards and the image itself will be fetched from Instagram's servers.
+
+**Note:** If you use the _simple mode_ for Instagram and a site styled with Bootstrap 4, you may want to disable the inlines styles provided by Hugo:
+
+ {{< code-toggle file="config">}}
+[services]
+[services.instagram]
+disableInlineCSS = true
+{{< /code-toggle >}}
+
+### Twitter
+
+enableDNT
+: Enabling this for the twitter/tweet shortcode, the tweet and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads.
+
+simple
+: If simple mode is enabled, a static and no-JS version of a tweet will be built.
+
+
+**Note:** If you use the _simple mode_ for Twitter, you may want to disable the inlines styles provided by Hugo:
+
+ {{< code-toggle file="config">}}
+[services]
+[services.twitter]
+disableInlineCSS = true
+{{< /code-toggle >}}
+
+### YouTube
+
+privacyEnhanced
+: When you turn on privacy-enhanced mode, YouTube won’t store information about visitors on your website unless the user plays the embedded video.
+
+### Vimeo
+
+simple
+: If simple mode is enabled, the video thumbnail is fetched from Vimeo's servers and it is overlayed with a play button. If the user clicks to play the video, it will open in a new tab directly on Vimeo's website.
+
diff --git a/content/en/about/license.md b/content/en/about/license.md
new file mode 100644
index 000000000..ae74b6047
--- /dev/null
+++ b/content/en/about/license.md
@@ -0,0 +1,165 @@
+---
+title: Apache License
+linktitle: License
+description: Hugo v0.15 and later are released under the Apache 2.0 license.
+date: 2016-02-01
+publishdate: 2016-02-01
+lastmod: 2016-03-02
+categories: ["about hugo"]
+keywords: ["License","apache"]
+menu:
+ docs:
+ parent: "about"
+ weight: 60
+weight: 60
+sections_weight: 60
+aliases: [/meta/license]
+toc: true
+---
+
+{{% note %}}
+Hugo v0.15 and later are released under the Apache 2.0 license.
+Earlier versions of Hugo were released under the [Simple Public License](https://opensource.org/licenses/Simple-2.0).
+{{% /note %}}
+
+_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.
+
+_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.
+
+{{< code file="apache-notice.txt" download="apache-notice.txt" >}}
+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
+
+ https://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.
+{{< /code >}}
diff --git a/content/en/about/what-is-hugo.md b/content/en/about/what-is-hugo.md
new file mode 100644
index 000000000..257c7e82d
--- /dev/null
+++ b/content/en/about/what-is-hugo.md
@@ -0,0 +1,65 @@
+---
+title: What is Hugo
+linktitle: What is Hugo
+description: Hugo is a fast and modern static site generator written in Go, and designed to make website creation fun again.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+layout: single
+menu:
+ docs:
+ parent: "about"
+ weight: 10
+weight: 10
+sections_weight: 10
+draft: false
+aliases: [/overview/introduction/,/about/why-i-built-hugo/]
+toc: true
+---
+
+Hugo is a general-purpose website framework. Technically speaking, Hugo is a [static site generator][]. Unlike systems that dynamically build a page with each visitor request, Hugo builds pages when you create or update your content. Since websites are viewed far more often than they are edited, Hugo is designed to provide an optimal viewing experience for your website's end users and an ideal writing experience for website authors.
+
+Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted anywhere, including [Netlify][], [Heroku][], [GoDaddy][], [DreamHost][], [GitHub Pages][], [GitLab Pages][], [Surge][], [Aerobatic][], [Firebase][], [Google Cloud Storage][], [Amazon S3][], [Rackspace][], [Azure][], and [CloudFront][] and work well with CDNs. Hugo sites run without the need for a database or dependencies on expensive runtimes like Ruby, Python, or PHP.
+
+We think of Hugo as the ideal website creation tool with nearly instant build times, able to rebuild whenever a change is made.
+
+## How Fast is Hugo?
+
+{{< youtube "CdiDYZ51a2o" >}}
+
+## What Does Hugo Do?
+
+In technical terms, Hugo takes a source directory of files and templates and uses these as input to create a complete website.
+
+## Who Should Use Hugo?
+
+Hugo is for people that prefer writing in a text editor over a browser.
+
+Hugo is for people who want to hand code their own website without worrying about setting up complicated runtimes, dependencies and databases.
+
+Hugo is for people building a blog, a company site, a portfolio site, documentation, a single landing page, or a website with thousands of pages.
+
+
+
+[@spf13]: https://twitter.com/@spf13
+[Aerobatic]: https://www.aerobatic.com/
+[Amazon S3]: https://aws.amazon.com/s3/
+[Azure]: https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website
+[CloudFront]: https://aws.amazon.com/cloudfront/ "Amazon CloudFront"
+[DreamHost]: https://www.dreamhost.com/
+[Firebase]: https://firebase.google.com/docs/hosting/ "Firebase static hosting"
+[GitHub Pages]: https://pages.github.com/
+[GitLab Pages]: https://about.gitlab.com/features/pages/
+[Go language]: https://golang.org/
+[GoDaddy]: https://www.godaddy.com/ "Godaddy.com Hosting"
+[Google Cloud Storage]: https://cloud.google.com/storage/
+[Heroku]: https://www.heroku.com/
+[Jekyll]: https://jekyllrb.com/
+[Middleman]: https://middlemanapp.com/
+[Nanoc]: https://nanoc.ws/
+[Netlify]: https://netlify.com
+[Rackspace]: https://www.rackspace.com/cloud/files
+[Surge]: https://surge.sh
+[contributing to it]: https://github.com/gohugoio/hugo
+[rackspace]: https://www.rackspace.com/cloud/files
+[static site generator]: /about/benefits/
diff --git a/content/en/commands/hugo.md b/content/en/commands/hugo.md
new file mode 100644
index 000000000..a802271bd
--- /dev/null
+++ b/content/en/commands/hugo.md
@@ -0,0 +1,82 @@
+---
+date: 2019-07-31
+title: "hugo"
+slug: hugo
+url: /commands/hugo/
+---
+## hugo
+
+hugo builds your site
+
+### Synopsis
+
+hugo is the main command, used to build your Hugo site.
+
+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. Defaults: $TMPDIR/hugo_cache/
+ --cleanDestinationDir remove files from destination not found in static directories
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ -c, --contentDir string filesystem path to content directory
+ --debug debug output
+ -d, --destination string filesystem path to write files to
+ --disableKinds strings disable different kind of pages (home, RSS etc.)
+ --enableGitInfo add Git revision, date and author 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
+ --i18n-warnings print missing translations
+ --ignoreCache ignores the cache directory
+ --ignoreVendor ignores any _vendor directory
+ -l, --layoutDir string filesystem path to layout directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --minify minify any supported output format (HTML, XML etc.)
+ --noChmod don't sync permission mode of files
+ --noTimes don't sync modification time of files
+ --path-warnings print warnings on duplicate target paths etc.
+ --quiet build in quiet mode
+ --renderToMemory render to memory (only useful for benchmark testing)
+ -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)
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+ -w, --watch watch filesystem for changes and recreate as needed
+```
+
+### SEE ALSO
+
+* [hugo check](/commands/hugo_check/) - Contains some verification checks
+* [hugo config](/commands/hugo_config/) - Print the site configuration
+* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
+* [hugo deploy](/commands/hugo_deploy/) - Deploy your site to a Cloud provider.
+* [hugo env](/commands/hugo_env/) - Print Hugo version and environment info
+* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
+* [hugo import](/commands/hugo_import/) - Import your site from others.
+* [hugo list](/commands/hugo_list/) - Listing out various types of content
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+* [hugo new](/commands/hugo_new/) - Create new content for your site
+* [hugo server](/commands/hugo_server/) - A high performance webserver
+* [hugo version](/commands/hugo_version/) - Print the version number of Hugo
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_check.md b/content/en/commands/hugo_check.md
new file mode 100644
index 000000000..fc727476a
--- /dev/null
+++ b/content/en/commands/hugo_check.md
@@ -0,0 +1,43 @@
+---
+date: 2019-07-31
+title: "hugo check"
+slug: hugo_check
+url: /commands/hugo_check/
+---
+## hugo check
+
+Contains some verification checks
+
+### Synopsis
+
+Contains some verification checks
+
+### Options
+
+```
+ -h, --help help for check
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo check ulimit](/commands/hugo_check_ulimit/) - Check system ulimit settings
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_check_ulimit.md b/content/en/commands/hugo_check_ulimit.md
new file mode 100644
index 000000000..478c9a850
--- /dev/null
+++ b/content/en/commands/hugo_check_ulimit.md
@@ -0,0 +1,47 @@
+---
+date: 2019-07-31
+title: "hugo check ulimit"
+slug: hugo_check_ulimit
+url: /commands/hugo_check_ulimit/
+---
+## hugo check ulimit
+
+Check system ulimit settings
+
+### Synopsis
+
+Hugo will inspect the current ulimit settings on the system.
+This is primarily to ensure that Hugo can watch enough files on some OSs
+
+```
+hugo check ulimit [flags]
+```
+
+### Options
+
+```
+ -h, --help help for ulimit
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo check](/commands/hugo_check/) - Contains some verification checks
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_config.md b/content/en/commands/hugo_config.md
new file mode 100644
index 000000000..6e1094903
--- /dev/null
+++ b/content/en/commands/hugo_config.md
@@ -0,0 +1,47 @@
+---
+date: 2019-07-31
+title: "hugo config"
+slug: hugo_config
+url: /commands/hugo_config/
+---
+## hugo config
+
+Print the site configuration
+
+### Synopsis
+
+Print the site configuration, both default and custom settings.
+
+```
+hugo config [flags]
+```
+
+### Options
+
+```
+ -h, --help help for config
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo config mounts](/commands/hugo_config_mounts/) - Print the configured file mounts
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_config_mounts.md b/content/en/commands/hugo_config_mounts.md
new file mode 100644
index 000000000..98cd6f9eb
--- /dev/null
+++ b/content/en/commands/hugo_config_mounts.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo config mounts"
+slug: hugo_config_mounts
+url: /commands/hugo_config_mounts/
+---
+## hugo config mounts
+
+Print the configured file mounts
+
+### Synopsis
+
+Print the configured file mounts
+
+```
+hugo config mounts [flags]
+```
+
+### Options
+
+```
+ -h, --help help for mounts
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo config](/commands/hugo_config/) - Print the site configuration
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_convert.md b/content/en/commands/hugo_convert.md
new file mode 100644
index 000000000..1e85b72f1
--- /dev/null
+++ b/content/en/commands/hugo_convert.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+title: "hugo convert"
+slug: hugo_convert
+url: /commands/hugo_convert/
+---
+## hugo convert
+
+Convert your content to different formats
+
+### Synopsis
+
+Convert your content (e.g. front matter) to different formats.
+
+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
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [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
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_convert_toJSON.md b/content/en/commands/hugo_convert_toJSON.md
new file mode 100644
index 000000000..484d942e6
--- /dev/null
+++ b/content/en/commands/hugo_convert_toJSON.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for toJSON
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ -o, --output string filesystem path to write files to
+ --quiet build in quiet mode
+ -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
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_convert_toTOML.md b/content/en/commands/hugo_convert_toTOML.md
new file mode 100644
index 000000000..53f25cf66
--- /dev/null
+++ b/content/en/commands/hugo_convert_toTOML.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for toTOML
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ -o, --output string filesystem path to write files to
+ --quiet build in quiet mode
+ -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
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_convert_toYAML.md b/content/en/commands/hugo_convert_toYAML.md
new file mode 100644
index 000000000..4173883d8
--- /dev/null
+++ b/content/en/commands/hugo_convert_toYAML.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for toYAML
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ -o, --output string filesystem path to write files to
+ --quiet build in quiet mode
+ -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
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_deploy.md b/content/en/commands/hugo_deploy.md
new file mode 100644
index 000000000..054a16d42
--- /dev/null
+++ b/content/en/commands/hugo_deploy.md
@@ -0,0 +1,56 @@
+---
+date: 2019-07-31
+title: "hugo deploy"
+slug: hugo_deploy
+url: /commands/hugo_deploy/
+---
+## hugo deploy
+
+Deploy your site to a Cloud provider.
+
+### Synopsis
+
+Deploy your site to a Cloud provider.
+
+See https://gohugo.io/hosting-and-deployment/hugo-deploy/ for detailed
+documentation.
+
+
+```
+hugo deploy [flags]
+```
+
+### 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 via the cloudFrontDistributionID 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
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_env.md b/content/en/commands/hugo_env.md
new file mode 100644
index 000000000..5d134d693
--- /dev/null
+++ b/content/en/commands/hugo_env.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo env"
+slug: hugo_env
+url: /commands/hugo_env/
+---
+## hugo env
+
+Print Hugo version and environment info
+
+### Synopsis
+
+Print Hugo version and environment info. This is useful in Hugo bug reports.
+
+```
+hugo env [flags]
+```
+
+### Options
+
+```
+ -h, --help help for env
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_gen.md b/content/en/commands/hugo_gen.md
new file mode 100644
index 000000000..e195882de
--- /dev/null
+++ b/content/en/commands/hugo_gen.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo gen"
+slug: hugo_gen
+url: /commands/hugo_gen/
+---
+## hugo gen
+
+A collection of several useful generators.
+
+### Synopsis
+
+A collection of several useful generators.
+
+### Options
+
+```
+ -h, --help help for gen
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo gen autocomplete](/commands/hugo_gen_autocomplete/) - Generate shell autocompletion script for Hugo
+* [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
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_gen_autocomplete.md b/content/en/commands/hugo_gen_autocomplete.md
new file mode 100644
index 000000000..0c4657a87
--- /dev/null
+++ b/content/en/commands/hugo_gen_autocomplete.md
@@ -0,0 +1,64 @@
+---
+date: 2019-07-31
+title: "hugo gen autocomplete"
+slug: hugo_gen_autocomplete
+url: /commands/hugo_gen_autocomplete/
+---
+## hugo gen autocomplete
+
+Generate shell autocompletion script for Hugo
+
+### Synopsis
+
+Generates a shell autocompletion script for Hugo.
+
+NOTE: The current version supports Bash only.
+ This should work for *nix systems with Bash installed.
+
+By default, the file is written directly to /etc/bash_completion.d
+for convenience, and the command may need superuser rights, e.g.:
+
+ $ sudo hugo gen autocomplete
+
+Add `--completionfile=/path/to/file` flag to set alternative
+file-path and name.
+
+Logout and in again to reload the completion scripts,
+or just source them in directly:
+
+ $ . /etc/bash_completion
+
+```
+hugo gen autocomplete [flags]
+```
+
+### Options
+
+```
+ --completionfile string autocompletion file (default "/etc/bash_completion.d/hugo.sh")
+ -h, --help help for autocomplete
+ --type string autocompletion type (currently only bash supported) (default "bash")
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_gen_chromastyles.md b/content/en/commands/hugo_gen_chromastyles.md
new file mode 100644
index 000000000..c893a7f33
--- /dev/null
+++ b/content/en/commands/hugo_gen_chromastyles.md
@@ -0,0 +1,51 @@
+---
+date: 2019-07-31
+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 pygmentsUseClasses is enabled in config.
+
+See https://help.farbox.com/pygments.html for preview of available styles
+
+```
+hugo gen chromastyles [flags]
+```
+
+### Options
+
+```
+ -h, --help help for chromastyles
+ --highlightStyle string style used for highlighting lines (see https://github.com/alecthomas/chroma) (default "bg:#ffffcc")
+ --linesStyle string style used for line numbers (see https://github.com/alecthomas/chroma)
+ --style string highlighter style (see https://help.farbox.com/pygments.html) (default "friendly")
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_gen_doc.md b/content/en/commands/hugo_gen_doc.md
new file mode 100644
index 000000000..cda495090
--- /dev/null
+++ b/content/en/commands/hugo_gen_doc.md
@@ -0,0 +1,53 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ --dir string the directory to write the doc. (default "/tmp/hugodoc/")
+ -h, --help help for doc
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_gen_man.md b/content/en/commands/hugo_gen_man.md
new file mode 100644
index 000000000..a6f7b7534
--- /dev/null
+++ b/content/en/commands/hugo_gen_man.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ --dir string the directory to write the man pages. (default "man/")
+ -h, --help help for man
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_import.md b/content/en/commands/hugo_import.md
new file mode 100644
index 000000000..67b91e66c
--- /dev/null
+++ b/content/en/commands/hugo_import.md
@@ -0,0 +1,45 @@
+---
+date: 2019-07-31
+title: "hugo import"
+slug: hugo_import
+url: /commands/hugo_import/
+---
+## hugo import
+
+Import your site from others.
+
+### Synopsis
+
+Import your site from other web site generators like Jekyll.
+
+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
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo import jekyll](/commands/hugo_import_jekyll/) - hugo import from Jekyll
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_import_jekyll.md b/content/en/commands/hugo_import_jekyll.md
new file mode 100644
index 000000000..e126c2107
--- /dev/null
+++ b/content/en/commands/hugo_import_jekyll.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ --force allow import into non-empty target directory
+ -h, --help help for jekyll
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo import](/commands/hugo_import/) - Import your site from others.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_list.md b/content/en/commands/hugo_list.md
new file mode 100644
index 000000000..16bd1933e
--- /dev/null
+++ b/content/en/commands/hugo_list.md
@@ -0,0 +1,48 @@
+---
+date: 2019-07-31
+title: "hugo list"
+slug: hugo_list
+url: /commands/hugo_list/
+---
+## hugo list
+
+Listing out various types of content
+
+### Synopsis
+
+Listing out various types of content.
+
+List requires a subcommand, e.g. `hugo list drafts`.
+
+### Options
+
+```
+ -h, --help help for list
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo list all](/commands/hugo_list_all/) - List all posts
+* [hugo list drafts](/commands/hugo_list_drafts/) - List all drafts
+* [hugo list expired](/commands/hugo_list_expired/) - List all posts already expired
+* [hugo list future](/commands/hugo_list_future/) - List all posts dated in the future
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_list_all.md b/content/en/commands/hugo_list_all.md
new file mode 100644
index 000000000..6a820ee18
--- /dev/null
+++ b/content/en/commands/hugo_list_all.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo list all"
+slug: hugo_list_all
+url: /commands/hugo_list_all/
+---
+## hugo list all
+
+List all posts
+
+### Synopsis
+
+List all of the posts in your content directory, include drafts, future and expired pages.
+
+```
+hugo list all [flags]
+```
+
+### Options
+
+```
+ -h, --help help for all
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo list](/commands/hugo_list/) - Listing out various types of content
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_list_drafts.md b/content/en/commands/hugo_list_drafts.md
new file mode 100644
index 000000000..2dff18040
--- /dev/null
+++ b/content/en/commands/hugo_list_drafts.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo list drafts"
+slug: hugo_list_drafts
+url: /commands/hugo_list_drafts/
+---
+## hugo list drafts
+
+List all drafts
+
+### Synopsis
+
+List all of the drafts in your content directory.
+
+```
+hugo list drafts [flags]
+```
+
+### Options
+
+```
+ -h, --help help for drafts
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo list](/commands/hugo_list/) - Listing out various types of content
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_list_expired.md b/content/en/commands/hugo_list_expired.md
new file mode 100644
index 000000000..1703d9277
--- /dev/null
+++ b/content/en/commands/hugo_list_expired.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo list expired"
+slug: hugo_list_expired
+url: /commands/hugo_list_expired/
+---
+## hugo list expired
+
+List all posts already expired
+
+### Synopsis
+
+List all of the posts in your content directory which has already expired.
+
+```
+hugo list expired [flags]
+```
+
+### Options
+
+```
+ -h, --help help for expired
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo list](/commands/hugo_list/) - Listing out various types of content
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_list_future.md b/content/en/commands/hugo_list_future.md
new file mode 100644
index 000000000..bb4621484
--- /dev/null
+++ b/content/en/commands/hugo_list_future.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo list future"
+slug: hugo_list_future
+url: /commands/hugo_list_future/
+---
+## hugo list future
+
+List all posts dated in the future
+
+### Synopsis
+
+List all of the posts in your content directory which will be posted in the future.
+
+```
+hugo list future [flags]
+```
+
+### Options
+
+```
+ -h, --help help for future
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo list](/commands/hugo_list/) - Listing out various types of content
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod.md b/content/en/commands/hugo_mod.md
new file mode 100644
index 000000000..f34cba2f3
--- /dev/null
+++ b/content/en/commands/hugo_mod.md
@@ -0,0 +1,83 @@
+---
+date: 2019-07-31
+title: "hugo mod"
+slug: hugo_mod
+url: /commands/hugo_mod/
+---
+## hugo mod
+
+Various Hugo Modules helpers.
+
+### 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 site
+configuration, provided by a _vendor directory (if no --ignoreVendor flag provided),
+Go Modules, or a folder inside the themes directory, in that order.
+
+See https://gohugo.io/hugo-modules/ for more information.
+
+
+
+### 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. Defaults: $TMPDIR/hugo_cache/
+ --cleanDestinationDir remove files from destination not found in static directories
+ -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 and author 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 mod
+ --i18n-warnings print missing translations
+ --ignoreCache ignores the cache directory
+ -l, --layoutDir string filesystem path to layout directory
+ --minify minify any supported output format (HTML, XML etc.)
+ --noChmod don't sync permission mode of files
+ --noTimes don't sync modification time of files
+ --path-warnings print warnings on duplicate target paths etc.
+ --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/)
+ --trace file write trace to file (not useful in general)
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo mod clean](/commands/hugo_mod_clean/) - Delete the entire Hugo Module cache.
+* [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 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.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_clean.md b/content/en/commands/hugo_mod_clean.md
new file mode 100644
index 000000000..ef21ddfcd
--- /dev/null
+++ b/content/en/commands/hugo_mod_clean.md
@@ -0,0 +1,52 @@
+---
+date: 2019-07-31
+title: "hugo mod clean"
+slug: hugo_mod_clean
+url: /commands/hugo_mod_clean/
+---
+## hugo mod clean
+
+Delete the entire Hugo Module cache.
+
+### Synopsis
+
+Delete the entire Hugo Module cache.
+
+Note that after you run this command, all of your dependencies will be re-downloaded next time you run "hugo".
+
+Also note that if you configure a positive maxAge for the "modules" file cache, it will also be cleaned as part of "hugo --gc".
+
+
+
+```
+hugo mod clean [flags]
+```
+
+### Options
+
+```
+ -h, --help help for clean
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_get.md b/content/en/commands/hugo_mod_get.md
new file mode 100644
index 000000000..678bcded5
--- /dev/null
+++ b/content/en/commands/hugo_mod_get.md
@@ -0,0 +1,71 @@
+---
+date: 2019-07-31
+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 module dependencies:
+
+ hugo mod get -u
+
+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 site
+configuration, provided by a _vendor directory (if no --ignoreVendor 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]
+```
+
+### Options
+
+```
+ -h, --help help for get
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_graph.md b/content/en/commands/hugo_mod_graph.md
new file mode 100644
index 000000000..397283180
--- /dev/null
+++ b/content/en/commands/hugo_mod_graph.md
@@ -0,0 +1,48 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for graph
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_init.md b/content/en/commands/hugo_mod_init.md
new file mode 100644
index 000000000..53c5412f1
--- /dev/null
+++ b/content/en/commands/hugo_mod_init.md
@@ -0,0 +1,53 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for init
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_tidy.md b/content/en/commands/hugo_mod_tidy.md
new file mode 100644
index 000000000..68e7ac2d1
--- /dev/null
+++ b/content/en/commands/hugo_mod_tidy.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+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.
+
+### Synopsis
+
+Remove unused entries in go.mod and go.sum.
+
+```
+hugo mod tidy [flags]
+```
+
+### Options
+
+```
+ -h, --help help for tidy
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_mod_vendor.md b/content/en/commands/hugo_mod_vendor.md
new file mode 100644
index 000000000..f3beb1253
--- /dev/null
+++ b/content/en/commands/hugo_mod_vendor.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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]
+```
+
+### Options
+
+```
+ -h, --help help for vendor
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo mod](/commands/hugo_mod/) - Various Hugo Modules helpers.
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_new.md b/content/en/commands/hugo_new.md
new file mode 100644
index 000000000..4587f4475
--- /dev/null
+++ b/content/en/commands/hugo_new.md
@@ -0,0 +1,80 @@
+---
+date: 2019-07-31
+title: "hugo new"
+slug: hugo_new
+url: /commands/hugo_new/
+---
+## hugo new
+
+Create new content for your site
+
+### 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 site, they will be used.
+
+Ensure you run this within the root directory of your site.
+
+```
+hugo new [path] [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. Defaults: $TMPDIR/hugo_cache/
+ --cleanDestinationDir remove files from destination not found in static directories
+ -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.)
+ --editor string edit new content with this editor, if provided
+ --enableGitInfo add Git revision, date and author 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 new
+ --i18n-warnings print missing translations
+ --ignoreCache ignores the cache directory
+ -k, --kind string content type to create
+ -l, --layoutDir string filesystem path to layout directory
+ --minify minify any supported output format (HTML, XML etc.)
+ --noChmod don't sync permission mode of files
+ --noTimes don't sync modification time of files
+ --path-warnings print warnings on duplicate target paths etc.
+ --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/)
+ --trace file write trace to file (not useful in general)
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+* [hugo new site](/commands/hugo_new_site/) - Create a new site (skeleton)
+* [hugo new theme](/commands/hugo_new_theme/) - Create a new theme
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_new_site.md b/content/en/commands/hugo_new_site.md
new file mode 100644
index 000000000..29ab9e8ef
--- /dev/null
+++ b/content/en/commands/hugo_new_site.md
@@ -0,0 +1,50 @@
+---
+date: 2019-07-31
+title: "hugo new site"
+slug: hugo_new_site
+url: /commands/hugo_new_site/
+---
+## hugo new site
+
+Create a new site (skeleton)
+
+### Synopsis
+
+Create a new site in the provided directory.
+The new site will have the correct structure, but no content or theme yet.
+Use `hugo new [contentPath]` to create new content.
+
+```
+hugo new site [path] [flags]
+```
+
+### Options
+
+```
+ --force init inside non-empty directory
+ -f, --format string config & frontmatter format (default "toml")
+ -h, --help help for site
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo new](/commands/hugo_new/) - Create new content for your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_new_theme.md b/content/en/commands/hugo_new_theme.md
new file mode 100644
index 000000000..8a97aad97
--- /dev/null
+++ b/content/en/commands/hugo_new_theme.md
@@ -0,0 +1,49 @@
+---
+date: 2019-07-31
+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 (skeleton) called [name] in the current directory.
+New theme is a skeleton. Please add content to the touched files. Add your
+name to the copyright line in the license and adjust the theme.toml file
+as you see fit.
+
+```
+hugo new theme [name] [flags]
+```
+
+### Options
+
+```
+ -h, --help help for theme
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo new](/commands/hugo_new/) - Create new content for your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_server.md b/content/en/commands/hugo_server.md
new file mode 100644
index 000000000..7d64bdc33
--- /dev/null
+++ b/content/en/commands/hugo_server.md
@@ -0,0 +1,93 @@
+---
+date: 2019-07-31
+title: "hugo server"
+slug: hugo_server
+url: /commands/hugo_server/
+---
+## hugo server
+
+A high performance webserver
+
+### Synopsis
+
+Hugo provides its own webserver which builds and serves the site.
+While hugo server is high performance, it is a webserver with limited options.
+Many run it in production, but the standard behavior is for people to use it
+in development and use a more full featured server such as Nginx or Caddy.
+
+'hugo server' will avoid writing the rendered and served content to disk,
+preferring to store it in memory.
+
+By default hugo will also watch your files for any changes you make and
+automatically rebuild the site. It will then live reload any open browser pages
+and push the latest content to them. As most Hugo sites are built in a fraction
+of a second, you will be able to save and see your changes nearly instantly.
+
+```
+hugo server [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. Defaults: $TMPDIR/hugo_cache/
+ --cleanDestinationDir remove files from destination not found in static directories
+ -c, --contentDir string filesystem path to content directory
+ -d, --destination string filesystem path to write files to
+ --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 and author 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
+ --i18n-warnings print missing translations
+ --ignoreCache ignores the cache directory
+ -l, --layoutDir string filesystem path to layout directory
+ --liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
+ --meminterval string interval to poll memory usage (requires --memstats), valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". (default "100ms")
+ --memstats string log memory usage to this file
+ --minify minify any supported output format (HTML, XML etc.)
+ --navigateToChanged navigate to changed content file on live browser reload
+ --noChmod don't sync permission mode of files
+ --noHTTPCache prevent HTTP caching
+ --noTimes don't sync modification time of files
+ --path-warnings print warnings on duplicate target paths etc.
+ -p, --port int port on which the server will listen (default 1313)
+ --renderToDisk render to Destination path (default is render to memory & serve from there)
+ --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/)
+ --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
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/commands/hugo_version.md b/content/en/commands/hugo_version.md
new file mode 100644
index 000000000..c0bc9359b
--- /dev/null
+++ b/content/en/commands/hugo_version.md
@@ -0,0 +1,46 @@
+---
+date: 2019-07-31
+title: "hugo version"
+slug: hugo_version
+url: /commands/hugo_version/
+---
+## hugo version
+
+Print the version number of Hugo
+
+### Synopsis
+
+All software has versions. This is Hugo's.
+
+```
+hugo version [flags]
+```
+
+### Options
+
+```
+ -h, --help help for version
+```
+
+### Options inherited from parent commands
+
+```
+ --config string config file (default is path/config.yaml|json|toml)
+ --configDir string config dir (default "config")
+ --debug debug output
+ -e, --environment string build environment
+ --ignoreVendor ignores any _vendor directory
+ --log enable Logging
+ --logFile string log File path (if set, logging enabled automatically)
+ --quiet build in quiet mode
+ -s, --source string filesystem path to read files relative from
+ --themesDir string filesystem path to themes directory
+ -v, --verbose verbose output
+ --verboseLog verbose logging
+```
+
+### SEE ALSO
+
+* [hugo](/commands/hugo/) - hugo builds your site
+
+###### Auto generated by spf13/cobra on 31-Jul-2019
diff --git a/content/en/content-management/_index.md b/content/en/content-management/_index.md
new file mode 100644
index 000000000..28f2ecf82
--- /dev/null
+++ b/content/en/content-management/_index.md
@@ -0,0 +1,20 @@
+---
+title: Content Management
+linktitle: Content Management Overview
+description: Hugo makes managing large static sites easy with support for archetypes, content types, menus, cross references, summaries, and more.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+menu:
+ docs:
+ parent: "content-management"
+ weight: 1
+keywords: [source, organization]
+categories: [content management]
+weight: 01 #rem
+draft: false
+aliases: [/content/,/content/organization]
+toc: false
+---
+
+A static site generator needs to extend beyond front matter and a couple of templates to be both scalable and *manageable*. Hugo was designed with not only developers in mind, but also content managers and authors.
diff --git a/content/en/content-management/archetypes.md b/content/en/content-management/archetypes.md
new file mode 100644
index 000000000..354ef0fef
--- /dev/null
+++ b/content/en/content-management/archetypes.md
@@ -0,0 +1,97 @@
+---
+title: Archetypes
+linktitle: Archetypes
+description: Archetypes are templates used when creating new content.
+date: 2017-02-01
+publishdate: 2017-02-01
+keywords: [archetypes,generators,metadata,front matter]
+categories: ["content management"]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 70
+ quicklinks:
+weight: 70 #rem
+draft: false
+aliases: [/content/archetypes/]
+toc: true
+---
+
+## What are Archetypes?
+
+**Archetypes** are content template files in the [archetypes directory][] of your project that contain preconfigured [front matter][] and possibly also a content disposition for your website's [content types][]. These will be used when you run `hugo new`.
+
+
+The `hugo new` uses the `content-section` to find the most suitable archetype template in your project. If your project does not contain any archetype files, it will also look in the theme.
+
+{{< code file="archetype-example.sh" >}}
+hugo new posts/my-first-post.md
+{{< /code >}}
+
+The above will create a new content file in `content/posts/my-first-post.md` using the first archetype file found of these:
+
+1. `archetypes/posts.md`
+2. `archetypes/default.md`
+3. `themes/my-theme/archetypes/posts.md`
+4. `themes/my-theme/archetypes/default.md`
+
+The last two list items are only applicable if you use a theme and it uses the `my-theme` theme name as an example.
+
+## Create a New Archetype Template
+
+A fictional example for the section `newsletter` and the archetype file `archetypes/newsletter.md`. Create a new file in `archetypes/newsletter.md` and open it in a text editor.
+
+{{< code file="archetypes/newsletter.md" >}}
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: true
+---
+
+**Insert Lead paragraph here.**
+
+## New Cool Posts
+
+{{ range first 10 ( where .Site.RegularPages "Type" "cool" ) }}
+* {{ .Title }}
+{{ end }}
+{{< /code >}}
+
+When you create a new newsletter with:
+
+```bash
+hugo new newsletter/the-latest-cool.stuff.md
+```
+
+It will create a new newsletter type of content file based on the archetype template.
+
+**Note:** the site will only be built if the `.Site` is in use in the archetype file, and this can be time consuming for big sites.
+
+The above _newsletter type archetype_ illustrates the possibilities: The full Hugo `.Site` and all of Hugo's template funcs can be used in the archetype file.
+
+
+## Directory based archetypes
+
+Since Hugo `0.49` you can use complete directories as archetype templates. Given this archetype directory:
+
+```bash
+archetypes
+├── default.md
+└── post-bundle
+ ├── bio.md
+ ├── images
+ │ └── featured.jpg
+ └── index.md
+```
+
+```bash
+hugo new --kind post-bundle posts/my-post
+```
+
+Will create a new folder in `/content/posts/my-post` with the same set of files as in the `post-bundle` archetypes folder. All content files (`index.md` etc.) can contain template logic, and will receive the correct `.Site` for the content's language.
+
+
+
+[archetypes directory]: /getting-started/directory-structure/
+[content types]: /content-management/types/
+[front matter]: /content-management/front-matter/
diff --git a/content/en/content-management/authors.md b/content/en/content-management/authors.md
new file mode 100644
index 000000000..4cec5281a
--- /dev/null
+++ b/content/en/content-management/authors.md
@@ -0,0 +1,184 @@
+---
+title: Authors
+linktitle: Authors
+description:
+date: 2016-08-22
+publishdate: 2017-03-12
+lastmod: 2017-03-12
+keywords: [authors]
+categories: ["content management"]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 55
+weight: 55 #rem
+draft: true
+aliases: [/content/archetypes/]
+toc: true
+comments: Before this page is published, need to also update both site- and page-level variables documentation.
+---
+
+
+
+Larger sites often have multiple content authors. Hugo provides standardized author profiles to organize relationships between content and content creators for sites operating under a distributed authorship model.
+
+## Author Profiles
+
+You can create a profile containing metadata for each author on your website. These profiles have to be saved under `data/_authors/`. The filename of the profile will later be used as an identifier. This way Hugo can associate content with one or multiple authors. An author's profile can be defined in the JSON, YAML, or TOML format.
+
+### Example: Author Profile
+
+Let's suppose Alice Allison is a blogger. A simple unique identifier would be `alice`. Now, we have to create a file called `alice.toml` in the `data/_authors/` directory. The following example is the standardized template written in TOML:
+
+{{< code file="data/_authors/alice.toml" >}}
+givenName = "Alice" # or firstName as alias
+familyName = "Allison" # or lastName as alias
+displayName = "Alice Allison"
+thumbnail = "static/authors/alice-thumb.jpg"
+image = "static/authors/alice-full.jpg"
+shortBio = "My name is Alice and I'm a blogger."
+bio = "My name is Alice and I'm a blogger... some other stuff"
+email = "alice.allison@email.com"
+weight = 10
+
+[social]
+ facebook = "alice.allison"
+ twitter = "alice"
+ website = "www.example.com"
+
+[params]
+ random = "whatever you want"
+{{< /code >}}
+
+All variables are optional but it's advised to fill all important ones (e.g. names and biography) because themes can vary in their usage.
+
+You can store files for the `thumbnail` and `image` attributes in the `static` folder. Then add the path to the photos relative to `static`; e.g., `/static/path/to/thumbnail.jpg`.
+
+`weight` allows you to define the order of an author in an `.Authors` list and can be accessed on list or via the `.Site.Authors` variable.
+
+The `social` section contains all the links to the social network accounts of an author. Hugo is able to generate the account links for the most popular social networks automatically. This way, you only have to enter your username. You can find a list of all supported social networks [here](#linking-social-network-accounts-automatically). All other variables, like `website` in the example above remain untouched.
+
+The `params` section can contain arbitrary data much like the same-named section in the config file. What it contains is up to you.
+
+## Associate Content Through Identifiers
+
+Earlier it was mentioned that content can be associated with an author through their corresponding identifier. In our case, blogger Alice has the identifier `alice`. In the front matter of a content file, you can create a list of identifiers and assign it to the `authors` variable. Here are examples for `alice` using YAML and TOML, respectively.
+
+```
+---
+title: Why Hugo is so Awesome
+date: 2016-08-22T14:27:502:00
+authors: ["alice"]
+---
+
+Nothing to read here. Move along...
+```
+
+```
++++
+title = Why Hugo is so Awesome
+date = "2016-08-22T14:27:502:00"
+authors: ["alice"]
++++
+
+Nothing to read here. Move along...
+```
+
+Future authors who might work on this blog post can append their identifiers to the `authors` array in the front matter as well.
+
+## Work with Templates
+
+After a successful setup it's time to give some credit to the authors by showing them on the website. Within the templates Hugo provides a list of the author's profiles if they are listed in the `authors` variable within the front matter.
+
+The list is accessible via the `.Authors` template variable. Printing all authors of a the blog post is straight forward:
+
+```
+{{ range .Authors }}
+ {{ .DisplayName }}
+{{ end }}
+=> Alice Allison
+```
+
+Even if there are co-authors you may only want to show the main author. For this case you can use the `.Author` template variable **(note the singular form)**. The template variable contains the profile of the author that is first listed with his identifier in the front matter.
+
+{{% note %}}
+You can find a list of all template variables to access the profile information in [Author Variables](/variables/authors/).
+{{% /note %}}
+
+### Link Social Network Accounts
+
+As aforementioned, Hugo is able to generate links to profiles of the most popular social networks. The following social networks with their corrersponding identifiers are supported: `github`, `facebook`, `twitter`, `pinterest`, `instagram`, `youtube` and `linkedin`.
+
+This is can be done with the `.Social.URL` function. Its only parameter is the name of the social network as they are defined in the profile (e.g. `facebook`, `twitter`). Custom variables like `website` remain as they are.
+
+Most articles feature a small section with information about the author at the end. Let's create one containing the author's name, a thumbnail, a (summarized) biography and links to all social networks:
+
+{{< code file="layouts/partials/author-info.html" download="author-info.html" >}}
+{{ with .Author }}
+
+{{ end }}
+{{< /code >}}
+
+## Who Published What?
+
+That question can be answered with a list of all authors and another list containing all articles that they each have written. Now we have to translate this idea into templates. The [taxonomy][] feature allows us to logically group content based on information that they have in common; e.g. a tag or a category. Well, many articles share the same author, so this should sound familiar, right?
+
+In order to let Hugo know that we want to group content based on their author, we have to create a new taxonomy called `author` (the name corresponds to the variable in the front matter). Here is the snippet in a `config.yaml` and `config.toml`, respectively:
+
+```
+taxonomies:
+ author: authors
+```
+
+```
+[taxonomies]
+ author = "authors"
+```
+
+
+### List All Authors
+
+In the next step we can create a template to list all authors of your website. Later, the list can be accessed at `www.example.com/authors/`. Create a new template in the `layouts/taxonomy/` directory called `authors.term.html`. This template will be exclusively used for this taxonomy.
+
+{{< code file="layouts/taxonomy/author.term.html" download="author.term.html" >}}
+
+{{< /code >}}
+
+`.Data.Terms` contains the identifiers of all authors and we can range over it to create a list with all author names. The `$profile` variable gives us access to the profile of the current author. This allows you to generate a nice info box with a thumbnail, a biography and social media links, like at the [end of a blog post](#linking-social-network-accounts-automatically).
+
+### List Each Author's Publications
+
+Last but not least, we have to create the second list that contains all publications of an author. Each list will be shown in its own page and can be accessed at `www.example.com/authors/`. Replace `` with a valid author identifier like `alice`.
+
+The layout for this page can be defined in the template `layouts/taxonomy/author.html`.
+
+{{< code file="layouts/taxonomy/author.html" download="author.html" >}}
+{{ range .Pages }}
+
+ written by {{ .Author.DisplayName }}
+ {{ .Summary }}
+{{ end }}
+{{< /code >}}
+
+The example above generates a simple list of all posts written by a single author. Inside the loop you've access to the complete set of [page variables][pagevars]. Therefore, you can add additional information about the current posts like the publishing date or the tags.
+
+With a lot of content this list can quickly become very long. Consider to use the [pagination][] feature. It splits the list into smaller chunks and spreads them over multiple pages.
+
+[pagevars]: /variables/page/
+[pagination]: /templates/pagination/
diff --git a/content/en/content-management/comments.md b/content/en/content-management/comments.md
new file mode 100644
index 000000000..47b3ccf16
--- /dev/null
+++ b/content/en/content-management/comments.md
@@ -0,0 +1,88 @@
+---
+title: Comments
+linktitle: Comments
+description: Hugo ships with an internal Disqus template, but this isn't the only commenting system that will work with your new Hugo website.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-03-09
+keywords: [sections,content,organization]
+categories: [project organization, fundamentals]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 140
+weight: 140 #rem
+draft: false
+aliases: [/extras/comments/]
+toc: true
+---
+
+Hugo ships with support for [Disqus](https://disqus.com/), 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 built-in Disqus partial][disquspartial].
+
+## 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][disqussetup].
+
+### Configure Disqus
+
+Disqus comments require you set a single value in your [site's configuration file][configuration] like so:
+
+{{< code-toggle copy="false" >}}
+disqusShortname = "yourdiscussshortname"
+{{ code-toggle >}}
+
+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:
+
+* `disqus_identifier`
+* `disqus_title`
+* `disqus_url`
+
+### Render Hugo's Built-in Disqus Partial Template
+
+Disqus has its own [internal template](https://gohugo.io/templates/internal/#disqus) available, to render it add the following code where you want comments to appear:
+
+```
+{{ template "_internal/disqus.html" . }}
+```
+
+## Comments Alternatives
+
+There are a few alternatives to commenting on static sites for those who do not want to use Disqus:
+
+* [Static Man](https://staticman.net/)
+* [Talkyard](https://www.talkyard.io/blog-comments) (Open source, & serverless hosting)
+* [txtpen](https://txtpen.github.io/hn/)
+* [IntenseDebate](https://intensedebate.com/)
+* [Graph Comment][]
+* [Muut](https://muut.com/)
+* [isso](https://posativ.org/isso/) (Self-hosted, Python)
+ * [Tutorial on Implementing Isso with Hugo][issotutorial]
+* [Utterances](https://utteranc.es/) (Open source, Github comments widget built on Github issues)
+* [Remark](https://github.com/umputun/remark) (Open source, Golang, Easy to run docker)
+* [Commento](https://commento.io/) (Open Source, available as a service, local install, or docker image)
+* [JustComments](https://just-comments.com) (Open Source, available as a service, can be self-hosted)
+
+
+
+
+
+
+[configuration]: /getting-started/configuration/
+[disquspartial]: /templates/partials/#disqus
+[disqussetup]: https://disqus.com/profile/signup/
+[forum]: https://discourse.gohugo.io
+[front matter]: /content-management/front-matter/
+[Graph Comment]: https://graphcomment.com/
+[kaijuissue]: https://github.com/spf13/kaiju/issues/new
+[issotutorial]: https://stiobhart.net/2017-02-24-isso-comments/
+[partials]: /templates/partials/
+[MongoDB]: https://www.mongodb.com/
+[tweet]: https://twitter.com/spf13
diff --git a/content/en/content-management/cross-references.md b/content/en/content-management/cross-references.md
new file mode 100644
index 000000000..21a73b861
--- /dev/null
+++ b/content/en/content-management/cross-references.md
@@ -0,0 +1,100 @@
+---
+title: Links and Cross References
+description: Shortcodes for creating links to documents.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-03-31
+categories: [content management]
+keywords: ["cross references","references", "anchors", "urls"]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 100
+weight: 100 #rem
+aliases: [/extras/crossreferences/]
+toc: true
+---
+
+
+The `ref` and `relref` shortcode resolves the absolute or relative permalink given a path to a document.
+
+## Use `ref` and `relref`
+
+```go-html-template
+{{* ref "document.md" */>}}
+{{* ref "#anchor" */>}}
+{{* ref "document.md#anchor" */>}}
+{{* ref "/blog/my-post" */>}}
+{{* ref "/blog/my-post.md" */>}}
+{{* relref "document.md" */>}}
+{{* relref "#anchor" */>}}
+{{* relref "document.md#anchor" */>}}
+```
+
+The single parameter to `ref` is a string with a content `documentname` (e.g., `about.md`) with or without an appended in-document `anchor` (`#who`) without spaces. Hugo is flexible in how we search for documents, so the file suffix may be omitted.
+
+**Paths without a leading `/` will first be tried resolved relative to the current page.**
+
+You will get an error if your document could not be uniquely resolved. The error behaviour can be configured, see below.
+
+### Link to another language version
+
+Link to another language version of a document, you need to use this syntax:
+
+```go-html-template
+{{* relref path="document.md" lang="ja" */>}}
+```
+
+### Get another Output Format
+
+To link to a given Output Format of a document, you can use this syntax:
+
+```go-html-template
+{{* relref path="document.md" outputFormat="rss" */>}}
+```
+
+### Anchors
+
+When an `anchor` is provided by itself, the current page’s unique identifier will be appended; when an `anchor` is provided appended to `documentname`, the found page's unique identifier will be appended:
+
+```go-html-template
+{{* relref "#anchors" */>}} => #anchors:9decaf7
+```
+
+The above examples render as follows for this very page as well as a reference to the "Content" heading in the Hugo docs features pageyoursite
+
+```go-html-template
+{{* relref "#who" */>}} => #who:9decaf7
+{{* relref "/blog/post.md#who" */>}} => /blog/post/#who:badcafe
+```
+
+More information about document unique identifiers and headings can be found [below]({{< ref "#hugo-heading-anchors" >}}).
+
+## Hugo Heading Anchors
+
+When using Markdown document types, Hugo generates heading anchors automatically. The generated anchor for this section is `hugo-heading-anchors`. Because the heading anchors are generated automatically, Hugo takes some effort to ensure that heading anchors are unique both inside a document and across the entire site.
+
+Ensuring heading uniqueness across the site is accomplished with a unique identifier for each document based on its path. Unless a document is renamed or moved between sections *in the filesystem*, the unique identifier for the document will not change: `blog/post.md` will always have a unique identifier of `81df004c333b392d34a49fd3a91ba720`.
+
+`ref` and `relref` were added so you can make these reference links without having to know the document’s unique identifier. (The links in document tables of contents are automatically up-to-date with this value.)
+
+```
+{{* relref "content-management/cross-references.md#hugo-heading-anchors" */>}}
+/content-management/cross-references/#hugo-heading-anchors:77cd9ea530577debf4ce0f28c8dca242
+```
+
+## Ref and RelRef Configuration
+
+The behaviour can, since Hugo 0.45, be configured in `config.toml`:
+
+refLinksErrorLevel ("ERROR")
+: When using `ref` or `relref` to resolve page links and a link cannot resolved, it will be logged with this log level. Valid values are `ERROR` (default) or `WARNING`. Any `ERROR` will fail the build (`exit -1`).
+
+refLinksNotFoundURL
+: URL to be used as a placeholder when a page reference cannot be found in `ref` or `relref`. Is used as-is.
+
+
+[lists]: /templates/lists/
+[output formats]: /templates/output-formats/
+[shortcode]: /content-management/shortcodes/
+[bfext]: /content-management/formats/#blackfriday-extensions
diff --git a/content/en/content-management/formats.md b/content/en/content-management/formats.md
new file mode 100644
index 000000000..a0ed992f2
--- /dev/null
+++ b/content/en/content-management/formats.md
@@ -0,0 +1,248 @@
+---
+title: Supported Content Formats
+linktitle: Supported Content Formats
+description: Both HTML and Markdown are supported content formats.
+date: 2017-01-10
+publishdate: 2017-01-10
+lastmod: 2017-04-06
+categories: [content management]
+keywords: [markdown,asciidoc,mmark,pandoc,content format]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 20
+weight: 20 #rem
+draft: false
+aliases: [/content/markdown-extras/,/content/supported-formats/,/doc/supported-formats/,/tutorials/mathjax/]
+toc: true
+---
+
+**Markdown is the main content format** and comes in two flavours: The excellent [Blackfriday project][blackfriday] (name your files `*.md` or set `markup = "markdown"` in front matter) or its fork [Mmark][mmark] (name your files `*.mmark` or set `markup = "mmark"` in front matter), both very fast markdown engines written in Go.
+
+For Emacs users, [go-org](https://github.com/niklasfasching/go-org) provides built-in native support for Org-mode (name your files `*.org` or set `markup = "org"` in front matter)
+
+But in many situations, plain HTML is what you want. Just name your files with `.html` or `.htm` extension inside your content folder. Note that if you want your HTML files to have a layout, they need front matter. It can be empty, but it has to be there:
+
+```html
+---
+title: "This is a content file in HTML"
+---
+
+
+
Hello, Hugo!
+
+```
+
+{{% note "Deeply Nested Lists" %}}
+Before you begin writing your content in markdown, Blackfriday has a known issue [(#329)](https://github.com/russross/blackfriday/issues/329) with handling deeply nested lists. Luckily, there is an easy workaround. Use 4-spaces (i.e., tab) rather than 2-space indentations.
+{{% /note %}}
+
+## Configure BlackFriday Markdown Rendering
+
+You can configure multiple aspects of Blackfriday as shown in the following list. See the docs on [Configuration][config] for the full list of explicit directions you can give to Hugo when rendering your site.
+
+{{< readfile file="/content/en/readfiles/bfconfig.md" markdown="true" >}}
+
+## Extend Markdown
+
+Hugo provides some convenient methods for extending markdown.
+
+### Task Lists
+
+Hugo supports [GitHub-styled task lists (i.e., TODO lists)][gfmtasks] for the Blackfriday markdown renderer. If you do not want to use this feature, you can disable it in your configuration.
+
+#### Example Task List Input
+
+{{< code file="content/my-to-do-list.md" >}}
+- [ ] a task list item
+- [ ] list syntax required
+- [ ] incomplete
+- [x] completed
+{{< /code >}}
+
+#### Example Task List Output
+
+The preceding markdown produces the following HTML in your rendered website:
+
+```
+
+
a task list item
+
list syntax required
+
incomplete
+
completed
+
+```
+
+#### Example Task List Display
+
+The following shows how the example task list will look to the end users of your website. Note that visual styling of lists is up to you. This list has been styled according to [the Hugo Docs stylesheet][hugocss].
+
+- [ ] a task list item
+- [ ] list syntax required
+- [ ] incomplete
+- [x] completed
+
+### Emojis
+
+To add emojis directly to content, set `enableEmoji` to `true` in your [site configuration][config]. To use emojis in templates or shortcodes, see [`emojify` function][].
+
+For a full list of emojis, see the [Emoji cheat sheet][emojis].
+
+### Shortcodes
+
+If you write in Markdown and find yourself frequently embedding your content with raw HTML, Hugo provides built-in shortcodes functionality. This is one of the most powerful features in Hugo and allows you to create your own Markdown extensions very quickly.
+
+See [Shortcodes][sc] for usage, particularly for the built-in shortcodes that ship with Hugo, and [Shortcode Templating][sct] to learn how to build your own.
+
+### Code Blocks
+
+Hugo supports GitHub-flavored markdown's use of triple back ticks, as well as provides a special [`highlight` shortcode][hlsc], and syntax highlights those code blocks natively using *Chroma*. Users also have an option to use *Pygments* instead. See the [Syntax Highlighting][hl] section for details.
+
+## Mmark
+
+Mmark is a [fork of BlackFriday][mmark] and markdown superset that is well suited for writing [IETF documentation][ietf]. You can see examples of the syntax in the [Mmark GitHub repository][mmark] or the full syntax on [Miek Gieben's website][].
+
+### Use Mmark
+
+As Hugo ships with Mmark, using the syntax is as easy as changing the extension of your content files from `.md` to `.mmark`.
+
+In the event that you want to only use Mmark in specific files, you can also define the Mmark syntax in your content's front matter:
+
+```
+---
+title: My Post
+date: 2017-04-01
+markup: mmark
+---
+```
+
+{{% warning %}}
+Thare are some features not available in Mmark; one example being that shortcodes are not translated when used in an included `.mmark` file ([#3131](https://github.com/gohugoio/hugo/issues/3137)), and `EXTENSION_ABBREVIATION` ([#1970](https://github.com/gohugoio/hugo/issues/1970)) and the aforementioned GFM todo lists ([#2270](https://github.com/gohugoio/hugo/issues/2270)) are not fully supported. Contributions are welcome.
+{{% /warning %}}
+
+## MathJax with Hugo
+
+[MathJax](https://www.mathjax.org/) is a JavaScript library that allows the display of mathematical expressions described via a LaTeX-style syntax in the HTML (or Markdown) source of a web page. As it is a pure a JavaScript library, getting it to work within Hugo is fairly straightforward, but does have some oddities that will be discussed here.
+
+This is not an introduction into actually using MathJax to render typeset mathematics on your website. Instead, this page is a collection of tips and hints for one way to get MathJax working on a website built with Hugo.
+
+### Enable MathJax
+
+The first step is to enable MathJax on pages that you would like to have typeset math. There are multiple ways to do this (adventurous readers can consult the [Loading and Configuring](https://docs.mathjax.org/en/latest/web/configuration.html) section of the MathJax documentation for additional methods of including MathJax), but the easiest way is to use the secure MathJax CDN by include a `
+{{< /code >}}
+
+One way to ensure that this code is included in all pages is to put it in one of the templates that live in the `layouts/partials/` directory. For example, I have included this in the bottom of my template `footer.html` because I know that the footer will be included in every page of my website.
+
+### Options and Features
+
+MathJax is a stable open-source library with many features. I encourage the interested reader to view the [MathJax Documentation](https://docs.mathjax.org/en/latest/index.html), specifically the sections on [Basic Usage](http://docs.mathjax.org/en/latest/index.html#basic-usage) and [MathJax Configuration Options](http://docs.mathjax.org/en/latest/index.html#mathjax-configuration-options).
+
+### Issues with Markdown
+
+{{% note %}}
+The following issues with Markdown assume you are using `.md` for content and BlackFriday for parsing. Using [Mmark](#mmark) as your content format will obviate the need for the following workarounds.
+
+When using Mmark with MathJax, use `displayMath: [['$$','$$'], ['\\[','\\]']]`. See the [Mmark `README.md`](https://github.com/miekg/mmark/wiki/Syntax#math-blocks) for more information. In addition to MathJax, Mmark has been shown to work well with [KaTeX](https://github.com/Khan/KaTeX). See this [related blog post from a Hugo user](http://nosubstance.me/post/a-great-toolset-for-static-blogging/).
+{{% /note %}}
+
+After enabling MathJax, any math entered between proper markers (see the [MathJax documentation][mathjaxdocs]) will be processed and typeset in the web page. One issue that comes up, however, with Markdown is that the underscore character (`_`) is interpreted by Markdown as a way to wrap text in `emph` blocks while LaTeX (MathJax) interprets the underscore as a way to create a subscript. This "double speak" of the underscore can result in some unexpected and unwanted behavior.
+
+### Solution
+
+There are multiple ways to remedy this problem. One solution is to simply escape each underscore in your math code by entering `\_` instead of `_`. This can become quite tedious if the equations you are entering are full of subscripts.
+
+Another option is to tell Markdown to treat the MathJax code as verbatim code and not process it. One way to do this is to wrap the math expression inside a `
` `
` block. Markdown would ignore these sections and they would get passed directly on to MathJax and processed correctly. This works great for display style mathematics, but for inline math expressions the line break induced by the `
` is not acceptable. The syntax for instructing Markdown to treat inline text as verbatim is by wrapping it in backticks (`` ` ``). You might have noticed, however, that the text included in between backticks is rendered differently than standard text (on this site these are items highlighted in red). To get around this problem, we could create a new CSS entry that would apply standard styling to all inline verbatim text that includes MathJax code. Below I will show the HTML and CSS source that would accomplish this (note this solution was adapted from [this blog post](http://doswa.com/2011/07/20/mathjax-in-markdown.html)---all credit goes to the original author).
+
+{{< code file="mathjax-markdown-solution.html" >}}
+
+{{< /code >}}
+
+As before, this content should be included in the HTML source of each page that will be using MathJax. The next code snippet contains the CSS that is used to have verbatim MathJax blocks render with the same font style as the body of the page.
+
+{{< code file="mathjax-style.css" >}}
+code.has-jax {
+ font: inherit;
+ font-size: 100%;
+ background: inherit;
+ border: inherit;
+ color: #515151;
+}
+{{< /code >}}
+
+In the CSS snippet, notice the line `color: #515151;`. `#515151` is the value assigned to the `color` attribute of the `body` class in my CSS. In order for the equations to fit in with the body of a web page, this value should be the same as the color of the body.
+
+### Usage
+
+With this setup, everything is in place for a natural usage of MathJax on pages generated using Hugo. In order to include inline mathematics, just put LaTeX code in between `` `$ TeX Code $` `` or `` `\( TeX Code \)` ``. To include display style mathematics, just put LaTeX code in between `
$$TeX Code$$
`. All the math will be properly typeset and displayed within your Hugo generated web page!
+
+## Additional Formats Through External Helpers
+
+Hugo has a new concept called _external helpers_. It means that you can write your content using [Asciidoc][ascii], [reStructuredText][rest], or [pandoc]. If you have files with associated extensions, Hugo will call external commands to generate the content. ([See the Hugo source code for external helpers][helperssource].)
+
+For example, for Asciidoc files, Hugo will try to call the `asciidoctor` or `asciidoc` command. This means that you will have to install the associated tool on your machine to be able to use these formats. ([See the Asciidoctor docs for installation instructions](https://asciidoctor.org/docs/install-toolchain/)).
+
+To use these formats, just use the standard extension and the front matter exactly as you would do with natively supported `.md` files.
+
+Hugo passes reasonable default arguments to these external helpers by default:
+
+- `asciidoc`: `--no-header-footer --safe -`
+- `asciidoctor`: `--no-header-footer --safe --trace -`
+- `rst2html`: `--leave-comments --initial-header-level=2`
+- `pandoc`: `--mathjax`
+
+{{% warning "Performance of External Helpers" %}}
+Because additional formats are external commands generation performance will rely heavily on the performance of the external tool you are using. As this feature is still in its infancy, feedback is welcome.
+{{% /warning %}}
+
+## Learn Markdown
+
+Markdown syntax is simple enough to learn in a single sitting. The following are excellent resources to get you up and running:
+
+* [Daring Fireball: Markdown, John Gruber (Creator of Markdown)][fireball]
+* [Markdown Cheatsheet, Adam Pritchard][mdcheatsheet]
+* [Markdown Tutorial (Interactive), Garen Torikian][mdtutorial]
+* [The Markdown Guide, Matt Cone][mdguide]
+
+[`emojify` function]: /functions/emojify/
+[ascii]: https://asciidoctor.org/
+[bfconfig]: /getting-started/configuration/#configuring-blackfriday-rendering
+[blackfriday]: https://github.com/russross/blackfriday
+[mmark]: https://github.com/miekg/mmark
+[config]: /getting-started/configuration/
+[developer tools]: /tools/
+[emojis]: https://www.webpagefx.com/tools/emoji-cheat-sheet/
+[fireball]: https://daringfireball.net/projects/markdown/
+[gfmtasks]: https://guides.github.com/features/mastering-markdown/#syntax
+[helperssource]: https://github.com/gohugoio/hugo/blob/77c60a3440806067109347d04eb5368b65ea0fe8/helpers/general.go#L65
+[hl]: /content-management/syntax-highlighting/
+[hlsc]: /content-management/shortcodes/#highlight
+[hugocss]: /css/style.css
+[ietf]: https://tools.ietf.org/html/
+[mathjaxdocs]: https://docs.mathjax.org/en/latest/
+[mdcheatsheet]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
+[mdguide]: https://www.markdownguide.org/
+[mdtutorial]: https://www.markdowntutorial.com/
+[Miek Gieben's website]: https://miek.nl/2016/march/05/mmark-syntax-document/
+[mmark]: https://github.com/mmarkdown/mmark
+[org]: https://orgmode.org/
+[pandoc]: https://www.pandoc.org/
+[Pygments]: http://pygments.org/
+[rest]: http://docutils.sourceforge.net/rst.html
+[sc]: /content-management/shortcodes/
+[sct]: /templates/shortcode-templates/
diff --git a/content/en/content-management/front-matter.md b/content/en/content-management/front-matter.md
new file mode 100644
index 000000000..0e3baf437
--- /dev/null
+++ b/content/en/content-management/front-matter.md
@@ -0,0 +1,210 @@
+---
+title: Front Matter
+linktitle:
+description: Hugo allows you to add front matter in yaml, toml, or json to your content files.
+date: 2017-01-09
+publishdate: 2017-01-09
+lastmod: 2017-02-24
+categories: [content management]
+keywords: ["front matter", "yaml", "toml", "json", "metadata", "archetypes"]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 30
+weight: 30 #rem
+draft: false
+aliases: [/content/front-matter/]
+toc: true
+---
+
+**Front matter** allows you to keep metadata attached to an instance of a [content type][]---i.e., embedded inside a content file---and is one of the many features that gives Hugo its strength.
+
+{{< youtube Yh2xKRJGff4 >}}
+
+## Front Matter Formats
+
+Hugo supports four formats for front matter, each with their own identifying tokens.
+
+TOML
+: identified by opening and closing `+++`.
+
+YAML
+: identified by opening and closing `---`.
+
+JSON
+: a single JSON object surrounded by '`{`' and '`}`', followed by a new line.
+
+ORG
+: a group of Org mode keywords in the format '`#+KEY: VALUE`'. Any line that does not start with `#+` ends the front matter section.
+ Keyword values can be either strings (`#+KEY: VALUE`) or a whitespace separated list of strings (`#+KEY[]: VALUE_1 VALUE_2`).
+
+### Example
+
+{{< code-toggle >}}
+title = "spf13-vim 3.0 release and new website"
+description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
+tags = [ ".vimrc", "plugins", "spf13-vim", "vim" ]
+date = "2012-04-06"
+categories = [
+ "Development",
+ "VIM"
+]
+slug = "spf13-vim-3-0-release-and-new-website"
+{{< /code-toggle >}}
+
+## Front Matter Variables
+
+### Predefined
+
+There are a few predefined variables that Hugo is aware of. See [Page Variables][pagevars] for how to call many of these predefined variables in your templates.
+
+aliases
+: an array of one or more aliases (e.g., old published paths of renamed content) that will be created in the output directory structure . See [Aliases][aliases] for details.
+
+audio
+: an array of paths to audio files related to the page; used by the `opengraph` [internal template](/templates/internal) to populate `og:audio`.
+
+cascade
+: a map of Front Matter keys whose values are passed down to the page's descendents unless overwritten by self or a closer ancestor's cascade. See [Front Matter Cascade](#front-matter-cascade) for details.
+
+date
+: the datetime assigned to this page. This is usually fetched from the `date` field in front matter, but this behaviour is configurable.
+
+description
+: the description for the content.
+
+draft
+: if `true`, the content will not be rendered unless the `--buildDrafts` flag is passed to the `hugo` command.
+
+expiryDate
+: the datetime at which the content should no longer be published by Hugo; expired content will not be rendered unless the `--buildExpired` flag is passed to the `hugo` command.
+
+headless
+: if `true`, sets a leaf bundle to be [headless][headless-bundle].
+
+images
+: an array of paths to images related to the page; used by [internal templates](/templates/internal) such as `_internal/twitter_cards.html`.
+
+isCJKLanguage
+: if `true`, Hugo will explicitly treat the content as a CJK language; both `.Summary` and `.WordCount` work properly in CJK languages.
+
+keywords
+: the meta keywords for the content.
+
+layout
+: the layout Hugo should select from the [lookup order][lookup] when rendering the content. If a `type` is not specified in the front matter, Hugo will look for the layout of the same name in the layout directory that corresponds with a content's section. See ["Defining a Content Type"][definetype]
+
+lastmod
+: the datetime at which the content was last modified.
+
+linkTitle
+: used for creating links to content; if set, Hugo defaults to using the `linktitle` before the `title`. Hugo can also [order lists of content by `linktitle`][bylinktitle].
+
+markup
+: **experimental**; specify `"rst"` for reStructuredText (requires`rst2html`) or `"md"` (default) for Markdown.
+
+outputs
+: allows you to specify output formats specific to the content. See [output formats][outputs].
+
+publishDate
+: if in the future, content will not be rendered unless the `--buildFuture` flag is passed to `hugo`.
+
+resources
+: used for configuring page bundle resources. See [Page Resources][page-resources].
+
+series
+: an array of series this page belongs to, as a subset of the `series` [taxonomy](/content-management/taxonomies/); used by the `opengraph` [internal template](/templates/internal) to populate `og:see_also`.
+
+slug
+: appears as the tail of the output URL. A value specified in front matter will override the segment of the URL based on the filename.
+
+summary
+: text used when providing a summary of the article in the `.Summary` page variable; details available in the [content-summaries](/content-management/summaries/) section.
+
+title
+: the title for the content.
+
+type
+: the type of the content; this value will be automatically derived from the directory (i.e., the [section][]) if not specified in front matter.
+
+url
+: the full path to the content from the web root. It makes no assumptions about the path of the content file. It also ignores any language prefixes of
+the multilingual feature.
+
+videos
+: an array of paths to videos related to the page; used by the `opengraph` [internal template](/templates/internal) to populate `og:video`.
+
+weight
+: used for [ordering your content in lists][ordering]. Lower weight gets higher precedence. So content with lower weight will come first.
+
+\
+: field name of the *plural* form of the index. See `tags` and `categories` in the above front matter examples. _Note that the plural form of user-defined taxonomies cannot be the same as any of the predefined front matter variables._
+
+{{% note "Hugo's Default URL Destinations" %}}
+If neither `slug` nor `url` is present and [permalinks are not configured otherwise in your site `config` file](/content-management/urls/#permalinks), Hugo will use the filename of your content to create the output URL. See [Content Organization](/content-management/organization) for an explanation of paths in Hugo and [URL Management](/content-management/urls/) for ways to customize Hugo's default behaviors.
+{{% /note %}}
+
+### User-Defined
+
+You can add fields to your front matter arbitrarily to meet your needs. These user-defined key-values are placed into a single `.Params` variable for use in your templates.
+
+The following fields can be accessed via `.Params.include_toc` and `.Params.show_comments`, respectively. The [Variables][] section provides more information on using Hugo's page- and site-level variables in your templates.
+
+{{< code-toggle copy="false" >}}
+include_toc: true
+show_comments: false
+{{ code-toggle >}}
+
+## Front Matter Cascade
+
+Any node or section can pass down to descendents a set of Front Matter values as long as defined underneath the reserved `cascade` Front Matter key.
+
+### Example
+```yaml
+# content/blog/_index.md
+title: Blog
+cascade:
+ banner: images/typewriter.jpg
+```
+
+With the above example the Blog section page and its descendents will return `images/typewriter.jpg` when `.Params.banner` is invoked unless:
+
+- Said descendent has its own `banner` value set
+- Or a closer ancestor node has its own `cascade.banner` value set.
+
+## Order Content Through Front Matter
+
+You can assign content-specific `weight` in the front matter of your content. These values are especially useful for [ordering][ordering] in list views. You can use `weight` for ordering of content and the convention of [`_weight`][taxweight] for ordering content within a taxonomy. See [Ordering and Grouping Hugo Lists][lists] to see how `weight` can be used to organize your content in list views.
+
+## Override Global Markdown Configuration
+
+It's possible to set some options for Markdown rendering in a content's front matter as an override to the [BlackFriday rendering options set in your project configuration][config].
+
+## Front Matter Format Specs
+
+* [TOML Spec][toml]
+* [YAML Spec][yaml]
+* [JSON Spec][json]
+
+[variables]: /variables/
+[aliases]: /content-management/urls/#aliases
+[archetype]: /content-management/archetypes/
+[bylinktitle]: /templates/lists/#by-link-title
+[config]: /getting-started/configuration/ "Hugo documentation for site configuration"
+[content type]: /content-management/types/
+[contentorg]: /content-management/organization/
+[definetype]: /content-management/types/#defining-a-content-type "Learn how to specify a type and a layout in a content's front matter"
+[headless-bundle]: /content-management/page-bundles/#headless-bundle
+[json]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf "Specification for JSON, JavaScript Object Notation"
+[lists]: /templates/lists/#ordering-content "See how to order content in list pages; for example, templates that look to specific _index.md for content and front matter."
+[lookup]: /templates/lookup-order/ "Hugo traverses your templates in a specific order when rendering content to allow for DRYer templating."
+[ordering]: /templates/lists/ "Hugo provides multiple ways to sort and order your content in list templates"
+[outputs]: /templates/output-formats/ "With the release of v22, you can output your content to any text format using Hugo's familiar templating"
+[page-resources]: /content-management/page-resources/
+[pagevars]: /variables/page/
+[section]: /content-management/sections/
+[taxweight]: /content-management/taxonomies/
+[toml]: https://github.com/toml-lang/toml "Specification for TOML, Tom's Obvious Minimal Language"
+[urls]: /content-management/urls/
+[variables]: /variables/
+[yaml]: https://yaml.org/spec/ "Specification for YAML, YAML Ain't Markup Language"
diff --git a/content/en/content-management/image-processing/index.md b/content/en/content-management/image-processing/index.md
new file mode 100644
index 000000000..63911b159
--- /dev/null
+++ b/content/en/content-management/image-processing/index.md
@@ -0,0 +1,292 @@
+---
+title: "Image Processing"
+description: "Image Page resources can be resized and cropped."
+date: 2018-01-24T13:10:00-05:00
+linktitle: "Image Processing"
+categories: ["content management"]
+keywords: [resources,images]
+weight: 4004
+draft: false
+toc: true
+menu:
+ docs:
+ parent: "content-management"
+ weight: 32
+---
+
+## The Image Page Resource
+
+The `image` is a [Page Resource]({{< relref "/content-management/page-resources" >}}), and the processing methods listed below does not work on images inside your `/static` folder.
+
+To get all images in a [Page Bundle]({{< relref "/content-management/organization#page-bundles" >}}):
+
+```go-html-template
+{{ with .Resources.ByType "image" }}
+{{ end }}
+
+```
+
+## Image Processing Methods
+
+
+The `image` resource implements the methods `Resize`, `Fit` and `Fill`, each returning the transformed image using the specified dimensions and processing options. The `image` resource also, since Hugo 0.58, implements the method `Exif` and `Filter`.
+
+### Resize
+
+Resizes the image to the specified width and height.
+
+```go
+// Resize to a width of 600px and preserve ratio
+{{ $image := $resource.Resize "600x" }}
+
+// Resize to a height of 400px and preserve ratio
+{{ $image := $resource.Resize "x400" }}
+
+// Resize to a width 600px and a height of 400px
+{{ $image := $resource.Resize "600x400" }}
+```
+
+### Fit
+Scale down the image to fit the given dimensions while maintaining aspect ratio. Both height and width are required.
+
+```go
+{{ $image := $resource.Fit "600x400" }}
+```
+
+### Fill
+Resize and crop the image to match the given dimensions. Both height and width are required.
+
+```go
+{{ $image := $resource.Fill "600x400" }}
+```
+
+### Filter
+
+Apply one or more filters to your image. See [Image Filters](/functions/images/#image-filters) for a full list.
+
+```go-html-template
+{{ $img = $img.Filter (images.GaussianBlur 6) (images.Pixelate 8) }}
+```
+
+The above can also be written in a more functional style using pipes:
+
+```go-html-template
+{{ $img = $img | images.Filter (images.GaussianBlur 6) (images.Pixelate 8) }}
+```
+
+The filters will be applied in the given order.
+
+Sometimes it can be useful to create the filter chain once and then reuse it:
+
+```go-html-template
+{{ $filters := slice (images.GaussianBlur 6) (images.Pixelate 8) }}
+{{ $img1 = $img1.Filter $filters }}
+{{ $img2 = $img2.Filter $filters }}
+```
+
+### Exif
+
+Provides an [Exif](https://en.wikipedia.org/wiki/Exif) object with metadata about the image.
+
+Note that this is only suported for JPG and TIFF images, so it's recommended to wrap the access with a `with`, e.g.:
+
+```go-html-template
+{{ with $img.Exif }}
+Date: {{ .Date }}
+Lat/Long: {{ .Lat}}/{{ .Long }}
+Tags:
+{{ range $k, $v := .Tags }}
+TAG: {{ $k }}: {{ $v }}
+{{ end }}
+```
+
+#### Exif fields
+
+Data
+: "photo taken" date/time
+
+Lat
+: "photo taken where", GPS latitude
+
+Long
+: "photo taken where", GPS longitude
+
+See [Image Processing Config](#image-processing-config) for how to configure what gets included in Exif.
+
+
+
+
+
+## Image Processing Options
+
+In addition to the dimensions (e.g. `600x400`), Hugo supports a set of additional image options.
+
+### Background Color
+
+The background color to fill into the transparency layer. This is mostly useful when converting to a format that does not support transparency, e.g. `JPEG`.
+
+You can set the background color to use with a 3 or 6 digit hex code starting with `#`.
+
+```go
+{{ $image.Resize "600x jpg #b31280" }}
+```
+
+For color codes, see https://www.google.com/search?q=color+picker
+
+### JPEG Quality
+Only relevant for JPEG images, values 1 to 100 inclusive, higher is better. Default is 75.
+
+```go
+{{ $image.Resize "600x q50" }}
+```
+
+### Rotate
+Rotates an image by the given angle counter-clockwise. The rotation will be performed first to get the dimensions correct. The main use of this is to be able to manually correct for [EXIF orientation](https://github.com/golang/go/issues/4341) of JPEG images.
+
+```go
+{{ $image.Resize "600x r90" }}
+```
+
+### Anchor
+Only relevant for the `Fill` method. This is useful for thumbnail generation where the main motive is located in, say, the left corner.
+Valid are `Center`, `TopLeft`, `Top`, `TopRight`, `Left`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`.
+
+```go
+{{ $image.Fill "300x200 BottomLeft" }}
+```
+
+### Resample Filter
+Filter used in resizing. Default is `Box`, a simple and fast resampling filter appropriate for downscaling.
+
+Examples are: `Box`, `NearestNeighbor`, `Linear`, `Gaussian`.
+
+See https://github.com/disintegration/imaging for more. If you want to trade quality for faster processing, this may be a option to test.
+
+```go
+{{ $image.Resize "600x400 Gaussian" }}
+```
+
+### Target Format
+
+By default the images is encoded in the source format, but you can set the target format as an option.
+
+Valid values are `jpg`, `png`, `tif`, `bmp`, and `gif`.
+
+```go
+{{ $image.Resize "600x jpg" }}
+```
+
+## Image Processing Examples
+
+_The photo of the sunset used in the examples below is Copyright [Bjørn Erik Pedersen](https://commons.wikimedia.org/wiki/User:Bep) (Creative Commons Attribution-Share Alike 4.0 International license)_
+
+
+{{< imgproc sunset Resize "300x" />}}
+
+{{< imgproc sunset Fill "90x120 left" />}}
+
+{{< imgproc sunset Fill "90x120 right" />}}
+
+{{< imgproc sunset Fit "90x90" />}}
+
+{{< imgproc sunset Resize "300x q10" />}}
+
+
+This is the shortcode used in the examples above:
+
+
+{{< code file="layouts/shortcodes/imgproc.html" >}}
+{{< readfile file="layouts/shortcodes/imgproc.html" >}}
+{{< /code >}}
+
+And it is used like this:
+
+```go-html-template
+{{* imgproc sunset Resize "300x" /*/>}}
+```
+
+
+{{% note %}}
+**Tip:** Note the self-closing shortcode syntax above. The `imgproc` shortcode can be called both with and without **inner content**.
+{{% /note %}}
+
+## Image Processing Config
+
+You can configure an `imaging` section in `config.toml` with default image processing options:
+
+```toml
+[imaging]
+# Default resample filter used for resizing. Default is Box,
+# a simple and fast averaging filter appropriate for downscaling.
+# See https://github.com/disintegration/imaging
+resampleFilter = "box"
+
+# Default JPEG quality setting. Default is 75.
+quality = 75
+
+# Anchor used when cropping pictures.
+# Default is "smart" which does Smart Cropping, using https://github.com/muesli/smartcrop
+# Smart Cropping is content aware and tries to find the best crop for each image.
+# Valid values are Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight
+anchor = "smart"
+
+# Default background color.
+# Hugo will preserve transparency for target formats that supports it,
+# but will fall back to this color for JPEG.
+# Expects a standard HEX color string with 3 or 6 digits.
+# See https://www.google.com/search?q=color+picker
+bgColor = "#ffffff"
+
+[imaging.exif]
+ # Regexp matching the fields you want to Exclude from the (massive) set of Exif info
+# available. As we cache this info to disk, this is for performance and
+# disk space reasons more than anything.
+# If you want it all, put ".*" in this config setting.
+# Note that if neither this or ExcludeFields is set, Hugo will return a small
+# default set.
+includeFields = ""
+
+# Regexp matching the Exif fields you want to exclude. This may be easier to use
+# than IncludeFields above, depending on what you want.
+excludeFields = ""
+
+# Hugo extracts the "photo taken" date/time into .Date by default.
+# Set this to true to turn it off.
+disableDate = false
+
+# Hugo extracts the "photo taken where" (GPS latitude and longitude) into
+# .Long and .Lat. Set this to true to turn it off.
+disableLatLong = false
+
+
+```
+
+## Smart Cropping of Images
+
+By default, Hugo will use the [Smartcrop](https://github.com/muesli/smartcrop), a library created by [muesli](https://github.com/muesli), when cropping images with `.Fill`. You can set the anchor point manually, but in most cases the smart option will make a good choice. And we will work with the library author to improve this in the future.
+
+An example using the sunset image from above:
+
+
+{{< imgproc sunset Fill "200x200 smart" />}}
+
+
+## Image Processing Performance Consideration
+
+Processed images are stored below `/resources` (can be set with `resourceDir` config setting). This folder is deliberately placed in the project, as it is recommended to check these into source control as part of the project. These images are not "Hugo fast" to generate, but once generated they can be reused.
+
+If you change your image settings (e.g. size), remove or rename images etc., you will end up with unused images taking up space and cluttering your project.
+
+To clean up, run:
+
+```bash
+hugo --gc
+```
+
+
+{{% note %}}
+**GC** is short for **Garbage Collection**.
+{{% /note %}}
+
+
+
diff --git a/hugolib/assets/images/sunset.jpg b/content/en/content-management/image-processing/sunset.jpg
similarity index 100%
rename from hugolib/assets/images/sunset.jpg
rename to content/en/content-management/image-processing/sunset.jpg
diff --git a/content/en/content-management/menus.md b/content/en/content-management/menus.md
new file mode 100644
index 000000000..9ac6f8bff
--- /dev/null
+++ b/content/en/content-management/menus.md
@@ -0,0 +1,123 @@
+---
+title: Menus
+linktitle: Menus
+description: Hugo has a simple yet powerful menu system.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-03-31
+categories: [content management]
+keywords: [menus]
+draft: false
+menu:
+ docs:
+ parent: "content-management"
+ weight: 120
+weight: 120 #rem
+aliases: [/extras/menus/]
+toc: true
+---
+
+{{% note "Lazy Blogger"%}}
+If all you want is a simple menu for your sections, see the ["Section Menu for Lazy Bloggers" in Menu Templates](/templates/menu-templates/#section-menu-for-lazy-bloggers).
+{{% /note %}}
+
+You can do this:
+
+* Place content in one or many menus
+* Handle nested menus with unlimited depth
+* Create menu entries without being attached to any content
+* Distinguish active element (and active branch)
+
+## What is a Menu in Hugo?
+
+A **menu** is a named array of menu entries accessible by name via the [`.Site.Menus` site variable][sitevars]. For example, you can access your site's `main` menu via `.Site.Menus.main`.
+
+{{% note "Menus on Multilingual Sites" %}}
+If you make use of the [multilingual feature](/content-management/multilingual/), you can define language-independent menus.
+{{% /note %}}
+
+See the [Menu Entry Properties][me-props] for all the variables and functions related to a menu entry.
+
+## Add content to menus
+
+Hugo allows you to add content to a menu via the content's [front matter](/content-management/front-matter/).
+
+### Simple
+
+If all you need to do is add an entry to a menu, the simple form works well.
+
+#### A Single Menu
+
+```
+---
+menu: "main"
+---
+```
+
+#### Multiple Menus
+
+```
+---
+menu: ["main", "footer"]
+---
+```
+
+#### Advanced
+
+
+```
+---
+menu:
+ docs:
+ parent: 'extras'
+ weight: 20
+---
+```
+
+## Add Non-content Entries to a Menu
+
+You can also add entries to menus that aren’t attached to a piece of content. This takes place in your Hugo project's [`config` file][config].
+
+Here’s an example snippet pulled from a configuration file:
+
+{{< code-toggle file="config" >}}
+[[menu.main]]
+ name = "about hugo"
+ pre = ""
+ weight = -110
+ identifier = "about"
+ url = "/about/"
+[[menu.main]]
+ name = "getting started"
+ pre = ""
+ post = "New!"
+ weight = -100
+ url = "/getting-started/"
+{{< /code-toggle >}}
+
+{{% note %}}
+The URLs must be relative to the context root. If the `baseURL` is `https://example.com/mysite/`, then the URLs in the menu must not include the context root `mysite`. Using an absolute URL will override the baseURL. If the value used for `URL` in the above example is `https://subdomain.example.com/`, the output will be `https://subdomain.example.com`.
+{{% /note %}}
+
+## Nesting
+
+All nesting of content is done via the `parent` field.
+
+The parent of an entry should be the identifier of another entry. The identifier should be unique (within a menu).
+
+The following order is used to determine an Identifier:
+
+`.Name > .LinkTitle > .Title`
+
+This means that `.Title` will be used unless `.LinkTitle` is present, etc. In practice, `.Name` and `.Identifier` are only used to structure relationships and therefore never displayed.
+
+In this example, the top level of the menu is defined in your [site `config` file][config]. All content entries are attached to one of these entries via the `.Parent` field.
+
+## Render Menus
+
+See [Menu Templates](/templates/menu-templates/) for information on how to render your site menus within your templates.
+
+[config]: /getting-started/configuration/
+[multilingual]: /content-management/multilingual/
+[sitevars]: /variables/
+[me-props]: /variables/menus/
diff --git a/content/en/content-management/multilingual.md b/content/en/content-management/multilingual.md
new file mode 100644
index 000000000..d3f71676a
--- /dev/null
+++ b/content/en/content-management/multilingual.md
@@ -0,0 +1,460 @@
+---
+title: Multilingual Mode
+linktitle: Multilingual and i18n
+description: Hugo supports the creation of websites with multiple languages side by side.
+date: 2017-01-10
+publishdate: 2017-01-10
+lastmod: 2017-01-10
+categories: [content management]
+keywords: [multilingual,i18n, internationalization]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 150
+weight: 150 #rem
+draft: false
+aliases: [/content/multilingual/,/tutorials/create-a-multilingual-site/]
+toc: true
+---
+
+You should define the available languages in a `languages` section in your site configuration.
+
+> Also See [Hugo Multilingual Part 1: Content translation](https://regisphilibert.com/blog/2018/08/hugo-multilingual-part-1-managing-content-translation/)
+
+## Configure Languages
+
+The following is an example of a site configuration for a multilingual Hugo project:
+
+{{< code-toggle file="config" >}}
+DefaultContentLanguage = "en"
+copyright = "Everything is mine"
+
+[params]
+[params.navigation]
+help = "Help"
+
+[languages]
+[languages.en]
+title = "My blog"
+weight = 1
+[languages.en.params]
+linkedin = "https://linkedin.com/whoever"
+
+[languages.fr]
+title = "Mon blogue"
+weight = 2
+[languages.fr.params]
+linkedin = "https://linkedin.com/fr/whoever"
+[languages.fr.params.navigation]
+help = "Aide"
+{{< /code-toggle >}}
+
+Anything not defined in a `languages` block will fall back to the global value for that key (e.g., `copyright` for the English `en` language). This also works for `params`, as demonstrated with `help` above: You will get the value `Aide` in French and `Help` in all the languages without this parameter set.
+
+With the configuration above, all content, sitemap, RSS feeds, paginations,
+and taxonomy pages will be rendered below `/` in English (your default content language) and then below `/fr` in French.
+
+When working with front matter `Params` in [single page templates][singles], omit the `params` in the key for the translation.
+
+`defaultContentLanguage` sets the project's default language. If not set, the default language will be `en`.
+
+If the default language needs to be rendererd below its own language code (`/en`) like the others, set `defaultContentLanguageInSubdir: true`.
+
+Only the obvious non-global options can be overridden per language. Examples of global options are `baseURL`, `buildDrafts`, etc.
+
+### Disable a Language
+
+You can disable one or more languages. This can be useful when working on a new translation.
+
+```toml
+disableLanguages = ["fr", "ja"]
+```
+
+Note that you cannot disable the default content language.
+
+We kept this as a standalone setting to make it easier to set via [OS environment](/getting-started/configuration/#configure-with-environment-variables):
+
+```bash
+HUGO_DISABLELANGUAGES="fr ja" hugo
+```
+If you have already a list of disabled languages in `config.toml`, you can enable them in development like this:
+
+```bash
+HUGO_DISABLELANGUAGES=" " hugo server
+```
+
+
+### Configure Multilingual Multihost
+
+From **Hugo 0.31** we support multiple languages in a multihost configuration. See [this issue](https://github.com/gohugoio/hugo/issues/4027) for details.
+
+This means that you can now configure a `baseURL` per `language`:
+
+
+> If a `baseURL` is set on the `language` level, then all languages must have one and they must all be different.
+
+Example:
+
+{{< code-toggle file="config" >}}
+[languages]
+[languages.fr]
+baseURL = "https://example.fr"
+languageName = "Français"
+weight = 1
+title = "En Français"
+
+[languages.en]
+baseURL = "https://example.com"
+languageName = "English"
+weight = 2
+title = "In English"
+{{ code-toggle >}}
+
+With the above, the two sites will be generated into `public` with their own root:
+
+```bash
+public
+├── en
+└── fr
+```
+
+**All URLs (i.e `.Permalink` etc.) will be generated from that root. So the English home page above will have its `.Permalink` set to `https://example.com/`.**
+
+When you run `hugo server` we will start multiple HTTP servers. You will typlically see something like this in the console:
+
+```bash
+Web Server is available at 127.0.0.1:1313 (bind address 127.0.0.1)
+Web Server is available at 127.0.0.1:1314 (bind address 127.0.0.1)
+Press Ctrl+C to stop
+```
+
+Live reload and `--navigateToChanged` between the servers work as expected.
+
+### Taxonomies and Blackfriday
+
+Taxonomies and [Blackfriday configuration][config] can also be set per language:
+
+
+{{< code-toggle file="config" >}}
+[Taxonomies]
+tag = "tags"
+
+[blackfriday]
+angledQuotes = true
+hrefTargetBlank = true
+
+[languages]
+[languages.en]
+weight = 1
+title = "English"
+[languages.en.blackfriday]
+angledQuotes = false
+
+[languages.fr]
+weight = 2
+title = "Français"
+[languages.fr.Taxonomies]
+plaque = "plaques"
+{{ code-toggle >}}
+
+## 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 filename
+
+Considering the following example:
+
+1. `/content/about.en.md`
+2. `/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 filename__.
+
+By having the same **path and base filename**, the content pieces are __linked__ together as translated pages.
+
+{{< note >}}
+If a file has no language code, it will be assigned the default language.
+{{ note >}}
+
+### 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` param.
+
+{{< code-toggle file="config" >}}
+
+languages:
+ en:
+ weight: 10
+ languageName: "English"
+ contentDir: "content/english"
+ fr:
+ weight: 20
+ languageName: "Français"
+ contentDir: "content/french"
+
+{{< /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`
+2. `/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`
+2. `/content/om.nn.md`
+3. `/content/presentation/a-propos.fr.md`
+
+```yaml
+# set in all three pages
+translationKey: "about"
+```
+
+By setting the `translationKey` front matter param to `about` in all three pages, they will be __linked__ as translated pages.
+
+
+### Localizing permalinks
+
+Because paths and filenames are used to handle linking, all translated pages will share the same URL (apart from the language subdirectory).
+
+To localize the URLs, the [`slug`]({{< ref "/content-management/organization/index.md#slug" >}}) or [`url`]({{< ref "/content-management/organization/index.md#url" >}}) front matter param can be set in any of the non-default language file.
+
+For example, a French translation (`content/about.fr.md`) can have its own localized slug.
+
+{{< code-toggle >}}
+Title: A Propos
+slug: "a-propos"
+{{< /code-toggle >}}
+
+
+At render, Hugo will build both `/about/` and `/fr/a-propos/` while maintaining their translation linking.
+
+{{% note %}}
+If using `url`, remember to include the language part as well: `/fr/compagnie/a-propos/`.
+{{%/ note %}}
+
+### 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 filename (`image.jpg`, `image.fr.jpg`) and by directory (`english/about/header.jpg`, `french/about/header.jpg`).
+{{%/ note %}}
+
+## Reference the Translated Content
+
+To create a list of links to translated content, use a template similar to the following:
+
+{{< code file="layouts/partials/i18nlist.html" >}}
+{{ if .IsTranslated }}
+
+{{ end }}
+{{< /code >}}
+
+The above can be put in a `partial` (i.e., inside `layouts/partials/`) and included in any template, whether a [single content page][contenttemplate] or the [homepage][]. It will not print anything if there are no translations for a given page.
+
+The above also uses the [`i18n` function][i18func] described in the next section.
+
+### List All Available Languages
+
+`.AllTranslations` on a `Page` can be used to list all translations, including the page itself. On the home page it can be used to build a language navigator:
+
+
+{{< code file="layouts/partials/allLanguages.html" >}}
+
+{{< /code >}}
+
+## Translation of Strings
+
+Hugo uses [go-i18n][] to support string translations. [See the project's source repository][go-i18n-source] to find tools that will help you manage your translation workflows.
+
+Translations are collected from the `themes//i18n/` folder (built into the theme), as well as translations present in `i18n/` at the root of your project. In the `i18n`, the translations will be merged and take precedence over what is in the theme folder. Language files should be named according to [RFC 5646][] with names such as `en-US.toml`, `fr.toml`, etc.
+
+{{% note %}}
+From **Hugo 0.31** you no longer need to use a valid language code. It can be anything.
+
+See https://github.com/gohugoio/hugo/issues/3564
+
+{{% /note %}}
+
+From within your templates, use the `i18n` function like this:
+
+```
+{{ i18n "home" }}
+```
+
+This uses a definition like this one in `i18n/en-US.toml`:
+
+```
+[home]
+other = "Home"
+```
+
+Often you will want to use to the page variables in the translations strings. To do that, pass on the "." context when calling `i18n`:
+
+```
+{{ i18n "wordCount" . }}
+```
+
+This uses a definition like this one in `i18n/en-US.toml`:
+
+```
+[wordCount]
+other = "This article has {{ .WordCount }} words."
+```
+An example of singular and plural form:
+
+```
+[readingTime]
+one = "One minute to read"
+other = "{{.Count}} minutes to read"
+```
+And then in the template:
+
+```
+{{ i18n "readingTime" .ReadingTime }}
+```
+
+## Customize Dates
+
+At the time of this writing, Go does not yet have support for internationalized locales for dates, but if you do some work, you can simulate it. For example, if you want to use French month names, you can add a data file like ``data/mois.yaml`` with this content:
+
+~~~yaml
+1: "janvier"
+2: "février"
+3: "mars"
+4: "avril"
+5: "mai"
+6: "juin"
+7: "juillet"
+8: "août"
+9: "septembre"
+10: "octobre"
+11: "novembre"
+12: "décembre"
+~~~
+
+...then index the non-English date names in your templates like so:
+
+~~~html
+
+~~~
+
+This technique extracts the day, month and year by specifying ``.Date.Day``, ``.Date.Month``, and ``.Date.Year``, and uses the month number as a key, when indexing the month name data file.
+
+## Menus
+
+You can define your menus for each language independently. Creating multilingual menus works just like [creating regular menus][menus], except they're defined in language-specific blocks in the configuration file:
+
+```
+defaultContentLanguage = "en"
+
+[languages.en]
+weight = 0
+languageName = "English"
+
+[[languages.en.menu.main]]
+url = "/"
+name = "Home"
+weight = 0
+
+
+[languages.de]
+weight = 10
+languageName = "Deutsch"
+
+[[languages.de.menu.main]]
+url = "/"
+name = "Startseite"
+weight = 0
+```
+
+The rendering of the main navigation works as usual. `.Site.Menus` will just contain the menu in the current language. Note that `absLangURL` below will link to the correct locale of your website. Without it, menu entries in all languages would link to the English version, since it's the default content language that resides in the root directory.
+
+```
+
+
+```
+
+## 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 handy to have a visual indicator of missing translations. The [`enableMissingTranslationPlaceholders` configuration option][config] 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.
+{{% /note %}}
+
+For merging of content from other languages (i.e. missing content translations), see [lang.Merge](/functions/lang.merge/).
+
+To track down missing translation strings, run Hugo with the `--i18n-warnings` flag:
+
+```
+ hugo --i18n-warnings | 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 [`relLangURL` template function][rellangurl] or the [`absLangURL` template function][abslangurl] **OR** be prefixed with `{{ .LanguagePrefix }}`
+
+If there is more than one language defined, the `LanguagePrefix` variable will equal `/en` (or whatever your `CurrentLanguage` is). If not enabled, it will be an empty string (and is therefore harmless for single-language Hugo websites).
+
+[abslangurl]: /functions/abslangurl
+[config]: /getting-started/configuration/
+[contenttemplate]: /templates/single-page-templates/
+[go-i18n-source]: https://github.com/nicksnyder/go-i18n
+[go-i18n]: https://github.com/nicksnyder/go-i18n
+[homepage]: /templates/homepage/
+[i18func]: /functions/i18n/
+[menus]: /content-management/menus/
+[rellangurl]: /functions/rellangurl
+[RFC 5646]: https://tools.ietf.org/html/rfc5646
+[singles]: /templates/single-page-templates/
diff --git a/content/en/content-management/organization/1-featured-content-bundles.png b/content/en/content-management/organization/1-featured-content-bundles.png
new file mode 100644
index 000000000..1706a29d6
Binary files /dev/null and b/content/en/content-management/organization/1-featured-content-bundles.png differ
diff --git a/content/en/content-management/organization/index.md b/content/en/content-management/organization/index.md
new file mode 100644
index 000000000..9ed2dbff3
--- /dev/null
+++ b/content/en/content-management/organization/index.md
@@ -0,0 +1,240 @@
+---
+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.
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+categories: [content management,fundamentals]
+keywords: [sections,content,organization,bundle,resources]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 10
+weight: 10 #rem
+draft: false
+aliases: [/content/sections/]
+toc: true
+---
+
+## Page Bundles
+
+Hugo `0.32` announced page-relative images and other resources packaged into `Page Bundles`.
+
+These terms are connected, and you also need to read about [Page Resources]({{< relref "/content-management/page-resources" >}}) and [Image Processing]({{< relref "/content-management/image-processing" >}}) to get the full picture.
+
+{{% imgproc 1-featured Resize "300x" %}}
+The illustration shows 3 bundles. Note that the home page bundle cannot contain other content pages, but other files (images etc.) are fine.
+{{% /imgproc %}}
+
+
+{{% note %}}
+The bundle documentation is **work in progress**. We will publish more comprehensive docs about this soon.
+{{% /note %}}
+
+
+# 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 just work:
+
+```
+.
+└── content
+ └── about
+ | └── index.md // <- https://example.com/about/
+ ├── posts
+ | ├── firstpost.md // <- https://example.com/posts/firstpost/
+ | ├── happy
+ | | └── ness.md // <- https://example.com/posts/happy/ness/
+ | └── secondpost.md // <- https://example.com/posts/secondpost/
+ └── quote
+ ├── first.md // <- https://example.com/quote/first/
+ └── second.md // <- https://example.com/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.com"` in your [site's configuration file][config].
+
+### Index Pages: `_index.md`
+
+`_index.md` has a special role in Hugo. It allows you to add front matter and content to your [list templates][lists]. These templates include those for [section templates][], [taxonomy templates][], [taxonomy terms templates][], and your [homepage template][].
+
+{{% note %}}
+**Tip:** You can get a reference to the content and metadata in `_index.md` using the [`.Site.GetPage` function](/functions/getpage/).
+{{% /note %}}
+
+You can keep one `_index.md` for your homepage and one in each of your content sections, taxonomies, and taxonomy 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:
+
+
+```
+. url
+. ⊢--^-⊣
+. path slug
+. ⊢--^-⊣⊢---^---⊣
+. filepath
+. ⊢------^------⊣
+content/posts/_index.md
+```
+
+At build, this will output to the following destination with the associated values:
+
+```
+
+ url ("/posts/")
+ ⊢-^-⊣
+ baseurl section ("posts")
+⊢--------^---------⊣⊢-^-⊣
+ permalink
+⊢----------^-------------⊣
+https://example.com/posts/index.html
+```
+
+The [sections][] can be nested as deeply as you need. The important part to understand is, that to make the section tree fully navigational, at least the lower-most section needs a content file. (i.e. `_index.md`).
+
+
+### Single Pages in Sections
+
+Single content files in each of your sections are going to be rendered as [single page templates][singles]. Here is an example of a single `post` within `posts`:
+
+
+```
+ path ("posts/my-first-hugo-post.md")
+. ⊢-----------^------------⊣
+. section slug
+. ⊢-^-⊣⊢--------^----------⊣
+content/posts/my-first-hugo-post.md
+```
+
+At the time Hugo builds your site, the content will be output to the following destination:
+
+```
+
+ url ("/posts/my-first-hugo-post/")
+ ⊢------------^----------⊣
+ baseurl section slug
+⊢--------^--------⊣⊢-^--⊣⊢-------^---------⊣
+ permalink
+⊢--------------------^---------------------⊣
+https://example.com/posts/my-first-hugo-post/index.html
+```
+
+
+## Paths Explained
+
+The following concepts will provide more insight into the relationship between your project's organization and the default behaviors of Hugo when building the output website.
+
+### `section`
+
+A default content type is determined by a piece of content's section. `section` is determined by the location within the project's `content` directory. `section` *cannot* be specified or overridden in front matter.
+
+### `slug`
+
+A content's `slug` is either `name.extension` or `name/`. The value for `slug` is determined by
+
+* the name of the content file (e.g., `lollapalooza.md`) OR
+* front matter overrides
+
+### `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 relative URL for the piece of content. The `url`
+
+* is based on the content's location within the directory structure OR
+* is defined in front matter and *overrides all the above*
+
+## Override Destination Paths via Front Matter
+
+Hugo believes that you organize your content with a purpose. The same structure that works to organize your source content is used to organize the rendered site. As displayed above, the organization of the source content will be mirrored in the destination.
+
+There are times where you may need more control over your content. In these cases, there are fields that can be specified in the front matter to determine the destination of a specific piece of content.
+
+The following items are defined in this order for a specific reason: items explained further down in the list will override earlier items, and not all of these items can be defined in front matter:
+
+### `filename`
+
+This isn't in the front matter, but is the actual name of the file minus the extension. This will be the name of the file in the destination (e.g., `content/posts/my-post.md` becomes `example.com/posts/my-post/`).
+
+### `slug`
+
+When defined in the front matter, the `slug` can take the place of the filename for the destination.
+
+{{< code file="content/posts/old-post.md" >}}
+---
+title: New Post
+slug: "new-post"
+---
+{{< /code >}}
+
+This will render to the following destination according to Hugo's default behavior:
+
+```
+example.com/posts/new-post/
+```
+
+### `section`
+
+`section` is determined by a content's location on disk and *cannot* be specified in the front matter. See [sections][] for more information.
+
+### `type`
+
+A content's `type` is also determined by its location on disk but, unlike `section`, it *can* be specified in the front matter. See [types][]. This can come in especially handy when you want a piece of content to render using a different layout. In the following example, you can create a layout at `layouts/new/mylayout.html` that Hugo will use to render this piece of content, even in the midst of many other posts.
+
+{{< code file="content/posts/my-post.md" >}}
+---
+title: My Post
+type: new
+layout: mylayout
+---
+{{< /code >}}
+
+
+
+
+
+### `url`
+
+A complete URL can be provided. This will override all the above as it pertains to the end destination. This must be the path from the baseURL (starting with a `/`). `url` will be used exactly as it provided in the front matter and will ignore the `--uglyURLs` setting in your site configuration:
+
+{{< code file="content/posts/old-url.md" >}}
+---
+title: Old URL
+url: /blog/new-url/
+---
+{{< /code >}}
+
+Assuming your `baseURL` is [configured][config] to `https://example.com`, the addition of `url` to the front matter will make `old-url.md` render to the following destination:
+
+```
+https://example.com/blog/new-url/
+```
+
+You can see more information on how to control output paths in [URL Management][urls].
+
+[config]: /getting-started/configuration/
+[formats]: /content-management/formats/
+[front matter]: /content-management/front-matter/
+[getpage]: /functions/getpage/
+[homepage template]: /templates/homepage/
+[homepage]: /templates/homepage/
+[lists]: /templates/lists/
+[pretty]: /content-management/urls/#pretty-urls
+[section templates]: /templates/section-templates/
+[sections]: /content-management/sections/
+[singles]: /templates/single-page-templates/
+[taxonomy templates]: /templates/taxonomy-templates/
+[taxonomy terms templates]: /templates/taxonomy-templates/
+[types]: /content-management/types/
+[urls]: /content-management/urls/
diff --git a/content/en/content-management/page-bundles.md b/content/en/content-management/page-bundles.md
new file mode 100644
index 000000000..0d665759c
--- /dev/null
+++ b/content/en/content-management/page-bundles.md
@@ -0,0 +1,185 @@
+---
+title : "Page Bundles"
+description : "Content organization using Page Bundles"
+date : 2018-01-24T13:09:00-05:00
+lastmod : 2018-01-28T22:26:40-05:00
+linktitle : "Page Bundles"
+keywords : ["page", "bundle", "leaf", "branch"]
+categories : ["content management"]
+toc : true
+menu :
+ docs:
+ identifier : "page-bundles"
+ parent : "content-management"
+ weight : 11
+---
+
+Page Bundles are a way to group [Page Resources](/content-management/page-resources/).
+
+A Page Bundle can be one of:
+
+- Leaf Bundle (leaf means it has no children)
+- Branch Bundle (home page, section, taxonomy terms, taxonomy list)
+
+| | Leaf Bundle | Branch Bundle |
+|-------------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Usage | Collection of content and attachments for single pages | Collection of attachments for section pages (home page, section, taxonomy terms, taxonomy list) |
+| Index file name | `index.md` [^fn:1] | `_index.md` [^fn:1] |
+| Allowed Resources | Page and non-page (like images, pdf, etc.) types | Only non-page (like images, pdf, etc.) types |
+| Where can the Resources live? | At any directory level within the leaf bundle directory. | Only in the directory level **of** the branch bundle directory i.e. the directory containing the `_index.md` ([ref](https://discourse.gohugo.io/t/question-about-content-folder-structure/11822/4?u=kaushalmodi)). |
+| Layout type | `single` | `list` |
+| Nesting | Does not allow nesting of more bundles under it | Allows nesting of leaf or branch bundles under it |
+| Example | `content/posts/my-post/index.md` | `content/posts/_index.md` |
+| Content from non-index page files... | Accessed only as page resources | Accessed only as regular pages |
+
+
+## Leaf Bundles {#leaf-bundles}
+
+A _Leaf Bundle_ is a directory at any hierarchy within the `content/`
+directory, that contains an **`index.md`** file.
+
+### Examples of Leaf Bundle organization {#examples-of-leaf-bundle-organization}
+
+```text
+content/
+├── about
+│ ├── index.md
+├── posts
+│ ├── my-post
+│ │ ├── content1.md
+│ │ ├── content2.md
+│ │ ├── image1.jpg
+│ │ ├── image2.png
+│ │ └── index.md
+│ └── my-other-post
+│ └── index.md
+│
+└── another-section
+ ├── ..
+ └── not-a-leaf-bundle
+ ├── ..
+ └── another-leaf-bundle
+ └── index.md
+```
+
+In the above example `content/` directory, there are four leaf
+bundles:
+
+about
+: This leaf bundle is at the root level (directly under
+ `content` directory) and has only the `index.md`.
+
+my-post
+: This leaf bundle has the `index.md`, two other content
+ Markdown files and two image files.
+
+my-other-post
+: This leaf bundle has only the `index.md`.
+
+another-leaf-bundle
+: This leaf bundle is nested under couple of
+ directories. This bundle also has only the `index.md`.
+
+{{% note %}}
+The hierarchy depth at which a leaf bundle is created does not matter,
+as long as it is not inside another **leaf** bundle.
+{{% /note %}}
+
+
+### Headless Bundle {#headless-bundle}
+
+A headless bundle is a bundle that is configured to not get published
+anywhere:
+
+- It will have no `Permalink` and no rendered HTML in `public/`.
+- It will not be part of `.Site.RegularPages`, etc.
+
+But you can get it by `.Site.GetPage`. Here is an example:
+
+```go-html-template
+{{ $headless := .Site.GetPage "/some-headless-bundle" }}
+{{ $reusablePages := $headless.Resources.Match "author*" }}
+
Authors
+{{ range $reusablePages }}
+
{{ .Title }}
+ {{ .Content }}
+{{ end }}
+```
+
+_In this example, we are assuming the `some-headless-bundle` to be a headless
+ bundle containing one or more **page** resources whose `.Name` matches
+ `"author*"`._
+
+Explanation of the above example:
+
+1. Get the `some-headless-bundle` Page "object".
+2. Collect a *slice* of resources in this *Page Bundle* that matches
+ `"author*"` using `.Resources.Match`.
+3. Loop through that *slice* of nested pages, and output their `.Title` and
+ `.Content`.
+
+---
+
+A leaf bundle can be made headless by adding below in the Front Matter
+(in the `index.md`):
+
+```toml
+headless = true
+```
+
+{{% note %}}
+Only leaf bundles can be made headless.
+{{% /note %}}
+
+There are many use cases of such headless page bundles:
+
+- Shared media galleries
+- Reusable page content "snippets"
+
+
+## Branch Bundles {#branch-bundles}
+
+A _Branch Bundle_ is any directory at any hierarchy within the
+`content/` directory, that contains at least an **`_index.md`** file.
+
+This `_index.md` can also be directly under the `content/` directory.
+
+{{% note %}}
+Here `md` (markdown) is used just as an example. You can use any file
+type as a content resource as long as it is a content type recognized by Hugo.
+{{% /note %}}
+
+
+### Examples of Branch Bundle organization {#examples-of-branch-bundle-organization}
+
+```text
+content/
+├── branch-bundle-1
+│ ├── branch-content1.md
+│ ├── branch-content2.md
+│ ├── image1.jpg
+│ ├── image2.png
+│ └── _index.md
+└── branch-bundle-2
+ ├── _index.md
+ └── a-leaf-bundle
+ └── index.md
+```
+
+In the above example `content/` directory, there are two branch
+bundles (and a leaf bundle):
+
+`branch-bundle-1`
+: This branch bundle has the `_index.md`, two
+ other content Markdown files and two image files.
+
+`branch-bundle-2`
+: This branch bundle has the `_index.md` and a
+ nested leaf bundle.
+
+{{% note %}}
+The hierarchy depth at which a branch bundle is created does not
+matter.
+{{% /note %}}
+
+[^fn:1]: The `.md` extension is just an example. The extension can be `.html`, `.json` or any of any valid MIME type.
diff --git a/content/en/content-management/page-resources.md b/content/en/content-management/page-resources.md
new file mode 100644
index 000000000..dcd19e42f
--- /dev/null
+++ b/content/en/content-management/page-resources.md
@@ -0,0 +1,165 @@
+---
+title : "Page Resources"
+description : "Page Resources -- images, other pages, documents etc. -- have page-relative URLs and their own metadata."
+date: 2018-01-24
+categories: ["content management"]
+keywords: [bundle,content,resources]
+weight: 4003
+draft: false
+toc: true
+linktitle: "Page Resources"
+menu:
+ docs:
+ parent: "content-management"
+ weight: 31
+---
+
+## Properties
+
+ResourceType
+: The main type of the resource. For example, a file of MIME type `image/jpg` has the ResourceType `image`.
+
+Name
+: Default value is the filename (relative to the owning page). Can be set in front matter.
+
+Title
+: Default value is the same as `.Name`. Can be set in front matter.
+
+Permalink
+: The absolute URL to the resource. Resources of type `page` will have no value.
+
+RelPermalink
+: The relative URL to the resource. Resources of type `page` will have no value.
+
+Content
+: The content of the resource itself. For most resources, this returns a string with the contents of the file. This can be used to inline some resources, such as `` or ``.
+
+MediaType
+: The MIME type of the resource, such as `image/jpg`.
+
+MediaType.MainType
+: The main type of the resource's MIME type. For example, a file of MIME type `application/pdf` has for MainType `application`.
+
+MediaType.SubType
+: The subtype of the resource's MIME type. For example, a file of MIME type `application/pdf` has for SubType `pdf`. Note that this is not the same as the file extension - PowerPoint files have a subtype of `vnd.mspowerpoint`.
+
+MediaType.Suffixes
+: A slice of possible suffixes for the resource's MIME type.
+
+## Methods
+ByType
+: Returns the page resources of the given type.
+
+```go
+{{ .Resources.ByType "image" }}
+```
+Match
+: Returns all the page resources (as a slice) whose `Name` matches the given Glob pattern ([examples](https://github.com/gobwas/glob/blob/master/readme.md)). The matching is case-insensitive.
+
+```go
+{{ .Resources.Match "images/*" }}
+```
+
+GetMatch
+: Same as `Match` but will return the first match.
+
+### Pattern Matching
+```go
+// Using Match/GetMatch to find this images/sunset.jpg ?
+.Resources.Match "images/sun*" ✅
+.Resources.Match "**/Sunset.jpg" ✅
+.Resources.Match "images/*.jpg" ✅
+.Resources.Match "**.jpg" ✅
+.Resources.Match "*" 🚫
+.Resources.Match "sunset.jpg" 🚫
+.Resources.Match "*sunset.jpg" 🚫
+
+```
+
+## Page Resources Metadata
+
+Page Resources' metadata is managed from their page's front matter with an array/table parameter named `resources`. You can batch assign values using a [wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm).
+
+{{% note %}}
+Resources of type `page` get `Title` etc. from their own front matter.
+{{% /note %}}
+
+name
+: Sets the value returned in `Name`.
+
+{{% warning %}}
+The methods `Match` and `GetMatch` use `Name` to match the resources.
+{{%/ warning %}}
+
+title
+: Sets the value returned in `Title`
+
+params
+: A map of custom key/values.
+
+
+### Resources metadata example
+
+{{< code-toggle copy="false">}}
+title: Application
+date : 2018-01-25
+resources :
+- src : "images/sunset.jpg"
+ name : "header"
+- src : "documents/photo_specs.pdf"
+ title : "Photo Specifications"
+ params:
+ icon : "photo"
+- 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"
+{{ code-toggle >}}
+
+From the example above:
+
+- `sunset.jpg` will receive a new `Name` and can now be found with `.GetMatch "header"`.
+- `documents/photo_specs.pdf` will get the `photo` icon.
+- `documents/checklist.pdf`, `documents/guide.pdf` and `documents/payment.docx` will get `Title` as set by `title`.
+- Every `PDF` in the bundle except `documents/photo_specs.pdf` will get the `pdf` icon.
+- All `PDF` files will get 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`.
+- Every docx in the bundle will receive the `word` icon.
+
+{{% warning %}}
+The __order matters__ --- Only the **first set** values of the `title`, `name` and `params`-**keys** will be used. Consecutive parameters will be set only for the ones not already set. For example, in the above example, `.Params.icon` is already first set to `"photo"` in `src = "documents/photo_specs.pdf"`. So that would not get overridden to `"pdf"` by the later set `src = "**.pdf"` rule.
+{{%/ warning %}}
+
+### The `:counter` placeholder in `name` and `title`
+
+The `:counter` is a special placeholder recognized in `name` and `title` parameters `resources`.
+
+The counter starts at 1 the first time they are used in either `name` or `title`.
+
+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 copy="false">}}
+[[resources]]
+ src = "*specs.pdf"
+ title = "Specification #:counter"
+[[resources]]
+ src = "**.pdf"
+ name = "pdf-file-:counter"
+{{ code-toggle >}}
+
+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"` |
diff --git a/content/en/content-management/related.md b/content/en/content-management/related.md
new file mode 100644
index 000000000..640cb04c0
--- /dev/null
+++ b/content/en/content-management/related.md
@@ -0,0 +1,135 @@
+---
+title: Related Content
+description: List related content in "See Also" sections.
+date: 2017-09-05
+categories: [content management]
+keywords: [content]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 40
+weight: 30
+draft: false
+aliases: [/content/related/,/related/]
+toc: true
+---
+
+
+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 or left to Hugo's default [Related Content configuration](#configure-related-content).
+
+## 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 single page template:
+
+{{< code file="layouts/partials/related.html" >}}
+{{ $related := .Site.RegularPages.Related . | first 5 }}
+{{ with $related }}
+
+{{ end }}
+{{< /code >}}
+
+### Methods
+
+Here is the list of "Related" methods available on a page collection such `.RegularPages`.
+
+#### .Related PAGE
+Returns a collection of pages related the given one.
+
+```
+{{ $related := .Site.RegularPages.Related . }}
+```
+
+#### .RelatedIndices PAGE INDICE1 [INDICE2 ...]
+Returns a collection of pages related to a given one restricted to a list of indices.
+
+```
+{{ $related := .Site.RegularPages.RelatedIndices . "tags" "date" }}
+```
+
+#### .RelatedTo KEYVALS [KEYVALS2 ...]
+Returns a collection of pages related together by a set of indices and their match.
+
+In order to build those set and pass them as argument, one must use the `keyVals` function where the first argument would be the `indice` and the consective ones its potential `matches`.
+
+```
+{{ $related := .Site.RegularPages.RelatedTo ( keyVals "tags" "hugo" "rocks") ( keyVals "date" .Date ) }}
+```
+
+{{% note %}}
+Read [this blog article](https://regisphilibert.com/blog/2018/04/hugo-optmized-relashionships-with-related-content/) for a great explanation of more advanced usage of this feature.
+{{% /note %}}
+
+## Configure Related Content
+Hugo provides a sensible default configuration of Related Content, but you can fine-tune this in your configuration, on the global or language level if needed.
+
+### Default configuration
+
+Without any `related` configuration set on the project, Hugo's Related Content methods will use the following.
+
+```yaml
+related:
+ threshold: 80
+ includeNewer: false
+ toLower: false
+ indices:
+ - name: keywords
+ weight: 100
+ - name: date
+ weight: 10
+```
+
+Custom configuration should be set using the same syntax.
+
+{{% note %}}
+If you add a `related` config section, you need to add a complete configuration. It is not possible to just set, say, `includeNewer` and use the rest from the Hugo defaults.
+{{% /note %}}
+
+### Top Level Config Options
+
+threshold
+: A value between 0-100. Lower value will give more, but maybe not so relevant, matches.
+
+includeNewer
+: Set to true 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.
+
+toLower
+: Set to true to lower case keywords in both the indexes and the queries. This may give more accurate results at a slight performance penalty. Note that this can also be set per index.
+
+### Config Options per Index
+
+name
+: The index name. This value maps directly to a page param. Hugo supports string values (`author` in the example) and lists (`tags`, `keywords` etc.) and time and date objects.
+
+weight
+: 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.
+
+pattern
+: 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
+: See above.
+
+## Performance Considerations
+
+**Fast is Hugo's middle name** and we would not have released this feature had it not been blistering fast.
+
+This feature has been in the back log and requested by many for a long time. The development got this recent kick start from this Twitter thread:
+
+{{< tweet 898398437527363585 >}}
+
+Scott S. Lowe removed the "Related Content" section built using the `intersect` template function on tags, and the build time dropped from 30 seconds to less than 2 seconds on his 1700 content page sized blog.
+
+He should now be able to add an improved version of that "Related Content" section without giving up the fast live-reloads. But it's worth noting that:
+
+* If you don't use any of the `Related` methods, you will not use the Relate Content feature, and performance will be the same as before.
+* Calling `.RegularPages.Related` etc. will create one inverted index, also sometimes named posting list, that will be reused for any lookups in that same page collection. Doing that in addition to, as an example, calling `.Pages.Related` will work as expected, but will create one additional inverted index. This should still be very fast, but worth having in mind, especially for bigger sites.
+
+{{% note %}}
+We currently do not index **Page content**. We thought we would release something that will make most people happy before we start solving [Sherlock's last case](https://github.com/joearms/sherlock).
+{{% /note %}}
diff --git a/content/en/content-management/sections.md b/content/en/content-management/sections.md
new file mode 100644
index 000000000..79ae201d4
--- /dev/null
+++ b/content/en/content-management/sections.md
@@ -0,0 +1,98 @@
+---
+title: Content Sections
+linktitle: Sections
+description: "Hugo generates a **section tree** that matches your content."
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-02-01
+categories: [content management]
+keywords: [lists,sections,content types,organization]
+menu:
+ docs:
+ parent: "content-management"
+ weight: 50
+weight: 50 #rem
+draft: false
+aliases: [/content/sections/]
+toc: true
+---
+
+A **Section** is a collection of pages that gets defined based on the
+organization structure under the `content/` directory.
+
+By default, all the **first-level** directories under `content/` form their own
+sections (**root sections**).
+
+If a user needs to define a section `foo` at a deeper level, they need to create
+a directory named `foo` with an `_index.md` file (see [Branch Bundles][branch bundles]
+for more information).
+
+
+{{% note %}}
+A **section** cannot be defined or overridden by a front matter parameter -- it
+is strictly derived from the content organization structure.
+{{% /note %}}
+
+## Nested Sections
+
+The sections can be nested as deeply as you need.
+
+```bash
+content
+└── blog <-- Section, because first-level dir under content/
+ ├── funny-cats
+ │ ├── mypost.md
+ │ └── kittens <-- Section, because contains _index.md
+ │ └── _index.md
+ └── tech <-- Section, because contains _index.md
+ └── _index.md
+```
+
+**The important part to understand is, that to make the section tree fully navigational, at least the lower-most section needs a content file. (e.g. `_index.md`).**
+
+{{% note %}}
+When we talk about a **section** in correlation with template selection, it is
+currently always the *root section* only (`/blog/funny-cats/mypost/ => blog`).
+
+If you need a specific template for a sub-section, you need to adjust either the `type` or `layout` in front matter.
+{{% /note %}}
+
+## Example: Breadcrumb Navigation
+
+With the available [section variables and methods](#section-page-variables-and-methods) you can build powerful navigation. One common example would be a partial to show Breadcrumb navigation:
+
+{{< code file="layouts/partials/breadcrumb.html" download="breadcrumb.html" >}}
+
+ {{ template "breadcrumbnav" (dict "p1" . "p2" .) }}
+
+{{ define "breadcrumbnav" }}
+{{ if .p1.Parent }}
+{{ template "breadcrumbnav" (dict "p1" .p1.Parent "p2" .p2 ) }}
+{{ else if not .p1.IsHome }}
+{{ template "breadcrumbnav" (dict "p1" .p1.Site.Home "p2" .p2 ) }}
+{{ end }}
+
+{{ end }}
+{{< /code >}}
+
+## Section Page Variables and Methods
+
+Also see [Page Variables](/variables/page/).
+
+{{< readfile file="/content/en/readfiles/sectionvars.md" markdown="true" >}}
+
+## Content Section Lists
+
+Hugo will automatically create pages for each *root section* that list all of the content in that section. See the documentation on [section templates][] for details on customizing the way these pages are rendered.
+
+## Content *Section* vs Content *Type*
+
+By default, everything created within a section will use the [content `type`][content type] that matches the *root section* name. For example, Hugo will assume that `posts/post-1.md` has a `posts` content `type`. If you are using an [archetype][] for your `posts` section, Hugo will generate front matter according to what it finds in `archetypes/posts.md`.
+
+[archetype]: /content-management/archetypes/
+[content type]: /content-management/types/
+[directory structure]: /getting-started/directory-structure/
+[section templates]: /templates/section-templates/
+[branch bundles]: /content-management/page-bundles/#branch-bundles
diff --git a/content/en/content-management/shortcodes.md b/content/en/content-management/shortcodes.md
new file mode 100644
index 000000000..94966343a
--- /dev/null
+++ b/content/en/content-management/shortcodes.md
@@ -0,0 +1,422 @@
+---
+title: Shortcodes
+linktitle:
+description: Shortcodes are simple snippets inside your content files calling built-in or custom templates.
+godocref:
+date: 2017-02-01
+publishdate: 2017-02-01
+lastmod: 2017-03-31
+menu:
+ docs:
+ parent: "content-management"
+ weight: 35
+weight: 35 #rem
+categories: [content management]
+keywords: [markdown,content,shortcodes]
+draft: false
+aliases: [/extras/shortcodes/]
+testparam: "Hugo Rocks!"
+toc: true
+---
+
+## What a Shortcode is
+
+Hugo loves Markdown because of its simple content format, but there are times when Markdown falls short. Often, content authors are forced to add raw HTML (e.g., video ``) to Markdown content. We think this contradicts the beautiful simplicity of Markdown's syntax.
+
+Hugo created **shortcodes** to circumvent these limitations.
+
+A shortcode is a simple snippet inside a content file that Hugo will render using a predefined template. Note that shortcodes will not work in template files. If you need the type of drop-in functionality that shortcodes provide but in a template, you most likely want a [partial template][partials] instead.
+
+In addition to cleaner Markdown, shortcodes can be updated any time to reflect new classes, techniques, or standards. At the point of site generation, Hugo shortcodes will easily merge in your changes. You avoid a possibly complicated search and replace operation.
+
+## Use Shortcodes
+
+{{< youtube 2xkNJL4gJ9E >}}
+
+In your content files, a shortcode can be called by calling `{{%/* shortcodename parameters */%}}`. Shortcode parameters are space delimited, and parameters with internal spaces can be quoted.
+
+The first word in the shortcode declaration is always the name of the shortcode. Parameters follow the name. Depending upon how the shortcode is defined, the parameters may be named, positional, or both, although you can't mix parameter types in a single call. The format for named parameters models that of HTML with the format `name="value"`.
+
+Some shortcodes use or require closing shortcodes. Again like HTML, the opening and closing shortcodes match (name only) with the closing declaration, which is prepended with a slash.
+
+Here are two examples of paired shortcodes:
+
+```
+{{%/* mdshortcode */%}}Stuff to `process` in the *center*.{{%/* /mdshortcode */%}}
+```
+
+```
+{{* highlight go */>}} A bunch of code here {{* /highlight */>}}
+```
+
+The examples above use two different delimiters, the difference being the `%` character in the first and the `<>` characters in the second.
+
+### Shortcodes with Markdown
+
+In Hugo `0.55` we changed how the `%` delimiter works. Shortcodes using the `%` as the outer-most delimiter will now be fully rendered when sent to the content renderer (e.g. Blackfriday for Markdown), meaning they can be part of the generated table of contents, footnotes, etc.
+
+If you want the old behavior, you can put the following line in the start of your shortcode template:
+
+```
+{{ $_hugo_config := `{ "version": 1 }` }}
+```
+
+
+### Shortcodes Without Markdown
+
+The `<` character indicates that the shortcode's inner content does *not* need further rendering. Often shortcodes without markdown include internal HTML:
+
+```
+{{* myshortcode */>}}
Hello World!
{{* /myshortcode */>}}
+```
+
+### Nested Shortcodes
+
+You can call shortcodes within other shortcodes by creating your own templates that leverage the `.Parent` variable. `.Parent` allows you to check the context in which the shortcode is being called. See [Shortcode templates][sctemps].
+
+## Use Hugo's Built-in Shortcodes
+
+Hugo ships with a set of predefined shortcodes that represent very common usage. These shortcodes are provided for author convenience and to keep your markdown content clean.
+
+### `figure`
+
+`figure` is an extension of the image syntax in markdown, which does not provide a shorthand for the more semantic [HTML5 `` element][figureelement].
+
+The `figure` shortcode can use the following named parameters:
+
+src
+: URL of the image to be displayed.
+
+link
+: If the image needs to be hyperlinked, URL of the destination.
+
+target
+: Optional `target` attribute for the URL if `link` parameter is set.
+
+rel
+: Optional `rel` attribute for the URL if `link` parameter is set.
+
+alt
+: Alternate text for the image if the image cannot be displayed.
+
+title
+: Image title.
+
+caption
+: Image caption.
+
+class
+: `class` attribute of the HTML `figure` tag.
+
+height
+: `height` attribute of the image.
+
+width
+: `width` attribute of the image.
+
+attr
+: Image attribution text.
+
+attrlink
+: If the attribution text needs to be hyperlinked, URL of the destination.
+
+#### Example `figure` Input
+
+{{< code file="figure-input-example.md" >}}
+{{* figure src="/media/spf13.jpg" title="Steve Francia" */>}}
+{{< /code >}}
+
+#### Example `figure` Output
+
+{{< output file="figure-output-example.html" >}}
+
+
+
+
Steve Francia
+
+
+{{< /output >}}
+
+### `gist`
+
+Bloggers often want to include GitHub gists when writing posts. Let's suppose we want to use the [gist at the following url][examplegist]:
+
+```
+https://gist.github.com/spf13/7896402
+```
+
+We can embed the gist in our content via username and gist ID pulled from the URL:
+
+```
+{{* gist spf13 7896402 */>}}
+```
+
+#### Example `gist` Input
+
+If the gist contains several files and you want to quote just one of them, you can pass the filename (quoted) as an optional third argument:
+
+{{< code file="gist-input.md" >}}
+{{* gist spf13 7896402 "img.html" */>}}
+{{< /code >}}
+
+#### Example `gist` Output
+
+{{< output file="gist-output.html" >}}
+{{< gist spf13 7896402 >}}
+{{< /output >}}
+
+#### Example `gist` Display
+
+To demonstrate the remarkably efficiency of Hugo's shortcode feature, we have embedded the `spf13` `gist` example in this page. The following simulates the experience for visitors to your website. Naturally, the final display will be contingent on your stylesheets and surrounding markup.
+
+{{< gist spf13 7896402 >}}
+
+### `highlight`
+
+This shortcode will convert the source code provided into syntax-highlighted HTML. Read more on [highlighting](/tools/syntax-highlighting/). `highlight` takes exactly one required `language` parameter and requires a closing shortcode.
+
+#### Example `highlight` Input
+
+{{< code file="content/tutorials/learn-html.md" >}}
+{{* highlight html */>}}
+
+
+
{{ .Title }}
+ {{ range .Pages }}
+ {{ .Render "summary"}}
+ {{ end }}
+
+
+{{* /highlight */>}}
+{{< /code >}}
+
+#### Example `highlight` Output
+
+The `highlight` shortcode example above would produce the following HTML when the site is rendered:
+
+{{< output file="tutorials/learn-html/index.html" >}}
+<sectionid="main">
+ <div>
+ <h1id="title">{{ .Title }}</h1>
+ {{ range .Pages }}
+ {{ .Render "summary"}}
+ {{ end }}
+ </div>
+</section>
+{{< /output >}}
+
+{{% note "More on Syntax Highlighting" %}}
+To see even more options for adding syntax-highlighted code blocks to your website, see [Syntax Highlighting in Developer Tools](/tools/syntax-highlighting/).
+{{% /note %}}
+
+### `instagram`
+
+If you'd like to embed a photo from [Instagram][], you only need the photo's ID. You can discern an Instagram photo ID from the URL:
+
+```
+https://www.instagram.com/p/BWNjjyYFxVx/
+```
+
+#### Example `instagram` Input
+
+{{< code file="instagram-input.md" >}}
+{{* instagram BWNjjyYFxVx */>}}
+{{< /code >}}
+
+You also have the option to hide the caption:
+
+{{< code file="instagram-input-hide-caption.md" >}}
+{{* instagram BWNjjyYFxVx hidecaption */>}}
+{{< /code >}}
+
+#### Example `instagram` Output
+
+By adding the preceding `hidecaption` example, the following HTML will be added to your rendered website's markup:
+
+{{< output file="instagram-hide-caption-output.html" >}}
+{{< instagram BWNjjyYFxVx hidecaption >}}
+{{< /output >}}
+
+#### Example `instagram` Display
+
+Using the preceding `instagram` with `hidecaption` example above, the following simulates the displayed experience for visitors to your website. Naturally, the final display will be contingent on your stylesheets and surrounding markup.
+
+{{< instagram BWNjjyYFxVx hidecaption >}}
+
+
+### `param`
+
+Gets a value from the current `Page's` params set in front matter, with a fall back to the site param value. It will log an `ERROR` if the param with the given key could not be found in either.
+
+```bash
+{{* param testparam */>}}
+```
+
+Since `testparam` is a param defined in front matter of this page with the value `Hugo Rocks!`, the above will print:
+
+{{< param testparam >}}
+
+To access deeply nested params, use "dot syntax", e.g:
+
+```bash
+{{* param "my.nested.param" */>}}
+```
+
+### `ref` and `relref`
+
+These shortcodes will look up the pages by their relative path (e.g., `blog/post.md`) or their logical name (`post.md`) and return the permalink (`ref`) or relative permalink (`relref`) for the found page.
+
+`ref` and `relref` also make it possible to make fragmentary links that work for the header links generated by Hugo.
+
+{{% note "More on Cross References" %}}
+Read a more extensive description of `ref` and `relref` in the [cross references](/content-management/cross-references/) documentation.
+{{% /note %}}
+
+`ref` and `relref` take exactly one required parameter of _reference_, quoted and in position `0`.
+
+#### Example `ref` and `relref` Input
+
+```
+[Neat]({{* ref "blog/neat.md" */>}})
+[Who]({{* relref "about.md#who" */>}})
+```
+
+#### Example `ref` and `relref` Output
+
+Assuming that standard Hugo pretty URLs are turned on.
+
+```
+Neat
+Who
+```
+
+### `tweet`
+
+You want to include a single tweet into your blog post? Everything you need is the URL of the tweet:
+
+```
+https://twitter.com/spf13/status/877500564405444608
+```
+
+#### Example `tweet` Input
+
+Pass the tweet's ID from the URL as a parameter to the `tweet` shortcode:
+
+{{< code file="example-tweet-input.md" >}}
+{{* tweet 877500564405444608 */>}}
+{{< /code >}}
+
+#### Example `tweet` Output
+
+Using the preceding `tweet` example, the following HTML will be added to your rendered website's markup:
+
+{{< output file="example-tweet-output.html" >}}
+{{< tweet 877500564405444608 >}}
+{{< /output >}}
+
+#### Example `tweet` Display
+
+Using the preceding `tweet` example, the following simulates the displayed experience for visitors to your website. Naturally, the final display will be contingent on your stylesheets and surrounding markup.
+
+{{< tweet 877500564405444608 >}}
+
+### `vimeo`
+
+Adding a video from [Vimeo][] is equivalent to the YouTube shortcode above.
+
+```
+https://vimeo.com/channels/staffpicks/146022717
+```
+
+#### Example `vimeo` Input
+
+Extract the ID from the video's URL and pass it to the `vimeo` shortcode:
+
+{{< code file="example-vimeo-input.md" >}}
+{{* vimeo 146022717 */>}}
+{{< /code >}}
+
+#### Example `vimeo` Output
+
+Using the preceding `vimeo` example, the following HTML will be added to your rendered website's markup:
+
+{{< output file="example-vimeo-output.html" >}}
+{{< vimeo 146022717 >}}
+{{< /output >}}
+
+{{% tip %}}
+If you want to further customize the visual styling of the YouTube or Vimeo output, add a `class` named parameter when calling the shortcode. The new `class` will be added to the `