diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 3eab10e730..1bfdf8b510 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -157,7 +157,6 @@ The `exclude_docs` config follows the [.gitignore pattern format](https://git-sc ```yaml exclude_docs: | *.py # Excludes e.g. docs/hooks/foo.py - /drafts # Excludes e.g. docs/drafts/hello.md /requirements.txt # Excludes docs/requirements.txt ``` @@ -169,6 +168,10 @@ See [**documentation**](../user-guide/configuration.md#exclude_docs). Context: # #### Drafts +> DANGER: **Dropped from version 1.6:** +> +> The `exclude_docs` config no longer applies the "drafts" functionality for `mkdocs serve`. This was renamed to [`draft_docs`](../user-guide/configuration.md#draft_docs). + The `exclude_docs` config has another behavior: all excluded Markdown pages will still be previewable in `mkdocs serve` only, just with a "DRAFT" marker on top. Then they will of course be excluded from `mkdocs build` or `gh-deploy`. If you don't want `mkdocs serve` to have any special behaviors and instead want it to perform completely normal builds, use the new flag `mkdocs serve --clean`. diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 8353b0cf6a..7154355b14 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -295,6 +295,10 @@ sub-directories. Index files will always be listed first within a sub-section. NEW: **New in version 1.5.** +> DANGER: **Changed in version 1.6:** +> +> This config no longer applies the "drafts" functionality for `mkdocs serve`. If you have draft documents that you want available in "serve" and not "build", replace `exclude_docs` with the new [`draft_docs`](#draft_docs) config option. + This config defines patterns of files (under [`docs_dir`](#docs_dir)) to not be picked up into the built site. Example: @@ -302,7 +306,6 @@ Example: ```yaml exclude_docs: | api-config.json # A file with this name anywhere. - drafts/ # A "drafts" directory anywhere. /requirements.txt # Top-level "docs/requirements.txt". *.py # Any file with this extension anywhere. !/foo/example.py # But keep this particular file. @@ -310,8 +313,6 @@ exclude_docs: | This follows the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format). -Note that `mkdocs serve` does *not* follow this setting and instead displays excluded documents but with a "DRAFT" mark. To prevent this effect, you can run `mkdocs serve --clean`. - The following defaults are always implicitly prepended - to exclude dot-files (and directories) as well as the top-level `templates` directory: ```yaml @@ -329,6 +330,23 @@ exclude_docs: | !.assets # Don't exclude '.assets' although all other '.*' are excluded ``` +### draft_docs + +NEW: **New in version 1.6.** + +This config defines patterns of files (under [`docs_dir`](#docs_dir)) to be treated as a draft. Draft files are available during `mkdocs serve` and include a "DRAFT" mark but will not be included in the build. To prevent this effect and make "serve" behave the same as "build", you can run `mkdocs serve --clean`. + +Example: + +```yaml +draft_docs: | + drafts/ # A "drafts" directory anywhere. + _unpublished.md # A md file ending in _unpublished.md + !/foo_unpublished.md # But keep this particular file. +``` + +This follows the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format). + ### not_in_nav NEW: **New in version 1.5.** diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py index 2eec30aad8..24e4de4fbf 100644 --- a/mkdocs/commands/build.py +++ b/mkdocs/commands/build.py @@ -257,7 +257,7 @@ def build(config: MkDocsConfig, *, serve_url: str | None = None, dirty: bool = F if config.strict: logging.getLogger('mkdocs').addHandler(warning_counter) - inclusion = InclusionLevel.all if serve_url else InclusionLevel.is_included + inclusion = InclusionLevel.is_in_serve if serve_url else InclusionLevel.is_included try: start = time.monotonic() @@ -312,7 +312,7 @@ def build(config: MkDocsConfig, *, serve_url: str | None = None, dirty: bool = F if excluded: log.info( "The following pages are being built only for the preview " - "but will be excluded from `mkdocs build` per `exclude_docs`:\n - " + "but will be excluded from `mkdocs build` per `draft_docs` config:\n - " + "\n - ".join(excluded) ) diff --git a/mkdocs/config/defaults.py b/mkdocs/config/defaults.py index a6f2871b9a..f93579adbe 100644 --- a/mkdocs/config/defaults.py +++ b/mkdocs/config/defaults.py @@ -29,6 +29,9 @@ class MkDocsConfig(base.Config): exclude_docs = c.Optional(c.PathSpec()) """Gitignore-like patterns of files (relative to docs dir) to exclude from the site.""" + draft_docs = c.Optional(c.PathSpec()) + """Gitignore-like patterns of files (relative to docs dir) to mark as draft.""" + not_in_nav = c.Optional(c.PathSpec()) """Gitignore-like patterns of files (relative to docs dir) that are not intended to be in the nav. diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py index 01d2f63983..da028dacbd 100644 --- a/mkdocs/structure/files.py +++ b/mkdocs/structure/files.py @@ -28,7 +28,9 @@ class InclusionLevel(enum.Enum): - EXCLUDED = -2 + EXCLUDED = -3 + """The file is excluded and will not be processed.""" + DRAFT = -2 """The file is excluded from the final site, but will still be populated during `mkdocs serve`.""" NOT_IN_NAV = -1 """The file is part of the site, but doesn't produce nav warnings.""" @@ -41,10 +43,13 @@ def all(self): return True def is_included(self): - return self.value > self.EXCLUDED.value + return self.value > self.DRAFT.value def is_excluded(self): - return self.value <= self.EXCLUDED.value + return self.value <= self.DRAFT.value + + def is_in_serve(self): + return self.value >= self.DRAFT.value def is_in_nav(self): return self.value > self.NOT_IN_NAV.value @@ -326,12 +331,15 @@ def set_exclusions(files: Iterable[File], config: MkDocsConfig) -> None: """Re-calculate which files are excluded, based on the patterns in the config.""" exclude: pathspec.gitignore.GitIgnoreSpec | None = config.get('exclude_docs') exclude = _default_exclude + exclude if exclude else _default_exclude + drafts: pathspec.gitignore.GitIgnoreSpec | None = config.get('draft_docs') nav_exclude: pathspec.gitignore.GitIgnoreSpec | None = config.get('not_in_nav') for file in files: if file.inclusion == InclusionLevel.UNDEFINED: if exclude.match_file(file.src_uri): file.inclusion = InclusionLevel.EXCLUDED + elif drafts and drafts.match_file(file.src_uri): + file.inclusion = InclusionLevel.DRAFT elif nav_exclude and nav_exclude.match_file(file.src_uri): file.inclusion = InclusionLevel.NOT_IN_NAV else: diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py index ae1b474ceb..c4521a78fc 100644 --- a/mkdocs/tests/build_tests.py +++ b/mkdocs/tests/build_tests.py @@ -580,12 +580,12 @@ def _assert_build_logs(self, expected): } ) @tempdir() - def test_exclude_pages_with_invalid_links(self, site_dir, docs_dir): + def test_draft_pages_with_invalid_links(self, site_dir, docs_dir): cfg = load_config( docs_dir=docs_dir, site_dir=site_dir, use_directory_urls=False, - exclude_docs='ba*.md', + draft_docs='ba*.md', ) with self.subTest(serve_url=None): @@ -603,8 +603,7 @@ def test_exclude_pages_with_invalid_links(self, site_dir, docs_dir): expected_logs = ''' INFO:Doc file 'test/bar.md' contains a relative link 'nonexistent.md', but the target 'test/nonexistent.md' is not found among documentation files. INFO:Doc file 'test/foo.md' contains a link to 'test/bar.md' which is excluded from the built site. - INFO:The following pages are being built only for the preview but will be excluded from `mkdocs build` per `exclude_docs`: - - http://localhost:123/documentation/.zoo.html + INFO:The following pages are being built only for the preview but will be excluded from `mkdocs build` per `draft_docs` config: - http://localhost:123/documentation/test/bar.html - http://localhost:123/documentation/test/baz.html ''' @@ -619,7 +618,7 @@ def test_exclude_pages_with_invalid_links(self, site_dir, docs_dir): self.assertPathIsFile(baz_path) self.assertIn('DRAFT', baz_path.read_text()) - self.assertPathIsFile(site_dir, '.zoo.html') + self.assertPathNotExists(site_dir, '.zoo.html') @tempdir( files={ @@ -686,13 +685,14 @@ def on_files_2(files: Files, config: MkDocsConfig) -> None: config.nav = Path(f.abs_src_path).read_text().splitlines() for serve_url in None, 'http://localhost:123/': - for exclude in 'full', 'nav', None: + for exclude in 'full', 'drafts', 'nav', None: with self.subTest(serve_url=serve_url, exclude=exclude): cfg = load_config( docs_dir=docs_dir, site_dir=site_dir, use_directory_urls=False, exclude_docs='SUMMARY.md' if exclude == 'full' else '', + draft_docs='SUMMARY.md' if exclude == 'drafts' else '', not_in_nav='SUMMARY.md' if exclude == 'nav' else '', ) cfg.plugins.events['files'] += [on_files_1, on_files_2] @@ -703,9 +703,9 @@ def on_files_2(files: Files, config: MkDocsConfig) -> None: INFO:The following pages exist in the docs directory, but are not included in the "nav" configuration: - SUMMARY.md ''' - if exclude == 'full' and serve_url: + if exclude == 'drafts' and serve_url: expected_logs = ''' - INFO:The following pages are being built only for the preview but will be excluded from `mkdocs build` per `exclude_docs`: + INFO:The following pages are being built only for the preview but will be excluded from `mkdocs build` per `draft_docs` config: - http://localhost:123/SUMMARY.html ''' with self._assert_build_logs(expected_logs): @@ -719,7 +719,9 @@ def on_files_2(files: Files, config: MkDocsConfig) -> None: ) summary_path = Path(site_dir, 'SUMMARY.html') - if exclude == 'full' and not serve_url: + if exclude == 'full': + self.assertPathNotExists(summary_path) + elif exclude == 'drafts' and not serve_url: self.assertPathNotExists(summary_path) else: self.assertPathExists(summary_path)