├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── continuous-integration.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vale.ini ├── CHANGELOG.rst ├── LICENSE ├── MAINTENANCE.md ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── _vale │ ├── custom │ │ └── Spelling.yml │ ├── ignore_words.txt │ └── write-good │ │ ├── Cliches.yml │ │ ├── E-Prime.yml │ │ ├── Illusions.yml │ │ ├── Passive.yml │ │ ├── README.md │ │ ├── So.yml │ │ ├── ThereIs.yml │ │ ├── TooWordy.yml │ │ ├── Weasel.yml │ │ └── meta.json ├── requirements.txt └── source │ ├── _static │ └── sitemap-icon.svg │ ├── advanced-configuration.rst │ ├── changelog.rst │ ├── conf.py │ ├── configuration-values.rst │ ├── contributing.rst │ ├── getting-started.rst │ ├── index.rst │ └── search-optimization.rst ├── pyproject.toml ├── readthedocs.yml ├── requirements_dev.txt ├── setup.cfg ├── setup.py ├── sphinx_sitemap └── __init__.py ├── tests ├── conftest.py ├── roots │ └── test-root │ │ ├── bar.rst │ │ ├── conf.py │ │ ├── dolor.rst │ │ ├── elitr.rst │ │ ├── foo.rst │ │ ├── index.rst │ │ ├── ipsum.rst │ │ └── lorem.rst ├── test_parallel_mode.py └── test_simple.py └── tox.ini /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jdillard] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | groups: 13 | # Name for the group, which will be used in PR titles and branch names 14 | all-github-actions: 15 | # Group all updates together 16 | patterns: 17 | - "*" 18 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | 2 | name: continuous-integration 3 | on: 4 | push: 5 | branches: [master] 6 | tags: 7 | - "v[0-9]+.[0-9]+.[0-9]+*" 8 | pull_request: 9 | jobs: 10 | pre-commit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Python 3.10 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: "3.10" 18 | - uses: pre-commit/action@v3.0.1 19 | tests: 20 | runs-on: ubuntu-latest 21 | strategy: 22 | matrix: 23 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] 24 | sphinx-version: [''] 25 | include: 26 | - python-version: '3.12' 27 | sphinx-version: 'dev' 28 | - python-version: '3.11' 29 | sphinx-version: '7' 30 | - python-version: '3.10' 31 | sphinx-version: '6' 32 | - python-version: '3.9' 33 | sphinx-version: '5' 34 | - python-version: '3.8' 35 | sphinx-version: '5' 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Setup Python versions 39 | uses: actions/setup-python@v5 40 | with: 41 | python-version: ${{ matrix.python-version }} 42 | - name: Install Python dependencies 43 | run: | 44 | set -eo pipefail 45 | if [[ "${{ matrix.sphinx-version }}" != "" ]]; then 46 | if [[ "${{ matrix.sphinx-version }}" == "dev" ]]; then 47 | SPHINX_INSTALL="git+https://github.com/sphinx-doc/sphinx.git" 48 | else 49 | SPHINX_INSTALL="sphinx==${{ matrix.sphinx-version }}.*" 50 | fi 51 | fi 52 | set -x 53 | python -VV 54 | python -m site 55 | python -m pip install --upgrade pip setuptools wheel 56 | pip install -r requirements_dev.txt $SPHINX_INSTALL 57 | - name: Install Package 58 | run: | 59 | python -m pip install . 60 | - name: Run Tests for ${{ matrix.python-version }} 61 | run: | 62 | python -m tox 63 | vale: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - name: Checkout repository 67 | uses: actions/checkout@v4 68 | 69 | - name: Install docutils 70 | run: sudo apt-get install -y docutils 71 | 72 | - name: Lint with Vale 73 | uses: errata-ai/vale-action@reviewdog 74 | env: 75 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 76 | with: 77 | files: docs 78 | # github-pr-check, github-pr-review, github-check 79 | reporter: github-pr-check 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea/ 3 | *.code-workspace 4 | 5 | # Unit test / coverage reports 6 | .tox 7 | 8 | # Distribution / packaging 9 | *.egg-info/ 10 | dist/ 11 | build/ 12 | 13 | # Environments 14 | .venv 15 | 16 | # Sphinx documentation 17 | docs/_build/ 18 | 19 | # vale packages 20 | docs/_vale/Microsoft/ 21 | docs/_vale/write-good/ 22 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Install pre-commit hooks via 2 | # pre-commit install 3 | 4 | repos: 5 | 6 | - repo: https://github.com/PyCQA/isort 7 | rev: 6.0.1 8 | hooks: 9 | - id: isort 10 | 11 | - repo: https://github.com/psf/black 12 | rev: 25.1.0 13 | hooks: 14 | - id: black 15 | 16 | - repo: https://github.com/PyCQA/flake8 17 | rev: 7.2.0 18 | hooks: 19 | - id: flake8 20 | 21 | - repo: https://github.com/sphinx-contrib/sphinx-lint 22 | rev: v1.0.0 23 | hooks: 24 | - id: sphinx-lint 25 | args: [--jobs=1] 26 | files: ^docs/|CHANGELOG.rst|README.rst 27 | types: [rst] 28 | -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | StylesPath = ./docs/_vale 2 | MinAlertLevel = suggestion 3 | 4 | # SphinxBuildPath = docs/_build 5 | # SphinxAutoBuild = make html 6 | 7 | Packages = write-good, Microsoft 8 | 9 | [*.rst] 10 | BasedOnStyles = Vale, custom 11 | 12 | Vale.Redundancy = YES 13 | Vale.Repetition = YES 14 | Vale.GenderBias = YES 15 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | .. vale off 2 | 3 | Changelog 4 | ========= 5 | 6 | 2.6.0 7 | ----- 8 | 9 | *Release date: 2024-04-28* 10 | 11 | * |:wrench:| MAINT: Fix deprecated sphinx.testing.path 12 | `#83 `_ 13 | * Drop test support for Python 3.7 and Sphinx 2, 3, and 4. 14 | * |:sparkles:| NEW: Add sitemap_excludes configuration 15 | `#91 `_ 16 | 17 | 2.5.1 18 | ----- 19 | 20 | *Release date: 2023-08-17* 21 | 22 | * |:bug:| FIX: Fix Path use for Sphinx 7.2 23 | `#70 `_ 24 | * |:bug:| FIX: Fix incremental building by preventing multiprocessing queue from being pickled with environment 25 | `#62 `_ 26 | * |:wrench:| MAINT: Add docstrings and type hints 27 | `#61 `_ 28 | 29 | 2.5.0 30 | ----- 31 | 32 | *Release date: 2023-01-28* 33 | 34 | * |:books:| DOCS: Calculate version for sitemap based on current tag 35 | `#53 `_ 36 | * |:test_tube:| TESTS: Add Sphinx 6 env to tox 37 | `#55 `_ 38 | * |:sparkles:| NEW: Add support for Sphinx config "html_file_suffix" 39 | `#57 `_ 40 | * |:books:| DOCS: Add site search optimization 41 | `#58 `_ 42 | 43 | 2.4.0 44 | ----- 45 | 46 | *Release date: 2022-12-26* 47 | 48 | * |:books:| DOCS: Add ReadTheDocs docs 49 | `#45 `_ 50 | * |:wrench:| MAINT: General code clean up 51 | `#46 `_ 52 | * |:sparkles:| NEW: Add support for parallel mode 53 | `#47 `_ 54 | * |:test_tube:| TESTS: Add tests for ``dirhtml`` builder 55 | `#48 `_ 56 | * |:test_tube:| TESTS: Add vale support for docs 57 | `#49 `_ 58 | * |:bug:| FIX: Fix wheel includes so they don't include docs and tests 59 | `#51 `_ 60 | * |:books:| DOCS: Add write-good and improve writing 61 | `#52 `_ 62 | 63 | 2.3.0 64 | ----- 65 | 66 | *Release date: 2022-12-21* 67 | 68 | * |:wrench:| MAINT: Clean up how package versions are handled 69 | * |:test_tube:| TESTS: Install pre-commit with ``isort``, ``black``, and ``flake8`` 70 | `#35 `_ 71 | * |:books:| DOCS: Improve the wording of the README to help with issues upgrading to Sphinx 5 72 | `#36 `_ 73 | * |:bug:| FIX: Follow correct format for multilingual sitemaps 74 | `#38 `_ 75 | * |:wrench:| MAINT: Update the build process 76 | `#39 `_ 77 | * |:test_tube:| TESTS: Add testing infrastructure 78 | `#41 `_ 79 | `#42 `_ 80 | * |:wrench:| MAINT: Use logging for all logging messages 81 | `#40 `_ 82 | 83 | 2.2.1 84 | ----- 85 | 86 | *Release date: 2022-11-11* 87 | 88 | * |:books:| DOCS: Fix :confval:`sitemap_url_scheme` default value in **README** file 89 | `#32 `_ 90 | * |:wrench:| MAINT: Clean up package classifiers 91 | * |:wrench:| MAINT: Add **LICENSE** to source distributions 92 | `#27 `_ 93 | * |:books:| DOCS: Add Conda Forge badge to **README** file 94 | 95 | 2.2.0 96 | ------ 97 | 98 | *Release date: 2020-08-05* 99 | 100 | * |:wrench:| MAINT: Add ``parallel_write_safe`` flag and set to `False` 101 | `#20 `_. 102 | * |:sparkles:| Add :confval:`sitemap_locales` that creates an allow list of locales 103 | `#25 `_. 104 | * |:sparkles:| Add :confval:`sitemap_filename` that allows for custom sitemap name 105 | `#26 `_. 106 | 107 | 2.1.0 108 | ----- 109 | 110 | *Release date: 2020-02-22* 111 | 112 | * |:bug:| FIX: Make sure the regional variants for the ``hreflang`` attribute are valid 113 | `#19 `_. 114 | 115 | 2.0.0 116 | ----- 117 | 118 | *Release date: 2020-02-19* 119 | 120 | * |:sparkles:| NEW: Add :confval:`sitemap_url_scheme` that allows the URL scheme to be customized with a default of ``{version}{lang}{link}`` 121 | `#22 `_. 122 | 123 | .. note:: This has the potential to be a breaking change depending on how the ``version`` and ``language`` values are set. **Confirm the accuracy of the sitemap after upgrading**. 124 | 125 | 1.1.0 126 | ----- 127 | 128 | *Release date: 2019-12-12* 129 | 130 | * |:sparkles:| NEW: Add support for ``DirectoryHTMLBuilder``. 131 | * |:wrench:| MAINT: Remove unused ``HTMLTranslator`` import. 132 | * |:sparkles:| NEW: Make ``version`` and ``language`` each optional. 133 | * |:wrench:| MAINT: Add license to **setup.py**. 134 | * |:wrench:| MAINT: Mark unsafe for parallel reading. 135 | 136 | 1.0.2 137 | ----- 138 | 139 | *Release date: 2019-02-09* 140 | 141 | * |:wrench:| MAINT: Add ``html_baseurl`` if it doesn't exist for sphinx versions prior to 1.8.0. 142 | 143 | 1.0.1 144 | ----- 145 | 146 | *Release date: 2019-01-17* 147 | 148 | * |:bug:| FIX: Fix for ``AttributeError: No such config value: html_baseurl`` on versions of sphinx older than 1.8.0. 149 | 150 | 1.0.0 151 | ----- 152 | 153 | *Release date: 2019-01-17* 154 | 155 | * |:wrench:| MAINT: Use native ``html_baseurl``, instead of the custom ``site_url``. It checks for both for backwards compatibility. 156 | * |:sparkles:| NEW: Add support for multiple languages. 157 | 158 | 0.3.1 159 | ----- 160 | 161 | *Release date: 2018-03-04* 162 | 163 | * |:books:| DOCS: Add instructions on maintaining PyPI version to the docs 164 | 165 | 0.3.0 166 | ----- 167 | 168 | *Release date: 2018-03-04* 169 | 170 | * |:wrench:| MAINT: Remove unnecessary ``HTMLTranslator``. 171 | * |:books:| DOCS: Improve documentation 172 | 173 | 0.2 174 | --- 175 | 176 | *Release date: 2017-11-28* 177 | 178 | * |:wrench:| MAINT: Fix PyPI description 179 | 180 | 0.1 181 | --- 182 | 183 | *Release date: 2017-11-28* 184 | 185 | * Initial Release |:tada:| 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jared Dillard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | # Maintaining PyPI Version 2 | 3 | These are the steps, to be run by the maintainer, for making a new Python 4 | package release. 5 | 6 | 1. Rev `__version__` in **sphinx_sitemap/\_\_init\_\_.py**. 7 | 2. Update **CHANGELOG.rst** 8 | 3. Create a tag and push to GitHub: 9 | 10 | git tag -a vX.Y.Z -m "Release vX.Y.Z" 11 | git push --tags origin master 12 | 13 | 4. Build the latest distribution locally: 14 | 15 | python -m build 16 | 17 | 5. Upload to the test pypi.org repository: 18 | 19 | twine upload --repository testpypi dist/* 20 | 21 | 6. Upload to the production pypi.org repository: 22 | 23 | twine upload dist/* 24 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Sphinx Sitemap Generator Extension 2 | ================================== 3 | 4 | A `Sphinx`_ extension to generate multiversion and multilanguage 5 | `sitemaps.org`_ compliant sitemaps for the HTML version of your Sphinx 6 | documentation. 7 | 8 | | |PyPI version| |Conda Forge| |Downloads| |Parallel Safe| 9 | | |Code style: Black| |Docs Build| |CI Workflow| 10 | 11 | Documentation 12 | ------------- 13 | 14 | See `sphinx-sitemap documentation`_ for installation and configuration instructions. 15 | 16 | Contributing 17 | ------------ 18 | 19 | Pull Requests welcome! See `Contributing`_ for instructions on how best to contribute. 20 | 21 | License 22 | ------- 23 | 24 | **sphinx-sitemap** is made available under a **MIT license**; see `LICENSE`_ for 25 | details. 26 | 27 | Originally based on the sitemap generator in the `guzzle_sphinx_theme`_ project, 28 | also licensed under the MIT license. 29 | 30 | .. _Contributing: https://sphinx-sitemap.readthedocs.io/en/latest/contributing.html 31 | .. _guzzle_sphinx_theme: https://github.com/guzzle/guzzle_sphinx_theme 32 | .. _LICENSE: LICENSE 33 | .. _Sphinx: http://sphinx-doc.org/ 34 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html 35 | .. _sphinx-sitemap documentation: https://sphinx-sitemap.readthedocs.io/en/latest/index.html 36 | 37 | .. |PyPI version| image:: https://img.shields.io/pypi/v/sphinx-sitemap.svg 38 | :target: https://pypi.python.org/pypi/sphinx-sitemap 39 | .. |Conda Forge| image:: https://img.shields.io/conda/vn/conda-forge/sphinx-sitemap.svg 40 | :target: https://anaconda.org/conda-forge/sphinx-sitemap 41 | .. |Downloads| image:: https://static.pepy.tech/badge/sphinx-sitemap/month 42 | :target: https://pepy.tech/project/sphinx-sitemap 43 | .. |Code style: Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg 44 | :target: https://github.com/psf/black 45 | .. |Parallel Safe| image:: https://img.shields.io/badge/parallel%20safe-true-brightgreen 46 | :target: # 47 | .. |Docs Build| image:: https://readthedocs.org/projects/sphinx-sitemap/badge/?version=latest 48 | :target: https://sphinx-sitemap.readthedocs.io/en/latest/?badge=latest 49 | :alt: Documentation Status 50 | .. |CI Workflow| image:: https://github.com/jdillard/sphinx-sitemap/actions/workflows/continuous-integration.yml/badge.svg 51 | :target: https://github.com/jdillard/sphinx-sitemap/actions/workflows/continuous-integration.yml 52 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = SphinxSitemap 8 | SOURCEDIR = source 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_vale/custom/Spelling.yml: -------------------------------------------------------------------------------- 1 | extends: spelling 2 | message: "Did you really mean '%s'?" 3 | level: error 4 | ignore: 5 | - ignore_words.txt 6 | -------------------------------------------------------------------------------- /docs/_vale/ignore_words.txt: -------------------------------------------------------------------------------- 1 | Conda 2 | Algolia 3 | -------------------------------------------------------------------------------- /docs/_vale/write-good/Cliches.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using clichés like '%s'." 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - a chip off the old block 7 | - a clean slate 8 | - a dark and stormy night 9 | - a far cry 10 | - a fine kettle of fish 11 | - a loose cannon 12 | - a penny saved is a penny earned 13 | - a tough row to hoe 14 | - a word to the wise 15 | - ace in the hole 16 | - acid test 17 | - add insult to injury 18 | - against all odds 19 | - air your dirty laundry 20 | - all fun and games 21 | - all in a day's work 22 | - all talk, no action 23 | - all thumbs 24 | - all your eggs in one basket 25 | - all's fair in love and war 26 | - all's well that ends well 27 | - almighty dollar 28 | - American as apple pie 29 | - an axe to grind 30 | - another day, another dollar 31 | - armed to the teeth 32 | - as luck would have it 33 | - as old as time 34 | - as the crow flies 35 | - at loose ends 36 | - at my wits end 37 | - avoid like the plague 38 | - babe in the woods 39 | - back against the wall 40 | - back in the saddle 41 | - back to square one 42 | - back to the drawing board 43 | - bad to the bone 44 | - badge of honor 45 | - bald faced liar 46 | - ballpark figure 47 | - banging your head against a brick wall 48 | - baptism by fire 49 | - barking up the wrong tree 50 | - bat out of hell 51 | - be all and end all 52 | - beat a dead horse 53 | - beat around the bush 54 | - been there, done that 55 | - beggars can't be choosers 56 | - behind the eight ball 57 | - bend over backwards 58 | - benefit of the doubt 59 | - bent out of shape 60 | - best thing since sliced bread 61 | - bet your bottom dollar 62 | - better half 63 | - better late than never 64 | - better mousetrap 65 | - better safe than sorry 66 | - between a rock and a hard place 67 | - beyond the pale 68 | - bide your time 69 | - big as life 70 | - big cheese 71 | - big fish in a small pond 72 | - big man on campus 73 | - bigger they are the harder they fall 74 | - bird in the hand 75 | - bird's eye view 76 | - birds and the bees 77 | - birds of a feather flock together 78 | - bit the hand that feeds you 79 | - bite the bullet 80 | - bite the dust 81 | - bitten off more than he can chew 82 | - black as coal 83 | - black as pitch 84 | - black as the ace of spades 85 | - blast from the past 86 | - bleeding heart 87 | - blessing in disguise 88 | - blind ambition 89 | - blind as a bat 90 | - blind leading the blind 91 | - blood is thicker than water 92 | - blood sweat and tears 93 | - blow off steam 94 | - blow your own horn 95 | - blushing bride 96 | - boils down to 97 | - bolt from the blue 98 | - bone to pick 99 | - bored stiff 100 | - bored to tears 101 | - bottomless pit 102 | - boys will be boys 103 | - bright and early 104 | - brings home the bacon 105 | - broad across the beam 106 | - broken record 107 | - brought back to reality 108 | - bull by the horns 109 | - bull in a china shop 110 | - burn the midnight oil 111 | - burning question 112 | - burning the candle at both ends 113 | - burst your bubble 114 | - bury the hatchet 115 | - busy as a bee 116 | - by hook or by crook 117 | - call a spade a spade 118 | - called onto the carpet 119 | - calm before the storm 120 | - can of worms 121 | - can't cut the mustard 122 | - can't hold a candle to 123 | - case of mistaken identity 124 | - cat got your tongue 125 | - cat's meow 126 | - caught in the crossfire 127 | - caught red-handed 128 | - checkered past 129 | - chomping at the bit 130 | - cleanliness is next to godliness 131 | - clear as a bell 132 | - clear as mud 133 | - close to the vest 134 | - cock and bull story 135 | - cold shoulder 136 | - come hell or high water 137 | - cool as a cucumber 138 | - cool, calm, and collected 139 | - cost a king's ransom 140 | - count your blessings 141 | - crack of dawn 142 | - crash course 143 | - creature comforts 144 | - cross that bridge when you come to it 145 | - crushing blow 146 | - cry like a baby 147 | - cry me a river 148 | - cry over spilt milk 149 | - crystal clear 150 | - curiosity killed the cat 151 | - cut and dried 152 | - cut through the red tape 153 | - cut to the chase 154 | - cute as a bugs ear 155 | - cute as a button 156 | - cute as a puppy 157 | - cuts to the quick 158 | - dark before the dawn 159 | - day in, day out 160 | - dead as a doornail 161 | - devil is in the details 162 | - dime a dozen 163 | - divide and conquer 164 | - dog and pony show 165 | - dog days 166 | - dog eat dog 167 | - dog tired 168 | - don't burn your bridges 169 | - don't count your chickens 170 | - don't look a gift horse in the mouth 171 | - don't rock the boat 172 | - don't step on anyone's toes 173 | - don't take any wooden nickels 174 | - down and out 175 | - down at the heels 176 | - down in the dumps 177 | - down the hatch 178 | - down to earth 179 | - draw the line 180 | - dressed to kill 181 | - dressed to the nines 182 | - drives me up the wall 183 | - dull as dishwater 184 | - dyed in the wool 185 | - eagle eye 186 | - ear to the ground 187 | - early bird catches the worm 188 | - easier said than done 189 | - easy as pie 190 | - eat your heart out 191 | - eat your words 192 | - eleventh hour 193 | - even the playing field 194 | - every dog has its day 195 | - every fiber of my being 196 | - everything but the kitchen sink 197 | - eye for an eye 198 | - face the music 199 | - facts of life 200 | - fair weather friend 201 | - fall by the wayside 202 | - fan the flames 203 | - feast or famine 204 | - feather your nest 205 | - feathered friends 206 | - few and far between 207 | - fifteen minutes of fame 208 | - filthy vermin 209 | - fine kettle of fish 210 | - fish out of water 211 | - fishing for a compliment 212 | - fit as a fiddle 213 | - fit the bill 214 | - fit to be tied 215 | - flash in the pan 216 | - flat as a pancake 217 | - flip your lid 218 | - flog a dead horse 219 | - fly by night 220 | - fly the coop 221 | - follow your heart 222 | - for all intents and purposes 223 | - for the birds 224 | - for what it's worth 225 | - force of nature 226 | - force to be reckoned with 227 | - forgive and forget 228 | - fox in the henhouse 229 | - free and easy 230 | - free as a bird 231 | - fresh as a daisy 232 | - full steam ahead 233 | - fun in the sun 234 | - garbage in, garbage out 235 | - gentle as a lamb 236 | - get a kick out of 237 | - get a leg up 238 | - get down and dirty 239 | - get the lead out 240 | - get to the bottom of 241 | - get your feet wet 242 | - gets my goat 243 | - gilding the lily 244 | - give and take 245 | - go against the grain 246 | - go at it tooth and nail 247 | - go for broke 248 | - go him one better 249 | - go the extra mile 250 | - go with the flow 251 | - goes without saying 252 | - good as gold 253 | - good deed for the day 254 | - good things come to those who wait 255 | - good time was had by all 256 | - good times were had by all 257 | - greased lightning 258 | - greek to me 259 | - green thumb 260 | - green-eyed monster 261 | - grist for the mill 262 | - growing like a weed 263 | - hair of the dog 264 | - hand to mouth 265 | - happy as a clam 266 | - happy as a lark 267 | - hasn't a clue 268 | - have a nice day 269 | - have high hopes 270 | - have the last laugh 271 | - haven't got a row to hoe 272 | - head honcho 273 | - head over heels 274 | - hear a pin drop 275 | - heard it through the grapevine 276 | - heart's content 277 | - heavy as lead 278 | - hem and haw 279 | - high and dry 280 | - high and mighty 281 | - high as a kite 282 | - hit paydirt 283 | - hold your head up high 284 | - hold your horses 285 | - hold your own 286 | - hold your tongue 287 | - honest as the day is long 288 | - horns of a dilemma 289 | - horse of a different color 290 | - hot under the collar 291 | - hour of need 292 | - I beg to differ 293 | - icing on the cake 294 | - if the shoe fits 295 | - if the shoe were on the other foot 296 | - in a jam 297 | - in a jiffy 298 | - in a nutshell 299 | - in a pig's eye 300 | - in a pinch 301 | - in a word 302 | - in hot water 303 | - in the gutter 304 | - in the nick of time 305 | - in the thick of it 306 | - in your dreams 307 | - it ain't over till the fat lady sings 308 | - it goes without saying 309 | - it takes all kinds 310 | - it takes one to know one 311 | - it's a small world 312 | - it's only a matter of time 313 | - ivory tower 314 | - Jack of all trades 315 | - jockey for position 316 | - jog your memory 317 | - joined at the hip 318 | - judge a book by its cover 319 | - jump down your throat 320 | - jump in with both feet 321 | - jump on the bandwagon 322 | - jump the gun 323 | - jump to conclusions 324 | - just a hop, skip, and a jump 325 | - just the ticket 326 | - justice is blind 327 | - keep a stiff upper lip 328 | - keep an eye on 329 | - keep it simple, stupid 330 | - keep the home fires burning 331 | - keep up with the Joneses 332 | - keep your chin up 333 | - keep your fingers crossed 334 | - kick the bucket 335 | - kick up your heels 336 | - kick your feet up 337 | - kid in a candy store 338 | - kill two birds with one stone 339 | - kiss of death 340 | - knock it out of the park 341 | - knock on wood 342 | - knock your socks off 343 | - know him from Adam 344 | - know the ropes 345 | - know the score 346 | - knuckle down 347 | - knuckle sandwich 348 | - knuckle under 349 | - labor of love 350 | - ladder of success 351 | - land on your feet 352 | - lap of luxury 353 | - last but not least 354 | - last hurrah 355 | - last-ditch effort 356 | - law of the jungle 357 | - law of the land 358 | - lay down the law 359 | - leaps and bounds 360 | - let sleeping dogs lie 361 | - let the cat out of the bag 362 | - let the good times roll 363 | - let your hair down 364 | - let's talk turkey 365 | - letter perfect 366 | - lick your wounds 367 | - lies like a rug 368 | - life's a bitch 369 | - life's a grind 370 | - light at the end of the tunnel 371 | - lighter than a feather 372 | - lighter than air 373 | - like clockwork 374 | - like father like son 375 | - like taking candy from a baby 376 | - like there's no tomorrow 377 | - lion's share 378 | - live and learn 379 | - live and let live 380 | - long and short of it 381 | - long lost love 382 | - look before you leap 383 | - look down your nose 384 | - look what the cat dragged in 385 | - looking a gift horse in the mouth 386 | - looks like death warmed over 387 | - loose cannon 388 | - lose your head 389 | - lose your temper 390 | - loud as a horn 391 | - lounge lizard 392 | - loved and lost 393 | - low man on the totem pole 394 | - luck of the draw 395 | - luck of the Irish 396 | - make hay while the sun shines 397 | - make money hand over fist 398 | - make my day 399 | - make the best of a bad situation 400 | - make the best of it 401 | - make your blood boil 402 | - man of few words 403 | - man's best friend 404 | - mark my words 405 | - meaningful dialogue 406 | - missed the boat on that one 407 | - moment in the sun 408 | - moment of glory 409 | - moment of truth 410 | - money to burn 411 | - more power to you 412 | - more than one way to skin a cat 413 | - movers and shakers 414 | - moving experience 415 | - naked as a jaybird 416 | - naked truth 417 | - neat as a pin 418 | - needle in a haystack 419 | - needless to say 420 | - neither here nor there 421 | - never look back 422 | - never say never 423 | - nip and tuck 424 | - nip it in the bud 425 | - no guts, no glory 426 | - no love lost 427 | - no pain, no gain 428 | - no skin off my back 429 | - no stone unturned 430 | - no time like the present 431 | - no use crying over spilled milk 432 | - nose to the grindstone 433 | - not a hope in hell 434 | - not a minute's peace 435 | - not in my backyard 436 | - not playing with a full deck 437 | - not the end of the world 438 | - not written in stone 439 | - nothing to sneeze at 440 | - nothing ventured nothing gained 441 | - now we're cooking 442 | - off the top of my head 443 | - off the wagon 444 | - off the wall 445 | - old hat 446 | - older and wiser 447 | - older than dirt 448 | - older than Methuselah 449 | - on a roll 450 | - on cloud nine 451 | - on pins and needles 452 | - on the bandwagon 453 | - on the money 454 | - on the nose 455 | - on the rocks 456 | - on the spot 457 | - on the tip of my tongue 458 | - on the wagon 459 | - on thin ice 460 | - once bitten, twice shy 461 | - one bad apple doesn't spoil the bushel 462 | - one born every minute 463 | - one brick short 464 | - one foot in the grave 465 | - one in a million 466 | - one red cent 467 | - only game in town 468 | - open a can of worms 469 | - open and shut case 470 | - open the flood gates 471 | - opportunity doesn't knock twice 472 | - out of pocket 473 | - out of sight, out of mind 474 | - out of the frying pan into the fire 475 | - out of the woods 476 | - out on a limb 477 | - over a barrel 478 | - over the hump 479 | - pain and suffering 480 | - pain in the 481 | - panic button 482 | - par for the course 483 | - part and parcel 484 | - party pooper 485 | - pass the buck 486 | - patience is a virtue 487 | - pay through the nose 488 | - penny pincher 489 | - perfect storm 490 | - pig in a poke 491 | - pile it on 492 | - pillar of the community 493 | - pin your hopes on 494 | - pitter patter of little feet 495 | - plain as day 496 | - plain as the nose on your face 497 | - play by the rules 498 | - play your cards right 499 | - playing the field 500 | - playing with fire 501 | - pleased as punch 502 | - plenty of fish in the sea 503 | - point with pride 504 | - poor as a church mouse 505 | - pot calling the kettle black 506 | - pretty as a picture 507 | - pull a fast one 508 | - pull your punches 509 | - pulling your leg 510 | - pure as the driven snow 511 | - put it in a nutshell 512 | - put one over on you 513 | - put the cart before the horse 514 | - put the pedal to the metal 515 | - put your best foot forward 516 | - put your foot down 517 | - quick as a bunny 518 | - quick as a lick 519 | - quick as a wink 520 | - quick as lightning 521 | - quiet as a dormouse 522 | - rags to riches 523 | - raining buckets 524 | - raining cats and dogs 525 | - rank and file 526 | - rat race 527 | - reap what you sow 528 | - red as a beet 529 | - red herring 530 | - reinvent the wheel 531 | - rich and famous 532 | - rings a bell 533 | - ripe old age 534 | - ripped me off 535 | - rise and shine 536 | - road to hell is paved with good intentions 537 | - rob Peter to pay Paul 538 | - roll over in the grave 539 | - rub the wrong way 540 | - ruled the roost 541 | - running in circles 542 | - sad but true 543 | - sadder but wiser 544 | - salt of the earth 545 | - scared stiff 546 | - scared to death 547 | - sealed with a kiss 548 | - second to none 549 | - see eye to eye 550 | - seen the light 551 | - seize the day 552 | - set the record straight 553 | - set the world on fire 554 | - set your teeth on edge 555 | - sharp as a tack 556 | - shoot for the moon 557 | - shoot the breeze 558 | - shot in the dark 559 | - shoulder to the wheel 560 | - sick as a dog 561 | - sigh of relief 562 | - signed, sealed, and delivered 563 | - sink or swim 564 | - six of one, half a dozen of another 565 | - skating on thin ice 566 | - slept like a log 567 | - slinging mud 568 | - slippery as an eel 569 | - slow as molasses 570 | - smart as a whip 571 | - smooth as a baby's bottom 572 | - sneaking suspicion 573 | - snug as a bug in a rug 574 | - sow wild oats 575 | - spare the rod, spoil the child 576 | - speak of the devil 577 | - spilled the beans 578 | - spinning your wheels 579 | - spitting image of 580 | - spoke with relish 581 | - spread like wildfire 582 | - spring to life 583 | - squeaky wheel gets the grease 584 | - stands out like a sore thumb 585 | - start from scratch 586 | - stick in the mud 587 | - still waters run deep 588 | - stitch in time 589 | - stop and smell the roses 590 | - straight as an arrow 591 | - straw that broke the camel's back 592 | - strong as an ox 593 | - stubborn as a mule 594 | - stuff that dreams are made of 595 | - stuffed shirt 596 | - sweating blood 597 | - sweating bullets 598 | - take a load off 599 | - take one for the team 600 | - take the bait 601 | - take the bull by the horns 602 | - take the plunge 603 | - takes one to know one 604 | - takes two to tango 605 | - the more the merrier 606 | - the real deal 607 | - the real McCoy 608 | - the red carpet treatment 609 | - the same old story 610 | - there is no accounting for taste 611 | - thick as a brick 612 | - thick as thieves 613 | - thin as a rail 614 | - think outside of the box 615 | - third time's the charm 616 | - this day and age 617 | - this hurts me worse than it hurts you 618 | - this point in time 619 | - three sheets to the wind 620 | - through thick and thin 621 | - throw in the towel 622 | - tie one on 623 | - tighter than a drum 624 | - time and time again 625 | - time is of the essence 626 | - tip of the iceberg 627 | - tired but happy 628 | - to coin a phrase 629 | - to each his own 630 | - to make a long story short 631 | - to the best of my knowledge 632 | - toe the line 633 | - tongue in cheek 634 | - too good to be true 635 | - too hot to handle 636 | - too numerous to mention 637 | - touch with a ten foot pole 638 | - tough as nails 639 | - trial and error 640 | - trials and tribulations 641 | - tried and true 642 | - trip down memory lane 643 | - twist of fate 644 | - two cents worth 645 | - two peas in a pod 646 | - ugly as sin 647 | - under the counter 648 | - under the gun 649 | - under the same roof 650 | - under the weather 651 | - until the cows come home 652 | - unvarnished truth 653 | - up the creek 654 | - uphill battle 655 | - upper crust 656 | - upset the applecart 657 | - vain attempt 658 | - vain effort 659 | - vanquish the enemy 660 | - vested interest 661 | - waiting for the other shoe to drop 662 | - wakeup call 663 | - warm welcome 664 | - watch your p's and q's 665 | - watch your tongue 666 | - watching the clock 667 | - water under the bridge 668 | - weather the storm 669 | - weed them out 670 | - week of Sundays 671 | - went belly up 672 | - wet behind the ears 673 | - what goes around comes around 674 | - what you see is what you get 675 | - when it rains, it pours 676 | - when push comes to shove 677 | - when the cat's away 678 | - when the going gets tough, the tough get going 679 | - white as a sheet 680 | - whole ball of wax 681 | - whole hog 682 | - whole nine yards 683 | - wild goose chase 684 | - will wonders never cease? 685 | - wisdom of the ages 686 | - wise as an owl 687 | - wolf at the door 688 | - words fail me 689 | - work like a dog 690 | - world weary 691 | - worst nightmare 692 | - worth its weight in gold 693 | - wrong side of the bed 694 | - yanking your chain 695 | - yappy as a dog 696 | - years young 697 | - you are what you eat 698 | - you can run but you can't hide 699 | - you only live once 700 | - you're the boss 701 | - young and foolish 702 | - young and vibrant 703 | -------------------------------------------------------------------------------- /docs/_vale/write-good/E-Prime.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using '%s'." 3 | ignorecase: true 4 | level: suggestion 5 | tokens: 6 | - am 7 | - are 8 | - aren't 9 | - be 10 | - been 11 | - being 12 | - he's 13 | - here's 14 | - here's 15 | - how's 16 | - i'm 17 | - is 18 | - isn't 19 | - it's 20 | - she's 21 | - that's 22 | - there's 23 | - they're 24 | - was 25 | - wasn't 26 | - we're 27 | - were 28 | - weren't 29 | - what's 30 | - where's 31 | - who's 32 | - you're 33 | -------------------------------------------------------------------------------- /docs/_vale/write-good/Illusions.yml: -------------------------------------------------------------------------------- 1 | extends: repetition 2 | message: "'%s' is repeated!" 3 | level: warning 4 | alpha: true 5 | action: 6 | name: edit 7 | params: 8 | - truncate 9 | - " " 10 | tokens: 11 | - '[^\s]+' 12 | -------------------------------------------------------------------------------- /docs/_vale/write-good/Passive.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' may be passive voice. Use active voice if you can." 3 | ignorecase: true 4 | level: warning 5 | raw: 6 | - \b(am|are|were|being|is|been|was|be)\b\s* 7 | tokens: 8 | - '[\w]+ed' 9 | - awoken 10 | - beat 11 | - become 12 | - been 13 | - begun 14 | - bent 15 | - beset 16 | - bet 17 | - bid 18 | - bidden 19 | - bitten 20 | - bled 21 | - blown 22 | - born 23 | - bought 24 | - bound 25 | - bred 26 | - broadcast 27 | - broken 28 | - brought 29 | - built 30 | - burnt 31 | - burst 32 | - cast 33 | - caught 34 | - chosen 35 | - clung 36 | - come 37 | - cost 38 | - crept 39 | - cut 40 | - dealt 41 | - dived 42 | - done 43 | - drawn 44 | - dreamt 45 | - driven 46 | - drunk 47 | - dug 48 | - eaten 49 | - fallen 50 | - fed 51 | - felt 52 | - fit 53 | - fled 54 | - flown 55 | - flung 56 | - forbidden 57 | - foregone 58 | - forgiven 59 | - forgotten 60 | - forsaken 61 | - fought 62 | - found 63 | - frozen 64 | - given 65 | - gone 66 | - gotten 67 | - ground 68 | - grown 69 | - heard 70 | - held 71 | - hidden 72 | - hit 73 | - hung 74 | - hurt 75 | - kept 76 | - knelt 77 | - knit 78 | - known 79 | - laid 80 | - lain 81 | - leapt 82 | - learnt 83 | - led 84 | - left 85 | - lent 86 | - let 87 | - lighted 88 | - lost 89 | - made 90 | - meant 91 | - met 92 | - misspelt 93 | - mistaken 94 | - mown 95 | - overcome 96 | - overdone 97 | - overtaken 98 | - overthrown 99 | - paid 100 | - pled 101 | - proven 102 | - put 103 | - quit 104 | - read 105 | - rid 106 | - ridden 107 | - risen 108 | - run 109 | - rung 110 | - said 111 | - sat 112 | - sawn 113 | - seen 114 | - sent 115 | - set 116 | - sewn 117 | - shaken 118 | - shaven 119 | - shed 120 | - shod 121 | - shone 122 | - shorn 123 | - shot 124 | - shown 125 | - shrunk 126 | - shut 127 | - slain 128 | - slept 129 | - slid 130 | - slit 131 | - slung 132 | - smitten 133 | - sold 134 | - sought 135 | - sown 136 | - sped 137 | - spent 138 | - spilt 139 | - spit 140 | - split 141 | - spoken 142 | - spread 143 | - sprung 144 | - spun 145 | - stolen 146 | - stood 147 | - stridden 148 | - striven 149 | - struck 150 | - strung 151 | - stuck 152 | - stung 153 | - stunk 154 | - sung 155 | - sunk 156 | - swept 157 | - swollen 158 | - sworn 159 | - swum 160 | - swung 161 | - taken 162 | - taught 163 | - thought 164 | - thrived 165 | - thrown 166 | - thrust 167 | - told 168 | - torn 169 | - trodden 170 | - understood 171 | - upheld 172 | - upset 173 | - wed 174 | - wept 175 | - withheld 176 | - withstood 177 | - woken 178 | - won 179 | - worn 180 | - wound 181 | - woven 182 | - written 183 | - wrung 184 | -------------------------------------------------------------------------------- /docs/_vale/write-good/README.md: -------------------------------------------------------------------------------- 1 | Based on [write-good](https://github.com/btford/write-good). 2 | 3 | > Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too. 4 | 5 | ``` 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Brian Ford 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/_vale/write-good/So.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | level: error 4 | raw: 5 | - '(?:[;-]\s)so[\s,]|\bSo[\s,]' 6 | -------------------------------------------------------------------------------- /docs/_vale/write-good/ThereIs.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | ignorecase: false 4 | level: error 5 | raw: 6 | - '(?:[;-]\s)There\s(is|are)|\bThere\s(is|are)\b' 7 | -------------------------------------------------------------------------------- /docs/_vale/write-good/TooWordy.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is too wordy." 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - a number of 7 | - abundance 8 | - accede to 9 | - accelerate 10 | - accentuate 11 | - accompany 12 | - accomplish 13 | - accorded 14 | - accrue 15 | - acquiesce 16 | - acquire 17 | - additional 18 | - adjacent to 19 | - adjustment 20 | - admissible 21 | - advantageous 22 | - adversely impact 23 | - advise 24 | - aforementioned 25 | - aggregate 26 | - aircraft 27 | - all of 28 | - all things considered 29 | - alleviate 30 | - allocate 31 | - along the lines of 32 | - already existing 33 | - alternatively 34 | - amazing 35 | - ameliorate 36 | - anticipate 37 | - apparent 38 | - appreciable 39 | - as a matter of fact 40 | - as a means of 41 | - as far as I'm concerned 42 | - as of yet 43 | - as to 44 | - as yet 45 | - ascertain 46 | - assistance 47 | - at the present time 48 | - at this time 49 | - attain 50 | - attributable to 51 | - authorize 52 | - because of the fact that 53 | - belated 54 | - benefit from 55 | - bestow 56 | - by means of 57 | - by virtue of 58 | - by virtue of the fact that 59 | - cease 60 | - close proximity 61 | - commence 62 | - comply with 63 | - concerning 64 | - consequently 65 | - consolidate 66 | - constitutes 67 | - demonstrate 68 | - depart 69 | - designate 70 | - discontinue 71 | - due to the fact that 72 | - each and every 73 | - economical 74 | - eliminate 75 | - elucidate 76 | - employ 77 | - endeavor 78 | - enumerate 79 | - equitable 80 | - equivalent 81 | - evaluate 82 | - evidenced 83 | - exclusively 84 | - expedite 85 | - expend 86 | - expiration 87 | - facilitate 88 | - factual evidence 89 | - feasible 90 | - finalize 91 | - first and foremost 92 | - for all intents and purposes 93 | - for the most part 94 | - for the purpose of 95 | - forfeit 96 | - formulate 97 | - have a tendency to 98 | - honest truth 99 | - however 100 | - if and when 101 | - impacted 102 | - implement 103 | - in a manner of speaking 104 | - in a timely manner 105 | - in a very real sense 106 | - in accordance with 107 | - in addition 108 | - in all likelihood 109 | - in an effort to 110 | - in between 111 | - in excess of 112 | - in lieu of 113 | - in light of the fact that 114 | - in many cases 115 | - in my opinion 116 | - in order to 117 | - in regard to 118 | - in some instances 119 | - in terms of 120 | - in the case of 121 | - in the event that 122 | - in the final analysis 123 | - in the nature of 124 | - in the near future 125 | - in the process of 126 | - inception 127 | - incumbent upon 128 | - indicate 129 | - indication 130 | - initiate 131 | - irregardless 132 | - is applicable to 133 | - is authorized to 134 | - is responsible for 135 | - it is 136 | - it is essential 137 | - it seems that 138 | - it was 139 | - magnitude 140 | - maximum 141 | - methodology 142 | - minimize 143 | - minimum 144 | - modify 145 | - monitor 146 | - multiple 147 | - necessitate 148 | - nevertheless 149 | - not certain 150 | - not many 151 | - not often 152 | - not unless 153 | - not unlike 154 | - notwithstanding 155 | - null and void 156 | - numerous 157 | - objective 158 | - obligate 159 | - obtain 160 | - on the contrary 161 | - on the other hand 162 | - one particular 163 | - optimum 164 | - overall 165 | - owing to the fact that 166 | - participate 167 | - particulars 168 | - pass away 169 | - pertaining to 170 | - point in time 171 | - portion 172 | - possess 173 | - preclude 174 | - previously 175 | - prior to 176 | - prioritize 177 | - procure 178 | - proficiency 179 | - provided that 180 | - purchase 181 | - put simply 182 | - readily apparent 183 | - refer back 184 | - regarding 185 | - relocate 186 | - remainder 187 | - remuneration 188 | - requirement 189 | - reside 190 | - residence 191 | - retain 192 | - satisfy 193 | - shall 194 | - should you wish 195 | - similar to 196 | - solicit 197 | - span across 198 | - strategize 199 | - subsequent 200 | - substantial 201 | - successfully complete 202 | - sufficient 203 | - terminate 204 | - the month of 205 | - the point I am trying to make 206 | - therefore 207 | - time period 208 | - took advantage of 209 | - transmit 210 | - transpire 211 | - type of 212 | - until such time as 213 | - utilization 214 | - utilize 215 | - validate 216 | - various different 217 | - what I mean to say is 218 | - whether or not 219 | - with respect to 220 | - with the exception of 221 | - witnessed 222 | -------------------------------------------------------------------------------- /docs/_vale/write-good/Weasel.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is a weasel word!" 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - absolutely 7 | - accidentally 8 | - additionally 9 | - allegedly 10 | - alternatively 11 | - angrily 12 | - anxiously 13 | - approximately 14 | - awkwardly 15 | - badly 16 | - barely 17 | - beautifully 18 | - blindly 19 | - boldly 20 | - bravely 21 | - brightly 22 | - briskly 23 | - bristly 24 | - bubbly 25 | - busily 26 | - calmly 27 | - carefully 28 | - carelessly 29 | - cautiously 30 | - cheerfully 31 | - clearly 32 | - closely 33 | - coldly 34 | - completely 35 | - consequently 36 | - correctly 37 | - courageously 38 | - crinkly 39 | - cruelly 40 | - crumbly 41 | - cuddly 42 | - currently 43 | - daily 44 | - daringly 45 | - deadly 46 | - definitely 47 | - deliberately 48 | - doubtfully 49 | - dumbly 50 | - eagerly 51 | - early 52 | - easily 53 | - elegantly 54 | - enormously 55 | - enthusiastically 56 | - equally 57 | - especially 58 | - eventually 59 | - exactly 60 | - exceedingly 61 | - exclusively 62 | - extremely 63 | - fairly 64 | - faithfully 65 | - fatally 66 | - fiercely 67 | - finally 68 | - fondly 69 | - few 70 | - foolishly 71 | - fortunately 72 | - frankly 73 | - frantically 74 | - generously 75 | - gently 76 | - giggly 77 | - gladly 78 | - gracefully 79 | - greedily 80 | - happily 81 | - hardly 82 | - hastily 83 | - healthily 84 | - heartily 85 | - helpfully 86 | - honestly 87 | - hourly 88 | - hungrily 89 | - hurriedly 90 | - immediately 91 | - impatiently 92 | - inadequately 93 | - ingeniously 94 | - innocently 95 | - inquisitively 96 | - interestingly 97 | - irritably 98 | - jiggly 99 | - joyously 100 | - justly 101 | - kindly 102 | - largely 103 | - lately 104 | - lazily 105 | - likely 106 | - literally 107 | - lonely 108 | - loosely 109 | - loudly 110 | - loudly 111 | - luckily 112 | - madly 113 | - many 114 | - mentally 115 | - mildly 116 | - monthly 117 | - mortally 118 | - mostly 119 | - mysteriously 120 | - neatly 121 | - nervously 122 | - nightly 123 | - noisily 124 | - normally 125 | - obediently 126 | - occasionally 127 | - only 128 | - openly 129 | - painfully 130 | - particularly 131 | - patiently 132 | - perfectly 133 | - politely 134 | - poorly 135 | - powerfully 136 | - presumably 137 | - previously 138 | - promptly 139 | - punctually 140 | - quarterly 141 | - quickly 142 | - quietly 143 | - rapidly 144 | - rarely 145 | - really 146 | - recently 147 | - recklessly 148 | - regularly 149 | - remarkably 150 | - relatively 151 | - reluctantly 152 | - repeatedly 153 | - rightfully 154 | - roughly 155 | - rudely 156 | - sadly 157 | - safely 158 | - selfishly 159 | - sensibly 160 | - seriously 161 | - sharply 162 | - shortly 163 | - shyly 164 | - significantly 165 | - silently 166 | - simply 167 | - sleepily 168 | - slowly 169 | - smartly 170 | - smelly 171 | - smoothly 172 | - softly 173 | - solemnly 174 | - sparkly 175 | - speedily 176 | - stealthily 177 | - sternly 178 | - stupidly 179 | - substantially 180 | - successfully 181 | - suddenly 182 | - surprisingly 183 | - suspiciously 184 | - swiftly 185 | - tenderly 186 | - tensely 187 | - thoughtfully 188 | - tightly 189 | - timely 190 | - truthfully 191 | - unexpectedly 192 | - unfortunately 193 | - usually 194 | - very 195 | - victoriously 196 | - violently 197 | - vivaciously 198 | - warmly 199 | - waverly 200 | - weakly 201 | - wearily 202 | - weekly 203 | - wildly 204 | - wisely 205 | - worldly 206 | - wrinkly 207 | - yearly 208 | -------------------------------------------------------------------------------- /docs/_vale/write-good/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "feed": "https://github.com/errata-ai/write-good/releases.atom", 3 | "vale_version": ">=1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | furo 2 | esbonio 3 | sphinx-contributors 4 | sphinx 5 | sphinx-sitemap 6 | sphinxemoji 7 | sphinxext-opengraph 8 | -------------------------------------------------------------------------------- /docs/source/_static/sitemap-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/source/advanced-configuration.rst: -------------------------------------------------------------------------------- 1 | Advanced Configuration 2 | ====================== 3 | 4 | .. _configuration_customizing_url_scheme: 5 | 6 | Customizing the URL Scheme 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | :confval:`sitemap_url_scheme` defaults to ``{lang}{version}{link}``, where ``{lang}`` and ``{version}`` get set by :confval:`language` and :confval:`version` in **conf.py**. 10 | 11 | .. important:: As of Sphinx version 5, ``language`` defaults to ``"en"``, if that 12 | makes the default scheme produce the incorrect URL, then change the default behavior. 13 | 14 | To change the default behavior, set the value of :confval:`sitemap_url_scheme` in **conf.py** to the 15 | desired format. For example: 16 | 17 | .. code-block:: python 18 | 19 | sitemap_url_scheme = "{link}" 20 | 21 | Or for nested deployments, something like: 22 | 23 | .. code-block:: python 24 | 25 | sitemap_url_scheme = "{version}{lang}subdir/{link}" 26 | 27 | .. note:: The extension automatically appends trailing slashes to both the ``language`` and ``version`` values. 28 | You can also omit values from the scheme for desired behavior. 29 | 30 | 31 | .. _configuration_changing_filename: 32 | 33 | Changing the Filename 34 | ^^^^^^^^^^^^^^^^^^^^^ 35 | 36 | Set :confval:`sitemap_filename` in **conf.py** to the desired filename, for example: 37 | 38 | .. code-block:: python 39 | 40 | sitemap_filename = "sitemap.xml" 41 | 42 | Version Support 43 | ^^^^^^^^^^^^^^^ 44 | 45 | :confval:`version` specifies the version of the sitemap. 46 | For multi-version sitemaps, generate a sitemap per version and then manually add each to a `sitemapindex.xml`_ file. 47 | 48 | Tagged Releases 49 | ~~~~~~~~~~~~~~~ 50 | 51 | For a tagged release deploy strategy where the ``latest`` gets created from head of the branch and versions get created from tagged commits, check to see if the current commit matches the release tag regex and set :confval:`version` accordingly. 52 | 53 | .. code-block:: python 54 | 55 | # check if the current commit is tagged as a release (vX.Y.Z) and set the version 56 | GIT_TAG_OUTPUT = subprocess.check_output(["git", "tag", "--points-at", "HEAD"]) 57 | current_tag = GIT_TAG_OUTPUT.decode().strip() 58 | if re.match(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$", current_tag): 59 | version = current_tag 60 | else: 61 | version = "latest" 62 | 63 | .. tip:: Set the canonical URL in the theme layout of all versions to the latest version of that page, for example: 64 | 65 | .. code-block:: html 66 | 67 | 68 | 69 | .. _configuration_supporting_multiple_languages: 70 | 71 | Language Support 72 | ^^^^^^^^^^^^^^^^ 73 | 74 | :confval:`language` specifies the primary language. Any alternative languages get detected using the contents of :confval:`locale_dirs`. 75 | 76 | For example, with a primary language of **en**, and **es** and **fr** as detected translations, the sitemap look like this: 77 | 78 | .. code-block:: xml 79 | 80 | 81 | 82 | 83 | https://my-site.com/docs/en/index.html 84 | 85 | 86 | 87 | 88 | 89 | https://my-site.com/docs/en/about.html 90 | 91 | 92 | 93 | 94 | 95 | 96 | Use :confval:`sitemap_locales` to manually specify a list of locales to include in the sitemap: 97 | 98 | .. code-block:: python 99 | 100 | sitemap_locales = ['en', 'es'] 101 | 102 | The end result looks something like the following for each language/version build: 103 | 104 | .. code-block:: xml 105 | 106 | 107 | 108 | 109 | https://my-site.com/docs/en/index.html 110 | 111 | 112 | 113 | 114 | https://my-site.com/docs/en/about.html 115 | 116 | 117 | 118 | 119 | 120 | To generate the primary language with no alternatives, set :confval:`sitemap_locales` to ``[None]``: 121 | 122 | .. code-block:: python 123 | 124 | sitemap_locales = [None] 125 | 126 | For multilingual sitemaps, generate a sitemap per language and then manually add each to a `sitemapindex.xml`_ file. 127 | 128 | .. _configuration_excluding_pages: 129 | 130 | Excluding Pages 131 | ^^^^^^^^^^^^^^^ 132 | 133 | To exclude a set of pages, add each page's path to ``sitemap_exclude``: 134 | 135 | .. code-block:: python 136 | 137 | sitemap_excludes = [ 138 | "search.html", 139 | "genindex.html", 140 | ] 141 | 142 | 143 | .. _sitemapindex.xml: https://support.google.com/webmasters/answer/75712?hl=en 144 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html 145 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # 2 | # Configuration file for the Sphinx documentation builder. 3 | # 4 | # This file does only contain a selection of the most common options. For a 5 | # full list see the documentation: 6 | # http://www.sphinx-doc.org/en/master/config 7 | 8 | # -- Path setup -------------------------------------------------------------- 9 | 10 | # If extensions (or modules to document with autodoc) are in another directory, 11 | # add these directories to sys.path here. If the directory is relative to the 12 | # documentation root, use os.path.abspath to make it absolute, like shown here. 13 | # 14 | import re 15 | import subprocess 16 | 17 | # -- Project information ----------------------------------------------------- 18 | 19 | project = "Sphinx Sitemap" 20 | copyright = "Jared Dillard" 21 | author = "Jared Dillard" 22 | 23 | # check if the current commit is tagged as a release (vX.Y.Z) 24 | GIT_TAG_OUTPUT = subprocess.check_output(["git", "tag", "--points-at", "HEAD"]) 25 | current_tag = GIT_TAG_OUTPUT.decode().strip() 26 | if re.match(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$", current_tag): 27 | version = current_tag 28 | else: 29 | version = "latest" 30 | # The full version, including alpha/beta/rc tags 31 | release = "" 32 | 33 | 34 | # -- General configuration --------------------------------------------------- 35 | 36 | # If your documentation needs a minimal Sphinx version, state it here. 37 | # 38 | # needs_sphinx = '1.0' 39 | 40 | # Add any Sphinx extension module names here, as strings. They can be 41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 42 | # ones. 43 | extensions = [ 44 | "sphinx_sitemap", 45 | "sphinxemoji.sphinxemoji", 46 | "sphinx_contributors", 47 | "sphinx.ext.intersphinx", 48 | "sphinxext.opengraph", 49 | ] 50 | 51 | # Add any paths that contain templates here, relative to this directory. 52 | templates_path = ["_templates"] 53 | 54 | # The suffix(es) of source filenames. 55 | # You can specify multiple suffix as a list of string: 56 | # 57 | # source_suffix = ['.rst', '.md'] 58 | source_suffix = ".rst" 59 | 60 | # The master toctree document. 61 | master_doc = "index" 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | # 66 | # This is also used if you do content translation via gettext catalogs. 67 | # Usually you set "language" from the command line for these cases. 68 | language = "en" 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | # This pattern also affects html_static_path and html_extra_path . 73 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 74 | 75 | # The name of the Pygments (syntax highlighting) style to use. 76 | pygments_style = "sphinx" 77 | 78 | intersphinx_mapping = { 79 | "sphinx": ("https://www.sphinx-doc.org/en/master/", None), 80 | } 81 | 82 | 83 | # -- Options for HTML output ------------------------------------------------- 84 | 85 | # The theme to use for HTML and HTML Help pages. See the documentation for 86 | # a list of builtin themes. 87 | # 88 | html_theme = "furo" 89 | 90 | # Theme options are theme-specific and customize the look and feel of a theme 91 | # further. For a list of options available for each theme, see the 92 | # documentation. 93 | # 94 | html_theme_options = {} 95 | 96 | # Add any paths that contain custom static files (such as style sheets) here, 97 | # relative to this directory. They are copied after the builtin static files, 98 | # so a file named "default.css" will overwrite the builtin "default.css". 99 | html_static_path = ["_static"] 100 | html_logo = "_static/sitemap-icon.svg" 101 | html_title = "Sphinx Sitemap" 102 | 103 | ogp_site_url = "https://sphinx-sitemap.readthedocs.io/" 104 | ogp_image = "https://sphinx-sitemap.readthedocs.io/en/latest/_static/sitemap-icon.svg" 105 | 106 | # Custom sidebar templates, must be a dictionary that maps document names 107 | # to template names. 108 | # 109 | # The default sidebars (for documents that don't match any pattern) are 110 | # defined by theme itself. Builtin themes are using these templates by 111 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 112 | # 'searchbox.html']``. 113 | # 114 | # html_sidebars = {} 115 | 116 | html_baseurl = "https://sphinx-sitemap.readthedocs.org/" 117 | 118 | 119 | # -- Options for HTMLHelp output --------------------------------------------- 120 | 121 | # Output file base name for HTML help builder. 122 | htmlhelp_basename = "SphinxSitemapdoc" 123 | 124 | 125 | # -- Options for LaTeX output ------------------------------------------------ 126 | 127 | latex_elements = { 128 | # The paper size ('letterpaper' or 'a4paper'). 129 | # 130 | # 'papersize': 'letterpaper', 131 | # The font size ('10pt', '11pt' or '12pt'). 132 | # 133 | # 'pointsize': '10pt', 134 | # Additional stuff for the LaTeX preamble. 135 | # 136 | # 'preamble': '', 137 | # Latex figure (float) alignment 138 | # 139 | # 'figure_align': 'htbp', 140 | } 141 | 142 | # Grouping the document tree into LaTeX files. List of tuples 143 | # (source start file, target name, title, 144 | # author, documentclass [howto, manual, or own class]). 145 | latex_documents = [ 146 | ( 147 | master_doc, 148 | "SphinxSitemap.tex", 149 | "Sphinx Sitemap Documentation", 150 | "Jared Dillard", 151 | "manual", 152 | ) 153 | ] 154 | 155 | 156 | # -- Options for manual page output ------------------------------------------ 157 | 158 | # One entry per manual page. List of tuples 159 | # (source start file, name, description, authors, manual section). 160 | man_pages = [(master_doc, "sphinxsitemap", "Sphinx Sitemap Documentation", [author], 1)] 161 | 162 | 163 | # -- Options for Texinfo output ---------------------------------------------- 164 | 165 | # Grouping the document tree into Texinfo files. List of tuples 166 | # (source start file, target name, title, author, 167 | # dir menu entry, description, category) 168 | texinfo_documents = [ 169 | ( 170 | master_doc, 171 | "SphinxSitemap", 172 | "Sphinx Sitemap Documentation", 173 | author, 174 | "SphinxSitemap", 175 | "One line description of project.", 176 | "Miscellaneous", 177 | ) 178 | ] 179 | 180 | 181 | def setup(app): 182 | app.add_object_type( 183 | "confval", 184 | "confval", 185 | objname="configuration value", 186 | indextemplate="pair: %s; configuration value", 187 | ) 188 | -------------------------------------------------------------------------------- /docs/source/configuration-values.rst: -------------------------------------------------------------------------------- 1 | Project Configuration Values 2 | ============================ 3 | 4 | A list of of possible configuration values to configure in **conf.py**: 5 | 6 | .. confval:: sitemap_url_scheme 7 | 8 | The scheme used for URL structure. Default is ``{lang}{version}{link}``. 9 | 10 | See :ref:`configuration_customizing_url_scheme` for more information. 11 | 12 | .. versionadded:: 2.0.0 13 | 14 | .. confval:: sitemap_filename 15 | 16 | The filename used for the the sitemap. Default is ``sitemap.xml``. 17 | 18 | See :ref:`configuration_changing_filename` for more information. 19 | 20 | .. versionadded:: 2.2.0 21 | 22 | .. confval:: sitemap_locales 23 | 24 | The list of locales to include in the sitemap. 25 | 26 | See :ref:`configuration_supporting_multiple_languages` for more information. 27 | 28 | .. versionadded:: 2.2.0 29 | 30 | .. confval:: sitemap_excludes 31 | 32 | The list of pages to exclude from the sitemap. 33 | 34 | See :ref:`configuration_excluding_pages` for more information. 35 | 36 | .. versionadded:: 2.6.0 37 | -------------------------------------------------------------------------------- /docs/source/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | You will need to set up a development environment to make and test your changes 5 | before submitting them. 6 | 7 | Local development 8 | ----------------- 9 | 10 | #. Clone the `sphinx-sitemap repository`_. 11 | 12 | #. Create and activate a virtual environment: 13 | 14 | .. code-block:: console 15 | 16 | python3 -m venv .venv 17 | source .venv/bin/activate 18 | 19 | #. Install development dependencies: 20 | 21 | .. code-block:: console 22 | 23 | pip3 install -r requirements_dev.txt 24 | 25 | #. Install pre-commit Git hook scripts: 26 | 27 | .. code-block:: console 28 | 29 | pre-commit install 30 | 31 | Install a local copy of the extension 32 | ------------------------------------- 33 | 34 | Add **sphinx-sitemap** as a `third party extension`_. 35 | 36 | #. If your project doesn't have an extensions directory, create ``_exts`` and 37 | point **conf.py** to it: 38 | 39 | .. code-block:: python 40 | 41 | sys.path.append(os.path.abspath('../_exts')) 42 | 43 | #. Copy ``sphinx_sitemap`` as a directory in your project's extensions 44 | directory, and rename it to ``sphinx_sitemap_dev``. 45 | 46 | #. Add ``sphinx_sitemap_dev`` to :confval:`extensions`, or if already installed via ``pip``, change ``sphinx_sitemap`` to ``sphinx_sitemap_dev`` in **conf.py**: 47 | 48 | .. code-block:: python 49 | 50 | extensions = ['sphinx_sitemap_dev'] 51 | 52 | You can now make changes to ``sphinx_sitemap_dev``. 53 | 54 | Testing changes 55 | --------------- 56 | 57 | Run ``tox`` before committing changes. 58 | 59 | Current contributors 60 | -------------------- 61 | 62 | Thanks to all who have contributed! 63 | The people that have improved the code: 64 | 65 | .. contributors:: jdillard/sphinx-sitemap 66 | :avatars: 67 | :limit: 100 68 | :exclude: pre-commit-ci[bot],dependabot[bot] 69 | :order: ASC 70 | 71 | 72 | .. _sphinx-sitemap repository: https://github.com/jdillard/sphinx-sitemap 73 | .. _third party extension: http://www.sphinx-doc.org/en/master/ext/thirdparty.html 74 | -------------------------------------------------------------------------------- /docs/source/getting-started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Installation 5 | ------------ 6 | 7 | Directly install via ``pip`` by using: 8 | 9 | .. code:: 10 | 11 | pip install sphinx-sitemap 12 | 13 | Or with ``conda`` via ``conda-forge``: 14 | 15 | .. code:: 16 | 17 | conda install -c conda-forge sphinx-sitemap 18 | 19 | Usage 20 | ----- 21 | 22 | Add ``sphinx_sitemap`` to :confval:`extensions` in your Sphinx **conf.py**. 23 | For example: 24 | 25 | .. code:: python 26 | 27 | extensions = ['sphinx_sitemap'] 28 | 29 | Set the value of :confval:`html_baseurl` in your Sphinx **conf.py** to the current 30 | base URL of your documentation. For example: 31 | 32 | .. code:: python 33 | 34 | html_baseurl = 'https://my-site.com/docs/' 35 | 36 | After the HTML finishes building, **sphinx-sitemap** will output the location of the sitemap:: 37 | 38 | sitemap.xml was generated for URL https://my-site.com/docs/ in /path/to/_build/sitemap.xml 39 | 40 | .. tip:: Make sure to confirm the accuracy of the sitemap after installs and upgrades. 41 | 42 | See :doc:`advanced-configuration` for more information about how to use **sphinx-sitemap**. 43 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | sphinx-sitemap 2 | ============== 3 | 4 | A `Sphinx`_ extension to generate multi-version and multi-language 5 | `sitemaps.org`_ compliant sitemaps for the HTML version of your Sphinx 6 | documentation. 7 | 8 | |PyPI version| |Conda Forge| |Downloads| |Parallel Safe| |GitHub Stars| 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | getting-started 14 | advanced-configuration 15 | search-optimization 16 | configuration-values 17 | contributing 18 | changelog 19 | 20 | 21 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html 22 | .. _Sphinx: http://sphinx-doc.org/ 23 | 24 | .. |PyPI version| image:: https://img.shields.io/pypi/v/sphinx-sitemap.svg 25 | :target: https://pypi.python.org/pypi/sphinx-sitemap 26 | :alt: Latest PyPi Version 27 | .. |Conda Forge| image:: https://img.shields.io/conda/vn/conda-forge/sphinx-sitemap.svg 28 | :target: https://anaconda.org/conda-forge/sphinx-sitemap 29 | :alt: Latest Conda Forge version 30 | .. |Downloads| image:: https://static.pepy.tech/badge/sphinx-sitemap/month 31 | :target: https://pepy.tech/project/sphinx-sitemap 32 | :alt: PyPi Downloads per month 33 | .. |Parallel Safe| image:: https://img.shields.io/badge/parallel%20safe-true-brightgreen 34 | :target: # 35 | :alt: Parallel read/write safe 36 | .. |GitHub Stars| image:: https://img.shields.io/github/stars/jdillard/sphinx-sitemap?style=social 37 | :target: https://github.com/jdillard/sphinx-sitemap 38 | :alt: GitHub Repository stars 39 | -------------------------------------------------------------------------------- /docs/source/search-optimization.rst: -------------------------------------------------------------------------------- 1 | Getting the Most out of the Sitemap 2 | =================================== 3 | 4 | Search Engine Optimization 5 | -------------------------- 6 | 7 | Using robots.txt 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | Add a **robots.txt** file in the **source** directory which has a link to the ``sitemap.xml`` or ``sitemapindex.xml`` file. For example:: 11 | 12 | User-agent: * 13 | 14 | Sitemap: https://my-site.com/docs/sitemap.xml 15 | 16 | Then, add **robots.txt** to :confval:`html_extra_path` in **conf.py**: 17 | 18 | .. code-block:: python 19 | 20 | html_extra_path = ['robots.txt'] 21 | 22 | Submit Sitemap to Search Engines 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 24 | 25 | Submit the ``sitemap.xml`` or ``sitemapindex.xml`` to the appropriate search engine tools. 26 | 27 | Site Search Optimization 28 | ------------------------ 29 | 30 | Site search crawlers can also take advantage of sitemaps as starting points for crawling. 31 | 32 | Examples: 33 | 34 | - `Algolia`_ 35 | 36 | .. _Algolia: https://www.algolia.com/doc/tools/crawler/apis/configuration/sitemaps/ 37 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | 2 | [build-system] 3 | requires = [ 4 | "setuptools", 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | 8 | [project] 9 | name = "sphinx-sitemap" 10 | description = "Sitemap generator for Sphinx" 11 | authors = [ 12 | {name = "Jared Dillard", email = "jared.dillard@gmail.com"}, 13 | ] 14 | maintainers = [ 15 | {name = "Jared Dillard", email = "jared.dillard@gmail.com"}, 16 | ] 17 | classifiers = [ 18 | "License :: OSI Approved :: MIT License", 19 | "Topic :: Documentation :: Sphinx", 20 | "Programming Language :: Python :: 3.8", 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | "Framework :: Sphinx :: Extension", 25 | ] 26 | license = {text = "MIT"} 27 | readme = "README.rst" 28 | dynamic = [ 29 | "version", 30 | "dependencies", 31 | "optional-dependencies", 32 | ] 33 | 34 | [project.urls] 35 | documentation = "https://sphinx-sitemap.readthedocs.io/en/latest/index.html" 36 | download = "https://pypi.org/project/sphinx-sitemap/" 37 | source = "https://github.com/jdillard/sphinx-sitemap" 38 | changelog = "https://github.com/jdillard/sphinx-sitemap/blob/master/CHANGELOG.rst" 39 | 40 | [tool.setuptools.dynamic] 41 | optional-dependencies = {dev = { file = ["requirements_dev.txt"] }} 42 | 43 | [tool.isort] 44 | profile = "black" 45 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: "ubuntu-20.04" 5 | tools: 6 | python: "3.10" 7 | 8 | sphinx: 9 | configuration: docs/source/conf.py 10 | 11 | python: 12 | install: 13 | - requirements: docs/requirements.txt 14 | - method: pip 15 | path: . 16 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | tox 2 | build 3 | pre-commit 4 | flake8 5 | sphinx 6 | pytest 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | version = attr: sphinx_sitemap.__version__ 3 | 4 | [options] 5 | packages = find_namespace: 6 | install_requires = 7 | sphinx>=1.2 8 | 9 | [options.packages.find] 10 | where = . 11 | include = sphinx_sitemap* 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | if __name__ == "__main__": 4 | setuptools.setup() 5 | -------------------------------------------------------------------------------- /sphinx_sitemap/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Michael Dowling 2 | # Copyright (c) 2017 Jared Dillard 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | 14 | import os 15 | import queue 16 | from multiprocessing import Manager 17 | from pathlib import Path 18 | from typing import Any, Dict, List, Optional 19 | from xml.etree import ElementTree 20 | 21 | from sphinx.application import Sphinx 22 | from sphinx.util.logging import getLogger 23 | 24 | __version__ = "2.6.0" 25 | 26 | logger = getLogger(__name__) 27 | 28 | 29 | def setup(app: Sphinx) -> Dict[str, Any]: 30 | """ 31 | Sphinx extension setup function. 32 | It adds config values and connects Sphinx events to the sitemap builder. 33 | 34 | :param app: The Sphinx Application instance 35 | :return: A dict of Sphinx extension options 36 | """ 37 | app.add_config_value("site_url", default=None, rebuild="") 38 | app.add_config_value( 39 | "sitemap_url_scheme", default="{lang}{version}{link}", rebuild="" 40 | ) 41 | app.add_config_value("sitemap_locales", default=None, rebuild="") 42 | 43 | app.add_config_value("sitemap_filename", default="sitemap.xml", rebuild="") 44 | 45 | app.add_config_value("sitemap_excludes", default=[], rebuild="") 46 | 47 | try: 48 | app.add_config_value("html_baseurl", default=None, rebuild="") 49 | except BaseException: 50 | pass 51 | 52 | app.connect("builder-inited", record_builder_type) 53 | app.connect("html-page-context", add_html_link) 54 | app.connect("build-finished", create_sitemap) 55 | 56 | return { 57 | "parallel_read_safe": True, 58 | "parallel_write_safe": True, 59 | "version": __version__, 60 | } 61 | 62 | 63 | def get_locales(app: Sphinx) -> List[str]: 64 | """ 65 | Get a list of locales from the extension config or automatically detect based 66 | on Sphinx Application config. 67 | 68 | :param app: The Sphinx Application instance 69 | :return: A list of locales 70 | """ 71 | # Manually configured list of locales 72 | sitemap_locales: Optional[List[str]] = app.builder.config.sitemap_locales 73 | if sitemap_locales: 74 | # special value to add nothing -> use primary language only 75 | if sitemap_locales == [None]: 76 | return [] 77 | 78 | # otherwise, add each locale 79 | return [locale for locale in sitemap_locales] 80 | 81 | # Or autodetect locales 82 | locales = [] 83 | for locale_dir in app.builder.config.locale_dirs: 84 | locale_dir = os.path.join(app.confdir, locale_dir) 85 | if os.path.isdir(locale_dir): 86 | for locale in os.listdir(locale_dir): 87 | if os.path.isdir(os.path.join(locale_dir, locale)): 88 | locales.append(locale) 89 | return locales 90 | 91 | 92 | def record_builder_type(app: Sphinx): 93 | """ 94 | Determine if the Sphinx Builder is an instance of DirectoryHTMLBuilder and store that in the 95 | application environment. 96 | 97 | :param app: The Sphinx Application instance 98 | """ 99 | # builder isn't initialized in the setup so we do it here 100 | builder = getattr(app, "builder", None) 101 | if builder is None: 102 | return 103 | builder.env.is_directory_builder = type(builder).__name__ == "DirectoryHTMLBuilder" 104 | builder.env.app.sitemap_links = Manager().Queue() 105 | 106 | 107 | def hreflang_formatter(lang: str) -> str: 108 | """ 109 | Format the supplied locale code into a string that is compatible with `hreflang`. 110 | See also: 111 | 112 | - https://en.wikipedia.org/wiki/Hreflang#Common_Mistakes 113 | - https://github.com/readthedocs/readthedocs.org/pull/5638 114 | 115 | :param lang: The locale string to format 116 | :return: The formatted locale string 117 | """ 118 | if "_" in lang: 119 | return lang.replace("_", "-") 120 | return lang 121 | 122 | 123 | def add_html_link(app: Sphinx, pagename: str, templatename, context, doctree): 124 | """ 125 | As each page is built, collect page names for the sitemap 126 | 127 | :param app: The Sphinx Application instance 128 | :param pagename: The current page being built 129 | """ 130 | env = app.builder.env 131 | if app.builder.config.html_file_suffix is None: 132 | file_suffix = ".html" 133 | else: 134 | file_suffix = app.builder.config.html_file_suffix 135 | 136 | # Support DirectoryHTMLBuilder path structure 137 | # where generated links between pages omit the index.html 138 | if env.is_directory_builder: # type: ignore 139 | if pagename == "index": 140 | sitemap_link = "" 141 | elif pagename.endswith("/index"): 142 | sitemap_link = pagename[:-6] + "/" 143 | else: 144 | sitemap_link = pagename + "/" 145 | else: 146 | sitemap_link = pagename + file_suffix 147 | 148 | if sitemap_link not in app.builder.config.sitemap_excludes: 149 | env.app.sitemap_links.put(sitemap_link) # type: ignore 150 | 151 | 152 | def create_sitemap(app: Sphinx, exception): 153 | """ 154 | Generates the sitemap.xml from the collected HTML page links. 155 | 156 | :param app: The Sphinx Application instance 157 | """ 158 | site_url = app.builder.config.site_url or app.builder.config.html_baseurl 159 | if site_url: 160 | site_url.rstrip("/") + "/" 161 | else: 162 | logger.warning( 163 | "sphinx-sitemap: html_baseurl is required in conf.py." "Sitemap not built.", 164 | type="sitemap", 165 | subtype="configuration", 166 | ) 167 | return 168 | 169 | if app.env.app.sitemap_links.empty(): # type: ignore 170 | logger.info( 171 | "sphinx-sitemap: No pages generated for %s" % app.config.sitemap_filename, 172 | type="sitemap", 173 | subtype="information", 174 | ) 175 | return 176 | 177 | ElementTree.register_namespace("xhtml", "http://www.w3.org/1999/xhtml") 178 | 179 | root = ElementTree.Element( 180 | "urlset", xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" 181 | ) 182 | 183 | locales = get_locales(app) 184 | 185 | if app.builder.config.version: 186 | version = app.builder.config.version + "/" 187 | else: 188 | version = "" 189 | 190 | while True: 191 | try: 192 | link = app.env.app.sitemap_links.get_nowait() # type: ignore 193 | except queue.Empty: 194 | break 195 | 196 | url = ElementTree.SubElement(root, "url") 197 | scheme = app.config.sitemap_url_scheme 198 | if app.builder.config.language: 199 | lang = app.builder.config.language + "/" 200 | else: 201 | lang = "" 202 | 203 | ElementTree.SubElement(url, "loc").text = site_url + scheme.format( 204 | lang=lang, version=version, link=link 205 | ) 206 | 207 | for lang in locales: 208 | lang = lang + "/" 209 | ElementTree.SubElement( 210 | url, 211 | "{http://www.w3.org/1999/xhtml}link", 212 | rel="alternate", 213 | hreflang=hreflang_formatter(lang.rstrip("/")), 214 | href=site_url + scheme.format(lang=lang, version=version, link=link), 215 | ) 216 | 217 | filename = Path(app.outdir) / app.config.sitemap_filename 218 | ElementTree.ElementTree(root).write( 219 | filename, xml_declaration=True, encoding="utf-8", method="xml" 220 | ) 221 | 222 | logger.info( 223 | "sphinx-sitemap: %s was generated for URL %s in %s" 224 | % (app.config.sitemap_filename, site_url, filename), 225 | type="sitemap", 226 | subtype="information", 227 | ) 228 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | import sphinx 5 | 6 | pytest_plugins = "sphinx.testing.fixtures" 7 | # Exclude 'roots' dirs for pytest test collector 8 | collect_ignore = ["roots"] 9 | 10 | 11 | def pytest_configure(config): 12 | # before Sphinx 3.3.0, the `sphinx` marker is not registered by 13 | # the extension (but by Sphinx's internal pytest config) 14 | config.addinivalue_line("markers", "sphinx") 15 | 16 | 17 | @pytest.fixture(scope="session") 18 | def rootdir(): 19 | if sphinx.version_info[:2] < (7, 2): 20 | from sphinx.testing.path import path 21 | 22 | return path(__file__).parent.abspath() / "roots" 23 | 24 | return Path(__file__).resolve().parent / "roots" 25 | -------------------------------------------------------------------------------- /tests/roots/test-root/bar.rst: -------------------------------------------------------------------------------- 1 | bar 2 | === 3 | 4 | This is a bar page 5 | -------------------------------------------------------------------------------- /tests/roots/test-root/conf.py: -------------------------------------------------------------------------------- 1 | extensions = ["sphinx_sitemap"] 2 | -------------------------------------------------------------------------------- /tests/roots/test-root/dolor.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Dolor 4 | ===== 5 | 6 | This is the dolor page 7 | -------------------------------------------------------------------------------- /tests/roots/test-root/elitr.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Elitr 4 | ===== 5 | 6 | This is the elitr page 7 | -------------------------------------------------------------------------------- /tests/roots/test-root/foo.rst: -------------------------------------------------------------------------------- 1 | foo 2 | === 3 | 4 | This is a foo page 5 | -------------------------------------------------------------------------------- /tests/roots/test-root/index.rst: -------------------------------------------------------------------------------- 1 | test for basic sitemap 2 | ====================== 3 | 4 | .. toctree:: 5 | 6 | foo 7 | bar 8 | -------------------------------------------------------------------------------- /tests/roots/test-root/ipsum.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Ipsum 4 | ===== 5 | 6 | This is the ipsum page 7 | -------------------------------------------------------------------------------- /tests/roots/test-root/lorem.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Lorem 4 | ===== 5 | 6 | This is the lorem page 7 | -------------------------------------------------------------------------------- /tests/test_parallel_mode.py: -------------------------------------------------------------------------------- 1 | import os 2 | from xml.etree import ElementTree as etree 3 | 4 | import pytest 5 | 6 | 7 | @pytest.mark.sphinx( 8 | "html", 9 | freshenv=True, 10 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"}, 11 | ) 12 | def test_parallel(app, status, warning): 13 | app.parallel = 2 14 | app.warningiserror = True 15 | app.build() 16 | assert "sitemap.xml" in os.listdir(app.outdir) 17 | doc = etree.parse(app.outdir / "sitemap.xml") 18 | urls = { 19 | e.text 20 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc") 21 | } 22 | 23 | assert urls == { 24 | f"https://example.org/docs/en/{d}.html" 25 | for d in [ 26 | "index", 27 | "foo", 28 | "bar", 29 | "lorem", 30 | "ipsum", 31 | "dolor", 32 | "elitr", 33 | "genindex", 34 | "search", 35 | ] 36 | } 37 | assert not warning.getvalue() 38 | -------------------------------------------------------------------------------- /tests/test_simple.py: -------------------------------------------------------------------------------- 1 | import os 2 | from xml.etree import ElementTree as etree 3 | 4 | import pytest 5 | 6 | 7 | @pytest.mark.sphinx( 8 | "html", 9 | freshenv=True, 10 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"}, 11 | ) 12 | def test_simple_html(app, status, warning): 13 | app.warningiserror = True 14 | app.build() 15 | assert "sitemap.xml" in os.listdir(app.outdir) 16 | doc = etree.parse(app.outdir / "sitemap.xml") 17 | urls = { 18 | e.text 19 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc") 20 | } 21 | 22 | assert urls == { 23 | f"https://example.org/docs/en/{d}.html" 24 | for d in [ 25 | "index", 26 | "foo", 27 | "bar", 28 | "lorem", 29 | "ipsum", 30 | "dolor", 31 | "elitr", 32 | "genindex", 33 | "search", 34 | ] 35 | } 36 | 37 | 38 | @pytest.mark.sphinx( 39 | "html", 40 | freshenv=True, 41 | confoverrides={ 42 | "html_baseurl": "https://example.org/docs/", 43 | "language": "en", 44 | "html_file_suffix": ".htm", 45 | }, 46 | ) 47 | def test_html_file_suffix(app, status, warning): 48 | app.warningiserror = True 49 | app.build() 50 | assert "sitemap.xml" in os.listdir(app.outdir) 51 | doc = etree.parse(app.outdir / "sitemap.xml") 52 | urls = { 53 | e.text 54 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc") 55 | } 56 | 57 | assert urls == { 58 | f"https://example.org/docs/en/{d}.htm" 59 | for d in [ 60 | "index", 61 | "foo", 62 | "bar", 63 | "lorem", 64 | "ipsum", 65 | "dolor", 66 | "elitr", 67 | "genindex", 68 | "search", 69 | ] 70 | } 71 | 72 | 73 | @pytest.mark.sphinx( 74 | "dirhtml", 75 | freshenv=True, 76 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"}, 77 | ) 78 | def test_simple_dirhtml(app, status, warning): 79 | app.warningiserror = True 80 | app.build() 81 | assert "sitemap.xml" in os.listdir(app.outdir) 82 | doc = etree.parse(app.outdir / "sitemap.xml") 83 | urls = { 84 | e.text 85 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc") 86 | } 87 | 88 | assert urls == { 89 | f"https://example.org/docs/en/{d}" 90 | for d in [ 91 | "", 92 | "foo/", 93 | "bar/", 94 | "lorem/", 95 | "ipsum/", 96 | "dolor/", 97 | "elitr/", 98 | "genindex/", 99 | "search/", 100 | ] 101 | } 102 | 103 | 104 | @pytest.mark.sphinx( 105 | "html", 106 | freshenv=True, 107 | confoverrides={ 108 | "html_baseurl": "https://example.org/docs/", 109 | "language": "en", 110 | "sitemap_excludes": ["search.html", "genindex.html"], 111 | }, 112 | ) 113 | def test_simple_excludes(app, status, warning): 114 | app.warningiserror = True 115 | app.build() 116 | assert "sitemap.xml" in os.listdir(app.outdir) 117 | doc = etree.parse(app.outdir / "sitemap.xml") 118 | urls = { 119 | e.text 120 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc") 121 | } 122 | 123 | assert urls == { 124 | f"https://example.org/docs/en/{d}.html" 125 | for d in [ 126 | "index", 127 | "foo", 128 | "bar", 129 | "lorem", 130 | "ipsum", 131 | "dolor", 132 | "elitr", 133 | ] 134 | } 135 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py3{8,9}-sphinx{5,6,7,last} 4 | # Python 3.10 is unsupported below Sphinx4 5 | # See https://github.com/sphinx-doc/sphinx/issues/9816 6 | py3{10,11,12}-sphinx{5,6,7,last} 7 | 8 | [testenv] 9 | deps = 10 | pytest 11 | sphinx5: Sphinx[test]~=5.0 12 | sphinx6: Sphinx[test]~=6.0 13 | sphinx7: Sphinx[test]~=7.0 14 | sphinxlast: Sphinx[test] 15 | commands = 16 | pytest -W ignore::DeprecationWarning 17 | 18 | [flake8] 19 | max-line-length = 100 20 | extend-ignore = E203 21 | --------------------------------------------------------------------------------