{"id":42212890,"url":"https://github.com/foliant-docs/foliantcontrib.escapecode","last_synced_at":"2026-01-27T01:06:52.581Z","repository":{"id":57431772,"uuid":"197218601","full_name":"foliant-docs/foliantcontrib.escapecode","owner":"foliant-docs","description":"Preprocessors for Foliant to escape/unescape raw content, e.g. code blocks","archived":false,"fork":false,"pushed_at":"2025-09-29T14:01:09.000Z","size":72,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-12-26T01:58:06.242Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/foliant-docs.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-16T15:18:50.000Z","updated_at":"2025-09-29T14:00:57.000Z","dependencies_parsed_at":"2023-12-13T10:28:13.340Z","dependency_job_id":"5039c533-4035-4b6a-b01d-39e3cc46d7f5","html_url":"https://github.com/foliant-docs/foliantcontrib.escapecode","commit_stats":{"total_commits":21,"total_committers":2,"mean_commits":10.5,"dds":0.09523809523809523,"last_synced_commit":"954c4a234485bd604113f6a3b67dba7f316eebdb"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/foliant-docs/foliantcontrib.escapecode","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foliant-docs%2Ffoliantcontrib.escapecode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foliant-docs%2Ffoliantcontrib.escapecode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foliant-docs%2Ffoliantcontrib.escapecode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foliant-docs%2Ffoliantcontrib.escapecode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foliant-docs","download_url":"https://codeload.github.com/foliant-docs/foliantcontrib.escapecode/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foliant-docs%2Ffoliantcontrib.escapecode/sbom","scorecard":{"id":406073,"data":{"date":"2025-08-11","repo":{"name":"github.com/foliant-docs/foliantcontrib.escapecode","commit":"c23d1ad7c0778048be9aae11e9eb7fb9b49fb93f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":3,"reason":"Found 5/16 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/python-publish.yml:17","Warn: no topLevel permission defined: .github/workflows/python-test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-test.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/foliant-docs/foliantcontrib.escapecode/python-test.yml/master?enable=pin","Warn: pipCommand not pinned by hash: test.sh:4","Warn: pipCommand not pinned by hash: test.sh:6","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:34","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:35","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:36","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:52","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:53","Warn: pipCommand not pinned by hash: .github/workflows/python-test.yml:21","Warn: pipCommand not pinned by hash: .github/workflows/python-test.yml:22","Warn: pipCommand not pinned by hash: .github/workflows/python-test.yml:23","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of  10 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/python-publish.yml:40"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T21:17:38.525Z","repository_id":57431772,"created_at":"2025-08-18T21:17:38.526Z","updated_at":"2025-08-18T21:17:38.526Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28794640,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:49:50.245Z","status":"ssl_error","status_checked_at":"2026-01-26T21:48:29.455Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-27T01:06:51.945Z","updated_at":"2026-01-27T01:06:52.571Z","avatar_url":"https://github.com/foliant-docs.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![](https://img.shields.io/pypi/v/foliantcontrib.escapecode.svg)](https://pypi.org/project/foliantcontrib.escapecode/) [![](https://img.shields.io/github/v/tag/foliant-docs/foliantcontrib.escapecode.svg?label=GitHub)](https://github.com/foliant-docs/foliantcontrib.escapecode)\n\n# EscapeCode and UnescapeCode\n\n\u003e **Warning**\n\u003e\n\u003e Starting from version `1.0.5`, the preprocessor uses the [marko parser](https://github.com/frostming/marko/tree/6ee598746bc9a76e62a158c5aa5226a3d65c0864).\n\u003e This is necessary to more accurately identify code blocks nested in other markdown elements. But using the parser imposes the following restrictions:\n\u003e - the indent of the list items will be converted to 2 spaces after processing by the preprocessor.\n\u003e\n\u003e If your documentation does not use deep nesting of markdown elements, you may want to use version `1.0.4`, as it is more stable. For install version `1.0.4`, run:\n\u003e ```\n\u003e pip install foliantcontrib.escapecode==1.0.4\n\u003e ```\n\nEscapeCode and UnescapeCode preprocessors work in pair.\n\nEscapeCode finds in the source Markdown content the parts that should not be modified by any next preprocessors. Examples of content that should be left raw: fence code blocks, pre code blocks, inline code.\n\nEscapeCode replaces these raw content parts with pseudo-XML tags recognized by UnescapeCode preprocessor.\n\nEscapeCode saves raw content parts into files. Later, UnescapeCode restores this content from files.\n\nAlso, before the replacement, EscapeCode normalizes the source Markdown content to unify and simplify further operations. The preprocessor replaces `CRLF` with `LF`, removes excessive whitespace characters, provides trailing newline, etc.\n\n## Installation\n\nTo install EscapeCode and UnescapeCode preprocessors, run:\n\n```bash\n$ pip install foliantcontrib.escapecode\n```\n\nSee more details below.\n\n## Integration with Foliant and Includes\n\nYou may call EscapeCode and UnescapeCode explicitly, but these preprocessors are integrated with Foliant core (since version 1.0.10) and with Includes preprocessor (since version 1.1.1).\n\nThe `escape_code` project’s config option, if set to `true`, provides applying EscapeCode before all other preprocessors, and applying UnescapeCode after all other preprocessors. Also this option tells Includes preprocessor to apply EscapeCode to each included file.\n\nIn this mode EscapeCode and UnescapeCode preprocessors deprecate _unescape preprocessor.\n\n    \u003e    **Note**\n    \u003e\n    \u003e    The preprocessor _unescape is a part of Foliant core. It allows to use pseudo-XML tags in code examples. If you want an opening tag not to be interpreted by any preprocessor, precede this tag with the `\u003c` character. The preprocessor _unescape applies after all other preprocessors and removes such characters.\n\nConfig example:\n\n```yaml\ntitle: My Awesome Project\n\nchapters:\n    - index.md\n    ...\n\nescape_code: true\n\npreprocessors:\n    ...\n    - includes\n    ...\n...\n```\n\nIf the `escape_code` option isn’t used or set to `false`, backward compatibility mode is involved. In this mode EscapeCode and UnescapeCode aren’t applied automatically, but _unescape preprocessor is applied.\n\nIn more complicated case, you may pass some custom options to EscapeCode preprocessor:\n\n```\nescape_code:\n    options:\n        ...\n```\n\nCustom options available in EscapeCode since version 1.0.2. Foliant core supports passing custom options to EscapeCode preprocessor as the value of `escape_code.options` parameter since version 1.0.11. Options are described below.\n\nThe Python package that includes EscapeCode and UnescapeCode preprocessors is the dependence of Includes preprocessor since version 1.1.1. At the same time this package isn’t a dependence of Foliant core. To use `escape_code` config option in Foliant core, you have to install the package with EscapeCode and UnescapeCode preprocessors separately.\n\n## Explicit Enabling\n\nYou may not want to use the `escape_code` option and call the preprocessors explicitly:\n\n```yaml\npreprocessors:\n    - escapecode      # usually the first list item\n    ...\n    - unescapecode    # usually the last list item\n```\n\nBoth preprocessors allow to override the path to the directory that is used to store temporary files:\n\n```yaml\npreprocessors:\n    - escapecode:\n        cache_dir: !path .escapecodecache\n    ...\n    - unescapecode:\n        cache_dir: !path .escapecodecache\n```\n\nThe default values are shown in this example. EscapeCode and related UnescapeCode must work with the same cache directory.\n\nNote that if you use Includes preprocessor, and the included content doesn’t belong to the current Foliant project, there’s no way to escape raw parts of this content before Includes preprocessor is applied.\n\n## Config\n\nSince version 1.0.2, EscapeCode preprocessor supports the option `actions` in additional to `cache_dir`.\n\nThe value of `actions` options should be a list of acceptable actions. By default, the following list is used:\n\n```yaml\nactions:\n    - normalize\n    - escape:\n        - fence_blocks\n        - pre_blocks\n        - inline_code\n```\n\nThis default list may be overridden. For example:\n\n```yaml\nactions:\n    - normalize\n    - escape:\n        - fence_blocks\n        - inline_code\n        - tags:\n            - plantuml\n            - seqdiag\n        - comments\n    - pattern_override:\n        inline_code: '\\\u003cpattern_override_inline_code_\\d+\\\u003e'\n```\n\nMeanings of parameters:\n\n* `normalize`—perform normalization;\n* `escape`—perform escaping of certain types of raw content:\n    * `fence_blocks`—fence code blocks;\n    * `pre_blocks`—pre code blocks;\n    * `inline_code`—inline code;\n    * `comments`—HTML-style comments, also usual for Markdown;\n    * `tags`—content of certain tags with the tags themselves, for example `plantuml` for `\u003cplantuml\u003e...\u003c/plantuml\u003e`;\n    * `frontmatter`—the part with metadata at the beginning of the Markdown file, supports YAML `---` and TOML `+++` formats.\n* `pattern_override`—a regular expression that will not be escaped:\n    * `pre_blocks`—the lines of the pre code block containing this template will not be escaped;\n    * `inline_code`—pattern for inline code;\n    * `comments`—pattern for HTML-style comments, also usual for Markdown.\n\n## Usage\n\nBelow you can see an example of Markdown content with code blocks and inline code.\n\n    # Heading\n\n    Text that contains some `inline code`. Text containing `\u003cpattern_override_inline_code_01\u003e`.\n\n    Below is a fence code block, language is optional:\n\n    ```python\n    import this\n    ```\n\n    One more fence code block:\n\n    ~~~\n    # This is a comment that should not be interpreted as a heading\n\n    print('Hello World')\n    ~~~\n\n    One more fence code block in list:\n\n    - first list item\n\n      ```python\n      import this\n      ```\n\n    - second list item\n\n    And this is a pre code block:\n\n        mov dx, hello;\n        mov ah, 9;\n        int 21h;\n\nThe preprocessor EscapeCode with default behavior will do the following replacements:\n\n    # Heading\n\n    Text that contains some \u003c\u003cescaped hash=\"2bb20aeb00314e915ecfefd86d26f46a\"\u003e\u003c/escaped\u003e. Text containing `\u003cpattern_override_inline_code_01\u003e`.\n\n    Below is a fence code block, language is optional:\n\n    \u003c\u003cescaped hash=\"15e1e46a75ef29eb760f392bb2df4ebb\"\u003e\u003c/escaped\u003e\n\n    One more fence code block:\n\n    \u003c\u003cescaped hash=\"91c3d3da865e24c33c4b366760c99579\"\u003e\u003c/escaped\u003e\n\n    One more fence code block in list:\n\n    - first list item\n\n      \u003c\u003cescaped hash=\"15e1e46a75ef29eb760f392bb2df4ebb\"\u003e\u003c/escaped\u003e\n\n    - second list item\n\n    And this is a pre code block:\n\n        \u003c\u003cescaped hash=\"644952599350cd6676697cc95f52b999\"\u003e\u003c/escaped\u003e\n        \u003c\u003cescaped hash=\"e24afa46b55281fcf0b4b0e38d10419c\"\u003e\u003c/escaped\u003e\n        \u003c\u003cescaped hash=\"6bf655ac3a98b481ef3d33ac8dfcd93f\"\u003e\u003c/escaped\u003e\n\nEscaped content parts will be saved into files located in the cache directory. The names of the files correspond the values of the `hash` attributes. For example, that’s the content of the file `15e1e46a75ef29eb760f392bb2df4ebb.md`:\n\n    ```python\n    import this\n    ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoliant-docs%2Ffoliantcontrib.escapecode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoliant-docs%2Ffoliantcontrib.escapecode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoliant-docs%2Ffoliantcontrib.escapecode/lists"}