Skip to content

plugin

Class info

Classes

Name Children Inherits
MkNodesPlugin
mkdocs_mknodes.plugin

      🛈 DocStrings

      The Mkdocs Plugin.

      MkNodesPlugin

      Bases: BasePlugin[PluginConfig]

      Source code in mkdocs_mknodes/plugin/__init__.py
       36
       37
       38
       39
       40
       41
       42
       43
       44
       45
       46
       47
       48
       49
       50
       51
       52
       53
       54
       55
       56
       57
       58
       59
       60
       61
       62
       63
       64
       65
       66
       67
       68
       69
       70
       71
       72
       73
       74
       75
       76
       77
       78
       79
       80
       81
       82
       83
       84
       85
       86
       87
       88
       89
       90
       91
       92
       93
       94
       95
       96
       97
       98
       99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      class MkNodesPlugin(BasePlugin[pluginconfig.PluginConfig]):
          def __init__(self, **kwargs):
              super().__init__(**kwargs)
              self.link_replacer = linkreplacer.LinkReplacer()
              logger.debug("Finished initializing plugin")
              self.build_folder = None
              self._dir = None
              self.linkprovider = None
              self.theme = None
              self.folderinfo = None
              self.context = None
              self.root = None
      
          def on_startup(self, *, command: CommandStr, dirty: bool):
              """Activates new-style MkDocs plugin lifecycle."""
      
          def on_config(self, config: mknodesconfig.MkNodesConfig):  # type: ignore
              """Create the project based on MkDocs config."""
              if config.build_folder:
                  self.build_folder = pathlib.Path(config.build_folder)
              else:
                  self._dir = tempfile.TemporaryDirectory(
                      prefix="mknodes_",
                      ignore_cleanup_errors=True,
                  )
                  self.build_folder = pathlib.Path(self._dir.name)
                  logger.debug("Creating temporary dir %s", self._dir.name)
      
              if not config.build_fn:
                  return
              self.linkprovider = linkprovider.LinkProvider(
                  base_url=config.site_url or "",
                  use_directory_urls=config.use_directory_urls,
                  include_stdlib=True,
              )
              self.theme = mk.Theme.get_theme(
                  theme_name=config.theme.name or "material",
                  data=dict(config.theme),
              )
              git_repo = reporegistry.get_repo(
                  str(config.repo_path or "."),
                  clone_depth=config.clone_depth,
              )
              self.folderinfo = folderinfo.FolderInfo(git_repo.working_dir)
              self.context = contexts.ProjectContext(
                  metadata=self.folderinfo.context,
                  git=self.folderinfo.git.context,
                  # github=self.folderinfo.github.context,
                  theme=self.theme.context,
                  links=self.linkprovider,
                  env_config=config.get_jinja_config(),
              )
      
          def on_files(self, files: Files, *, config: mknodesconfig.MkNodesConfig) -> Files:  # type: ignore
              """Create the node tree and write files to build folder.
      
              In this step we aggregate all files and info we need to build the website.
              This includes:
      
                - Markdown pages (MkPages)
                - Templates
                - CSS files
              """
              if not config.build_fn:
                  return files
      
              logger.info("Generating pages...")
              build_fn = config.get_builder()
              self.root = mk.MkNav(context=self.context)
              build_fn(theme=self.theme, root=self.root)
              logger.debug("Finished building page.")
              paths = [
                  pathlib.Path(node.resolved_file_path).stem
                  for _level, node in self.root.iter_nodes()
                  if hasattr(node, "resolved_file_path")
              ]
              self.linkprovider.set_excludes(paths)
      
              # now we add our stuff to the MkDocs build environment
              cfg = mkdocsconfig.Config(config)
      
              logger.info("Updating MkDocs config metadata...")
              cfg.update_from_context(self.root.ctx)
              self.theme.adapt_extras(cfg.extra)
      
              logger.info("Setting up build backends...")
              mkdocs_backend = mkdocsbackend.MkDocsBackend(
                  files=files,
                  config=config,
                  directory=self.build_folder,
              )
      
              markdown_backend = markdownbackend.MarkdownBackend(
                  directory=pathlib.Path(config.site_dir) / "src",
                  extension=".original",
              )
              collector = buildcollector.BuildCollector(
                  backends=[mkdocs_backend, markdown_backend],
                  show_page_info=config.show_page_info,
                  global_resources=config.global_resources,
                  render_by_default=config.render_by_default,
              )
              self.build_info = collector.collect(self.root, self.theme)
              if nav_dict := self.root.nav.to_nav_dict():
                  match config.nav:
                      case list():
                          for k, v in nav_dict.items():
                              config.nav.append({k: v})
                      case dict():
                          config.nav |= nav_dict
                      case None:
                          config.nav = nav_dict
              return mkdocs_backend.files
      
          def on_nav(
              self,
              nav: Navigation,
              /,
              *,
              config: MkDocsConfig,
              files: Files,
          ) -> Navigation | None:
              """Populate LinkReplacer and build path->MkPage mapping for following steps."""
              for file_ in files:
                  assert file_.abs_src_path
                  filename = pathlib.Path(file_.abs_src_path).name
                  url = urllib.parse.unquote(file_.src_uri)
                  self.link_replacer.mapping[filename].append(url)
              return nav
      
          def on_env(
              self,
              env: jinja2.Environment,
              /,
              *,
              config: mknodesconfig.MkNodesConfig,  # type: ignore
              files: Files,
          ) -> jinja2.Environment | None:
              """Add our own info to the MkDocs environment."""
              rope_env = jinjarope.Environment()
              env.globals["mknodes"] = rope_env.globals
              env.filters |= rope_env.filters
              logger.debug("Added macros / filters to MkDocs jinja2 environment.")
              if config.rewrite_theme_templates:
                  assert env.loader
                  env.loader = jinjarope.RewriteLoader(env.loader, rewriteloader.rewrite)
                  logger.debug("Injected Jinja2 Rewrite loader.")
              return env
      
          def on_pre_page(
              self,
              page: Page,
              /,
              *,
              config: MkDocsConfig,
              files: Files,
          ) -> Page | None:
              """During this phase we set the edit paths."""
              node = self.build_info.page_mapping.get(page.file.src_uri)
              edit_path = node._edit_path if isinstance(node, mk.MkPage) else None
              cfg = mkdocsconfig.Config(config)
              if path := cfg.get_edit_url(edit_path):
                  page.edit_url = path
              return page
      
          def on_page_markdown(
              self,
              markdown: str,
              /,
              *,
              page: Page,
              config: MkDocsConfig,
              files: Files,
          ) -> str | None:
              """During this phase links get replaced and `jinja2` stuff get rendered."""
              return self.link_replacer.replace(markdown, page.file.src_uri)
      
          def on_post_build(self, *, config: mknodesconfig.MkNodesConfig) -> None:  # type: ignore
              """Delete the temporary template files."""
              if not config.theme.custom_dir or not config.build_fn:
                  return
              if config.auto_delete_generated_templates:
                  logger.debug("Deleting page templates...")
                  for template in self.build_info.templates:
                      assert template.filename
                      path = pathlib.Path(config.theme.custom_dir) / template.filename
                      path.unlink(missing_ok=True)
      

      on_config

      on_config(config: MkNodesConfig)
      

      Create the project based on MkDocs config.

      Source code in mkdocs_mknodes/plugin/__init__.py
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      def on_config(self, config: mknodesconfig.MkNodesConfig):  # type: ignore
          """Create the project based on MkDocs config."""
          if config.build_folder:
              self.build_folder = pathlib.Path(config.build_folder)
          else:
              self._dir = tempfile.TemporaryDirectory(
                  prefix="mknodes_",
                  ignore_cleanup_errors=True,
              )
              self.build_folder = pathlib.Path(self._dir.name)
              logger.debug("Creating temporary dir %s", self._dir.name)
      
          if not config.build_fn:
              return
          self.linkprovider = linkprovider.LinkProvider(
              base_url=config.site_url or "",
              use_directory_urls=config.use_directory_urls,
              include_stdlib=True,
          )
          self.theme = mk.Theme.get_theme(
              theme_name=config.theme.name or "material",
              data=dict(config.theme),
          )
          git_repo = reporegistry.get_repo(
              str(config.repo_path or "."),
              clone_depth=config.clone_depth,
          )
          self.folderinfo = folderinfo.FolderInfo(git_repo.working_dir)
          self.context = contexts.ProjectContext(
              metadata=self.folderinfo.context,
              git=self.folderinfo.git.context,
              # github=self.folderinfo.github.context,
              theme=self.theme.context,
              links=self.linkprovider,
              env_config=config.get_jinja_config(),
          )
      

      on_env

      on_env(env: Environment, /, *, config: MkNodesConfig, files: Files) -> Environment | None
      

      Add our own info to the MkDocs environment.

      Source code in mkdocs_mknodes/plugin/__init__.py
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      def on_env(
          self,
          env: jinja2.Environment,
          /,
          *,
          config: mknodesconfig.MkNodesConfig,  # type: ignore
          files: Files,
      ) -> jinja2.Environment | None:
          """Add our own info to the MkDocs environment."""
          rope_env = jinjarope.Environment()
          env.globals["mknodes"] = rope_env.globals
          env.filters |= rope_env.filters
          logger.debug("Added macros / filters to MkDocs jinja2 environment.")
          if config.rewrite_theme_templates:
              assert env.loader
              env.loader = jinjarope.RewriteLoader(env.loader, rewriteloader.rewrite)
              logger.debug("Injected Jinja2 Rewrite loader.")
          return env
      

      on_files

      on_files(files: Files, *, config: MkNodesConfig) -> Files
      

      Create the node tree and write files to build folder.

      In this step we aggregate all files and info we need to build the website. This includes:

      • Markdown pages (MkPages)
      • Templates
      • CSS files
      Source code in mkdocs_mknodes/plugin/__init__.py
       89
       90
       91
       92
       93
       94
       95
       96
       97
       98
       99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      def on_files(self, files: Files, *, config: mknodesconfig.MkNodesConfig) -> Files:  # type: ignore
          """Create the node tree and write files to build folder.
      
          In this step we aggregate all files and info we need to build the website.
          This includes:
      
            - Markdown pages (MkPages)
            - Templates
            - CSS files
          """
          if not config.build_fn:
              return files
      
          logger.info("Generating pages...")
          build_fn = config.get_builder()
          self.root = mk.MkNav(context=self.context)
          build_fn(theme=self.theme, root=self.root)
          logger.debug("Finished building page.")
          paths = [
              pathlib.Path(node.resolved_file_path).stem
              for _level, node in self.root.iter_nodes()
              if hasattr(node, "resolved_file_path")
          ]
          self.linkprovider.set_excludes(paths)
      
          # now we add our stuff to the MkDocs build environment
          cfg = mkdocsconfig.Config(config)
      
          logger.info("Updating MkDocs config metadata...")
          cfg.update_from_context(self.root.ctx)
          self.theme.adapt_extras(cfg.extra)
      
          logger.info("Setting up build backends...")
          mkdocs_backend = mkdocsbackend.MkDocsBackend(
              files=files,
              config=config,
              directory=self.build_folder,
          )
      
          markdown_backend = markdownbackend.MarkdownBackend(
              directory=pathlib.Path(config.site_dir) / "src",
              extension=".original",
          )
          collector = buildcollector.BuildCollector(
              backends=[mkdocs_backend, markdown_backend],
              show_page_info=config.show_page_info,
              global_resources=config.global_resources,
              render_by_default=config.render_by_default,
          )
          self.build_info = collector.collect(self.root, self.theme)
          if nav_dict := self.root.nav.to_nav_dict():
              match config.nav:
                  case list():
                      for k, v in nav_dict.items():
                          config.nav.append({k: v})
                  case dict():
                      config.nav |= nav_dict
                  case None:
                      config.nav = nav_dict
          return mkdocs_backend.files
      

      on_nav

      on_nav(nav: Navigation, /, *, config: MkDocsConfig, files: Files) -> Navigation | None
      

      Populate LinkReplacer and build path->MkPage mapping for following steps.

      Source code in mkdocs_mknodes/plugin/__init__.py
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      def on_nav(
          self,
          nav: Navigation,
          /,
          *,
          config: MkDocsConfig,
          files: Files,
      ) -> Navigation | None:
          """Populate LinkReplacer and build path->MkPage mapping for following steps."""
          for file_ in files:
              assert file_.abs_src_path
              filename = pathlib.Path(file_.abs_src_path).name
              url = urllib.parse.unquote(file_.src_uri)
              self.link_replacer.mapping[filename].append(url)
          return nav
      

      on_page_markdown

      on_page_markdown(
          markdown: str, /, *, page: Page, config: MkDocsConfig, files: Files
      ) -> str | None
      

      During this phase links get replaced and jinja2 stuff get rendered.

      Source code in mkdocs_mknodes/plugin/__init__.py
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      def on_page_markdown(
          self,
          markdown: str,
          /,
          *,
          page: Page,
          config: MkDocsConfig,
          files: Files,
      ) -> str | None:
          """During this phase links get replaced and `jinja2` stuff get rendered."""
          return self.link_replacer.replace(markdown, page.file.src_uri)
      

      on_post_build

      on_post_build(*, config: MkNodesConfig) -> None
      

      Delete the temporary template files.

      Source code in mkdocs_mknodes/plugin/__init__.py
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      def on_post_build(self, *, config: mknodesconfig.MkNodesConfig) -> None:  # type: ignore
          """Delete the temporary template files."""
          if not config.theme.custom_dir or not config.build_fn:
              return
          if config.auto_delete_generated_templates:
              logger.debug("Deleting page templates...")
              for template in self.build_info.templates:
                  assert template.filename
                  path = pathlib.Path(config.theme.custom_dir) / template.filename
                  path.unlink(missing_ok=True)
      

      on_pre_page

      on_pre_page(page: Page, /, *, config: MkDocsConfig, files: Files) -> Page | None
      

      During this phase we set the edit paths.

      Source code in mkdocs_mknodes/plugin/__init__.py
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      def on_pre_page(
          self,
          page: Page,
          /,
          *,
          config: MkDocsConfig,
          files: Files,
      ) -> Page | None:
          """During this phase we set the edit paths."""
          node = self.build_info.page_mapping.get(page.file.src_uri)
          edit_path = node._edit_path if isinstance(node, mk.MkPage) else None
          cfg = mkdocsconfig.Config(config)
          if path := cfg.get_edit_url(edit_path):
              page.edit_url = path
          return page
      

      on_startup

      on_startup(*, command: CommandStr, dirty: bool)
      

      Activates new-style MkDocs plugin lifecycle.

      Source code in mkdocs_mknodes/plugin/__init__.py
      49
      50
      def on_startup(self, *, command: CommandStr, dirty: bool):
          """Activates new-style MkDocs plugin lifecycle."""