From 97106ae88cc3dfac71b4e84097b69857fc91536d Mon Sep 17 00:00:00 2001 From: Jeremy Maitin-Shepard Date: Sat, 22 May 2021 10:17:09 -0700 Subject: [PATCH] Use new documentation theme based on mkdocs-material PiperOrigin-RevId: 375270777 Change-Id: Ie97052d1f0bcde1cbc2aec7cdd79cbb3eabdbbf8 --- .style.yapf | 2 +- LICENSE | 63 + WORKSPACE | 8 +- docs/BUILD | 27 +- .../sphinx_rtd_theme_table_word_wrap_fix.css | 32 - docs/_templates/autosummary/base.rst | 5 - docs/_templates/autosummary/class.rst | 30 - docs/_templates/autosummary/module.rst | 39 - docs/_templates/autosummary_root_module.rst | 48 - docs/build_docs.py | 64 +- docs/conf.py | 85 +- docs/context.rst | 2 + docs/index.rst | 87 +- docs/index_space.rst | 59 +- docs/python/api/index.rst | 29 +- docs/python/index.rst | 11 - docs/python/indexing.rst | 8 +- docs/spec.rst | 4 +- docs/tensorstore_schema.yml | 7 +- docs/tensorstore_sphinx_ext/BUILD | 20 +- docs/tensorstore_sphinx_ext/autodoc.py | 447 + docs/tensorstore_sphinx_ext/autosummary.py | 1521 +++- .../jsonschema_sphinx.py | 23 +- docs/tensorstore_sphinx_ext/mathjax.py | 52 + docs/tensorstore_sphinx_ext/sphinx_utils.py | 20 + docs/tensorstore_sphinx_material/.style.yapf | 2 + docs/tensorstore_sphinx_material/BUILD | 163 + docs/tensorstore_sphinx_material/LICENSE | 51 + .../MKDOCS_MATERIAL_MERGE_BASE | 1 + docs/tensorstore_sphinx_material/README.md | 39 + docs/tensorstore_sphinx_material/build_css.js | 78 + docs/tensorstore_sphinx_material/build_js.js | 24 + docs/tensorstore_sphinx_material/icons.bzl | 39 + .../merge_from_mkdocs_material.py | 236 + .../minify_html.js | 41 + .../nodejs_rules.bzl | 8 + .../package-lock.json | 7774 +++++++++++++++++ docs/tensorstore_sphinx_material/package.json | 93 + .../sphinx_material/__init__.py | 255 + .../sphinx_material/_version.py | 556 ++ .../sphinx_material/apidoc_formatting.py | 248 + .../sphinx_material/autodoc_property_type.py | 95 + .../sphinx_material/inlinesyntaxhighlight.py | 108 + .../sphinx_material/nav_adapt.py | 396 + .../sphinx_material/object_toc.py | 149 + .../sphinx_material/postprocess_html.py | 52 + .../sphinx_material/search_adapt.py | 70 + .../sphinx_material/theme.conf | 70 + .../src/assets/javascripts/_/index.ts | 139 + .../src/assets/javascripts/actions/_/index.ts | 76 + .../javascripts/actions/anchor/index.ts | 73 + .../javascripts/actions/dialog/index.ts | 62 + .../javascripts/actions/header/_/index.ts | 48 + .../javascripts/actions/header/index.ts | 24 + .../javascripts/actions/header/title/index.ts | 48 + .../src/assets/javascripts/actions/index.ts | 31 + .../javascripts/actions/search/index.ts | 24 + .../javascripts/actions/search/query/index.ts | 50 + .../actions/search/result/index.ts | 91 + .../javascripts/actions/sidebar/index.ts | 88 + .../javascripts/actions/source/index.ts | 49 + .../assets/javascripts/actions/tabs/index.ts | 48 + .../assets/javascripts/actions/top/index.ts | 48 + .../javascripts/browser/document/index.ts | 48 + .../javascripts/browser/element/_/.eslintrc | 5 + .../javascripts/browser/element/_/index.ts | 142 + .../browser/element/focus/index.ts | 67 + .../javascripts/browser/element/index.ts | 27 + .../browser/element/offset/index.ts | 112 + .../browser/element/selection/index.ts | 39 + .../javascripts/browser/element/size/index.ts | 154 + .../src/assets/javascripts/browser/index.ts | 30 + .../javascripts/browser/keyboard/index.ts | 108 + .../javascripts/browser/location/_/index.ts | 61 + .../browser/location/hash/index.ts | 85 + .../javascripts/browser/location/index.ts | 24 + .../assets/javascripts/browser/media/index.ts | 92 + .../javascripts/browser/request/index.ts | 90 + .../javascripts/browser/toggle/index.ts | 98 + .../javascripts/browser/viewport/_/index.ts | 121 + .../javascripts/browser/viewport/index.ts | 25 + .../browser/viewport/offset/index.ts | 84 + .../browser/viewport/size/index.ts | 67 + .../src/assets/javascripts/bundle.ts | 238 + .../assets/javascripts/components/_/index.ts | 128 + .../javascripts/components/content/_/index.ts | 89 + .../components/content/code/index.ts | 183 + .../components/content/details/index.ts | 115 + .../javascripts/components/content/index.ts | 26 + .../components/content/table/index.ts | 70 + .../javascripts/components/dialog/index.ts | 139 + .../javascripts/components/header/_/index.ts | 206 + .../javascripts/components/header/index.ts | 24 + .../components/header/title/index.ts | 148 + .../assets/javascripts/components/index.ts | 34 + .../javascripts/components/main/index.ts | 124 + .../javascripts/components/palette/index.ts | 148 + .../javascripts/components/search/_/index.ts | 160 + .../javascripts/components/search/index.ts | 25 + .../components/search/query/index.ts | 140 + .../components/search/result/index.ts | 159 + .../javascripts/components/sidebar/index.ts | 165 + .../javascripts/components/source/_/index.ts | 125 + .../components/source/facts/_/index.ts | 88 + .../components/source/facts/github/index.ts | 95 + .../components/source/facts/gitlab/index.ts | 55 + .../components/source/facts/index.ts | 25 + .../javascripts/components/source/index.ts | 24 + .../javascripts/components/tabs/index.ts | 143 + .../javascripts/components/toc/index.ts | 275 + .../javascripts/components/top/index.ts | 160 + .../integrations/clipboard/index.ts | 58 + .../assets/javascripts/integrations/index.ts | 26 + .../integrations/instant/.eslintrc | 6 + .../javascripts/integrations/instant/index.ts | 397 + .../integrations/search/_/.eslintrc | 6 + .../integrations/search/_/index.ts | 96 + .../integrations/search/document/index.ts | 41 + .../integrations/search/highlighter/index.ts | 84 + .../javascripts/integrations/search/index.ts | 26 + .../integrations/search/query/_/.eslintrc | 5 + .../integrations/search/query/_/index.ts | 40 + .../integrations/search/query/index.ts | 23 + .../search/query/transform/.eslintrc | 5 + .../search/query/transform/index.ts | 71 + .../javascripts/integrations/version/index.ts | 48 + .../patches/indeterminate/index.ts | 84 + .../src/assets/javascripts/patches/index.ts | 25 + .../javascripts/patches/scrollfix/index.ts | 99 + .../javascripts/patches/scrolllock/index.ts | 86 + .../src/assets/javascripts/sphinx_search.ts | 762 ++ .../javascripts/templates/clipboard/index.tsx | 45 + .../src/assets/javascripts/templates/index.ts | 27 + .../javascripts/templates/search/index.tsx | 149 + .../javascripts/templates/source/index.tsx | 47 + .../javascripts/templates/table/index.tsx | 44 + .../javascripts/templates/version/index.tsx | 97 + .../assets/javascripts/utilities/h/.eslintrc | 6 + .../assets/javascripts/utilities/h/index.ts | 116 + .../src/assets/javascripts/utilities/index.ts | 24 + .../javascripts/utilities/string/index.ts | 90 + .../src/assets/javascripts/workers/search.ts | 23 + .../src/assets/stylesheets/_config.scss | 42 + .../src/assets/stylesheets/main.scss | 79 + .../src/assets/stylesheets/main/_api.scss | 216 + .../src/assets/stylesheets/main/_colors.scss | 104 + .../src/assets/stylesheets/main/_icons.scss | 37 + .../assets/stylesheets/main/_modifiers.scss | 63 + .../src/assets/stylesheets/main/_reset.scss | 113 + .../src/assets/stylesheets/main/_shame.scss | 25 + .../src/assets/stylesheets/main/_sphinx.scss | 82 + .../src/assets/stylesheets/main/_typeset.scss | 641 ++ .../main/extensions/markdown/_admonition.scss | 197 + .../main/extensions/markdown/_footnotes.scss | 153 + .../main/extensions/markdown/_toc.scss | 118 + .../main/extensions/pymdownx/_arithmatex.scss | 48 + .../main/extensions/pymdownx/_critic.scss | 81 + .../main/extensions/pymdownx/_details.scss | 122 + .../main/extensions/pymdownx/_emoji.scss | 45 + .../main/extensions/pymdownx/_highlight.scss | 275 + .../main/extensions/pymdownx/_keys.scss | 120 + .../main/extensions/pymdownx/_tabbed.scss | 123 + .../main/extensions/pymdownx/_tasklist.scss | 91 + .../stylesheets/main/layout/_announce.scss | 44 + .../assets/stylesheets/main/layout/_base.scss | 184 + .../stylesheets/main/layout/_clipboard.scss | 103 + .../stylesheets/main/layout/_content.scss | 119 + .../stylesheets/main/layout/_dialog.scss | 73 + .../stylesheets/main/layout/_footer.scss | 211 + .../assets/stylesheets/main/layout/_form.scss | 83 + .../stylesheets/main/layout/_header.scss | 272 + .../assets/stylesheets/main/layout/_hero.scss | 65 + .../assets/stylesheets/main/layout/_nav.scss | 687 ++ .../stylesheets/main/layout/_search.scss | 738 ++ .../stylesheets/main/layout/_select.scss | 126 + .../stylesheets/main/layout/_sidebar.scss | 191 + .../stylesheets/main/layout/_source.scss | 203 + .../assets/stylesheets/main/layout/_tabs.scss | 113 + .../assets/stylesheets/main/layout/_top.scss | 66 + .../stylesheets/main/layout/_version.scss | 148 + .../src/assets/stylesheets/palette.scss | 40 + .../assets/stylesheets/palette/_accent.scss | 60 + .../assets/stylesheets/palette/_primary.scss | 194 + .../assets/stylesheets/palette/_scheme.scss | 96 + .../assets/stylesheets/utilities/_break.scss | 215 + .../stylesheets/utilities/_convert.scss | 77 + .../tensorstore_sphinx_material/src/base.html | 400 + .../src/layout.html | 2 + .../tensorstore_sphinx_material/src/main.html | 23 + .../src/partials/footer.html | 118 + .../src/partials/header.html | 140 + .../src/partials/hero.html | 8 + .../src/partials/integrations/analytics.html | 55 + .../src/partials/integrations/disqus.html | 46 + .../src/partials/javascripts/base.html | 39 + .../src/partials/javascripts/palette.html | 29 + .../src/partials/language.html | 28 + .../src/partials/languages/af.html | 45 + .../src/partials/languages/ar.html | 46 + .../src/partials/languages/bg.html | 52 + .../src/partials/languages/bn.html | 50 + .../src/partials/languages/ca.html | 44 + .../src/partials/languages/cs.html | 44 + .../src/partials/languages/da.html | 45 + .../src/partials/languages/de.html | 49 + .../src/partials/languages/en.html | 58 + .../src/partials/languages/eo.html | 50 + .../src/partials/languages/es.html | 54 + .../src/partials/languages/et.html | 44 + .../src/partials/languages/fa.html | 46 + .../src/partials/languages/fi.html | 45 + .../src/partials/languages/fr.html | 45 + .../src/partials/languages/gl.html | 45 + .../src/partials/languages/gr.html | 45 + .../src/partials/languages/he.html | 46 + .../src/partials/languages/hi.html | 45 + .../src/partials/languages/hr.html | 44 + .../src/partials/languages/hu.html | 54 + .../src/partials/languages/id.html | 45 + .../src/partials/languages/is.html | 51 + .../src/partials/languages/it.html | 45 + .../src/partials/languages/ja.html | 56 + .../src/partials/languages/ka.html | 50 + .../src/partials/languages/kr.html | 44 + .../src/partials/languages/my.html | 50 + .../src/partials/languages/nl.html | 45 + .../src/partials/languages/nn.html | 45 + .../src/partials/languages/no.html | 45 + .../src/partials/languages/pl.html | 54 + .../src/partials/languages/pt.html | 45 + .../src/partials/languages/ro.html | 45 + .../src/partials/languages/ru.html | 45 + .../src/partials/languages/sh.html | 43 + .../src/partials/languages/si.html | 52 + .../src/partials/languages/sk.html | 44 + .../src/partials/languages/sl.html | 44 + .../src/partials/languages/sr.html | 45 + .../src/partials/languages/sv.html | 54 + .../src/partials/languages/th.html | 45 + .../src/partials/languages/tr.html | 45 + .../src/partials/languages/uk.html | 45 + .../src/partials/languages/vi.html | 45 + .../src/partials/languages/zh-Hant.html | 48 + .../src/partials/languages/zh-TW.html | 48 + .../src/partials/languages/zh.html | 48 + .../src/partials/logo.html | 31 + .../src/partials/nav-item.html | 135 + .../src/partials/nav.html | 68 + .../src/partials/palette.html | 66 + .../src/partials/search.html | 68 + .../src/partials/social.html | 42 + .../src/partials/source-date.html | 24 + .../src/partials/source-file.html | 44 + .../src/partials/source.html | 39 + .../src/partials/tabs-item.html | 56 + .../src/partials/tabs.html | 39 + .../src/partials/toc-item.html | 39 + .../src/partials/toc.html | 43 + .../tensorstore_sphinx_material/tsconfig.json | 38 + .../typings/_/index.d.ts | 77 + .../typings/svgo/index.d.ts | 68 + external.bzl | 7 +- external2.bzl | 32 + python/tensorstore/__init__.py | 194 +- python/tensorstore/array_type_caster.h | 2 +- python/tensorstore/context.cc | 212 +- python/tensorstore/data_type.cc | 20 +- python/tensorstore/downsample.cc | 18 + python/tensorstore/future.cc | 90 +- python/tensorstore/index.h | 4 +- python/tensorstore/json_type_caster.h | 2 +- python/tensorstore/tests/context_test.py | 8 +- python/tensorstore/transaction.cc | 85 +- python/tensorstore/write_futures.cc | 28 +- tensorstore/driver/downsample/index.rst | 2 + tensorstore/driver/n5/driver_test.cc | 2 +- tensorstore/driver/zarr/driver_test.cc | 2 +- tensorstore/examples/python/beam/BUILD | 1 - tensorstore/index_space/dim_expression.h | 3 +- tensorstore/index_space/json.h | 2 +- tensorstore/transaction_test.cc | 2 - .../patches/no-Werror.diff | 12 + .../com_google_boringssl/workspace.bzl | 8 +- third_party/pypa/generate_workspace.py | 75 +- third_party/pypa/workspace.bzl | 130 +- 285 files changed, 32822 insertions(+), 620 deletions(-) delete mode 100644 docs/_static/sphinx_rtd_theme_table_word_wrap_fix.css delete mode 100644 docs/_templates/autosummary/base.rst delete mode 100644 docs/_templates/autosummary/class.rst delete mode 100644 docs/_templates/autosummary/module.rst delete mode 100644 docs/_templates/autosummary_root_module.rst delete mode 100644 docs/python/index.rst create mode 100644 docs/tensorstore_sphinx_ext/autodoc.py create mode 100644 docs/tensorstore_sphinx_ext/mathjax.py create mode 100644 docs/tensorstore_sphinx_material/.style.yapf create mode 100644 docs/tensorstore_sphinx_material/BUILD create mode 100644 docs/tensorstore_sphinx_material/LICENSE create mode 100644 docs/tensorstore_sphinx_material/MKDOCS_MATERIAL_MERGE_BASE create mode 100644 docs/tensorstore_sphinx_material/README.md create mode 100644 docs/tensorstore_sphinx_material/build_css.js create mode 100644 docs/tensorstore_sphinx_material/build_js.js create mode 100644 docs/tensorstore_sphinx_material/icons.bzl create mode 100755 docs/tensorstore_sphinx_material/merge_from_mkdocs_material.py create mode 100644 docs/tensorstore_sphinx_material/minify_html.js create mode 100644 docs/tensorstore_sphinx_material/nodejs_rules.bzl create mode 100644 docs/tensorstore_sphinx_material/package-lock.json create mode 100644 docs/tensorstore_sphinx_material/package.json create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/__init__.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/_version.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/apidoc_formatting.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/autodoc_property_type.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/inlinesyntaxhighlight.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/nav_adapt.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/object_toc.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/postprocess_html.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/search_adapt.py create mode 100644 docs/tensorstore_sphinx_material/sphinx_material/theme.conf create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/anchor/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/dialog/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/title/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/query/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/result/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/sidebar/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/source/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/tabs/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/actions/top/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/document/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/focus/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/offset/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/selection/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/size/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/keyboard/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/hash/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/media/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/request/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/toggle/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/offset/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/size/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/bundle.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/code/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/details/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/table/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/dialog/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/title/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/main/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/palette/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/query/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/result/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/sidebar/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/github/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/gitlab/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/tabs/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/toc/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/components/top/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/clipboard/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/document/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/highlighter/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/version/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/patches/indeterminate/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/patches/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrollfix/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrolllock/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/sphinx_search.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/clipboard/index.tsx create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/search/index.tsx create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/source/index.tsx create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/table/index.tsx create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/templates/version/index.tsx create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/utilities/h/.eslintrc create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/utilities/h/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/utilities/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/utilities/string/index.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/javascripts/workers/search.ts create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/_config.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_api.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_colors.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_icons.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_modifiers.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_reset.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_shame.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_sphinx.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/_typeset.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/markdown/_admonition.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/markdown/_footnotes.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/markdown/_toc.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_critic.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_details.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_keys.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_announce.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_base.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_clipboard.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_content.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_dialog.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_footer.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_form.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_header.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_hero.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_nav.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_search.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_select.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_sidebar.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_source.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_tabs.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_top.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/main/layout/_version.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/palette.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/palette/_accent.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/palette/_primary.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/palette/_scheme.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/utilities/_break.scss create mode 100644 docs/tensorstore_sphinx_material/src/assets/stylesheets/utilities/_convert.scss create mode 100644 docs/tensorstore_sphinx_material/src/base.html create mode 100644 docs/tensorstore_sphinx_material/src/layout.html create mode 100644 docs/tensorstore_sphinx_material/src/main.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/footer.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/header.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/hero.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/integrations/analytics.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/integrations/disqus.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/javascripts/base.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/javascripts/palette.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/language.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/af.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ar.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/bg.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/bn.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ca.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/cs.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/da.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/de.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/en.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/eo.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/es.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/et.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/fa.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/fi.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/fr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/gl.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/gr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/he.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/hi.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/hr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/hu.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/id.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/is.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/it.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ja.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ka.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/kr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/my.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/nl.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/nn.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/no.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/pl.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/pt.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ro.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/ru.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/sh.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/si.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/sk.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/sl.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/sr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/sv.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/th.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/tr.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/uk.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/vi.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/zh-Hant.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/zh-TW.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/languages/zh.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/logo.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/nav-item.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/nav.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/palette.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/search.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/social.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/source-date.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/source-file.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/source.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/tabs-item.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/tabs.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/toc-item.html create mode 100644 docs/tensorstore_sphinx_material/src/partials/toc.html create mode 100644 docs/tensorstore_sphinx_material/tsconfig.json create mode 100644 docs/tensorstore_sphinx_material/typings/_/index.d.ts create mode 100644 docs/tensorstore_sphinx_material/typings/svgo/index.d.ts create mode 100644 external2.bzl create mode 100644 third_party/com_google_boringssl/patches/no-Werror.diff diff --git a/.style.yapf b/.style.yapf index c088b0997..883f33f88 100644 --- a/.style.yapf +++ b/.style.yapf @@ -1,2 +1,2 @@ [style] -based_on_style: chromium +based_on_style: yapf diff --git a/LICENSE b/LICENSE index 93292e804..60895836d 100644 --- a/LICENSE +++ b/LICENSE @@ -228,3 +228,66 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +Files: docs/tensorstore_sphinx_ext/autodoc.py + +Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------------------- + +Files: third_party/com_google_snappy/bundled.BUILD.bazel + +Copyright 2011 Google Inc. All Rights Reserved. +Author: sesse@google.com (Steinar H. Gunderson) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/WORKSPACE b/WORKSPACE index c680cd3e3..6a065c4e5 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,7 +1,13 @@ -workspace(name = "com_github_google_tensorstore") +workspace( + name = "com_github_google_tensorstore", +) load("//:external.bzl", "tensorstore_dependencies") tensorstore_dependencies() register_toolchains("@local_config_python//:py_toolchain") + +load("//:external2.bzl", "tensorstore_dependencies2") + +tensorstore_dependencies2() diff --git a/docs/BUILD b/docs/BUILD index f4472ee10..b4c60f925 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -1,5 +1,6 @@ load("//python/tensorstore:pytype.bzl", "pytype_strict_binary", "pytype_strict_test") load("//python/tensorstore:pytest.bzl", "tensorstore_pytest_test") +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") package( default_visibility = ["//visibility:public"], @@ -13,8 +14,6 @@ filegroup( "conf.py", "_templates/logo.svg", ] + glob([ - "_templates/**", - "_static/**", "**/*.rst", "**/*.yml", "intersphinx_inv/**", @@ -52,20 +51,42 @@ genrule( ], ) +bool_flag( + name = "no_minify", + build_setting_default = False, +) + +config_setting( + name = "no_minify_setting", + flag_values = { + ":no_minify": "True", + }, + visibility = ["//docs:__subpackages__"], +) + pytype_strict_test( name = "build_docs", srcs = ["build_docs.py"], + args = [ + "--minify-tool=$(location //docs/tensorstore_sphinx_material:minify_html)", + ] + select({ + ":no_minify_setting": ["--no-minify"], + "//conditions:default": [], + }), data = [ ":doc_sources", + "//docs/tensorstore_sphinx_material:minify_html", ], python_version = "PY3", tags = ["manual"], deps = [ + "//docs/tensorstore_sphinx_ext:autodoc", "//docs/tensorstore_sphinx_ext:autosummary", "//docs/tensorstore_sphinx_ext:jsonschema_sphinx", + "//docs/tensorstore_sphinx_ext:mathjax", + "//docs/tensorstore_sphinx_material", "//python/tensorstore", "@pypa_sphinx//:sphinx", - "@pypa_sphinx_rtd_theme//:sphinx_rtd_theme", ], ) diff --git a/docs/_static/sphinx_rtd_theme_table_word_wrap_fix.css b/docs/_static/sphinx_rtd_theme_table_word_wrap_fix.css deleted file mode 100644 index 30f05d0b9..000000000 --- a/docs/_static/sphinx_rtd_theme_table_word_wrap_fix.css +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2020 The TensorStore Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Fix table wrapping rpoblem in sphinx_rtd_theme: - https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html -*/ -/* override table width restrictions */ -@media screen and (min-width: 767px) { - - .wy-table-responsive table td { - /* !important prevents the common CSS stylesheets from overriding - this as on RTD they are loaded after this stylesheet */ - white-space: normal !important; - } - - .wy-table-responsive { - overflow: visible !important; - } -} diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst deleted file mode 100644 index b7556ebf7..000000000 --- a/docs/_templates/autosummary/base.rst +++ /dev/null @@ -1,5 +0,0 @@ -{{ fullname | escape | underline}} - -.. currentmodule:: {{ module }} - -.. auto{{ objtype }}:: {{ objname }} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst deleted file mode 100644 index f61d0e6ed..000000000 --- a/docs/_templates/autosummary/class.rst +++ /dev/null @@ -1,30 +0,0 @@ -{{ fullname | escape | underline}} - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - - {% block methods %} - - {% if methods %} - .. rubric:: Methods - - .. autosummary:: - :toctree: . - {% for item in methods %} - ~{{ name }}.{{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block attributes %} - {% if attributes %} - .. rubric:: Attributes - - .. autosummary:: - :toctree: . - {% for item in attributes %} - ~{{ name }}.{{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst deleted file mode 100644 index 63061feee..000000000 --- a/docs/_templates/autosummary/module.rst +++ /dev/null @@ -1,39 +0,0 @@ -{{ fullname | escape | underline}} - -.. automodule:: {{ fullname }} - - {% block functions %} - {% if functions %} - .. rubric:: Functions - - .. autosummary:: - :toctree: . - {% for item in functions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block classes %} - {% if classes %} - .. rubric:: Classes - - .. autosummary:: - :toctree: . - {% for item in classes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block exceptions %} - {% if exceptions %} - .. rubric:: Exceptions - - .. autosummary:: - :toctree: . - {% for item in exceptions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/docs/_templates/autosummary_root_module.rst b/docs/_templates/autosummary_root_module.rst deleted file mode 100644 index 415048b31..000000000 --- a/docs/_templates/autosummary_root_module.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. currentmodule:: {{ fullname }} - -{% block functions %} -{% if functions %} -.. rubric:: Functions - -.. autosummary:: - :toctree: . -{% for item in functions %} - {{ item }} -{%- endfor %} -{% endif %} -{% endblock %} - -{% block classes %} -{% if classes %} -.. rubric:: Classes - -.. autosummary:: - :toctree: . -{% for item in classes %} - {{ item }} -{%- endfor %} -{% endif %} -{% endblock %} - -{% block exceptions %} -{% if exceptions %} -.. rubric:: Exceptions - -.. autosummary:: - :toctree: . -{% for item in exceptions %} - {{ item }} -{%- endfor %} -{% endif %} -{% endblock %} - - -{% block attributes %} -.. rubric:: Constants - -.. autosummary:: - :toctree: . - - newaxis - -{% endblock %} diff --git a/docs/build_docs.py b/docs/build_docs.py index 43a3259be..0f9d27b76 100644 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -15,9 +15,11 @@ import argparse import contextlib +import glob import os import pathlib import re +import subprocess import sys import tempfile from typing import List @@ -91,9 +93,20 @@ def _prepare_source_tree(runfiles_dir: str): runfiles_dir=runfiles_dir, output_path=os.path.join(temp_src_dir, 'third_party_libraries.rst')) + abs_docs_root = os.path.join(runfiles_dir, DOCS_ROOT) + + # Exclude theme and extension directories from temporary directory since + # they are not needed and slow down file globbing. + excluded_paths = frozenset([ + os.path.join(abs_docs_root, 'tensorstore_sphinx_material'), + os.path.join(abs_docs_root, 'tensorstore_sphinx_ext'), + ]) + def create_symlinks(source_dir, target_dir): for name in os.listdir(source_dir): source_path = os.path.join(source_dir, name) + if source_path in excluded_paths: + continue target_path = os.path.join(target_dir, name) if os.path.isdir(source_path): os.makedirs(target_path, exist_ok=True) @@ -106,14 +119,18 @@ def create_symlinks(source_dir, target_dir): create_symlinks(os.path.join(runfiles_dir, DOCS_ROOT), temp_src_dir) source_cpp_root = os.path.abspath(os.path.join(runfiles_dir, CPP_ROOT)) - temp_cpp_root = os.path.join(temp_src_dir, 'tensorstore') - os.makedirs(temp_cpp_root) for name in ['driver', 'kvstore']: os.symlink(os.path.join(source_cpp_root, name), - os.path.join(temp_cpp_root, name)) + os.path.join(temp_src_dir, name)) yield temp_src_dir +def _minify_html(runfiles_dir: str, minify_tool: str, paths: List[str]) -> None: + print('Minifying %d html output files' % (len(paths),)) + minify_tool = os.path.join(runfiles_dir, minify_tool) + subprocess.run([minify_tool] + paths + paths, check=True) + + def run(args: argparse.Namespace, unknown: List[str]): # Ensure tensorstore sphinx extensions can be imported as absolute modules. sys.path.insert(0, os.path.abspath(DOCS_ROOT)) @@ -121,7 +138,15 @@ def run(args: argparse.Namespace, unknown: List[str]): # sphinxcontrib.serializinghtml` not to work unless we first import # `sphinxcontrib.applehelp`. import sphinxcontrib.applehelp - sphinx_args = ['-a'] + sphinx_args = [ + # Always write all files (incremental mode not used) + '-a', + # Don't look for saved environment (since we just use a temporary directory + # anyway). + '-E', + # Show full tracebacks for errors. + '-T', + ] if args.sphinx_help: sphinx_args.append('--help') if args.pdb_on_error: @@ -133,10 +158,30 @@ def run(args: argparse.Namespace, unknown: List[str]): args.output) os.makedirs(output_dir, exist_ok=True) with _prepare_source_tree(runfiles_dir) as temp_src_dir: - sphinx_args += [temp_src_dir, output_dir] - sphinx_args += unknown - import sphinx.cmd.build - sys.exit(sphinx.cmd.build.main(sphinx_args)) + # Use a separate temporary directory for the doctrees, since we don't want + # them mixed into the output directory. + with tempfile.TemporaryDirectory() as doctree_dir: + sphinx_args += ['-d', doctree_dir] + sphinx_args += [temp_src_dir, output_dir] + sphinx_args += unknown + import sphinx.cmd.build + result = sphinx.cmd.build.main(sphinx_args) + if result != 0: + sys.exit(result) + + # Delete buildinfo file. + buildinfo_path = os.path.join(output_dir, '.buildinfo') + if os.path.exists(buildinfo_path): + os.remove(buildinfo_path) + + if args.minify: + # Minify HTML + html_output = glob.glob(os.path.join(output_dir, "**/*.html"), + recursive=True) + _minify_html(runfiles_dir=runfiles_dir, minify_tool=args.minify_tool, + paths=html_output) + print('Output written to: %s' % (os.path.abspath(output_dir),)) + sys.exit(result) def main(): @@ -144,8 +189,11 @@ def main(): default_output = os.environ.get('TEST_UNDECLARED_OUTPUTS_DIR', None) ap.add_argument('--output', '-o', help='Output directory', default=default_output, required=default_output is None) + ap.add_argument('--minify-tool', type=str, + help='Path to HTML minify tool', required=True) ap.add_argument('-P', dest='pdb_on_error', action='store_true', help='Run pdb on exception') + ap.add_argument('--no-minify', dest='minify', action='store_false') ap.add_argument('--sphinx-help', action='store_true', help='Show sphinx build command-line help') ap.add_argument('--pdb', action='store_true', help='Run under pdb') diff --git a/docs/conf.py b/docs/conf.py index efa6da09e..66fd4d0f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,44 +35,89 @@ html_show_copyright = False extensions = [ - 'sphinx_rtd_theme', + 'sphinx.ext.extlinks', + 'tensorstore_sphinx_material.sphinx_material', 'tensorstore_sphinx_ext.jsonschema_sphinx', 'sphinx.ext.intersphinx', 'sphinx.ext.autodoc', 'sphinx.ext.doctest', + 'tensorstore_sphinx_ext.autodoc', 'tensorstore_sphinx_ext.autosummary', + 'tensorstore_sphinx_ext.mathjax', 'sphinx.ext.mathjax', ] exclude_patterns = [ # Included by installation.rst 'third_party_libraries.rst', - # This is included directly by `python/api/index.rst`, so we don't want to - # generate a separate page for it. - 'python/api/tensorstore.rst', '_templates/**', ] source_suffix = '.rst' master_doc = 'index' -language = None +language = 'en' -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +html_theme = 'sphinx_material' -html_theme = 'sphinx_rtd_theme' +html_title = 'TensorStore' + +# html_logo = 'logo.svg' + +html_use_index = False html_favicon = '_templates/logo.svg' html_theme_options = { - 'navigation_depth': -1, + 'logo_svg': + 'logo.svg', + 'site_url': + 'https://google.github.io/tensorstore/', + 'repo_url': + 'https://github.com/google/tensorstore/', + 'repo_name': + 'google/tensorstore', + 'repo_type': + 'github', + 'globaltoc_depth': + -1, + 'globaltoc_collapse': + True, + 'globaltoc_includehidden': + True, + 'features': [ + 'navigation.expand', + # 'navigation.tabs', + # 'toc.integrate', + 'navigation.sections', + # 'navigation.instant', + # 'header.autohide', + 'navigation.top', + ], + 'palette': [ + { + 'media': '(prefers-color-scheme: dark)', + 'scheme': 'slate', + 'primary': 'green', + 'accent': 'light blue', + 'toggle': { + 'icon': 'material/lightbulb', + 'name': 'Switch to light mode', + }, + }, + { + 'media': '(prefers-color-scheme: light)', + 'scheme': 'default', + 'primary': 'green', + 'accent': 'light blue', + 'toggle': { + 'icon': 'material/lightbulb-outline', + 'name': 'Switch to dark mode', + }, + }, + ], } -html_static_path = ['_static'] templates_path = ['_templates'] -html_context = { - 'css_files': ['_static/sphinx_rtd_theme_table_word_wrap_fix.css',], -} intersphinx_mapping = { 'python': @@ -103,18 +148,20 @@ # Extension options # ----------------- -mathjax_config = { - 'displayAlign': 'left', -} - # Use MathJax 3. mathjax_path = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js' always_document_param_types = True -autosummary_generate = True - doctest_global_setup = """ import tensorstore as ts import numpy as np """ + +extlinks = { + 'wikipedia': ('https://en.wikipedia.org/wiki/%s', None), +} + +napoleon_numpy_docstring = False +napoleon_use_admonition_for_examples = True +napoleon_use_admonition_for_notes = True diff --git a/docs/context.rst b/docs/context.rst index e6ba03039..975f6d1f0 100644 --- a/docs/context.rst +++ b/docs/context.rst @@ -1,3 +1,5 @@ +.. _context: + Context framework ================= diff --git a/docs/index.rst b/docs/index.rst index 6efe1522f..b677bfec5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,27 +1,90 @@ +:hero: One-stop shop for high-performance array storage. + TensorStore =========== .. toctree:: :hidden: + :caption: Python API + + python/tutorial + python/indexing + python/api/index + +.. toctree:: + :hidden: + :caption: Setup installation environment + +.. toctree:: + :hidden: + :caption: JSON + + spec index_space context - spec - tensorstore/driver/index - tensorstore/kvstore/index - python/index + + driver/index + kvstore/index TensorStore is a library for efficiently reading and writing large multi-dimensional arrays. -The core abstraction, a **TensorStore** is an asynchronous view of a multi-dimensional array. Every -TensorStore is backed by a **driver**, which connects the high-level TensorStore -interface to an underlying data storage mechanism. Using an appropriate driver, -a TensorStore may be used to access: +Highlights +---------- + +- Provides a uniform API for reading and writing multiple array formats, + including :doc:`zarr `, `N5 `, and + `Neuroglancer precomputed `. + +- Natively supports multiple :doc:`storage drivers`, including + `Google Cloud Storage `, `local and network filesystems + `, `in-memory storage `. +- Support for read/writeback caching and transactions, with strong atomicity, + isolation, consistency, and durability (:wikipedia:`ACID `) guarantees. +- Supports safe, efficient access from multiple processes and machines via optimistic concurrency. +- High-performance implementation in C++ automatically takes advantage of + multiple cores for encoding/decoding and performs multiple concurrent I/O + operations to saturate network bandwidth. +- Asynchronous API enables high-throughput access even to high-latency remote storage. +- Advanced, fully composable :doc:`indexing operations` and virtual + views. + +Getting started +--------------- + +To get started using the Python API, start with the +:doc:`tutorial` and :doc:`indexing operation +guide`, then refer to the detailed :doc:`Python API +reference`. + +For setup instructions, refer to the :doc:`installation` section. + +For details for using a particular driver, refer to the +:doc:`driver` and :doc:`key-value storage` +reference. + +Concepts +-------- + +The core abstraction, a **TensorStore** is an asynchronous view of a +multi-dimensional array. Every TensorStore is backed by a +:doc:`driver`, which connects the high-level TensorStore interface +to an underlying data storage mechanism. + +Opening or creating a TensorStore is done using a :doc:`JSON Spec`, which +is analogous to a URL/file path/database connection string. + +TensorStore introduces a new indexing abstraction, the :ref:`index-transform`, +which underlies all indexing operations. All indexing operations result in +virtual views and are fully composable. Dimension labels are also supported, +and can be used in indexing operations through the `dimension +expression` mechanism. -- contiguous in-memory arrays -- chunked storage formats like `zarr `_, `N5 `_, `Neuroglancer precomputed `_, backed by a supported key-value storage system, such as: +Shared resources like in-memory caches and concurrency limits are configured +using the :doc:`context` mechanism. - - Google Cloud Storage - - Local and network filesystems +Properties of a TensorStore, like the domain, data type, chunk layout, fill +value, and encoding, can be queried and specified/constrained in a uniform way +using the :doc:`schema` mechanism. diff --git a/docs/index_space.rst b/docs/index_space.rst index 85f871cf3..92855ddb7 100644 --- a/docs/index_space.rst +++ b/docs/index_space.rst @@ -1,3 +1,5 @@ +.. _index-space: + Index space =========== @@ -74,35 +76,44 @@ It is defined by its input index domain and :math:`n` *output index maps*, one for each dimension :math:`j` of the output space, each of one of the following three forms: -.. list-table:: - :widths: auto +.. table:: Output index methods + :name: output-index-methods + + .. list-table:: + :widths: auto + + * - .. _index-transform-constant-map: + + constant + - .. math:: + + \mathtt{output}[j] = \mathtt{offset}, - * - constant - - .. math:: + where :math:`\mathtt{offset}` is an arbitrary 64-bit integer. + * - .. _index-transform-single-input-dimension-map: - \mathtt{output}[j] = \mathtt{offset}, + single input dimension + - .. math:: - where :math:`\mathtt{offset}` is an arbitrary 64-bit integer. - * - single input dimension - - .. math:: + \mathtt{output}[j] = \mathtt{offset} + \mathtt{stride} \cdot \mathtt{input}[\mathtt{input\_dimension}], - \mathtt{output}[j] = \mathtt{offset} + \mathtt{stride} \cdot \mathtt{input}[\mathtt{input\_dimension}], + where :math:`\mathtt{offset}` and :math:`\mathtt{stride}` are arbitrary + 64-bit integers and :math:`\mathtt{input\_dimension}` is in the range + :math:`[0, m)`. + * - .. _index-transform-array-map: - where :math:`\mathtt{offset}` and :math:`\mathtt{stride}` are arbitrary - 64-bit integers and :math:`\mathtt{input\_dimension}` is in the range - :math:`[0, m)`. - * - index array - - .. math:: + index array + - .. math:: - \mathtt{output}[j] = \mathtt{offset} + \mathtt{stride} \cdot \mathtt{index\_array}[\mathtt{input}], + \mathtt{output}[j] = \mathtt{offset} + \mathtt{stride} \cdot \mathtt{index\_array}[\mathtt{input}], - where :math:`\mathtt{offset}` and :math:`\mathtt{stride}` are - arbitrary 64-bit integers and :math:`\mathtt{index\_array}` is - an :math:`n`-dimensional array of 64-bit integers indexed by a - subset of the dimensions of the input index domain with - explicit lower and upper bounds, stored as a strided array in - memory. (The dimensions by which it is indexed are indicated - by non-zero strides.) + where :math:`\mathtt{offset}` and :math:`\mathtt{stride}` are + arbitrary 64-bit integers and :math:`\mathtt{index\_array}` is + an :math:`n`-dimensional array of 64-bit integers indexed by a + subset of the dimensions of the input index domain with + explicit lower and upper bounds, stored as a strided array in + memory. (The dimensions by which it is indexed are indicated + by non-zero strides.) TensorStore uses this normalized index transform representation to represent any composition of indexing operations. This representation @@ -276,5 +287,5 @@ Examples: .. note:: The alignment behavior supported by TensorStore is fully compatible with - `NumPy broadcasting` but additionally is - extended to support non-zero origins and labeled dimensions. + :py:obj:`NumPy broadcasting` but additionally + is extended to support non-zero origins and labeled dimensions. diff --git a/docs/python/api/index.rst b/docs/python/api/index.rst index 8b7ebbfd5..36b7202c6 100644 --- a/docs/python/api/index.rst +++ b/docs/python/api/index.rst @@ -1,11 +1,28 @@ +:duplicate-local-toc: + +.. _python-api: + API reference ============= -.. HACK - .. autosummary:: - :toctree: . - :template: autosummary_root_module.rst +.. tensorstore-python-apidoc:: + :fullname: tensorstore + :objtype: module + + Core + ==== + + Indexing + ======== + + Spec + ==== + + Views + ===== - tensorstore + Data types + ========== -.. include:: tensorstore.rst + Asynchronous support + ==================== diff --git a/docs/python/index.rst b/docs/python/index.rst deleted file mode 100644 index 3f94929cc..000000000 --- a/docs/python/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _python-api: - -Python API -========== - -.. toctree:: - :maxdepth: 1 - - tutorial - indexing - api/index diff --git a/docs/python/indexing.rst b/docs/python/indexing.rst index 695a179ec..5d92b70f5 100644 --- a/docs/python/indexing.rst +++ b/docs/python/indexing.rst @@ -118,7 +118,7 @@ negative positions: `numpy.ndarray`, where a negative index specifies a position relative to the end (upper bound). -Specifying an index outside the explicit bounds of a dimension is results in an immediate error: +Specifying an index outside the explicit bounds of a dimension results in an immediate error: .. doctest:: @@ -334,9 +334,9 @@ lengths, but a sequence may be combined with a scalar value: Adding singleton dimensions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Specifying a value of `tensorstore.newaxis` (equal to `None`) adds a -new dummy/singleton dimension with `implicit bounds` -:math:`[0, 1)`: +Specifying a value of :py:obj:`tensorstore.newaxis` (equal to `None`) adds a new +dummy/singleton dimension with `implicit bounds` :math:`[0, +1)`: .. doctest:: diff --git a/docs/spec.rst b/docs/spec.rst index 4a5b89ff2..801589ffe 100644 --- a/docs/spec.rst +++ b/docs/spec.rst @@ -1,5 +1,5 @@ -TensorStore JSON Spec -===================== +JSON Spec +========= TensorStore relies on a JSON ``Spec`` format for specifying all parameters necessary for opening or creating a TensorStore. diff --git a/docs/tensorstore_schema.yml b/docs/tensorstore_schema.yml index d1227d1c3..975a4dcce 100644 --- a/docs/tensorstore_schema.yml +++ b/docs/tensorstore_schema.yml @@ -21,10 +21,11 @@ properties: type: integer minimum: 0 maximum: 32 + title: | + Specifies the rank of the TensorStore. description: | - Specifies the rank of the TensorStore. If `.transform` is also specified, - the input rank must match. Otherwise, the rank constraint applies to the - driver directly. + If `.transform` is also specified, the input rank must match. Otherwise, + the rank constraint applies to the driver directly. transform: $ref: IndexTransform title: Specifies a transform. diff --git a/docs/tensorstore_sphinx_ext/BUILD b/docs/tensorstore_sphinx_ext/BUILD index 43801b301..f852cdb9c 100644 --- a/docs/tensorstore_sphinx_ext/BUILD +++ b/docs/tensorstore_sphinx_ext/BUILD @@ -1,7 +1,7 @@ load("//python/tensorstore:pytype.bzl", "pytype_strict_library") package( - default_visibility = ["//visibility:public"], + default_visibility = ["//docs:__subpackages__"], ) licenses(["notice"]) @@ -36,8 +36,26 @@ pytype_strict_library( pytype_strict_library( name = "autosummary", srcs = ["autosummary.py"], + deps = [ + ":sphinx_utils", + "@pypa_docutils//:docutils", + "@pypa_sphinx//:sphinx", + ], +) + +pytype_strict_library( + name = "autodoc", + srcs = ["autodoc.py"], deps = [ "@pypa_docutils//:docutils", "@pypa_sphinx//:sphinx", ], ) + +pytype_strict_library( + name = "mathjax", + srcs = ["mathjax.py"], + deps = [ + "@pypa_sphinx//:sphinx", + ], +) diff --git a/docs/tensorstore_sphinx_ext/autodoc.py b/docs/tensorstore_sphinx_ext/autodoc.py new file mode 100644 index 000000000..8f197288a --- /dev/null +++ b/docs/tensorstore_sphinx_ext/autodoc.py @@ -0,0 +1,447 @@ +# Copyright 2020 The TensorStore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""TensorStore customizations of sphinx.ext.autodoc.""" + +import functools +import re +import sys +from typing import List, Tuple, Any, Type, Optional + +import docutils.nodes +import sphinx.addnodes +import sphinx.application +import sphinx.domains.python +import sphinx.environment +import sphinx.ext.autodoc +import sphinx.pycode.ast +from sphinx.pycode.ast import ast +import sphinx.util.docstrings +import sphinx.util.inspect +import sphinx.util.typing + + +def _remove_pymethod_self_type_annotation(): + """Monkey patches PyMethod to omit type annotation on `self` parameters. + + Also removes the return type annotation for `__init__`. + + pybind11 includes these annotations which just add verbosity. + """ + PyMethod = sphinx.domains.python.PyMethod # pylint: disable=invalid-name + orig_handle_signature = PyMethod.handle_signature + + def handle_signature( + self, sig: str, + signode: sphinx.addnodes.desc_signature) -> Tuple[str, str]: + result = orig_handle_signature(self, sig, signode) + for param in signode.traverse(condition=sphinx.addnodes.desc_parameter): + if param.children[0].astext() == 'self': + # Remove any annotations on `self` + del param.children[1:] + break + + if signode['fullname'].endswith('.__init__'): + # Remove first parameter. + for param in signode.traverse(condition=sphinx.addnodes.desc_parameter): + if param.children[0].astext() == 'self': + param.parent.remove(param) + break + + # Remove return type. + for node in signode.traverse(condition=sphinx.addnodes.desc_returns): + node.parent.remove(node) + + elif signode['fullname'].endswith('.__setitem__'): + # Remove return type. + for node in signode.traverse(condition=sphinx.addnodes.desc_returns): + node.parent.remove(node) + + return result + + PyMethod.handle_signature = handle_signature + + +def _monkey_patch_py_xref_mixin(): + """Monkey patches PyXrefMixin to format types using `_parse_annotation`. + + This allows our custom `type_to_xref` implementation to be used. + """ + PyXrefMixin = sphinx.domains.python.PyXrefMixin # pylint: disable=invalid-name + + def make_xrefs( + self, rolename: str, domain: str, target: str, + innernode: Type[docutils.nodes.TextElement] = docutils.nodes.emphasis, + contnode: Optional[docutils.nodes.Node] = None, + env: Optional[sphinx.environment.BuildEnvironment] = None + ) -> List[docutils.nodes.Node]: + del self + del rolename + del domain + del innernode + del contnode + return sphinx.domains.python._parse_annotation(target, env) # pylint: disable=protected-access + + PyXrefMixin.make_xrefs = make_xrefs + + +def _process_docstring(app: sphinx.application.Sphinx, what: str, name: str, + obj: Any, options: Any, lines: List[str]) -> None: # pylint: disable=g-doc-args + """Adds `:type : ` fields to docstrings based on annotations. + + This function is intended to be registered for the 'autodoc-process-docstring' + signal, and must be registered *after* sphinx.ext.napoleon if Google-style + docstrings are to be supported. + + Sphinx allows the type of a parameter to be indicated by a `:type :` + field. However, in the TensorStore documentation all parameter types are + instead indicated by annotations in the signature. + + Sphinx provides the `autodoc_typehints='description'` option which adds `:type + ` fields based on the annotations. However, it has a number of + problematic limitations: + + - It only supports real annotations, stored in the `__annotations__` attribute + of a function. It isn't compatible with the `autodoc_docstring_signature` + option, which we rely on for pybind11-defined functions and to support + overloaded functions. + + - If you specify `autodoc_typehints='description'`, autodoc strips out + annotations from the signature. We would like to include them in both + places. + + - It adds a `:type` field for all parameters, even ones that are not + documented. + + This function providse the same functionality as the + `autodoc_typehints='description'` option but without those limitations. + """ + del what + del obj + del options + current_documenter_map = app.env.temp_data.get( + 'tensorstore_autodoc_current_documenter') + if not current_documenter_map: + return + documenter = current_documenter_map.get(name) + if not documenter: + return + + def _get_field_pattern(field_names: List[str], param: str) -> str: + return '^:(?:' + '|'.join( + re.escape(name) + for name in field_names) + r')\s+' + re.escape(param) + ':' + + def insert_type(param: str, typ: str) -> None: + type_pattern = _get_field_pattern(['paramtype', 'type'], param) + pattern = _get_field_pattern([ + 'param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam' + ], param) + # First check if there is already a type field + for line in lines: + if re.match(type_pattern, line): + # :type: field already present for `param`, don't add another one. + return + + # Only add :type: field if `param` is documented. + for i, line in enumerate(lines): + if re.match(pattern, line): + lines.insert(i, f':type {param}: {typ}') + return + + if not documenter.args: + return + + try: + sig = sphinx.util.inspect.signature_from_str( + f'func({documenter.args}) -> None') + except: # pylint: disable=bare-except + # ignore errors + return + + for param in sig.parameters.values(): + if param.annotation is not param.empty: + insert_type(param.name, sphinx.util.typing.stringify(param.annotation)) + + # TODO(jbms): Disable inserting rtype for now, since sphinx does display it + # very nicely anyway. + + # def insert_rtype(rtype: str) -> None: + # rtype_pattern = '^:rtype: ' + # # Skip insert if there is already an :rtype: field. + # if any(re.match(rtype_pattern, line) for line in lines): + # return + + # # Only add :rtype: field if thre is a :returns: field + # returns_pattern = '^:returns?: ' + # for i, line in enumerate(lines): + # if re.match(returns_pattern, line): + # lines.insert(i, f':rtype: {rtype}') + # break + + # if documenter.retann: + # insert_rtype(documenter.retann) + + +def _type_to_xref( + text: str, + env: sphinx.environment.BuildEnvironment) -> sphinx.addnodes.pending_xref: + """Tensorstore-customized version of sphinx.domains.python.type_to_xref. + + This handles certain aliases and short names used in TensorStore type + annotations. + + Args: + text: "Type" referenced in the annotation. + env: Sphinx build environment. + + Returns: + Reference node. + """ + reftarget = text + refdomain = 'py' + reftype = 'obj' + if text in ('Optional', 'List', 'Union', 'Dict', 'Any', 'Iterator', 'Tuple', + 'Literal', 'Sequence'): + reftarget = 'typing.' + text + elif text == 'array': + reftarget = 'numpy.ndarray' + elif text == 'dtype': + reftarget = 'numpy.dtype' + elif text == 'array_like': + reftarget = 'numpy:array_like' + refdomain = 'std' + reftype = 'any' + elif text == 'NumpyIndexingSpec': + reftarget = 'python-numpy-style-indexing' + refdomain = 'std' + reftype = 'ref' + elif text == 'DimSelectionLike': + reftarget = 'python-dim-selections' + refdomain = 'std' + reftype = 'ref' + elif text == 'DownsampleMethod': + reftarget = 'DownsampleMethod' + refdomain = 'json' + reftype = 'schema' + prefix = 'tensorstore.' + if text.startswith(prefix): + text = text[len(prefix):] + if env: + kwargs = { + 'py:module': env.ref_context.get('py:module'), + 'py:class': env.ref_context.get('py:class') + } + else: + kwargs = {} + + return sphinx.addnodes.pending_xref('', docutils.nodes.Text(text), + refdomain=refdomain, reftype=reftype, + reftarget=reftarget, refwarn=True, + refexplicit=True, **kwargs) + + +def _transform_annotation_ast(tree: ast.AST) -> ast.AST: + """Transforms the AST of a type annotation to improve the display. + + - Converts Union/Optional/Literal to "|" syntax allowed by PEP 604. This + syntax is not actually supported until Python 3.10 but displays better in + the documentation. + + Args: + tree: Original AST. + + Returns: + Transformed AST. + """ + + class Transformer(ast.NodeTransformer): # pylint: disable=missing-class-docstring + + def visit_Subscript(self, node: ast.Subscript) -> ast.AST: + if not isinstance(node.value, ast.Name): + return node + if node.value.id in ('Optional', 'Union', 'Literal'): + elts = node.slice + if isinstance(node.slice, ast.Index): + elts = elts.value + if isinstance(elts, ast.Tuple): + elts = elts.elts + else: + elts = [node.slice] + elts = [_transform_annotation_ast(x) for x in elts] + if node.value.id == 'Optional': + elts.append(ast.Constant(value=None)) + elif node.value.id == 'Literal': + elts = [ast.Subscript(node.value, x, node.ctx) for x in elts] + result = functools.reduce( + (lambda left, right: ast.BinOp(left, ast.BitOr(), right)), + elts) + return result + return ast.Subscript(node.value, _transform_annotation_ast(node.slice), + node.ctx) + + return ast.fix_missing_locations(Transformer().visit(tree)) + + +def _parse_annotation( + annotation: str, + env: sphinx.environment.BuildEnvironment) -> List[docutils.nodes.Node]: + """Parse type annotation.""" + + # This is copied from Sphinx version 4, in order to support the type union + # operator `|`. We can eliminate most of this once we depend on Sphinx + # version 4. + # + # It is subject to the following license notice: + # + # Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are + # met: + # + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + def unparse(node: ast.AST) -> List[docutils.nodes.Node]: + if isinstance(node, ast.Attribute): + return [ + docutils.nodes.Text('%s.%s' % (unparse(node.value)[0], node.attr)) + ] + elif isinstance(node, ast.BinOp): + result: List[docutils.nodes.Node] = unparse(node.left) + result.extend(unparse(node.op)) + result.extend(unparse(node.right)) + return result + elif isinstance(node, ast.BitOr): + return [ + docutils.nodes.Text(' '), + sphinx.addnodes.desc_sig_punctuation('', '|'), + docutils.nodes.Text(' ') + ] + elif isinstance(node, ast.Constant): # type: ignore + if node.value is Ellipsis: + return [sphinx.addnodes.desc_sig_punctuation('', '...')] + if node.value is None: + return [ + docutils.nodes.literal(text='None', classes=['code', 'python'], + language='python') + ] + else: + return [docutils.nodes.Text(node.value)] + elif isinstance(node, ast.Expr): + return unparse(node.value) + elif isinstance(node, ast.Index): + return unparse(node.value) + elif isinstance(node, ast.List): + result = [sphinx.addnodes.desc_sig_punctuation('', '[')] + for elem in node.elts: + result.extend(unparse(elem)) + result.append(sphinx.addnodes.desc_sig_punctuation('', ', ')) + result.pop() + result.append(sphinx.addnodes.desc_sig_punctuation('', ']')) + return result + elif isinstance(node, ast.Module): + return sum((unparse(e) for e in node.body), []) + elif isinstance(node, ast.Name): + return [docutils.nodes.Text(node.id)] + elif isinstance(node, ast.Subscript): + if isinstance(node.value, ast.Name) and node.value.id == 'Literal': + constant = node.slice + if isinstance(constant, ast.Index): + constant = constant.value + if isinstance(constant, (ast.Constant, ast.Str)): + if isinstance(constant, ast.Constant): + value = constant.value + else: + value = constant.s + return [ + docutils.nodes.literal(text=repr(value), + classes=['code', + 'python'], language='python') + ] + result = unparse(node.value) + result.append(sphinx.addnodes.desc_sig_punctuation('', '[')) + result.extend(unparse(node.slice)) + result.append(sphinx.addnodes.desc_sig_punctuation('', ']')) + return result + elif isinstance(node, ast.Tuple): + if node.elts: + result = [] + for elem in node.elts: + result.extend(unparse(elem)) + result.append(sphinx.addnodes.desc_sig_punctuation('', ', ')) + result.pop() + else: + result = [ + sphinx.addnodes.desc_sig_punctuation('', '('), + sphinx.addnodes.desc_sig_punctuation('', ')') + ] + + return result + else: + if sys.version_info < (3, 8): + if isinstance(node, ast.Ellipsis): + return [sphinx.addnodes.desc_sig_punctuation('', '...')] + elif isinstance(node, ast.NameConstant): + return [docutils.nodes.Text(node.value)] + + raise SyntaxError # unsupported syntax + + try: + tree = sphinx.pycode.ast.parse(annotation) + tree = _transform_annotation_ast(tree) + result = unparse(tree) + for i, node in enumerate(result): + if isinstance(node, docutils.nodes.Text) and node.strip(): + result[i] = _type_to_xref(str(node), env) + except SyntaxError: + result = [_type_to_xref(annotation, env)] + return [sphinx.addnodes.desc_type('', '', *result)] + + +def _monkey_patch_python_type_to_xref(): + """Modifies type_to_xref to support TensorStore-specific aliases.""" + + sphinx.domains.python.type_to_xref = _type_to_xref + sphinx.domains.python._parse_annotation = _parse_annotation # pylint: disable=protected-access + + +def setup(app): # pylint: disable=missing-function-docstring + _monkey_patch_py_xref_mixin() + _monkey_patch_python_type_to_xref() + _remove_pymethod_self_type_annotation() + + # Must register `sphinx.ext.napoleon` first, since the `_process_docstring` + # handler for the `autodoc-process-docstring` event needs to run after the + # handler registered by `sphinx.ext.napoleon`. + app.setup_extension('sphinx.ext.napoleon') + app.connect('autodoc-process-docstring', _process_docstring) + return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/docs/tensorstore_sphinx_ext/autosummary.py b/docs/tensorstore_sphinx_ext/autosummary.py index 53a1f32df..c9e9e76d2 100644 --- a/docs/tensorstore_sphinx_ext/autosummary.py +++ b/docs/tensorstore_sphinx_ext/autosummary.py @@ -13,48 +13,148 @@ # limitations under the License. """TensorStore-specific Python documentation generation. -This extension overrides the built-in autosummary directive to automatically -include module members, as described here: -https://stackoverflow.com/questions/20569011/python-sphinx-autosummary-automated-listing-of-member-functions +This extension generates the Python API reference documentation. A separate +page is generated for each class/function/member/constant to be documented. -Additionally, this modifies autosummary and autodoc to handle pybind11 -overloaded functions. +As with sphinx.ext.autosummary, we have to physically write a separate rST file +to the source tree for each object to document, as an initial preprocesing step, +since that provides the simplest way to get Sphinx to process those pages. +Since the build is always run with the source tree copied to a temporary +directory, this does not modify the real source tree. + +Unlike the sphinx.ext.autosummary extension, we use Sphinx Python domain +directives for the "summaries" as well, rather than a plain table, in order to +display the signatures nicely. """ +import copy +import inspect +import os +import pathlib import re -from typing import List, Tuple, Any, Optional, Type, Union +from typing import List, Tuple, Any, Optional, Type, cast, Dict, NamedTuple, Iterator, Set + +from . import sphinx_utils # pylint: disable=relative-beyond-top-level import docutils.nodes +import docutils.parsers.rst.states +import docutils.statemachine import sphinx.addnodes import sphinx.application import sphinx.domains.python import sphinx.environment import sphinx.ext.autodoc -import sphinx.ext.autosummary +import sphinx.ext.napoleon.docstring +import sphinx.pycode import sphinx.util.docstrings import sphinx.util.docutils import sphinx.util.inspect +import sphinx.util.logging import sphinx.util.typing +logger = sphinx.util.logging.getLogger(__name__) + +SIGNATURE_SUMMARY_LENGTH = 70 +"""Target maximum length in characters for a signature summary. + +Parameters will be elided (replaced with an ellipsis) to reduce the length to +this limit. +""" + +OBJECT_SYNOPSES_KEY = 'object_synopses' +"""Key within the Python domain `data` dict used to store object synopses.""" + + +class ParsedOverload(NamedTuple): + """Parsed representation of a single overload. + + For non-function types and non-overloaded functions, this just represents the + object itself. + + Sphinx does not really support pybind11-style overloaded functions directly. + It has minimal support functions with multiple signatures, with a single + docstring. However, pybind11 produces overloaded functions each with their + own docstring. This module adds support for documenting each overload as an + independent function. + + Additionally, we need a way to identify each overload, for the purpose of + generating a page name, listing in the table of contents sidebar, and + cross-referencing. Sphinx does not have a native solution to this problem + because it is not designed to support overloads. Doxygen uses some sort of + hash as the identifier, but that means links break with even minor changes to + the signature. + + Instead, we require that a unique id be manually assigned to each overload, + and specified as: + + Overload: + XXX + + in the docstring. Then the overload will be identified as + `module.Class.function(overload)`, and will be documented using the page name + `module.Class.function-overload`. Typically the overload id should be chosen + to be a parameter name that is unique to the overload. + """ + + doc: Optional[str] + """Docstring for individual overload. First line is the signature.""" + + overload_id: Optional[str] = None + """Overload id specified in the docstring. -def _parse_overloaded_function_docstring(doc: str): - m = re.match('^([^(]+)\\(', doc) + If there is just a single overload, will be `None`. Otherwise, if no overload + id is specified, a warning is produced and the index of the overload, + i.e. "1", "2", etc., is used as the id. + """ + + +def _extract_field(doc: str, field: str) -> Tuple[str, Optional[str]]: + pattern = f'\n\\s*\n{field}:\\s*\n\\s+([^\n]+)\n' + m = re.search(pattern, doc) + if m is None: + return doc, None + start, end = m.span() + return f'{doc[:start]}\n\n{doc[end:]}', m.group(1).strip() + + +_OVERLOADED_FUNCTION_RE = '^([^(]+)\\([^\n]*\nOverloaded function.\n' + + +def _parse_overloaded_function_docstring( + doc: Optional[str]) -> List[ParsedOverload]: + """Parses a pybind11 overloaded function docstring. + + If the docstring is not for an overloaded function, just returns the full + docstring as a single "overload". + + Args: + doc: Original docstring. + Returns: + List of parsed overloads. + Raises: + ValueError: If docstring has unexpected format. + """ + + if doc is None: + return [ParsedOverload(doc=doc, overload_id=None)] + m = re.match(_OVERLOADED_FUNCTION_RE, doc) if m is None: - raise ValueError( - f'Failed to determine display name from docstring: {repr(doc)}') + # Non-overloaded function + doc, overload_id = _extract_field(doc, 'Overload') + return [ParsedOverload(doc=doc, overload_id=overload_id)] + display_name = m.group(1) - overloaded_prefix = '\nOverloaded function.\n' - doc = doc[doc.index(overloaded_prefix) + len(overloaded_prefix):] + doc = doc[m.end():] i = 1 def get_prefix(i: int): return '\n%d. %s(' % (i, display_name) prefix = get_prefix(i) - parts: List[Tuple[str, str]] = [] + parts: List[ParsedOverload] = [] while doc: if not doc.startswith(prefix): - raise RuntimeError('Docstring does not contain %r as expected: %r' % ( + raise ValueError('Docstring does not contain %r as expected: %r' % ( prefix, doc, )) @@ -71,254 +171,1245 @@ def get_prefix(i: int): else: part = doc[:end_index] doc = doc[end_index:] - parts.append((part_sig, part)) + + part, overload_id = _extract_field(part, 'Overload') + if overload_id is None: + overload_id = str(i - 1) + + part_doc_with_sig = f'{display_name}{part_sig}\n{part}' + parts.append(ParsedOverload( + doc=part_doc_with_sig, + overload_id=overload_id, + )) return parts -def _get_attribute_type(obj: property): - if obj.fget is not None: - doc = obj.fget.__doc__ - if doc is not None: - lines = doc.splitlines() - match = re.fullmatch('^(\\(.*)\\)\\s*->\\s*(.*)$', lines[0]) - if match: - args, retann = match.groups() - del args - return retann - return None +def _get_overloads_from_documenter( + documenter: sphinx.ext.autodoc.Documenter) -> List[ParsedOverload]: + docstring = sphinx.util.inspect.getdoc( + documenter.object, documenter.get_attr, + documenter.env.config.autodoc_inherit_docstrings, documenter.parent, + documenter.object_name) + return _parse_overloaded_function_docstring(docstring) -class TensorstoreAutosummary(sphinx.ext.autosummary.Autosummary): +def _get_python_object_synopses( + domain: sphinx.domains.python.Domain) -> Dict[str, str]: + data = domain.data.get(OBJECT_SYNOPSES_KEY) + if data is not None: + return data + return domain.data.setdefault(OBJECT_SYNOPSES_KEY, {}) - def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]: - items: List[Tuple[str, str, str, str]] = [] - for display_name, sig, summary, real_name in super().get_items(names): - if summary == 'Initialize self.': - continue - real_name, obj, parent, modname = sphinx.ext.autosummary.import_by_name( - real_name) - del parent - del modname - if summary == 'Overloaded function.': - for part_sig, part in _parse_overloaded_function_docstring(obj.__doc__): - max_item_chars = 50 - max_chars = max(10, max_item_chars - len(display_name)) - mangled_sig = sphinx.ext.autosummary.mangle_signature( - part_sig, max_chars=max_chars) - part_summary = sphinx.ext.autosummary.extract_summary( - part.splitlines(), self.state.document) - items.append((display_name, mangled_sig, part_summary, real_name)) - else: - if isinstance(obj, property): - retann = _get_attribute_type(obj) - if retann is not None: - sig = ': ' + retann - items.append((display_name, sig, summary, real_name)) - return items +def _has_default_value(node: sphinx.addnodes.desc_parameter): + for sub_node in node.traverse(condition=docutils.nodes.literal): + if 'default_value' in sub_node.get('classes'): + return True + return False - def run(self): - result = super().run() - # Strip out duplicate toc entries due to overloaded functions - if result: - toc_node = result[-1].children[0] - seen_docnames = set() - new_entries = [] - if 'entries' in toc_node: - for _, docn in toc_node['entries']: - if docn in seen_docnames: - continue - seen_docnames.add(docn) - new_entries.append((None, docn)) - toc_node['entries'] = new_entries +def _summarize_signature(node: sphinx.addnodes.desc_signature): + """Shortens a signature line to fit within SIGNATURE_SUMMARY_LENGTH.""" - return result + def _must_shorten(): + return len(node.astext()) > SIGNATURE_SUMMARY_LENGTH + parameterlist: Optional[sphinx.addnodes.desc_parameterlist] = None + for parameterlist in node.traverse( + condition=sphinx.addnodes.desc_parameterlist): + break -def _monkey_patch_autodoc_function_documenter( - documenter_cls: Union[Type[sphinx.ext.autodoc.FunctionDocumenter], - Type[sphinx.ext.autodoc.MethodDocumenter]]): + if parameterlist is None: + # Can't shorten a signature without a parameterlist + return - orig_generate = documenter_cls.generate + # Remove initial `self` parameter + if parameterlist.children and parameterlist.children[0].astext() == 'self': + del parameterlist.children[0] - def generate(self, *args, **kwargs): - if not self.parse_name(): - return - if not self.import_object(): + added_ellipsis = False + for next_parameter_index in range(len(parameterlist.children) - 1, -1, -1): + if not _must_shorten(): return + # First remove type annotation of last parameter, but only if it doesn't + # have a default value. + last_parameter = parameterlist.children[next_parameter_index] + if not _has_default_value(last_parameter): + del last_parameter.children[1:] + if not _must_shorten(): + return + + # Elide last parameter entirely + del parameterlist.children[next_parameter_index] + if not added_ellipsis: + added_ellipsis = True + ellipsis_node = sphinx.addnodes.desc_sig_punctuation('', '...') + param = sphinx.addnodes.desc_parameter() + param += ellipsis_node + parameterlist += param + + +class _MemberDocumenterEntry(NamedTuple): + """Represents a member of some outer scope (module/class) to document.""" + + documenter: sphinx.ext.autodoc.Documenter + is_attr: bool + name: str + """Member name within parent, e.g. class member name.""" + + full_name: str + """Full name under which to document the member. + + For example, "modname.ClassName.method". + """ + + import_name: str + """Name to import to access the member.""" + + overload: Optional[ParsedOverload] = None + + is_inherited: bool = False + """Indicates whether this is an inherited member.""" + + subscript: bool = False + """Whether this is a "subscript" method to be shown with [] instead of ().""" + + @property + def page_name(self): + page = self.full_name + if self.overload and self.overload.overload_id: + page += f'-{self.overload.overload_id}' + return page + + @property + def object_name(self): + name = self.full_name + if self.overload and self.overload.overload_id: + name += f'({self.overload.overload_id})' + return name + + @property + def toc_title(self): + name = self.name + if self.overload and self.overload.overload_id: + name += f'({self.overload.overload_id})' + return name + + +_INIT_SUFFIX = '.__init__' + +_CLASS_GETITEM_SUFFIX = '.__class_getitem__' + + +def _get_python_object_name_for_signature(entry: _MemberDocumenterEntry) -> str: + """Returns the name of a Python object to use in a :py: domain directive. + + This modifies __init__ and __class_getitem__ objects so that they are shown + with the invocation syntax. + + Args: + entry: Entry to document. + """ + full_name = entry.full_name + + if full_name.endswith(_INIT_SUFFIX): + full_name = full_name[:-len(_INIT_SUFFIX)] + + elif full_name.endswith(_CLASS_GETITEM_SUFFIX): + full_name = full_name[:-len(_CLASS_GETITEM_SUFFIX)] + + documenter = entry.documenter + if documenter.modname and full_name.startswith(documenter.modname + '.'): + return full_name[len(documenter.modname) + 1:] + return full_name + + +def _ensure_module_name_in_signature( + signode: sphinx.addnodes.desc_signature) -> None: + """Ensures non-summary objects are documented with the module name. + + Sphinx by default excludes the module name from class members, and does not + provide an option to override that. Since we display all objects on separate + pages, we want to include the module name for clarity. + + Args: + signode: Signature to modify in place. + """ + for node in signode.traverse(condition=sphinx.addnodes.desc_addname): + modname = signode.get('module') + if modname and not node.astext().startswith(modname + '.'): + node.insert(0, docutils.nodes.Text(modname + '.')) + break + + +MEMBER_NAME_TO_GROUP_NAME_MAP = { + '__init__': 'Constructors', + '__class_getitem__': 'Constructors', + '__eq__': 'Special members', +} + + +def _get_group_name(entry: _MemberDocumenterEntry) -> str: + """Returns a default group name for an entry. + + This is used if the group name is not explicitly specified via "Group:" in the + docstring. + + Args: + entry: Entry to document. + + Returns: + The group name. + """ + group_name = MEMBER_NAME_TO_GROUP_NAME_MAP.get(entry.name) + if group_name is None: + if entry.documenter.objtype == 'class': + group_name = 'Classes' + else: + group_name = 'Public members' + return group_name + + +def _mark_subscript_parameterlist(node: sphinx.addnodes.desc) -> None: + """Modifies an object description to display as a "subscript method". + + A "subscript method" is a property that defines __getitem__ and is intended to + be treated as a method invoked using [] rather than (), in order to allow + subscript syntax like ':'. + + Args: + node: Object description to modify in place. + """ + signode = cast(sphinx.addnodes.desc_signature, node.children[0]) + for sub_node in signode.traverse( + condition=sphinx.addnodes.desc_parameterlist): + sub_node['parens'] = ('[', ']') + + +def _clean_init_signature(node: sphinx.addnodes.desc) -> None: + """Modifies an object description of an __init__ method. + + Removes the return type (always None) and the self paramter (since these + methods are displayed as the class name, without showing __init__). + + Args: + node: Object description to modify in place. + """ + signode = cast(sphinx.addnodes.desc_signature, node.children[0]) + # Remove first parameter. + for param in signode.traverse(condition=sphinx.addnodes.desc_parameter): + if param.children[0].astext() == 'self': + param.parent.remove(param) + break + + # Remove return type. + for node in signode.traverse(condition=sphinx.addnodes.desc_returns): + node.parent.remove(node) + + +def _clean_class_getitem_signature(node: sphinx.addnodes.desc) -> None: + """Modifies an object description of a __class_getitem__ method. + + Removes the `static` prefix since these methods are shown using the class + name (i.e. as "subscript" constructors). + + Args: + node: Object description to modify in place. + + """ + signode = cast(sphinx.addnodes.desc_signature, node.children[0]) + + # Remove `static` prefix + for prefix in signode.traverse(condition=sphinx.addnodes.desc_annotation): + prefix.parent.remove(prefix) + break + + +def _postprocess_autodoc_rst_output( + rst_strings: docutils.statemachine.StringList, + summary: bool) -> Optional[str]: + """Postprocesses generated RST from autodoc before parsing into nodes. + + Args: + rst_strings: Generated RST content, modified in place. + summary: Whether to produce a summary rather than a full description. + + Returns: + Group name if it was specified in `rst_strings`. + """ + # Extract :group: field if present. + group_name = None + group_field_prefix = ' :group: ' + for i, line in enumerate(rst_strings): + if line.startswith(group_field_prefix): + group_name = line[len(group_field_prefix):].strip() + del rst_strings[i] + break + + if summary: + # Remove all but the first paragraph of the description + + # First skip over any directive fields + i = 2 + while i < len(rst_strings) and rst_strings[i].startswith(' :'): + i += 1 + # Skip over blank lines before start of directive content + while i < len(rst_strings) and not rst_strings[i].strip(): + i += 1 + # Skip over first paragraph + while i < len(rst_strings) and rst_strings[i].strip(): + i += 1 + + # Delete remaining content + del rst_strings[i:] + + return group_name + + +class TensorstorePythonApidoc(sphinx.util.docutils.SphinxDirective): + """Adds a summary of the members of an object. + + This is used to generate both the top-level module summary as well as the + summary of individual classes and functions. + + Except for the top-level module summary, the `objectdescription` option is + specified, which results in an `auto{objtype}` directive also being added, + with the member summary added as its content. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + + option_spec = { + 'fullname': str, + 'importname': str, + 'objtype': str, + 'objectdescription': lambda arg: True, + 'subscript': lambda arg: True, + 'overload': str, + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._objtype = self.options['objtype'] + self._fullname = self.options['fullname'] + self._importname = self.options.get('importname') or self._fullname + self._is_subscript = self.options.get('subscript') + documenter_cls = self.env.app.registry.documenters[self._objtype] + self._documenter = _create_documenter(env=self.env, + documenter_cls=documenter_cls, + name=self._importname) + overloads = _get_overloads_from_documenter(self._documenter) + overload_id = self.options.get('overload') + for overload in overloads: + if overload.overload_id == overload_id: + break + else: + raise ValueError('Could not find overload %s(%s)' % + (self._fullname, overload_id)) + self._entry = _MemberDocumenterEntry( + documenter=self._documenter, + subscript=self._is_subscript, + full_name=self._fullname, + import_name=self._importname, + overload=overload, + is_attr=False, + name='', + ) + + def _generate_autodoc( + self, entry: _MemberDocumenterEntry, + summary=False) -> Tuple[sphinx.addnodes.desc, Optional[str]]: + """Generates an object description for the given entry. + + Args: + entry: Entry to document. + summary: Whether to generate a summary rather than full description. + Returns: + Tuple of object description and group name. Note that the returned object + description still needs additional postprocessing performed by + `_add_member_summary` for (`summary=True`) or `_make_object_description` + (for `summary=False`). + """ + + rst_strings = docutils.statemachine.StringList() + entry.documenter.directive.result = rst_strings + + if entry.overload and entry.overload.overload_id is not None: + # Force autodoc to use the overload-specific signature. autodoc already + # has an internal mechanism for overriding the docstrings based on the + # `_new_docstrings` member. + entry.documenter._new_docstrings = [ # pylint: disable=protected-access + sphinx.util.docstrings.prepare_docstring( + entry.overload.doc or '', 1, + self.state.document.settings.tab_width) + ] + else: + # Force autodoc to obtain the docstring through its normal mechanism, + # which includes the "ModuleAnalyzer" for reading docstrings of + # variables/attributes that are only contained in the source code. + entry.documenter._new_docstrings = None # pylint: disable=protected-access + + if summary and entry.is_inherited: + overridename = entry.name + else: + overridename = _get_python_object_name_for_signature(entry) + entry.documenter.format_name = lambda: overridename + + # Record the documenter for use by _process_docstring in `autodoc.py`. current_documenter_map = self.env.temp_data.setdefault( 'tensorstore_autodoc_current_documenter', {}) - current_documenter_map[self.fullname] = self + current_documenter_map[entry.documenter.fullname] = entry.documenter + entry.documenter.generate() + del current_documenter_map[entry.documenter.fullname] - doc = self.object.__doc__ - if doc is None or '\nOverloaded function.\n\n' not in doc: - orig_generate(self, *args, **kwargs) - return - old_indent = self.indent + group_name = _postprocess_autodoc_rst_output(rst_strings, summary=summary) + + entry.documenter.titles_allowed = True + nodes = [ + x for x in sphinx.ext.autodoc.directive.parse_generated_content( + self.state, entry.documenter.directive.result, entry.documenter) + if isinstance(x, sphinx.addnodes.desc) + ] + assert len(nodes) == 1 + node = nodes[0] + + if entry.subscript: + _mark_subscript_parameterlist(node) + if entry.full_name.endswith(_INIT_SUFFIX): + _clean_init_signature(node) + if entry.full_name.endswith(_CLASS_GETITEM_SUFFIX): + _clean_class_getitem_signature(node) + + return node, group_name + + def _add_member_summary( + self, entry: _MemberDocumenterEntry) -> Tuple[str, sphinx.addnodes.desc]: + objdesc, group_name = self._generate_autodoc(entry, summary=True) + objdesc['classes'].append('summary') + sig_node = cast(sphinx.addnodes.desc_signature, objdesc.children[0]) + _summarize_signature(sig_node) + # Insert a link around the `desc_name` field + for sub_node in sig_node.traverse(condition=sphinx.addnodes.desc_name): + xref_node = sphinx.addnodes.pending_xref( + '', + sub_node.deepcopy(), + refdomain='py', + reftype='obj', + reftarget=entry.object_name, + refwarn=True, + refexplicit=True, + ) + sub_node.replace_self(xref_node) + break + + contentnode = cast(sphinx.addnodes.desc_content, objdesc.children[1]) + # Store synopsis in environment. + domain = cast(sphinx.domains.python.PythonDomain, self.env.get_domain('py')) + _get_python_object_synopses(domain)[ + entry.object_name] = contentnode.astext() + + if group_name is None: + group_name = _get_group_name(entry) + + return group_name, objdesc + + def _add_group_summary( + self, contentnode: docutils.nodes.Element, + sections: Dict[str, docutils.nodes.section], group_name: str, + group_members: List[Tuple[_MemberDocumenterEntry, sphinx.addnodes.desc]] + ) -> None: + toc_lines = '' + + group_id = docutils.nodes.make_id(group_name) + section = sections.get(group_id) + if section is None: + section = docutils.nodes.section() + section['ids'].append(group_id) + title = docutils.nodes.title('', group_name) + section += title + contentnode += section + sections[group_id] = section + + for entry, entry_node in group_members: + # FIXME(jbms): Currently sphinx does not have particularly good support + # for a page occurring in multiple places in the TOC, which occurs with + # inherited members. Nonetheless it mostly works. The next/prev ordering + # of documents is based on first occurrence of a document within pre-order + # traversal, which gives the correct result as long as the base class + # occurs before the derived class. The parent relationship for TOC + # collapsing, however, appears to be based on the last occurrence. + # Ideally we would be able to explicitly control the parent relationship + # for TOC collapsing, so that a member is listed under its "real" classs. + toc_lines += f'{entry.toc_title} <{entry.page_name}>\n' + section += entry_node - for part_sig, part_doc in _parse_overloaded_function_docstring(doc): - tab_width = self.directive.state.document.settings.tab_width - full_part_doc = '%s%s\n%s' % (self.object.__name__, part_sig, part_doc) - self._new_docstrings = [ - sphinx.util.docstrings.prepare_docstring(full_part_doc, 1, tab_width) - ] # pylint: disable=protected-access + section.extend( + sphinx_utils.parse_rst( + state=self.state, + text=sphinx_utils.format_directive('toctree', + options={'hidden': True}, + content=toc_lines), + source_path='tensorstore-apidoc', source_line=0)) - self.indent = old_indent - orig_generate(self, *args, **kwargs) - self.options.noindex = True + def _merge_summary_nodes_into(self, + contentnode: docutils.nodes.Element) -> None: + """Merges the member summary into `contentnode`. - documenter_cls.generate = generate + Members are organized into groups. The group is either specified explicitly + by a `Group:` field in the docstring, or determined automatically by + `_get_group_name`. If there is an existing section, the member summary is + appended to it. Otherwise, a new section is created. + Args: + contentnode: The existing container to which the member summaries will be + added. If `contentnode` contains sections, those sections correspond to + group names. + """ -def _monkey_patch_py_xref_mixin(): - PyXrefMixin = sphinx.domains.python.PyXrefMixin + sections: Dict[str, docutils.nodes.section] = {} + for section in contentnode.traverse(condition=docutils.nodes.section): + if section['ids']: + sections[section['ids'][0]] = section - def make_xrefs( - self, rolename: str, domain: str, target: str, - innernode: Type[docutils.nodes.TextElement] = docutils.nodes.emphasis, - contnode: docutils.nodes.Node = None, - env: sphinx.environment.BuildEnvironment = None - ) -> List[docutils.nodes.Node]: - return sphinx.domains.python._parse_annotation(target, env) + # Maps group name to the list of members and corresponding summary object + # description. + groups: Dict[str, List[Tuple[_MemberDocumenterEntry, + sphinx.addnodes.desc]]] = {} - PyXrefMixin.make_xrefs = make_xrefs + for entry in _get_documenter_members(self._documenter): + group_name, node = self._add_member_summary(entry) + groups.setdefault(group_name, []).append((entry, node)) + for group_name, group_members in groups.items(): + self._add_group_summary(contentnode=contentnode, sections=sections, + group_name=group_name, + group_members=group_members) -def _process_docstring(app: sphinx.application.Sphinx, what: str, name: str, - obj: Any, options: Any, lines: List[str]) -> None: - """Adds `:type : ` fields to docstrings based on annotations. + def _make_object_description(self): + assert not self.content + # In order to allow the actual summary entries to be inserted while the + # appropriate "py:class" and "py:module" context is set in + # ``self.env.ref_context``, instead of inserting them directly, we insert + # them from the _insert_autosummary function, which is registered with the + # object-description-transform signal. The global + # `_cur_autosummary_directive` variable is used to pass a reference to + # `self` to `_insert_autosummary`. + global _cur_autosummary_directive + _cur_autosummary_directive = self - This function is intended to be registered for the 'autodoc-process-docstring' - signal, and must be registered *after* sphinx.ext.napoleon if Google-style - docstrings are to be supported. + objtype = self._objtype - Sphinx allows the type of a parameter to be indicated by a `:type :` - field. However, in the TensorStore documentation all parameter types are - instead indicated by annotations in the signature. + try: + if objtype == 'function' or objtype.endswith('method'): + # Store the full function name in the ref_context in order to allow + # py:param references to be resolved relative to the current + # function/method. + self.env.ref_context['py:func'] = self._entry.object_name + objdesc, _ = self._generate_autodoc(self._entry) + finally: + self.env.ref_context.pop('py:func', None) - Sphinx provides the `autodoc_typehints='description'` option which adds `:type - ` fields based on the annotations. However, it has a number of - problematic limitations: + # If not set to None, it indicates that the _insert_autosummary was never + # called. + assert _cur_autosummary_directive is None - - It only supports real annotations, stored in the `__annotations__` attribute - of a function. It isn't compatible with the `autodoc_docstring_signature` - option, which we rely on for pybind11-defined functions and to support - overloaded functions. + domain = cast(sphinx.domains.python.PythonDomain, self.env.get_domain('py')) - - If you specify `autodoc_typehints='description'`, autodoc strips out - annotations from the signature. We would like to include them in both - places. + signode = cast(sphinx.addnodes.desc_signature, objdesc.children[0]) + _ensure_module_name_in_signature(signode) - - It adds a `:type` field for all parameters, even ones that are not - documented. + # Register this object with the Python domain. We have to do this + # manually since we specified :noindex:. + domain.note_object(name=self._entry.object_name, objtype=objtype, + node_id='', location=signode) - This function providse the same functionality as the - `autodoc_typehints='description'` option but without those limitations. + # Find parameter nodes in signature + sig_param_nodes: Dict[str, sphinx.addnodes.desc_parameter] = {} + for sig_param_node in signode.traverse( + condition=sphinx.addnodes.desc_parameter): + name = sig_param_node[0].astext() + sig_param_nodes[name] = sig_param_node + + # Add parameter links + for param_node in objdesc.traverse(condition=docutils.nodes.term): + paramname = param_node.get('paramname') + if not paramname: + continue + param_refid = f'p-{paramname}' + param_node['ids'].append(param_refid) + param_refname = f'{self._entry.object_name}.{paramname}' + + # Generate and store synopsis + _get_python_object_synopses( + domain)[param_refname] = sphinx_utils.summarize_element_text( + param_node.parent[-1]) + + domain.note_object(name=param_refname, objtype='parameter', + node_id=param_refid, location=param_node) + sig_param_node = sig_param_nodes.get(paramname) + if sig_param_node is not None: + first_child = sig_param_node[0] + del sig_param_node[0] + sig_param_ref = sphinx.addnodes.pending_xref('', first_child, + refdomain='py', + reftype='parameter', + reftarget=param_refname, + refwarn=True) + sig_param_node.insert(0, sig_param_ref) + + # Add ids to field_name nodes to include them in toc + for field_name in objdesc.traverse(condition=docutils.nodes.field_name): + field_name['ids'].append(docutils.nodes.make_id(field_name.astext())) + + # Wrap in a section + section = docutils.nodes.section() + section['ids'].append('') + # Sphinx treates the first child of a `section` node as the title, + # regardless of its type. We use a comment node to avoid adding a title + # that would be redundant with the object description. + section += docutils.nodes.comment('', self._entry.object_name) + section += objdesc + return [section] + + def run(self) -> List[docutils.nodes.Node]: + if 'objectdescription' in self.options: + return self._make_object_description() + + contentnode = docutils.nodes.section() + sphinx.util.nodes.nested_parse_with_titles(self.state, self.content, + contentnode) + self._merge_summary_nodes_into(contentnode) + return contentnode.children + + +# Outer autosummary directive that is currently processing an `auto{objtype}` +# directive. This is used by the `_insert_autosummary` function which is called +# from the `object-description-transform` signal to insert the autosummary +# members. By inserting the members within the context of the `auto{objtype}` +# directive, the appropriate "py:class" and "py:module" context in +# `env.ref_context` will be set. +_cur_autosummary_directive = None + + +def _insert_autosummary( + app: sphinx.application.Sphinx, domain: str, objtype: str, + content: docutils.nodes.Element) -> None: # pylint: disable=g-doc-args + """object-description-transform handler that inserts member summaries. + + This is a noop except when called indirectly from + `TensorstorePythonApidoc._make_object_description`. It allows the member + summaries to be generated with the appropriate `app.env.ref_context` set. + That way we don't have to attempt to replicate the logic from the sphinx + Python domain of setting the app.env.ref_context. """ - current_documenter_map = app.env.temp_data.get( - 'tensorstore_autodoc_current_documenter') - if not current_documenter_map: - return - documenter = current_documenter_map.get(name) - if not documenter: + del app + del domain + del objtype + global _cur_autosummary_directive + if _cur_autosummary_directive is None: return + directive = _cur_autosummary_directive + # Unset global variable to ensure it does not affect the summary entries + # themselves (which also use `auto{objtype}` directives). + _cur_autosummary_directive = None + directive._merge_summary_nodes_into(content) # pylint: disable=protected-access - def _get_field_pattern(field_names: List[str], param: str) -> str: - return '^:(?:' + '|'.join( - re.escape(name) - for name in field_names) + r')\s+' + re.escape(param) + ':' - - def insert_type(param: str, typ: str) -> None: - type_pattern = _get_field_pattern(['paramtype', 'type'], param) - pattern = _get_field_pattern([ - 'param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam' - ], param) - # First check if there is already a type field - for line in lines: - if re.match(type_pattern, line): - # :type: field already present for `param`, don't add another one. - return - # Only add :type: field if `param` is documented. - for i, line in enumerate(lines): - if re.match(pattern, line): - lines.insert(i, f':type {param}: {typ}') - return +class _FakeBridge(sphinx.ext.autodoc.directive.DocumenterBridge): - if not documenter.args: - return + def __init__(self, env: sphinx.environment.BuildEnvironment) -> None: + settings = docutils.parsers.rst.states.Struct(tab_width=8) + document = docutils.parsers.rst.states.Struct(settings=settings) + state = docutils.parsers.rst.states.Struct(document=document) + options = sphinx.ext.autodoc.Options() + options['undoc-members'] = True + options['noindex'] = True + super().__init__( + env=env, + reporter=sphinx.util.docutils.NullReporter(), + options=options, + lineno=0, + state=state, + ) + +_EXCLUDED_SPECIAL_MEMBERS = frozenset([ + '__module__', + '__abstractmethods__', + '__dict__', + '__weakref__', + '__class__', + '__base__', + # Exclude pickling members since they are never documented. + '__getstate__', + '__setstate__', + # Exclude __repr__ since it is not interesting + '__repr__', +]) + + +def _create_documenter(env: sphinx.environment.BuildEnvironment, + documenter_cls: Type[sphinx.ext.autodoc.Documenter], + name: str) -> sphinx.ext.autodoc.Documenter: + """Creates a documenter for the given full object name. + + Since we are using the documenter independent of any autodoc directive, we use + a `_FakeBridge` as the documenter bridge, similar to the strategy used by + `sphinx.ext.autosummary`. + + Args: + env: Sphinx build environment. + documenter_cls: Documenter class to use. + name: Full object name, e.g. `tensorstore.TensorStore.read`. + Returns: + The documenter object. + + """ + bridge = _FakeBridge(env) + documenter = documenter_cls(bridge, name) + assert documenter.parse_name() + assert documenter.import_object() + if documenter_cls.objtype == 'class': + bridge.genopt['special-members'] = [ + '__eq__', + '__getitem__', + '__setitem__', + # '__hash__', + '__init__', + '__class_getitem__', + '__call__', + '__array__', + ] try: - sig = sphinx.util.inspect.signature_from_str( - f'func({documenter.args}) -> None') - except: - # ignore errors + documenter.analyzer = sphinx.pycode.ModuleAnalyzer.for_module( + documenter.get_real_modname()) + # parse right now, to get PycodeErrors on parsing (results will + # be cached anyway) + documenter.analyzer.find_attr_docs() + except sphinx.pycode.PycodeError: + # no source file -- e.g. for builtin and C modules + documenter.analyzer = None + return documenter + + +def _get_member_documenter( + parent: sphinx.ext.autodoc.Documenter, member_name: str, member_value: Any, + is_attr: bool) -> Optional[sphinx.ext.autodoc.Documenter]: + """Creates a documenter for the given member. + + Args: + parent: Parent documenter. + member_name: Name of the member. + member_value: Value of the member. + is_attr: Whether the member is an attribute. + Returns: + The documenter object. + """ + classes = [ + cls for cls in parent.documenters.values() + if cls.can_document_member(member_value, member_name, is_attr, parent) + ] + if not classes: + return None + # prefer the documenter with the highest priority + classes.sort(key=lambda cls: cls.priority) + full_mname = parent.modname + '::' + '.'.join(parent.objpath + [member_name]) + documenter = _create_documenter(env=parent.env, documenter_cls=classes[-1], + name=full_mname) + return documenter + + +def _include_member(member_name: str, member_value: Any, is_attr: bool) -> bool: + """Determines whether a member should be documented. + + Args: + member_name: Name of the member. + member_value: Value of the member. + is_attr: Whether the member is an attribute. + Returns: + True if the member should be documented. + """ + del is_attr + if member_name == '__init__': + doc = getattr(member_value, '__doc__', None) + if isinstance(doc, str) and doc.startswith('Initialize self. '): + return False + elif member_name in ('__hash__', '__iter__'): + if member_value is None: + return False + return True + + +PRIVATE_TENSORSTORE_TYPE_RE = re.compile(r'(tensorstore\.(?:.*\.))?(_[^\.]+)') + + +def _get_subscript_method(parent_documenter: sphinx.ext.autodoc.Documenter, + entry: _MemberDocumenterEntry) -> Any: + """Checks for a property that defines a subscript method. + + A subscript method is a property like `Class.vindex` where `fget` has a return + type of `Class._Vindex`, which is a class type. + + Args: + parent_documenter: Parent documenter for `entry`. + entry: Entry to check. + + Returns: + The type object (e.g. `Class._Vindex`) representing the subscript method, or + None if `entry` does not define a subscript method. + """ + if not isinstance(entry.documenter, sphinx.ext.autodoc.PropertyDocumenter): + return None + retann = entry.documenter.retann + if not retann: + return None + match = PRIVATE_TENSORSTORE_TYPE_RE.fullmatch(retann) + if not match: + return None + + # Attempt to import value + mem = getattr(parent_documenter.object, match[2], None) + if not mem: + return None + getitem = getattr(mem, '__getitem__', None) + if getitem is None: + return None + + return mem + + +def _transform_member( + parent_documenter: sphinx.ext.autodoc.Documenter, + entry: _MemberDocumenterEntry) -> Iterator[_MemberDocumenterEntry]: + """Converts an individual member into a sequence of members to document. + + Args: + parent_documenter: The parent documenter. + entry: The original entry to document. For most entries we simply yield the + entry unmodified. For entries that correspond to subscript methods, + though, we yield the __getitem__ member (and __setitem__, if applicable) + separately. + + Yields: + Modified entries to document. + """ + if entry.name == '__class_getitem__': + entry = entry._replace(subscript=True) + + mem = _get_subscript_method(parent_documenter, entry) + if mem is None: + yield entry return + retann = entry.documenter.retann - for param in sig.parameters.values(): - if param.annotation is not param.empty: - insert_type(param.name, sphinx.util.typing.stringify(param.annotation)) - - -def _monkey_patch_python_type_to_xref(): - # Modified version of `sphinx.domains.python.type_to_xref` to handle - # TensorStore-specific aliases. - # - # This is monkey-patched in below. - def type_to_xref( - text: str, - env: sphinx.environment.BuildEnvironment) -> sphinx.addnodes.pending_xref: - reftarget = text - refdomain = 'py' - reftype = 'obj' - if text in ('Optional', 'List', 'Union', 'Dict'): - reftarget = 'typing.' + text - elif text == 'array': - reftarget = 'numpy.ndarray' - elif text == 'dtype': - reftarget = 'numpy.dtype' - elif text == 'array_like': - reftarget = 'numpy:array_like' - refdomain = 'std' - reftype = 'any' - elif text == 'DownsampleMethod': - reftarget = 'DownsampleMethod' - refdomain = 'json' - reftype = 'schema' - prefix = 'tensorstore.' - if text.startswith(prefix): - text = text[len(prefix):] - if env: - kwargs = { - 'py:module': env.ref_context.get('py:module'), - 'py:class': env.ref_context.get('py:class') - } + for suffix in ('__getitem__', '__setitem__'): + method = getattr(mem, suffix, None) + if method is None: + continue + import_name = f'{retann}.{suffix}' + if import_name.startswith(entry.documenter.modname + '.'): + import_name = (entry.documenter.modname + '::' + + import_name[len(entry.documenter.modname) + 1:]) + new_documenter = _create_documenter( + env=parent_documenter.env, + documenter_cls=sphinx.ext.autodoc.MethodDocumenter, name=import_name) + if suffix != '__getitem__': + new_member_name = f'{entry.name}.{suffix}' + full_name = f'{entry.full_name}.{suffix}' + subscript = False else: - kwargs = {} + new_member_name = f'{entry.name}' + full_name = entry.full_name + subscript = True + + yield _MemberDocumenterEntry( + documenter=new_documenter, + name=new_member_name, + is_attr=False, + import_name=import_name, + full_name=full_name, + subscript=subscript, + ) + + +def _get_member_overloads( + entry: _MemberDocumenterEntry) -> Iterator[_MemberDocumenterEntry]: + """Returns the list of overloads for a given entry.""" + overloads = _get_overloads_from_documenter(entry.documenter) + for overload in overloads: + # Shallow copy the documenter. Certain methods on the documenter mutate it, + # and we don't want those mutations to affect other overloads. + yield entry._replace(overload=overload, + documenter=copy.copy(entry.documenter)) + + +def _get_documenter_direct_members( + documenter: sphinx.ext.autodoc.Documenter +) -> Iterator[_MemberDocumenterEntry]: + """Yields the sequence of direct members to document. + + The order is mostly determined by the definition order. + + This excludes inherited members. + + Args: + documenter: Documenter for which to obtain members. + Yields: + Members to document. + """ + members_check_module, members = documenter.get_object_members(want_all=True) + del members_check_module + if members: + try: + # get_object_members does not preserve definition order, but __dict__ does + # in Python 3.6 and later. + member_dict = sphinx.util.inspect.safe_getattr(documenter.object, + '__dict__') + member_order = {k: i for i, k in enumerate(member_dict.keys())} + members.sort(key=lambda entry: member_order.get(entry[0], float('inf'))) + except AttributeError: + pass + filtered_members = [ + x for x in documenter.filter_members(members, want_all=True) + if _include_member(*x) + ] + for member_name, member_value, is_attr in filtered_members: + member_documenter = _get_member_documenter(parent=documenter, + member_name=member_name, + member_value=member_value, + is_attr=is_attr) + if member_documenter is None: + continue + full_name = f'{documenter.fullname}.{member_name}' + entry = _MemberDocumenterEntry( + cast(sphinx.ext.autodoc.Documenter, member_documenter), + is_attr, + member_name, + full_name=full_name, + import_name=full_name, + ) + for transformed_entry in _transform_member(documenter, entry): + yield from _get_member_overloads(transformed_entry) + + +def _get_documenter_members( + documenter: sphinx.ext.autodoc.Documenter +) -> Iterator[_MemberDocumenterEntry]: + """Yields the sequence of members to document, including inherited members. + + Args: + documenter: Parent documenter for which to find members. + Yields: + Members to document. + """ + seen_members: Set[str] = set() + + def _get_unseen_members( + members: Iterator[_MemberDocumenterEntry], + is_inherited: bool) -> Iterator[_MemberDocumenterEntry]: + for member in members: + overload_name = member.toc_title + if overload_name in seen_members: + continue + seen_members.add(overload_name) + yield member._replace(is_inherited=is_inherited) + + yield from _get_unseen_members(_get_documenter_direct_members(documenter), + is_inherited=False) + + if documenter.objtype != 'class': + return + + for cls in inspect.getmro(documenter.object): + if cls is documenter.object: + continue + if cls.__module__ in ('builtins', 'pybind11_builtins'): + continue + class_name = f'{cls.__module__}::{cls.__qualname__}' + try: + superclass_documenter = _create_documenter( + env=documenter.env, documenter_cls=sphinx.ext.autodoc.ClassDocumenter, + name=class_name) + yield from _get_unseen_members( + _get_documenter_direct_members(superclass_documenter), + is_inherited=True) + except Exception as e: + logger.warning('Cannot obtain documenter for base class %r of %r: %r', + cls, documenter.fullname, e) + + +def _write_member_documentation_pages( + documenter: sphinx.ext.autodoc.Documenter): + """Writes the RST files that document each member of `documenter`. + + This runs recursively and excludes inherited members, since they will be + handled by their own parent. + + This simply writes a `tensorstore-python-apidoc` directive to each generated + file. The actual documentation is generated by that directive. + + Args: + documenter: Parent documenter. + + """ + for entry in _get_documenter_members(documenter): + if entry.is_inherited: + continue + if (entry.overload and entry.overload.overload_id and + re.fullmatch('[0-9]+', entry.overload.overload_id)): + logger.warning('Unspecified overload id: %s', entry.object_name) + member_rst_path = os.path.join(documenter.env.app.srcdir, 'python', 'api', + entry.page_name + '.rst') + objtype = entry.documenter.objtype + member_content = '' + if objtype == 'class': + member_content += ':duplicate-local-toc:\n\n' + member_content += sphinx_utils.format_directive( + 'tensorstore-python-apidoc', + options=dict( + fullname=entry.full_name, + objtype=objtype, + importname=entry.import_name, + objectdescription=True, + subscript=entry.subscript, + overload=cast(ParsedOverload, entry.overload).overload_id, + ), + ) + pathlib.Path(member_rst_path).write_text(member_content) + _write_member_documentation_pages(entry.documenter) + + +def _builder_inited(app: sphinx.application.Sphinx) -> None: + """Generates the rST files for API members.""" + _write_member_documentation_pages( + _create_documenter(env=app.env, + documenter_cls=sphinx.ext.autodoc.ModuleDocumenter, + name='tensorstore')) + + +def _monkey_patch_napoleon_to_add_group_field(): + """Adds support to sphinx.ext.napoleon for the "Group" field. + + This field is used by this module to organize members into groups. + """ + GoogleDocstring = sphinx.ext.napoleon.docstring.GoogleDocstring # pylint: disable=invalid-name + orig_load_custom_sections = GoogleDocstring._load_custom_sections # pylint: disable=protected-access + + def parse_group_section(self: GoogleDocstring, section: str) -> List[str]: + del section + lines = self._strip_empty(self._consume_to_next_section()) # pylint: disable=protected-access + lines = self._dedent(lines) # pylint: disable=protected-access + if len(lines) != 1: + raise ValueError('Expected exactly one group in group section') + return [':group: ' + lines[0], ''] + + def load_custom_sections(self: GoogleDocstring) -> None: + orig_load_custom_sections(self) + self._sections['group'] = lambda section: parse_group_section(self, section) # pylint: disable=protected-access + + GoogleDocstring._load_custom_sections = load_custom_sections # pylint: disable=protected-access + + +def _monkey_patch_python_domain_to_support_titles(): + """Enables support for titles in all Python directive types. + + Normally sphinx only supports titles in `automodule`. We use titles to group + member summaries. + """ + + PyObject = sphinx.domains.python.PyObject # pylint: disable=invalid-name + orig_before_content = PyObject.before_content + + def before_content(self: PyObject) -> None: + orig_before_content(self) + self._saved_content = self.content # pylint: disable=protected-access + self.content = docutils.statemachine.StringList() + + orig_transform_content = PyObject.transform_content + + def transform_content(self: PyObject, + contentnode: docutils.nodes.Node) -> None: + sphinx.util.nodes.nested_parse_with_titles( + self.state, + self._saved_content, # pylint: disable=protected-access + contentnode) + orig_transform_content(self, contentnode) + + sphinx.domains.python.PyObject.before_content = before_content + sphinx.domains.python.PyObject.transform_content = transform_content + + +def _monkey_patch_python_domain_to_merge_object_synopses(): + """Modifies Python domain to properly merge synopses. + + The Python domain does not natively support synopses, but we add them in this + module, and need to take care to merge them when using Sphinx's parallel build + mode. + """ + PythonDomain = sphinx.domains.python.PythonDomain # pylint: disable=invalid-name + orig_merge_domaindata = PythonDomain.merge_domaindata + + def merge_domaindata( + self: PythonDomain, docnames: List[str], otherdata: dict) -> None: # pylint: disable=g-bare-generic + orig_merge_domaindata(self, docnames, otherdata) + _get_python_object_synopses(self).update( + otherdata.get(OBJECT_SYNOPSES_KEY, {})) + + PythonDomain.merge_domaindata = merge_domaindata + + +class PyParamXRefRole(sphinx.domains.python.PyXRefRole): + + def process_link(self, env: sphinx.environment.BuildEnvironment, + refnode: docutils.nodes.Element, has_explicit_title: bool, + title: str, target: str) -> Tuple[str, str]: + refnode['py:func'] = env.ref_context.get('py:func') + return super().process_link(env, refnode, has_explicit_title, title, target) + + +def _monkey_patch_python_domain_to_resolve_params(): + """Adds support to the Python domain for resolving parameter references.""" + + PythonDomain = sphinx.domains.python.PythonDomain # pylint: disable=invalid-name + orig_resolve_xref = PythonDomain.resolve_xref + + def resolve_xref( + self: PythonDomain, env: sphinx.environment.BuildEnvironment, + fromdocname: str, builder: sphinx.builders.Builder, typ: str, target: str, + node: sphinx.addnodes.pending_xref, + contnode: docutils.nodes.Element) -> Optional[docutils.nodes.Element]: + if typ == 'param': + func_name = node.get('py:func') + if func_name and '.' not in target: + return orig_resolve_xref(self, env, fromdocname, builder, typ, + '%s.%s' % (func_name, target), node, contnode) + + return orig_resolve_xref(self, env, fromdocname, builder, typ, target, node, + contnode) + + PythonDomain.resolve_xref = resolve_xref + + orig_resolve_any_xref = PythonDomain.resolve_any_xref + + def resolve_any_xref( + self: PythonDomain, env: sphinx.environment.BuildEnvironment, + fromdocname: str, builder: sphinx.builders.Builder, target: str, + node: sphinx.addnodes.pending_xref, contnode: docutils.nodes.Element + ) -> List[Tuple[str, docutils.nodes.Element]]: + results = orig_resolve_any_xref(self, env, fromdocname, builder, target, + node, contnode) + # Don't resolve parameters as any refs, as they introduce too many + # ambiguities. + return [r for r in results if r[0] != 'py:param'] + + PythonDomain.resolve_any_xref = resolve_any_xref + + +def _monkey_patch_python_domain_to_add_object_synopses_to_references(): + """Adds support to the Python domain for "object synopses". + + A synopsis is a brief description associated with an object that is displayed + as a tooltip (i.e. "title" attribute) on cross-references and is shown in + search results. + + The synopsis is taken from the first paragraph of the description, which is + also used for the "summary". + """ + PythonDomain = sphinx.domains.python.PythonDomain # pylint: disable=invalid-name + + def get_object_synopsis(self: PythonDomain, objtype: str, + name: str) -> Optional[str]: + del objtype + return _get_python_object_synopses(self).get(name) + + PythonDomain.get_object_synopsis = get_object_synopsis + + def _add_synopsis(self: PythonDomain, + refnode: docutils.nodes.Element) -> None: + name = refnode.get('reftitle') + entry = self.objects.get(name) + if entry is None: + return + label = self.get_type_name(self.object_types[entry.objtype]) + reftitle = f'{name} ({label})' + synopsis = _get_python_object_synopses(self).get(name) + if synopsis is not None: + synopsis = synopsis.strip() + if synopsis: + reftitle = f'{reftitle} — {synopsis}' + refnode['reftitle'] = reftitle + + orig_resolve_xref = PythonDomain.resolve_xref + + def resolve_xref( + self: PythonDomain, env: sphinx.environment.BuildEnvironment, + fromdocname: str, builder: sphinx.builders.Builder, typ: str, target: str, + node: sphinx.addnodes.pending_xref, + contnode: docutils.nodes.Element) -> Optional[docutils.nodes.Element]: + refnode = orig_resolve_xref(self, env, fromdocname, builder, typ, target, + node, contnode) + if refnode is not None: + _add_synopsis(self, refnode) + return refnode + + PythonDomain.resolve_xref = resolve_xref + + orig_resolve_any_xref = PythonDomain.resolve_any_xref + + def resolve_any_xref( + self: PythonDomain, env: sphinx.environment.BuildEnvironment, + fromdocname: str, builder: sphinx.builders.Builder, target: str, + node: sphinx.addnodes.pending_xref, contnode: docutils.nodes.Element + ) -> List[Tuple[str, docutils.nodes.Element]]: + results = orig_resolve_any_xref(self, env, fromdocname, builder, target, + node, contnode) + for _, refnode in results: + _add_synopsis(self, refnode) + return results + + PythonDomain.resolve_any_xref = resolve_any_xref + + +OBJECT_PRIORITY_DEFAULT = 1 +OBJECT_PRIORITY_IMPORTANT = 0 +OBJECT_PRIORITY_UNIMPORTANT = 2 +OBJECT_PRIORITY_EXCLUDE_FROM_SEARCH = -1 + + +def _monkey_patch_python_domain_to_deprioritize_params_in_search(): + """Ensures parameters have OBJECT_PRIORITY_UNIMPORTANT.""" + PythonDomain = sphinx.domains.python.PythonDomain # pylint: disable=invalid-name + orig_get_objects = PythonDomain.get_objects + + def get_objects( + self: PythonDomain) -> Iterator[Tuple[str, str, str, str, str, int]]: + for obj in orig_get_objects(self): + if obj[2] != 'parameter': + yield obj + else: + yield (obj[0], obj[1], obj[2], obj[3], obj[4], + OBJECT_PRIORITY_UNIMPORTANT) + + PythonDomain.get_objects = get_objects - return sphinx.addnodes.pending_xref('', docutils.nodes.Text(text), - refdomain=refdomain, reftype=reftype, - reftarget=reftarget, refwarn=True, - refexplicit=True, **kwargs) - # Monkey-patch in modified `type_to_xref` implementation. - sphinx.domains.python.type_to_xref = type_to_xref +sphinx.domains.python.PythonDomain.object_types[ + "parameter"] = sphinx.domains.ObjType("parameter", "param") def setup(app): - _monkey_patch_autodoc_function_documenter( - sphinx.ext.autodoc.FunctionDocumenter) - _monkey_patch_autodoc_function_documenter(sphinx.ext.autodoc.MethodDocumenter) - _monkey_patch_py_xref_mixin() - _monkey_patch_python_type_to_xref() - - app.setup_extension('sphinx.ext.autosummary') - # Must register `sphinx.ext.napoleon` first, since the `_process_docstring` - # handler for the `autodoc-process-docstring` event needs to run after the - # handler registered by `sphinx.ext.napoleon`. - app.setup_extension('sphinx.ext.napoleon') - app.add_directive('autosummary', TensorstoreAutosummary, override=True) - app.connect('autodoc-process-docstring', _process_docstring) + _monkey_patch_napoleon_to_add_group_field() + _monkey_patch_python_domain_to_support_titles() + _monkey_patch_python_domain_to_merge_object_synopses() + _monkey_patch_python_domain_to_resolve_params() + _monkey_patch_python_domain_to_add_object_synopses_to_references() + _monkey_patch_python_domain_to_deprioritize_params_in_search() + app.connect('builder-inited', _builder_inited) + app.connect('object-description-transform', _insert_autosummary) + app.add_directive('tensorstore-python-apidoc', TensorstorePythonApidoc) + app.add_role_to_domain('py', 'param', PyParamXRefRole()) return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/docs/tensorstore_sphinx_ext/jsonschema_sphinx.py b/docs/tensorstore_sphinx_ext/jsonschema_sphinx.py index 12a32aed5..865d71fca 100644 --- a/docs/tensorstore_sphinx_ext/jsonschema_sphinx.py +++ b/docs/tensorstore_sphinx_ext/jsonschema_sphinx.py @@ -874,7 +874,7 @@ def run(self): docname=self.env.docname, node_id=self._node_id, schema_id=self._schema_entry.id, - title=self._rendered_title, + synopsis=''.join(x.astext() for x in self._rendered_title), objtype='schema' if not self._nested else 'subschema', ) return super().run() @@ -915,8 +915,8 @@ class DomainSchemaEntry(NamedTuple): schema_id: str """Canonical schema id""" - title: Optional[List[docutils.nodes.Node]] - """Rendered text content of title""" + synopsis: Optional[str] + """Rendered text content of synopsis (title property)""" objtype: str """Object type, either 'schema' or 'subschema'""" @@ -1019,13 +1019,10 @@ def _make_refnode( contnode: docutils.nodes.Element, match: Tuple[str, DomainSchemaEntry]) -> docutils.nodes.Node: full_name, domain_entry = match - full_title = f'{full_name} (json)' - if domain_entry.title: - # FIXME: this won't work correctly if there are unresolved references in - # the title. - title = ''.join(x.astext() for x in domain_entry.title).strip() - if title: - full_title += f' — {title}' + label = self.get_type_name(self.object_types[domain_entry.objtype]) + full_title = f'{full_name} ({label})' + if domain_entry.synopsis: + full_title += f' — {domain_entry.synopsis}' return sphinx.util.nodes.make_refnode( builder=builder, @@ -1052,6 +1049,12 @@ def resolve_any_xref( contnode=contnode, match=match))) return results + def get_object_synopsis(self, objtype: str, name: str) -> Optional[str]: + entry = self.schemas.get(name) + if entry is None: + return None + return entry.synopsis + def setup(app: sphinx.application.Sphinx): app.add_domain(JsonSchemaDomain) diff --git a/docs/tensorstore_sphinx_ext/mathjax.py b/docs/tensorstore_sphinx_ext/mathjax.py new file mode 100644 index 000000000..35e1ad23c --- /dev/null +++ b/docs/tensorstore_sphinx_ext/mathjax.py @@ -0,0 +1,52 @@ +# Copyright 2020 The TensorStore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""MathJax configuration for TensorStore. + +We can't use the normal MathJax configuration option provided by sphinx because +it does not support MathJax version 3. +""" + +import json +from typing import cast + +import sphinx.application +import sphinx.builders.html +import sphinx.domains.math +import sphinx.environment + + +def _install_mathjax(app: sphinx.application.Sphinx, + env: sphinx.environment.BuildEnvironment) -> None: + """Adds the MathJax configuration if needed.""" + builder = cast(sphinx.builders.html.StandaloneHTMLBuilder, app.builder) + domain = cast(sphinx.domains.math.MathDomain, env.get_domain('math')) + if not domain.has_equations(): + return + mathjax_config = { + 'chtml': { + 'displayAlign': 'left', + }, + } + builder.add_js_file( + None, + body=f'window.MathJax = {json.dumps(mathjax_config)};') # type: ignore + + +def setup(app: sphinx.application.Sphinx): + # Set the mathjax configuration manually, since Sphinx v3 does not set it + # correctly. + # + # The mathjax_config configuration option should not be used. + app.connect('env-updated', _install_mathjax) + return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/docs/tensorstore_sphinx_ext/sphinx_utils.py b/docs/tensorstore_sphinx_ext/sphinx_utils.py index ce5c54c1a..694d4b115 100644 --- a/docs/tensorstore_sphinx_ext/sphinx_utils.py +++ b/docs/tensorstore_sphinx_ext/sphinx_utils.py @@ -87,3 +87,23 @@ def parse_rst(state: docutils.parsers.rst.states.RSTState, text: str, node.document = state.document state.nested_parse(content, 0, node) return node.children + + +def summarize_element_text(node: docutils.nodes.Element) -> str: + """Extracts a short text synopsis, e.g. for use as a tooltip.""" + + # Recurisvely extract first paragraph + while True: + for p in node.traverse(condition=docutils.nodes.paragraph): + if p is node: + continue + node = p + break + else: + break + + text = node.astext() + sentence_end = text.find('. ') + if sentence_end != -1: + text = text[:sentence_end + 1] + return text diff --git a/docs/tensorstore_sphinx_material/.style.yapf b/docs/tensorstore_sphinx_material/.style.yapf new file mode 100644 index 000000000..557fa7bf8 --- /dev/null +++ b/docs/tensorstore_sphinx_material/.style.yapf @@ -0,0 +1,2 @@ +[style] +based_on_style = pep8 diff --git a/docs/tensorstore_sphinx_material/BUILD b/docs/tensorstore_sphinx_material/BUILD new file mode 100644 index 000000000..e11cf9baf --- /dev/null +++ b/docs/tensorstore_sphinx_material/BUILD @@ -0,0 +1,163 @@ +load("//docs/tensorstore_sphinx_material:nodejs_rules.bzl", "nodejs_binary") +load("@bazel_skylib//rules:copy_file.bzl", "copy_file") +load("@bazel_skylib//lib:shell.bzl", "shell") +load(":icons.bzl", "icon_symlinks") + +package( + default_visibility = ["//docs:__subpackages__"], +) + +licenses(["notice"]) + +ICON_PACKAGES = [ + "@npm//@fortawesome/fontawesome-free", + "@npm//@mdi/svg", + "@npm//@primer/octicons", +] + +ICON_ALIASES = { + "material": "@mdi/svg/svg/", + "octicons": "@primer/octicons/build/svg/", + "fontawesome": "@fortawesome/fontawesome-free/svgs/", +} + +nodejs_binary( + name = "build_css", + srcs = ["build_css.js"], + entry_point = ":build_css.js", + deps = [ + "@npm//autoprefixer", + "@npm//cssnano", + "@npm//material-design-color", + "@npm//material-shadows", + "@npm//postcss", + "@npm//postcss-inline-svg", + "@npm//sass", + ] + ICON_PACKAGES, +) + +genrule( + name = "build_css_genrule", + srcs = glob([ + "src/assets/stylesheets/**/*.scss", + ]), + outs = [ + "sphinx_material/static/stylesheets/main.css", + "sphinx_material/static/stylesheets/main.css.map", + "sphinx_material/static/stylesheets/palette.css", + "sphinx_material/static/stylesheets/palette.css.map", + ], + cmd = "$(location :build_css) " + + shell.quote(json.encode(ICON_ALIASES)) + " " + + "$(location src/assets/stylesheets/main.scss) " + + "$(location src/assets/stylesheets/palette.scss) " + + "$(OUTS)" + + select({ + "//docs:no_minify_setting": "", + "//conditions:default": " --optimize", + }), + tags = ["no-sandbox"], + tools = [ + ":build_css", + ], +) + +nodejs_binary( + name = "build_js", + srcs = ["build_js.js"] + glob([ + "src/assets/javascripts/**", + "tsconfig.json", + "typings/**/*.d.ts", + ]), + entry_point = ":build_js.js", + deps = [ + "@npm//@types/clipboard", + "@npm//@types/escape-html", + "@npm//@types/resize-observer-browser", + "@npm//clipboard", + "@npm//esbuild", + "@npm//escape-html", + "@npm//focus-visible", + "@npm//github-types", + "@npm//preact", + "@npm//rxjs", + ], +) + +genrule( + name = "build_js_genrule", + outs = [ + "sphinx_material/static/javascripts/bundle.js", + "sphinx_material/static/javascripts/bundle.js.map", + ], + cmd = "./$(location :build_js) $(OUTS)" + + select({ + "//docs:no_minify_setting": "", + "//conditions:default": " --optimize", + }), + tags = ["no-sandbox"], + tools = [":build_js"], +) + +HTML_TEMPLATE_SRC_PREFIX = "src/" + +HTML_TEMPLATE_SRCS = glob([HTML_TEMPLATE_SRC_PREFIX + "**/*.html"]) + +[ + copy_file( + name = x + "_copy_rule", + src = x, + out = "sphinx_material/" + x[len(HTML_TEMPLATE_SRC_PREFIX):], + allow_symlink = True, + ) + for x in HTML_TEMPLATE_SRCS +] + +HTML_TEMPLATES_MINIFIED = [ + "sphinx_material/" + x[len(HTML_TEMPLATE_SRC_PREFIX):] + for x in HTML_TEMPLATE_SRCS +] + +nodejs_binary( + name = "minify_html", + srcs = ["minify_html.js"], + entry_point = ":minify_html.js", + deps = [ + "@npm//html-minifier", + ], +) + +filegroup( + name = "html_templates", + srcs = HTML_TEMPLATES_MINIFIED, +) + +icon_symlinks( + name = "icons", + aliases = { + target_prefix: "sphinx_material/.icons/" + alias + "/" + for alias, target_prefix in ICON_ALIASES.items() + }, + deps = ICON_PACKAGES, +) + +py_library( + name = "tensorstore_sphinx_material", + srcs = glob(["sphinx_material/**/*.py"]), + data = [ + "sphinx_material/theme.conf", + ":html_templates", + ":icons", + ":sphinx_material/static/javascripts/bundle.js", + ":sphinx_material/static/javascripts/bundle.js.map", + ":sphinx_material/static/stylesheets/main.css", + ":sphinx_material/static/stylesheets/main.css.map", + ":sphinx_material/static/stylesheets/palette.css", + ":sphinx_material/static/stylesheets/palette.css.map", + ], + deps = [ + "@pypa_docutils//:docutils", + "@pypa_jinja2//:jinja2", + "@pypa_sphinx//:sphinx", + ], +) diff --git a/docs/tensorstore_sphinx_material/LICENSE b/docs/tensorstore_sphinx_material/LICENSE new file mode 100644 index 000000000..98aadb8f8 --- /dev/null +++ b/docs/tensorstore_sphinx_material/LICENSE @@ -0,0 +1,51 @@ +Copyright (c) 2019 Kevin Sheppard + +Derived from: + * Material for Mkdocs: Copyright (c) 2016-2019 Martin Donath + * Guzzle Sphinx Theme: Copyright (c) 2013 Michael Dowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +Files: sphinx_material/inlinesyntaxhighlight.py + +Copyright (c) 2013, Kay-Uwe (Kiwi) Lorenz + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL "Kay-Uwe (Kiwi) Lorenz" BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/tensorstore_sphinx_material/MKDOCS_MATERIAL_MERGE_BASE b/docs/tensorstore_sphinx_material/MKDOCS_MATERIAL_MERGE_BASE new file mode 100644 index 000000000..6e4a9c83c --- /dev/null +++ b/docs/tensorstore_sphinx_material/MKDOCS_MATERIAL_MERGE_BASE @@ -0,0 +1 @@ +285ad883883311a72b9adf8345ca7d919ba491cb diff --git a/docs/tensorstore_sphinx_material/README.md b/docs/tensorstore_sphinx_material/README.md new file mode 100644 index 000000000..b3966b556 --- /dev/null +++ b/docs/tensorstore_sphinx_material/README.md @@ -0,0 +1,39 @@ +Material design theme for sphinx +================================ + +This theme is a port of the +[mkdocs-material](https://github.com/squidfunk/mkdocs-material) theme to work +with sphinx. + +The port has been done in such a way as to retain the file structure of the +mkdocs-material repository, and avoid unncessary differences, in order to allow +changes to be merged bidirectionally relatively easily. + +The merge base within the mkdocs-material repository is indicated by +the `MKDOCS_MATERIAL_MERGE_BASE` file. + +This theme is derived from https://github.com/bashtage/sphinx-material, which is +a port of an older version of mkdocs-material. + +Features +-------- + +Essentially all of the features of the mkdocs-material theme are supported. + +In addition, this theme has the following additional features: + +- The mkdocs-material theme relies on a lunr.js search index produced by the + mkdocs search plugin. Sphinx generates its own search index in a custom + format. This theme replaces the mkdocs search backend logic with new logic + for querying the sphinx search index and incrementally displaying snippets as + you scroll. + +- An additional "hero" heading may be added to pages. This was supported by a + previous version of mkdocs-material, but was removed. This theme "forward + ports" that feature. + +- Specific styling of Sphinx "object descriptions" (e.g. Python + classes/functions/methods) is included. + +- Object descriptions are added to the table of contents, with an icon that + depends on the object type. diff --git a/docs/tensorstore_sphinx_material/build_css.js b/docs/tensorstore_sphinx_material/build_css.js new file mode 100644 index 000000000..fe25f524d --- /dev/null +++ b/docs/tensorstore_sphinx_material/build_css.js @@ -0,0 +1,78 @@ +const postcss = require('postcss'); +const sass = require('sass'); +const {promisify} = require('util'); + +const fs = require('fs').promises; +const path = require('path'); + +if (!process.env.HOME) { + // When invoked by Bazel, HOME may be unset which can cause problems with + // uv_os_homedir. + process.env.HOME = process.cwd(); +} + +function resolvePackageDir(package) { + return path.dirname(require.resolve(package + '/package.json')); +} + +function convertIconPaths(cssContent, iconAliases) { + const pattern = new RegExp( + '([\'"])(' + Object.keys(iconAliases).join('|') + + ')/([/a-z0-9\\-_]+\\.svg)\\1', + 'g'); + return cssContent.toString().replace(pattern, (match, quote, alias, name) => { + return require.resolve(iconAliases[alias] + name); + }); +} + +async function transformStyle( + iconAliases, inputPath, outputPath, outputMapPath) { + const result = sass.renderSync({ + file: inputPath, + outFile: outputPath, + includePaths: [ + resolvePackageDir('material-design-color'), + resolvePackageDir('material-shadows'), + ], + sourceMap: true, + sourceMapContents: true + }); + let {css: css0, map: map0} = result; + css0 = convertIconPaths(css0, iconAliases); + const {css: css1, map: map1} = + await postcss([ + require('autoprefixer'), require('postcss-inline-svg')({ + encode: false, + }), + ...process.argv.includes('--optimize') ? [require('cssnano')] : [] + ]).process(css0, { + from: inputPath, + map: { + prev: `${map0}`, + inline: false, + }, + }); + let missingSvgs = false; + for (const match of css1.matchAll(/svg-load\([^\)]*\)/g)) { + console.log('Failed to inline: ' + match[0]); + missingSvgs = true; + } + if (missingSvgs) { + process.exit(1); + } + await fs.writeFile( + outputPath, + css1.replace( + /(sourceMappingURL=)(.*)/, `$1${path.basename(outputPath)}.map\n`)); + await fs.writeFile(outputMapPath, map1); +} + +async function main() { + const [, , iconAliasesJson, inputPath1, inputPath2, outputPath1, outputMapPath1, outputPath2, outputMapPath2] = + process.argv; + const iconAliases = JSON.parse(iconAliasesJson); + await transformStyle(iconAliases, inputPath1, outputPath1, outputMapPath1); + await transformStyle(iconAliases, inputPath2, outputPath2, outputMapPath2); +} + +main(); diff --git a/docs/tensorstore_sphinx_material/build_js.js b/docs/tensorstore_sphinx_material/build_js.js new file mode 100644 index 000000000..f6805573c --- /dev/null +++ b/docs/tensorstore_sphinx_material/build_js.js @@ -0,0 +1,24 @@ +const esbuild = require('esbuild'); +const path = require('path'); + +if (!process.env.HOME) { + // When invoked by Bazel, HOME may be unset which can cause problems with + // uv_os_homedir. + process.env.HOME = process.cwd(); +} + +async function main() { + const [, , outputPath, outputMapPath] = process.argv; + const inputPath = path.join(__dirname, 'src/assets/javascripts/bundle.ts'); + await esbuild.build({ + entryPoints: [inputPath], + target: 'es2015', + outfile: outputPath, + preserveSymlinks: true, + bundle: true, + sourcemap: true, + minify: process.argv.includes('--optimize') + }); +} + +main(); diff --git a/docs/tensorstore_sphinx_material/icons.bzl b/docs/tensorstore_sphinx_material/icons.bzl new file mode 100644 index 000000000..ee2ac5231 --- /dev/null +++ b/docs/tensorstore_sphinx_material/icons.bzl @@ -0,0 +1,39 @@ +_ICONS_ATTRS = { + "deps": attr.label_list(), + "aliases": attr.string_dict(), +} + +def _icons_impl(ctx): + runfiles = [] + source_files = [] + for dep in ctx.attr.deps: + if DefaultInfo not in dep: + continue + dep_runfiles = dep[DefaultInfo].default_runfiles + if not dep_runfiles: + continue + source_files.append(dep_runfiles.files) + for f in depset(transitive = source_files).to_list(): + for alias_source, alias_target in ctx.attr.aliases.items(): + idx = f.short_path.find("/" + alias_source) + if idx == -1: + continue + symlink_name = alias_target + f.short_path[idx + 1 + len(alias_source):] + symlink = ctx.actions.declare_file(symlink_name) + runfiles.append(symlink) + ctx.actions.symlink(output = symlink, target_file = f) + + return struct( + providers = [ + DefaultInfo( + runfiles = ctx.runfiles( + files = runfiles, + ), + ), + ], + ) + +icon_symlinks = rule( + implementation = _icons_impl, + attrs = _ICONS_ATTRS, +) diff --git a/docs/tensorstore_sphinx_material/merge_from_mkdocs_material.py b/docs/tensorstore_sphinx_material/merge_from_mkdocs_material.py new file mode 100755 index 000000000..66eca8622 --- /dev/null +++ b/docs/tensorstore_sphinx_material/merge_from_mkdocs_material.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 + +"""Applies changes from mkdocs-material to the current directory. + +The upstream mkdocs-material revision on which the current directory is based is +assumed to be stored in the MKDOCS_MATERIAL_MERGE_BASE file. + +This script computes the differences between that revision and the current +master branch, and then applies them to the current directory via 3-way merge. +If there are conflicts, conflict markers are left in modified files. +""" + +import argparse +import contextlib +import json +import os +import pathlib +import shutil +import subprocess +import tempfile + +MKDOCS_EXCLUDE_PATTERNS = [ + # mkdocs-specific configuration files + '.gitignore', + '.gitattributes', + '.github', + '.browserslistrc', + '.dockerignore', + 'requirements.txt', + 'setup.py', + 'Dockerfile', + 'MANIFEST.in', + + # Generated files + 'material', + + # mkdocs-specific files + 'src/*.py', + 'src/mkdocs_theme.yml', + 'src/404.html', + 'mkdocs.yml', + + # Unneeded files + 'typings/lunr', + 'src/assets/javascripts/browser/worker', + 'src/assets/javascripts/integrations/search/worker', + + # Files specific to mkdocs' own documentation + 'src/overrides', + 'src/assets/images/favicon.png', + 'src/.icons/logo.*', + 'docs', + 'LICENSE', + 'CHANGELOG', + 'package-lock.json', + '*.md', +] + +ap = argparse.ArgumentParser() +ap.add_argument('--clone-dir', type=str, default='/tmp/mkdocs-material'), +ap.add_argument('--patch-output', + type=str, + default='/tmp/mkdocs-material-patch') +ap.add_argument('--source-ref', type=str, default='origin/master') +ap.add_argument('--keep-temp', + action='store_true', + help='Keep temporary workdir') +ap.add_argument('--dry-run', + action='store_true', + help='Just print the patch but do not apply.') +args = ap.parse_args() +source_ref = args.source_ref + +script_dir = os.path.dirname(__file__) + +merge_base_path = os.path.join(script_dir, 'MKDOCS_MATERIAL_MERGE_BASE') +merge_base = pathlib.Path(merge_base_path).read_text().strip() + +clone_dir = args.clone_dir + +if not os.path.exists(clone_dir): + subprocess.run( + [ + 'git', 'clone', 'https://github.com/squidfunk/mkdocs-material', + clone_dir + ], + check=True, + ) +else: + subprocess.run( + ['git', 'fetch', 'origin'], + cwd=clone_dir, + check=True, + ) + + +def _fix_package_json(path: pathlib.Path) -> None: + content = json.loads(path.read_text(encoding='utf-8')) + content.pop('version', None) + content['dependencies'].pop('lunr') + content['dependencies'].pop('lunr-languages') + content['devDependencies'].pop('@types/lunr') + path.write_text(json.dumps(content, indent=2) + '\n', encoding='utf-8') + + +def _resolve_ref(ref: str) -> str: + return subprocess.run(['git', 'rev-parse', ref], + encoding='utf-8', + cwd=clone_dir, + check=True, + stdout=subprocess.PIPE).stdout.strip() + + +@contextlib.contextmanager +def _temp_worktree_path(): + if args.keep_temp: + temp_workdir = tempfile.mkdtemp() + yield temp_workdir + return + with tempfile.TemporaryDirectory() as temp_workdir: + try: + yield temp_workdir + finally: + subprocess.run( + ['git', 'worktree', 'remove', '--force', temp_workdir], + cwd=clone_dir, + check=True, + ) + + +def _create_adjusted_tree(ref: str, temp_workdir: str) -> str: + print(f'Checking out {source_ref} -> {temp_workdir}') + subprocess.run( + ['git', 'worktree', 'add', '--detach', temp_workdir, ref], + cwd=clone_dir, + check=True, + ) + subprocess.run( + ['git', 'rm', '--quiet', '-r'] + MKDOCS_EXCLUDE_PATTERNS, + cwd=temp_workdir, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + _fix_package_json(pathlib.Path(temp_workdir) / 'package.json') + subprocess.run( + [ + 'git', + 'commit', + '--no-verify', + '-a', + '-m', + 'Exclude files', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=temp_workdir, + check=True, + ) + return subprocess.run(['git', 'rev-parse', 'HEAD'], + cwd=temp_workdir, + check=True, + encoding='utf-8', + stdout=subprocess.PIPE).stdout.strip() + + +resolved_source_ref = _resolve_ref(args.source_ref) + +with _temp_worktree_path() as temp_workdir: + new_tree_commit = _create_adjusted_tree(resolved_source_ref, temp_workdir) + +patch_path = args.patch_output + +with _temp_worktree_path() as temp_workdir: + old_tree_commit = _create_adjusted_tree(merge_base, temp_workdir) + subprocess.run( + ['git', 'rm', '-r', '.'], + cwd=temp_workdir, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + shutil.copytree(script_dir, temp_workdir, dirs_exist_ok=True) + subprocess.run( + [ + 'git', + 'add', + '-A', + '--force', + '.', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=temp_workdir, + check=True, + ) + subprocess.run( + [ + 'git', + 'commit', + '--no-verify', + '-a', + '-m', + 'Working changes', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=temp_workdir, + check=True, + ) + + with tempfile.NamedTemporaryFile(mode='wb') as patch_f: + subprocess.run( + ['git', 'diff', f'{old_tree_commit}..{new_tree_commit}'], + cwd=clone_dir, + stdout=patch_f, + check=True) + patch_f.flush() + subprocess.run(['git', 'apply', '--3way', patch_f.name], + cwd=temp_workdir) + with open(patch_path, 'wb') as patch_f: + subprocess.run(['git', 'diff', 'HEAD'], + check=True, + cwd=temp_workdir, + stdout=patch_f) + +print('Patch in: ' + patch_path) + +if not args.dry_run: + subprocess.run(['patch', '-p1'], + stdin=open(patch_path, 'rb'), + check=True, + cwd=script_dir) + pathlib.Path(merge_base_path).write_text(resolved_source_ref + '\n') +else: + print(pathlib.Path(patch_path).read_text()) diff --git a/docs/tensorstore_sphinx_material/minify_html.js b/docs/tensorstore_sphinx_material/minify_html.js new file mode 100644 index 000000000..659b7c7c9 --- /dev/null +++ b/docs/tensorstore_sphinx_material/minify_html.js @@ -0,0 +1,41 @@ +const minhtml = require('html-minifier').minify; +const fs = require('fs').promises; + +if (!process.env.HOME) { + // When invoked by Bazel, HOME may be unset which can cause problems with + // uv_os_homedir. + process.env.HOME = process.cwd(); +} + +async function main() { + const sourcesAndOutputs = process.argv.slice(2); + if (sourcesAndOutputs.length % 2 !== 0) { + console.log('Expected an even number of arguments'); + process.exit(1); + } + + const numInputs = sourcesAndOutputs.length / 2; + for (let i = 0; i < numInputs; ++i) { + const inputPath = sourcesAndOutputs[i]; + const outputPath = sourcesAndOutputs[i + numInputs]; + + const data = await fs.readFile(inputPath, 'utf8'); + let html = data.replace(/\r\n/gm, '\n'); + html = minhtml(html, { + collapseBooleanAttributes: true, + includeAutoGeneratedTags: false, + collapseWhitespace: true, + minifyCSS: true, + minifyJS: true, + removeComments: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }) + .replace( + /* Remove empty lines without collapsing everything */ + /^\s*[\r\n]/gm, ''); + await fs.writeFile(outputPath, html); + } +} + +main(); diff --git a/docs/tensorstore_sphinx_material/nodejs_rules.bzl b/docs/tensorstore_sphinx_material/nodejs_rules.bzl new file mode 100644 index 000000000..a353d1b2c --- /dev/null +++ b/docs/tensorstore_sphinx_material/nodejs_rules.bzl @@ -0,0 +1,8 @@ +load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary") + +def nodejs_binary(name, srcs = [], deps = [], data = [], templated_args = [], **kwargs): + _nodejs_binary( + name = name, + data = srcs + deps + data, + **kwargs + ) diff --git a/docs/tensorstore_sphinx_material/package-lock.json b/docs/tensorstore_sphinx_material/package-lock.json new file mode 100644 index 000000000..d50a32aa6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/package-lock.json @@ -0,0 +1,7774 @@ +{ + "name": "sphinx-material", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/compat-data": { + "version": "7.13.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.6.tgz", + "integrity": "sha512-VhgqKOWYVm7lQXlvbJnWOzwfAQATd2nV52koT0HZ/LdDH0m4DUDwkKYsH+IwpXb+bKPyBJzawA4I6nBKqZcpQw==", + "dev": true + }, + "@babel/core": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.1.tgz", + "integrity": "sha512-FzeKfFBG2rmFtGiiMdXZPFt/5R5DXubVi82uYhjGX4Msf+pgYQMCFIqFXZWs5vbIYbf14VeBIgdGI03CDOOM1w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.0", + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helpers": "^7.13.0", + "@babel/parser": "^7.13.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "7.0.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.0.tgz", + "integrity": "sha512-zBZfgvBB/ywjx0Rgc2+BwoH/3H+lDtlgD4hBOpEv5LxRnYsm/753iRuLepqnYlynpjC3AdQxtxsoeHJoEEwOAw==", + "dev": true, + "requires": { + "@babel/types": "^7.13.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.0.tgz", + "integrity": "sha512-SOWD0JK9+MMIhTQiUVd4ng8f3NXhPVQvTv7D3UN4wbp/6cAHnB2EmMaU1zZA2Hh1gwme+THBrVSqTFxHczTh0Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz", + "integrity": "sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ==", + "dev": true, + "requires": { + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-transforms": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz", + "integrity": "sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-replace-supers": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz", + "integrity": "sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.0.tgz", + "integrity": "sha512-aan1MeFPxFacZeSz6Ld7YZo5aPuqnKlD7+HZY75xQsueczFccP9A7V05+oe0XpLwHK3oLorPe9eaAUljL7WEaQ==", + "dev": true, + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.13.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.4.tgz", + "integrity": "sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + } + } + }, + "@babel/traverse": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.0", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz", + "integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@es-joy/jsdoccomment": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.6.0.tgz", + "integrity": "sha512-zT1EtysKMITJ7vE4RvOJqitxk/Str6It8hq+fykxkwLuTyzgak+TnVuVSIyovT/qrEz3i46ypCSXgNtIDYwNOg==", + "dev": true, + "requires": { + "comment-parser": "^1.1.5", + "esquery": "^1.4.0", + "jsdoctypeparser": "^9.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", + "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "@fortawesome/fontawesome-free": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz", + "integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==", + "dev": true + }, + "@mdi/svg": { + "version": "5.9.55", + "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-5.9.55.tgz", + "integrity": "sha512-gO0ZpKIeCn9vFg46QduK9MM+n1fuCNwSdcdlBTtbafnnuvwLveK2uj+byhdLtg/8VJGXDhp+DJ35QUMbeWeULA==", + "dev": true + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@primer/octicons": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-14.1.0.tgz", + "integrity": "sha512-I/gRlM2meKPKXFN/1fxLoigPXvAUsivxRCih7vgeO7o4qrNNsl6Ah85l3UBbFi0t7ttjMde2+bS1A32a1Hu0BA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1" + } + }, + "@stylelint/postcss-css-in-js": { + "version": "0.37.2", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz", + "integrity": "sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA==", + "dev": true, + "requires": { + "@babel/core": ">=7.9.0" + } + }, + "@stylelint/postcss-markdown": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz", + "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", + "dev": true, + "requires": { + "remark": "^13.0.0", + "unist-util-find-all-after": "^3.0.2" + } + }, + "@trysound/sax": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", + "integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==", + "dev": true + }, + "@types/clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-ET0ldU/vpXecy5vO8JRIhtJWSrk1vzXdJcp3Bjf8bARZynl6vfkhEKY/A7njfNIRlmyTGuVFuqnD6I3tOGdXpQ==", + "dev": true, + "requires": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, + "@types/clipboard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz", + "integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==", + "dev": true + }, + "@types/escape-html": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.1.tgz", + "integrity": "sha512-4mI1FuUUZiuT95fSVqvZxp/ssQK9zsa86S43h9x3zPOSU9BBJ+BfDkXwuaU7BfsD+e7U0/cUUfJFk3iW2M4okA==", + "dev": true + }, + "@types/fuzzaldrin-plus": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@types/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.1.tgz", + "integrity": "sha512-UFGM/hVBPlttAqSDMbYdupckngYNY/DAYBPHrHw4Pl2bK3mPwSabhkRHK1uK9udi5KZG/qX7D6z1/Jo5smTJFw==", + "dev": true + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-eFnGhrKmjWBlnSGNtunetE3UU2Tc/LUl92htFslSSTmpp9EKHQVcYQadCyYfnzUEFB5G/3wLWo/USQS/mEPKrA==", + "dev": true, + "requires": { + "@types/clean-css": "*", + "@types/relateurl": "*", + "@types/uglify-js": "*" + } + }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", + "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true + }, + "@types/node": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.1.tgz", + "integrity": "sha512-weaeiP4UF4XgF++3rpQhpIJWsCTS4QJw5gvBhQu6cFIxTwyxWIe3xbnrY/o2lTCQ0lsdb8YIUDUvLR4Vuz5rbw==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/relateurl": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz", + "integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=", + "dev": true + }, + "@types/resize-observer-browser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz", + "integrity": "sha512-8k/67Z95Goa6Lznuykxkfhq9YU3l1Qe6LNZmwde1u7802a3x8v44oq0j91DICclxatTr0rNnhXx7+VTIetSrSQ==", + "dev": true + }, + "@types/sass": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz", + "integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/uglify-js": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.12.0.tgz", + "integrity": "sha512-sYAF+CF9XZ5cvEBkI7RtrG9g2GtMBkviTnBxYYyq+8BWvO4QtXfwwR6a2LFwCi4evMKZfpv6U43ViYvv17Wz3Q==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "@types/vfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz", + "integrity": "sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/unist": "*", + "@types/vfile-message": "*" + } + }, + "@types/vfile-message": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-2.0.0.tgz", + "integrity": "sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw==", + "dev": true, + "requires": { + "vfile-message": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz", + "integrity": "sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.24.0", + "@typescript-eslint/scope-manager": "4.24.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz", + "integrity": "sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.24.0", + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/typescript-estree": "4.24.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.24.0.tgz", + "integrity": "sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.24.0", + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/typescript-estree": "4.24.0", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz", + "integrity": "sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/visitor-keys": "4.24.0" + } + }, + "@typescript-eslint/types": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.24.0.tgz", + "integrity": "sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz", + "integrity": "sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/visitor-keys": "4.24.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz", + "integrity": "sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.24.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz", + "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==", + "dev": true, + "requires": { + "browserslist": "^4.16.3", + "caniuse-lite": "^1.0.30001196", + "colorette": "^1.2.2", + "fraction.js": "^4.0.13", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30001204", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz", + "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==", + "dev": true + } + } + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001192", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz", + "integrity": "sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==", + "dev": true + }, + "ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true + }, + "character-entities-html4": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", + "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", + "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==", + "dev": true + }, + "clipboard": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", + "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "requires": { + "is-regexp": "^2.0.0" + } + }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "comment-parser": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.5.tgz", + "integrity": "sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "contains-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-1.0.0.tgz", + "integrity": "sha1-NFizMhhWA+ju0Y9RjUoQiIo6vJE=", + "dev": true, + "requires": { + "normalize-path": "^2.1.1", + "path-starts-with": "^1.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "dev": true + }, + "css-declaration-sorter": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.0.0.tgz", + "integrity": "sha512-S0TE4E0ha5+tBHdLWPc5n+S8E4dFBS5xScPvgHkLNZwWvX4ISoFGhGeerLC9uS1cKA/sC+K2wHq6qEbcagT/fg==", + "dev": true, + "requires": { + "timsort": "^0.3.0" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.1.tgz", + "integrity": "sha512-5WubEmKcK2cqw43DUAayRBiIlTdX7iX3ZowrWDVxSVcW3hyohVnbJ4K4mbnWtJp5rfJnUwHg5H4mDAGzmuCM3g==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "cssnano-preset-default": "^5.0.0", + "is-resolvable": "^1.1.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "cssnano-preset-default": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.0.1.tgz", + "integrity": "sha512-cfmfThYODGqhpQKDq9H0MTAqkMvZ3dGbOUTBKw0xWZiIycMqHid22LsJXJl4r1qX4qzDeKxcSyQ/Xb5Mu3Z//Q==", + "dev": true, + "requires": { + "css-declaration-sorter": "6.0.0", + "cssnano-utils": "^2.0.0", + "postcss-calc": "^8.0.0", + "postcss-colormin": "^5.0.0", + "postcss-convert-values": "^5.0.0", + "postcss-discard-comments": "^5.0.0", + "postcss-discard-duplicates": "^5.0.0", + "postcss-discard-empty": "^5.0.0", + "postcss-discard-overridden": "^5.0.0", + "postcss-merge-longhand": "^5.0.1", + "postcss-merge-rules": "^5.0.0", + "postcss-minify-font-values": "^5.0.0", + "postcss-minify-gradients": "^5.0.0", + "postcss-minify-params": "^5.0.0", + "postcss-minify-selectors": "^5.0.0", + "postcss-normalize-charset": "^5.0.0", + "postcss-normalize-display-values": "^5.0.0", + "postcss-normalize-positions": "^5.0.0", + "postcss-normalize-repeat-style": "^5.0.0", + "postcss-normalize-string": "^5.0.0", + "postcss-normalize-timing-functions": "^5.0.0", + "postcss-normalize-unicode": "^5.0.0", + "postcss-normalize-url": "^5.0.0", + "postcss-normalize-whitespace": "^5.0.0", + "postcss-ordered-values": "^5.0.0", + "postcss-reduce-initial": "^5.0.0", + "postcss-reduce-transforms": "^5.0.0", + "postcss-svgo": "^5.0.0", + "postcss-unique-selectors": "^5.0.0" + } + }, + "cssnano-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", + "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", + "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "dev": true, + "requires": { + "domelementtype": "^2.1.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + } + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "electron-to-chromium": { + "version": "1.3.675", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.675.tgz", + "integrity": "sha512-GEQw+6dNWjueXGkGfjgm7dAMtXfEqrfDG3uWcZdeaD4cZ3dKYdPRQVruVXQRXtPLtOr5GNVVlNLRMChOZ611pQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "esbuild": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.23.tgz", + "integrity": "sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", + "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-eslint-comments": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", + "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + } + }, + "eslint-plugin-import": { + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.2.tgz", + "integrity": "sha512-LmNoRptHBxOP+nb0PIKz1y6OSzCJlB+0g0IGS3XV4KaKk2q4szqQ6s6F1utVf5ZRkxk/QOTjdxe7v4VjS99Bsg==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "contains-path": "^1.0.0", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-jsdoc": { + "version": "34.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-34.8.2.tgz", + "integrity": "sha512-UOU9A40Cl806JMtla2vF+RM6sNqfLPbhLv9FZqhcC7+LmChD3DVaWqM7ADxpF0kMyZNWe1QKUnqGnXaA3NTn+w==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "^0.6.0", + "comment-parser": "1.1.5", + "debug": "^4.3.1", + "esquery": "^1.4.0", + "jsdoctypeparser": "^9.0.0", + "lodash": "^4.17.21", + "regextras": "^0.7.1", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + } + }, + "eslint-plugin-no-null": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz", + "integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true + }, + "execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "requires": { + "clone-regexp": "^2.1.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "focus-visible": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", + "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fraction.js": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.13.tgz", + "integrity": "sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "fuzzaldrin-plus": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz", + "integrity": "sha1-gy9kifvodnaUWVmckUpnDsIpR+4=" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "github-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/github-types/-/github-types-1.0.0.tgz", + "integrity": "sha512-cUyNZq/cC853l0mlbWaugGWNqegqoxj0iVJCfQYBYSjfqhz6mDRExfsfdp6a5kFvGfwsDiNduE+e18hlsERSuA==", + "dev": true + }, + "gitlab": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/gitlab/-/gitlab-14.2.2.tgz", + "integrity": "sha512-MVLGXLFk5erJCeDnDtV+srDHogadVV8y94G4mAulukCwdCwh+IUqsU4ESZksZvBis8VTJsK9Mj01lRJ1JrxVsg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "form-data": "^3.0.0", + "ky": "^0.16.0", + "ky-universal": "^0.3.0", + "li": "^1.3.0", + "ora": "^4.0.3", + "query-string": "^6.9.0", + "sywac": "^1.2.2", + "xcase": "^2.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "dev": true, + "requires": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + } + }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "dev": true + }, + "htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "dev": true, + "requires": { + "domelementtype": "^2.1.0" + } + } + } + }, + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "dev": true, + "requires": { + "domelementtype": "^2.1.0" + } + } + } + } + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + }, + "dependencies": { + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + } + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true + }, + "is-supported-regexp-flag": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz", + "integrity": "sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdoctypeparser": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", + "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "known-css-properties": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "dev": true + }, + "ky": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.16.2.tgz", + "integrity": "sha512-4/Xcb0hqeueNX9sa+G2jREiam9yb+I2Y3p3J42lIeitAenHXUZwpyejEgeQcQsaGl+hbuA0s7c3u+nlcIYFtog==", + "dev": true + }, + "ky-universal": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.3.0.tgz", + "integrity": "sha512-CM4Bgb2zZZpsprcjI6DNYTaH3oGHXL2u7BU4DK+lfCuC4snkt9/WRpMYeKbBbXscvKkeqBwzzjFX2WwmKY5K/A==", + "dev": true, + "requires": { + "abort-controller": "^3.0.0", + "node-fetch": "^2.6.0" + } + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "li": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz", + "integrity": "sha1-IsWbyu+qmo7zWc91l4TkvxBq6hs=", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, + "longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "dev": true + }, + "markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true + }, + "material-design-color": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/material-design-color/-/material-design-color-2.3.2.tgz", + "integrity": "sha1-6K+VjYUqh0e/shHkjOEoK9qRiBU=", + "dev": true + }, + "material-shadows": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/material-shadows/-/material-shadows-3.0.1.tgz", + "integrity": "sha1-WGrRKxZzYKjk6Je/dTDLaazqURA=", + "dev": true + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "mdast-util-compact": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz", + "integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==", + "dev": true, + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + } + }, + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + } + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "requires": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "dependencies": { + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "dev": true + } + } + }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "dev": true, + "requires": { + "mime-db": "1.46.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + } + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", + "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-starts-with": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-1.0.0.tgz", + "integrity": "sha1-soJDAV6LE43lcmgqxS2kLmRq2E4=", + "dev": true, + "requires": { + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "8.2.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", + "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map": "^0.6.1" + } + }, + "postcss-calc": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", + "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.0.0.tgz", + "integrity": "sha512-Yt84+5V6CgS/AhK7d7MA58vG8dSZ7+ytlRtWLaQhag3HXOncTfmYpuUOX4cDoXjvLfw1sHRCHMiBjYhc35CymQ==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "color": "^3.1.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-convert-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz", + "integrity": "sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-discard-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", + "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", + "dev": true + }, + "postcss-discard-duplicates": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", + "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", + "dev": true + }, + "postcss-discard-empty": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", + "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", + "dev": true + }, + "postcss-discard-overridden": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", + "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", + "dev": true + }, + "postcss-html": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", + "dev": true, + "requires": { + "htmlparser2": "^3.10.0" + }, + "dependencies": { + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + } + } + }, + "postcss-inline-svg": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-inline-svg/-/postcss-inline-svg-5.0.0.tgz", + "integrity": "sha512-Agqkrn91Qgi+KAO+cTvUS1IAZbHPD4sryPoG0q5U0ThokL4UGoMcmwvNV6tDoRp69B5tgD1VNkn9P09E+xpQAg==", + "dev": true, + "requires": { + "css-select": "^3.1.0", + "dom-serializer": "^1.1.0", + "htmlparser2": "^5.0.1", + "postcss-value-parser": "^4.0.0" + }, + "dependencies": { + "css-select": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", + "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^4.0.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.3", + "nth-check": "^2.0.0" + } + }, + "css-what": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", + "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", + "dev": true + }, + "dom-serializer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + }, + "domutils": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" + } + }, + "nth-check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + } + } + }, + "postcss-jsx": { + "version": "0.36.4", + "resolved": "https://registry.npmjs.org/postcss-jsx/-/postcss-jsx-0.36.4.tgz", + "integrity": "sha512-jwO/7qWUvYuWYnpOb0+4bIIgJt7003pgU3P6nETBLaOyBXuTD55ho21xnals5nBrlpTIFodyd3/jBi6UO3dHvA==", + "dev": true, + "requires": { + "@babel/core": ">=7.2.2" + } + }, + "postcss-less": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", + "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-markdown": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.36.0.tgz", + "integrity": "sha512-rl7fs1r/LNSB2bWRhyZ+lM/0bwKv9fhl38/06gF6mKMo/NPnp55+K1dSTosSVjFZc0e1ppBlu+WT91ba0PMBfQ==", + "dev": true, + "requires": { + "remark": "^10.0.1", + "unist-util-find-all-after": "^1.0.2" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "remark": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz", + "integrity": "sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ==", + "dev": true, + "requires": { + "remark-parse": "^6.0.0", + "remark-stringify": "^6.0.0", + "unified": "^7.0.0" + } + }, + "remark-parse": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-6.0.3.tgz", + "integrity": "sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg==", + "dev": true, + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "remark-stringify": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-6.0.4.tgz", + "integrity": "sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "is-alphanumeric": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "longest-streak": "^2.0.1", + "markdown-escapes": "^1.0.0", + "markdown-table": "^1.1.0", + "mdast-util-compact": "^1.0.0", + "parse-entities": "^1.0.2", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "stringify-entities": "^1.0.1", + "unherit": "^1.0.4", + "xtend": "^4.0.1" + } + }, + "unified": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz", + "integrity": "sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "@types/vfile": "^3.0.0", + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^3.0.0", + "x-is-string": "^0.1.0" + } + }, + "unist-util-find-all-after": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz", + "integrity": "sha512-lWgIc3rrTMTlK1Y0hEuL+k+ApzFk78h+lsaa2gHf63Gp5Ww+mt11huDniuaoq1H+XMK2lIIjjPkncxXcDp3QDw==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "dev": true + }, + "vfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz", + "integrity": "sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ==", + "dev": true, + "requires": { + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "dev": true, + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + } + } + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "postcss-merge-longhand": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz", + "integrity": "sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw==", + "dev": true, + "requires": { + "css-color-names": "^1.0.1", + "postcss-value-parser": "^4.1.0", + "stylehacks": "^5.0.1" + } + }, + "postcss-merge-rules": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.1.tgz", + "integrity": "sha512-UR6R5Ph0c96QB9TMBH3ml8/kvPCThPHepdhRqAbvMRDRHQACPC8iM5NpfIC03+VRMZTGXy4L/BvFzcDFCgb+fA==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^2.0.1", + "postcss-selector-parser": "^6.0.5", + "vendors": "^1.0.3" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "postcss-minify-font-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz", + "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-minify-gradients": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz", + "integrity": "sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "is-color-stop": "^1.1.0", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-minify-params": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz", + "integrity": "sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "browserslist": "^4.16.0", + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz", + "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "postcss-normalize-charset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", + "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", + "dev": true + }, + "postcss-normalize-display-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz", + "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-positions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz", + "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz", + "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-string": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz", + "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz", + "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz", + "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-url": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.1.tgz", + "integrity": "sha512-hkbG0j58Z1M830/CJ73VsP7gvlG1yF+4y7Fd1w4tD2c7CaA2Psll+pQ6eQhth9y9EaqZSLzamff/D0MZBMbYSg==", + "dev": true, + "requires": { + "is-absolute-url": "^3.0.3", + "normalize-url": "^4.5.0", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz", + "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-ordered-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.1.tgz", + "integrity": "sha512-6mkCF5BQ25HvEcDfrMHCLLFHlraBSlOXFnQMHYhSpDO/5jSR1k8LdEXOkv+7+uzW6o6tBYea1Km0wQSRkPJkwA==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-reduce-initial": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz", + "integrity": "sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz", + "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-reporter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz", + "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "lodash": "^4.17.11", + "log-symbols": "^2.2.0", + "postcss": "^7.0.7" + }, + "dependencies": { + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dev": true, + "requires": { + "postcss": "^7.0.26" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-sass": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", + "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "dev": true, + "requires": { + "gonzales-pe": "^4.3.0", + "postcss": "^7.0.21" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "requires": { + "postcss": "^7.0.6" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + } + }, + "postcss-sorting": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-4.1.0.tgz", + "integrity": "sha512-r4T2oQd1giURJdHQ/RMb72dKZCuLOdWx2B/XhXN1Y1ZdnwXsKH896Qz6vD4tFy9xSjpKNYhlZoJmWyhH/7JUQw==", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "postcss-svgo": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.1.tgz", + "integrity": "sha512-cD7DFo6tF9i5eWvwtI4irKOHCpmASFS0xvZ5EQIgEdA1AWfM/XiHHY/iss0gcKHhkqwgYmuo2M0KhJLd5Us6mg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0", + "svgo": "^2.3.0" + } + }, + "postcss-syntax": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", + "dev": true + }, + "postcss-unique-selectors": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz", + "integrity": "sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "preact": { + "version": "10.5.13", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.13.tgz", + "integrity": "sha512-q/vlKIGNwzTLu+jCcvywgGrt+H/1P/oIRSD6mV4ln3hmlC+Aa34C7yfPI4+5bzW8pONyVXYS7SvXosy2dKKtWQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "regextras": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.1.tgz", + "integrity": "sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w==", + "dev": true + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remark": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", + "dev": true, + "requires": { + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" + } + }, + "remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "requires": { + "mdast-util-from-markdown": "^0.8.0" + } + }, + "remark-stringify": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", + "dev": true, + "requires": { + "mdast-util-to-markdown": "^0.6.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.0.1.tgz", + "integrity": "sha512-wViQ4Vgps1xJwqWIBooMNN44usCSthL7wCUl4qWqrVjhGfWyVyXcxlYzfDKkJKACQvZMTOft/jJ3RkbwK1j9QQ==", + "requires": { + "tslib": "~2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "sass": { + "version": "1.32.13", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.13.tgz", + "integrity": "sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "stringify-entities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", + "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", + "dev": true, + "requires": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "stylehacks": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", + "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylelint": { + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", + "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "dev": true, + "requires": { + "@stylelint/postcss-css-in-js": "^0.37.2", + "@stylelint/postcss-markdown": "^0.36.2", + "autoprefixer": "^9.8.6", + "balanced-match": "^2.0.0", + "chalk": "^4.1.1", + "cosmiconfig": "^7.0.0", + "debug": "^4.3.1", + "execall": "^2.0.0", + "fast-glob": "^3.2.5", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.0.3", + "globjoin": "^0.1.4", + "html-tags": "^3.1.0", + "ignore": "^5.1.8", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.21.0", + "lodash": "^4.17.21", + "log-symbols": "^4.1.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.4", + "normalize-selector": "^0.2.0", + "postcss": "^7.0.35", + "postcss-html": "^0.36.0", + "postcss-less": "^3.1.4", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.2", + "postcss-sass": "^0.4.4", + "postcss-scss": "^2.1.1", + "postcss-selector-parser": "^6.0.5", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^4.1.0", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.2", + "strip-ansi": "^6.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^6.6.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + } + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "stylelint-config-rational-order": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/stylelint-config-rational-order/-/stylelint-config-rational-order-0.1.2.tgz", + "integrity": "sha512-Qo7ZQaihCwTqijfZg4sbdQQHtugOX/B1/fYh018EiDZHW+lkqH9uHOnsDwDPGZrYJuB6CoyI7MZh2ecw2dOkew==", + "dev": true, + "requires": { + "stylelint": "^9.10.1", + "stylelint-order": "^2.2.1" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + } + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "clone-regexp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", + "integrity": "sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw==", + "dev": true, + "requires": { + "is-regexp": "^1.0.0", + "is-supported-regexp-flag": "^1.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "execall": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-1.0.0.tgz", + "integrity": "sha1-c9CQTjlbPKsGWLCNCewlMH8pu3M=", + "dev": true, + "requires": { + "clone-regexp": "^1.0.0" + } + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "file-entry-cache": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-4.0.0.tgz", + "integrity": "sha512-AVSwsnbV8vH/UVbvgEhf3saVQXORNv0ZzSkvkhQIaia5Tia+JhGTaa/ePUSVoPHQyGayQNmYfkzFi3WZV5zcpA==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, + "import-lazy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", + "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "known-css-properties": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.11.0.tgz", + "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-sass": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.3.5.tgz", + "integrity": "sha512-B5z2Kob4xBxFjcufFnhQ2HqJQ2y/Zs/ic5EZbCywCkxKd756Q40cIQ/veRDwSrw1BF6+4wUgmpm0sBASqVi65A==", + "dev": true, + "requires": { + "gonzales-pe": "^4.2.3", + "postcss": "^7.0.1" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "stylelint": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-9.10.1.tgz", + "integrity": "sha512-9UiHxZhOAHEgeQ7oLGwrwoDR8vclBKlSX7r4fH0iuu0SfPwFaLkb1c7Q2j1cqg9P7IDXeAV2TvQML/fRQzGBBQ==", + "dev": true, + "requires": { + "autoprefixer": "^9.0.0", + "balanced-match": "^1.0.0", + "chalk": "^2.4.1", + "cosmiconfig": "^5.0.0", + "debug": "^4.0.0", + "execall": "^1.0.0", + "file-entry-cache": "^4.0.0", + "get-stdin": "^6.0.0", + "global-modules": "^2.0.0", + "globby": "^9.0.0", + "globjoin": "^0.1.4", + "html-tags": "^2.0.0", + "ignore": "^5.0.4", + "import-lazy": "^3.1.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.11.0", + "leven": "^2.1.0", + "lodash": "^4.17.4", + "log-symbols": "^2.0.0", + "mathml-tag-names": "^2.0.1", + "meow": "^5.0.0", + "micromatch": "^3.1.10", + "normalize-selector": "^0.2.0", + "pify": "^4.0.0", + "postcss": "^7.0.13", + "postcss-html": "^0.36.0", + "postcss-jsx": "^0.36.0", + "postcss-less": "^3.1.0", + "postcss-markdown": "^0.36.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-reporter": "^6.0.0", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.0", + "postcss-sass": "^0.3.5", + "postcss-scss": "^2.0.0", + "postcss-selector-parser": "^3.1.0", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^3.3.0", + "resolve-from": "^4.0.0", + "signal-exit": "^3.0.2", + "slash": "^2.0.0", + "specificity": "^0.4.1", + "string-width": "^3.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^5.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "stylelint-config-recommended": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", + "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", + "dev": true + }, + "stylelint-config-standard": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-22.0.0.tgz", + "integrity": "sha512-uQVNi87SHjqTm8+4NIP5NMAyY/arXrBgimaaT7skvRfE9u3JKXRK9KBkbr4pVmeciuCcs64kAdjlxfq6Rur7Hw==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^5.0.0" + } + }, + "stylelint-order": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-2.2.1.tgz", + "integrity": "sha512-019KBV9j8qp1MfBjJuotse6MgaZqGVtXMc91GU9MsS9Feb+jYUvUU3Z8XiClqPdqJZQ0ryXQJGg3U3PcEjXwfg==", + "dev": true, + "requires": { + "lodash": "^4.17.10", + "postcss": "^7.0.2", + "postcss-sorting": "^4.1.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "stylelint-scss": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.19.0.tgz", + "integrity": "sha512-Ic5bsmpS4wVucOw44doC1Yi9f5qbeVL4wPFiEOaUElgsOuLEN6Ofn/krKI8BeNL2gAn53Zu+IcVV4E345r6rBw==", + "dev": true, + "requires": { + "lodash": "^4.17.15", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svgo": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz", + "integrity": "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==", + "dev": true, + "requires": { + "@trysound/sax": "0.1.1", + "chalk": "^4.1.0", + "commander": "^7.1.0", + "css-select": "^3.1.2", + "css-tree": "^1.1.2", + "csso": "^4.2.0", + "stable": "^0.1.8" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "css-select": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", + "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^4.0.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.3", + "nth-check": "^2.0.0" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", + "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", + "dev": true + }, + "dom-serializer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domutils": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.1.tgz", + "integrity": "sha512-hO1XwHMGAthA/1KL7c83oip/6UWo3FlUNIuWiWKltoiQ5oCOiqths8KknvY2jpOohUoUgnwa/+Rm7UpwpSbY/Q==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0" + }, + "dependencies": { + "domhandler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz", + "integrity": "sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "nth-check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sywac": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sywac/-/sywac-1.3.0.tgz", + "integrity": "sha512-LDt2stNTp4bVPMgd70Jj9PWrSa4batl+bv+Ea5NLNGT7ufc4oQPtRfQ73wbddNV6RilaPqnEt6y1Wkm5FVTNEg==", + "dev": true + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.4.0.tgz", + "integrity": "sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "dev": true + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true + }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, + "uglify-js": { + "version": "3.12.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", + "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dev": true, + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "unified": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", + "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unist-util-find-all-after": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", + "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", + "dev": true, + "requires": { + "unist-util-is": "^4.0.0" + } + }, + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "dev": true, + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", + "dev": true + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "dev": true + }, + "xcase": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz", + "integrity": "sha1-x/pyyqD0QNt4/VZzQyA4rJhEULk=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true + } + } +} diff --git a/docs/tensorstore_sphinx_material/package.json b/docs/tensorstore_sphinx_material/package.json new file mode 100644 index 000000000..5adc94099 --- /dev/null +++ b/docs/tensorstore_sphinx_material/package.json @@ -0,0 +1,93 @@ +{ + "name": "sphinx-material", + "version": "0.0.0", + "description": "Sphinx Material design theme", + "keywords": [ + "sphinx", + "documentation", + "theme" + ], + "homepage": "https://bashtage.github.io/sphinx-material/", + "bugs": { + "url": "https://github.com/bashtage/sphinx-material/issues", + "email": "kevin.k.sheppard@gmail.com" + }, + "license": "MIT", + "private": true, + "author": { + "name": "Kevin Sheppard", + "email": "kevin.k.sheppard@gmail.com" + }, + "contributors": [], + "repository": { + "type": "git", + "url": "https://github.com/bashtage/sphinx-material.git" + }, + "scripts": { + "build": "npm run clean && ts-node -T tools/build --optimize", + "build:dev": "npm run clean && ts-node -T tools/build", + "build:dirty": "ts-node -T tools/build --dirty", + "build:noclean": "ts-node -T tools/build --verbose", + "docs": "npm run build && rimraf docs/_build && cd docs && sphinx-build . _build -a", + "docs:dev": "npm run build:dev && rimraf docs/_build/* && cd docs && sphinx-build . _build -a", + "clean": "rimraf sphinx_material/{*.html,partials,.icons,static}", + "lint": "npm run lint:scss && npm run lint:ts", + "lint:scss": "stylelint \"src/assets/**/*.scss\"", + "lint:ts": "eslint --cache \"{src,tools}/**/*.ts\"", + "start": "ts-node -T tools/build --verbose --dirty --watch" + }, + "dependencies": { + "clipboard": "^2.0.8", + "escape-html": "^1.0.3", + "focus-visible": "^5.2.0", + "fuzzaldrin-plus": "^0.6.0", + "rxjs": "^7.0.1" + }, + "devDependencies": { + "@fortawesome/fontawesome-free": "^5.15.3", + "@mdi/svg": "^5.9.55", + "@primer/octicons": "^14.1.0", + "@types/clipboard": "^2.0.1", + "@types/escape-html": "1.0.1", + "@types/fuzzaldrin-plus": "^0.6.1", + "@types/html-minifier": "^4.0.0", + "@types/node": "^15.3.0", + "@types/resize-observer-browser": "^0.1.5", + "@types/sass": "^1.16.0", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", + "autoprefixer": "^10.2.5", + "chokidar": "^3.5.1", + "cssnano": "5.0.1", + "cssnano-preset-default": "5.0.1", + "esbuild": "^0.11.23", + "eslint": "^7.26.0", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-import": "^2.23.2", + "eslint-plugin-jsdoc": "^34.6.3", + "eslint-plugin-no-null": "^1.0.2", + "github-types": "^1.0.0", + "gitlab": "^14.2.2", + "html-minifier": "^4.0.0", + "material-design-color": "^2.3.2", + "material-shadows": "^3.0.1", + "postcss": "^8.2.15", + "postcss-colormin": "5.0.0", + "postcss-inline-svg": "^5.0.0", + "preact": "^10.5.13", + "rimraf": "^3.0.2", + "sass": "^1.32.13", + "stylelint": "^13.13.1", + "stylelint-config-rational-order": "^0.1.2", + "stylelint-config-recommended": "^5.0.0", + "stylelint-config-standard": "^22.0.0", + "stylelint-scss": "^3.19.0", + "svgo": "^2.3.0", + "tiny-glob": "^0.2.9", + "ts-node": "^9.1.1", + "typescript": "^4.2.4" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/docs/tensorstore_sphinx_material/sphinx_material/__init__.py b/docs/tensorstore_sphinx_material/sphinx_material/__init__.py new file mode 100644 index 000000000..751fdf752 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/__init__.py @@ -0,0 +1,255 @@ +"""Sphinx Material theme.""" + +import collections +import os +import sys +from typing import List, Optional, Union, Type, Dict, Sequence, Tuple + +import docutils.nodes +import sphinx.builders +import sphinx.builders.html +import sphinx.util.logging +import sphinx.util.fileutil +import sphinx.util.matching +import sphinx.util.docutils +import sphinx.writers.html5 + +from . import apidoc_formatting +from . import inlinesyntaxhighlight +from . import nav_adapt +from . import object_toc +from . import postprocess_html +from . import autodoc_property_type +from . import search_adapt + +logger = sphinx.util.logging.getLogger(__name__) + +DEFAULT_THEME_OPTIONS = { + 'features': [], + 'font': { + 'text': 'Roboto', + 'code': 'Roboto Mono' + }, + 'plugins': { + 'search': {}, + }, + 'icon': { + }, + 'globaltoc_depth': -1, + 'globaltoc_collapse': True, + 'globaltoc_includehidden': True, +} + + +def _get_html_translator( + base_translator: Type[sphinx.writers.html5.HTML5Translator] +) -> Type[sphinx.writers.html5.HTML5Translator]: + class CustomHTMLTranslator(apidoc_formatting.HTMLTranslatorMixin, + base_translator): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Ensure pygments uses elements, for compatibility with the + # mkdocs-material CSS which expects that. + self.highlighter.formatter_args.update(wrapcode=True) + + # Ensure all tables are marked as data tables. The material CSS only + # applies to tables with this class, in order to leave tables used for + # layout purposes alone. + self.settings.table_style = ','.join( + self.settings.table_style.split(',') + ['data']) + + def visit_section(self, node: docutils.nodes.section) -> None: + # Sphinx normally writes sections with a section heading as: + # + #
......
+ # + # but that is incompatible with the way scroll-margin-top and the + # `:target` selector are used in the mkdocs-material CSS. For + # compatibility with mkdocs-material, we strip the outer `
` and + # instead add the `id` to the inner `` element. + # + # That is accomplished by overriding `visit_section` and + # `depart_section` not to add the `
` and `
` tags, and also + # modifying `viist_title` to insert the `id`. + self.section_level += 1 + + def depart_section(self, node: docutils.nodes.section) -> None: + self.section_level -= 1 + + def visit_title(self, node: docutils.nodes.title) -> None: + if isinstance(node.parent, docutils.nodes.section): + if node.parent.get('ids') and not node.get('ids'): + node['ids'] = node.parent.get('ids') + super().visit_title(node) + del node['ids'] + return + super().visit_title(node) + + return CustomHTMLTranslator + + +def _get_html_builder( + base_builder: Type[sphinx.builders.html.StandaloneHTMLBuilder]): + """Returns a modified HTML translator.""" + class CustomHTMLBuilder(base_builder): + @property + def default_translator_class(self): + return _get_html_translator(super().default_translator_class) + + def init_js_files(self): + # Remove unnecessary scripts + + excluded_scripts = set([ + '_static/jquery.js', + '_static/underscore.js', + '_static/doctools.js', + '_static/language_data.js', + ]) + self.script_files = [ + x for x in self.script_files if x not in excluded_scripts + ] + + def gen_additional_pages(self): + # Prevent the search.html page from being written since this theme provides + # its own search results display that does not use it. + search = self.search + self.search = False + super().gen_additional_pages() + self.search = search + + def create_pygments_style_file(self): + pass + + def copy_theme_static_files(self, context: Dict) -> None: + + # Modified from version in sphinx.builders.html.__init__.py to + # exclude copying unused static files from `basic` theme. + def onerror(filename: str, error: Exception) -> None: + logger.warning( + 'Failed to copy a file in html_static_file: %s: %r', + filename, error) + + if self.theme: + for entry in self.theme.get_theme_dirs()[::-1]: + if os.path.basename(entry) == 'basic': + excluded = sphinx.util.matching.Matcher([ + '**/.*', + '**/jquery*.js', + '**/doctools.js', + '**/underscore*.js', + '**/*.png', + '**/basic.css_t', + '**/documentation_options.js_t', + '**/searchtools.js', + ]) + else: + excluded = sphinx.util.matching.DOTFILES + sphinx.util.fileutil.copy_asset( + os.path.join(entry, 'static'), + os.path.join(self.outdir, '_static'), + excluded=excluded, + context=context, + renderer=self.templates, + onerror=onerror, + ) + + def get_target_uri(self, docname: str, typ: str = None) -> str: + """Strips `index.html` suffix from URIs for cleaner links.""" + orig_uri = super().get_target_uri(docname, typ) + if self.app.config['html_use_directory_uris_for_index_pages']: + index_suffix = 'index' + self.link_suffix + if orig_uri == index_suffix: + return '' + if orig_uri.endswith('/' + index_suffix): + return orig_uri[:-len(index_suffix)] + return orig_uri + + return CustomHTMLBuilder + + +def dict_merge(*dicts: List[collections.Mapping]): + """Recursively merges the members of one or more dicts.""" + result = dict() + for d in dicts: + for k, v in d.items(): + if (isinstance(v, collections.Mapping) and k in result + and isinstance(result[k], dict)): + result[k] = dict_merge(result[k], v) + else: + result[k] = v + return result + + +def html_page_context(app, pagename, templaetname, context, doctree): + theme_options = app.config["html_theme_options"] + theme_options = dict_merge(DEFAULT_THEME_OPTIONS, theme_options) + + num_slashes = pagename.count('/') + if num_slashes == 0: + base_url = '.' + else: + base_url = '/'.join('..' for _ in range(num_slashes)) + + version_config = None + if theme_options.get('version_dropdown'): + version_config = { + 'provider': 'mike', + 'staticVersions': theme_options.get('version_info'), + 'versionPath': theme_options.get('version_json'), + } + + context.update( + config={ + "theme": theme_options, + 'site_url': theme_options.get('site_url'), + 'site_name': context['docstitle'], + 'repo_url': theme_options.get('repo_url', None), + 'repo_name': theme_options.get('repo_name', None), + "extra": { + 'version': version_config, + 'social': theme_options.get('social'), + 'disqus': theme_options.get('disqus'), + 'manifest': theme_options.get('pwa_manifest'), + }, + "plugins": theme_options.get('plugins'), + "google_analytics": theme_options.get("google_analytics"), + }, + base_url=base_url, + ) + + +def _builder_inited(app: sphinx.application.Sphinx) -> None: + # For compatibility with mkdocs + app.builder.templates.environment.filters['url'] = lambda url: url + + +def _config_inited(app: sphinx.application.Sphinx, config: dict) -> None: + """Merge defaults into theme options.""" + config["html_theme_options"] = dict_merge(DEFAULT_THEME_OPTIONS, + config["html_theme_options"]) + + +def setup(app): + app.connect("config-inited", _config_inited) + app.setup_extension(apidoc_formatting.__name__) + app.setup_extension(nav_adapt.__name__) + app.setup_extension(postprocess_html.__name__) + app.setup_extension(inlinesyntaxhighlight.__name__) + app.setup_extension(object_toc.__name__) + app.setup_extension(search_adapt.__name__) + app.connect("html-page-context", html_page_context) + app.connect("builder-inited", _builder_inited) + app.add_config_value('html_use_directory_uris_for_index_pages', + False, + rebuild='html', + types=bool) + + app.add_builder(_get_html_builder(app.registry.builders['html']), + override=True) + app.add_html_theme("sphinx_material", + os.path.abspath(os.path.dirname(__file__))) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/_version.py b/docs/tensorstore_sphinx_material/sphinx_material/_version.py new file mode 100644 index 000000000..ec8244e65 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/_version.py @@ -0,0 +1,556 @@ +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.18 (https://github.com/warner/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440" + cfg.tag_prefix = "v" + cfg.parentdir_prefix = "sphinx_material-" + cfg.versionfile_source = "sphinx_material/_version.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Decorator to mark a method as the handler for a particular VCS.""" + + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip() + if sys.version_info[0] >= 3: + stdout = stdout.decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r"\d", r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix) :] + if verbose: + print("picking %s" % r) + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[: git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix) :] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split("/"): + root = os.path.dirname(root) + except NameError: + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/apidoc_formatting.py b/docs/tensorstore_sphinx_material/sphinx_material/apidoc_formatting.py new file mode 100644 index 000000000..049637910 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/apidoc_formatting.py @@ -0,0 +1,248 @@ +"""Modifies the formatting of API documentation.""" + +from typing import Sequence, Tuple, List, Dict, Type, Union +import docutils.nodes +import sphinx.addnodes +import sphinx.application +import sphinx.domains +import sphinx.domains.python +import sphinx.ext.napoleon +from sphinx.locale import _ + +SIGNATURE_WRAP_LENGTH = 70 + +def _ensure_wrapped_in_desc_type( + nodes: List[docutils.nodes.Node]) -> List[docutils.nodes.Node]: + if len(nodes) != 1 or not isinstance(nodes[0], sphinx.addnodes.desc_type): + nodes = [sphinx.addnodes.desc_type('', '', *nodes)] + return nodes + + +def _monkey_patch_python_doc_fields(): + PyTypedField = sphinx.domains.python.PyTypedField + + def make_field( + self: PyTypedField, + types: Dict[str, List[docutils.nodes.Node]], + domain: str, + items: Sequence[Tuple[str, str]], + env: sphinx.environment.BuildEnvironment = None + ) -> docutils.nodes.field: + bodynode = docutils.nodes.definition_list() + bodynode['classes'].append('api-field') + + def handle_item( + fieldarg: str, + content: List[docutils.nodes.Node]) -> docutils.nodes.Node: + node = docutils.nodes.definition_list_item() + term_node = docutils.nodes.term() + term_node['paramname'] = fieldarg + term_node += sphinx.addnodes.desc_name(fieldarg, fieldarg) + fieldtype = types.pop(fieldarg, None) + if fieldtype: + term_node += sphinx.addnodes.desc_sig_punctuation('', ' : ') + fieldtype_node = sphinx.addnodes.desc_type() + if len(fieldtype) == 1 and isinstance(fieldtype[0], + docutils.nodes.Text): + typename = fieldtype[0].astext() + fieldtype_node.extend( + _ensure_wrapped_in_desc_type( + self.make_xrefs(self.typerolename, + domain, + typename, + docutils.nodes.Text, + env=env))) + else: + fieldtype_node += fieldtype + term_node += fieldtype_node + node += term_node + def_node = docutils.nodes.definition() + p = docutils.nodes.paragraph() + p += content + def_node += p + node += def_node + return node + + for fieldarg, content in items: + bodynode += handle_item(fieldarg, content) + fieldname = docutils.nodes.field_name('', self.label) + fieldbody = docutils.nodes.field_body('', bodynode) + return docutils.nodes.field('', fieldname, fieldbody) + + PyTypedField.make_field = make_field + + +def _monkey_patch_python_parse_annotation(): + """Ensures that type annotations in signatures are wrapped in `desc_type`. + + This allows them to be distinguished from parameter names in CSS rules. + """ + orig_parse_annotation = sphinx.domains.python._parse_annotation + + def parse_annotation( + annotation: str, + env: sphinx.environment.BuildEnvironment = None + ) -> List[docutils.nodes.Node]: + return _ensure_wrapped_in_desc_type( + orig_parse_annotation(annotation, env)) + + sphinx.domains.python._parse_annotation = parse_annotation + + +def _monkey_patch_python_parse_arglist(): + """Ensures default values in signatures are styled as code.""" + + orig_parse_arglist = sphinx.domains.python._parse_arglist + + def parse_arglist( + arglist: str, + env: sphinx.environment.BuildEnvironment = None + ) -> sphinx.addnodes.desc_parameterlist: + result = orig_parse_arglist(arglist, env) + for node in result.traverse(condition=docutils.nodes.inline): + if 'default_value' not in node['classes']: continue + node.replace_self( + docutils.nodes.literal(text=node.astext(), + classes=['code', 'python', 'default_value'], + language='python')) + return result + + sphinx.domains.python._parse_arglist = parse_arglist + + +class HTMLTranslatorMixin: + """Mixin for HTMLTranslator that adds additional CSS classes.""" + def visit_desc(self, node: sphinx.addnodes.desc) -> None: + # Object description node + + # These are converted to `
` elements with the domain and objtype + # as classes. + + # Augment the list of classes with `objdesc` to make it easier to + # style these without resorting to hacks. + node['classes'].append('objdesc') + super().visit_desc(node) + + def visit_desc_type(self, node: sphinx.addnodes.desc_type) -> None: + self.body.append( + self.starttag(node, tagname='span', suffix='', CLASS='desctype')) + + def depart_desc_type(self, node: sphinx.addnodes.desc_type) -> None: + self.body.append('') + + def visit_desc_signature(self, + node: sphinx.addnodes.desc_signature) -> None: + node_text = node.astext() + if len(node_text) > SIGNATURE_WRAP_LENGTH: + node['classes'].append('sig-wrap') + super().visit_desc_signature(node) + + def visit_desc_parameterlist( + self, node: sphinx.addnodes.desc_parameterlist) -> None: + super().visit_desc_parameterlist(node) + open_paren, _ = node.get('parens', ('(', ')')) + self.body[-1] = self.body[-1].replace('(', open_paren) + + def depart_desc_parameterlist( + self, node: sphinx.addnodes.desc_parameterlist) -> None: + super().depart_desc_parameterlist(node) + _, close_paren = node.get('parens', ('(', ')')) + self.body[-1] = self.body[-1].replace(')', close_paren) + + def depart_field_name(self, node: docutils.nodes.Element) -> None: + self.add_permalink_ref(node, _('Permalink to this headline')) + super().depart_field_name(node) + + def depart_term(self, node: docutils.nodes.Element) -> None: + if 'ids' in node: + self.add_permalink_ref(node, _('Permalink to this definition')) + super().depart_term(node) + + +def _monkey_patch_python_get_signature_prefix( + directive_cls: Type[sphinx.domains.python.PyObject]) -> None: + orig_get_signature_prefix = directive_cls.get_signature_prefix + + def get_signature_prefix(self, sig: str) -> str: + prefix = orig_get_signature_prefix(self, sig) + parts = prefix.strip().split(' ') + if 'property' in parts: + parts.remove('property') + if parts: + return ' '.join(parts) + ' ' + return '' + + directive_cls.get_signature_prefix = get_signature_prefix + + +def _monkey_patch_pyattribute_handle_signature( + directive_cls: Type[sphinx.domains.python.PyObject]): + """Modifies PyAttribute or PyVariable to improve styling of signature.""" + + def handle_signature( + self, sig: str, + signode: sphinx.addnodes.desc_signature) -> Tuple[str, str]: + result = super(directive_cls, self).handle_signature(sig, signode) + typ = self.options.get('type') + if typ: + signode += sphinx.addnodes.desc_sig_punctuation('', ' : ') + signode += sphinx.domains.python._parse_annotation(typ, self.env) + + value = self.options.get('value') + if value: + signode += sphinx.addnodes.desc_sig_punctuation('', ' = ') + signode += docutils.nodes.literal(text=value, + classes=['code', 'python'], + language='python') + return result + + directive_cls.handle_signature = handle_signature + + +def _monkey_patch_parameterlist_to_support_subscript(): + desc_parameterlist = sphinx.addnodes.desc_parameterlist + + def astext(self: desc_parameterlist) -> str: + open_paren, close_paren = self.get('parens', ('(', ')')) + return f'{open_paren}{super(desc_parameterlist, self).astext()}{close_paren}' + + desc_parameterlist.astext = astext + + +def _monkey_patch_napoleon_admonition_classes(): + GoogleDocstring = sphinx.ext.napoleon.docstring.GoogleDocstring + + def _add_admonition_class(method_name: str, class_name: str) -> None: + orig_method = getattr(GoogleDocstring, method_name) + + def wrapper(self: GoogleDocstring, section: str) -> List[str]: + result = orig_method(self, section) + result.insert(1, f' :class: {class_name}') + return result + + setattr(GoogleDocstring, method_name, wrapper) + + _add_admonition_class('_parse_examples_section', 'example') + + +def setup(app: sphinx.application.Sphinx) -> None: + """Registers the monkey patches. + + Does not register HTMLTranslatorMixin, the caller must do that. + """ + _monkey_patch_python_doc_fields() + _monkey_patch_python_parse_annotation() + _monkey_patch_python_parse_arglist() + _monkey_patch_python_get_signature_prefix(sphinx.domains.python.PyFunction) + _monkey_patch_python_get_signature_prefix(sphinx.domains.python.PyMethod) + _monkey_patch_pyattribute_handle_signature( + sphinx.domains.python.PyAttribute) + _monkey_patch_pyattribute_handle_signature( + sphinx.domains.python.PyVariable) + _monkey_patch_parameterlist_to_support_subscript() + _monkey_patch_napoleon_admonition_classes() + + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/autodoc_property_type.py b/docs/tensorstore_sphinx_material/sphinx_material/autodoc_property_type.py new file mode 100644 index 000000000..fe9372196 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/autodoc_property_type.py @@ -0,0 +1,95 @@ +# Backports https://github.com/sphinx-doc/sphinx/pull/8983/, which adds autodoc +# support for return annotations on property getters. + +import re +from typing import Tuple, Optional, List + +import docutils.nodes +import docutils.parsers.rst.directives +import sphinx.addnodes +import sphinx.domains +import sphinx.domains.python +import sphinx.ext.autodoc + +property_sig_re = re.compile('^(\\(.*)\\)\\s*->\\s*(.*)$') + + +def _get_property_return_type(obj: property) -> Optional[str]: + if obj.fget is None: return None + doc = obj.fget.__doc__ + if doc is None: return None + line = doc.splitlines()[0] + line = line.rstrip('\\').strip() + match = property_sig_re.match(line) + if not match: return None + _, retann = match.groups() + return retann + + +def _apply_property_documenter_type_annotation_fix(): + + sphinx.domains.python.PythonDomain.object_types[ + 'property'] = sphinx.domains.ObjType(sphinx.locale._('property'), + 'meth', 'obj') + + # Modify PropertyDocumenter to include :type: option + PropertyDocumenter = sphinx.ext.autodoc.PropertyDocumenter + + orig_import_object = PropertyDocumenter.import_object + + def import_object(self: PropertyDocumenter, raiseerror: bool = False) -> bool: + result = orig_import_object(self, raiseerror) + if not result: + return False + if not self.retann: + self.retann = _get_property_return_type(self.object) + return True + + PropertyDocumenter.import_object = import_object + + old_add_directive_header = PropertyDocumenter.add_directive_header + + def add_directive_header(self, sig: str) -> None: + old_add_directive_header(self, sig) + + # Check for return annotation + retann = self.retann or get_property_return_type(self.object) + if retann is not None: + self.add_line(' :type: ' + retann, self.get_sourcename()) + + PropertyDocumenter.add_directive_header = add_directive_header + + # Modify PyMethod to support :type: option + PyMethod = sphinx.domains.python.PyMethod + PyMethod.option_spec['type'] = docutils.parsers.rst.directives.unchanged + + orig_handle_signature = PyMethod.handle_signature + + def handle_signature( + self, sig: str, + signode: sphinx.addnodes.desc_signature) -> Tuple[str, str]: + fullname, prefix = orig_handle_signature(self, sig, signode) + + typ = self.options.get('type') + if typ: + signode += sphinx.addnodes.desc_sig_punctuation('', ' : ') + signode += sphinx.domains.python._parse_annotation(typ, self.env) + + return fullname, prefix + + PyMethod.handle_signature = handle_signature + + orig_add_target_and_index = PyMethod.add_target_and_index + + def add_target_and_index(self, name_cls: Tuple[str, str], sig: str, + signode: sphinx.addnodes.desc_signature) -> None: + orig_objtype = self.objtype + if 'property' in self.options: + self.objtype = 'property' + orig_add_target_and_index(self, name_cls, sig, signode) + self.objtype = orig_objtype + + PyMethod.add_target_and_index = add_target_and_index + + +_apply_property_documenter_type_annotation_fix() diff --git a/docs/tensorstore_sphinx_material/sphinx_material/inlinesyntaxhighlight.py b/docs/tensorstore_sphinx_material/sphinx_material/inlinesyntaxhighlight.py new file mode 100644 index 000000000..390ec71b6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/inlinesyntaxhighlight.py @@ -0,0 +1,108 @@ +# Sphinx provides the `code-block` directive for highlighting code blocks. +# Docutils provides the `code` role which in theory can be used similarly by +# defining a custom role for a given programming language: +# +# .. .. role:: python(code) +# :language: python +# :class: highlight +# +# In practice this does not produce correct highlighting because it uses a +# separate highlighting mechanism that results in the "long" pygments class +# names rather than "short" pygments class names produced by the Sphinx +# `code-block` directive and for which this extension contains CSS rules. +# +# In addition, even if that issue is fixed, because the highlighting +# implementation in docutils, despite being based on pygments, differs from that +# used by Sphinx, the output does not exactly match that produced by the Sphinx +# `code-block` directive. +# +# This issue is noted here: //github.com/sphinx-doc/sphinx/issues/5157 +# +# This module fixes the problem by modifying the Sphinx HTML translator to +# perform highlighting for the `code` role in the same way as the Sphinx +# `code-block` directive. The solution is derived from this extension +# +# https://github.com/sphinx-contrib/inlinesyntaxhighlight/blob/master/sphinxcontrib/inlinesyntaxhighlight.py +# +# and from the discussion on this pull request: +# +# https://github.com/sphinx-doc/sphinx/pull/6916 + +import docutils.parsers.rst.roles +import docutils.parsers.rst +import docutils.nodes +import sphinx.application +import sphinx.writers.html +import sphinx.writers.html5 + + +def code_role(role, rawtext, text, lineno, inliner, options={}, content=[]): + r'''code_role override or create if older docutils used. + This only creates a literal node without parsing the code. This will + be done later in sphinx. This override is not really needed, but it + might give some speed + ''' + + docutils.parsers.rst.roles.set_classes(options) + + language = options.get('language', '') + classes = ['code'] + + if 'classes' in options: + classes.extend(options['classes']) + + if language and language not in classes: + classes.append(language) + + node = docutils.nodes.literal(rawtext, + text, + classes=classes, + language=language) + + return [node], [] + + +code_role.options = { + 'class': docutils.parsers.rst.directives.class_option, + 'language': docutils.parsers.rst.directives.unchanged, +} + + +def _monkey_patch_html_translator(translator_class): + orig_visit_literal = translator_class.visit_literal + + def visit_literal(self, node: docutils.nodes.literal) -> None: + lang = node.get('language', None) + if 'code' not in node['classes'] or not lang: + return orig_visit_literal(self, node) + + def warner(msg): + self.builder.warn(msg, (self.builder.current_docname, node.line)) + + highlight_args = dict(node.get('highlight_args', {}), nowrap=True) + highlighted = self.highlighter.highlight_block(node.astext(), + lang, + warn=warner, + **highlight_args) + starttag = self.starttag( + node, + 'code', + suffix='', + CLASS='docutils literal highlight highlight-%s' % lang) + self.body.append(starttag + highlighted.strip() + '') + raise docutils.nodes.SkipNode + + translator_class.visit_literal = visit_literal + # Due to the use of `SkipNode`, `depart_literal` is only called if the base + # (non-highlighting) implementation was used in `visit_literal`. Therefore, + # we don't need to override `depart_literal`. + + +def setup(app: sphinx.application.Sphinx) -> None: + docutils.parsers.rst.roles.register_canonical_role('code', code_role) + _monkey_patch_html_translator(sphinx.writers.html.HTMLTranslator) + _monkey_patch_html_translator(sphinx.writers.html5.HTML5Translator) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/nav_adapt.py b/docs/tensorstore_sphinx_material/sphinx_material/nav_adapt.py new file mode 100644 index 000000000..d43a73d55 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/nav_adapt.py @@ -0,0 +1,396 @@ +"""Injects mkdocs-style `nav` and `page` objects into the HTML jinja2 context.""" + +from typing import List, Union, NamedTuple, Optional, Tuple, Set, Iterator, Dict +import copy +import docutils.nodes +import jinja2 +import sphinx.builders +import sphinx.application +import sphinx.environment.adapters.toctree +import urllib.parse + + +def _strip_fragment(url: str) -> str: + """Returns the url with any fragment identifier removed.""" + fragment_start = url.find('#') + if fragment_start == -1: return url + return url[:fragment_start] + + +class MkdocsNavEntry: + # Title to display, as HTML. + title: str + + # Aria label text, plain text. + aria_label: str = None + + # URL of this page, or the first descendent if `caption_only` is `True`. + url: Optional[str] + # List of children + children: List['MkdocsNavEntry'] + # Set to `True` if this page, or a descendent, is the current page. + active: bool + # Set to `True` if this page is the current page. + current: bool + # Set to `True` if this entry does not refer to a unique page but is merely + # a TOC caption. + caption_only: bool + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + if not self.aria_label: + self.aria_label = self.title + + def __repr__(self): + return repr(self.__dict__) + + +class _TocVisitor(docutils.nodes.NodeVisitor): + """NodeVisitor used by `_get_mkdocs_toc`.""" + def __init__(self, document: docutils.nodes.document, + builder: sphinx.builders.Builder): + super().__init__(document) + self._prev_caption: Optional[docutils.nodes.Element] = None + self._rendered_title: Optional[str] = None + self._url: Optional[str] = None + self._builder = builder + # Indicates if this node or one of its descendents is the current page. + self._active = False + # List of direct children. + self._children: List[MkdocsNavEntry] = [] + + def _render(self, node: Union[docutils.nodes.Node, + List[docutils.nodes.Node]]): + """Returns the HTML representation of `node`.""" + if not isinstance(node, list): + node = [node] + return ''.join( + self._builder.render_partial(x)['fragment'] for x in node) + + def _render_title(self, node: Union[docutils.nodes.Node, + List[docutils.nodes.Node]]): + """Returns the text representation of `node`.""" + if not isinstance(node, list): + node = [node] + return jinja2.Markup.escape(''.join(x.astext() for x in node)) + + def visit_reference(self, node: docutils.nodes.reference): + self._rendered_title = self._render_title(node.children) + self._url = node.get('refuri') + raise docutils.nodes.SkipChildren + + def visit_compact_paragraph(self, node: docutils.nodes.Element): + pass + + def visit_toctree(self, node: docutils.nodes.Node): + raise docutils.nodes.SkipChildren + + def visit_paragraph(self, node: docutils.nodes.Node): + pass + + # In sphinx < 3.5.4, TOC captions are represented using a caption node. + def visit_caption(self, node: docutils.nodes.caption): + self._prev_caption = node + raise docutils.nodes.SkipChildren + + # In sphinx >= 3.5.4, TOC captions are represented using a title node. + def visit_title(self, node: docutils.nodes.title): + self._prev_caption = node + raise docutils.nodes.SkipChildren + + def visit_bullet_list(self, node: docutils.nodes.bullet_list): + if self._prev_caption is not None and self._prev_caption.parent is node.parent: + # Insert as sub-entry of the previous caption. + title = self._render_title(self._prev_caption.children) + self._prev_caption = None + child_visitor = _TocVisitor(self.document, self._builder) + if node.get('iscurrent', False): + child_visitor._active = True + node.walk(child_visitor) + url = None + children = child_visitor._children + if children: + url = children[0].url + self._children.append( + MkdocsNavEntry( + title=title, + url=url, + children=children, + active=child_visitor._active, + current=False, + caption_only=True, + )) + raise docutils.nodes.SkipChildren + # Otherwise, just process the each list_item as direct children. + + def get_result(self) -> MkdocsNavEntry: + return MkdocsNavEntry( + title=self._rendered_title, + url=self._url, + children=self._children, + active=self._active, + current=self._active and self._url == '', + caption_only=False, + ) + + def visit_list_item(self, node: docutils.nodes.list_item): + # Child node. Collect its url, title, and any children using a separate + # `_TocVisitor`. + child_visitor = _TocVisitor(self.document, self._builder) + if node.get('iscurrent', False): + child_visitor._active = True + for child in node.children: + child.walk(child_visitor) + child_result = child_visitor.get_result() + self._children.append(child_result) + raise docutils.nodes.SkipChildren + + +def _get_mkdocs_toc(toc_node: docutils.nodes.Node, + builder: sphinx.builders.Builder) -> List[MkdocsNavEntry]: + """Converts a docutils toc node into a mkdocs-format JSON toc.""" + visitor = _TocVisitor(sphinx.util.docutils.new_document(''), builder) + toc_node.walk(visitor) + return visitor._children + + +class _NavContextObject(list): + homepage: dict + + +def _traverse_mkdocs_toc( + toc: List[MkdocsNavEntry]) -> Iterator[MkdocsNavEntry]: + for entry in toc: + yield entry + yield from _traverse_mkdocs_toc(entry.children) + + +def _relative_uri_to_root_relative_and_anchor( + builder: sphinx.builders.html.StandaloneHTMLBuilder, + base_pagename: str, relative_uri: str) -> Optional[Tuple[str, str]]: + """Converts a relative URI to a root-relative uri and anchor.""" + uri = urllib.parse.urlparse( + urllib.parse.urljoin(builder.get_target_uri(base_pagename), + relative_uri)) + if uri.netloc: return None + return (uri.path, uri.fragment) + + +class DomainAnchorEntry(NamedTuple): + domain_name: str + name: str + display_name: str + objtype: str + priority: int + + +class ObjectIconInfo(NamedTuple): + icon_class: str + icon_text: str + + +OBJECT_ICON_INFO: Dict[Tuple[str, str], ObjectIconInfo] = { + ('std', 'envvar'): ObjectIconInfo(icon_class='alias', icon_text='$'), + ('json', 'schema'): ObjectIconInfo(icon_class='data', icon_text='J'), + ('json', 'subschema'): ObjectIconInfo(icon_class='sub-data', + icon_text='j'), + ('py', 'class'): ObjectIconInfo(icon_class='data', icon_text='C'), + ('py', 'function'): ObjectIconInfo(icon_class='procedure', icon_text='F'), + ('py', 'method'): ObjectIconInfo(icon_class='procedure', icon_text='M'), + ('py', 'classmethod'): ObjectIconInfo(icon_class='procedure', + icon_text='M'), + ('py', 'staticmethod'): ObjectIconInfo(icon_class='procedure', + icon_text='M'), + ('py', 'property'): ObjectIconInfo(icon_class='alias', icon_text='P'), + ('py', 'attribute'): ObjectIconInfo(icon_class='alias', icon_text='A'), + ('py', 'data'): ObjectIconInfo(icon_class='alias', icon_text='V'), + ('py', 'parameter'): ObjectIconInfo(icon_class='sub-data', icon_text='p'), +} + + +def _make_domain_anchor_map( + env: sphinx.environment.BuildEnvironment +) -> Dict[Tuple[str, str], DomainAnchorEntry]: + builder = env.app.builder + docname_to_url = { + docname: builder.get_target_uri(docname) + for docname in env.found_docs + } + m: Dict[Tuple[str, str], DomainAnchorEntry] = dict() + for domain_name, domain in env.domains.items(): + for (name, dispname, objtype, docname, anchor, + priority) in domain.get_objects(): + if (domain_name, objtype) not in OBJECT_ICON_INFO: + continue + key = (docname_to_url[docname], anchor) + m[key] = DomainAnchorEntry(domain_name, name, dispname, objtype, + priority) + return m + + +def _get_domain_anchor_map( + app: sphinx.application.Sphinx +) -> Dict[Tuple[str, str], DomainAnchorEntry]: + key = 'sphinx_material_domain_anchor_map' + m = app.env.temp_data.get(key) + if m is None: + m = _make_domain_anchor_map(app.env) + app.env.temp_data[key] = m + return m + + +def _add_domain_info_to_toc(app: sphinx.application.Sphinx, + toc: List[MkdocsNavEntry], pagename: str) -> None: + m = _get_domain_anchor_map(app) + for entry in _traverse_mkdocs_toc(toc): + if entry.caption_only or entry.url is None: continue + refinfo = _relative_uri_to_root_relative_and_anchor( + app.builder, pagename, entry.url) + if refinfo is None: continue + objinfo = m.get(refinfo) + if objinfo is None: continue + domain = app.env.domains[objinfo.domain_name] + get_object_synopsis = getattr(domain, 'get_object_synopsis', None) + label = domain.get_type_name(domain.object_types[objinfo.objtype]) + tooltip = f'{objinfo.name} ({label})' + if get_object_synopsis is not None: + synopsis = get_object_synopsis(objinfo.objtype, objinfo.name) + if synopsis: + synopsis = synopsis.strip() + if synopsis: + tooltip += f' — {synopsis}' + icon_info = OBJECT_ICON_INFO.get( + (objinfo.domain_name, objinfo.objtype)) + title_prefix = '' + if icon_info is not None: + title_prefix = ( + f'{icon_info.icon_text}') + entry.title = (title_prefix + + f'' + + f'{entry.title}') + + +def _get_current_page_in_toc( + toc: List[MkdocsNavEntry]) -> Optional[MkdocsNavEntry]: + for entry in toc: + if not entry.active: continue + if entry.current: + return entry + return _get_current_page_in_toc(entry.children) + return None + + +def _collapse_children_not_on_same_page( + entry: MkdocsNavEntry) -> MkdocsNavEntry: + entry = copy.copy(entry) + if not entry.active: + entry.children = [] + else: + entry.children = [ + _collapse_children_not_on_same_page(child) + for child in entry.children + ] + return entry + + +def _get_mkdocs_tocs( + app: sphinx.application.Sphinx, pagename: str, duplicate_local_toc: bool +) -> Tuple[List[MkdocsNavEntry], List[MkdocsNavEntry]]: + theme_options = app.config["html_theme_options"] + global_toc_node = sphinx.environment.adapters.toctree.TocTree( + app.env).get_toctree_for( + pagename, + app.builder, + collapse=theme_options.get('globaltoc_collapse', False), + includehidden=theme_options.get('globaltoc_includehidden', True), + maxdepth=theme_options.get('globaltoc_depth', -1), + titles_only=False, + ) + global_toc = _get_mkdocs_toc(global_toc_node, app.builder) + local_toc = [] + if pagename != app.env.config.master_doc: + # Extract entry from `global_toc` corresponding to the current page. + current_page_toc_entry = _get_current_page_in_toc(global_toc) + if current_page_toc_entry: + local_toc = [ + _collapse_children_not_on_same_page(current_page_toc_entry) + ] + if not duplicate_local_toc: + current_page_toc_entry.children = [] + + else: + # Every page is a child of the root page. We still want a full TOC + # tree, though. + local_toc_node = sphinx.environment.adapters.toctree.TocTree( + app.env).get_toc_for( + pagename, + app.builder, + ) + local_toc = _get_mkdocs_toc(local_toc_node, app.builder) + + _add_domain_info_to_toc(app, global_toc, pagename) + _add_domain_info_to_toc(app, local_toc, pagename) + + if len(local_toc) == 1 and len(local_toc[0].children) == 0: + local_toc = [] + + return global_toc, local_toc + + +def _html_page_context(app: sphinx.application.Sphinx, pagename: str, + templatename: str, context: dict, + doctree: docutils.nodes.Node) -> None: + theme_options = app.config["html_theme_options"] + page_title = jinja2.Markup.escape( + jinja2.Markup(context.get('title')).striptags()) + meta = context.get('meta', {}) + global_toc, local_toc = _get_mkdocs_tocs( + app, + pagename, + duplicate_local_toc=bool( + meta and isinstance(meta.get('duplicate-local-toc'), str))) + context.update(nav=_NavContextObject(global_toc)) + context['nav'].homepage = dict(url=context['pathto']( + context['master_doc']), ) + + # Add other context values in mkdocs/mkdocs-material format. + page = dict( + title=page_title, + is_homepage=(pagename == context['master_doc']), + toc=local_toc, + meta={ + 'hide': [], + 'revision_date': context.get('last_updated') + }, + content=context.get('body'), + ) + if meta and meta.get('tocdepth') == 0: + page['meta']['hide'].append('toc') + if context.get('next'): + page['next_page'] = { + 'title': + jinja2.Markup.escape( + jinja2.Markup(context['next']['title']).striptags()), + 'url': + context['next']['link'], + } + if context.get('prev'): + page['previous_page'] = { + 'title': + jinja2.Markup.escape( + jinja2.Markup(context['prev']['title']).striptags()), + 'url': + context['prev']['link'], + } + + context.update(page=page, ) + + +def setup(app: sphinx.application.Sphinx) -> None: + app.connect("html-page-context", _html_page_context) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/object_toc.py b/docs/tensorstore_sphinx_material/sphinx_material/object_toc.py new file mode 100644 index 000000000..b38b610d5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/object_toc.py @@ -0,0 +1,149 @@ +"""Adds all Sphinx "objects" to the table of contents.""" + +from typing import Optional +import docutils.nodes +import sphinx.addnodes +import sphinx.application +import sphinx.environment.collectors.toctree + +def _monkey_patch_toc_tree_process_doc(app: sphinx.application.Sphinx): + """Enables support for also finding Sphinx domain objects. + + Args: + app: Sphinx application. + """ + TocTreeCollector = sphinx.environment.collectors.toctree.TocTreeCollector + + # Apply the monkey pach + orig_process_doc = TocTreeCollector.process_doc + + def _make_section_from_desc(source: sphinx.addnodes.desc) -> Optional[docutils.nodes.section]: + signature: sphinx.addnodes.desc_signature + for child in source._traverse(): + if not isinstance(child, sphinx.addnodes.desc_signature): continue + signature = child + break + else: + # No signature found + return None + ids = signature['ids'] + if not ids: + # Not indexed. + return None + section = docutils.nodes.section() + section['ids'] = ids + + # Extract title from signature + title = '' + for child in signature._traverse(): + if isinstance(child, (sphinx.addnodes.desc_name, sphinx.addnodes.desc_addname)): + title += child.astext() + if not title: + # No name found + return None + # Sphinx uses the first child of the section node as the title. + titlenode = docutils.nodes.comment(title, title) + section += titlenode + return section + + def _make_section_from_field( + source: docutils.nodes.field) -> Optional[docutils.nodes.section]: + fieldname = source[0] + fieldbody = source[1] + ids = fieldname['ids'] + if not ids: + # Not indexed + return None + section = docutils.nodes.section() + section['ids'] = ids + title = fieldname.astext() + # Sphinx uses the first child of the section node as the title. + titlenode = docutils.nodes.comment(title, title) + section += titlenode + return section + + def _make_section_from_parameter( + source: docutils.nodes.term) -> Optional[docutils.nodes.section]: + ids = source['ids'] + if not ids: + # Not indexed + return None + section = docutils.nodes.section() + section['ids'] = ids + paramname = source['paramname'] + titlenode = docutils.nodes.comment(paramname, paramname) + section += titlenode + return section + + def _patched_process_doc( + self: sphinx.environment.collectors.toctree.TocTreeCollector, + app: sphinx.application.Sphinx, + doctree: docutils.nodes.document) -> None: + new_document = doctree.copy() # Shallow copy + + def _collect(source: docutils.nodes.Node, + target: docutils.nodes.Element) -> None: + if not isinstance(source, docutils.nodes.Element): + return + children = iter(source.children) + if isinstance(source, docutils.nodes.section): + new_node = source.copy() + # Also copy first child, which sphinx interprets as the title + new_node += next(children).deepcopy() + target += new_node + target = new_node + elif isinstance(source, sphinx.addnodes.only): + # Retain "only" nodes since they affect toc generation. + new_node = source.copy() + target += new_node + target = new_node + elif isinstance(source, sphinx.addnodes.toctree): + # Deep copy entire toctree + new_node = source.deepcopy() + target += new_node + return + elif isinstance(source, sphinx.addnodes.desc): + # Object description. Try to create synthetic section. + new_node = _make_section_from_desc(source) + if new_node is not None: + target += new_node + target = new_node + elif isinstance(source, docutils.nodes.field): + # Field. Try to create synthetic section. + new_node = _make_section_from_field(source) + if new_node is not None: + target += new_node + target = new_node + elif isinstance(source, + docutils.nodes.term) and source.get('paramname'): + # Parameter within object description. Try to create synthetic section. + new_node = _make_section_from_parameter(source) + if new_node is not None: + target += new_node + # Parameters cannot contain sub-sections + return + + for child in children: + _collect(child, target) + + _collect(doctree, new_document) + return orig_process_doc(self, app, new_document) + + TocTreeCollector.process_doc = _patched_process_doc + + # TocTreeCollector is registered before our extension is. In order for the + # monkey patching to take effect, we need to unregister it and re-register it. + for read_listener in app.events.listeners['doctree-read']: + obj = getattr(read_listener.handler, '__self__', None) + if obj is not None and isinstance(obj, TocTreeCollector): + obj.disable(app) + app.add_env_collector(TocTreeCollector) + break + + +def setup(app: sphinx.application.Sphinx) -> None: + _monkey_patch_toc_tree_process_doc(app) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/postprocess_html.py b/docs/tensorstore_sphinx_material/sphinx_material/postprocess_html.py new file mode 100644 index 000000000..8c39edff3 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/postprocess_html.py @@ -0,0 +1,52 @@ +import multiprocessing +from xml.etree import ElementTree + +import docutils.nodes +import sphinx.application +import sphinx.util.console + + +def add_html_link(app: sphinx.application.Sphinx, pagename: str, + templatename: str, context: dict, + doctree: docutils.nodes.Node): + """As each page is built, collect page names for the sitemap""" + base_url = app.config["html_theme_options"].get("site_url", "") + if base_url: + if not base_url.endswith('/'): + base_url += '/' + full_url = base_url + app.builder.get_target_uri(pagename) + app.sitemap_links.append(full_url) + + +def create_sitemap(app: sphinx.application.Sphinx, exception): + """Generates the sitemap.xml from the collected HTML page links""" + if (not app.config["html_theme_options"].get("site_url", "") + or exception is not None or not app.sitemap_links): + return + + filename = app.outdir + "/sitemap.xml" + print("Generating sitemap for {0} pages in " + "{1}".format(len(app.sitemap_links), + sphinx.util.console.colorize("blue", filename))) + + root = ElementTree.Element("urlset") + root.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9") + + for link in app.sitemap_links: + url = ElementTree.SubElement(root, "url") + ElementTree.SubElement(url, "loc").text = link + app.sitemap_links[:] = [] + + ElementTree.ElementTree(root).write(filename) + + +def setup(app: sphinx.application.Sphinx) -> None: + app.connect("html-page-context", add_html_link) + app.connect("build-finished", create_sitemap) + manager = multiprocessing.Manager() + app.sitemap_links = manager.list() + app.multiprocess_manager = manager + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/search_adapt.py b/docs/tensorstore_sphinx_material/sphinx_material/search_adapt.py new file mode 100644 index 000000000..e86d3ce7c --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/search_adapt.py @@ -0,0 +1,70 @@ +"""Modifies the sphinx search index. + +- object synopses are added + +- instead of the list of docnames, there is a list of URLs. That way we don't + need to duplicate in JavaScript the logic of determining a URL from a page + name. + +- the unused list of filenames is removed, since it just bloated the index. +""" + +from typing import Dict, Tuple + +import sphinx.search +import sphinx.application + + +class IndexBuilder(sphinx.search.IndexBuilder): + def get_objects( + self, fn2index: Dict[str, int] + ) -> Dict[str, Dict[str, Tuple[int, int, int, str, str]]]: + rv = super().get_objects(fn2index) + onames = self._objnames + for prefix, children in rv.items(): + if prefix: + name_prefix = prefix + '.' + else: + name_prefix = '' + for name, (docindex, typeindex, prio, + shortanchor) in children.items(): + objtype_entry = onames[typeindex] + domain_name = objtype_entry[0] + domain = self.env.domains[domain_name] + synopsis = '' + get_object_synopsis = getattr(domain, 'get_object_synopsis', + None) + if get_object_synopsis: + objtype = objtype_entry[1] + full_name = name_prefix + name + synopsis = get_object_synopsis(objtype, full_name) + if synopsis: + synopsis = synopsis.strip() + children[name] = (docindex, typeindex, prio, shortanchor, + synopsis) + return rv + + def freeze(self): + result = super().freeze() + + # filenames are unused + result.pop('filenames') + + docnames = result.pop('docnames') + + builder = self.env.app.builder + result.update( + docurls=[builder.get_target_uri(docname) for docname in docnames]) + return result + + +def _monkey_patch_index_builder(): + sphinx.search.IndexBuilder = IndexBuilder + + +def setup(app: sphinx.application.Sphinx) -> None: + _monkey_patch_index_builder() + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/tensorstore_sphinx_material/sphinx_material/theme.conf b/docs/tensorstore_sphinx_material/sphinx_material/theme.conf new file mode 100644 index 000000000..6558b7541 --- /dev/null +++ b/docs/tensorstore_sphinx_material/sphinx_material/theme.conf @@ -0,0 +1,70 @@ +[theme] + +inherit = basic +stylesheet = material.css + +[options] + +logo_svg = + +direction = + +icon = + +# TODO: This could be improved +# Path to a touch icon. Should be 152x152 or larger. Path is relative +# to _static, so that if your icon is in _static/images/touch-icon.png, +# touch_icon would be images/touch-icon.png. Icon must be in _static since +# this file is not copied +touch_icon = + +font = + +features = + +palette = + +# Set to an array `["UA-XXXXX", "auto"]` to enable google analytics +google_analytics = + +# Set to disqus id to enable disqus integration +disqus = + +# Repository integration +# Set the repo url for the link to appear +repo_url = +# The name of the repo. If must be set if repo_url is set +repo_name = +# Must be one of github, gitlab or bitbucket +repo_type = github + +# Sitemap generation +# Specify a base_url used to generate sitemap.xml links. If not specified, then +# no sitemap will be built. +site_url = + +pwa_manifest = + +plugins = + +# TOC Tree generation +# The maximum depth of the global TOC; set it to -1 to allow unlimited depth +globaltoc_depth = +# If true, TOC entries that are not ancestors of the current page are collapsed +globaltoc_collapse = +# If true, the global TOC tree will also contain hidden entries +globaltoc_includehidden = + +# Enable the version dropdown feature. See the demo site for the structure +# of the json file required. +version_dropdown = False + +# Optional dictionary that to use when populating the version dropdown. +# The key is the text that appears, and the value is the absolute path +# of the alternative versions +version_info = + +# Relative path to json file. The default is "versions.json" which assumes the +# file hosted in the root of the site. You can use other file names or locations, e.g., +# "_static/old_versions.json" +version_json = "versions.json" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/_/index.ts new file mode 100644 index 000000000..1053d17d7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/_/index.ts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { getElementOrThrow, getLocation } from "~/browser" +import { Version } from "~/templates/version" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Feature flag + */ +export type Flag = + | "header.autohide" /* Hide header */ + | "navigation.expand" /* Automatic expansion */ + | "navigation.instant" /* Instant loading */ + | "navigation.sections" /* Sections navigation */ + | "navigation.tabs" /* Tabs navigation */ + | "navigation.top" /* Back-to-top button */ + | "toc.integrate" /* Integrated table of contents */ + +/* ------------------------------------------------------------------------- */ + +/** + * Translation + */ +export type Translation = + | "clipboard.copy" /* Copy to clipboard */ + | "clipboard.copied" /* Copied to clipboard */ + | "search.config.lang" /* Search language */ + | "search.config.pipeline" /* Search pipeline */ + | "search.config.separator" /* Search separator */ + | "search.placeholder" /* Search */ + | "search.result.placeholder" /* Type to start searching */ + | "search.result.none" /* No matching documents */ + | "search.result.one" /* 1 matching document */ + | "search.result.other" /* # matching documents */ + | "search.result.more.one" /* 1 more on this page */ + | "search.result.more.other" /* # more on this page */ + | "search.result.term.missing" /* Missing */ + +/** + * Translations + */ +export type Translations = Record + +/* ------------------------------------------------------------------------- */ + +/** + * Versioning + */ +export interface Versioning { + provider: "mike" /* Version provider */ + staticVersions?: Version[] /* Static version list to use */ + versionPath?: string /* Base-relative path to versions.json */ +} + +/** + * Configuration + */ +export interface Config { + base: string /* Base URL */ + features: Flag[] /* Feature flags */ + translations: Translations /* Translations */ + version?: Versioning /* Versioning */ +} + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Retrieve global configuration and make base URL absolute + */ +const script = getElementOrThrow("#__config") +const config: Config = JSON.parse(script.textContent!) +config.base = new URL(config.base, getLocation()) + .toString() + .replace(/\/$/, "") + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve global configuration + * + * @returns Global configuration + */ +export function configuration(): Config { + return config +} + +/** + * Check whether a feature flag is enabled + * + * @param flag - Feature flag + * + * @returns Test result + */ +export function feature(flag: Flag): boolean { + return config.features.includes(flag) +} + +/** + * Retrieve the translation for the given key + * + * @param key - Key to be translated + * @param value - Positional value, if any + * + * @returns Translation + */ +export function translation( + key: Translation, value?: string | number +): string { + return typeof value !== "undefined" + ? config.translations[key].replace("#", value.toString()) + : config.translations[key] +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/_/index.ts new file mode 100644 index 000000000..babb9d33a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/_/index.ts @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set focusable property + * + * @param el - Element + * @param value - Tabindex value + */ +export function setFocusable( + el: HTMLElement, value = 0 +): void { + el.setAttribute("tabindex", value.toString()) +} + +/** + * Reset focusable property + * + * @param el - Element + */ +export function resetFocusable( + el: HTMLElement +): void { + el.removeAttribute("tabindex") +} + +/** + * Set scroll lock + * + * @param el - Scrollable element + * @param value - Vertical offset + */ +export function setScrollLock( + el: HTMLElement, value: number +): void { + el.setAttribute("data-md-state", "lock") + el.style.top = `-${value}px` +} + +/** + * Reset scroll lock + * + * @param el - Scrollable element + */ +export function resetScrollLock( + el: HTMLElement +): void { + const value = -1 * parseInt(el.style.top, 10) + el.removeAttribute("data-md-state") + el.style.top = "" + if (value) + window.scrollTo(0, value) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/anchor/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/anchor/index.ts new file mode 100644 index 000000000..b1d3c8964 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/anchor/index.ts @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set anchor state + * + * @param el - Anchor element + * @param state - Anchor state + */ +export function setAnchorState( + el: HTMLElement, state: "blur" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset anchor state + * + * @param el - Anchor element + */ +export function resetAnchorState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} + +/* ------------------------------------------------------------------------- */ + +/** + * Set anchor active + * + * @param el - Anchor element + * @param value - Whether the anchor is active + */ +export function setAnchorActive( + el: HTMLElement, value: boolean +): void { + el.classList.toggle("md-nav__link--active", value) +} + +/** + * Reset anchor active + * + * @param el - Anchor element + */ +export function resetAnchorActive( + el: HTMLElement +): void { + el.classList.remove("md-nav__link--active") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/dialog/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/dialog/index.ts new file mode 100644 index 000000000..d951d34d6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/dialog/index.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set dialog message + * + * @param el - Dialog element + * @param value - Dialog message + */ +export function setDialogMessage( + el: HTMLElement, value: string +): void { + el.firstElementChild!.innerHTML = value +} + +/* ------------------------------------------------------------------------- */ + +/** + * Set dialog state + * + * @param el - Dialog element + * @param state - Dialog state + */ +export function setDialogState( + el: HTMLElement, state: "open" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset dialog state + * + * @param el - Dialog element + */ +export function resetDialogState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/_/index.ts new file mode 100644 index 000000000..400f44bdb --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/_/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set header state + * + * @param el - Header element + * @param state - Header state + */ +export function setHeaderState( + el: HTMLElement, state: "shadow" | "hidden" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset header state + * + * @param el - Header element + */ +export function resetHeaderState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/index.ts new file mode 100644 index 000000000..e98ef06a7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./title" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/title/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/title/index.ts new file mode 100644 index 000000000..62e247980 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/header/title/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set header title state + * + * @param el - Header title element + * @param state - Header title state + */ +export function setHeaderTitleState( + el: HTMLElement, state: "active" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset header title state + * + * @param el - Header title element + */ +export function resetHeaderTitleState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/index.ts new file mode 100644 index 000000000..24f2676dc --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./anchor" +export * from "./dialog" +export * from "./header" +export * from "./search" +export * from "./sidebar" +export * from "./source" +export * from "./tabs" +export * from "./top" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/index.ts new file mode 100644 index 000000000..efacc39da --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./query" +export * from "./result" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/query/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/query/index.ts new file mode 100644 index 000000000..42696fa0d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/query/index.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { translation } from "~/_" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set search query placeholder + * + * @param el - Search query element + * @param value - Placeholder + */ +export function setSearchQueryPlaceholder( + el: HTMLInputElement, value: string +): void { + el.placeholder = value +} + +/** + * Reset search query placeholder + * + * @param el - Search query element + */ +export function resetSearchQueryPlaceholder( + el: HTMLInputElement +): void { + el.placeholder = translation("search.placeholder") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/result/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/result/index.ts new file mode 100644 index 000000000..b9d23952e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/search/result/index.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { translation } from "~/_" +import { round } from "~/utilities" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set number of search results + * + * @param el - Search result metadata element + * @param value - Number of results + */ +export function setSearchResultMeta( + el: HTMLElement, value: number +): void { + switch (value) { + + /* No results */ + case 0: + el.textContent = translation("search.result.none") + break + + /* One result */ + case 1: + el.textContent = translation("search.result.one") + break + + /* Multiple result */ + default: + el.textContent = translation("search.result.other", round(value)) + } +} + +/** + * Reset number of search results + * + * @param el - Search result metadata element + */ +export function resetSearchResultMeta( + el: HTMLElement +): void { + el.textContent = translation("search.result.placeholder") +} + +/* ------------------------------------------------------------------------- */ + +/** + * Add an element to the search result list + * + * @param el - Search result list element + * @param child - Search result element + */ +export function addToSearchResultList( + el: HTMLElement, child: Element +): void { + el.appendChild(child) +} + +/** + * Reset search result list + * + * @param el - Search result list element + */ +export function resetSearchResultList( + el: HTMLElement +): void { + el.innerHTML = "" +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/sidebar/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/sidebar/index.ts new file mode 100644 index 000000000..403bcc606 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/sidebar/index.ts @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set sidebar offset + * + * @param el - Sidebar element + * @param value - Sidebar offset + */ +export function setSidebarOffset( + el: HTMLElement, value: number +): void { + el.style.top = `${value}px` +} + +/** + * Reset sidebar offset + * + * @param el - Sidebar element + */ +export function resetSidebarOffset( + el: HTMLElement +): void { + el.style.top = "" +} + +/* ------------------------------------------------------------------------- */ + +/** + * Set sidebar height + * + * This function doesn't set the height of the actual sidebar, but of its first + * child – the `.md-sidebar__scrollwrap` element in order to mitigiate jittery + * sidebars when the footer is scrolled into view. At some point we switched + * from `absolute` / `fixed` positioning to `sticky` positioning, significantly + * reducing jitter in some browsers (respectively Firefox and Safari) when + * scrolling from the top. However, top-aligned sticky positioning means that + * the sidebar snaps to the bottom when the end of the container is reached. + * This is what leads to the mentioned jitter, as the sidebar's height may be + * updated too slowly. + * + * This behaviour can be mitigiated by setting the height of the sidebar to `0` + * while preserving the padding, and the height on its first element. + * + * @param el - Sidebar element + * @param value - Sidebar height + */ +export function setSidebarHeight( + el: HTMLElement, value: number +): void { + const scrollwrap = el.firstElementChild as HTMLElement + scrollwrap.style.height = `${value - 2 * scrollwrap.offsetTop}px` +} + +/** + * Reset sidebar height + * + * @param el - Sidebar element + */ +export function resetSidebarHeight( + el: HTMLElement +): void { + const scrollwrap = el.firstElementChild as HTMLElement + scrollwrap.style.height = "" +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/source/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/source/index.ts new file mode 100644 index 000000000..d2074e0ad --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/source/index.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set repository facts + * + * @param el - Repository element + * @param child - Repository facts element + */ +export function setSourceFacts( + el: HTMLElement, child: Element +): void { + el.lastElementChild!.appendChild(child) +} + +/** + * Set repository state + * + * @param el - Repository element + * @param state - Repository state + */ +export function setSourceState( + el: HTMLElement, state: "done" +): void { + el.lastElementChild!.setAttribute("data-md-state", state) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/tabs/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/tabs/index.ts new file mode 100644 index 000000000..5fd31afef --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/tabs/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set tabs state + * + * @param el - Tabs element + * @param state - Tabs state + */ +export function setTabsState( + el: HTMLElement, state: "hidden" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset tabs state + * + * @param el - Tabs element + */ +export function resetTabsState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/top/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/top/index.ts new file mode 100644 index 000000000..2a24ddd99 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/actions/top/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set back-to-top state + * + * @param el - Back-to-top element + * @param state - Back-to-top state + */ +export function setBackToTopState( + el: HTMLElement, state: "hidden" +): void { + el.setAttribute("data-md-state", state) +} + +/** + * Reset back-to-top state + * + * @param el - Back-to-top element + */ +export function resetBackToTopState( + el: HTMLElement +): void { + el.removeAttribute("data-md-state") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/document/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/document/index.ts new file mode 100644 index 000000000..dbb290ad6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/document/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { ReplaySubject, Subject, fromEvent } from "rxjs" +import { mapTo } from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch document + * + * Documents are implemented as subjects, so all downstream observables are + * automatically updated when a new document is emitted. + * + * @returns Document subject + */ +export function watchDocument(): Subject { + const document$ = new ReplaySubject() + fromEvent(document, "DOMContentLoaded") + .pipe( + mapTo(document) + ) + .subscribe(document$) + + /* Return document */ + return document$ +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/.eslintrc b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/.eslintrc new file mode 100644 index 000000000..82472629d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "jsdoc/require-jsdoc": "off" + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/index.ts new file mode 100644 index 000000000..48b3f805d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/_/index.ts @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve an element matching the query selector + * + * @template T - Element type + * + * @param selector - Query selector + * @param node - Node of reference + * + * @returns Element or nothing + */ +export function getElement( + selector: T, node?: ParentNode +): HTMLElementTagNameMap[T] + +export function getElement( + selector: string, node?: ParentNode +): T | undefined + +export function getElement( + selector: string, node: ParentNode = document +): T | undefined { + return node.querySelector(selector) || undefined +} + +/** + * Retrieve an element matching a query selector or throw a reference error + * + * @template T - Element type + * + * @param selector - Query selector + * @param node - Node of reference + * + * @returns Element + */ +export function getElementOrThrow( + selector: T, node?: ParentNode +): HTMLElementTagNameMap[T] + +export function getElementOrThrow( + selector: string, node?: ParentNode +): T + +export function getElementOrThrow( + selector: string, node: ParentNode = document +): T { + const el = getElement(selector, node) + if (typeof el === "undefined") + throw new ReferenceError( + `Missing element: expected "${selector}" to be present` + ) + return el +} + +/** + * Retrieve the currently active element + * + * @returns Element or nothing + */ +export function getActiveElement(): HTMLElement | undefined { + return document.activeElement instanceof HTMLElement + ? document.activeElement + : undefined +} + +/** + * Retrieve all elements matching the query selector + * + * @template T - Element type + * + * @param selector - Query selector + * @param node - Node of reference + * + * @returns Elements + */ +export function getElements( + selector: T, node?: ParentNode +): HTMLElementTagNameMap[T][] + +export function getElements( + selector: string, node?: ParentNode +): T[] + +export function getElements( + selector: string, node: ParentNode = document +): T[] { + return Array.from(node.querySelectorAll(selector)) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Create an element + * + * @template T - Tag name type + * + * @param tagName - Tag name + * + * @returns Element + */ +export function createElement( + tagName: T +): HTMLElementTagNameMap[T] { + return document.createElement(tagName) +} + +/** + * Replace an element with the given list of nodes + * + * @param el - Element + * @param nodes - Replacement nodes + */ +export function replaceElement( + el: HTMLElement, ...nodes: Node[] +): void { + el.replaceWith(...nodes) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/focus/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/focus/index.ts new file mode 100644 index 000000000..292a5f875 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/focus/index.ts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, merge } from "rxjs" +import { map, startWith } from "rxjs/operators" + +import { getActiveElement } from "../_" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set element focus + * + * @param el - Element + * @param value - Whether the element should be focused + */ +export function setElementFocus( + el: HTMLElement, value = true +): void { + if (value) + el.focus() + else + el.blur() +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch element focus + * + * @param el - Element + * + * @returns Element focus observable + */ +export function watchElementFocus( + el: HTMLElement +): Observable { + return merge( + fromEvent(el, "focus"), + fromEvent(el, "blur") + ) + .pipe( + map(({ type }) => type === "focus"), + startWith(el === getActiveElement()) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/index.ts new file mode 100644 index 000000000..4cbe7f983 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./focus" +export * from "./offset" +export * from "./selection" +export * from "./size" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/offset/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/offset/index.ts new file mode 100644 index 000000000..e51f784fe --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/offset/index.ts @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, merge } from "rxjs" +import { + distinctUntilChanged, + map, + startWith +} from "rxjs/operators" + +import { + getElementContentSize, + getElementSize +} from "../size" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Element offset + */ +export interface ElementOffset { + x: number /* Horizontal offset */ + y: number /* Vertical offset */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve element offset + * + * @param el - Element + * + * @returns Element offset + */ +export function getElementOffset(el: HTMLElement): ElementOffset { + return { + x: el.scrollLeft, + y: el.scrollTop + } +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch element offset + * + * @param el - Element + * + * @returns Element offset observable + */ +export function watchElementOffset( + el: HTMLElement +): Observable { + return merge( + fromEvent(el, "scroll"), + fromEvent(window, "resize") + ) + .pipe( + map(() => getElementOffset(el)), + startWith(getElementOffset(el)) + ) +} + +/** + * Watch element threshold + * + * This function returns an observable which emits whether the bottom scroll + * offset of an elements is within a certain threshold. + * + * @param el - Element + * @param threshold - Threshold + * + * @returns Element threshold observable + */ +export function watchElementThreshold( + el: HTMLElement, threshold = 16 +): Observable { + return watchElementOffset(el) + .pipe( + map(({ y }) => { + const visible = getElementSize(el) + const content = getElementContentSize(el) + return y >= ( + content.height - visible.height - threshold + ) + }), + distinctUntilChanged() + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/selection/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/selection/index.ts new file mode 100644 index 000000000..ebbbaee04 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/selection/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set element text selection + * + * @param el - Element + */ +export function setElementSelection( + el: HTMLElement +): void { + if (el instanceof HTMLInputElement) + el.select() + else + throw new Error("Not implemented") +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/size/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/size/index.ts new file mode 100644 index 000000000..b45c29fb3 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/element/size/index.ts @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + NEVER, + Observable, + Subject, + defer, + of +} from "rxjs" +import { + filter, + finalize, + map, + shareReplay, + startWith, + switchMap, + tap +} from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Element offset + */ +export interface ElementSize { + width: number /* Element width */ + height: number /* Element height */ +} + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Resize observer entry subject + */ +const entry$ = new Subject() + +/** + * Resize observer observable + * + * This observable will create a `ResizeObserver` on the first subscription + * and will automatically terminate it when there are no more subscribers. + * It's quite important to centralize observation in a single `ResizeObserver`, + * as the performance difference can be quite dramatic, as the link shows. + * + * @see https://bit.ly/3iIYfEm - Google Groups on performance + */ +const observer$ = defer(() => of( + new ResizeObserver(entries => { + for (const entry of entries) + entry$.next(entry) + }) +)) + .pipe( + switchMap(resize => NEVER.pipe(startWith(resize)) + .pipe( + finalize(() => resize.disconnect()) + ) + ), + shareReplay(1) + ) + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve element size + * + * @param el - Element + * + * @returns Element size + */ +export function getElementSize(el: HTMLElement): ElementSize { + return { + width: el.offsetWidth, + height: el.offsetHeight + } +} + +/** + * Retrieve element content size, i.e. including overflowing content + * + * @param el - Element + * + * @returns Element size + */ +export function getElementContentSize(el: HTMLElement): ElementSize { + return { + width: el.scrollWidth, + height: el.scrollHeight + } +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch element size + * + * This function returns an observable that subscribes to a single internal + * instance of `ResizeObserver` upon subscription, and emit resize events until + * termination. Note that this function should not be called with the same + * element twice, as the first unsubscription will terminate observation. + * + * Sadly, we can't use the `DOMRect` objects returned by the observer, because + * we need the emitted values to be consistent with `getElementSize`, which will + * return the used values (rounded) and not actual values (unrounded). Thus, we + * use the `offset*` properties. See the linked GitHub issue. + * + * @see https://bit.ly/3m0k3he - GitHub issue + * + * @param el - Element + * + * @returns Element size observable + */ +export function watchElementSize( + el: HTMLElement +): Observable { + return observer$ + .pipe( + tap(observer => observer.observe(el)), + switchMap(observer => entry$ + .pipe( + filter(({ target }) => target === el), + finalize(() => observer.unobserve(el)), + map(() => getElementSize(el)) + ) + ), + startWith(getElementSize(el)) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/index.ts new file mode 100644 index 000000000..6b4dc2345 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./document" +export * from "./element" +export * from "./keyboard" +export * from "./location" +export * from "./media" +export * from "./request" +export * from "./toggle" +export * from "./viewport" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/keyboard/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/keyboard/index.ts new file mode 100644 index 000000000..8c7a46df6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/keyboard/index.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent } from "rxjs" +import { filter, map, share } from "rxjs/operators" + +import { getActiveElement } from "../element" +import { getToggle } from "../toggle" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Keyboard mode + */ +export type KeyboardMode = + | "global" /* Global */ + | "search" /* Search is open */ + +/* ------------------------------------------------------------------------- */ + +/** + * Keyboard + */ +export interface Keyboard { + mode: KeyboardMode /* Keyboard mode */ + type: string /* Key type */ + claim(): void /* Key claim */ +} + +/* ---------------------------------------------------------------------------- + * Helper functions + * ------------------------------------------------------------------------- */ + +/** + * Check whether an element may receive keyboard input + * + * @param el - Element + * + * @returns Test result + */ +function isSusceptibleToKeyboard(el: HTMLElement): boolean { + switch (el.tagName) { + + /* Form elements */ + case "INPUT": + case "SELECT": + case "TEXTAREA": + return true + + /* Everything else */ + default: + return el.isContentEditable + } +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch keyboard + * + * @returns Keyboard observable + */ +export function watchKeyboard(): Observable { + return fromEvent(window, "keydown") + .pipe( + filter(ev => !(ev.metaKey || ev.ctrlKey)), + map(ev => ({ + mode: getToggle("search") ? "search" : "global", + type: ev.key, + claim() { + ev.preventDefault() + ev.stopPropagation() + } + } as Keyboard)), + filter(({ mode }) => { + if (mode === "global") { + const active = getActiveElement() + if (typeof active !== "undefined") + return !isSusceptibleToKeyboard(active) + } + return true + }), + share() + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/_/index.ts new file mode 100644 index 000000000..a2ab32dce --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/_/index.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Subject } from "rxjs" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve location + * + * This function returns a `URL` object (and not `Location`) to normalize the + * typings across the application. Furthermore, locations need to be tracked + * without setting them and `Location` is a singleton which represents the + * current location. + * + * @returns URL + */ +export function getLocation(): URL { + return new URL(location.href) +} + +/** + * Set location + * + * @param url - URL to change to + */ +export function setLocation(url: URL): void { + location.href = url.href +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch location + * + * @returns Location subject + */ +export function watchLocation(): Subject { + return new Subject() +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/hash/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/hash/index.ts new file mode 100644 index 000000000..3e0212684 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/hash/index.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, of } from "rxjs" +import { filter, map, share, startWith, switchMap } from "rxjs/operators" + +import { createElement, getElement } from "~/browser" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve location hash + * + * @returns Location hash + */ +export function getLocationHash(): string { + return location.hash.substring(1) +} + +/** + * Set location hash + * + * Setting a new fragment identifier via `location.hash` will have no effect + * if the value doesn't change. When a new fragment identifier is set, we want + * the browser to target the respective element at all times, which is why we + * use this dirty little trick. + * + * @param hash - Location hash + */ +export function setLocationHash(hash: string): void { + const el = createElement("a") + el.href = hash + el.addEventListener("click", ev => ev.stopPropagation()) + el.click() +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch location hash + * + * @returns Location hash observable + */ +export function watchLocationHash(): Observable { + return fromEvent(window, "hashchange") + .pipe( + map(getLocationHash), + startWith(getLocationHash()), + filter(hash => hash.length > 0), + share() + ) +} + +/** + * Watch location target + * + * @returns Location target observable + */ +export function watchLocationTarget(): Observable { + return watchLocationHash() + .pipe( + switchMap(id => of(getElement(`[id="${id}"]`)!)) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/index.ts new file mode 100644 index 000000000..551a09ac0 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/location/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./hash" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/media/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/media/index.ts new file mode 100644 index 000000000..b6a9ae24c --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/media/index.ts @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + NEVER, + Observable, + fromEvent, + fromEventPattern +} from "rxjs" +import { + mapTo, + startWith, + switchMap +} from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch media query + * + * Note that although `MediaQueryList.addListener` is deprecated we have to + * use it, because it's the only way to ensure proper downward compatibility. + * + * @see https://bit.ly/3dUBH2m - GitHub issue + * + * @param query - Media query + * + * @returns Media observable + */ +export function watchMedia(query: string): Observable { + const media = matchMedia(query) + return fromEventPattern(next => ( + media.addListener(() => next(media.matches)) + )) + .pipe( + startWith(media.matches) + ) +} + +/** + * Watch print mode, cross-browser + * + * @returns Print mode observable + */ +export function watchPrint(): Observable { + return fromEvent(window, "beforeprint") + .pipe( + mapTo(undefined) + ) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Toggle an observable with a media observable + * + * @template T - Data type + * + * @param query$ - Media observable + * @param factory - Observable factory + * + * @returns Toggled observable + */ +export function at( + query$: Observable, factory: () => Observable +): Observable { + return query$ + .pipe( + switchMap(active => active ? factory() : NEVER) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/request/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/request/index.ts new file mode 100644 index 000000000..c6f09a9f7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/request/index.ts @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, from } from "rxjs" +import { + filter, + map, + shareReplay, + switchMap +} from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Fetch the given URL + * + * @param url - Request URL + * @param options - Options + * + * @returns Response observable + */ +export function request( + url: URL | string, options: RequestInit = { credentials: "same-origin" } +): Observable { + return from(fetch(`${url}`, options)) + .pipe( + filter(res => res.status === 200), + ) +} + +/** + * Fetch JSON from the given URL + * + * @template T - Data type + * + * @param url - Request URL + * @param options - Options + * + * @returns Data observable + */ +export function requestJSON( + url: URL | string, options?: RequestInit +): Observable { + return request(url, options) + .pipe( + switchMap(res => res.json()), + shareReplay(1) + ) +} + +/** + * Fetch XML from the given URL + * + * @param url - Request URL + * @param options - Options + * + * @returns Data observable + */ +export function requestXML( + url: URL | string, options?: RequestInit +): Observable { + const dom = new DOMParser() + return request(url, options) + .pipe( + switchMap(res => res.text()), + map(res => dom.parseFromString(res, "text/xml")), + shareReplay(1) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/toggle/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/toggle/index.ts new file mode 100644 index 000000000..ae60577e0 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/toggle/index.ts @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent } from "rxjs" +import { map, startWith } from "rxjs/operators" + +import { getElementOrThrow } from "../element" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Toggle + */ +export type Toggle = + | "drawer" /* Toggle for drawer */ + | "search" /* Toggle for search */ + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Toggle map + */ +const toggles: Record = { + drawer: getElementOrThrow("[data-md-toggle=drawer]"), + search: getElementOrThrow("[data-md-toggle=search]") +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve the value of a toggle + * + * @param name - Toggle + * + * @returns Toggle value + */ +export function getToggle(name: Toggle): boolean { + return toggles[name].checked +} + +/** + * Set toggle + * + * Simulating a click event seems to be the most cross-browser compatible way + * of changing the value while also emitting a `change` event. Before, Material + * used `CustomEvent` to programmatically change the value of a toggle, but this + * is a much simpler and cleaner solution which doesn't require a polyfill. + * + * @param name - Toggle + * @param value - Toggle value + */ +export function setToggle(name: Toggle, value: boolean): void { + if (toggles[name].checked !== value) + toggles[name].click() +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch toggle + * + * @param name - Toggle + * + * @returns Toggle value observable + */ +export function watchToggle(name: Toggle): Observable { + const el = toggles[name] + return fromEvent(el, "change") + .pipe( + map(() => el.checked), + startWith(el.checked) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/_/index.ts new file mode 100644 index 000000000..827d9716e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/_/index.ts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, combineLatest } from "rxjs" +import { + distinctUntilKeyChanged, + map, + shareReplay +} from "rxjs/operators" + +import { Header } from "~/components" + +import { + ViewportOffset, + watchViewportOffset +} from "../offset" +import { + ViewportSize, + watchViewportSize +} from "../size" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Viewport + */ +export interface Viewport { + offset: ViewportOffset /* Viewport offset */ + size: ViewportSize /* Viewport size */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch at options + */ +interface WatchAtOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch viewport + * + * @returns Viewport observable + */ +export function watchViewport(): Observable { + return combineLatest([ + watchViewportOffset(), + watchViewportSize() + ]) + .pipe( + map(([offset, size]) => ({ offset, size })), + shareReplay(1) + ) +} + +/** + * Watch viewport relative to element + * + * @param el - Element + * @param options - Options + * + * @returns Viewport observable + */ +export function watchViewportAt( + el: HTMLElement, { viewport$, header$ }: WatchAtOptions +): Observable { + const size$ = viewport$ + .pipe( + distinctUntilKeyChanged("size") + ) + + /* Compute element offset */ + const offset$ = combineLatest([size$, header$]) + .pipe( + map((): ViewportOffset => ({ + x: el.offsetLeft, + y: el.offsetTop + })) + ) + + /* Compute relative viewport, return hot observable */ + return combineLatest([header$, viewport$, offset$]) + .pipe( + map(([{ height }, { offset, size }, { x, y }]) => ({ + offset: { + x: offset.x - x, + y: offset.y - y + height + }, + size + })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/index.ts new file mode 100644 index 000000000..d43797f78 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./offset" +export * from "./size" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/offset/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/offset/index.ts new file mode 100644 index 000000000..d9d81e82e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/offset/index.ts @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, merge } from "rxjs" +import { map, startWith } from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Viewport offset + */ +export interface ViewportOffset { + x: number /* Horizontal offset */ + y: number /* Vertical offset */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve viewport offset + * + * On iOS Safari, viewport offset can be negative due to overflow scrolling. + * As this may induce strange behaviors downstream, we'll just limit it to 0. + * + * @returns Viewport offset + */ +export function getViewportOffset(): ViewportOffset { + return { + x: Math.max(0, pageXOffset), + y: Math.max(0, pageYOffset) + } +} + +/** + * Set viewport offset + * + * @param offset - Viewport offset + */ +export function setViewportOffset( + { x, y }: Partial +): void { + window.scrollTo(x || 0, y || 0) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch viewport offset + * + * @returns Viewport offset observable + */ +export function watchViewportOffset(): Observable { + return merge( + fromEvent(window, "scroll", { passive: true }), + fromEvent(window, "resize", { passive: true }) + ) + .pipe( + map(getViewportOffset), + startWith(getViewportOffset()) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/size/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/size/index.ts new file mode 100644 index 000000000..6bdfaaf8b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/browser/viewport/size/index.ts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent } from "rxjs" +import { map, startWith } from "rxjs/operators" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Viewport size + */ +export interface ViewportSize { + width: number /* Viewport width */ + height: number /* Viewport height */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve viewport size + * + * @returns Viewport size + */ +export function getViewportSize(): ViewportSize { + return { + width: innerWidth, + height: innerHeight + } +} + +/* ------------------------------------------------------------------------- */ + +/** + * Watch viewport size + * + * @returns Viewport size observable + */ +export function watchViewportSize(): Observable { + return fromEvent(window, "resize", { passive: true }) + .pipe( + map(getViewportSize), + startWith(getViewportSize()) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/bundle.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/bundle.ts new file mode 100644 index 000000000..97f956cf1 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/bundle.ts @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import "focus-visible" +import { Subject, defer, merge } from "rxjs" +import { + delay, + filter, + map, + mergeWith, + shareReplay, + switchMap +} from "rxjs/operators" + +import { configuration, feature } from "./_" +import { + at, + getElement, + setToggle, + watchDocument, + watchKeyboard, + watchLocation, + watchLocationTarget, + watchMedia, + watchPrint, + watchViewport +} from "./browser" +import { + getComponentElement, + getComponentElements, + mountBackToTop, + mountContent, + mountDialog, + mountHeader, + mountHeaderTitle, + mountPalette, + mountSearch, + mountSidebar, + mountSource, + mountTableOfContents, + mountTabs, + watchHeader, + watchMain +} from "./components" +import { + setupClipboardJS, + setupInstantLoading, + setupVersionSelector +} from "./integrations" +import { + patchIndeterminate, + patchScrollfix, + patchScrolllock +} from "./patches" + +/* ---------------------------------------------------------------------------- + * Application + * ------------------------------------------------------------------------- */ + +/* Yay, JavaScript is available */ +document.documentElement.classList.remove("no-js") +document.documentElement.classList.add("js") + +/* Set up navigation observables and subjects */ +const document$ = watchDocument() +const location$ = watchLocation() +const target$ = watchLocationTarget() +const keyboard$ = watchKeyboard() + +/* Set up media observables */ +const viewport$ = watchViewport() +const tablet$ = watchMedia("(min-width: 960px)") +const screen$ = watchMedia("(min-width: 1220px)") +const print$ = watchPrint() + +const config = configuration() + +/* Set up Clipboard.js integration */ +const alert$ = new Subject() +setupClipboardJS({ alert$ }) + +/* Set up instant loading, if enabled */ +if (feature("navigation.instant")) + setupInstantLoading({ document$, location$, viewport$ }) + +/* Set up version selector */ +if (config.version?.provider === "mike") + setupVersionSelector() + +/* Always close drawer and search on navigation */ +merge(location$, target$) + .pipe( + delay(125) + ) + .subscribe(() => { + setToggle("drawer", false) + setToggle("search", false) + }) + +/* Set up global keyboard handlers */ +keyboard$ + .pipe( + filter(({ mode }) => mode === "global") + ) + .subscribe(key => { + switch (key.type) { + + /* Go to previous page */ + case "p": + case ",": + const prev = getElement("[href][rel=prev]") + if (typeof prev !== "undefined") + prev.click() + break + + /* Go to next page */ + case "n": + case ".": + const next = getElement("[href][rel=next]") + if (typeof next !== "undefined") + next.click() + break + } + }) + +/* Set up patches */ +patchIndeterminate({ document$, tablet$ }) +patchScrollfix({ document$ }) +patchScrolllock({ viewport$, tablet$ }) + +/* Set up header and main area observable */ +const header$ = watchHeader(getComponentElement("header"), { viewport$ }) +const main$ = document$ + .pipe( + map(() => getComponentElement("main")), + switchMap(el => watchMain(el, { viewport$, header$ })), + shareReplay(1) + ) + +/* Set up control component observables */ +const control$ = merge( + + /* Dialog */ + ...getComponentElements("dialog") + .map(el => mountDialog(el, { alert$ })), + + /* Header */ + ...getComponentElements("header") + .map(el => mountHeader(el, { viewport$, header$, main$ })), + + /* Color palette */ + ...getComponentElements("palette") + .map(el => mountPalette(el)), + + /* Search */ + ...getComponentElements("search") + .map(el => mountSearch(el, { keyboard$ })), + + /* Repository information */ + ...getComponentElements("source") + .map(el => mountSource(el)) +) + +/* Set up content component observables */ +const content$ = defer(() => merge( + + /* Content */ + ...getComponentElements("content") + .map(el => mountContent(el, { target$, viewport$, print$ })), + + /* Header title */ + ...getComponentElements("header-title") + .map(el => mountHeaderTitle(el, { viewport$, header$ })), + + /* Sidebar */ + ...getComponentElements("sidebar") + .map(el => el.getAttribute("data-md-type") === "navigation" + ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ })) + : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ })) + ), + + /* Navigation tabs */ + ...getComponentElements("tabs") + .map(el => mountTabs(el, { viewport$, header$ })), + + /* Table of contents */ + ...getComponentElements("toc") + .map(el => mountTableOfContents(el, { viewport$, header$ })), + + /* Back-to-top button */ + ...getComponentElements("top") + .map(el => mountBackToTop(el, { viewport$, main$ })) +)) + +/* Set up component observables */ +const component$ = document$ + .pipe( + switchMap(() => content$), + mergeWith(control$), + shareReplay(1) + ) + +/* Subscribe to all components */ +component$.subscribe() + +/* ---------------------------------------------------------------------------- + * Exports + * ------------------------------------------------------------------------- */ + +window.document$ = document$ /* Document observable */ +window.location$ = location$ /* Location subject */ +window.target$ = target$ /* Location target observable */ +window.keyboard$ = keyboard$ /* Keyboard observable */ +window.viewport$ = viewport$ /* Viewport observable */ +window.tablet$ = tablet$ /* Tablet observable */ +window.screen$ = screen$ /* Screen observable */ +window.print$ = print$ /* Print mode observable */ +window.alert$ = alert$ /* Alert subject */ +window.component$ = component$ /* Component observable */ diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/_/index.ts new file mode 100644 index 000000000..1626e7189 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/_/index.ts @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { getElementOrThrow, getElements } from "~/browser" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Component + */ +export type ComponentType = + | "announce" /* Announcement bar */ + | "container" /* Container */ + | "content" /* Content */ + | "dialog" /* Dialog */ + | "header" /* Header */ + | "header-title" /* Header title */ + | "header-topic" /* Header topic */ + | "main" /* Main area */ + | "palette" /* Color palette */ + | "search" /* Search */ + | "search-query" /* Search input */ + | "search-result" /* Search results */ + | "sidebar" /* Sidebar */ + | "skip" /* Skip link */ + | "source" /* Repository information */ + | "tabs" /* Navigation tabs */ + | "toc" /* Table of contents */ + | "top" /* Back-to-top button */ + +/** + * A component + * + * @template T - Component type + * @template U - Reference type + */ +export type Component< + T extends {} = {}, + U extends HTMLElement = HTMLElement +> = + T & { + ref: U /* Component reference */ + } + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Component type map + */ +interface ComponentTypeMap { + "announce": HTMLElement /* Announcement bar */ + "container": HTMLElement /* Container */ + "content": HTMLElement /* Content */ + "dialog": HTMLElement /* Dialog */ + "header": HTMLElement /* Header */ + "header-title": HTMLElement /* Header title */ + "header-topic": HTMLElement /* Header topic */ + "main": HTMLElement /* Main area */ + "palette": HTMLElement /* Color palette */ + "search": HTMLElement /* Search */ + "search-query": HTMLInputElement /* Search input */ + "search-result": HTMLElement /* Search results */ + "sidebar": HTMLElement /* Sidebar */ + "skip": HTMLAnchorElement /* Skip link */ + "source": HTMLAnchorElement /* Repository information */ + "tabs": HTMLElement /* Navigation tabs */ + "toc": HTMLElement /* Table of contents */ + "top": HTMLAnchorElement /* Back-to-top button */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Retrieve the element for a given component or throw a reference error + * + * @template T - Component type + * + * @param type - Component type + * @param node - Node of reference + * + * @returns Element + */ +export function getComponentElement( + type: T, node: ParentNode = document +): ComponentTypeMap[T] { + return getElementOrThrow(`[data-md-component=${type}]`, node) +} + +/** + * Retrieve all elements for a given component + * + * @template T - Component type + * + * @param type - Component type + * @param node - Node of reference + * + * @returns Elements + */ +export function getComponentElements( + type: T, node: ParentNode = document +): ComponentTypeMap[T][] { + return getElements(`[data-md-component=${type}]`, node) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/_/index.ts new file mode 100644 index 000000000..ddd1504aa --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/_/index.ts @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, merge } from "rxjs" + +import { Viewport, getElements } from "~/browser" + +import { Component } from "../../_" +import { CodeBlock, mountCodeBlock } from "../code" +import { Details, mountDetails } from "../details" +import { DataTable, mountDataTable } from "../table" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Content + */ +export type Content = + | CodeBlock + | DataTable + | Details + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Mount options + */ +interface MountOptions { + target$: Observable /* Location target observable */ + viewport$: Observable /* Viewport observable */ + print$: Observable /* Print mode observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Mount content + * + * This function mounts all components that are found in the content of the + * actual article, including code blocks, data tables and details. + * + * @param el - Content element + * @param options - Options + * + * @returns Content component observable + */ +export function mountContent( + el: HTMLElement, { target$, viewport$, print$ }: MountOptions +): Observable> { + return merge( + + /* Code blocks */ + ...getElements("pre > code", el) + .map(child => mountCodeBlock(child, { viewport$ })), + + /* Data tables */ + ...getElements("table:not([class])", el) + .map(child => mountDataTable(child)), + + /* Details */ + ...getElements("details", el) + .map(child => mountDetails(child, { target$, print$ })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/code/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/code/index.ts new file mode 100644 index 000000000..3549b775f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/code/index.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import ClipboardJS from "clipboard" +import { + NEVER, + Observable, + Subject, + fromEvent, + merge, + of +} from "rxjs" +import { + distinctUntilKeyChanged, + finalize, + map, + switchMap, + tap, + withLatestFrom +} from "rxjs/operators" + +import { resetFocusable, setFocusable } from "~/actions" +import { + Viewport, + getElementContentSize, + getElementSize, + getElements, + watchMedia +} from "~/browser" +import { renderClipboardButton } from "~/templates" + +import { Component } from "../../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Code block + */ +export interface CodeBlock { + scroll: boolean /* Code block overflows */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ +} + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Global index for Clipboard.js integration + */ +let index = 0 + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch code block + * + * This function monitors size changes of the viewport, as well as switches of + * content tabs with embedded code blocks, as both may trigger overflow. + * + * @param el - Code block element + * @param options - Options + * + * @returns Code block observable + */ +export function watchCodeBlock( + el: HTMLElement, { viewport$ }: WatchOptions +): Observable { + const container$ = of(el) + .pipe( + switchMap(child => { + const container = child.closest("[data-tabs]") + if (container instanceof HTMLElement) { + return merge( + ...getElements("input", container) + .map(input => fromEvent(input, "change")) + ) + } + return NEVER + }) + ) + + /* Check overflow on resize and tab change */ + return merge( + viewport$.pipe(distinctUntilKeyChanged("size")), + container$ + ) + .pipe( + map(() => { + const visible = getElementSize(el) + const content = getElementContentSize(el) + return { + scroll: content.width > visible.width + } + }), + distinctUntilKeyChanged("scroll") + ) +} + +/** + * Mount code block + * + * This function ensures that an overflowing code block is focusable through + * keyboard, so it can be scrolled without a mouse to improve on accessibility. + * + * @param el - Code block element + * @param options - Options + * + * @returns Code block component observable + */ +export function mountCodeBlock( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + withLatestFrom(watchMedia("(hover)")) + ) + .subscribe(([{ scroll }, hover]) => { + if (scroll && hover) + setFocusable(el) + else + resetFocusable(el) + }) + + /* Render button for Clipboard.js integration */ + if (ClipboardJS.isSupported()) { + const parent = el.closest("pre")! + parent.id = `__code_${index++}` + parent.insertBefore( + renderClipboardButton(parent.id), + el + ) + } + + /* Create and return component */ + return watchCodeBlock(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/details/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/details/index.ts new file mode 100644 index 000000000..40b04a185 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/details/index.ts @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, Subject } from "rxjs" +import { + filter, + finalize, + map, + mapTo, + mergeWith, + tap +} from "rxjs/operators" + +import { Component } from "../../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Details + */ +export interface Details {} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + target$: Observable /* Location target observable */ + print$: Observable /* Print mode observable */ +} + +/** + * Mount options + */ +interface MountOptions { + target$: Observable /* Location target observable */ + print$: Observable /* Print mode observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch details + * + * @param el - Details element + * @param options - Options + * + * @returns Details observable + */ +export function watchDetails( + el: HTMLDetailsElement, { target$, print$ }: WatchOptions +): Observable
{ + return target$ + .pipe( + map(target => target.closest("details:not([open])")!), + filter(details => el === details), + mergeWith(print$), + mapTo(el) + ) +} + +/** + * Mount details + * + * This function ensures that `details` tags are opened on anchor jumps and + * prior to printing, so the whole content of the page is visible. + * + * @param el - Details element + * @param options - Options + * + * @returns Details component observable + */ +export function mountDetails( + el: HTMLDetailsElement, options: MountOptions +): Observable> { + const internal$ = new Subject
() + internal$.subscribe(() => { + el.setAttribute("open", "") + el.scrollIntoView() + }) + + /* Create and return component */ + return watchDetails(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + mapTo({ ref: el }) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/index.ts new file mode 100644 index 000000000..5d1f2f32c --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./code" +export * from "./details" +export * from "./table" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/table/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/table/index.ts new file mode 100644 index 000000000..6b0f53d35 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/content/table/index.ts @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, of } from "rxjs" + +import { createElement, replaceElement } from "~/browser" +import { renderTable } from "~/templates" + +import { Component } from "../../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Data table + */ +export interface DataTable {} + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Sentinel for replacement + */ +const sentinel = createElement("table") + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Mount data table + * + * This function wraps a data table in another scrollable container, so it can + * be smoothly scrolled on smaller screen sizes and won't break the layout. + * + * @param el - Data table element + * + * @returns Data table component observable + */ +export function mountDataTable( + el: HTMLElement +): Observable> { + replaceElement(el, sentinel) + replaceElement(sentinel, renderTable(el)) + + /* Create and return component */ + return of({ ref: el }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/dialog/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/dialog/index.ts new file mode 100644 index 000000000..f4475bcaf --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/dialog/index.ts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + animationFrameScheduler, + merge, + of +} from "rxjs" +import { + delay, + finalize, + map, + observeOn, + switchMap, + tap +} from "rxjs/operators" + +import { + resetDialogState, + setDialogMessage, + setDialogState +} from "~/actions" + +import { Component } from "../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Dialog + */ +export interface Dialog { + message: string /* Dialog message */ + open: boolean /* Dialog is visible */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + alert$: Subject /* Alert subject */ +} + +/** + * Mount options + */ +interface MountOptions { + alert$: Subject /* Alert subject */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch dialog + * + * @param _el - Dialog element + * @param options - Options + * + * @returns Dialog observable + */ +export function watchDialog( + _el: HTMLElement, { alert$ }: WatchOptions +): Observable { + return alert$ + .pipe( + switchMap(message => merge( + of(true), + of(false).pipe(delay(2000)) + ) + .pipe( + map(open => ({ message, open })) + ) + ) + ) +} + +/** + * Mount dialog + * + * This function reveals the dialog in the right cornerwhen a new alert is + * emitted through the subject that is passed as part of the options. + * + * @param el - Dialog element + * @param options - Options + * + * @returns Dialog component observable + */ +export function mountDialog( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler) + ) + .subscribe(({ message, open }) => { + setDialogMessage(el, message) + if (open) + setDialogState(el, "open") + else + resetDialogState(el) + }) + + /* Create and return component */ + return watchDialog(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/_/index.ts new file mode 100644 index 000000000..4a5275ba5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/_/index.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + animationFrameScheduler, + combineLatest, + defer, + of +} from "rxjs" +import { + bufferCount, + combineLatestWith, + distinctUntilChanged, + distinctUntilKeyChanged, + filter, + map, + observeOn, + shareReplay, + startWith, + switchMap +} from "rxjs/operators" + +import { feature } from "~/_" +import { resetHeaderState, setHeaderState } from "~/actions" +import { + Viewport, + watchElementSize, + watchToggle +} from "~/browser" + +import { Component } from "../../_" +import { Main } from "../../main" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Header + */ +export interface Header { + height: number /* Header visible height */ + sticky: boolean /* Header stickyness */ + hidden: boolean /* User scrolled past threshold */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ + main$: Observable
/* Main area observable */ +} + +/* ---------------------------------------------------------------------------- + * Helper functions + * ------------------------------------------------------------------------- */ + +/** + * Compute whether the header is hidden + * + * If the user scrolls past a certain threshold, the header can be hidden when + * scrolling down, and shown when scrolling up. + * + * @param options - Options + * + * @returns Toggle observable + */ +function isHidden({ viewport$ }: WatchOptions): Observable { + if (!feature("header.autohide")) + return of(false) + + /* Compute direction and turning point */ + const direction$ = viewport$ + .pipe( + map(({ offset: { y } }) => y), + bufferCount(2, 1), + map(([a, b]) => [a < b, b] as const), + distinctUntilKeyChanged(0) + ) + + /* Compute whether header should be hidden */ + const hidden$ = combineLatest([viewport$, direction$]) + .pipe( + filter(([{ offset }, [, y]]) => Math.abs(y - offset.y) > 100), + map(([, [direction]]) => direction), + distinctUntilChanged() + ) + + /* Compute threshold for hiding */ + const search$ = watchToggle("search") + return combineLatest([viewport$, search$]) + .pipe( + map(([{ offset }, search]) => offset.y > 400 && !search), + distinctUntilChanged(), + switchMap(active => active ? hidden$ : of(false)), + startWith(false) + ) +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch header + * + * @param el - Header element + * @param options - Options + * + * @returns Header observable + */ +export function watchHeader( + el: HTMLElement, options: WatchOptions +): Observable
{ + return defer(() => { + const styles = getComputedStyle(el) + return of( + styles.position === "sticky" || + styles.position === "-webkit-sticky" + ) + }) + .pipe( + combineLatestWith(watchElementSize(el), isHidden(options)), + map(([sticky, { height }, hidden]) => ({ + height: sticky ? height : 0, + sticky, + hidden + })), + distinctUntilChanged((a, b) => ( + a.sticky === b.sticky && + a.height === b.height && + a.hidden === b.hidden + )), + shareReplay(1) + ) +} + +/** + * Mount header + * + * This function manages the different states of the header, i.e. whether it's + * hidden or rendered with a shadow. This depends heavily on the main area. + * + * @param el - Header element + * @param options - Options + * + * @returns Header component observable + */ +export function mountHeader( + el: HTMLElement, { header$, main$ }: MountOptions +): Observable> { + const internal$ = new Subject
() + internal$ + .pipe( + distinctUntilKeyChanged("active"), + combineLatestWith(header$), + observeOn(animationFrameScheduler) + ) + .subscribe(([{ active }, { hidden }]) => { + if (active) + setHeaderState(el, hidden ? "hidden" : "shadow") + else + resetHeaderState(el) + }) + + /* Connect to long-living subject and return component */ + main$.subscribe(main => internal$.next(main)) + return header$ + .pipe( + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/index.ts new file mode 100644 index 000000000..e98ef06a7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./title" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/title/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/title/index.ts new file mode 100644 index 000000000..27dc47fc6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/header/title/index.ts @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + NEVER, + Observable, + Subject, + animationFrameScheduler +} from "rxjs" +import { + distinctUntilKeyChanged, + finalize, + map, + observeOn, + tap +} from "rxjs/operators" + +import { + resetHeaderTitleState, + setHeaderTitleState +} from "~/actions" +import { + Viewport, + getElement, + getElementSize, + watchViewportAt +} from "~/browser" + +import { Component } from "../../_" +import { Header } from "../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Header + */ +export interface HeaderTitle { + active: boolean /* User scrolled past first headline */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch header title + * + * @param el - Heading element + * @param options - Options + * + * @returns Header title observable + */ +export function watchHeaderTitle( + el: HTMLHeadingElement, { viewport$, header$ }: WatchOptions +): Observable { + return watchViewportAt(el, { header$, viewport$ }) + .pipe( + map(({ offset: { y } }) => { + const { height } = getElementSize(el) + return { + active: y >= height + } + }), + distinctUntilKeyChanged("active") + ) +} + +/** + * Mount header title + * + * This function swaps the header title from the site title to the title of the + * current page when the user scrolls past the first headline. + * + * @param el - Header title element + * @param options - Options + * + * @returns Header title component observable + */ +export function mountHeaderTitle( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler) + ) + .subscribe(({ active }) => { + if (active) + setHeaderTitleState(el, "active") + else + resetHeaderTitleState(el) + }) + + /* Obtain headline, if any */ + /* sphinx-material: treat first object description as title if there is no h1 */ + const headline = getElement("article h1, .objdesc > dt .descname") + if (typeof headline === "undefined") + return NEVER + + /* Create and return component */ + return watchHeaderTitle(headline, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/index.ts new file mode 100644 index 000000000..7f42bc38a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/index.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./content" +export * from "./dialog" +export * from "./header" +export * from "./main" +export * from "./palette" +export * from "./search" +export * from "./sidebar" +export * from "./source" +export * from "./tabs" +export * from "./toc" +export * from "./top" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/main/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/main/index.ts new file mode 100644 index 000000000..aac74e07b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/main/index.ts @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + combineLatest +} from "rxjs" +import { + distinctUntilChanged, + distinctUntilKeyChanged, + map, + switchMap +} from "rxjs/operators" + +import { Viewport, watchElementSize } from "~/browser" + +import { Header } from "../header" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Main area + */ +export interface Main { + offset: number /* Main area top offset */ + height: number /* Main area visible height */ + active: boolean /* User scrolled past header */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch main area + * + * This function returns an observable that computes the visual parameters of + * the main area which depends on the viewport vertical offset and height, as + * well as the height of the header element, if the header is fixed. + * + * @param el - Main area element + * @param options - Options + * + * @returns Main area observable + */ +export function watchMain( + el: HTMLElement, { viewport$, header$ }: WatchOptions +): Observable
{ + + /* Compute necessary adjustment for header */ + const adjust$ = header$ + .pipe( + map(({ height }) => height), + distinctUntilChanged() + ) + + /* Compute the main area's top and bottom borders */ + const border$ = adjust$ + .pipe( + switchMap(() => watchElementSize(el) + .pipe( + map(({ height }) => ({ + top: el.offsetTop, + bottom: el.offsetTop + height + })), + distinctUntilKeyChanged("bottom") + ) + ) + ) + + /* Compute the main area's offset, visible height and if we scrolled past */ + return combineLatest([adjust$, border$, viewport$]) + .pipe( + map(([header, { top, bottom }, { offset: { y }, size: { height } }]) => { + height = Math.max(0, height + - Math.max(0, top - y, header) + - Math.max(0, height + y - bottom) + ) + return { + offset: top - header, + height, + active: top - header <= y + } + }), + distinctUntilChanged((a, b) => ( + a.offset === b.offset && + a.height === b.height && + a.active === b.active + )) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/palette/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/palette/index.ts new file mode 100644 index 000000000..e6df4a458 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/palette/index.ts @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + fromEvent, + of +} from "rxjs" +import { + finalize, + map, + mapTo, + mergeMap, + shareReplay, + startWith, + tap +} from "rxjs/operators" + +import { getElements } from "~/browser" + +import { Component } from "../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Palette colors + */ +export interface PaletteColor { + scheme?: string /* Color scheme */ + primary?: string /* Primary color */ + accent?: string /* Accent color */ +} + +/** + * Palette + */ +export interface Palette { + index: number /* Palette index */ + color: PaletteColor /* Palette colors */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch color palette + * + * @param inputs - Color palette element + * + * @returns Color palette observable + */ +export function watchPalette( + inputs: HTMLInputElement[] +): Observable { + const data = localStorage.getItem(__prefix("__palette"))! + const current = JSON.parse(data) || { + index: inputs.findIndex(input => ( + matchMedia(input.getAttribute("data-md-color-media")!).matches + )) + } + + /* Emit changes in color palette */ + const palette$ = of(...inputs) + .pipe( + mergeMap(input => fromEvent(input, "change") + .pipe( + mapTo(input) + ) + ), + startWith(inputs[Math.max(0, current.index)]), + map(input => ({ + index: inputs.indexOf(input), + color: { + scheme: input.getAttribute("data-md-color-scheme"), + primary: input.getAttribute("data-md-color-primary"), + accent: input.getAttribute("data-md-color-accent") + } + } as Palette)), + shareReplay(1) + ) + + /* Persist preference in local storage */ + palette$.subscribe(palette => { + localStorage.setItem(__prefix("__palette"), JSON.stringify(palette)) + }) + + /* Return palette */ + return palette$ +} + +/** + * Mount color palette + * + * @param el - Color palette element + * + * @returns Color palette component observable + */ +export function mountPalette( + el: HTMLElement +): Observable> { + const internal$ = new Subject() + + /* Set color palette */ + internal$.subscribe(palette => { + for (const [key, value] of Object.entries(palette.color)) + if (typeof value === "string") + document.body.setAttribute(`data-md-color-${key}`, value) + + /* Toggle visibility */ + for (let index = 0; index < inputs.length; index++) { + const label = inputs[index].nextElementSibling + if (label instanceof HTMLElement) + label.hidden = palette.index !== index + } + }) + + /* Create and return component */ + const inputs = getElements("input", el) + return watchPalette(inputs) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/_/index.ts new file mode 100644 index 000000000..0f2785a57 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/_/index.ts @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { NEVER, Observable, merge } from "rxjs" +import { filter } from "rxjs/operators" + +import { + Keyboard, + getActiveElement, + getElements, + setElementFocus, + setElementSelection, + setToggle +} from "~/browser" + +import { Component, getComponentElement } from "../../_" +import { SearchQuery, mountSearchQuery } from "../query" +import { SearchResult, mountSearchResult } from "../result" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search + */ +export type Search = + | SearchQuery + | SearchResult + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Mount options + */ +interface MountOptions { + keyboard$: Observable /* Keyboard observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Mount search + * + * This function sets up the search functionality, including the underlying + * web worker and all keyboard bindings. + * + * @param el - Search element + * @param options - Options + * + * @returns Search component observable + */ +export function mountSearch( + el: HTMLElement, { keyboard$ }: MountOptions +): Observable> { + + /* Retrieve nested components */ + const query = getComponentElement("search-query", el) + const result = getComponentElement("search-result", el) + + /* Set up search keyboard handlers */ + keyboard$ + .pipe( + filter(({ mode }) => mode === "search") + ) + .subscribe(key => { + const active = getActiveElement() + switch (key.type) { + + /* Enter: prevent form submission */ + case "Enter": + if (active === query) + key.claim() + break + + /* Escape or Tab: close search */ + case "Escape": + case "Tab": + setToggle("search", false) + setElementFocus(query, false) + break + + /* Vertical arrows: select previous or next search result */ + case "ArrowUp": + case "ArrowDown": + if (typeof active === "undefined") { + setElementFocus(query) + } else { + const els = [query, ...getElements( + ":not(details) > [href], summary, details[open] [href]", + result + )] + const i = Math.max(0, ( + Math.max(0, els.indexOf(active)) + els.length + ( + key.type === "ArrowUp" ? -1 : +1 + ) + ) % els.length) + setElementFocus(els[i]) + } + + /* Prevent scrolling of page */ + key.claim() + break + + /* All other keys: hand to search query */ + default: + if (query !== getActiveElement()) + setElementFocus(query) + } + }) + + /* Set up global keyboard handlers */ + keyboard$ + .pipe( + filter(({ mode }) => mode === "global"), + ) + .subscribe(key => { + switch (key.type) { + + /* Open search and select query */ + case "f": + case "s": + case "/": + setElementFocus(query) + setElementSelection(query) + key.claim() + break + } + }) + + /* Create and return component */ + const query$ = mountSearchQuery(query) + return merge( + query$, + mountSearchResult(result, { query$ }) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/index.ts new file mode 100644 index 000000000..ad7fe77b7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./query" +export * from "./result" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/query/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/query/index.ts new file mode 100644 index 000000000..d62396c23 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/query/index.ts @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + combineLatest, + fromEvent, + merge +} from "rxjs" +import { + delay, + distinctUntilChanged, + distinctUntilKeyChanged, + finalize, + map, + takeLast, + takeUntil, + tap +} from "rxjs/operators" + +import { + resetSearchQueryPlaceholder, + setSearchQueryPlaceholder +} from "~/actions" +import { + setElementFocus, + setToggle, + watchElementFocus +} from "~/browser" + +import { Component } from "../../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search query + */ +export interface SearchQuery { + value: string /* Query value */ + focus: boolean /* Query focus */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch search query + * + * Note that the focus event which triggers re-reading the current query value + * is delayed by `1ms` so the input's empty state is allowed to propagate. + * + * @param el - Search query element + * + * @returns Search query observable + */ +export function watchSearchQuery( + el: HTMLInputElement +): Observable { + /* Intercept focus and input events */ + const focus$ = watchElementFocus(el) + const value$ = merge( + fromEvent(el, "keyup"), + fromEvent(el, "focus").pipe(delay(1)) + ) + .pipe( + map(() => el.value.trim()), + distinctUntilChanged() + ) + + /* Combine into single observable */ + return combineLatest([value$, focus$]) + .pipe( + map(([value, focus]) => ({ value, focus })) + ) +} + +/** + * Mount search query + * + * @param el - Search query element + * + * @returns Search query component observable + */ +export function mountSearchQuery( + el: HTMLInputElement, +): Observable> { + const internal$ = new Subject() + + /* Handle focus changes */ + internal$ + .pipe( + distinctUntilKeyChanged("focus") + ) + .subscribe(({ focus }) => { + if (focus) { + setToggle("search", focus) + setSearchQueryPlaceholder(el, "") + } else { + resetSearchQueryPlaceholder(el) + } + }) + + /* Handle reset */ + fromEvent(el.form!, "reset") + .pipe( + takeUntil(internal$.pipe(takeLast(1))) + ) + .subscribe(() => setElementFocus(el)) + + /* Create and return component */ + return watchSearchQuery(el) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/result/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/result/index.ts new file mode 100644 index 000000000..4482c2151 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/search/result/index.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + animationFrameScheduler, + interval, + of +} from "rxjs" +import { + concatMap, + debounce, + distinctUntilKeyChanged, observeOn +} from "rxjs/operators" + +import { + addToSearchResultList, + resetSearchResultList, + resetSearchResultMeta, + setSearchResultMeta +} from "~/actions" +import { + getElementOrThrow +} from "~/browser" +import { + SearchResult as SearchResultData +} from "~/integrations" +import { SearchResultStream, getResults } from "~/sphinx_search" +import { renderSearchResult } from "~/templates" + +import { Component } from "../../_" +import { SearchQuery } from "../query" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search result + */ +export interface SearchResult { + data: SearchResultData[] /* Search result data */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Mount options + */ +interface MountOptions { + query$: Observable /* Search query observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Mount search result list + * + * This function performs a lazy rendering of the search results, depending on + * the vertical offset of the search result container. + * + * @param el - Search result list element + * @param options - Options + * + * @returns Search result list component observable + */ +export function mountSearchResult( + el: HTMLElement, { query$ }: MountOptions +): Observable> { + /* Update search result metadata */ + const meta = getElementOrThrow(":scope > :first-child", el) + + /* Update search result list */ + const list = getElementOrThrow(":scope > :last-child", el) + + let lastResults: SearchResultStream|undefined + let blocked: (() => void)|undefined + + const scrollContainer = el.parentElement! + const threshold = 16 + const atScrollLimit = () => + scrollContainer.scrollTop + scrollContainer.clientHeight + threshold > + scrollContainer.scrollHeight + const checkScrollLimit = () => { + if (blocked === undefined) return + if (atScrollLimit()) { + blocked() + blocked = undefined + } + } + scrollContainer.addEventListener("scroll", checkScrollLimit, {passive: true}) + window.addEventListener("resize", checkScrollLimit, {passive: true}) + const startAddingResults = async (results: SearchResultStream) => { + lastResults = results + const blockSize = 4 + let limit = blockSize + for (let i = 0; i < results.count; ++i) { + if (i === limit) { + if (!atScrollLimit()) { + await new Promise(resolve => { + blocked = resolve + }) + } + limit += blockSize + } + if (lastResults !== results) { + // Cancelled. + return + } + const result = await results.get(i) + if (lastResults !== results) { + // Cancelled. + return + } + addToSearchResultList(list, renderSearchResult(result)) + } + } + query$ + .pipe( + distinctUntilKeyChanged("value"), debounce(() => interval(250)), + concatMap(async query => { + if (!query.value) return undefined + return getResults(query.value) + }), + observeOn(animationFrameScheduler)) + .subscribe(results => { + resetSearchResultList(list) + if (results) { + setSearchResultMeta(meta, results.count) + void startAddingResults(results) + } else { + resetSearchResultMeta(meta) + } + }) + return of() +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/sidebar/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/sidebar/index.ts new file mode 100644 index 000000000..1839b6bcb --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/sidebar/index.ts @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + animationFrameScheduler, + combineLatest +} from "rxjs" +import { + distinctUntilChanged, + finalize, + map, + observeOn, + tap, + withLatestFrom +} from "rxjs/operators" + +import { + resetSidebarHeight, + resetSidebarOffset, + setSidebarHeight, + setSidebarOffset +} from "~/actions" +import { Viewport } from "~/browser" + +import { Component } from "../_" +import { Header } from "../header" +import { Main } from "../main" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Sidebar + */ +export interface Sidebar { + height: number /* Sidebar height */ + locked: boolean /* User scrolled past header */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + main$: Observable
/* Main area observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ + main$: Observable
/* Main area observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch sidebar + * + * This function returns an observable that computes the visual parameters of + * the sidebar which depends on the vertical viewport offset, as well as the + * height of the main area. When the page is scrolled beyond the header, the + * sidebar is locked and fills the remaining space. + * + * @param el - Sidebar element + * @param options - Options + * + * @returns Sidebar observable + */ +export function watchSidebar( + el: HTMLElement, { viewport$, main$ }: WatchOptions +): Observable { + const adjust = + el.parentElement!.offsetTop - + el.parentElement!.parentElement!.offsetTop + + /* Compute the sidebar's available height and if it should be locked */ + return combineLatest([main$, viewport$]) + .pipe( + map(([{ offset, height }, { offset: { y } }]) => { + height = height + + Math.min(adjust, Math.max(0, y - offset)) + - adjust + return { + height, + locked: y >= offset + adjust + } + }), + distinctUntilChanged((a, b) => ( + a.height === b.height && + a.locked === b.locked + )) + ) +} + +/** + * Mount sidebar + * + * @param el - Sidebar element + * @param options - Options + * + * @returns Sidebar component observable + */ +export function mountSidebar( + el: HTMLElement, { header$, ...options }: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler), + withLatestFrom(header$) + ) + .subscribe({ + + /* Update height and offset */ + next([{ height }, { height: offset }]) { + setSidebarHeight(el, height) + setSidebarOffset(el, offset) + }, + + /* Reset on complete */ + complete() { + resetSidebarOffset(el) + resetSidebarHeight(el) + } + }) + + /* Create and return component */ + return watchSidebar(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/_/index.ts new file mode 100644 index 000000000..a8f70834f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/_/index.ts @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { NEVER, Observable, Subject, defer, of } from "rxjs" +import { + catchError, + filter, + finalize, + map, + shareReplay, + tap +} from "rxjs/operators" + +import { setSourceFacts, setSourceState } from "~/actions" +import { renderSourceFacts } from "~/templates" + +import { Component } from "../../_" +import { SourceFacts, fetchSourceFacts } from "../facts" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Repository information + */ +export interface Source { + facts: SourceFacts /* Repository facts */ +} + +/* ---------------------------------------------------------------------------- + * Data + * ------------------------------------------------------------------------- */ + +/** + * Repository information observable + */ +let fetch$: Observable + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch repository information + * + * This function tries to read the repository facts from session storage, and + * if unsuccessful, fetches them from the underlying provider. + * + * @param el - Repository information element + * + * @returns Repository information observable + */ +export function watchSource( + el: HTMLAnchorElement +): Observable { + return fetch$ ||= defer(() => { + const data = sessionStorage.getItem(__prefix("__source")) + if (data) { + return of(JSON.parse(data)) + } else { + const value$ = fetchSourceFacts(el.href) + value$.subscribe(value => { + try { + sessionStorage.setItem(__prefix("__source"), JSON.stringify(value)) + } catch (err) { + /* Uncritical, just swallow */ + } + }) + + /* Return value */ + return value$ + } + }) + .pipe( + catchError(() => NEVER), + filter(facts => Object.keys(facts).length > 0), + map(facts => ({ facts })), + shareReplay(1) + ) +} + +/** + * Mount repository information + * + * @param el - Repository information element + * + * @returns Repository information component observable + */ +export function mountSource( + el: HTMLAnchorElement +): Observable> { + const internal$ = new Subject() + internal$.subscribe(({ facts }) => { + setSourceFacts(el, renderSourceFacts(facts)) + setSourceState(el, "done") + }) + + /* Create and return component */ + return watchSource(el) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/_/index.ts new file mode 100644 index 000000000..10ca37ba5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/_/index.ts @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { NEVER, Observable } from "rxjs" + +import { fetchSourceFactsFromGitHub } from "../github" +import { fetchSourceFactsFromGitLab } from "../gitlab" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Repository facts for repositories + */ +export interface RepositoryFacts { + stars?: number /* Number of stars */ + forks?: number /* Number of forks */ + version?: string /* Latest version */ +} + +/** + * Repository facts for organizations + */ +export interface OrganizationFacts { + repositories?: number /* Number of repositories */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Repository facts + */ +export type SourceFacts = + | RepositoryFacts + | OrganizationFacts + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Fetch repository facts + * + * @param url - Repository URL + * + * @returns Repository facts observable + */ +export function fetchSourceFacts( + url: string +): Observable { + const [type] = url.match(/(git(?:hub|lab))/i) || [] + switch (type.toLowerCase()) { + + /* GitHub repository */ + case "github": + const [, user, repo] = url.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i)! + return fetchSourceFactsFromGitHub(user, repo) + + /* GitLab repository */ + case "gitlab": + const [, base, slug] = url.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i)! + return fetchSourceFactsFromGitLab(base, slug) + + /* Everything else */ + default: + return NEVER + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/github/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/github/index.ts new file mode 100644 index 000000000..cdf325f4c --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/github/index.ts @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Repo, User } from "github-types" +import { Observable, zip } from "rxjs" +import { defaultIfEmpty, map } from "rxjs/operators" + +import { requestJSON } from "~/browser" + +import { SourceFacts } from "../_" + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * GitHub release (partial) + */ +interface Release { + tag_name: string /* Tag name */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Fetch GitHub repository facts + * + * @param user - GitHub user + * @param repo - GitHub repository + * + * @returns Repository facts observable + */ +export function fetchSourceFactsFromGitHub( + user: string, repo?: string +): Observable { + if (typeof repo !== "undefined") { + const url = `https://api.github.com/repos/${user}/${repo}` + return zip( + + /* Fetch version */ + requestJSON(`${url}/releases/latest`) + .pipe( + map(release => ({ + version: release.tag_name + })), + defaultIfEmpty({}) + ), + + /* Fetch stars and forks */ + requestJSON(url) + .pipe( + map(info => ({ + stars: info.stargazers_count, + forks: info.forks_count + })), + defaultIfEmpty({}) + ) + ) + .pipe( + map(([release, info]) => ({ ...release, ...info })) + ) + + /* User or organization */ + } else { + const url = `https://api.github.com/repos/${user}` + return requestJSON(url) + .pipe( + map(info => ({ + repositories: info.public_repos + })), + defaultIfEmpty({}) + ) + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/gitlab/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/gitlab/index.ts new file mode 100644 index 000000000..0d80e2fa5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/gitlab/index.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { ProjectSchema } from "gitlab" +import { Observable } from "rxjs" +import { defaultIfEmpty, map } from "rxjs/operators" + +import { requestJSON } from "~/browser" + +import { SourceFacts } from "../_" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Fetch GitLab repository facts + * + * @param base - GitLab base + * @param project - GitLab project + * + * @returns Repository facts observable + */ +export function fetchSourceFactsFromGitLab( + base: string, project: string +): Observable { + const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}` + return requestJSON(url) + .pipe( + map(({ star_count, forks_count }) => ({ + stars: star_count, + forks: forks_count + })), + defaultIfEmpty({}) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/index.ts new file mode 100644 index 000000000..9d88a9c62 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/facts/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./github" +export * from "./gitlab" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/index.ts new file mode 100644 index 000000000..64468ace1 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/source/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./facts" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/tabs/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/tabs/index.ts new file mode 100644 index 000000000..1cbd4439b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/tabs/index.ts @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, Subject, animationFrameScheduler } from "rxjs" +import { + distinctUntilKeyChanged, + finalize, + map, + observeOn, + switchMap, + tap +} from "rxjs/operators" + +import { resetTabsState, setTabsState } from "~/actions" +import { + Viewport, + watchElementSize, + watchViewportAt +} from "~/browser" + +import { Component } from "../_" +import { Header } from "../header" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Navigation tabs + */ +export interface Tabs { + hidden: boolean /* User scrolled past tabs */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch navigation tabs + * + * @param el - Navigation tabs element + * @param options - Options + * + * @returns Navigation tabs observable + */ +export function watchTabs( + el: HTMLElement, { viewport$, header$ }: WatchOptions +): Observable { + return watchElementSize(document.body) + .pipe( + switchMap(() => watchViewportAt(el, { header$, viewport$ })), + map(({ offset: { y } }) => { + return { + hidden: y >= 10 + } + }), + distinctUntilKeyChanged("hidden") + ) +} + +/** + * Mount navigation tabs + * + * This function hides the navigation tabs when scrolling past the threshold + * and makes them reappear in a nice CSS animation when scrolling back up. + * + * @param el - Navigation tabs element + * @param options - Options + * + * @returns Navigation tabs component observable + */ +export function mountTabs( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler) + ) + .subscribe({ + + /* Update state */ + next({ hidden }) { + if (hidden) + setTabsState(el, "hidden") + else + resetTabsState(el) + }, + + /* Reset on complete */ + complete() { + resetTabsState(el) + } + }) + + /* Create and return component */ + return watchTabs(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/toc/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/toc/index.ts new file mode 100644 index 000000000..02642fc5e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/toc/index.ts @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + animationFrameScheduler, + combineLatest +} from "rxjs" +import { + bufferCount, + distinctUntilChanged, + distinctUntilKeyChanged, + finalize, + map, + observeOn, + scan, + startWith, + switchMap, + tap +} from "rxjs/operators" + +import { + resetAnchorActive, + resetAnchorState, + setAnchorActive, + setAnchorState +} from "~/actions" +import { + Viewport, + getElement, + getElements, + watchElementSize +} from "~/browser" + +import { Component } from "../_" +import { Header } from "../header" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Table of contents + */ +export interface TableOfContents { + prev: HTMLAnchorElement[][] /* Anchors (previous) */ + next: HTMLAnchorElement[][] /* Anchors (next) */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + header$: Observable
/* Header observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch table of contents + * + * This is effectively a scroll spy implementation which will account for the + * fixed header and automatically re-calculate anchor offsets when the viewport + * is resized. The returned observable will only emit if the table of contents + * needs to be repainted. + * + * This implementation tracks an anchor element's entire path starting from its + * level up to the top-most anchor element, e.g. `[h3, h2, h1]`. Although the + * Material theme currently doesn't make use of this information, it enables + * the styling of the entire hierarchy through customization. + * + * Note that the current anchor is the last item of the `prev` anchor list. + * + * @param anchors - Anchor elements + * @param options - Options + * + * @returns Table of contents observable + */ +export function watchTableOfContents( + anchors: HTMLAnchorElement[], { viewport$, header$ }: WatchOptions +): Observable { + const table = new Map() + for (const anchor of anchors) { + const id = decodeURIComponent(anchor.hash.substring(1)) + const target = getElement(`[id="${id}"]`) + if (typeof target !== "undefined") + table.set(anchor, target) + } + + /* Compute necessary adjustment for header */ + const adjust$ = header$ + .pipe( + map(header => 24 + header.height) + ) + + /* Compute partition of previous and next anchors */ + const partition$ = watchElementSize(document.body) + .pipe( + distinctUntilKeyChanged("height"), + + /* Build index to map anchor paths to vertical offsets */ + map(() => { + let path: HTMLAnchorElement[] = [] + return [...table].reduce((index, [anchor, target]) => { + while (path.length) { + const last = table.get(path[path.length - 1])! + if (last.tagName >= target.tagName) { + path.pop() + } else { + break + } + } + + /* If the current anchor is hidden, continue with its parent */ + let offset = target.offsetTop + while (!offset && target.parentElement) { + target = target.parentElement + offset = target.offsetTop + } + + /* Map reversed anchor path to vertical offset */ + return index.set( + [...path = [...path, anchor]].reverse(), + offset + ) + }, new Map()) + }), + + /* Sort index by vertical offset (see https://bit.ly/30z6QSO) */ + map(index => new Map([...index].sort(([, a], [, b]) => a - b))), + + /* Re-compute partition when viewport offset changes */ + switchMap(index => combineLatest([adjust$, viewport$]) + .pipe( + scan(([prev, next], [adjust, { offset: { y } }]) => { + + /* Look forward */ + while (next.length) { + const [, offset] = next[0] + if (offset - adjust < y) { + prev = [...prev, next.shift()!] + } else { + break + } + } + + /* Look backward */ + while (prev.length) { + const [, offset] = prev[prev.length - 1] + if (offset - adjust >= y) { + next = [prev.pop()!, ...next] + } else { + break + } + } + + /* Return partition */ + return [prev, next] + }, [[], [...index]]), + distinctUntilChanged((a, b) => ( + a[0] === b[0] && + a[1] === b[1] + )) + ) + ) + ) + + /* Compute and return anchor list migrations */ + return partition$ + .pipe( + map(([prev, next]) => ({ + prev: prev.map(([path]) => path), + next: next.map(([path]) => path) + })), + + /* Extract anchor list migrations */ + startWith({ prev: [], next: [] }), + bufferCount(2, 1), + map(([a, b]) => { + + /* Moving down */ + if (a.prev.length < b.prev.length) { + return { + prev: b.prev.slice(Math.max(0, a.prev.length - 1), b.prev.length), + next: [] + } + + /* Moving up */ + } else { + return { + prev: b.prev.slice(-1), + next: b.next.slice(0, b.next.length - a.next.length) + } + } + }) + ) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Mount table of contents + * + * @param el - Anchor list element + * @param options - Options + * + * @returns Table of contents component observable + */ +export function mountTableOfContents( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler), + ) + .subscribe(({ prev, next }) => { + + /* Look forward */ + for (const [anchor] of next) { + resetAnchorActive(anchor) + resetAnchorState(anchor) + } + + /* Look backward */ + for (const [index, [anchor]] of prev.entries()) { + setAnchorActive(anchor, index === prev.length - 1) + setAnchorState(anchor, "blur") + } + }) + + /* Create and return component */ + const anchors = getElements("[href^=\\#]", el) + return watchTableOfContents(anchors, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/components/top/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/top/index.ts new file mode 100644 index 000000000..7f1608e00 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/components/top/index.ts @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + Subject, + animationFrameScheduler, + combineLatest +} from "rxjs" +import { + bufferCount, + distinctUntilChanged, + distinctUntilKeyChanged, + finalize, + map, + observeOn, + tap +} from "rxjs/operators" + +import { resetBackToTopState, setBackToTopState } from "~/actions" +import { Viewport } from "~/browser" + +import { Component } from "../_" +import { Main } from "../main" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Back-to-top button + */ +export interface BackToTop { + hidden: boolean /* User scrolled up */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Watch options + */ +interface WatchOptions { + viewport$: Observable /* Viewport observable */ + main$: Observable
/* Main area observable */ +} + +/** + * Mount options + */ +interface MountOptions { + viewport$: Observable /* Viewport observable */ + main$: Observable
/* Main area observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch back-to-top + * + * @param _el - Back-to-top element + * @param options - Options + * + * @returns Back-to-top observable + */ +export function watchBackToTop( + _el: HTMLElement, { viewport$, main$ }: WatchOptions +): Observable { + + /* Compute direction */ + const direction$ = viewport$ + .pipe( + map(({ offset: { y } }) => y), + bufferCount(2, 1), + map(([a, b]) => a > b), + distinctUntilChanged() + ) + + /* Compute whether button should be hidden */ + const hidden$ = main$ + .pipe( + distinctUntilKeyChanged("active") + ) + + /* Compute threshold for hiding */ + return combineLatest([hidden$, direction$]) + .pipe( + map(([{ active }, direction]) => ({ + hidden: !(active && direction) + })), + distinctUntilChanged((a, b) => ( + a.hidden === b.hidden + )) + ) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Mount back-to-top + * + * @param el - Back-to-top element + * @param options - Options + * + * @returns Back-to-top component observable + */ +export function mountBackToTop( + el: HTMLElement, options: MountOptions +): Observable> { + const internal$ = new Subject() + internal$ + .pipe( + observeOn(animationFrameScheduler) + ) + .subscribe({ + + /* Update state */ + next({ hidden }) { + if (hidden) + setBackToTopState(el, "hidden") + else + resetBackToTopState(el) + }, + + /* Reset on complete */ + complete() { + resetBackToTopState(el) + } + }) + + /* Create and return component */ + return watchBackToTop(el, options) + .pipe( + tap(internal$), + finalize(() => internal$.complete()), + map(state => ({ ref: el, ...state })) + ) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/clipboard/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/clipboard/index.ts new file mode 100644 index 000000000..e27c26011 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/clipboard/index.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import ClipboardJS from "clipboard" +import { Observable, Subject } from "rxjs" + +import { translation } from "~/_" + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Setup options + */ +interface SetupOptions { + alert$: Subject /* Alert subject */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set up Clipboard.js integration + * + * @param options - Options + */ +export function setupClipboardJS( + { alert$ }: SetupOptions +): void { + if (ClipboardJS.isSupported()) { + new Observable(subscriber => { + new ClipboardJS("[data-clipboard-target], [data-clipboard-text]") + .on("success", ev => subscriber.next(ev)) + }) + .subscribe(() => alert$.next(translation("clipboard.copied"))) + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/index.ts new file mode 100644 index 000000000..6a1e986b1 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./clipboard" +export * from "./instant" +export * from "./search" +export * from "./version" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/.eslintrc b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/.eslintrc new file mode 100644 index 000000000..5adf108ab --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/.eslintrc @@ -0,0 +1,6 @@ +{ + "rules": { + "no-self-assign": "off", + "no-null/no-null": "off" + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/index.ts new file mode 100644 index 000000000..a533d7f36 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/instant/index.ts @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + EMPTY, + NEVER, + Observable, + Subject, + fromEvent, + merge, + of +} from "rxjs" +import { + bufferCount, + catchError, + concatMap, + debounceTime, + distinctUntilChanged, + distinctUntilKeyChanged, + filter, + map, + sample, + share, + skip, + skipUntil, + switchMap +} from "rxjs/operators" + +import { configuration } from "~/_" +import { + Viewport, + ViewportOffset, + createElement, + getElement, + getElements, + replaceElement, + request, + requestXML, + setLocation, + setLocationHash, + setViewportOffset +} from "~/browser" +import { getComponentElement } from "~/components" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * History state + */ +export interface HistoryState { + url: URL /* State URL */ + offset?: ViewportOffset /* State viewport offset */ +} + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Setup options + */ +interface SetupOptions { + document$: Subject /* Document subject */ + location$: Subject /* Location subject */ + viewport$: Observable /* Viewport observable */ +} + +/* ---------------------------------------------------------------------------- + * Helper functions + * ------------------------------------------------------------------------- */ + +/** + * Preprocess a list of URLs + * + * This function replaces the `site_url` in the sitemap with the actual base + * URL, to allow instant loading to work in occasions like Netlify previews. + * + * @param urls - URLs + * + * @returns Processed URLs + */ +function preprocess(urls: string[]): string[] { + if (urls.length < 2) + return urls + + /* Take the first two URLs and remove everything after the last slash */ + const [root, next] = urls + .sort((a, b) => a.length - b.length) + .map(url => url.replace(/[^/]+$/, "")) + + /* Compute common prefix */ + let index = 0 + if (root === next) + index = root.length + else + while (root.charCodeAt(index) === next.charCodeAt(index)) + index++ + + /* Replace common prefix (i.e. base) with effective base */ + const config = configuration() + return urls.map(url => ( + url.replace(root.slice(0, index), `${config.base}/`) + )) +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set up instant loading + * + * When fetching, theoretically, we could use `responseType: "document"`, but + * since all MkDocs links are relative, we need to make sure that the current + * location matches the document we just loaded. Otherwise any relative links + * in the document could use the old location. + * + * This is the reason why we need to synchronize history events and the process + * of fetching the document for navigation changes (except `popstate` events): + * + * 1. Fetch document via `XMLHTTPRequest` + * 2. Set new location via `history.pushState` + * 3. Parse and emit fetched document + * + * For `popstate` events, we must not use `history.pushState`, or the forward + * history will be irreversibly overwritten. In case the request fails, the + * location change is dispatched regularly. + * + * @param options - Options + */ +export function setupInstantLoading( + { document$, location$, viewport$ }: SetupOptions +): void { + const config = configuration() + if (location.protocol === "file:") + return + + /* Disable automatic scroll restoration */ + if ("scrollRestoration" in history) { + history.scrollRestoration = "manual" + + /* Hack: ensure that reloads restore viewport offset */ + fromEvent(window, "beforeunload") + .subscribe(() => { + history.scrollRestoration = "auto" + }) + } + + /* Hack: ensure absolute favicon link to omit 404s when switching */ + const favicon = getElement("link[rel=icon]") + if (typeof favicon !== "undefined") + favicon.href = favicon.href + + /* Intercept internal navigation */ + const push$ = requestXML(`${config.base}/sitemap.xml`) + .pipe( + map(sitemap => preprocess(getElements("loc", sitemap) + .map(node => node.textContent!) + )), + switchMap(urls => fromEvent(document.body, "click") + .pipe( + filter(ev => !ev.metaKey && !ev.ctrlKey), + switchMap(ev => { + + /* Handle HTML and SVG elements */ + if (ev.target instanceof Element) { + const el = ev.target.closest("a") + if (el && !el.target && urls.includes(el.href)) { + ev.preventDefault() + return of({ + url: new URL(el.href) + }) + } + } + return NEVER + }) + ) + ), + share() + ) + + /* Intercept history back and forward */ + const pop$ = fromEvent(window, "popstate") + .pipe( + filter(ev => ev.state !== null), + map(ev => ({ + url: new URL(location.href), + offset: ev.state + })), + share() + ) + + /* Emit location change */ + merge(push$, pop$) + .pipe( + distinctUntilChanged((a, b) => a.url.href === b.url.href), + map(({ url }) => url) + ) + .subscribe(location$) + + /* Fetch document via `XMLHTTPRequest` */ + const response$ = location$ + .pipe( + distinctUntilKeyChanged("pathname"), + switchMap(url => request(url.href) + .pipe( + catchError(() => { + setLocation(url) + return NEVER + }) + ) + ), + share() + ) + + /* Set new location via `history.pushState` */ + push$ + .pipe( + sample(response$) + ) + .subscribe(({ url }) => { + history.pushState({}, "", `${url}`) + }) + + /* Parse and emit fetched document */ + const dom = new DOMParser() + response$ + .pipe( + switchMap(res => res.text()), + map(res => dom.parseFromString(res, "text/html")) + ) + .subscribe(document$) + + /* Emit history state change */ + merge(push$, pop$) + .pipe( + sample(document$) + ) + .subscribe(({ url, offset }) => { + if (url.hash && !offset) + setLocationHash(url.hash) + else + setViewportOffset(offset || { y: 0 }) + }) + + const loadedScriptUrls = new Set() + const loadedInlineScripts = new Set() + for (const el of getElements("script", document)) { + if (el.src) { + loadedScriptUrls.add(new URL(el.src, document.baseURI).toString()) + } else { + loadedInlineScripts.add(el.outerHTML) + } + } + + /* Replace meta tags and components */ + document$ + .pipe( + skip(1), + concatMap(async replacement => { + for (const selector of [ + + /* Meta tags */ + "title", + "link[rel=canonical]", + "meta[name=author]", + "meta[name=description]", + + /* Components */ + "[data-md-component=announce]", + "[data-md-component=container]", + "[data-md-component=header-topic]", + "[data-md-component=logo], .md-logo", // compat + "[data-md-component=skip]" + ]) { + const source = getElement(selector) + const target = getElement(selector, replacement) + if ( + typeof source !== "undefined" && + typeof target !== "undefined" + ) { + replaceElement(source, target) + } + } + + /* Run-run MathJax if already loaded */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((window as any).MathJax?.typesetPromise !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await (window as any).MathJax.typesetPromise() + } + + /* Insert scripts */ + for (const el of getElements("script", replacement)) { + if (el.src) { + const url = new URL(el.src, document.baseURI).toString() + if (!loadedScriptUrls.has(url)) { + const script = createElement("script") + for (const name of el.getAttributeNames()) { + script.setAttribute(name, el.getAttribute(name)!) + } + let promise: Promise|undefined + script.src = url + if (!script.async) { + promise = new Promise(resolve => script.addEventListener("load", () => resolve())) + } + document.body.appendChild(script) + loadedScriptUrls.add(url) + if (promise !== undefined) await promise + } + } else { + const outerHTML = el.outerHTML + if (!loadedInlineScripts.has(outerHTML)) { + const script = createElement("script") + for (const name of el.getAttributeNames()) { + script.setAttribute(name, el.getAttribute(name)!) + } + script.textContent = el.textContent + document.body.appendChild(script) + loadedInlineScripts.add(outerHTML) + } + } + } + }) + ).subscribe() + + /* Re-evaluate scripts */ + document$ + .pipe( + skip(1), + map(() => getComponentElement("container")), + switchMap(el => of(...getElements("script", el))), + concatMap(el => { + const script = createElement("script") + if (el.src) { + for (const name of el.getAttributeNames()) + script.setAttribute(name, el.getAttribute(name)!) + replaceElement(el, script) + + /* Complete when script is loaded */ + return new Observable(observer => { + script.onload = () => observer.complete() + }) + + /* Complete immediately */ + } else { + script.textContent = el.textContent + replaceElement(el, script) + return EMPTY + } + }) + ) + .subscribe() + + /* Debounce update of viewport offset */ + viewport$ + .pipe( + skipUntil(push$), + debounceTime(250), + distinctUntilKeyChanged("offset") + ) + .subscribe(({ offset }) => { + history.replaceState(offset, "") + }) + + /* Set viewport offset from history */ + merge(push$, pop$) + .pipe( + bufferCount(2, 1), + filter(([a, b]) => a.url.pathname === b.url.pathname), + map(([, state]) => state) + ) + .subscribe(({ offset }) => { + setViewportOffset(offset || { y: 0 }) + }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/.eslintrc b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/.eslintrc new file mode 100644 index 000000000..fd92bace6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/.eslintrc @@ -0,0 +1,6 @@ +{ + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "no-console": "off" + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/index.ts new file mode 100644 index 000000000..1ef2efae9 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/_/index.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + SearchDocument +} from "../document" +import { + SearchQueryTerms +} from "../query" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search index configuration + */ +export interface SearchIndexConfig { + lang: string[] /* Search languages */ + separator: string /* Search separator */ +} + +/** + * Search index document + */ +export interface SearchIndexDocument { + location: string /* Document location */ + title: string /* Document title */ + text: string /* Document text */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Search index pipeline function + */ +export type SearchIndexPipelineFn = + | "trimmer" /* Trimmer */ + | "stopWordFilter" /* Stop word filter */ + | "stemmer" /* Stemmer */ + +/** + * Search index pipeline + */ +export type SearchIndexPipeline = SearchIndexPipelineFn[] + +/* ------------------------------------------------------------------------- */ + +/** + * Search index + * + * This interfaces describes the format of the `search_index.json` file which + * is automatically built by the MkDocs search plugin. + */ +export interface SearchIndex { + config: SearchIndexConfig /* Search index configuration */ + docs: SearchIndexDocument[] /* Search index documents */ + index?: object /* Prebuilt index */ + pipeline?: SearchIndexPipeline /* Search index pipeline */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Search metadata + */ +export interface SearchMetadata { + score: number /* Score (relevance) */ + terms: SearchQueryTerms /* Search query terms */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Search result + */ +export type SearchResult = Array diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/document/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/document/index.ts new file mode 100644 index 000000000..7ee726da7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/document/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { SearchIndexDocument } from "../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search document + */ +export interface SearchDocument extends SearchIndexDocument { + parent?: SearchIndexDocument /* Parent article */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Search document mapping + */ +export type SearchDocumentMap = Map diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/highlighter/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/highlighter/index.ts new file mode 100644 index 000000000..6a1beb91a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/highlighter/index.ts @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { SearchIndexConfig } from "../_" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search highlight function + * + * @param value - Value + * + * @returns Highlighted value + */ +export type SearchHighlightFn = (value: string) => string + +/** + * Search highlight factory function + * + * @param query - Query value + * + * @returns Search highlight function + */ +export type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Create a search highlighter + * + * @param config - Search index configuration + * + * @returns Search highlight factory function + */ +export function setupSearchHighlighter( + config: SearchIndexConfig +): SearchHighlightFactoryFn { + const separator = new RegExp(config.separator, "img") + const highlight = (_: unknown, data: string, term: string) => { + return `${data}${term}` + } + + /* Return factory function */ + return (query: string) => { + query = query + .replace(/[\s*+\-:~^]+/g, " ") + .trim() + + /* Create search term match expression */ + const match = new RegExp(`(^|${config.separator})(${ + query + .replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&") + .replace(separator, "|") + })`, "img") + + /* Highlight string value */ + return value => value + .replace(match, highlight) + .replace(/<\/mark>(\s+)]*>/img, "$1") + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/index.ts new file mode 100644 index 000000000..d3bf1803a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" +export * from "./document" +export * from "./highlighter" +export * from "./query" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/.eslintrc b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/.eslintrc new file mode 100644 index 000000000..8b8e4250e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/no-explicit-any": "off" + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/index.ts new file mode 100644 index 000000000..d8b60f412 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/_/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search query clause + */ +export interface SearchQueryClause { + presence: lunr.Query.presence /* Clause presence */ + term: string /* Clause term */ +} + +/* ------------------------------------------------------------------------- */ + +/** + * Search query terms + */ +export type SearchQueryTerms = Record diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/index.ts new file mode 100644 index 000000000..bb3602f60 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./_" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/.eslintrc b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/.eslintrc new file mode 100644 index 000000000..5645b172f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "no-control-regex": "off" + } +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/index.ts new file mode 100644 index 000000000..f498495c1 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/search/query/transform/index.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Search transformation function + * + * @param value - Query value + * + * @returns Transformed query value + */ +export type SearchTransformFn = (value: string) => string + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Default transformation function + * + * 1. Search for terms in quotation marks and prepend a `+` modifier to denote + * that the resulting document must contain all terms, converting the query + * to an `AND` query (as opposed to the default `OR` behavior). While users + * may expect terms enclosed in quotation marks to map to span queries, i.e. + * for which order is important, Lunr.js doesn't support them, so the best + * we can do is to convert the terms to an `AND` query. + * + * 2. Replace control characters which are not located at the beginning of the + * query or preceded by white space, or are not followed by a non-whitespace + * character or are at the end of the query string. Furthermore, filter + * unmatched quotation marks. + * + * 3. Trim excess whitespace from left and right. + * + * @param query - Query value + * + * @returns Transformed query value + */ +export function defaultTransform(query: string): string { + return query + .split(/"([^"]+)"/g) /* => 1 */ + .map((terms, index) => index & 1 + ? terms.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g, " +") + : terms + ) + .join("") + .replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g, "") /* => 2 */ + .trim() /* => 3 */ +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/version/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/version/index.ts new file mode 100644 index 000000000..5f446bd11 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/integrations/version/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, of } from "rxjs" + +import { configuration } from "~/_" +import { getElementOrThrow, requestJSON } from "~/browser" +import { Version, renderVersionSelector } from "~/templates" + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Set up version selector + */ +export function setupVersionSelector(): void { + const config = configuration() + const versionObservable: Observable = + config.version!.staticVersions ? + of(config.version!.staticVersions) : + requestJSON(new URL( + config.version!.versionPath ?? "versions.json", config.base)) + versionObservable + .subscribe(versions => { + const topic = getElementOrThrow(".md-header__topic") + topic.appendChild(renderVersionSelector(versions)) + }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/indeterminate/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/indeterminate/index.ts new file mode 100644 index 000000000..0397edc48 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/indeterminate/index.ts @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, of } from "rxjs" +import { + mapTo, + mergeMap, + switchMap, + takeWhile, + tap, + withLatestFrom +} from "rxjs/operators" + +import { getElements } from "~/browser" + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Patch options + */ +interface PatchOptions { + document$: Observable /* Document observable */ + tablet$: Observable /* Tablet breakpoint observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Patch indeterminate checkboxes + * + * This function replaces the indeterminate "pseudo state" with the actual + * indeterminate state, which is used to keep navigation always expanded. + * + * @param options - Options + */ +export function patchIndeterminate( + { document$, tablet$ }: PatchOptions +): void { + document$ + .pipe( + switchMap(() => of(...getElements( + "[data-md-state=indeterminate]" + ))), + tap(el => { + el.indeterminate = true + el.checked = false + }), + mergeMap(el => fromEvent(el, "change") + .pipe( + takeWhile(() => el.hasAttribute("data-md-state")), + mapTo(el) + ) + ), + withLatestFrom(tablet$) + ) + .subscribe(([el, tablet]) => { + el.removeAttribute("data-md-state") + if (tablet) + el.checked = false + }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/index.ts new file mode 100644 index 000000000..3195e6e56 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +export * from "./indeterminate" +export * from "./scrollfix" +export * from "./scrolllock" diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrollfix/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrollfix/index.ts new file mode 100644 index 000000000..941464ca7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrollfix/index.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, fromEvent, of } from "rxjs" +import { + filter, + mapTo, + mergeMap, + switchMap, + tap +} from "rxjs/operators" + +import { getElements } from "~/browser" + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Patch options + */ +interface PatchOptions { + document$: Observable /* Document observable */ +} + +/* ---------------------------------------------------------------------------- + * Helper functions + * ------------------------------------------------------------------------- */ + +/** + * Check whether the given device is an Apple device + * + * @returns Test result + */ +function isAppleDevice(): boolean { + return /(iPad|iPhone|iPod)/.test(navigator.userAgent) +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Patch all elements with `data-md-scrollfix` attributes + * + * This is a year-old patch which ensures that overflow scrolling works at the + * top and bottom of containers on iOS by ensuring a `1px` scroll offset upon + * the start of a touch event. + * + * @see https://bit.ly/2SCtAOO - Original source + * + * @param options - Options + */ +export function patchScrollfix( + { document$ }: PatchOptions +): void { + document$ + .pipe( + switchMap(() => of(...getElements("[data-md-scrollfix]"))), + tap(el => el.removeAttribute("data-md-scrollfix")), + filter(isAppleDevice), + mergeMap(el => fromEvent(el, "touchstart") + .pipe( + mapTo(el) + ) + ) + ) + .subscribe(el => { + const top = el.scrollTop + + /* We're at the top of the container */ + if (top === 0) { + el.scrollTop = 1 + + /* We're at the bottom of the container */ + } else if (top + el.offsetHeight === el.scrollHeight) { + el.scrollTop = top - 1 + } + }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrolllock/index.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrolllock/index.ts new file mode 100644 index 000000000..884c76120 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/patches/scrolllock/index.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { + Observable, + animationFrameScheduler, + combineLatest, + of +} from "rxjs" +import { + delay, + map, + observeOn, + switchMap, + withLatestFrom +} from "rxjs/operators" + +import { resetScrollLock, setScrollLock } from "~/actions" +import { Viewport, watchToggle } from "~/browser" + +/* ---------------------------------------------------------------------------- + * Helper types + * ------------------------------------------------------------------------- */ + +/** + * Patch options + */ +interface PatchOptions { + viewport$: Observable /* Viewport observable */ + tablet$: Observable /* Tablet breakpoint observable */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Patch the document body to lock when search is open + * + * For mobile and tablet viewports, the search is rendered full screen, which + * leads to scroll leaking when at the top or bottom of the search result. This + * function locks the body when the search is in full screen mode, and restores + * the scroll position when leaving. + * + * @param options - Options + */ +export function patchScrolllock( + { viewport$, tablet$ }: PatchOptions +): void { + combineLatest([watchToggle("search"), tablet$]) + .pipe( + map(([active, tablet]) => active && !tablet), + switchMap(active => of(active) + .pipe( + delay(active ? 400 : 100), + observeOn(animationFrameScheduler) + ) + ), + withLatestFrom(viewport$) + ) + .subscribe(([active, { offset: { y }}]) => { + if (active) + setScrollLock(document.body, y) + else + resetScrollLock(document.body) + }) +} diff --git a/docs/tensorstore_sphinx_material/src/assets/javascripts/sphinx_search.ts b/docs/tensorstore_sphinx_material/src/assets/javascripts/sphinx_search.ts new file mode 100644 index 000000000..df66bd0ab --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/assets/javascripts/sphinx_search.ts @@ -0,0 +1,762 @@ +// Derived from searchtools.js in sphinx. +// +// :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. +// :license: BSD, see LICENSE for details. + +import escapeHTML from "escape-html" + +import { configuration } from "~/_" + +import { SearchResult } from "./integrations/search/_" + +interface SphinxSearchResult { + docurl: string + title: string + anchor: string + objectLabel: string | null + synopsis: string | null + score: number +} + +const config = configuration() + +/** + * Returns a URL for a given path relative to the documentation root. + * + * @param path - Path relative to documentation root. + * @returns The full URL. + */ +function getAbsoluteUrl(path: string): string { + return `${config.base}/${path}` +} + +let searchIndexLoaded: Promise | undefined + +/** + * Loads a script by adding a ` + {% endblock %} + + + {% block scripts %} + + + + {%- for js in script_files %} + {{ js_tag(js) }} + {%- endfor %} + {% endblock %} + + diff --git a/docs/tensorstore_sphinx_material/src/layout.html b/docs/tensorstore_sphinx_material/src/layout.html new file mode 100644 index 000000000..b0d1d9d5f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/layout.html @@ -0,0 +1,2 @@ +{# Main layout template is in `base.html` to match mkdocs-material #} +{% extends "base.html" %} diff --git a/docs/tensorstore_sphinx_material/src/main.html b/docs/tensorstore_sphinx_material/src/main.html new file mode 100644 index 000000000..37d79e551 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/main.html @@ -0,0 +1,23 @@ + + +{% extends "base.html" %} diff --git a/docs/tensorstore_sphinx_material/src/partials/footer.html b/docs/tensorstore_sphinx_material/src/partials/footer.html new file mode 100644 index 000000000..17b0cf3cc --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/footer.html @@ -0,0 +1,118 @@ + + +{% import "partials/language.html" as lang with context %} + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/header.html b/docs/tensorstore_sphinx_material/src/partials/header.html new file mode 100644 index 000000000..5510c5d86 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/header.html @@ -0,0 +1,140 @@ + + + +
+ +
diff --git a/docs/tensorstore_sphinx_material/src/partials/hero.html b/docs/tensorstore_sphinx_material/src/partials/hero.html new file mode 100644 index 000000000..b3e1ed059 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/hero.html @@ -0,0 +1,8 @@ +{% if 'hero' in meta %} +{% set hero = meta['hero'] | e %} + +{% endif %} diff --git a/docs/tensorstore_sphinx_material/src/partials/integrations/analytics.html b/docs/tensorstore_sphinx_material/src/partials/integrations/analytics.html new file mode 100644 index 000000000..f81d59cfe --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/integrations/analytics.html @@ -0,0 +1,55 @@ + + + +{% set analytics = config.google_analytics %} + + diff --git a/docs/tensorstore_sphinx_material/src/partials/integrations/disqus.html b/docs/tensorstore_sphinx_material/src/partials/integrations/disqus.html new file mode 100644 index 000000000..0eaeb9a1b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/integrations/disqus.html @@ -0,0 +1,46 @@ + + + +{% set disqus = config.extra.disqus %} +{% if page and page.meta and page.meta.disqus is string %} + {% set disqus = page.meta.disqus %} +{% endif %} + + +{% if not page.is_homepage and disqus %} +

{{ lang.t("meta.comments") }}

+
+ +{% endif %} diff --git a/docs/tensorstore_sphinx_material/src/partials/javascripts/base.html b/docs/tensorstore_sphinx_material/src/partials/javascripts/base.html new file mode 100644 index 000000000..db6d4a24e --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/javascripts/base.html @@ -0,0 +1,39 @@ + + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/javascripts/palette.html b/docs/tensorstore_sphinx_material/src/partials/javascripts/palette.html new file mode 100644 index 000000000..994793290 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/javascripts/palette.html @@ -0,0 +1,29 @@ + + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/language.html b/docs/tensorstore_sphinx_material/src/partials/language.html new file mode 100644 index 000000000..4041804b4 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/language.html @@ -0,0 +1,28 @@ + + + +{% import "partials/languages/" ~ language ~ ".html" as lang %} +{% import "partials/languages/en.html" as fallback %} + + +{% macro t(key) %}{{ lang.t(key) or fallback.t(key) }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/af.html b/docs/tensorstore_sphinx_material/src/partials/languages/af.html new file mode 100644 index 000000000..abae52964 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/af.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "af", + "clipboard.copy": "Kopieer na knipbord", + "clipboard.copied": "gekopieer na knipbord", + "edit.link.title": "Wysig hierdie bladsy", + "footer.previous": "Vorige", + "footer.next": "Volgende", + "meta.comments": "Kommentaar", + "meta.source": "Bron", + "search.config.lang": "nl", + "search.placeholder": "Soek", + "search.result.placeholder": "Tik om te begin soek", + "search.result.none": "Geen ooreenstemmende dokumente", + "search.result.one": "1 ooreenstemmende dokument", + "search.result.other": "# ooreenstemmende dokumente", + "skip.link.title": "Slaan oor na inhoud", + "source.link.title": "Slaan oor na inhoud", + "source.revision.date": "Laaste opdatering", + "source.file.date.updated": "Laaste opdatering", + "source.file.date.created": "Geskep", + "toc.title": "Inhoudsopgawe" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ar.html b/docs/tensorstore_sphinx_material/src/partials/languages/ar.html new file mode 100644 index 000000000..254f6f21c --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ar.html @@ -0,0 +1,46 @@ + + + +{% macro t(key) %}{{ { + "language": "ar", + "direction": "rtl", + "clipboard.copy": "نسخ إلى الحافظة", + "clipboard.copied": "تم النسخ الى الحافظة", + "edit.link.title": "عدل الصفحة", + "footer.previous": "السابقة", + "footer.next": "التالية", + "meta.comments": "التعليقات", + "meta.source": "المصدر", + "search.config.pipeline": " ", + "search.placeholder": "بحث", + "search.result.placeholder": "اكتب لبدء البحث", + "search.result.none": "لا توجد نتائج", + "search.result.one": "نتائج البحث مستند واحد", + "search.result.other": "نتائج البحث # مستندات", + "skip.link.title": "انتقل إلى المحتوى", + "source.link.title": "اذهب إلى المصدر", + "source.revision.date": "اخر تحديث", + "source.file.date.updated": "اخر تحديث", + "source.file.date.created": "خلقت", + "toc.title": "جدول المحتويات" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/bg.html b/docs/tensorstore_sphinx_material/src/partials/languages/bg.html new file mode 100644 index 000000000..2204b6251 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/bg.html @@ -0,0 +1,52 @@ + + + +{% macro t(key) %}{{ { + "language": "bg", + "clipboard.copy": "Копирай", + "clipboard.copied": "Копирано", + "edit.link.title": "Редактирай тази страница", + "footer.previous": "Предишна", + "footer.next": "Следваща", + "footer.title": "Долен колонтитул", + "header.title": "Горен колонтитул", + "meta.comments": "Коментари", + "meta.source": "Код", + "nav.title": "Навигация", + "search.config.lang": "ru", + "search.placeholder": "Търси", + "search.reset": "Изчисти", + "search.result.placeholder": "Започнете да пишете, за да търсите", + "search.result.none": "Няма резултати", + "search.result.one": "1 резултат", + "search.result.other": "# резултата", + "search.result.more.one": "още 1 на тази страница", + "search.result.more.other": "още # на тази страница", + "skip.link.title": "Към съдържанието", + "source.link.title": "Към хранилището", + "source.revision.date": "Последна промяна", + "source.file.date.updated": "Последна промяна", + "source.file.date.created": "Създаден", + "tabs.title": "Табове", + "toc.title": "Съдържание" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/bn.html b/docs/tensorstore_sphinx_material/src/partials/languages/bn.html new file mode 100644 index 000000000..9acec3c14 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/bn.html @@ -0,0 +1,50 @@ + + + +{% macro t(key) %}{{ { + "language": "bn", + "clipboard.copy": "ক্লিপবোর্ডে কপি করুন", + "clipboard.copied": "ক্লিপবোর্ডে কপি হয়েছে", + "edit.link.title": "এই পেজ এডিট করুন", + "footer.previous": "পূর্ববর্তী", + "footer.next": "পরে", + "footer.title": "ফুটার", + "header.title": "হেডার", + "meta.comments": "কমেন্ট", + "meta.source": "সোর্স", + "nav.title": "ন্যাভিগেশন", + "search.config.pipeline": " ", + "search.placeholder": "সার্চ", + "search.reset": "মুছে ফেলুন", + "search.result.placeholder": "সার্চ টাইপ করুন", + "search.result.none": "কিছু পাওয়া যায়নি", + "search.result.one": "১ টা ডকুমেন্ট", + "search.result.other": "# টা ডকুমেন্ট", + "skip.link.title": "কনটেন্টে যান", + "source.link.title": "রিপোজিটরিতে যান", + "source.revision.date": "শেষ আপডেট", + "source.file.date.updated": "শেষ আপডেট", + "source.file.date.created": "তৈরি হয়েছে", + "tabs.title": "ট্যাব", + "toc.title": "টেবিল অফ কনটেন্ট" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ca.html b/docs/tensorstore_sphinx_material/src/partials/languages/ca.html new file mode 100644 index 000000000..898072ee8 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ca.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "ca", + "clipboard.copy": "Còpia al porta-retalls", + "clipboard.copied": "Copiat al porta-retalls", + "edit.link.title": "Edita aquesta pàgina", + "footer.previous": "Anterior", + "footer.next": "Següent", + "meta.comments": "Comentaris", + "meta.source": "Codi font", + "search.placeholder": "Cerca", + "search.result.placeholder": "Escriu per a començar a cercar", + "search.result.none": "Cap document coincideix", + "search.result.one": "1 document coincident", + "search.result.other": "# documents coincidents", + "skip.link.title": "Salta el contingut", + "source.link.title": "Ves al repositori", + "source.revision.date": "Darrera actualització", + "source.file.date.updated": "Darrera actualització", + "source.file.date.created": "Creada", + "toc.title": "Taula de continguts" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/cs.html b/docs/tensorstore_sphinx_material/src/partials/languages/cs.html new file mode 100644 index 000000000..e5d555a87 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/cs.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "cs", + "clipboard.copy": "Kopírovat do schránky", + "clipboard.copied": "Zkopírováno do schránky", + "edit.link.title": "Upravit tuto stránku", + "footer.previous": "Předchozí", + "footer.next": "Další", + "meta.comments": "Komentáře", + "meta.source": "Zdroj", + "search.placeholder": "Hledat", + "search.result.placeholder": "Pište co se má vyhledat", + "search.result.none": "Nenalezeny žádné dokumenty", + "search.result.one": "Nalezený dokument: 1", + "search.result.other": "Nalezené dokumenty: #", + "skip.link.title": "Přeskočit obsah", + "source.link.title": "Přejít do repozitáře", + "source.revision.date": "Poslední aktualizace", + "source.file.date.updated": "Poslední aktualizace", + "source.file.date.created": "Vytvořeno", + "toc.title": "Obsah" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/da.html b/docs/tensorstore_sphinx_material/src/partials/languages/da.html new file mode 100644 index 000000000..3b161a6aa --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/da.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "da", + "clipboard.copy": "Kopiér til udklipsholderen", + "clipboard.copied": "Kopieret til udklipsholderen", + "edit.link.title": "Redigér denne side", + "footer.previous": "Forrige", + "footer.next": "Næste", + "meta.comments": "Kommentarer", + "meta.source": "Kilde", + "search.config.lang": "da", + "search.placeholder": "Søg", + "search.result.placeholder": "Indtast søgeord", + "search.result.none": "Ingen resultater fundet", + "search.result.one": "1 resultat", + "search.result.other": "# resultater", + "skip.link.title": "Gå til indholdet", + "source.link.title": "Åbn arkiv", + "source.revision.date": "Sidste ændring", + "source.file.date.updated": "Sidste ændring", + "source.file.date.created": "Oprettet", + "toc.title": "Indholdsfortegnelse" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/de.html b/docs/tensorstore_sphinx_material/src/partials/languages/de.html new file mode 100644 index 000000000..41c3e5ca9 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/de.html @@ -0,0 +1,49 @@ + + + +{% macro t(key) %}{{ { + "language": "de", + "clipboard.copy": "In Zwischenablage kopieren", + "clipboard.copied": "In Zwischenablage kopiert", + "edit.link.title": "Seite editieren", + "footer.previous": "Zurück", + "footer.next": "Weiter", + "meta.comments": "Kommentare", + "meta.source": "Quellcode", + "search.config.lang": "de", + "search.placeholder": "Suche", + "search.result.initializer": "Suche wird initialisiert", + "search.result.placeholder": "Suchbegriff eingeben", + "search.result.none": "Keine Suchergebnisse", + "search.result.one": "1 Suchergebnis", + "search.result.other": "# Suchergebnisse", + "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", + "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", + "search.result.term.missing": "Es fehlt", + "skip.link.title": "Zum Inhalt", + "source.link.title": "Quellcode", + "source.revision.date": "Letztes Update", + "source.file.date.updated": "Letztes Update", + "source.file.date.created": "Erstellt", + "toc.title": "Inhaltsverzeichnis" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/en.html b/docs/tensorstore_sphinx_material/src/partials/languages/en.html new file mode 100644 index 000000000..edd63d12c --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/en.html @@ -0,0 +1,58 @@ + + + +{% macro t(key) %}{{ { + "language": "en", + "direction": "ltr", + "clipboard.copy": "Copy to clipboard", + "clipboard.copied": "Copied to clipboard", + "edit.link.title": "Edit this page", + "footer.previous": "Previous", + "footer.next": "Next", + "footer.title": "Footer", + "header.title": "Header", + "meta.comments": "Comments", + "meta.source": "Source", + "nav.title": "Navigation", + "search.config.lang": "en", + "search.config.pipeline": "trimmer, stopWordFilter", + "search.config.separator": "[\s\-]+", + "search.placeholder": "Search", + "search.reset": "Clear", + "search.result.initializer": "Initializing search", + "search.result.placeholder": "Type to start searching", + "search.result.none": "No matching documents", + "search.result.one": "1 matching document", + "search.result.other": "# matching documents", + "search.result.more.one": "1 more on this page", + "search.result.more.other": "# more on this page", + "search.result.term.missing": "Missing", + "skip.link.title": "Skip to content", + "source.link.title": "Go to repository", + "source.revision.date": "Last update", + "source.file.date.updated": "Last update", + "source.file.date.created": "Created", + "tabs.title": "Tabs", + "toc.title": "Table of contents", + "top.title": "Back to top" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/eo.html b/docs/tensorstore_sphinx_material/src/partials/languages/eo.html new file mode 100644 index 000000000..07af949a7 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/eo.html @@ -0,0 +1,50 @@ + + + +{% macro t(key) %}{{ { + "language": "eo", + "clipboard.copy": "Kopii al tondujo", + "clipboard.copied": "Kopiado al klipo", + "edit.link.title": "Redakti ĉi tiun paĝon", + "footer.previous": "Antaŭa", + "footer.next": "Sekva", + "footer.title": "Piedlinio", + "header.title": "Kaplinio", + "meta.comments": "Komentoj", + "meta.source": "Fontkodo", + "nav.title": "Navigado", + "search.config.lang": "es", + "search.placeholder": "Serĉo", + "search.reset": "Klara", + "search.result.placeholder": "Tajpu por komenci serĉadon", + "search.result.none": "Neniuj kongruaj dokumentoj", + "search.result.one": "1 kongrua dokumento", + "search.result.other": "# kongruaj dokumentoj", + "skip.link.title": "Saltu al enhavo", + "source.link.title": "Iru al deponejo", + "source.revision.date": "Lasta ĝisdatigo", + "source.file.date.updated": "Lasta ĝisdatigo", + "source.file.date.created": "Kreita", + "tabs.title": "Langetoj", + "toc.title": "Enhavtabelo" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/es.html b/docs/tensorstore_sphinx_material/src/partials/languages/es.html new file mode 100644 index 000000000..abf6bd6bc --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/es.html @@ -0,0 +1,54 @@ + + + +{% macro t(key) %}{{ { + "language": "es", + "clipboard.copy": "Copiar al portapapeles", + "clipboard.copied": "Copiado al portapapeles", + "edit.link.title": "Editar esta página", + "footer.previous": "Anterior", + "footer.next": "Siguiente", + "footer.title": "Pie", + "header.title": "Cabecera", + "meta.comments": "Comentarios", + "meta.source": "Fuente", + "nav.title": "Navegación", + "search.config.lang": "es", + "search.placeholder": "Búsqueda", + "search.reset": "Limpiar", + "search.result.initializer": "Inicializando búsqueda", + "search.result.placeholder": "Teclee para comenzar búsqueda", + "search.result.none": "No se encontraron documentos", + "search.result.one": "1 documento encontrado", + "search.result.other": "# documentos encontrados", + "search.result.more.one": "1 más en esta página", + "search.result.more.other": "# más en esta página", + "search.result.term.missing": "Falta", + "skip.link.title": "Saltar a contenido", + "source.link.title": "Ir al repositorio", + "source.revision.date": "Última actualización", + "source.file.date.updated": "Última actualización", + "source.file.date.created": "Created", + "tabs.title": "Pestañas", + "toc.title": "Tabla de contenidos" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/et.html b/docs/tensorstore_sphinx_material/src/partials/languages/et.html new file mode 100644 index 000000000..eac90dcbe --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/et.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "et", + "clipboard.copy": "Kopeeri lõikelauale", + "clipboard.copied": "Kopeeritud", + "edit.link.title": "Muuda seda lehte", + "footer.previous": "Eelmine", + "footer.next": "Järgmine", + "meta.comments": "Kommentaarid", + "meta.source": "Lähtekood", + "search.placeholder": "Otsi", + "search.result.placeholder": "Otsimiseks kirjuta siia", + "search.result.none": "Otsingule ei leitud ühtegi vastet", + "search.result.one": "Leiti üks tulemus", + "search.result.other": "Leiti # tulemust", + "skip.link.title": "Keri sisuni", + "source.link.title": "Ava repositooriumis", + "source.revision.date": "Viimane uuendus", + "source.file.date.updated": "Viimane uuendus", + "source.file.date.created": "Loodud", + "toc.title": "Sisukord" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/fa.html b/docs/tensorstore_sphinx_material/src/partials/languages/fa.html new file mode 100644 index 000000000..77fc6fb2a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/fa.html @@ -0,0 +1,46 @@ + + + +{% macro t(key) %}{{ { + "language": "fa", + "direction": "rtl", + "clipboard.copy": "کپی کردن", + "clipboard.copied": "کپی شد", + "edit.link.title": "این صفحه را ویرایش کنید", + "footer.previous": "قبلی", + "footer.next": "بعدی", + "meta.comments": "نظرات", + "meta.source": "منبع", + "search.config.pipeline": " ", + "search.placeholder": "جستجو", + "search.result.placeholder": "برای شروع جستجو تایپ کنید", + "search.result.none": "سندی یافت نشد", + "search.result.one": "1 سند یافت شد", + "search.result.other": "# سند یافت شد", + "skip.link.title": "پرش به محتویات", + "source.link.title": "رفتن به مخزن", + "source.revision.date": "اخرین بروزرسانی", + "source.file.date.updated": "اخرین بروزرسانی", + "source.file.date.created": "ایجاد شده", + "toc.title": "فهرست موضوعات" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/fi.html b/docs/tensorstore_sphinx_material/src/partials/languages/fi.html new file mode 100644 index 000000000..9ad2e3c8b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/fi.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "fi", + "clipboard.copy": "Kopioi leikepöydälle", + "clipboard.copied": "Kopioitu leikepöydälle", + "edit.link.title": "Muokkaa tätä sivua", + "footer.previous": "Edellinen", + "footer.next": "Seuraava", + "meta.comments": "Kommentit", + "meta.source": "Lähdekodi", + "search.config.lang": "fi", + "search.placeholder": "Hae", + "search.result.placeholder": "Kirjoita aloittaaksesi haun", + "search.result.none": "Ei täsmääviä dokumentteja", + "search.result.one": "1 täsmäävä dokumentti", + "search.result.other": "# täsmäävää dokumenttia", + "skip.link.title": "Hyppää sisältöön", + "source.link.title": "Mene repositoryyn", + "source.revision.date": "Viimeisin päivitys", + "source.file.date.updated": "Viimeisin päivitys", + "source.file.date.created": "Luotu", + "toc.title": "Sisällysluettelo" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/fr.html b/docs/tensorstore_sphinx_material/src/partials/languages/fr.html new file mode 100644 index 000000000..efd6f3928 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/fr.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "fr", + "clipboard.copy": "Copier dans le presse-papier", + "clipboard.copied": "Copié dans le presse-papier", + "edit.link.title": "Editer cette page", + "footer.previous": "Précédent", + "footer.next": "Suivant", + "meta.comments": "Commentaires", + "meta.source": "Source", + "search.config.lang": "fr", + "search.placeholder": "Rechercher", + "search.result.placeholder": "Taper pour démarrer la recherche", + "search.result.none": "Aucun document trouvé", + "search.result.one": "1 document trouvé", + "search.result.other": "# documents trouvés", + "skip.link.title": "Aller au contenu", + "source.link.title": "Aller au dépôt", + "source.revision.date": "Dernière mise à jour", + "source.file.date.updated": "Dernière mise à jour", + "source.file.date.created": "Créé", + "toc.title": "Table des matières" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/gl.html b/docs/tensorstore_sphinx_material/src/partials/languages/gl.html new file mode 100644 index 000000000..cffaf60c5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/gl.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "gl", + "clipboard.copy": "Copiar no cortapapeis", + "clipboard.copied": "Copiado no cortapapeis", + "edit.link.title": "Editar esta páxina", + "footer.previous": "Anterior", + "footer.next": "Seguinte", + "meta.comments": "Comentarios", + "meta.source": "Fonte", + "search.config.lang": "es", + "search.placeholder": "Busca", + "search.result.placeholder": "Insira un termo", + "search.result.none": "Sen resultados", + "search.result.one": "1 resultado atopado", + "search.result.other": "# resultados atopados", + "skip.link.title": "Ir ao contido", + "source.link.title": "Ir ao repositorio", + "source.revision.date": "Última actualización", + "source.file.date.updated": "Última actualización", + "source.file.date.created": "Creada", + "toc.title": "Táboa de contidos" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/gr.html b/docs/tensorstore_sphinx_material/src/partials/languages/gr.html new file mode 100644 index 000000000..0edc3e4dd --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/gr.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "gr", + "clipboard.copy": "Αντιγραφή", + "clipboard.copied": "Αντιγράφηκε", + "edit.link.title": "Επεξεργασία αυτής της σελίδας", + "footer.previous": "Επόμενη", + "footer.next": "Προηγούμενη", + "meta.comments": "Σχόλια", + "meta.source": "Πηγή", + "search.config.pipeline": " ", + "search.placeholder": "Αναζήτηση", + "search.result.placeholder": "Πληκτρολογήστε για να αρχίσει η αναζήτηση", + "search.result.none": "Δε βρέθηκαν αντίστοιχα αρχεία", + "search.result.one": "1 αντίστοιχο αρχείο", + "search.result.other": "# αντίστοιχα αρχεία", + "skip.link.title": "Μετάβαση στο περιεχόμενο", + "source.link.title": "Μετάβαση στο αποθετήριο", + "source.revision.date": "τελευταία ενημέρωση", + "source.file.date.updated": "τελευταία ενημέρωση", + "source.file.date.created": "Δημιουργήθηκε", + "toc.title": "Πίνακας περιεχομένων" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/he.html b/docs/tensorstore_sphinx_material/src/partials/languages/he.html new file mode 100644 index 000000000..79bbf3ea3 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/he.html @@ -0,0 +1,46 @@ + + + +{% macro t(key) %}{{ { + "language": "he", + "direction": "rtl", + "clipboard.copy": "העתק ללוח", + "clipboard.copied": "הועתק ללוח", + "edit.link.title": "ערוך דף זה", + "footer.previous": "קודם", + "footer.next": "הַבָּא", + "meta.comments": "הערות", + "meta.source": "מָקוֹר", + "search.config.pipeline": " ", + "search.placeholder": "לחפש", + "search.result.placeholder": "הקלד כדי להתחיל לחפש", + "search.result.none": "אין מסמכים תואמים", + "search.result.one": "1 מסמך תואם", + "search.result.other": "# מסמך תואם", + "skip.link.title": "דלג לתוכן", + "source.link.title": "עבור אל מאגר", + "source.revision.date": "העדכון אחרון", + "source.file.date.updated": "העדכון אחרון", + "source.file.date.created": "נוצר", + "toc.title": "תוכן העניינים" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/hi.html b/docs/tensorstore_sphinx_material/src/partials/languages/hi.html new file mode 100644 index 000000000..e8669388d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/hi.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "hi", + "clipboard.copy": "क्लिपबोर्ड पर कॉपी करें", + "clipboard.copied": "क्लिपबोर्ड पर कॉपी कर दिया गया", + "edit.link.title": "इस पृष्ठ को संपादित करें", + "footer.previous": "पिछला", + "footer.next": "आगामी", + "meta.comments": "टिप्पणियाँ", + "meta.source": "स्रोत", + "search.config.pipeline": " ", + "search.placeholder": "खोज", + "search.result.placeholder": "खोज शुरू करने के लिए टाइप करें", + "search.result.none": "कोई मिलान डॉक्यूमेंट नहीं", + "search.result.one": "1 मिलान डॉक्यूमेंट", + "search.result.other": "# मिलान डाक्यूमेंट्स", + "skip.link.title": "विषय पर बढ़ें", + "source.link.title": "रिपॉजिटरी पर जाएं", + "source.revision.date": "आखिरी अपडेट", + "source.file.date.updated": "आखिरी अपडेट", + "source.file.date.created": "बनाया था", + "toc.title": "विषय - सूची" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/hr.html b/docs/tensorstore_sphinx_material/src/partials/languages/hr.html new file mode 100644 index 000000000..6fa4118c5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/hr.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "hr", + "clipboard.copy": "Kopirajte u međuspremnik", + "clipboard.copied": "Kopirano u međuspremnik", + "edit.link.title": "Uredi stranicu", + "footer.previous": "Prethodno", + "footer.next": "Sljedeće", + "meta.comments": "Komentari", + "meta.source": "Izvor", + "search.placeholder": "Pretraživanje", + "search.result.placeholder": "Unesite pojam pretraživanja", + "search.result.none": "Ništa nije pronađeno", + "search.result.one": "1 rezultat pretraživanja", + "search.result.other": "# rezultata pretraživanja", + "skip.link.title": "Preskočite na sadržaj", + "source.link.title": "Idite u repozitorij", + "source.revision.date": "Zadnje ažuriranje", + "source.file.date.updated": "Zadnje ažuriranje", + "source.file.date.created": "Stvoreno", + "toc.title": "Sadržaj" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/hu.html b/docs/tensorstore_sphinx_material/src/partials/languages/hu.html new file mode 100644 index 000000000..2eeb3112f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/hu.html @@ -0,0 +1,54 @@ + + + +{% macro t(key) %}{{ { + "language": "hu", + "clipboard.copy": "Másolás vágólapra", + "clipboard.copied": "Vágólapra másolva", + "edit.link.title": "Oldal szerkesztése", + "footer.previous": "Előző", + "footer.next": "Következő", + "footer.title": "Élőláb", + "header.title": "Élőfej", + "meta.comments": "Hozzászólások", + "meta.source": "Forrás", + "nav.title": "Navigáció", + "search.config.lang": "hu", + "search.placeholder": "Keresés", + "search.reset": "Törlés", + "search.result.initializer": "Keresés inicializálása", + "search.result.placeholder": "Kereséshez írj ide valamit", + "search.result.none": "Nincs találat", + "search.result.one": "1 egyező dokumentum", + "search.result.other": "# egyező dokumentum", + "search.result.more.one": "1 további találat az oldalon", + "search.result.more.other": "# további találat az oldalon", + "search.result.term.missing": "Üres", + "skip.link.title": "Kihagyás", + "source.link.title": "Főoldalra ugrás", + "source.revision.date": "Utolsó frissítés", + "source.file.date.updated": "Utolsó frissítés", + "source.file.date.created": "Létrehozva", + "tabs.title": "Lapok", + "toc.title": "Tartalomjegyzék" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/id.html b/docs/tensorstore_sphinx_material/src/partials/languages/id.html new file mode 100644 index 000000000..2d68bd5ff --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/id.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "id", + "clipboard.copy": "Salin ke memori", + "clipboard.copied": "Tersalin ke memori", + "edit.link.title": "Ubah halaman ini", + "footer.previous": "Sebelumnya", + "footer.next": "Selanjutnya", + "meta.comments": "Komentar", + "meta.source": "Sumber", + "search.config.pipeline": " ", + "search.placeholder": "Cari", + "search.result.placeholder": "Ketik untuk mulai pencarian", + "search.result.none": "Tidak ada dokumen yang sesuai", + "search.result.one": "1 dokumen ditemukan", + "search.result.other": "# dokumen ditemukan", + "skip.link.title": "Lewati ke isi", + "source.link.title": "Menuju repositori", + "source.revision.date": "Pembaharuan Terakhir", + "source.revision.date": "Pembaharuan Terakhir", + "source.file.date.created": "Dibuat", + "toc.title": "Daftar isi" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/is.html b/docs/tensorstore_sphinx_material/src/partials/languages/is.html new file mode 100644 index 000000000..b3943e42a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/is.html @@ -0,0 +1,51 @@ + + + +{% macro t(key) %}{{ { + "language": "is", + "clipboard.copy": "Afrita í klemmuspjald", + "clipboard.copied": "Afritað í klemmuspjald", + "edit.link.title": "Ritvinna þessa síðu", + "footer.previous": "Fyrra", + "footer.next": "Næsta", + "footer.title": "Síðufótur", + "header.title": "Haus", + "meta.comments": "Athugasemdir", + "meta.source": "Grunnur", + "nav.title": "Valmynd", + "search.placeholder": "Leit", + "search.reset": "Hreinsa", + "search.result.placeholder": "Sláðu inn til að hefja leit", + "search.result.none": "Engin skjöl fundust", + "search.result.one": "1 skjal fannst", + "search.result.other": "# skjöl fundust", + "search.result.more.one": "1 til viðbótar á þessari síðu", + "search.result.more.other": "# til viðbótar á þessari síðu", + "skip.link.title": "Hoppa yfir í efni", + "source.link.title": "Fara í gagnahirslu (e. repository)", + "source.revision.date": "Síðasta uppfærsla", + "source.file.date.updated": "Síðasta uppfærsla", + "source.file.date.created": "Búið til", + "tabs.title": "Flipar", + "toc.title": "Efnisyfirlit" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/it.html b/docs/tensorstore_sphinx_material/src/partials/languages/it.html new file mode 100644 index 000000000..6be33aac0 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/it.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "it", + "clipboard.copy": "Copia", + "clipboard.copied": "Copiato", + "edit.link.title": "Modifica", + "footer.previous": "Precedente", + "footer.next": "Prossimo", + "meta.comments": "Commenti", + "meta.source": "Sorgente", + "search.config.lang": "it", + "search.placeholder": "Cerca", + "search.result.placeholder": "Scrivi per iniziare a cercare", + "search.result.none": "Nessun documento trovato", + "search.result.one": "1 documento trovato", + "search.result.other": "# documenti trovati", + "skip.link.title": "Vai al contenuto", + "source.link.title": "Apri repository", + "source.revision.date": "Ultimo aggiornamento", + "source.file.date.updated": "Ultimo aggiornamento", + "source.file.date.created": "Creata", + "toc.title": "Indice" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ja.html b/docs/tensorstore_sphinx_material/src/partials/languages/ja.html new file mode 100644 index 000000000..6710293b6 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ja.html @@ -0,0 +1,56 @@ + + + +{% macro t(key) %}{{ { + "language": "ja", + "clipboard.copy": "クリップボードへコピー", + "clipboard.copied": "コピーしました", + "edit.link.title": "編集", + "footer.previous": "前", + "footer.next": "次", + "footer.title": "フッター", + "header.title": "ヘッダー", + "meta.comments": "コメント", + "meta.source": "ソース", + "nav.title": "ナビゲーション", + "search.config.lang": "ja", + "search.config.pipeline": "trimmer, stemmer", + "search.config.separator": "[\s\- 、。,.]+", + "search.placeholder": "検索", + "search.reset": "クリア", + "search.result.initializer": "検索を初期化", + "search.result.placeholder": "検索キーワードを入力してください", + "search.result.none": "何も見つかりませんでした", + "search.result.one": "1件見つかりました", + "search.result.other": "#件見つかりました", + "search.result.more.one": "このページ内にもう1件見つかりました", + "search.result.more.other": "このページ内にあと#件見つかりました", + "search.result.term.missing": "検索に含まれない", + "skip.link.title": "コンテンツにスキップ", + "source.link.title": "リポジトリへ", + "source.revision.date": "最終更新日", + "source.file.date.updated": "最終更新日", + "source.file.date.created": "作成した", + "tabs.title": "タブ", + "toc.title": "目次" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ka.html b/docs/tensorstore_sphinx_material/src/partials/languages/ka.html new file mode 100644 index 000000000..c3711a486 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ka.html @@ -0,0 +1,50 @@ + + + +{% macro t(key) %}{{ { + "language": "ka", + "clipboard.copy": "კოპირება", + "clipboard.copied": "კოპირებულია", + "edit.link.title": "გვერდის რედარქირება", + "footer.previous": "წინა", + "footer.next": "შემდეგი", + "meta.comments": "კომენტარები", + "meta.source": "წყარო", + "nav.title": "ნავიგაცია", + "search.config.pipeline": " ", + "search.placeholder": "ძებნა", + "search.reset": "გასუფთავება", + "search.result.placeholder": "ჩაწერე ძებნის დასაწყებად", + "search.result.none": "დოკუმენტი ვერ მოიძებნა", + "search.result.one": "მოიძებნა 1 დოკუმენტი", + "search.result.other": "მოიძებნა # დოკუმენტი", + "search.result.more.one": "კიდევ 1 ამ გვერდზე", + "search.result.more.other": "კიდევ # ამ გვერდზე", + "skip.link.title": "კონტენტზე გადასვლა", + "source.link.title": "საცავში გადასვლა", + "source.revision.date": "ბოლო განახლება", + "source.file.date.updated": "ბოლო განახლება", + "source.file.date.created": "შეიქმნა", + "tabs.title": "ტაბები", + "toc.title": "სარჩევი" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/kr.html b/docs/tensorstore_sphinx_material/src/partials/languages/kr.html new file mode 100644 index 000000000..f8de2f2d2 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/kr.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "kr", + "clipboard.copy": "클립보드로 복사", + "clipboard.copied": "클립보드에 복사됨", + "edit.link.title": "이 페이지를 편집", + "footer.previous": "이전", + "footer.next": "다음", + "meta.comments": "댓글", + "meta.source": "출처", + "search.config.pipeline": " ", + "search.placeholder": "검색", + "search.result.placeholder": "검색어를 입력하세요", + "search.result.none": "검색어와 일치하는 문서가 없습니다", + "search.result.one": "1개의 일치하는 문서", + "search.result.other": "#개의 일치하는 문서", + "source.link.title": "저장소로 이동", + "source.revision.date": "마지막 업데이트", + "source.file.date.updated": "마지막 업데이트", + "source.file.date.created": "만들어진", + "toc.title": "목차" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/my.html b/docs/tensorstore_sphinx_material/src/partials/languages/my.html new file mode 100644 index 000000000..aa52360dd --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/my.html @@ -0,0 +1,50 @@ + + + +{% macro t(key) %}{{ { + "language": "my", + "clipboard.copy": "ကလစ်ဘုတ် သို့ ကူးယူရန်", + "clipboard.copied": "ကလစ်ဘုတ် သို့ ကူယူပြီး", + "edit.link.title": "ဤ စာမျက်နှာကို ပြင်ရန်", + "footer.previous": "နောက်သို့", + "footer.next": "ရှေ့သို့", + "footer.title": "အောက်ခြေ", + "header.title": "ခေါင်းပိုင်း", + "meta.comments": "မှတ်ချက်များ", + "meta.source": "ရင်းမြစ်", + "nav.title": "လမ်းညွှန်", + "search.config.pipeline": " ", + "search.placeholder": "ရှာရန်", + "search.reset": "ရှင်းလင်း", + "search.result.placeholder": "ရှာဖွေခြင်းစရန် စာရိုက်ပါ", + "search.result.none": "တူညီသော စာရွက်စာတမ်းများ မရှိပါ", + "search.result.one": "စာရွက်စာတမ်း ၁ ခု တူညီသည်", + "search.result.other": "စာရွက်စာတမ်း # ခု တူညီသည်", + "skip.link.title": "မာတိကာ သို့ သွားရန်", + "source.link.title": "repository သို့ သွားရန်", + "source.revision.date": "နောက်ဆုံး ထုတ်ပြန်ချက်", + "source.file.date.updated": "နောက်ဆုံး ထုတ်ပြန်ချက်", + "source.file.date.created": "နေပြည်တော်", + "tabs.title": "တက်များ", + "toc.title": "ပါဝင်အကြောင်းအရာများ" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/nl.html b/docs/tensorstore_sphinx_material/src/partials/languages/nl.html new file mode 100644 index 000000000..2b3920436 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/nl.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "nl", + "clipboard.copy": "Kopiëren naar klembord", + "clipboard.copied": "Gekopieerd naar klembord", + "edit.link.title": "Wijzig deze pagina", + "footer.previous": "Vorige", + "footer.next": "Volgende", + "meta.comments": "Reacties", + "meta.source": "Bron", + "search.config.lang": "nl", + "search.placeholder": "Zoeken", + "search.result.placeholder": "Typ om te beginnen met zoeken", + "search.result.none": "Geen overeenkomende documenten", + "search.result.one": "1 overeenkomende document", + "search.result.other": "# overeenkomende documenten", + "skip.link.title": "Ga naar inhoud", + "source.link.title": "Ga naar repository", + "source.revision.date": "Laatst geüpdatet", + "source.file.date.updated": "Laatst geüpdatet", + "source.file.date.created": "Gecreëerd", + "toc.title": "Inhoudsopgave" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/nn.html b/docs/tensorstore_sphinx_material/src/partials/languages/nn.html new file mode 100644 index 000000000..b7f83b3c9 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/nn.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "nn", + "clipboard.copy": "Kopier til utklippstavla", + "clipboard.copied": "Kopiert til utklippstavla", + "edit.link.title": "Rediger denne sida", + "footer.previous": "Førre", + "footer.next": "Neste", + "meta.comments": "Kommentarar", + "meta.source": "Kjelde", + "search.config.lang": "no", + "search.placeholder": "Søk", + "search.result.placeholder": "Skriv søkeord", + "search.result.none": "Ingen treff", + "search.result.one": "1 treff", + "search.result.other": "# treff", + "skip.link.title": "Gå til innhald", + "source.link.title": "Gå til kjelde", + "source.revision.date": "Siste oppdatering", + "source.file.date.updated": "Siste oppdatering", + "source.file.date.created": "Laget", + "toc.title": "Innhaldsliste" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/no.html b/docs/tensorstore_sphinx_material/src/partials/languages/no.html new file mode 100644 index 000000000..b034679f4 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/no.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "no", + "clipboard.copy": "Kopier til utklippstavlen", + "clipboard.copied": "Kopiert til utklippstavlen", + "edit.link.title": "Rediger denne siden", + "footer.previous": "Forrige", + "footer.next": "Neste", + "meta.comments": "Kommentarer", + "meta.source": "Kilde", + "search.config.lang": "no", + "search.placeholder": "Søk", + "search.result.placeholder": "Skriv søkeord", + "search.result.none": "Ingen treff", + "search.result.one": "1 treff", + "search.result.other": "# treff", + "skip.link.title": "Gå til innhold", + "source.link.title": "Gå til kilde", + "source.revision.date": "Siste oppdatering", + "source.file.date.updated": "Siste oppdatering", + "source.file.date.created": "Created", + "toc.title": "Innholdsfortegnelse" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/pl.html b/docs/tensorstore_sphinx_material/src/partials/languages/pl.html new file mode 100644 index 000000000..cdb7ba3ab --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/pl.html @@ -0,0 +1,54 @@ + + + +{% macro t(key) %}{{ { + "language": "pl", + "clipboard.copy": "Kopiuj do schowka", + "clipboard.copied": "Skopiowane", + "edit.link.title": "Edytuj tę stronę", + "footer.previous": "Poprzednia strona", + "footer.next": "Następna strona", + "footer.title": "Stopka", + "header.title": "Nagłówek", + "meta.comments": "Komentarze", + "meta.source": "Kod źródłowy", + "search.config.pipeline": " ", + "nav.title": "Nawigacja", + "search.placeholder": "Szukaj", + "search.reset": "Wyczyść", + "search.result.initializer": "Inicjowanie wyszukiwania", + "search.result.placeholder": "Zacznij pisać, aby szukać", + "search.result.none": "Brak wyników wyszukiwania", + "search.result.one": "Wyniki wyszukiwania: 1", + "search.result.other": "Wyniki wyszukiwania: #", + "search.result.more.one": "1 więcej na tej stronie", + "search.result.more.other": "# więcej na tej stronie", + "search.result.term.missing": "Brak", + "skip.link.title": "Przejdź do treści", + "source.link.title": "Idź do repozytorium", + "source.revision.date": "Ostatnia aktualizacja", + "source.file.date.updated": "Ostatnia aktualizacja", + "source.file.date.created": "Utworzony", + "tabs.title": "Zakładki", + "toc.title": "Spis treści" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/pt.html b/docs/tensorstore_sphinx_material/src/partials/languages/pt.html new file mode 100644 index 000000000..6b96d30cd --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/pt.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "pt", + "clipboard.copy": "Copiar para área de transferência", + "clipboard.copied": "Copiado para área de transferência", + "edit.link.title": "Editar esta página", + "footer.previous": "Anterior", + "footer.next": "Próximo", + "meta.comments": "Comentários", + "meta.source": "Fonte", + "search.config.lang": "pt", + "search.placeholder": "Buscar", + "search.result.placeholder": "Digite para iniciar a busca", + "search.result.none": "Nenhum resultado encontrado", + "search.result.one": "1 resultado encontrado", + "search.result.other": "# resultados encontrados", + "skip.link.title": "Ir para o conteúdo", + "source.link.title": "Ir ao repositório", + "source.revision.date": "Última atualização", + "source.file.date.updated": "Última atualização", + "source.file.date.created": "Criada", + "toc.title": "Índice" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ro.html b/docs/tensorstore_sphinx_material/src/partials/languages/ro.html new file mode 100644 index 000000000..caac19f2b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ro.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "ro", + "clipboard.copy": "Copiază în clipboard", + "clipboard.copied": "Copiat în clipboard", + "edit.link.title": "Editeaza această pagină", + "footer.previous": "Anterior", + "footer.next": "Următor", + "meta.comments": "Comentarii", + "meta.source": "Sursă", + "search.config.lang": "ro", + "search.placeholder": "Căutare", + "search.result.placeholder": "Tastează pentru a începe căutarea", + "search.result.none": "Nu a fost găsit niciun document", + "search.result.one": "1 document găsit", + "search.result.other": "# documente găsite", + "skip.link.title": "Sari la conținut", + "source.link.title": "Accesează repository-ul", + "source.revision.date": "Ultima actualizare", + "source.file.date.updated": "Ultima actualizare", + "source.file.date.created": "Creată", + "toc.title": "Cuprins" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/ru.html b/docs/tensorstore_sphinx_material/src/partials/languages/ru.html new file mode 100644 index 000000000..69dd7ce2b --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/ru.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "ru", + "clipboard.copy": "Копировать в буфер", + "clipboard.copied": "Скопировано в буфер", + "edit.link.title": "Редактировать страницу", + "footer.previous": "Назад", + "footer.next": "Вперед", + "meta.comments": "Комментарии", + "meta.source": "Исходный код", + "search.config.lang": "ru", + "search.placeholder": "Поиск", + "search.result.placeholder": "Начните печатать для поиска", + "search.result.none": "Совпадений не найдено", + "search.result.one": "Найдено 1 совпадение", + "search.result.other": "Найдено # совпадений", + "skip.link.title": "Перейти к содержанию", + "source.link.title": "Перейти к репозиторию", + "source.revision.date": "Последнее обновление", + "source.file.date.updated": "Последнее обновление", + "source.file.date.created": "Созданный", + "toc.title": "Содержание" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/sh.html b/docs/tensorstore_sphinx_material/src/partials/languages/sh.html new file mode 100644 index 000000000..2f64e5064 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/sh.html @@ -0,0 +1,43 @@ + + + +{% macro t(key) %}{{ { + "language": "sh", + "clipboard.copy": "Kopiraj u klipbord", + "clipboard.copied": "Iskopirano u klipbord", + "edit.link.title": "Uredi stranicu", + "footer.previous": "Prethodno", + "footer.next": "Sledeće", + "meta.comments": "Komentari", + "meta.source": "Izvor", + "search.placeholder": "Pretraga", + "search.result.placeholder": "Unesite pojam pretrage", + "search.result.none": "Ništa nije pronađeno", + "search.result.one": "1 rezultat pretrage", + "search.result.other": "# rezultata pretrage", + "skip.link.title": "Idi na tekst", + "source.link.title": "Idi u repozitorijum", + "source.file.date.updated": "Ажуриран", + "source.file.date.created": "Створено", + "toc.title": "Sadržaj" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/si.html b/docs/tensorstore_sphinx_material/src/partials/languages/si.html new file mode 100644 index 000000000..2dc89dcde --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/si.html @@ -0,0 +1,52 @@ + + + +{% macro t(key) %}{{ { + "language": "si", + "clipboard.copy": "කොපි කරන්න", + "clipboard.copied": "කොපි කළා", + "edit.link.title": "පිටුව සංස්කරණය", + "footer.previous": "පසුගිය", + "footer.next": "මීළඟ", + "footer.title": "පාදම", + "header.title": "ශීර්ෂය", + "meta.comments": "ප්‍රතිචාර", + "meta.source": "මූලාශ්‍රය", + "nav.title": "යාත්‍රණය", + "search.config.pipeline": " ", + "search.placeholder": "සොයන්න", + "search.reset": "මකන්න", + "search.result.placeholder": "සෙවීමට ටයිප් කරන්න", + "search.result.none": "කිසිවක් හමු නොවුණි", + "search.result.one": "1 ගැලපෙන ගොනුවක්", + "search.result.other": "ගැලපෙන ගොනු # ක්", + "search.result.more.one": "තව 1 ප්‍රතිඵලයක්", + "search.result.more.other": "තව ප්‍රතිඵල # ක්", + "skip.link.title": "අන්තර්ගතය වෙත යන්න", + "source.link.title": "රිපොසිටරියට යන්න", + "source.revision.date": "අවසන් යාවත්කාලීන වීම", + "source.file.date.updated": "අවසන් යාවත්කාලීන වීම", + "source.file.date.created": "ٺاھيو ويو", + "tabs.title": "ටැබ්ස්", + "toc.title": "පටුන" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/sk.html b/docs/tensorstore_sphinx_material/src/partials/languages/sk.html new file mode 100644 index 000000000..21d4870d9 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/sk.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "sk", + "clipboard.copy": "Kopírovať do schránky", + "clipboard.copied": "Skopírované do schránky", + "edit.link.title": "Upraviť túto stránku", + "footer.previous": "Späť", + "footer.next": "Ďalej", + "meta.comments": "Komentáre", + "meta.source": "Zdroj", + "search.placeholder": "Hľadať", + "search.result.placeholder": "Pre vyhľadávanie začni písať", + "search.result.none": "Žiadne vyhovujúce dokumenty", + "search.result.one": "Vyhovujúci dokument: 1", + "search.result.other": "Vyhovujúce dokumenty: #", + "skip.link.title": "Preskočiť na obsah", + "source.link.title": "Zobraziť repozitár", + "source.revision.date": "Posledná aktualizácia", + "source.file.date.updated": "Posledná aktualizácia", + "source.file.date.created": "Vytvorené", + "toc.title": "Obsah" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/sl.html b/docs/tensorstore_sphinx_material/src/partials/languages/sl.html new file mode 100644 index 000000000..312bcb851 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/sl.html @@ -0,0 +1,44 @@ + + + +{% macro t(key) %}{{ { + "language": "sl", + "clipboard.copy": "Kopiraj v odložišče", + "clipboard.copied": "Kopirano v odložišče", + "edit.link.title": "Uredi stran", + "footer.previous": "Prejšnja stran", + "footer.next": "Naslednja stran", + "meta.comments": "Komentarji", + "meta.source": "Izvorna koda", + "search.placeholder": "Išči", + "search.result.placeholder": "Vpiši iskalni niz", + "search.result.none": "Ni zadetkov", + "search.result.one": "1 zadetek", + "search.result.other": "# zadetkov", + "skip.link.title": "Skoči na vsebino", + "source.link.title": "Pojdi na repozitorij", + "source.revision.date": "Zadnja posodobitev", + "source.file.date.updated": "Zadnja posodobitev", + "source.file.date.created": "Ustvarjeno", + "toc.title": "Kazalo" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/sr.html b/docs/tensorstore_sphinx_material/src/partials/languages/sr.html new file mode 100644 index 000000000..776eede6d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/sr.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "sr", + "clipboard.copy": "Копирај у клипборд", + "clipboard.copied": "Ископирано у клипборд", + "edit.link.title": "Уреди страницу", + "footer.previous": "Претходно", + "footer.next": "Следеће", + "meta.comments": "Коментари", + "meta.source": "Извор", + "search.config.lang": "ru", + "search.placeholder": "Претрага", + "search.result.placeholder": "Унесите појам претраге", + "search.result.none": "Нису пронађени документи", + "search.result.one": "1 резултат претраге", + "search.result.other": "# резултата претраге", + "skip.link.title": "Иди на текст", + "source.link.title": "Иди у репозиторијум", + "source.revision.date": "Последња исправка", + "source.file.date.updated": "Последња исправка", + "source.file.date.created": "Створено", + "toc.title": "Садржај" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/sv.html b/docs/tensorstore_sphinx_material/src/partials/languages/sv.html new file mode 100644 index 000000000..d2f09d871 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/sv.html @@ -0,0 +1,54 @@ + + + +{% macro t(key) %}{{ { + "language": "sv", + "clipboard.copy": "Kopiera till urklipp", + "clipboard.copied": "Kopierat till urklipp", + "edit.link.title": "Redigera sidan", + "footer.previous": "Föregående", + "footer.next": "Nästa", + "footer.title": "Sidfot", + "header.title": "Sidhuvud", + "meta.comments": "Kommentarer", + "meta.source": "Källa", + "nav.title": "Navigation", + "search.config.lang": "sv", + "search.placeholder": "Sök", + "search.reset": "Rensa", + "search.result.initializer": "Initialiserar sök", + "search.result.placeholder": "Skriv sökord", + "search.result.none": "Inga sökresultat", + "search.result.one": "1 sökresultat", + "search.result.other": "# sökresultat", + "search.result.more.one": "1 till på denna sidan", + "search.result.more.other": "# till på denna sidan", + "search.result.term.missing": "Saknas", + "skip.link.title": "Gå till innehållet", + "source.link.title": "Gå till datakatalog", + "source.revision.date": "Senaste uppdateringen", + "source.file.date.updated": "Senaste uppdateringen", + "source.file.date.created": "Skapad", + "tabs.title": "Flikar", + "toc.title": "Innehållsförteckning" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/th.html b/docs/tensorstore_sphinx_material/src/partials/languages/th.html new file mode 100644 index 000000000..2c8c059d2 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/th.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "th", + "clipboard.copy": "คัดลอก", + "clipboard.copied": "คัดลอกแล้ว", + "edit.link.title": "แก้ไขหน้านี้", + "footer.previous": "ก่อนหน้า", + "footer.next": "ต่อไป", + "meta.comments": "ความคิดเห็น", + "meta.source": "แหล่งที่มา", + "search.config.pipeline": " ", + "search.placeholder": "ค้นหา", + "search.result.placeholder": "พิมพ์เพื่อเริ่มค้นหา", + "search.result.none": "ไม่พบเอกสารที่ตรงกัน", + "search.result.one": "พบเอกสารที่ตรงกัน", + "search.result.other": "พบ # เอกสารที่ตรงกัน", + "skip.link.title": "ข้ามไปที่เนื้อหา", + "source.link.title": "ไปที่ Repository", + "source.revision.date": "สร้าง", + "source.file.date.updated": "สร้าง", + "source.file.date.created": "สร้าง", + "toc.title": "สารบัญ" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/tr.html b/docs/tensorstore_sphinx_material/src/partials/languages/tr.html new file mode 100644 index 000000000..28645b883 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/tr.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "tr", + "clipboard.copy": "Kopyala", + "clipboard.copied": "Kopyalandı", + "edit.link.title": "Düzenle", + "footer.previous": "Önceki", + "footer.next": "Sonraki", + "meta.comments": "Yorumlar", + "meta.source": "Kaynak", + "search.config.lang": "tr", + "search.placeholder": "Ara", + "search.result.placeholder": "Aramaya başlamak için yazın", + "search.result.none": "Eşleşen doküman bulunamadı", + "search.result.one": "1 doküman bulundu", + "search.result.other": "# doküman bulundu", + "skip.link.title": "Ana içeriğe geç", + "source.link.title": "Depoya git", + "source.revision.date": "Son Güncelleme", + "source.file.date.updated": "Son Güncelleme", + "source.file.date.created": "Oluşturuldu", + "toc.title": "İçindekiler" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/uk.html b/docs/tensorstore_sphinx_material/src/partials/languages/uk.html new file mode 100644 index 000000000..266b0c93d --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/uk.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "uk", + "clipboard.copy": "Скопіювати в буфер", + "clipboard.copied": "Скопійовано в буфер", + "edit.link.title": "Редагувати сторінку", + "footer.previous": "Назад", + "footer.next": "Вперед", + "meta.comments": "Коментарі", + "meta.source": "Вихідний код", + "search.config.lang": "ru", + "search.placeholder": "Пошук", + "search.result.placeholder": "Розпочніть писати для пошуку", + "search.result.none": "Збігів не знайдено", + "search.result.one": "Знайдено 1 збіг", + "search.result.other": "Знайдено # збігів", + "skip.link.title": "Перейти до змісту", + "source.link.title": "Перейти до репозиторію", + "source.revision.date": "Останнє оновлення", + "source.file.date.updated": "Останнє оновлення", + "source.file.date.created": "Створено", + "toc.title": "Зміст" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/vi.html b/docs/tensorstore_sphinx_material/src/partials/languages/vi.html new file mode 100644 index 000000000..006eb6c5a --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/vi.html @@ -0,0 +1,45 @@ + + + +{% macro t(key) %}{{ { + "language": "vi", + "clipboard.copy": "Sao chép vào bộ nhớ", + "clipboard.copied": "Sao chép xong", + "edit.link.title": "Chỉnh sửa", + "footer.previous": "Trước", + "footer.next": "Sau", + "meta.comments": "Bình luận", + "meta.source": "Mã nguồn", + "search.config.lang": "vi", + "search.placeholder": "Tìm kiếm", + "search.result.placeholder": "Nhập để bắt đầu tìm kiếm", + "search.result.none": "Không tìm thấy tài liệu liên quan", + "search.result.one": "1 tài liệu liên quan", + "search.result.other": "# tài liệu liên quan", + "skip.link.title": "Vào thẳng nội dung", + "source.link.title": "Đến kho lưu trữ mã nguồn", + "source.revision.date": "Cập nhật cuối cùng", + "source.file.date.updated": "Cập nhật cuối cùng", + "source.file.date.created": "Tạo", + "toc.title": "Mục lục" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/zh-Hant.html b/docs/tensorstore_sphinx_material/src/partials/languages/zh-Hant.html new file mode 100644 index 000000000..4d52fdd88 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/zh-Hant.html @@ -0,0 +1,48 @@ + + + +{% macro t(key) %}{{ { + "language": "zh-Hant", + "clipboard.copy": "拷貝", + "clipboard.copied": "已拷貝", + "edit.link.title": "編輯此頁", + "footer.previous": "上一頁", + "footer.next": "下一頁", + "meta.comments": "評論", + "meta.source": "來源", + "search.config.lang": "ja", + "search.config.pipeline": "trimmer, stemmer", + "search.config.separator": "[\,\。]+", + "search.placeholder": "搜尋", + "search.result.initializer": "正在初始化搜尋引擎", + "search.result.placeholder": "鍵入以開始檢索", + "search.result.none": "沒有找到符合條件的結果", + "search.result.one": "找到 1 个符合條件的結果", + "search.result.other": "# 個符合條件的結果", + "skip.link.title": "跳轉至", + "source.link.title": "前往 GitHub 倉庫", + "source.revision.date": "最後更新", + "source.file.date.updated": "最後更新", + "source.file.date.created": "已建立", + "toc.title": "目錄" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/zh-TW.html b/docs/tensorstore_sphinx_material/src/partials/languages/zh-TW.html new file mode 100644 index 000000000..f69f28232 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/zh-TW.html @@ -0,0 +1,48 @@ + + + +{% macro t(key) %}{{ { + "language": "zh-Hant", + "clipboard.copy": "複製", + "clipboard.copied": "已複製", + "edit.link.title": "編輯此頁", + "footer.previous": "上一頁", + "footer.next": "下一頁", + "meta.comments": "留言", + "meta.source": "來源", + "search.config.lang": "ja", + "search.config.pipeline": "trimmer, stemmer", + "search.config.separator": "[\s\- 、。,.?;]+", + "search.placeholder": "搜尋", + "search.result.initializer": "正在初始化搜尋引擎", + "search.result.placeholder": "打字進行搜尋", + "search.result.none": "沒有符合的項目", + "search.result.one": "找到 1 個符合的項目", + "search.result.other": "找到 # 個符合的項目", + "skip.link.title": "跳轉到", + "source.link.title": "前往倉庫", + "source.revision.date": "最後更新", + "source.file.date.updated": "最後更新", + "source.file.date.created": "สร้าง", + "toc.title": "目錄" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/languages/zh.html b/docs/tensorstore_sphinx_material/src/partials/languages/zh.html new file mode 100644 index 000000000..592976fdf --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/languages/zh.html @@ -0,0 +1,48 @@ + + + +{% macro t(key) %}{{ { + "language": "zh", + "clipboard.copy": "复制", + "clipboard.copied": "已复制", + "edit.link.title": "编辑此页", + "footer.previous": "上一页", + "footer.next": "下一页", + "meta.comments": "评论", + "meta.source": "来源", + "search.config.lang": "ja", + "search.config.pipeline": "trimmer, stemmer", + "search.config.separator": "[\,\。]+", + "search.placeholder": "搜索", + "search.result.initializer": "正在初始化搜索引擎", + "search.result.placeholder": "键入以开始搜索", + "search.result.none": "没有找到符合条件的结果", + "search.result.one": "找到 1 个符合条件的结果", + "search.result.other": "# 个符合条件的结果", + "skip.link.title": "跳转至", + "source.link.title": "前往 GitHub 仓库", + "source.revision.date": "最后更新", + "source.file.date.updated": "最后更新", + "source.file.date.created": "已建立", + "toc.title": "目录" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/logo.html b/docs/tensorstore_sphinx_material/src/partials/logo.html new file mode 100644 index 000000000..d8d53d857 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/logo.html @@ -0,0 +1,31 @@ + + + +{%- if logo -%} + logo +{%- elif theme_logo_svg -%} + {%- include theme_logo_svg -%} +{%- else %} + {% set icon = config.theme.icon.logo or "material/library" %} + {% include ".icons/" ~ icon ~ ".svg" %} +{%- endif -%} diff --git a/docs/tensorstore_sphinx_material/src/partials/nav-item.html b/docs/tensorstore_sphinx_material/src/partials/nav-item.html new file mode 100644 index 000000000..39ab331b4 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/nav-item.html @@ -0,0 +1,135 @@ + + + +{% macro render(nav_item, path, level) %} + + + {% set class = "md-nav__item" %} + {% if nav_item.active %} + {% set class = class ~ " md-nav__item--active" %} + {% endif %} + + + {% if nav_item.children %} + + + {% if "navigation.sections" in features and level == 1 + ( + "navigation.tabs" in features + ) %} + {% set class = class ~ " md-nav__item--section" %} + {% endif %} + + +
  • + + + {% set checked = "checked" if nav_item.active %} + {% if "navigation.expand" in features and not checked %} + + {% else %} + + {% endif %} + + + + +
  • + + + {% elif nav_item.active %} +
  • + {% set toc = page.toc %} + + + + + + {% if toc | first is defined %} + + {% endif %} + + {{ nav_item.title }} + + + + {% if toc | first is defined %} + {% include "partials/toc.html" %} + {% endif %} +
  • + + + {% else %} +
  • + + {{ nav_item.title }} + +
  • + {% endif %} +{% endmacro %} + + +{{ render(nav_item, path, level) }} diff --git a/docs/tensorstore_sphinx_material/src/partials/nav.html b/docs/tensorstore_sphinx_material/src/partials/nav.html new file mode 100644 index 000000000..d64112ff5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/nav.html @@ -0,0 +1,68 @@ + + + +{% set class = "md-nav md-nav--primary" %} +{% if "navigation.tabs" in features %} + {% set class = class ~ " md-nav--lifted" %} +{% endif %} +{% if "toc.integrate" in features %} + {% set class = class ~ " md-nav--integrated" %} +{% endif %} + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/palette.html b/docs/tensorstore_sphinx_material/src/partials/palette.html new file mode 100644 index 000000000..5ee428b0f --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/palette.html @@ -0,0 +1,66 @@ + + + +{% macro primary(key) %}{{ { + "red": "#ef5552", + "pink": "#e92063", + "purple": "#ab47bd", + "deep-purple": "#7e56c2", + "indigo": "#4051b5", + "blue": "#2094f3", + "light-blue": "#02a6f2", + "cyan": "#00bdd6", + "teal": "#009485", + "green": "#4cae4f", + "light-green": "#8bc34b", + "lime": "#cbdc38", + "yellow": "#ffec3d", + "amber": "#ffc105", + "orange": "#ffa724", + "deep-orange": "#ff6e42", + "brown": "#795649", + "grey": "#757575", + "blue-grey": "#546d78", + "black": "#000000", + "white": "#ffffff" +}[key] }}{% endmacro %} + + +{% macro accent(key) %}{{ { + "red": "#ff1a47", + "pink": "#f50056", + "purple": "#df41fb", + "deep-purple": "#7c4dff", + "indigo": "#526cfe", + "blue": "#4287ff", + "light-blue": "#0091eb", + "cyan": "#00bad6", + "teal": "#00bda4", + "green": "#00c753", + "light-green": "#63de17", + "lime": "#b0eb00", + "yellow": "#ffd500", + "amber": "#ffaa00", + "orange": "#ff9100", + "deep-orange": "#ff6e42" +}[key] }}{% endmacro %} diff --git a/docs/tensorstore_sphinx_material/src/partials/search.html b/docs/tensorstore_sphinx_material/src/partials/search.html new file mode 100644 index 000000000..cc9a97fe2 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/search.html @@ -0,0 +1,68 @@ + + +{% import "partials/language.html" as lang with context %} + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/social.html b/docs/tensorstore_sphinx_material/src/partials/social.html new file mode 100644 index 000000000..1dddddcff --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/social.html @@ -0,0 +1,42 @@ + + + +{% if config.extra.social %} + +{% endif %} diff --git a/docs/tensorstore_sphinx_material/src/partials/source-date.html b/docs/tensorstore_sphinx_material/src/partials/source-date.html new file mode 100644 index 000000000..b1b86ab57 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/source-date.html @@ -0,0 +1,24 @@ + + + +{% include "partials/source-file.html" %} diff --git a/docs/tensorstore_sphinx_material/src/partials/source-file.html b/docs/tensorstore_sphinx_material/src/partials/source-file.html new file mode 100644 index 000000000..d1ce0b667 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/source-file.html @@ -0,0 +1,44 @@ + + +{% import "partials/language.html" as lang with context %} + + +{% set label = lang.t("source.file.date.updated") %} +
    +
    + + + + {% if page.meta.git_revision_date_localized %} + {{ label }}: {{ page.meta.git_revision_date_localized }} + + {% if page.meta.git_creation_date_localized %} +
    {{ lang.t("source.file.date.created") }}: {{ page.meta.git_creation_date_localized }} + {% endif %} + + + {% elif page.meta.revision_date %} + {{ label }}: {{ page.meta.revision_date }} + {% endif %} +
    +
    diff --git a/docs/tensorstore_sphinx_material/src/partials/source.html b/docs/tensorstore_sphinx_material/src/partials/source.html new file mode 100644 index 000000000..a69244a01 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/source.html @@ -0,0 +1,39 @@ + + +{% import "partials/language.html" as lang with context %} + + + +
    + {% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %} + {% include ".icons/" ~ icon ~ ".svg" %} +
    +
    + {{ config.repo_name }} +
    +
    diff --git a/docs/tensorstore_sphinx_material/src/partials/tabs-item.html b/docs/tensorstore_sphinx_material/src/partials/tabs-item.html new file mode 100644 index 000000000..5c9fd1105 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/tabs-item.html @@ -0,0 +1,56 @@ + + + +{% if not class %} + {% set class = "md-tabs__link" %} + {% if nav_item.active %} + {% set class = class ~ " md-tabs__link--active" %} + {% endif %} +{% endif %} + + +{% if nav_item.children %} + {% set atitle = atitle | d(nav_item.title) %} + {% set nav_item = nav_item.children | first %} + + + {% if nav_item.children %} + {% include "partials/tabs-item.html" %} + + + {% else %} +
  • + + {{ atitle }} + +
  • + {% endif %} + + +{% else %} +
  • + + {{ nav_item.title }} + +
  • +{% endif %} diff --git a/docs/tensorstore_sphinx_material/src/partials/tabs.html b/docs/tensorstore_sphinx_material/src/partials/tabs.html new file mode 100644 index 000000000..325a14597 --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/tabs.html @@ -0,0 +1,39 @@ + + + +{% set class = "" %} + + + diff --git a/docs/tensorstore_sphinx_material/src/partials/toc-item.html b/docs/tensorstore_sphinx_material/src/partials/toc-item.html new file mode 100644 index 000000000..28d68abbd --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/toc-item.html @@ -0,0 +1,39 @@ + + + +
  • + + {{ toc_item.title }} + + + + {% if toc_item.children %} + + {% endif %} +
  • diff --git a/docs/tensorstore_sphinx_material/src/partials/toc.html b/docs/tensorstore_sphinx_material/src/partials/toc.html new file mode 100644 index 000000000..d916898aa --- /dev/null +++ b/docs/tensorstore_sphinx_material/src/partials/toc.html @@ -0,0 +1,43 @@ + + +{% import "partials/language.html" as lang with context %} + + + diff --git a/docs/tensorstore_sphinx_material/tsconfig.json b/docs/tensorstore_sphinx_material/tsconfig.json new file mode 100644 index 000000000..66b8d8c93 --- /dev/null +++ b/docs/tensorstore_sphinx_material/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "alwaysStrict": true, + "baseUrl": "src", + "esModuleInterop": true, + "jsx": "react", + "jsxFactory": "h", + "lib": [ + "dom", + "es2017", + "esnext", + ], + "module": "commonjs", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "paths": { + "~/*": ["./assets/javascripts/*"], + }, + "removeComments": false, + "sourceMap": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "stripInternal": true, + "target": "es2015" + }, + "include": [ + "tools", + "src/assets/javascripts", + "typings", + ] +} diff --git a/docs/tensorstore_sphinx_material/typings/_/index.d.ts b/docs/tensorstore_sphinx_material/typings/_/index.d.ts new file mode 100644 index 000000000..9d0ff65f5 --- /dev/null +++ b/docs/tensorstore_sphinx_material/typings/_/index.d.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import { Observable, Subject } from "rxjs" + +import { Keyboard, Viewport } from "~/browser" +import { Component } from "~/components" +import { + SearchTransformFn +} from "~/integrations" + +/* ---------------------------------------------------------------------------- + * Global types + * ------------------------------------------------------------------------- */ + +/** + * Global search configuration + */ +export interface GlobalSearchConfig { + transform?: SearchTransformFn /* Transformation function */ +} + +/* ------------------------------------------------------------------------- */ + +declare global { + + /** + * Global search configuration + */ + const __search: GlobalSearchConfig | undefined + + /** + * Global function to prefix storage items + */ + function __prefix(key: string): string + + /** + * Google Analytics + */ + function ga(...args: string[]): void +} + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +declare global { + var document$: Observable /* Document observable */ + var location$: Subject /* Location subject */ + var target$: Observable /* Location target observable */ + var keyboard$: Observable /* Keyboard observable */ + var viewport$: Observable /* Viewport obsevable */ + var tablet$: Observable /* Tablet breakpoint observable */ + var screen$: Observable /* Screen breakpoint observable */ + var print$: Observable /* Print mode observable */ + var alert$: Subject /* Alert subject */ + var component$: Observable/* Component observable */ +} diff --git a/docs/tensorstore_sphinx_material/typings/svgo/index.d.ts b/docs/tensorstore_sphinx_material/typings/svgo/index.d.ts new file mode 100644 index 000000000..7fb7bce32 --- /dev/null +++ b/docs/tensorstore_sphinx_material/typings/svgo/index.d.ts @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016-2021 Martin Donath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +declare module "svgo" { + + /** + * Plugin + */ + interface Plugin { + name: string + active: boolean + } + + /** + * Optimization configuration + */ + interface OptimizeConfig { + plugins: Plugin[] + } + + /** + * Optimization result + */ + interface OptimizeResult { + data: string + } + + /** + * Optimize SVG + * + * @param data - SVG data + * + * @returns Optimization result + */ + function optimize(data: string, config: OptimizeConfig): OptimizeResult + + /** + * Extend the list of default plugins + * + * @param plugins - Plugins + * + * @returns Plugins + */ + function extendDefaultPlugins(plugins: Plugin[]): Plugin[] +} diff --git a/external.bzl b/external.bzl index a8a79af8f..20e3a268c 100644 --- a/external.bzl +++ b/external.bzl @@ -45,11 +45,12 @@ def _bazel_dependencies(): sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", ) + # Needed to build documentation maybe( http_archive, - name = "rules_python", - url = "https://github.com/bazelbuild/rules_python/releases/download/0.0.1/rules_python-0.0.1.tar.gz", - sha256 = "aa96a691d3a8177f3215b14b0edc9641787abaaa30363a080165d06ab65e1161", + name = "build_bazel_rules_nodejs", + sha256 = "f533eeefc8fe1ddfe93652ec50f82373d0c431f7faabd5e6323f6903195ef227", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.3.0/rules_nodejs-3.3.0.tar.gz"], ) def _python_dependencies(): diff --git a/external2.bzl b/external2.bzl new file mode 100644 index 000000000..ce5b75c45 --- /dev/null +++ b/external2.bzl @@ -0,0 +1,32 @@ +# Copyright 2021 The TensorStore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Second stage of workspace setup. + +This cannot be in the same file as `external.bzl` because we need to load from +repositories created by `external.bzl`. +""" + +load("@build_bazel_rules_nodejs//:index.bzl", "npm_install") + +def tensorstore_dependencies2(): + npm_install( + name = "npm", + package_json = "//docs/tensorstore_sphinx_material:package.json", + package_lock_json = "//docs/tensorstore_sphinx_material:package-lock.json", + package_path = "docs/tensorstore_sphinx_material", + # Setting this to `True` causes rules_nodejs to sometimes get into an + # inconsistent state. + symlink_node_modules = False, + ) diff --git a/python/tensorstore/__init__.py b/python/tensorstore/__init__.py index ef4402991..bf018359c 100644 --- a/python/tensorstore/__init__.py +++ b/python/tensorstore/__init__.py @@ -16,25 +16,209 @@ import abc as _abc from ._tensorstore import * -from ._tensorstore import _ContextResource newaxis = None -"""Alias for `None` used in `indexing expressions` to specify a new singleton dimension.""" +"""Alias for `None` used in `indexing expressions` to specify a new singleton dimension. +Example: + + >>> transform = ts.IndexTransform(input_rank=3) + >>> transform[ts.newaxis, 5] + Rank 3 -> 3 index space transform: + Input domain: + 0: [0*, 1*) + 1: (-inf*, +inf*) + 2: (-inf*, +inf*) + Output index maps: + out[0] = 5 + out[1] = 0 + 1 * in[1] + out[2] = 0 + 1 * in[2] + +Group: + Indexing +""" + +inf: int +"""Special constant equal to :math:`2^{62}-1` that indicates an unbounded `index domain`. + +Example: + + >>> d = ts.Dim() + >>> d.inclusive_min + -4611686018427387903 + >>> d.inclusive_max + 4611686018427387903 + >>> assert d.inclusive_min == -ts.inf + >>> assert d.inclusive_max == +ts.inf + +Group: + Indexing +""" -class Indexable(metaclass=_abc.ABCMeta): - """Abstract base class for types that - support :ref:`TensorStore indexing operations`. +class Indexable(metaclass=_abc.ABCMeta): + """Abstract base class for types that support :ref:`TensorStore indexing operations`. Supported types are: - :py:class:`tensorstore.TensorStore` - :py:class:`tensorstore.Spec` - :py:class:`tensorstore.IndexTransform` + + Group: + Indexing """ Indexable.register(TensorStore) Indexable.register(Spec) Indexable.register(IndexTransform) + +bool: dtype +"""Boolean data type (0 or 1). Corresponds to the :py:obj:`python:bool` type and ``numpy.bool_``. + +Group: + Data types +""" + +int8: dtype +"""8-bit signed :wikipedia:`two's-complement ` integer data type. Corresponds to ``numpy.int8``. + +Group: + Data types +""" + +uint8: dtype +"""8-bit unsigned integer. Corresponds to ``numpy.uint8``. + +Group: + Data types +""" + +int16: dtype +"""16-bit signed :wikipedia:`two's-complement ` integer data type. Corresponds to ``numpy.int16``. + +Group: + Data types +""" + +uint16: dtype +"""16-bit unsigned integer. Corresponds to ``numpy.uint16``. + +Group: + Data types +""" + +int32: dtype +"""32-bit signed :wikipedia:`two's-complement ` integer data type. Corresponds to ``numpy.int32``. + +Group: + Data types +""" + +uint32: dtype +"""32-bit unsigned integer. Corresponds to ``numpy.uint32``. + +Group: + Data types +""" + +int64: dtype +"""32-bit signed :wikipedia:`two's-complement ` integer data type. Corresponds to ``numpy.int64``. + +Group: + Data types +""" + +uint64: dtype +"""64-bit unsigned integer data type. Corresponds to ``numpy.uint64``. + +Group: + Data types +""" + +float16: dtype +""":wikipedia:`IEEE 754 binary16 ` half-precision floating-point data type. Correspond to ``numpy.float16``. + +Group: + Data types +""" + +bfloat16: dtype +""":wikipedia:`bfloat16 floating-point ` data type. + +NumPy does not have built-in support for bfloat16. As an extension, TensorStore +defines the :python:`tensorstore.bfloat16.dtype` NumPy data type (also available +as :python:`numpy.dtype("bfloat16")`, as well as the corresponding +:python:`tensorstore.bfloat16.type` :ref:`array scalar +type`, and these types are guaranteed to interoperate with +`TensorFlow `_ and `JAX `_. + +Group: + Data types +""" + +float32: dtype +""":wikipedia:`IEEE 754 binary32 ` single-precision floating-point data type. Corresponds to ``numpy.float32``. + +Group: + Data types +""" + +float64: dtype +""":wikipedia:`IEEE 754 binary64 ` double-precision floating-point data type. Corresponds to ``numpy.float64``. + +Group: + Data types +""" + +complex64: dtype +"""Complex number based on :py:obj:`.float32`. Corresponds to ``numpy.complex64``. + +Group: + Data types +""" + +complex128: dtype +"""Complex number based on :py:obj:`.float64`. Corresponds to ``numpy.complex128``. + +Group: + Data types +""" + +string: dtype +"""Variable-length byte string data type. Corresponds to the Python :py:obj:`python:bytes` type. + +There is no precisely corresponding NumPy data type, but ``numpy.object_`` is used. + +.. note:: + + The :ref:`NumPy string types`, while related, differ + in that they are fixed-length and null-terminated. + +Group: + Data types +""" + +ustring: dtype +"""Variable-length Unicode string data type. Corresponds to the Python :py:obj:`python:str` type. + +There is no precisely corresponding NumPy data type, but ``numpy.object_`` is used. + +.. note:: + + The :ref:`NumPy string types`, while related, differ + in that they are fixed-length and null-terminated. + +Group: + Data types +""" + +json: dtype +"""JSON data type. Corresponds to an arbitrary Python JSON value. + +There is no precisely corresponding NumPy data type, but ``numpy.object_`` is used. + +Group: + Data types +""" diff --git a/python/tensorstore/array_type_caster.h b/python/tensorstore/array_type_caster.h index 19144fb0d..7551fe13a 100644 --- a/python/tensorstore/array_type_caster.h +++ b/python/tensorstore/array_type_caster.h @@ -234,7 +234,7 @@ struct type_caster> { template <> struct type_caster { using T = tensorstore::ContiguousLayoutOrder; - PYBIND11_TYPE_CASTER(T, _("ContiguousLayoutOrder")); + PYBIND11_TYPE_CASTER(T, _("Literal['C','F']")); bool load(handle src, bool convert) { value = tensorstore::internal_python::GetContiguousLayoutOrderOrThrow(src); return true; diff --git a/python/tensorstore/context.cc b/python/tensorstore/context.cc index 29f5f5b1a..1fa349edf 100644 --- a/python/tensorstore/context.cc +++ b/python/tensorstore/context.cc @@ -328,14 +328,39 @@ ContextImplPtr UnpickleContextSpecBuilder(py::tuple t, } void RegisterContextBindings(pybind11::module m) { - py::class_ cls_context_spec( - m, "ContextSpec"); + py::class_ cls_context(m, "Context", + R"( +Manages shared TensorStore :ref:`context resources`, such as caches and credentials. + +Group: + Core + +See also: + :ref:`context` + +)"); + + py::class_ cls_context_spec(cls_context, + "Spec", R"( +Parsed representation of a :json:schema:`JSON Context` specification. +)"); + + // `ContextResourceImplBase` represents a context resource. It is exposed + // primarily for pickling and testing. There isn't a whole lot that can be + // done with objects of this type, though their identity can be compared. + py::class_ + cls_context_resource(cls_context, "Resource", R"( +Handle to a context resource. +)"); + cls_context_spec .def(py::init([](const ::nlohmann::json& json, bool allow_unregistered) { return Access::impl(ValueOrThrow(Context::Spec::FromJson( json, AllowUnregistered{allow_unregistered}))); }), - "Creates a ContextSpec from a JSON object.", py::arg("json"), + R"( +Creates a context specification from its :json:schema:`JSON representation`. +)", py::arg("json"), py::arg("allow_unregistered") = false) .def( "to_json", @@ -343,12 +368,21 @@ void RegisterContextBindings(pybind11::module m) { return WrapImpl(std::move(self)) .ToJson(IncludeDefaults{include_defaults}); }, + R"( +Returns the :json:schema:`JSON representation`. + +Args: + include_defaults: Indicates whether to include members even if they are equal to the default value. + +Group: + Accessors +)", py::arg("include_defaults") = false) .def("__repr__", [](internal_context::ContextSpecImplPtr self) { return internal_python::PrettyPrintJsonAsPythonRepr( WrapImpl(std::move(self)).ToJson(IncludeDefaults{false}), - "ContextSpec(", ")"); + "Context.Spec(", ")"); }) .def(py::pickle( [](ContextSpecImplPtr self) { @@ -361,19 +395,42 @@ void RegisterContextBindings(pybind11::module m) { Context::Spec::FromJson(py::cast<::nlohmann::json>(t[0])))); })); - py::class_ cls_context(m, "Context", - R"( -Manages shared TensorStore resources, such as caches and credentials. -)"); cls_context .def(py::init([] { return Access::impl(Context::Default()); }), - "Returns a default context") + R"( +Constructs a default context. + +Example: + + >>> context = ts.Context() + >>> context.spec is None + True + +.. note:: + + Each call to this constructor returns a unique default context instance, that + does *not* share resources with other default context instances. To share + resources, you must use the same :py:obj:`Context` instance. + +Overload: + default +)") .def(py::init([](ContextSpecImplPtr spec, ContextImplPtr parent) { return Access::impl(Context(WrapImpl(std::move(spec)), WrapImpl(std::move(parent)))); }), - "Constructs a context.", py::arg("spec"), - py::arg("parent") = nullptr) + R"( +Constructs a context from a parsed spec. + +Args: + spec: Parsed context spec. + parent: Parent context from which to inherit. Defaults to a new default + context as returned by :python:`tensorstore.Context()`. + +Overload: + spec +)", + py::arg("spec"), py::arg("parent") = nullptr) .def(py::init([](::nlohmann::json json, ContextImplPtr parent, bool allow_unregistered) { return Access::impl( @@ -381,12 +438,85 @@ Manages shared TensorStore resources, such as caches and credentials. json, AllowUnregistered{allow_unregistered})), WrapImpl(std::move(parent)))); }), - "Constructs a context from a JSON spec.", py::arg("json"), - py::arg("parent") = nullptr, py::arg("allow_unregistered") = false) + R"( +Constructs a context from its :json:schema:`JSON representation`. + +Example: + + >>> context = ts.Context({'cache_pool': {'total_bytes_limit': 5000000}}) + >>> context.spec + Context.Spec({'cache_pool': {'total_bytes_limit': 5000000}}) + +Args: + json: :json:schema:`JSON representation` of the context. + parent: Parent context from which to inherit. Defaults to a new default + context as returned by :python:`tensorstore.Context()`. + +Overload: + json +)", + py::arg("json"), py::arg("parent") = nullptr, + py::arg("allow_unregistered") = false) .def_property_readonly( - "parent", [](const ContextImpl& self) { return self.parent_; }) - .def_property_readonly("spec", - [](const ContextImpl& self) { return self.spec_; }) + "parent", [](const ContextImpl& self) { return self.parent_; }, + R"( +Parent context from which this context inherits. + +Example: + + >>> parent = ts.Context({ + ... 'cache_pool': { + ... 'total_bytes_limit': 5000000 + ... }, + ... 'file_io_concurrency': { + ... 'limit': 10 + ... } + ... }) + >>> child = ts.Context({'cache_pool': { + ... 'total_bytes_limit': 10000000 + ... }}, + ... parent=parent) + >>> assert child.parent is parent + >>> parent['cache_pool'].to_json() + {'total_bytes_limit': 5000000} + >>> child['cache_pool'].to_json() + {'total_bytes_limit': 10000000} + >>> child['file_io_concurrency'].to_json() + {'limit': 10} + +Group: + Accessors +)") + .def_property_readonly( + "spec", [](const ContextImpl& self) { return self.spec_; }, + R"( +Spec from which this context was constructed. + +Example: + + >>> parent = ts.Context({ + ... 'cache_pool': { + ... 'total_bytes_limit': 5000000 + ... }, + ... 'file_io_concurrency': { + ... 'limit': 10 + ... } + ... }) + >>> child = ts.Context({'cache_pool': { + ... 'total_bytes_limit': 10000000 + ... }}, + ... parent=parent) + >>> child.spec + Context.Spec({'cache_pool': {'total_bytes_limit': 10000000}}) + >>> child.parent.spec + Context.Spec({ + 'cache_pool': {'total_bytes_limit': 5000000}, + 'file_io_concurrency': {'limit': 10}, + }) + +Group: + Accessors +)") .def( "__getitem__", [](ContextImplPtr self, std::string key) { @@ -402,15 +532,35 @@ Manages shared TensorStore resources, such as caches and credentials. return ValueOrThrow( internal_context::GetResource(self.get(), spec.get(), nullptr)); }, + R"( +Creates or retrieves the context resource for the given key. + +This is primarily useful for introspection of a context. + +Example: + + >>> context = ts.Context( + ... {'cache_pool#a': { + ... 'total_bytes_limit': 10000000 + ... }}) + >>> context['cache_pool#a'] + Context.Resource({'total_bytes_limit': 10000000}) + >>> context['cache_pool'] + Context.Resource({}) + +Args: + key: Resource key, of the form :python:`''` or + :python:`#`. + +Returns: + The resource handle. + +Group: + Accessors +)", py::arg("key")) .def(py::pickle(PickleContext, UnpickleContext)); - // `ContextResourceImplBase` represents a context resource. It is not part of - // the public API, and is exposed only for pickling and testing. There isn't - // a whole lot that can be done with objects of this type, though their - // identity can be compared. - py::class_ - cls_context_resource(m, "_ContextResource"); cls_context_resource .def( "to_json", @@ -419,12 +569,26 @@ Manages shared TensorStore resources, such as caches and credentials. self->spec_->ToJson(IncludeDefaults{include_defaults})); }, py::arg("include_defaults") = false, - "Returns the JSON representation of the context resource.") + R"( +Returns the :json:schema:`JSON representation` of the context resource. + +Example: + + >>> context = ts.Context( + ... {'cache_pool#a': { + ... 'total_bytes_limit': 10000000 + ... }}) + >>> context['cache_pool#a'].to_json() + {'total_bytes_limit': 10000000} + +Group: + Accessors +)") .def("__repr__", [](ContextResourceImplWeakPtr self) { return internal_python::PrettyPrintJsonAsPythonRepr( self->spec_->ToJson(IncludeDefaults{false}), - "_ContextResource(", ")"); + "Context.Resource(", ")"); }) .def(py::pickle(PickleContextResource, UnpickleContextResource)); } diff --git a/python/tensorstore/data_type.cc b/python/tensorstore/data_type.cc index 9c62bb374..9685bd225 100644 --- a/python/tensorstore/data_type.cc +++ b/python/tensorstore/data_type.cc @@ -117,13 +117,27 @@ void RegisterDataTypeBindings(pybind11::module m) { throw py::error_already_set(); } py::class_ cls_data_type(m, "dtype", R"( -Represents a TensorStore data type. +TensorStore data type representation. + +Group: + Data types )"); cls_data_type .def(py::init([](std::string name) { return GetDataTypeOrThrow(name); }), - "Construct by name.", py::arg("name")) + R"( +Construct by name. + +Overload: + name +)", + py::arg("name")) .def(py::init([](DataTypeLike dtype) { return dtype.value; }), - "Construct from an existing TensorStore or NumPy data type.", + R"( +Construct from an existing TensorStore or NumPy data type. + +Overload: + dtype +)", py::arg("dtype")) .def_property_readonly( "name", [](DataType self) { return std::string(self.name()); }) diff --git a/python/tensorstore/downsample.cc b/python/tensorstore/downsample.cc index 174c58e60..0c3b64ceb 100644 --- a/python/tensorstore/downsample.cc +++ b/python/tensorstore/downsample.cc @@ -38,6 +38,15 @@ void RegisterDownsampleBindings(pybind11::module m) { return ValueOrThrow(tensorstore::Downsample( std::move(base), downsample_factors, method)); }, + R"( +Returns a virtual :ref:`downsampled view` of a :py:obj:`TensorStore`. + +Group: + Views + +Overload: + store +)", py::arg("base"), py::arg("downsample_factors"), py::arg("method")); m.def( @@ -47,6 +56,15 @@ void RegisterDownsampleBindings(pybind11::module m) { return ValueOrThrow( tensorstore::Downsample(base, downsample_factors, method)); }, + R"( +Returns a virtual :ref:`downsampled view` view of a :py:obj:`Spec`. + +Group: + Views + +Overload: + spec +)", py::arg("base"), py::arg("downsample_factors"), py::arg("method")); } diff --git a/python/tensorstore/future.cc b/python/tensorstore/future.cc index 1bc8f02ec..6ca772529 100644 --- a/python/tensorstore/future.cc +++ b/python/tensorstore/future.cc @@ -342,7 +342,94 @@ absl::Time GetWaitDeadline(std::optional timeout, void RegisterFutureBindings(pybind11::module m) { py::class_> cls_future( - m, "Future"); + m, "Future", R"( +Handle for *consuming* the result of an asynchronous operation. + +This type supports several different patterns for consuming results: + +- Asynchronously with :py:mod:`asyncio`, using the `await` keyword: + + >>> future = ts.open({ + ... 'driver': 'array', + ... 'array': [1, 2, 3], + ... 'dtype': 'uint32' + ... }) + >>> await future + TensorStore({ + 'array': [1, 2, 3], + 'driver': 'array', + 'dtype': 'uint32', + 'transform': {'input_exclusive_max': [3], 'input_inclusive_min': [0]}, + }) + +- Synchronously blocking the current thread, by calling :py:meth:`.result()`. + + >>> future = ts.open({ + ... 'driver': 'array', + ... 'array': [1, 2, 3], + ... 'dtype': 'uint32' + ... }) + >>> future.result() + TensorStore({ + 'array': [1, 2, 3], + 'driver': 'array', + 'dtype': 'uint32', + 'transform': {'input_exclusive_max': [3], 'input_inclusive_min': [0]}, + }) + +- Asynchronously, by registering a callback using :py:meth:`.add_done_callback`: + + >>> future = ts.open({ + ... 'driver': 'array', + ... 'array': [1, 2, 3], + ... 'dtype': 'uint32' + ... }) + >>> future.add_done_callback( + ... lambda f: print(f'Callback: {f.result().domain}')) + ... future.force() # ensure the operation is started + ... # wait for completion (for testing only) + ... result = future.result() + Callback: { [0, 3) } + +If an error occurs, instead of returning a value, :py:obj:`.result()` or +`python:await` will raise an exception. + +This type supports a subset of the interfaces of +:py:class:`python:concurrent.futures.Future` and +:py:class:`python:asyncio.Future`. Unlike those types, however, +:py:class:`Future` provides only the *consumer* interface. The corresponding +*producer* interface is provided by :py:class:`Promise`. + +See also: + - :py:class:`WriteFutures` + +Group: + Asynchronous support +)"); + + py::class_> cls_promise(m, "Promise", R"( +Handle for *producing* the result of an asynchronous operation. + +A promise represents the producer interface corresponding to a +:py:class:`Future`, and may be used to signal the completion of an asynchronous +operation. + + >>> promise, future = ts.Promise.new() + >>> future.done() + False + >>> promise.set_result(5) + >>> future.done() + True + >>> future.result() + 5 + +See also: + - :py:class:`Future` + +Group: + Asynchronous support +)"); + cls_future.def("__await__", &PythonFutureBase::get_await_result); cls_future.def("add_done_callback", &PythonFutureBase::add_done_callback, @@ -368,7 +455,6 @@ void RegisterFutureBindings(pybind11::module m) { cls_future.def("cancelled", &PythonFutureBase::cancelled); cls_future.def("cancel", &PythonFutureBase::cancel); - py::class_> cls_promise(m, "Promise"); cls_promise.def("set_result", [](const Promise& self, py::object result) { self.SetResult(PythonValueOrException{std::move(result)}); diff --git a/python/tensorstore/index.h b/python/tensorstore/index.h index 271078be1..1c4e88ff3 100644 --- a/python/tensorstore/index.h +++ b/python/tensorstore/index.h @@ -140,7 +140,7 @@ namespace detail { template <> struct type_caster { PYBIND11_TYPE_CASTER(tensorstore::internal_python::PythonDimensionIndex, - _("DimensionIndex")); + _("int")); static handle cast(tensorstore::internal_python::PythonDimensionIndex x, return_value_policy /* policy */, handle /* parent */) { return int_(x.value).release(); @@ -166,7 +166,7 @@ struct type_caster { template <> struct type_caster { PYBIND11_TYPE_CASTER(tensorstore::internal_python::OptionallyImplicitIndex, - _("tensorstore.Index")); + _("Optional[int]")); static handle cast(tensorstore::internal_python::OptionallyImplicitIndex x, return_value_policy /* policy */, handle /* parent */) { if (x.value == tensorstore::kImplicit) return none().release(); diff --git a/python/tensorstore/json_type_caster.h b/python/tensorstore/json_type_caster.h index 82759ea07..517f2803a 100644 --- a/python/tensorstore/json_type_caster.h +++ b/python/tensorstore/json_type_caster.h @@ -46,7 +46,7 @@ namespace detail { /// Defines automatic conversion between Python objects and `::nlohmann::json`. template <> struct type_caster< ::nlohmann::json> { - PYBIND11_TYPE_CASTER(::nlohmann::json, _("json")); + PYBIND11_TYPE_CASTER(::nlohmann::json, _("Any")); static handle cast(const ::nlohmann::json& value, return_value_policy /* policy */, handle /* parent */) { diff --git a/python/tensorstore/tests/context_test.py b/python/tensorstore/tests/context_test.py index 51cadd261..8e1878866 100644 --- a/python/tensorstore/tests/context_test.py +++ b/python/tensorstore/tests/context_test.py @@ -26,11 +26,11 @@ def test_context_spec(): 'memory_key_value_store#b': {}, } - spec = ts.ContextSpec(json_spec) + spec = ts.Context.Spec(json_spec) assert spec.to_json() == json_spec - assert repr(spec) == '''ContextSpec({ + assert repr(spec) == '''Context.Spec({ 'memory_key_value_store': {}, 'memory_key_value_store#a': 'memory_key_value_store', 'memory_key_value_store#b': {}, @@ -51,7 +51,7 @@ def test_pickle(): 'memory_key_value_store#a': 'memory_key_value_store', 'memory_key_value_store#b': {}, } - child_spec = ts.ContextSpec(json_spec) + child_spec = ts.Context.Spec(json_spec) context = ts.Context(child_spec, parent_context) @@ -63,7 +63,7 @@ def test_pickle(): assert context.parent is parent_context assert parent_context.parent is None assert context['memory_key_value_store'].to_json() == {} - assert repr(context['memory_key_value_store']) == '_ContextResource({})' + assert repr(context['memory_key_value_store']) == 'Context.Resource({})' assert context['memory_key_value_store#b'].to_json() == {} assert context['memory_key_value_store#c'].to_json() == {} assert context['memory_key_value_store'] is context['memory_key_value_store#a'] diff --git a/python/tensorstore/transaction.cc b/python/tensorstore/transaction.cc index 7c02a8c1a..d1386c89b 100644 --- a/python/tensorstore/transaction.cc +++ b/python/tensorstore/transaction.cc @@ -108,6 +108,18 @@ asynchronous context manager: If the block exits normally, the transaction is committed automatically. If the block raises an exception, the transaction is aborted. +Group: + Core + +Constructors +============ + +Accessors +========= + +Operations +========== + )"); cls_transaction.def( @@ -115,20 +127,31 @@ block raises an exception, the transaction is aborted. return TransactionState::ToCommitPtr(Transaction( atomic ? tensorstore::atomic_isolated : tensorstore::isolated)); }), - py::arg("atomic") = false, R"(Creates a new transaction.)"); + py::arg("atomic") = false, R"( +Creates a new transaction. +)"); cls_transaction.def( "commit_async", [](const TransactionState::CommitPtr& self) { self->RequestCommit(); return self->future(); }, - R"(Asynchronously commits the transaction. + R"( +Asynchronously commits the transaction. Has no effect if :py:meth:`.commit_async` or :py:meth:`.abort` has already been called. Returns the associated :py:obj:`.future`, which may be used to check if the commit was successful. + +See also: + + - :py:obj:`.commit_sync` + - :py:obj:`.abort` + +Group: + Operations )"); cls_transaction.def( "commit_sync", @@ -136,58 +159,96 @@ commit was successful. self->RequestCommit(); ValueOrThrow(InterruptibleWait(self->future())); }, - R"(Synchronously commits the transaction. + R"( +Synchronously commits the transaction. + +Equivalent to :python:`self.commit_async().result()`. + +Returns: + + :py:obj:`None` if the commit is successful, and raises an error otherwise. -Equivalent to: +See also: - self.commit_async().result() + - :py:obj:`.commit_async` + - :py:obj:`.abort` -Returns `None` if the commit is successful, and raises an error otherwise. +Group: + Operations )"); cls_transaction.def( "abort", [](const TransactionState::CommitPtr& self) { self->RequestAbort(); }, - R"(Aborts the transaction. + R"( +Aborts the transaction. Has no effect if :py:meth:`.commit_async` or :py:meth:`.abort` has already been called. + + - :py:obj:`.commit_async` + +Group: + Operations )"); cls_transaction.def_property_readonly( "future", [](const TransactionState::CommitPtr& self) { return self->future(); }, - R"(Commit result future. + R"( +Commit result future. Becomes ready when the transaction has either been committed successfully or aborted. + +Group: + Accessors )"); cls_transaction.def_property_readonly( "aborted", [](const TransactionState::CommitPtr& self) { return self->aborted(); }, - "Indicates whether the transaction has been aborted."); + R"( +Indicates whether the transaction has been aborted. + +Group: + Accessors +)"); cls_transaction.def_property_readonly( "commit_started", [](const TransactionState::CommitPtr& self) { return self->commit_started(); }, - "Indicates whether the commit of the transaction has already started."); + R"( +Indicates whether the commit of the transaction has already started. + +Group: + Accessors +)"); cls_transaction.def_property_readonly( "atomic", [](const TransactionState::CommitPtr& self) { return self->mode() == tensorstore::atomic_isolated; }, - "Indicates whether the transaction is atomic."); + R"( +Indicates whether the transaction is atomic. + +Group: + Accessors +)"); cls_transaction.def_property_readonly( "open", [](const TransactionState::CommitPtr& self) { return !self->commit_started() && !self->aborted(); }, - R"(Indicates whether the transaction is still open. + R"( +Indicates whether the transaction is still open. The transaction remains open until commit starts or it is aborted. Once commit starts or it has been aborted, it may not be used for any additional transactional operations. + +Group: + Accessors )"); cls_transaction.def("__enter__", [](const TransactionState::CommitPtr& self) { diff --git a/python/tensorstore/write_futures.cc b/python/tensorstore/write_futures.cc index 80d5ece48..1d754c837 100644 --- a/python/tensorstore/write_futures.cc +++ b/python/tensorstore/write_futures.cc @@ -27,7 +27,33 @@ namespace internal_python { void RegisterWriteFuturesBindings(pybind11::module m) { namespace py = ::pybind11; - py::class_ cls_write_futures(m, "WriteFutures"); + py::class_ cls_write_futures(m, "WriteFutures", R"( +Handle for consuming the result of an asynchronous write operation. + +This holds two futures: + +- The :py:obj:`.copy` future indicates when reading has completed, after which + the source is no longer accessed. + +- The :py:obj:`.commit` future indicates when the write is guaranteed to be + reflected in subsequent reads. For non-transactional writes, the + :py:obj:`.commit` future completes successfully only once durability of the + write is guaranteed (subject to the limitations of the underlying storage + mechanism). For transactional writes, the :py:obj:`.commit` future merely + indicates when the write is reflected in subsequent reads using the same + transaction. Durability is *not* guaranteed until the transaction itself is + committed successfully. + +In addition, this class also provides the same interface as :py:class:`Future`, +which simply forwards to the corresponding operation on the :py:obj:`.commit` +future. + +See also: + - :py:meth:`TensorStore.write` + +Group: + Asynchronous support +)"); cls_write_futures .def("__await__", [](const PythonWriteFutures& self) { diff --git a/tensorstore/driver/downsample/index.rst b/tensorstore/driver/downsample/index.rst index 04ff0e8a8..e620045b9 100644 --- a/tensorstore/driver/downsample/index.rst +++ b/tensorstore/driver/downsample/index.rst @@ -1,3 +1,5 @@ +.. _driver/downsample: + ``downsample`` Driver ===================== diff --git a/tensorstore/driver/n5/driver_test.cc b/tensorstore/driver/n5/driver_test.cc index 9e69a59d8..ef3be7aec 100644 --- a/tensorstore/driver/n5/driver_test.cc +++ b/tensorstore/driver/n5/driver_test.cc @@ -678,7 +678,7 @@ TEST(N5DriverTest, InvalidSpec) { "Expected object, but member is missing")); } - for (const std::string& member_name : {"kvstore", "path", "metadata"}) { + for (auto member_name : {"kvstore", "path", "metadata"}) { auto spec = GetJsonSpec(); spec[member_name] = 5; EXPECT_THAT( diff --git a/tensorstore/driver/zarr/driver_test.cc b/tensorstore/driver/zarr/driver_test.cc index 5ad28d375..25ccaf90d 100644 --- a/tensorstore/driver/zarr/driver_test.cc +++ b/tensorstore/driver/zarr/driver_test.cc @@ -1470,7 +1470,7 @@ TEST(ZarrDriverTest, InvalidSpec) { "Expected object, but member is missing")); } - for (const std::string& member_name : + for (auto member_name : {"kvstore", "path", "field", "key_encoding", "metadata"}) { auto spec = GetJsonSpec(); spec[member_name] = 5; diff --git a/tensorstore/examples/python/beam/BUILD b/tensorstore/examples/python/beam/BUILD index 09b903fc1..96cf2fb5f 100644 --- a/tensorstore/examples/python/beam/BUILD +++ b/tensorstore/examples/python/beam/BUILD @@ -25,7 +25,6 @@ tensorstore_pytest_test( tags = ["manual"], deps = [ ":pipelines_lib", - "//net/proto2/python/public:use_pure_python", # Automatically added go/proto_python_default "//python/tensorstore", "@pypa_apache_beam//:apache_beam", "@pypa_numpy//:numpy", diff --git a/tensorstore/index_space/dim_expression.h b/tensorstore/index_space/dim_expression.h index 01224844e..1f9bfa001 100644 --- a/tensorstore/index_space/dim_expression.h +++ b/tensorstore/index_space/dim_expression.h @@ -1289,8 +1289,7 @@ class DimExpression { return {{index_arrays}, *this}; } - /// Sets (or changes) the labels of the selected dimensions, and marks those - /// labels as explicit. + /// Sets (or changes) the labels of the selected dimensions. /// /// The new dimension selection is the same as the prior dimension selection, /// with a static rank equal to the merged static rank of the prior dimension diff --git a/tensorstore/index_space/json.h b/tensorstore/index_space/json.h index 22df98874..df32e2220 100644 --- a/tensorstore/index_space/json.h +++ b/tensorstore/index_space/json.h @@ -85,7 +85,7 @@ /// /// { /// "input_inclusive_min": ["-inf", 7, ["-inf"], [8]], -/// "input_exclusive_max": ["+inf", 10, ["+inf"], [17]], +/// "input_exclusive_max": ["+inf", 11, ["+inf"], [17]], /// "input_labels": ["x", "y", "z", ""], /// "output": [ /// {"offset": 3}, diff --git a/tensorstore/transaction_test.cc b/tensorstore/transaction_test.cc index 89a288194..36b0d7e77 100644 --- a/tensorstore/transaction_test.cc +++ b/tensorstore/transaction_test.cc @@ -689,7 +689,6 @@ TEST(TransactionTest, ReleaseTransactionReferenceDuringAbort) { auto txn = Transaction(tensorstore::isolated); auto future = txn.future(); TestNode* node1; - SynchronousTestNode* node2; { TENSORSTORE_ASSERT_OK_AND_ASSIGN(auto open_ptr, AcquireOpenTransactionPtrOrError(txn)); @@ -705,7 +704,6 @@ TEST(TransactionTest, ReleaseTransactionReferenceDuringAbort) { new SynchronousTestNode(&log, 2)); weak_node->SetTransaction(*open_ptr); TENSORSTORE_EXPECT_OK(weak_node->Register()); - node2 = weak_node.get(); } } txn.CommitAsync().IgnoreFuture(); diff --git a/third_party/com_google_boringssl/patches/no-Werror.diff b/third_party/com_google_boringssl/patches/no-Werror.diff new file mode 100644 index 000000000..14a68399b --- /dev/null +++ b/third_party/com_google_boringssl/patches/no-Werror.diff @@ -0,0 +1,12 @@ +diff --git a/BUILD b/BUILD +index 65e0cdc2e..9d42caded 100644 +--- a/BUILD ++++ b/BUILD +@@ -94,7 +94,6 @@ posix_copts = [ + + # This list of warnings should match those in the top-level CMakeLists.txt. + "-Wall", +- "-Werror", + "-Wformat=2", + "-Wsign-compare", + "-Wmissing-field-initializers", diff --git a/third_party/com_google_boringssl/workspace.bzl b/third_party/com_google_boringssl/workspace.bzl index 56e189814..d1cdd1788 100644 --- a/third_party/com_google_boringssl/workspace.bzl +++ b/third_party/com_google_boringssl/workspace.bzl @@ -27,5 +27,11 @@ def repo(): ], sha256 = "ce183cb587c0a0f5982e441dff91cb5456d4c85cfa3fb12816e7a93f20645e51", strip_prefix = "boringssl-bdbe37905216bea8dd4d0fdee93f6ee415d3aa15", - system_build_file = Label("//third_party/com_google_boringssl:system.BUILD.bazel"), + system_build_file = Label("//third_party:com_google_boringssl/system.BUILD.bazel"), + patches = [ + # boringssl sets -Werror by default. That makes the build fragile + # and likely to break with new compiler versions. + "//third_party:com_google_boringssl/patches/no-Werror.diff", + ], + patch_args = ["-p1"], ) diff --git a/third_party/pypa/generate_workspace.py b/third_party/pypa/generate_workspace.py index 0868ba7e1..ed1a820af 100644 --- a/third_party/pypa/generate_workspace.py +++ b/third_party/pypa/generate_workspace.py @@ -28,10 +28,13 @@ import os import packaging.requirements +import packaging.specifiers import packaging.version import requests +SUPPORTED_PYTHON_VERSIONS = ("3.6", "3.7", "3.8", "3.9") + def get_package_json(name: str): r = requests.get(f'https://pypi.python.org/pypi/{name}/json') r.raise_for_status() @@ -71,23 +74,53 @@ def _evaluate_marker(marker): "sys_platform": _AnyValue(), "python_version": python_version, "extra": None - }) for python_version in ("3.6", "3.7", "3.8", "3.9")) - - -def get_package_metadata(name: str): + }) for python_version in SUPPORTED_PYTHON_VERSIONS) + + +def _is_suitable_release(release): + remaining_python_versions = set(SUPPORTED_PYTHON_VERSIONS) + for release_pkg in release: + requires_python = release_pkg.get('requires_python') + if requires_python is None: + return True + spec = packaging.specifiers.SpecifierSet(requires_python) + for v in list(remaining_python_versions): + if v in spec: + remaining_python_versions.remove(v) + if not remaining_python_versions: + return True + return False + + +def _find_suitable_version(name, j, spec): + releases = j["releases"] + versions = [(packaging.version.parse(v), v) for v in releases.keys()] + versions = [v for v in versions if not v[0].is_prerelease] + versions.sort() + versions.reverse() + for (_, v_str) in versions: + if v_str not in spec: continue + if _is_suitable_release(releases[v_str]): + return v_str + return None + +def get_package_metadata(req_str: str): + req = packaging.requirements.Requirement(req_str) + name = req.name j = get_package_json(name) + version_str = _find_suitable_version(name, j, req.specifier) + if version_str is None: + raise ValueError(f'Could not find suitable version for {name}') + j = get_package_json(f'{name}/{version_str}') requires_dist = j["info"].get("requires_dist", []) if requires_dist is None: requires_dist = [] deps = [] - for req_text in requires_dist: - req = packaging.requirements.Requirement(req_text) - if _evaluate_marker(req.marker): - deps.append(req.name.lower()) - versions = [packaging.version.parse(v) for v in j["releases"].keys()] - versions = [v for v in versions if not v.is_prerelease] - versions.sort() - return {"Requires": sorted(deps), "Name": name, "Version": str(versions[-1])} + for dep_req_text in requires_dist: + dep_req = packaging.requirements.Requirement(dep_req_text) + if _evaluate_marker(dep_req.marker): + deps.append(dep_req.name.lower()) + return {"Requires": sorted(deps), "Name": name, "Version": version_str} def get_target_name(package_name): @@ -175,11 +208,11 @@ def generate(args): # Build "wheel", # Docs - "sphinx", - "sphinx-rtd-theme", + "sphinx<4", "jsonschema", "pyyaml", - "docutils", + "docutils<0.17", + "jinja2", # Examples "apache-beam", "gin-config", @@ -190,12 +223,14 @@ def generate(args): cur_packages = all_packages all_metadata.update(get_package_info(cur_packages)) all_packages = set() - for package_name in cur_packages: - if package_name in seen_packages: + for req_text in cur_packages: + req = packaging.requirements.Requirement(req_text) + if req.name in seen_packages: continue - seen_packages.add(package_name) - metadata = all_metadata[package_name] - all_packages.update(metadata["Requires"]) + seen_packages.add(req.name) + metadata = all_metadata[req.name] + all_packages.update(x for x in metadata["Requires"]) + all_packages = all_packages - seen_packages write_workspace( all_metadata=all_metadata.values(), tools_workspace=args.tools_workspace, diff --git a/third_party/pypa/workspace.bzl b/third_party/pypa/workspace.bzl index 4133bc6cf..25c4c1db5 100644 --- a/third_party/pypa/workspace.bzl +++ b/third_party/pypa/workspace.bzl @@ -20,8 +20,10 @@ def repo(): repo_pypa_chardet() repo_pypa_colorama() repo_pypa_crcmod() + repo_pypa_dataclasses() repo_pypa_decorator() repo_pypa_dill() + repo_pypa_docopt() repo_pypa_docutils() repo_pypa_fastavro() repo_pypa_future() @@ -39,7 +41,6 @@ def repo(): repo_pypa_jinja2() repo_pypa_jsonschema() repo_pypa_markupsafe() - repo_pypa_mock() repo_pypa_numpy() repo_pypa_oauth2client() repo_pypa_packaging() @@ -70,7 +71,6 @@ def repo(): repo_pypa_six() repo_pypa_snowballstemmer() repo_pypa_sphinx() - repo_pypa_sphinx_rtd_theme() repo_pypa_sphinxcontrib_applehelp() repo_pypa_sphinxcontrib_devhelp() repo_pypa_sphinxcontrib_htmlhelp() @@ -92,7 +92,7 @@ def repo_pypa_absl_py(): third_party_python_package, name = "pypa_absl_py", target = "absl_py", - requirement = "absl-py==0.11.0", + requirement = "absl-py==0.12.0", deps = [ "@pypa_six//:six", ], @@ -109,13 +109,13 @@ def repo_pypa_alabaster(): def repo_pypa_apache_beam(): repo_pypa_avro_python3() repo_pypa_crcmod() + repo_pypa_dataclasses() repo_pypa_dill() repo_pypa_fastavro() repo_pypa_future() repo_pypa_grpcio() repo_pypa_hdfs() repo_pypa_httplib2() - repo_pypa_mock() repo_pypa_numpy() repo_pypa_oauth2client() repo_pypa_protobuf() @@ -130,17 +130,17 @@ def repo_pypa_apache_beam(): third_party_python_package, name = "pypa_apache_beam", target = "apache_beam", - requirement = "apache-beam==2.27.0", + requirement = "apache-beam==2.29.0", deps = [ "@pypa_avro_python3//:avro_python3", "@pypa_crcmod//:crcmod", + "@pypa_dataclasses//:dataclasses", "@pypa_dill//:dill", "@pypa_fastavro//:fastavro", "@pypa_future//:future", "@pypa_grpcio//:grpcio", "@pypa_hdfs//:hdfs", "@pypa_httplib2//:httplib2", - "@pypa_mock//:mock", "@pypa_numpy//:numpy", "@pypa_oauth2client//:oauth2client", "@pypa_protobuf//:protobuf", @@ -175,7 +175,7 @@ def repo_pypa_attrs(): third_party_python_package, name = "pypa_attrs", target = "attrs", - requirement = "attrs==20.3.0", + requirement = "attrs==21.2.0", ) def repo_pypa_avro_python3(): @@ -183,7 +183,7 @@ def repo_pypa_avro_python3(): third_party_python_package, name = "pypa_avro_python3", target = "avro_python3", - requirement = "avro-python3==1.10.1", + requirement = "avro-python3==1.10.2", ) def repo_pypa_babel(): @@ -192,7 +192,7 @@ def repo_pypa_babel(): third_party_python_package, name = "pypa_babel", target = "babel", - requirement = "babel==2.9.0", + requirement = "babel==2.9.1", deps = [ "@pypa_pytz//:pytz", ], @@ -238,12 +238,20 @@ def repo_pypa_crcmod(): requirement = "crcmod==1.7", ) +def repo_pypa_dataclasses(): + maybe( + third_party_python_package, + name = "pypa_dataclasses", + target = "dataclasses", + requirement = "dataclasses==0.6", + ) + def repo_pypa_decorator(): maybe( third_party_python_package, name = "pypa_decorator", target = "decorator", - requirement = "decorator==4.4.2", + requirement = "decorator==5.0.9", ) def repo_pypa_dill(): @@ -254,6 +262,14 @@ def repo_pypa_dill(): requirement = "dill==0.3.3", ) +def repo_pypa_docopt(): + maybe( + third_party_python_package, + name = "pypa_docopt", + target = "docopt", + requirement = "docopt==0.6.2", + ) + def repo_pypa_docutils(): maybe( third_party_python_package, @@ -267,7 +283,7 @@ def repo_pypa_fastavro(): third_party_python_package, name = "pypa_fastavro", target = "fastavro", - requirement = "fastavro==1.2.3", + requirement = "fastavro==1.4.1", ) def repo_pypa_future(): @@ -292,26 +308,38 @@ def repo_pypa_grpcio(): third_party_python_package, name = "pypa_grpcio", target = "grpcio", - requirement = "grpcio==1.34.0", + requirement = "grpcio==1.38.0", deps = [ "@pypa_six//:six", ], ) def repo_pypa_hdfs(): + repo_pypa_docopt() + repo_pypa_requests() + repo_pypa_six() maybe( third_party_python_package, name = "pypa_hdfs", target = "hdfs", - requirement = "hdfs==2.5.8", + requirement = "hdfs==2.6.0", + deps = [ + "@pypa_docopt//:docopt", + "@pypa_requests//:requests", + "@pypa_six//:six", + ], ) def repo_pypa_httplib2(): + repo_pypa_pyparsing() maybe( third_party_python_package, name = "pypa_httplib2", target = "httplib2", - requirement = "httplib2==0.18.1", + requirement = "httplib2==0.19.1", + deps = [ + "@pypa_pyparsing//:pyparsing", + ], ) def repo_pypa_idna(): @@ -337,7 +365,7 @@ def repo_pypa_importlib_metadata(): third_party_python_package, name = "pypa_importlib_metadata", target = "importlib_metadata", - requirement = "importlib-metadata==3.3.0", + requirement = "importlib-metadata==4.0.1", deps = [ "@pypa_typing_extensions//:typing_extensions", "@pypa_zipp//:zipp", @@ -368,7 +396,7 @@ def repo_pypa_ipython(): third_party_python_package, name = "pypa_ipython", target = "ipython", - requirement = "ipython==7.19.0", + requirement = "ipython==7.16.1", deps = [ "@pypa_appnope//:appnope", "@pypa_backcall//:backcall", @@ -410,7 +438,7 @@ def repo_pypa_jinja2(): third_party_python_package, name = "pypa_jinja2", target = "jinja2", - requirement = "jinja2==2.11.2", + requirement = "jinja2==3.0.1", deps = [ "@pypa_markupsafe//:markupsafe", ], @@ -441,15 +469,7 @@ def repo_pypa_markupsafe(): third_party_python_package, name = "pypa_markupsafe", target = "markupsafe", - requirement = "markupsafe==1.1.1", - ) - -def repo_pypa_mock(): - maybe( - third_party_python_package, - name = "pypa_mock", - target = "mock", - requirement = "mock==4.0.3", + requirement = "markupsafe==2.0.1", ) def repo_pypa_numpy(): @@ -486,7 +506,7 @@ def repo_pypa_packaging(): third_party_python_package, name = "pypa_packaging", target = "packaging", - requirement = "packaging==20.8", + requirement = "packaging==20.9", deps = [ "@pypa_pyparsing//:pyparsing", ], @@ -497,7 +517,7 @@ def repo_pypa_parso(): third_party_python_package, name = "pypa_parso", target = "parso", - requirement = "parso==0.8.1", + requirement = "parso==0.8.2", ) def repo_pypa_pexpect(): @@ -538,7 +558,7 @@ def repo_pypa_prompt_toolkit(): third_party_python_package, name = "pypa_prompt_toolkit", target = "prompt_toolkit", - requirement = "prompt-toolkit==3.0.10", + requirement = "prompt-toolkit==3.0.3", deps = [ "@pypa_wcwidth//:wcwidth", ], @@ -550,7 +570,7 @@ def repo_pypa_protobuf(): third_party_python_package, name = "pypa_protobuf", target = "protobuf", - requirement = "protobuf==3.14.0", + requirement = "protobuf==3.17.0", deps = [ "@pypa_six//:six", ], @@ -578,7 +598,7 @@ def repo_pypa_pyarrow(): third_party_python_package, name = "pypa_pyarrow", target = "pyarrow", - requirement = "pyarrow==2.0.0", + requirement = "pyarrow==4.0.0", deps = [ "@pypa_numpy//:numpy", ], @@ -610,7 +630,7 @@ def repo_pypa_pydot(): third_party_python_package, name = "pypa_pydot", target = "pydot", - requirement = "pydot==1.4.1", + requirement = "pydot==1.4.2", deps = [ "@pypa_pyparsing//:pyparsing", ], @@ -621,7 +641,7 @@ def repo_pypa_pygments(): third_party_python_package, name = "pypa_pygments", target = "pygments", - requirement = "pygments==2.7.3", + requirement = "pygments==2.9.0", ) def repo_pypa_pymongo(): @@ -629,7 +649,7 @@ def repo_pypa_pymongo(): third_party_python_package, name = "pypa_pymongo", target = "pymongo", - requirement = "pymongo==3.11.2", + requirement = "pymongo==3.11.4", ) def repo_pypa_pyparsing(): @@ -662,7 +682,7 @@ def repo_pypa_pytest(): third_party_python_package, name = "pypa_pytest", target = "pytest", - requirement = "pytest==6.2.1", + requirement = "pytest==6.2.4", deps = [ "@pypa_atomicwrites//:atomicwrites", "@pypa_attrs//:attrs", @@ -682,7 +702,7 @@ def repo_pypa_pytest_asyncio(): third_party_python_package, name = "pypa_pytest_asyncio", target = "pytest_asyncio", - requirement = "pytest-asyncio==0.14.0", + requirement = "pytest-asyncio==0.15.1", deps = [ "@pypa_pytest//:pytest", ], @@ -705,7 +725,7 @@ def repo_pypa_pytz(): third_party_python_package, name = "pypa_pytz", target = "pytz", - requirement = "pytz==2020.5", + requirement = "pytz==2021.1", ) def repo_pypa_pyyaml(): @@ -713,7 +733,7 @@ def repo_pypa_pyyaml(): third_party_python_package, name = "pypa_pyyaml", target = "pyyaml", - requirement = "pyyaml==5.3.1", + requirement = "pyyaml==5.4.1", ) def repo_pypa_requests(): @@ -740,7 +760,7 @@ def repo_pypa_rsa(): third_party_python_package, name = "pypa_rsa", target = "rsa", - requirement = "rsa==4.6", + requirement = "rsa==4.7.2", deps = [ "@pypa_pyasn1//:pyasn1", ], @@ -751,7 +771,7 @@ def repo_pypa_setuptools(): third_party_python_package, name = "pypa_setuptools", target = "setuptools", - requirement = "setuptools==51.1.1", + requirement = "setuptools==56.2.0", ) def repo_pypa_six(): @@ -759,7 +779,7 @@ def repo_pypa_six(): third_party_python_package, name = "pypa_six", target = "six", - requirement = "six==1.15.0", + requirement = "six==1.16.0", ) def repo_pypa_snowballstemmer(): @@ -767,7 +787,7 @@ def repo_pypa_snowballstemmer(): third_party_python_package, name = "pypa_snowballstemmer", target = "snowballstemmer", - requirement = "snowballstemmer==2.0.0", + requirement = "snowballstemmer==2.1.0", ) def repo_pypa_sphinx(): @@ -792,7 +812,7 @@ def repo_pypa_sphinx(): third_party_python_package, name = "pypa_sphinx", target = "sphinx", - requirement = "sphinx==3.4.3", + requirement = "sphinx==3.5.4", deps = [ "@pypa_alabaster//:alabaster", "@pypa_babel//:babel", @@ -814,18 +834,6 @@ def repo_pypa_sphinx(): ], ) -def repo_pypa_sphinx_rtd_theme(): - repo_pypa_sphinx() - maybe( - third_party_python_package, - name = "pypa_sphinx_rtd_theme", - target = "sphinx_rtd_theme", - requirement = "sphinx-rtd-theme==0.5.1", - deps = [ - "@pypa_sphinx//:sphinx", - ], - ) - def repo_pypa_sphinxcontrib_applehelp(): maybe( third_party_python_package, @@ -883,14 +891,18 @@ def repo_pypa_toml(): ) def repo_pypa_traitlets(): + repo_pypa_decorator() repo_pypa_ipython_genutils() + repo_pypa_six() maybe( third_party_python_package, name = "pypa_traitlets", target = "traitlets", - requirement = "traitlets==5.0.5", + requirement = "traitlets==4.3.3", deps = [ + "@pypa_decorator//:decorator", "@pypa_ipython_genutils//:ipython_genutils", + "@pypa_six//:six", ], ) @@ -899,7 +911,7 @@ def repo_pypa_typing_extensions(): third_party_python_package, name = "pypa_typing_extensions", target = "typing_extensions", - requirement = "typing-extensions==3.7.4.3", + requirement = "typing-extensions==3.10.0.0", ) def repo_pypa_urllib3(): @@ -907,7 +919,7 @@ def repo_pypa_urllib3(): third_party_python_package, name = "pypa_urllib3", target = "urllib3", - requirement = "urllib3==1.26.2", + requirement = "urllib3==1.26.4", ) def repo_pypa_wcwidth(): @@ -939,5 +951,5 @@ def repo_pypa_zipp(): third_party_python_package, name = "pypa_zipp", target = "zipp", - requirement = "zipp==3.4.0", + requirement = "zipp==3.4.1", )