mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into show-cluster-on-workspace-switch
This commit is contained in:
commit
d723474684
@ -36,11 +36,11 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make integration-win
|
||||
displayName: Run integration tests
|
||||
@ -76,11 +76,11 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
@ -122,13 +122,13 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make lint
|
||||
displayName: Lint
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
@ -164,4 +164,4 @@ jobs:
|
||||
displayName: Publish npm package
|
||||
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
||||
env:
|
||||
NPM_TOKEN: $(NPM_TOKEN)
|
||||
NPM_TOKEN: $(NPM_TOKEN)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
ignorePatterns: ["src/extensions/npm/extensions/api.d.ts"],
|
||||
ignorePatterns: ["src/extensions/npm/extensions/dist/**/*"],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
|
||||
31
.github/workflows/main.yml
vendored
31
.github/workflows/main.yml
vendored
@ -3,7 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
@ -27,13 +29,12 @@ jobs:
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: lensapp/lens
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git pull
|
||||
|
||||
- name: Using Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
@ -45,17 +46,19 @@ jobs:
|
||||
yarn install
|
||||
yarn typedocs-extensions-api
|
||||
|
||||
- name: mkdocs deploy latest
|
||||
- name: mkdocs deploy master
|
||||
if: contains(github.ref, 'refs/heads/master')
|
||||
run: |
|
||||
mike deploy --push latest
|
||||
mike deploy --push master
|
||||
|
||||
|
||||
- name: mkdocs deploy new release / tag
|
||||
if: contains(github.ref, 'refs/tags/v')
|
||||
- name: Get the release version
|
||||
if: contains(github.ref, 'refs/tags/v') # && !github.event.release.prerelease (generate pre-release docs until Lens 4.0.0 is GA, see #1408)
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
|
||||
- name: mkdocs deploy new release
|
||||
if: contains(github.ref, 'refs/tags/v') # && !github.event.release.prerelease (generate pre-release docs until Lens 4.0.0 is GA, see #1408)
|
||||
run: |
|
||||
mike deploy --push--update-aliases ${{ github.ref }} latest
|
||||
mike set-default --push ${{ github.ref }}
|
||||
|
||||
|
||||
|
||||
|
||||
mike deploy --push --update-aliases ${{ steps.get_version.outputs.VERSION }} latest
|
||||
mike set-default --push ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
|
||||
38
.github/workflows/mkdocs-delete-version.yml
vendored
Normal file
38
.github/workflows/mkdocs-delete-version.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Delete Documentation Version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version string to be deleted (e.g."v0.0.1")'
|
||||
required: true
|
||||
jobs:
|
||||
build:
|
||||
name: Delete docs Version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git
|
||||
pip install mike
|
||||
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
|
||||
- name: mkdocs delete version
|
||||
run: |
|
||||
mike delete --push ${{ github.event.inputs.version }}
|
||||
|
||||
38
.github/workflows/mkdocs-set-default-version.yml
vendored
Normal file
38
.github/workflows/mkdocs-set-default-version.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Update Default Documentation Version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version string to be default (e.g."v0.0.1")'
|
||||
required: true
|
||||
jobs:
|
||||
build:
|
||||
name: Update default docs Version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git
|
||||
pip install mike
|
||||
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
|
||||
- name: mkdocs update default version
|
||||
run: |
|
||||
mike set-default --push ${{ github.event.inputs.version }}
|
||||
|
||||
96
Makefile
96
Makefile
@ -1,4 +1,7 @@
|
||||
EXTENSIONS_DIR = ./extensions
|
||||
extensions = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir})
|
||||
extension_node_modules = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/node_modules)
|
||||
extension_dists = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/dist)
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
DETECTED_OS := Windows
|
||||
@ -6,79 +9,126 @@ else
|
||||
DETECTED_OS := $(shell uname)
|
||||
endif
|
||||
|
||||
.PHONY: init dev build test clean
|
||||
|
||||
init: install-deps download-bins compile-dev
|
||||
echo "Init done"
|
||||
|
||||
download-bins:
|
||||
binaries/client:
|
||||
yarn download-bins
|
||||
|
||||
install-deps:
|
||||
node_modules:
|
||||
yarn install --frozen-lockfile --verbose
|
||||
yarn check --verify-tree --integrity
|
||||
|
||||
static/build/LensDev.html:
|
||||
yarn compile:renderer
|
||||
|
||||
.PHONY: compile-dev
|
||||
compile-dev:
|
||||
yarn compile:main --cache
|
||||
yarn compile:renderer --cache
|
||||
|
||||
dev:
|
||||
ifeq ("$(wildcard static/build/main.js)","")
|
||||
make init
|
||||
endif
|
||||
.PHONY: dev
|
||||
dev: node_modules binaries/client build-extensions static/build/LensDev.html
|
||||
yarn dev
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
yarn lint
|
||||
|
||||
test: download-bins
|
||||
.PHONY: test
|
||||
test: binaries/client
|
||||
yarn test
|
||||
|
||||
.PHONY: integration-linux
|
||||
integration-linux: build-extension-types build-extensions
|
||||
yarn build:linux
|
||||
yarn integration
|
||||
|
||||
.PHONY: integration-mac
|
||||
integration-mac: build-extension-types build-extensions
|
||||
yarn build:mac
|
||||
yarn integration
|
||||
|
||||
.PHONY: integration-win
|
||||
integration-win: build-extension-types build-extensions
|
||||
yarn build:win
|
||||
yarn integration
|
||||
|
||||
.PHONY: test-app
|
||||
test-app:
|
||||
yarn test
|
||||
|
||||
build: install-deps download-bins build-extensions
|
||||
.PHONY: build
|
||||
build: node_modules binaries/client build-extensions
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
yarn dist:win
|
||||
else
|
||||
yarn dist
|
||||
endif
|
||||
|
||||
build-extensions:
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install && npm run build || exit $?);)
|
||||
$(extension_node_modules):
|
||||
cd $(@:/node_modules=) && npm install --no-audit --no-fund
|
||||
|
||||
test-extensions:
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install --dev && npm run test || exit $?);)
|
||||
$(extension_dists): src/extensions/npm/extensions/dist
|
||||
cd $(@:/dist=) && npm run build
|
||||
|
||||
build-npm: build-extension-types
|
||||
yarn npm:fix-package-version
|
||||
.PHONY: build-extensions
|
||||
build-extensions: $(extension_node_modules) $(extension_dists)
|
||||
|
||||
build-extension-types:
|
||||
.PHONY: test-extensions
|
||||
test-extensions: $(extension_node_modules)
|
||||
$(foreach dir, $(extensions), (cd $(dir) && npm run test || exit $?);)
|
||||
|
||||
.PHONY: copy-extension-themes
|
||||
copy-extension-themes:
|
||||
mkdir -p src/extensions/npm/extensions/dist/src/renderer/themes/
|
||||
cp $(wildcard src/renderer/themes/*.json) src/extensions/npm/extensions/dist/src/renderer/themes/
|
||||
|
||||
src/extensions/npm/extensions/__mocks__:
|
||||
cp -r __mocks__ src/extensions/npm/extensions/
|
||||
|
||||
src/extensions/npm/extensions/dist:
|
||||
yarn compile:extension-types
|
||||
|
||||
.PHONY: build-npm
|
||||
build-npm: build-extension-types copy-extension-themes src/extensions/npm/extensions/__mocks__
|
||||
yarn npm:fix-package-version
|
||||
|
||||
.PHONY: build-extension-types
|
||||
build-extension-types: src/extensions/npm/extensions/dist
|
||||
|
||||
.PHONY: publish-npm
|
||||
publish-npm: build-npm
|
||||
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
|
||||
cd src/extensions/npm/extensions && npm publish --access=public
|
||||
|
||||
clean:
|
||||
.PHONY: clean-extensions
|
||||
clean-extensions:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist binaries\client del /s /q binaries\client\*.*
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\dist del /s /q $(dir)\dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\node_modules del /s /q $(dir)\node_modules)
|
||||
else
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/node_modules)
|
||||
endif
|
||||
|
||||
.PHONY: clean-npm
|
||||
clean-npm:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist src\extensions\npm\extensions\dist del /s /q src\extensions\npm\extensions\dist
|
||||
if exist src\extensions\npm\extensions\__mocks__ del /s /q src\extensions\npm\extensions\__mocks__
|
||||
if exist src\extensions\npm\extensions\node_modules del /s /q src\extensions\npm\extensions\node_modules
|
||||
else
|
||||
rm -rf src/extensions/npm/extensions/dist
|
||||
rm -rf src/extensions/npm/extensions/__mocks__
|
||||
rm -rf src/extensions/npm/extensions/node_modules
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-npm clean-extensions
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist binaries\client del /s /q binaries\client
|
||||
if exist dist del /s /q dist\*.*
|
||||
if exist static\build del /s /q static\build\*.*
|
||||
else
|
||||
rm -rf binaries/client/*
|
||||
rm -rf binaries/client
|
||||
rm -rf dist/*
|
||||
rm -rf static/build/*
|
||||
endif
|
||||
|
||||
@ -36,7 +36,6 @@ brew cask install lens
|
||||
|
||||
> Prerequisites: Nodejs v12, make, yarn
|
||||
|
||||
* `make init` - initial compilation, installing deps, etc.
|
||||
* `make dev` - builds and starts the app
|
||||
* `make test` - run tests
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
# Contributing
|
||||
|
||||
There are multiple ways you can contribute to Lens - even if you are not a developer, you can still contribute. We are always looking for assistance with creating or updating documentation, testing the application, reporting and troubleshooting issues.
|
||||
There are multiple ways you can contribute to Lens. Even if you are not a developer, you can still contribute. We are always looking for assistance with creating or updating documentation, testing the application, reporting, and troubleshooting issues.
|
||||
|
||||
Here are some ideas how you can contribute!
|
||||
Here are some ways you can contribute!
|
||||
|
||||
* [Development](./development.md) – Help making Lens better.
|
||||
* [Maintaining the Project](./maintainers.md) – Become community maintainer and help us maintain the project.
|
||||
* [Development](./development.md) – Help make Lens better.
|
||||
* [Maintaining the Project](./maintainers.md) – Become a community maintainer and help us maintain the project.
|
||||
* [Extension Development](../extensions) – Add integrations via Lens Extensions.
|
||||
* [Documentation](./documentation.md) – Help improve Lens documentation.
|
||||
* [Promotion](./promotion.md) – Show your support, be an ambassador to Lens, write blogs and make videos!
|
||||
* [Promotion](./promotion.md) – Show your support, be an ambassador to Lens, write blogs, and make videos!
|
||||
|
||||
If you are an influencer, blogger or journalist, feel free to [spread the word](./promotion.md)!
|
||||
If you are an influencer, blogger, or journalist, feel free to [spread the word](./promotion.md)!
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. By participating and contributing to Lens, you are expected to uphold this code. Please report unacceptable behaviour to info@k8slens.dev
|
||||
This project adheres to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. By participating and contributing to Lens, you are expected to uphold this code. Please report unacceptable behaviour to info@k8slens.dev.
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
# Documentation
|
||||
|
||||
We are glad to see you are interested in contributing to Lens documentation. If this is the first Open Source project you will contribute to, we strongly suggest reading GitHub's excellent guide: [How to Contribute to Open Source](https://opensource.guide/how-to-contribute).
|
||||
We are glad to see you're interested in contributing to the Lens documentation. If this is the first Open Source project you've contributed to, we strongly suggest reading GitHub's excellent guide: [How to Contribute to Open Source](https://opensource.guide/how-to-contribute).
|
||||
|
||||
## Finding Documentation Issues to Work On
|
||||
|
||||
You can find a list of open documentation related issues [here](https://github.com/lensapp/lens/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Fdocumentation). When you find something you would like to work on:
|
||||
You can find a list of open documentation-related issues [here](https://github.com/lensapp/lens/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Fdocumentation). When you find something you would like to work on:
|
||||
|
||||
1. Express your interest to start working on an issue via comments.
|
||||
2. One of the maintainers will assign the issue for you.
|
||||
3. You can start working on the issue. Once done, simply submit a pull request.
|
||||
3. You can start working on the issue. When you're done, simply submit a pull request.
|
||||
|
||||
## Requirements for Documentation Pull Requests
|
||||
|
||||
@ -18,5 +18,5 @@ When you create a new pull request, we expect some requirements to be met.
|
||||
* When adding new documentation, add `New Documentation:` before the title. E.g. `New Documentation: Getting Started`
|
||||
* When fixing documentation, add `Fix Documentation:` before the title. E.g. `Fix Documentation: Getting Started`
|
||||
* When updating documentation, add `Update Documentation:` before the title. E.g. `Update Documentation: Getting Started`
|
||||
* If your Pull Request closes an issue you need to write `Closes #ISSUE_NUMBER` where the ISSUE_NUMBER is the number in the end of the link url that will link your pull request to the issue, when merged will close that issue.
|
||||
* For each pull request made, we run tests to check if there are any broken links, the markdown formatting is valid and the linter is passing.
|
||||
* If your Pull Request closes an issue, you must write `Closes #ISSUE_NUMBER` where the ISSUE_NUMBER is the number in the end of the link url or the relevent issue. This will link your pull request to the issue, and when it is merged, the issue will close.
|
||||
* For each pull request made, we run tests to check if there are any broken links, the markdown formatting is valid, and the linter is passing.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Maintainers
|
||||
|
||||
We are looking for community maintainers for the Lens project. Maintainers will be added to a special team with write permissions. These permissions consist of opening, closing, tagging and editing issues, and pull requests, as well as create and delete non protected branches.
|
||||
We are looking for community maintainers for the Lens project. Maintainers will be added to a special team with write permissions. These permissions consist of opening, closing, tagging, and editing issues and pull requests, as well as creating and deleting non-protected branches.
|
||||
|
||||
The responsibilities of a community maintainer are listed below.
|
||||
|
||||
@ -9,7 +9,7 @@ The responsibilities of a community maintainer are listed below.
|
||||
* **Labeling Issues:** Label issues accordingly.
|
||||
* **Finding Duplicates:** Finding and closing duplicate issues.
|
||||
* **Doing First Level Contact:** Getting more information on the issues (like version number or asking for clarification) if needed.
|
||||
* **Closing Irrelevant Issues:** Closing issues that are determined irrelevant, no longer needed, not relevant to the project and/or doesn't follow the issues guidelines.
|
||||
* **Closing Irrelevant Issues:** Closing issues that are determined irrelevant, no longer needed, not relevant to the project and/or don't follow the issues guidelines.
|
||||
|
||||
## Help with Contributions
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
# Promoting
|
||||
# Promotion
|
||||
|
||||
Help promote Lens! If you are not a developer (or even if you are), you can still contribute to the project, a lot, by helping us promote it. As we are a free open source project, the community is our most important asset, so here are some ways that you can help the project continue to grow.
|
||||
Help promote Lens! If you are not a developer (or even if you are), you can still contribute to the project – a lot – by helping us to promote it. As we are a free and open source project, the community is our most important asset. Here are some ways that you can help the project continue to grow.
|
||||
|
||||
## Follow, Like, Recommend, Favorite, Vote and Star Us
|
||||
|
||||
There are many sites where you can vote, recommend, favorite and star us.
|
||||
There are many sites where you can vote, recommend, favorite, and star us.
|
||||
|
||||
* [Twitter](https://twitter.com/k8slens) - Like, comment and retweet our posts, and follow us on Twitter
|
||||
* [Medium](https://medium.com/k8slens) - Give claps to our articles and follow us on Medium
|
||||
* [GitHub](https://github.com/lensapp/lens) - Become a stargazer on GitHub
|
||||
* [StackShare](https://stackshare.io/lens) - Indicate you are using Lens and follow us on StackShare
|
||||
* [Reddit](https://www.reddit.com/search/?q=lens%20kubernetes&sort=new) - Upvote and be an ambassador of Lens by participating relevant discussions on Reddit
|
||||
* [Hacker News](https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=lens%20kubernetes&sort=byDate&type=story) - Upvote and be an ambassador of Lens by participating relevant discussions on Hacker News
|
||||
* [Twitter](https://twitter.com/k8slens) - Like, comment and retweet our posts, and follow us on Twitter.
|
||||
* [Medium](https://medium.com/k8slens) - Give claps to our articles and follow us on Medium.
|
||||
* [GitHub](https://github.com/lensapp/lens) - Become a stargazer on GitHub.
|
||||
* [StackShare](https://stackshare.io/lens) - Indicate you are using Lens and follow us on StackShare.
|
||||
* [Reddit](https://www.reddit.com/search/?q=lens%20kubernetes&sort=new) - Upvote and be a Lens ambassador by participating in relevant discussions on Reddit.
|
||||
* [Hacker News](https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=lens%20kubernetes&sort=byDate&type=story) - Upvote and be a Lens ambassador by participating in relevant discussions on Hacker News.
|
||||
|
||||
## Write Blogs or Make Videos About Us
|
||||
|
||||
|
||||
@ -20,12 +20,11 @@ For an overview of the Lens Extension API, refer to the [Common Capabilities](ca
|
||||
|
||||
Here is what each section of the Lens Extension API docs can help you with:
|
||||
|
||||
* **Get Started** teaches fundamental concepts for building extensions with the Hello World sample.
|
||||
* **Getting Started** teaches fundamental concepts for building extensions with the Hello World sample.
|
||||
* **Extension Capabilities** dissects Lens's Extension API into smaller categories and points you to more detailed topics.
|
||||
* **Extension Guides** includes guides and code samples that explain specific usages of Lens Extension API.
|
||||
* **Testing and Publishing** includes in-depth guides on various extension development topics, such as testing and publishing extensions.
|
||||
* **Advanced Topics** explains advanced concepts such as integrating with 3rd party applications/services.
|
||||
* **References** contains exhaustive references for the Lens Extension API, Contribution Points, and many other topics.
|
||||
* **API Reference** contains exhaustive references for the Lens Extension API, Contribution Points, and many other topics.
|
||||
|
||||
## What's New
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Theme color reference
|
||||
# Theme Color Reference
|
||||
You can use theme-based CSS Variables to style an extension according to the active theme.
|
||||
|
||||
## Base colors
|
||||
## Base Colors
|
||||
- `--blue`: blue color.
|
||||
- `--magenta`: magenta color.
|
||||
- `--golden`: gold/yellow color.
|
||||
@ -17,16 +17,16 @@ You can use theme-based CSS Variables to style an extension according to the act
|
||||
- `--colorTerminated`: terminated, closed, stale color.
|
||||
- `--boxShadow`: semi-transparent box-shadow color.
|
||||
|
||||
## Text colors
|
||||
## Text Colors
|
||||
- `--textColorPrimary`: foreground text color.
|
||||
- `--textColorSecondary`: foreground text color for different paragraps, parts of text.
|
||||
- `--textColorAccent`: foreground text color to highlight its parts.
|
||||
|
||||
## Border colors
|
||||
## Border Colors
|
||||
- `--borderColor`: border color.
|
||||
- `--borderFaintColor`: fainted (lighter or darker, which depends on the theme) border color.
|
||||
|
||||
## Layout colors
|
||||
## Layout Colors
|
||||
- `--mainBackground`: main background color for the app.
|
||||
- `--contentColor`: background color for panels contains some data.
|
||||
- `--layoutBackground`: background color for layout parts.
|
||||
@ -34,19 +34,19 @@ You can use theme-based CSS Variables to style an extension according to the act
|
||||
- `--layoutTabsActiveColor`: foreground color for general tabs.
|
||||
- `--layoutTabsLineColor`: background color for lines under general tabs.
|
||||
|
||||
## Sidebar colors
|
||||
## Sidebar Colors
|
||||
- `--sidebarLogoBackground`: background color behind logo in sidebar.
|
||||
- `--sidebarActiveColor`: foreground color for active menu items in sidebar.
|
||||
- `--sidebarSubmenuActiveColor`: foreground color for active submenu items in sidebar.
|
||||
- `--sidebarBackground`: background color for sidebar.
|
||||
|
||||
## Button colors
|
||||
## Button Colors
|
||||
- `--buttonPrimaryBackground`: button background color for primary actions.
|
||||
- `--buttonDefaultBackground`: default button background color.
|
||||
- `--buttonAccentBackground`: accent button background color.
|
||||
- `--buttonDisabledBackground`: disabled button background color.
|
||||
|
||||
## Table colors
|
||||
## Table Colors
|
||||
- `--tableBgcStripe`: background color for odd rows in table.
|
||||
- `--tableBgcSelected`: background color for selected row in table.
|
||||
- `--tableHeaderBackground`: background color for table header.
|
||||
@ -55,12 +55,12 @@ You can use theme-based CSS Variables to style an extension according to the act
|
||||
- `--tableHeaderColor`: foreground color for table header.
|
||||
- `--tableSelectedRowColor`: foreground color for selected row in table.
|
||||
|
||||
## Dock colors
|
||||
## Dock Colors
|
||||
- `--dockHeadBackground`: background color for dock's header.
|
||||
- `--dockInfoBackground`: background color for dock's info panel.
|
||||
- `--dockInfoBorderColor`: border color for dock's info panel.
|
||||
|
||||
## Helm chart colors
|
||||
## Helm Chart Colors
|
||||
- `--helmLogoBackground`: background color for chart logo.
|
||||
- `--helmImgBackground`: background color for chart image.
|
||||
- `--helmStableRepo`: background color for stable repo.
|
||||
@ -77,7 +77,7 @@ You can use theme-based CSS Variables to style an extension according to the act
|
||||
- `--helmDescriptionPreBackground`: Helm chart description pre background color.
|
||||
- `--helmDescriptionPreColor`: Helm chart description pre foreground color.
|
||||
|
||||
## Terminal colors
|
||||
## Terminal Colors
|
||||
- `--terminalBackground`: Terminal background color.
|
||||
- `--terminalForeground`: Terminal foreground color.
|
||||
- `--terminalCursor`: Terminal cursor color.
|
||||
@ -100,17 +100,17 @@ You can use theme-based CSS Variables to style an extension according to the act
|
||||
- `--terminalBrightCyan`: Terminal bright cyan color.
|
||||
- `--terminalBrightWhite`: Terminal bright white color.
|
||||
|
||||
## Dialog colors
|
||||
## Dialog Colors
|
||||
- `--dialogHeaderBackground`: background color for dialog header.
|
||||
- `--dialogFooterBackground`: background color for dialog footer.
|
||||
|
||||
## Detail panel (Drawer) colors
|
||||
## Detail Panel (Drawer) Colors
|
||||
- `--drawerTitleText`: drawer title foreground color.
|
||||
- `--drawerSubtitleBackground`: drawer subtitle foreground color.
|
||||
- `--drawerItemNameColor`: foreground color for item name in drawer.
|
||||
- `--drawerItemValueColor`: foreground color for item value in drawer.
|
||||
|
||||
## Misc colors
|
||||
## Misc Colors
|
||||
- `--logsBackground`: background color for pod logs.
|
||||
- `--clusterMenuBackground`: background color for cluster menu.
|
||||
- `--clusterMenuBorderColor`: border color for cluster menu.
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
# Common Capabilities
|
||||
|
||||
Common Capabilities are important building blocks for your extensions. Almost all extensions use some of these functionalities. Here is how you can take advantage of them.
|
||||
Here we will discuss common and important building blocks for your extensions, and explain how you can use them. Almost all extensions use some of these functionalities.
|
||||
|
||||
## Main Extension
|
||||
|
||||
A main extension runs in the background and, apart from app menu items, does not add content to the Lens UI. If you want to see logs from this extension you need to start Lens from the command line.
|
||||
The main extension runs in the background. It adds app menu items to the Lens UI. In order to see logs from this extension, you need to start Lens from the command line.
|
||||
|
||||
### Activate
|
||||
|
||||
An extension can register a custom callback that is executed when an extension is activated (started).
|
||||
This extension can register a custom callback that is executed when an extension is activated (started).
|
||||
|
||||
``` javascript
|
||||
import { LensMainExtension } from "@k8slens/extensions"
|
||||
@ -22,7 +22,7 @@ export default class ExampleMainExtension extends LensMainExtension {
|
||||
|
||||
### Deactivate
|
||||
|
||||
An extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
|
||||
``` javascript
|
||||
import { LensMainExtension } from "@k8slens/extensions"
|
||||
@ -36,7 +36,7 @@ export default class ExampleMainExtension extends LensMainExtension {
|
||||
|
||||
### App Menus
|
||||
|
||||
An extension can register custom App menus that will be displayed on OS native menus.
|
||||
This extension can register custom app menus that will be displayed on OS native menus.
|
||||
|
||||
Example:
|
||||
|
||||
@ -58,11 +58,11 @@ export default class ExampleMainExtension extends LensMainExtension {
|
||||
|
||||
## Renderer Extension
|
||||
|
||||
A renderer extension runs in a browser context and it's visible directly via Lens main window. If you want to see logs from this extension you need to check them via View -> Toggle Developer Tools -> Console.
|
||||
The renderer extension runs in a browser context, and is visible in Lens's main window. In order to see logs from this extension you need to check them via **View** > **Toggle Developer Tools** > **Console**.
|
||||
|
||||
### Activate
|
||||
|
||||
An extension can register a custom callback that is executed when an extension is activated (started).
|
||||
This extension can register a custom callback that is executed when an extension is activated (started).
|
||||
|
||||
``` javascript
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
@ -76,7 +76,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
|
||||
### Deactivate
|
||||
|
||||
An extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
|
||||
``` javascript
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
@ -90,7 +90,7 @@ export default class ExampleMainExtension extends LensRendererExtension {
|
||||
|
||||
### Global Pages
|
||||
|
||||
An extension can register custom global pages (views) to Lens main window. Global page is a full screen page that hides all the other content from a window.
|
||||
This extension can register custom global pages (views) to Lens's main window. The global page is a full-screen page that hides all other content from a window.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
@ -122,7 +122,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
|
||||
### App Preferences
|
||||
|
||||
An extension can register custom app preferences. An extension is responsible for storing a state for custom preferences.
|
||||
This extension can register custom app preferences. It is responsible for storing a state for custom preferences.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
@ -146,7 +146,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
|
||||
### Cluster Pages
|
||||
|
||||
An extension can register custom cluster pages which are visible in a cluster menu when a cluster is opened.
|
||||
This extension can register custom cluster pages. These pages are visible in a cluster menu when a cluster is opened.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
@ -179,7 +179,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
|
||||
### Cluster Features
|
||||
|
||||
An extension can register installable features for a cluster. A cluster feature is visible in "Cluster Settings" page.
|
||||
This extension can register installable features for a cluster. These features are visible in the "Cluster Settings" page.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
@ -208,7 +208,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
|
||||
### Status Bar Items
|
||||
|
||||
An extension can register custom icons/texts to a status bar area.
|
||||
This extension can register custom icons and text to a status bar area.
|
||||
|
||||
``` typescript
|
||||
import React from "react";
|
||||
@ -230,7 +230,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
|
||||
### Kubernetes Object Menu Items
|
||||
|
||||
An extension can register custom menu items (actions) for specified Kubernetes kinds/apiVersions.
|
||||
This extension can register custom menu items (actions) for specified Kubernetes kinds/apiVersions.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
@ -253,7 +253,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
|
||||
### Kubernetes Object Details
|
||||
|
||||
An extension can register custom details (content) for specified Kubernetes kinds/apiVersions.
|
||||
This extension can register custom details (content) for specified Kubernetes kinds/apiVersions.
|
||||
|
||||
``` typescript
|
||||
import React from "react"
|
||||
|
||||
@ -4,14 +4,12 @@ Lens provides a set of global styles and UI components that can be used by any e
|
||||
|
||||
## Layout
|
||||
|
||||
For layout tasks Lens is using [flex.box](https://www.npmjs.com/package/flex.box) library which provides helpful class names to specify some of the [flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) properties. For example, `div` with class names:
|
||||
For layout tasks, Lens uses the [flex.box](https://www.npmjs.com/package/flex.box) library which provides helpful class names to specify some of the [flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) properties. For example, consider the following HTML and its associated CSS properties:
|
||||
|
||||
```html
|
||||
<div className="flex column align-center"></div>
|
||||
```
|
||||
|
||||
at the end will have following css properties:
|
||||
|
||||
```css
|
||||
div {
|
||||
display: flex;
|
||||
@ -20,11 +18,11 @@ div {
|
||||
}
|
||||
```
|
||||
|
||||
However, feel free to use any styling technique or framework like [Emotion](https://github.com/emotion-js/emotion) or just plain CSS if you prefer.
|
||||
However, you are free to use any styling technique or framework you like, including [Emotion](https://github.com/emotion-js/emotion) or even plain CSS.
|
||||
|
||||
### Layout Variables
|
||||
|
||||
There is a set of CSS Variables available for extensions to use for basic layout needs. They are located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
||||
There is a set of CSS variables available for for basic layout needs. They are located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
||||
|
||||
```css
|
||||
--unit: 8px;
|
||||
@ -33,7 +31,7 @@ There is a set of CSS Variables available for extensions to use for basic layout
|
||||
--border-radius: 3px;
|
||||
```
|
||||
|
||||
They are intended to set consistent margins and paddings across components, e.g.
|
||||
These variables are intended to set consistent margins and paddings across components. For example:
|
||||
|
||||
```css
|
||||
.status {
|
||||
@ -44,18 +42,18 @@ They are intended to set consistent margins and paddings across components, e.g.
|
||||
|
||||
## Themes
|
||||
|
||||
Lens is using two built-in themes defined in [the themes directory](https://github.com/lensapp/lens/tree/master/src/renderer/themes), one for light, and one for dark color schemes.
|
||||
Lens uses two built-in themes defined in [the themes directory](https://github.com/lensapp/lens/tree/master/src/renderer/themes) – one light and one dark.
|
||||
|
||||
### Theme Variables
|
||||
|
||||
When Lens is loaded, it transforms the selected theme `json` file into a list of [CSS Custom Properties (CSS Variables)](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) which then gets injected into the `:root` element so any of the down-level components can use them.
|
||||
When Lens is loaded, it transforms the selected theme's `json` file into a list of [CSS Custom Properties (CSS Variables)](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties). This list then gets injected into the `:root` element so that any of the down-level components can use them.
|
||||

|
||||
|
||||
When the user changes the theme, the process is repeated, and new CSS Variables appear instead of previous ones.
|
||||
When the user changes the theme, the above process is repeated, and new CSS variables appear, replacing the previous ones.
|
||||
|
||||
If you want to follow a selected theme to keep the 'native' Lens look and feel, respecting the light/dark appearance of your extension, you can use the provided variables and built-in Lens components such as `Button`, `Select`, `Table`, etc.
|
||||
If you want to preserve Lens's native look and feel, with respect to the lightness or darkness of your extension, you can use the provided variables and built-in Lens components such as `Button`, `Select`, `Table`, and so on.
|
||||
|
||||
There is a set of CSS Variables available for extensions to use for theming. They are all located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
||||
There is a set of CSS variables available for extensions to use for theming. They are all located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
||||
|
||||
```css
|
||||
--font-main: 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
||||
@ -90,7 +88,7 @@ as well as in [the theme modules](https://github.com/lensapp/lens/tree/master/sr
|
||||
...
|
||||
```
|
||||
|
||||
They can be used in form of `var(--magenta)`, e.g.
|
||||
These variables can be used in the following form: `var(--magenta)`. For example:
|
||||
|
||||
```css
|
||||
.status {
|
||||
@ -99,14 +97,14 @@ They can be used in form of `var(--magenta)`, e.g.
|
||||
}
|
||||
```
|
||||
|
||||
A complete list of all themable colors can be found in the [color reference](../color-reference).
|
||||
A complete list of themable colors can be found in the [Color Reference](../color-reference).
|
||||
|
||||
### Theme Switching
|
||||
|
||||
When the light theme is active, the `<body>` element gets a "theme-light" class, `<body class="theme-light">`. If the class isn't there, assume the theme is dark. The active theme can be changed in the `Preferences` page:
|
||||
When the light theme is active, the `<body>` element gets a "theme-light" class, or: `<body class="theme-light">`. If the class isn't there, the theme defaults to dark. The active theme can be changed in the **Preferences** page:
|
||||

|
||||
|
||||
Currently, there is no prescribed way of detecting changes to the theme in JavaScript. [This issue](https://github.com/lensapp/lens/issues/1336) hopes to improve on this. In the meantime, you can use a [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to observe the `<body>` element's `class` attribute to see if the "theme-light" class gets added to it:
|
||||
Currently, there is no prescribed way of detecting changes to the theme in JavaScript. [This issue](https://github.com/lensapp/lens/issues/1336) has been raised to resolve this problem. In the meantime, you can use a [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) in order to observe the `<body>` element's `class` attribute in order to see if the "theme-light" class gets added to it:
|
||||
|
||||
```javascript
|
||||
...
|
||||
@ -137,9 +135,9 @@ Currently, there is no prescribed way of detecting changes to the theme in JavaS
|
||||
|
||||
## Injected Styles
|
||||
|
||||
Every extension is affected by list of default global styles defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss). These are basic browser resets and element styles like setting the `box-sizing` property for every element, default text and background colors, default font sizes, basic heading formatting, etc.
|
||||
Every extension is affected by the list of default global styles defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss). These are basic browser resets and element styles, including setting the `box-sizing` property for every element, default text and background colors, default font sizes, basic heading formatting, and so on.
|
||||
|
||||
Extension may overwrite these if needed. They have low CSS specificity, so overriding them should be fairly easy.
|
||||
Extensions may overwrite these defaults if needed. They have low CSS specificity, so overriding them should be fairly easy.
|
||||
|
||||
## CSS-in-JS
|
||||
|
||||
|
||||
@ -2,29 +2,35 @@
|
||||
|
||||
## Console.log
|
||||
|
||||
`console.log()` might be handy for extension developers to prints out info/errors from extensions. To use `console.log`, note that Lens is based on Electron. Electron has two types of processes: [Main and Renderer](https://www.electronjs.org/docs/tutorial/quick-start#main-and-renderer-processes).
|
||||
Extension developers might find `console.log()` useful for printing out information and errors from extensions. To use `console.log()`, note that Lens is based on Electron, and that Electron has two types of processes: [Main and Renderer](https://www.electronjs.org/docs/tutorial/quick-start#main-and-renderer-processes).
|
||||
|
||||
### Renderer Process Logs
|
||||
|
||||
`console.log()` in Renderer process is printed in the Console in Developer Tools (View > Toggle Developer Tools).
|
||||
In the Renderer process, `console.log()` is printed in the Console in Developer Tools (**View** > **Toggle Developer Tools**).
|
||||
|
||||
### Main Process Logs
|
||||
|
||||
To view the logs from the main process is a bit trickier, since you cannot open developer tools for them. On MacOSX, one way is to run Lens from the terminal.
|
||||
Viewing the logs from the Main process is a little trickier, since they cannot be printed using Developer Tools.
|
||||
|
||||
#### macOS
|
||||
|
||||
On macOS, view the Main process logs by running Lens from the terminal:
|
||||
|
||||
```bash
|
||||
/Applications/Lens.app/Contents/MacOS/Lens
|
||||
```
|
||||
|
||||
You can alos use [Console.app](https://support.apple.com/en-gb/guide/console/welcome/mac) to view logs from Lens.
|
||||
You can also use [Console.app](https://support.apple.com/en-gb/guide/console/welcome/mac) to view the Main process logs.
|
||||
|
||||
On linux, you can get PID of Lens first
|
||||
#### Linux
|
||||
|
||||
On Linux, you can access the Main process logs using the Lens PID. First get the PID:
|
||||
|
||||
```bash
|
||||
ps aux | grep Lens | grep -v grep
|
||||
```
|
||||
|
||||
And get logs by the PID
|
||||
Then get the Main process logs using the PID:
|
||||
|
||||
```bash
|
||||
tail -f /proc/[pid]/fd/1 # stdout (console.log)
|
||||
|
||||
@ -13,7 +13,7 @@ We recommend:
|
||||
|
||||
Lens has been tested on the following platforms:
|
||||
|
||||
* OS X
|
||||
* macOS
|
||||
* Windows
|
||||
* Linux
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ export default class SupportPageMainExtension extends LensMainExtension {
|
||||
parentId: "help",
|
||||
label: "Support",
|
||||
click: () => {
|
||||
this.navigate("/support");
|
||||
this.navigate();
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -5,8 +5,6 @@ import { SupportPage } from "./src/support";
|
||||
export default class SupportPageRendererExtension extends LensRendererExtension {
|
||||
globalPages: Interface.PageRegistration[] = [
|
||||
{
|
||||
id: "support",
|
||||
routePath: "/support",
|
||||
components: {
|
||||
Page: SupportPage,
|
||||
}
|
||||
@ -16,7 +14,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
|
||||
statusBarItems: Interface.StatusBarRegistration[] = [
|
||||
{
|
||||
item: (
|
||||
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate("/support")}>
|
||||
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate()}>
|
||||
<Component.Icon interactive material="help" smallest/>
|
||||
</div>
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -2,7 +2,7 @@
|
||||
"name": "kontena-lens",
|
||||
"productName": "Lens",
|
||||
"description": "Lens - The Kubernetes IDE",
|
||||
"version": "4.0.0-beta.2",
|
||||
"version": "4.0.0-beta.3",
|
||||
"main": "static/build/main.js",
|
||||
"copyright": "© 2020, Mirantis, Inc.",
|
||||
"license": "MIT",
|
||||
@ -21,7 +21,7 @@
|
||||
"compile:main": "yarn run webpack --config webpack.main.ts",
|
||||
"compile:renderer": "yarn run webpack --config webpack.renderer.ts",
|
||||
"compile:i18n": "yarn run lingui compile",
|
||||
"compile:extension-types": "yarn run rollup --config src/extensions/rollup.config.js",
|
||||
"compile:extension-types": "yarn run tsc -p ./tsconfig.extensions.json --outDir src/extensions/npm/extensions/dist",
|
||||
"npm:fix-package-version": "yarn run ts-node build/set_npm_version.ts",
|
||||
"build:linux": "yarn run compile && electron-builder --linux --dir -c.productName=Lens",
|
||||
"build:mac": "yarn run compile && electron-builder --mac --dir -c.productName=Lens",
|
||||
@ -77,7 +77,8 @@
|
||||
"^@lingui/macro$": "<rootDir>/__mocks__/@linguiMacro.ts"
|
||||
},
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/dist"
|
||||
"<rootDir>/dist",
|
||||
"<rootDir>/src/extensions/npm"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/src/jest.setup.ts"
|
||||
@ -271,7 +272,6 @@
|
||||
"@lingui/react": "^3.0.0-13",
|
||||
"@material-ui/core": "^4.10.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@testing-library/jest-dom": "^5.11.5",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@types/chart.js": "^2.9.21",
|
||||
@ -369,10 +369,6 @@
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-select": "^3.1.0",
|
||||
"react-window": "^1.8.5",
|
||||
"rollup": "^2.28.2",
|
||||
"rollup-plugin-dts": "^1.4.13",
|
||||
"rollup-plugin-ignore-import": "^1.3.2",
|
||||
"rollup-pluginutils": "^2.8.2",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sharp": "^0.26.1",
|
||||
"spectron": "11.0.0",
|
||||
|
||||
@ -38,15 +38,18 @@ describe("workspace store tests", () => {
|
||||
expect(() => ws.removeWorkspaceById(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
|
||||
})
|
||||
|
||||
it("can update default workspace name", () => {
|
||||
it("can update workspace description", () => {
|
||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||
|
||||
ws.addWorkspace(new Workspace({
|
||||
id: WorkspaceStore.defaultId,
|
||||
const workspace = ws.addWorkspace(new Workspace({
|
||||
id: "foobar",
|
||||
name: "foobar",
|
||||
}));
|
||||
|
||||
expect(ws.currentWorkspace.name).toBe("foobar");
|
||||
workspace.description = "Foobar description";
|
||||
ws.updateWorkspace(workspace);
|
||||
|
||||
expect(ws.getById("foobar").description).toBe("Foobar description");
|
||||
})
|
||||
|
||||
it("can add workspaces", () => {
|
||||
|
||||
@ -4,6 +4,6 @@ export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Pr
|
||||
let timer: NodeJS.Timeout;
|
||||
return (...params: any[]) => new Promise((resolve, reject) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => resolve(func.apply(this, params)), timeout);
|
||||
timer = global.setTimeout(() => resolve(func.apply(this, params)), timeout);
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,20 +153,20 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
@action
|
||||
addWorkspace(workspace: Workspace) {
|
||||
const { id, name } = workspace;
|
||||
const existingWorkspace = this.getById(id);
|
||||
if (!name.trim() || this.getByName(name.trim())) {
|
||||
return;
|
||||
}
|
||||
if (existingWorkspace) {
|
||||
Object.assign(existingWorkspace, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "update"})
|
||||
} else {
|
||||
appEventBus.emit({name: "workspace", action: "add"})
|
||||
}
|
||||
this.workspaces.set(id, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "add"})
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@action
|
||||
updateWorkspace(workspace: Workspace) {
|
||||
this.workspaces.set(workspace.id, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "update"});
|
||||
}
|
||||
|
||||
@action
|
||||
removeWorkspace(workspace: Workspace) {
|
||||
this.removeWorkspaceById(workspace.id)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Extension-api types generation bundle (used by rollup.js)
|
||||
// Extension-api types generation bundle
|
||||
|
||||
export * from "./core-api"
|
||||
export * from "./renderer-api"
|
||||
|
||||
@ -2,14 +2,18 @@ import type { MenuRegistration } from "./registries/menu-registry";
|
||||
import { observable } from "mobx";
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import { WindowManager } from "../main/window-manager";
|
||||
import { getPageUrl } from "./registries/page-registry"
|
||||
import { getExtensionPageUrl } from "./registries/page-registry"
|
||||
|
||||
export class LensMainExtension extends LensExtension {
|
||||
@observable.shallow appMenus: MenuRegistration[] = []
|
||||
|
||||
async navigate(location?: string, frameId?: number) {
|
||||
async navigate<P extends object>(pageId?: string, params?: P, frameId?: number) {
|
||||
const windowManager = WindowManager.getInstance<WindowManager>();
|
||||
const url = getPageUrl(this, location); // get full path to extension's page
|
||||
await windowManager.navigate(url, frameId);
|
||||
const pageUrl = getExtensionPageUrl({
|
||||
extensionId: this.name,
|
||||
pageId: pageId,
|
||||
params: params ?? {}, // compile to url with params
|
||||
});
|
||||
await windowManager.navigate(pageUrl, frameId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
||||
import { observable } from "mobx";
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import { getPageUrl } from "./registries/page-registry"
|
||||
import { getExtensionPageUrl } from "./registries/page-registry"
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
@observable.shallow globalPages: PageRegistration[] = []
|
||||
@ -15,8 +15,13 @@ export class LensRendererExtension extends LensExtension {
|
||||
@observable.shallow kubeObjectDetailItems: KubeObjectDetailRegistration[] = []
|
||||
@observable.shallow kubeObjectMenuItems: KubeObjectMenuRegistration[] = []
|
||||
|
||||
async navigate(location?: string) {
|
||||
async navigate<P extends object>(pageId?: string, params?: P) {
|
||||
const { navigate } = await import("../renderer/navigation");
|
||||
navigate(getPageUrl(this, location));
|
||||
const pageUrl = getExtensionPageUrl({
|
||||
extensionId: this.name,
|
||||
pageId: pageId,
|
||||
params: params ?? {}, // compile to url with params
|
||||
});
|
||||
navigate(pageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
3
src/extensions/npm/extensions/.gitignore
vendored
3
src/extensions/npm/extensions/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
api.d.ts
|
||||
dist/
|
||||
yarn.lock
|
||||
__mocks__/
|
||||
|
||||
@ -5,9 +5,12 @@
|
||||
"version": "0.0.0",
|
||||
"copyright": "© 2020, Mirantis, Inc.",
|
||||
"license": "MIT",
|
||||
"types": "api.d.ts",
|
||||
"main": "dist/src/extensions/extension-api.js",
|
||||
"types": "dist/src/extensions/extension-api.d.ts",
|
||||
"files": [
|
||||
"api.d.ts"
|
||||
"dist/**/*.ts",
|
||||
"__mocks__/*.ts",
|
||||
"dist/**/*.js"
|
||||
],
|
||||
"author": {
|
||||
"name": "Mirantis, Inc.",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getPageUrl, globalPageRegistry } from "../page-registry"
|
||||
import { getExtensionPageUrl, globalPageRegistry, PageRegistration } from "../page-registry"
|
||||
import { LensExtension } from "../../lens-extension"
|
||||
import React from "react";
|
||||
|
||||
@ -18,20 +18,19 @@ describe("getPageUrl", () => {
|
||||
})
|
||||
|
||||
it("returns a page url for extension", () => {
|
||||
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
||||
expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar")
|
||||
})
|
||||
|
||||
it("allows to pass base url as parameter", () => {
|
||||
expect(getPageUrl(ext, "/test")).toBe("/extension/foo-bar/test")
|
||||
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test")
|
||||
})
|
||||
|
||||
it("removes @", () => {
|
||||
ext.manifest.name = "@foo/bar"
|
||||
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
||||
expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo-bar")
|
||||
})
|
||||
|
||||
it("adds / prefix", () => {
|
||||
expect(getPageUrl(ext, "test")).toBe("/extension/foo-bar/test")
|
||||
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test")
|
||||
})
|
||||
})
|
||||
|
||||
@ -57,12 +56,24 @@ describe("globalPageRegistry", () => {
|
||||
id: "another-page",
|
||||
components: {
|
||||
Page: () => React.createElement('Text')
|
||||
},
|
||||
},
|
||||
{
|
||||
components: {
|
||||
Page: () => React.createElement('Default')
|
||||
}
|
||||
},
|
||||
], ext)
|
||||
})
|
||||
|
||||
describe("getByPageMenuTarget", () => {
|
||||
it("matching to first registered page without id", () => {
|
||||
const page = globalPageRegistry.getByPageMenuTarget({ extensionId: ext.name })
|
||||
expect(page.id).toEqual(undefined);
|
||||
expect(page.extensionId).toEqual(ext.name);
|
||||
expect(page.routePath).toEqual(getExtensionPageUrl({ extensionId: ext.name }));
|
||||
});
|
||||
|
||||
it("returns matching page", () => {
|
||||
const page = globalPageRegistry.getByPageMenuTarget({
|
||||
pageId: "test-page",
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
// Base class for extensions-api registries
|
||||
import { action, observable } from "mobx";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
|
||||
export class BaseRegistry<T = any> {
|
||||
export class BaseRegistry<T = object, I extends T = T> {
|
||||
private items = observable<T>([], { deep: false });
|
||||
|
||||
getItems(): T[] {
|
||||
return this.items.toJS();
|
||||
getItems(): I[] {
|
||||
return this.items.toJS() as I[];
|
||||
}
|
||||
|
||||
add(items: T | T[], ext?: LensExtension): () => void; // allow method overloading with required "ext"
|
||||
@action
|
||||
add(items: T | T[]) {
|
||||
const normalizedItems = (Array.isArray(items) ? items : [items])
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
// Extensions-api -> Register page menu items
|
||||
|
||||
import type { IconProps } from "../../renderer/components/icon";
|
||||
import type React from "react";
|
||||
import { action } from "mobx";
|
||||
import type { IconProps } from "../../renderer/components/icon";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
|
||||
export interface PageMenuTarget {
|
||||
pageId: string;
|
||||
export interface PageMenuTarget<P extends object = any> {
|
||||
extensionId?: string;
|
||||
params?: object;
|
||||
pageId?: string;
|
||||
params?: P;
|
||||
}
|
||||
|
||||
export interface PageMenuRegistration {
|
||||
@ -22,19 +21,19 @@ export interface PageMenuComponents {
|
||||
Icon: React.ComponentType<IconProps>;
|
||||
}
|
||||
|
||||
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
||||
|
||||
export class PageMenuRegistry extends BaseRegistry<PageMenuRegistration, Required<PageMenuRegistration>> {
|
||||
@action
|
||||
add(items: T[], ext?: LensExtension) {
|
||||
const normalizedItems = items.map((menu) => {
|
||||
if (menu.target && !menu.target.extensionId) {
|
||||
menu.target.extensionId = ext.name
|
||||
}
|
||||
return menu
|
||||
add(items: PageMenuRegistration[], ext: LensExtension) {
|
||||
const normalizedItems = items.map(menuItem => {
|
||||
menuItem.target = {
|
||||
extensionId: ext.name,
|
||||
...(menuItem.target || {}),
|
||||
};
|
||||
return menuItem
|
||||
})
|
||||
return super.add(normalizedItems);
|
||||
}
|
||||
}
|
||||
|
||||
export const globalPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||
export const clusterPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||
export const globalPageMenuRegistry = new PageMenuRegistry();
|
||||
export const clusterPageMenuRegistry = new PageMenuRegistry();
|
||||
|
||||
@ -1,59 +1,96 @@
|
||||
// Extensions-api -> Custom page registration
|
||||
|
||||
import React from "react";
|
||||
import type { PageMenuTarget } from "./page-menu-registry";
|
||||
import type React from "react";
|
||||
import path from "path";
|
||||
import { action } from "mobx";
|
||||
import { compile } from "path-to-regexp";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { LensExtension } from "../lens-extension"
|
||||
import type { PageMenuTarget } from "./page-menu-registry";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
import logger from "../../main/logger";
|
||||
|
||||
export interface PageRegistration {
|
||||
id: string; // will be automatically prefixed with extension name
|
||||
routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
|
||||
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
|
||||
/**
|
||||
* Page ID or additional route path to indicate uniqueness within current extension registered pages
|
||||
* Might contain special url placeholders, e.g. "/users/:userId?" (? - marks as optional param)
|
||||
* When not provided, first registered page without "id" would be used for page-menus without target.pageId for same extension
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Alias to page ID which assume to be used as path with possible :param placeholders
|
||||
* @deprecated
|
||||
*/
|
||||
routePath?: string;
|
||||
/**
|
||||
* Strict route matching to provided page-id, read also: https://reactrouter.com/web/api/NavLink/exact-bool
|
||||
* In case when more than one page registered at same extension "pageId" is required to identify different pages,
|
||||
* It might be useful to provide `exact: true` in some cases to avoid overlapping routes.
|
||||
* Without {exact:true} second page never matches since first page-id/route already includes partial route.
|
||||
* @example const pages = [
|
||||
* {id: "/users", exact: true},
|
||||
* {id: "/users/:userId?"}
|
||||
* ]
|
||||
* Pro-tip: registering pages in opposite order will make same effect without "exact".
|
||||
*/
|
||||
exact?: boolean;
|
||||
components: PageComponents;
|
||||
}
|
||||
|
||||
export interface RegisteredPage extends PageRegistration {
|
||||
extensionId: string; // required for compiling registered page to url with page-menu-target to compare
|
||||
routePath: string; // full route-path to registered extension page
|
||||
}
|
||||
|
||||
export interface PageComponents {
|
||||
Page: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
const routePrefix = "/extension/:name"
|
||||
|
||||
export function sanitizeExtensioName(name: string) {
|
||||
export function sanitizeExtensionName(name: string) {
|
||||
return name.replace("@", "").replace("/", "-")
|
||||
}
|
||||
|
||||
export function getPageUrl(ext: LensExtension, baseUrl = "") {
|
||||
if (baseUrl !== "" && !baseUrl.startsWith("/")) {
|
||||
baseUrl = "/" + baseUrl
|
||||
export function getExtensionPageUrl<P extends object>({ extensionId, pageId = "", params }: PageMenuTarget<P>): string {
|
||||
const extensionBaseUrl = compile(`/extension/:name`)({
|
||||
name: sanitizeExtensionName(extensionId), // compile only with extension-id first and define base path
|
||||
});
|
||||
const extPageRoutePath = path.join(extensionBaseUrl, pageId); // page-id might contain route :param-s, so don't compile yet
|
||||
if (params) {
|
||||
return compile(extPageRoutePath)(params); // might throw error when required params not passed
|
||||
}
|
||||
const validUrlName = sanitizeExtensioName(ext.name);
|
||||
return compile(routePrefix)({ name: validUrlName }) + baseUrl;
|
||||
return extPageRoutePath;
|
||||
}
|
||||
|
||||
export class PageRegistry<T extends PageRegistration> extends BaseRegistry<T> {
|
||||
|
||||
export class PageRegistry extends BaseRegistry<PageRegistration, RegisteredPage> {
|
||||
@action
|
||||
add(items: T[], ext?: LensExtension) {
|
||||
const normalizedItems = items.map((page) => {
|
||||
if (!page.routePath) {
|
||||
page.routePath = `/${page.id}`
|
||||
}
|
||||
page.routePath = getPageUrl(ext, page.routePath)
|
||||
return page
|
||||
})
|
||||
return super.add(normalizedItems);
|
||||
add(items: PageRegistration[], ext: LensExtension) {
|
||||
let registeredPages: RegisteredPage[] = [];
|
||||
try {
|
||||
registeredPages = items.map(page => ({
|
||||
...page,
|
||||
extensionId: ext.name,
|
||||
routePath: getExtensionPageUrl({ extensionId: ext.name, pageId: page.id ?? page.routePath }),
|
||||
}))
|
||||
} catch (err) {
|
||||
logger.error(`[EXTENSION]: page-registration failed`, {
|
||||
items,
|
||||
extension: ext,
|
||||
error: String(err),
|
||||
})
|
||||
}
|
||||
return super.add(registeredPages);
|
||||
}
|
||||
|
||||
getByPageMenuTarget(target: PageMenuTarget) {
|
||||
if (!target) {
|
||||
return null
|
||||
}
|
||||
const routePath = `/extension/${sanitizeExtensioName(target.extensionId)}/`
|
||||
return this.getItems().find((page) => page.routePath.startsWith(routePath) && page.id === target.pageId) || null
|
||||
getUrl<P extends object>({ extensionId, id: pageId }: RegisteredPage, params?: P) {
|
||||
return getExtensionPageUrl({ extensionId, pageId, params });
|
||||
}
|
||||
|
||||
getByPageMenuTarget(target: PageMenuTarget = {}): RegisteredPage | null {
|
||||
const targetUrl = getExtensionPageUrl(target);
|
||||
return this.getItems().find(({ id: pageId, extensionId }) => {
|
||||
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params }); // compiled with provided params
|
||||
return targetUrl === pageUrl;
|
||||
}) || null;
|
||||
}
|
||||
}
|
||||
|
||||
export const globalPageRegistry = new PageRegistry<PageRegistration>();
|
||||
export const clusterPageRegistry = new PageRegistry<PageRegistration>();
|
||||
export const globalPageRegistry = new PageRegistry();
|
||||
export const clusterPageRegistry = new PageRegistry();
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
// Workaround for using Typescript in Rollup configutation
|
||||
// https://stackoverflow.com/questions/54711437/does-rollup-support-typescript-in-rollup-config-file
|
||||
|
||||
require('ts-node').register();
|
||||
module.exports = require('./rollup.config.ts');
|
||||
@ -1,57 +0,0 @@
|
||||
// Generating declaration types for extensions-api
|
||||
// Rollup: https://rollupjs.org/guide/en/
|
||||
// Plugin docs: https://github.com/Swatinem/rollup-plugin-dts
|
||||
|
||||
import { OutputChunk, Plugin, RollupOptions } from 'rollup';
|
||||
import json from '@rollup/plugin-json';
|
||||
import dts from "rollup-plugin-dts";
|
||||
import ignoreImport from 'rollup-plugin-ignore-import'
|
||||
|
||||
const config: RollupOptions = {
|
||||
input: "src/extensions/extension-api.ts",
|
||||
output: [
|
||||
{ file: "src/extensions/npm/extensions/api.d.ts", format: "es", }
|
||||
],
|
||||
plugins: [
|
||||
dts(),
|
||||
dtsModuleWrap({ name: "@k8slens/extensions" }),
|
||||
ignoreImport({ extensions: ['.scss'] }),
|
||||
json(),
|
||||
],
|
||||
};
|
||||
|
||||
function dtsModuleWrap({ name }: { name: string }): Plugin {
|
||||
return {
|
||||
name,
|
||||
generateBundle: (options, bundle) => {
|
||||
const apiTypes = Object.values(bundle)[0] as OutputChunk; // extension-api.d.ts
|
||||
const typeRefs: string[] = []
|
||||
const declarations: string[] = []
|
||||
const apiLines = apiTypes.code.split("\n")
|
||||
let outputCode = ""
|
||||
|
||||
apiLines.forEach(line => {
|
||||
if (line.startsWith("///")) {
|
||||
typeRefs.push(line)
|
||||
} else {
|
||||
declarations.push(line)
|
||||
}
|
||||
})
|
||||
|
||||
// print external @types refs first
|
||||
if (typeRefs.length) {
|
||||
outputCode += typeRefs.join("\n") + "\n\n"
|
||||
}
|
||||
|
||||
// wrap declarations into global module definition
|
||||
outputCode += `declare module "${name}" {\n`
|
||||
outputCode += declarations.map(line => `\t${line}`).join("\n")
|
||||
outputCode += `\n}`
|
||||
|
||||
// save
|
||||
apiTypes.code = outputCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default [config];
|
||||
@ -253,16 +253,12 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
}
|
||||
|
||||
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
const apiUrl = this.kubeProxyUrl + path;
|
||||
return request(apiUrl, {
|
||||
json: true,
|
||||
timeout: 30000,
|
||||
...options,
|
||||
headers: {
|
||||
Host: `${this.id}.${new URL(this.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest()
|
||||
...(options.headers || {}),
|
||||
},
|
||||
})
|
||||
options.headers ??= {}
|
||||
options.json ??= true
|
||||
options.timeout ??= 30000
|
||||
options.headers.Host = `${this.id}.${new URL(this.kubeProxyUrl).host}` // required in ClusterManager.getClusterForRequest()
|
||||
|
||||
return request(this.kubeProxyUrl + path, options)
|
||||
}
|
||||
|
||||
getMetrics(prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) {
|
||||
|
||||
@ -1,72 +1,73 @@
|
||||
import { LensApiRequest } from "../router"
|
||||
import { LensApi } from "../lens-api"
|
||||
import { PrometheusClusterQuery, PrometheusIngressQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusProvider, PrometheusPvcQuery, PrometheusQueryOpts } from "../prometheus/provider-registry"
|
||||
import { Cluster } from "../cluster"
|
||||
import _ from "lodash"
|
||||
|
||||
export type IMetricsQuery = string | string[] | {
|
||||
[metricName: string]: string;
|
||||
}
|
||||
|
||||
// This is used for backoff retry tracking.
|
||||
const MAX_ATTEMPTS = 5
|
||||
const ATTEMPTS = [...(_.fill(Array(MAX_ATTEMPTS - 1), false)), true]
|
||||
|
||||
// prometheus metrics loader
|
||||
async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> {
|
||||
const queries = promQueries.map(p => p.trim())
|
||||
const loaders = new Map<string, Promise<any>>()
|
||||
|
||||
async function loadMetric(query: string): Promise<any> {
|
||||
async function loadMetricHelper(): Promise<any> {
|
||||
for (const [attempt, lastAttempt] of ATTEMPTS.entries()) { // retry
|
||||
try {
|
||||
return await cluster.getMetrics(prometheusPath, { query, ...queryParams })
|
||||
} catch (error) {
|
||||
if (lastAttempt || error?.statusCode === 404) {
|
||||
return {
|
||||
status: error.toString(),
|
||||
data: { result: [] },
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query)
|
||||
}
|
||||
|
||||
return Promise.all(queries.map(loadMetric))
|
||||
}
|
||||
|
||||
class MetricsRoute extends LensApi {
|
||||
async routeMetrics(request: LensApiRequest) {
|
||||
const { response, cluster, payload } = request
|
||||
const queryParams: IMetricsQuery = {}
|
||||
request.query.forEach((value: string, key: string) => {
|
||||
queryParams[key] = value
|
||||
})
|
||||
let prometheusPath: string
|
||||
let prometheusProvider: PrometheusProvider
|
||||
async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
|
||||
const queryParams: IMetricsQuery = Object.fromEntries(query.entries())
|
||||
|
||||
try {
|
||||
[prometheusPath, prometheusProvider] = await Promise.all([
|
||||
const [prometheusPath, prometheusProvider] = await Promise.all([
|
||||
cluster.contextHandler.getPrometheusPath(),
|
||||
cluster.contextHandler.getPrometheusProvider()
|
||||
])
|
||||
|
||||
// return data in same structure as query
|
||||
if (typeof payload === "string") {
|
||||
const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams)
|
||||
this.respondJson(response, data)
|
||||
} else if (Array.isArray(payload)) {
|
||||
const data = await loadMetrics(payload, cluster, prometheusPath, queryParams)
|
||||
this.respondJson(response, data)
|
||||
} else {
|
||||
const queries = Object.entries(payload).map(([queryName, queryOpts]) => (
|
||||
(prometheusProvider.getQueries(queryOpts) as Record<string, string>)[queryName]
|
||||
))
|
||||
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams)
|
||||
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]))
|
||||
this.respondJson(response, data)
|
||||
}
|
||||
} catch {
|
||||
this.respondJson(response, {})
|
||||
return
|
||||
}
|
||||
// prometheus metrics loader
|
||||
const attempts: { [query: string]: number } = {};
|
||||
const maxAttempts = 5;
|
||||
const loadMetrics = (promQuery: string): Promise<any> => {
|
||||
const query = promQuery.trim()
|
||||
const attempt = attempts[query] = (attempts[query] || 0) + 1;
|
||||
return cluster.getMetrics(prometheusPath, { query, ...queryParams }).catch(async error => {
|
||||
if (attempt < maxAttempts && (error.statusCode && error.statusCode != 404)) {
|
||||
await new Promise(resolve => setTimeout(resolve, attempt * 1000)); // add delay before repeating request
|
||||
return loadMetrics(query);
|
||||
}
|
||||
return {
|
||||
status: error.toString(),
|
||||
data: {
|
||||
result: []
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// return data in same structure as query
|
||||
let data: any;
|
||||
if (typeof payload === "string") {
|
||||
data = await loadMetrics(payload)
|
||||
} else if (Array.isArray(payload)) {
|
||||
data = await Promise.all(payload.map(loadMetrics));
|
||||
} else {
|
||||
data = {};
|
||||
const result = await Promise.all(
|
||||
Object.entries(payload).map((queryEntry: any) => {
|
||||
const queryName: string = queryEntry[0]
|
||||
const queryOpts: PrometheusQueryOpts = queryEntry[1]
|
||||
const queries = prometheusProvider.getQueries(queryOpts)
|
||||
const q = queries[queryName as keyof (PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery)]
|
||||
return loadMetrics(q)
|
||||
})
|
||||
);
|
||||
Object.keys(payload).forEach((metricName, index) => {
|
||||
data[metricName] = result[index];
|
||||
});
|
||||
}
|
||||
|
||||
this.respondJson(response, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ export class Workspaces extends React.Component {
|
||||
|
||||
@computed get workspaces(): Workspace[] {
|
||||
const currentWorkspaces: Map<WorkspaceId, Workspace> = new Map()
|
||||
workspaceStore.enabledWorkspacesList.forEach((w) => {
|
||||
workspaceStore.workspacesList.forEach((w) => {
|
||||
currentWorkspaces.set(w.id, w)
|
||||
})
|
||||
const allWorkspaces = new Map([
|
||||
@ -45,9 +45,13 @@ export class Workspaces extends React.Component {
|
||||
}
|
||||
|
||||
saveWorkspace = (id: WorkspaceId) => {
|
||||
const draft = toJS(this.editingWorkspaces.get(id));
|
||||
const workspace = workspaceStore.addWorkspace(draft);
|
||||
if (workspace) {
|
||||
const workspace = new Workspace(this.editingWorkspaces.get(id));
|
||||
if (workspaceStore.getById(id)) {
|
||||
workspaceStore.updateWorkspace(workspace);
|
||||
this.clearEditing(id);
|
||||
return;
|
||||
}
|
||||
if (workspaceStore.addWorkspace(workspace)) {
|
||||
this.clearEditing(id);
|
||||
}
|
||||
}
|
||||
@ -127,7 +131,7 @@ export class Workspaces extends React.Component {
|
||||
validate: value => !workspaceStore.getByName(value.trim())
|
||||
}
|
||||
return (
|
||||
<div key={workspaceId} className={className}>
|
||||
<div key={workspaceId} className={cssNames(className)}>
|
||||
{!isEditing && (
|
||||
<Fragment>
|
||||
<span className="name flex gaps align-center">
|
||||
|
||||
@ -29,7 +29,6 @@ import { CustomResources } from "./+custom-resources/custom-resources";
|
||||
import { crdRoute } from "./+custom-resources";
|
||||
import { isAllowedResource } from "../../common/rbac";
|
||||
import { MainLayout } from "./layout/main-layout";
|
||||
import { TabLayout, TabLayoutRoute } from "./layout/tab-layout";
|
||||
import { ErrorBoundary } from "./error-boundary";
|
||||
import { Terminal } from "./dock/terminal";
|
||||
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
||||
@ -37,7 +36,6 @@ import logger from "../../main/logger";
|
||||
import { clusterIpc } from "../../common/cluster-ipc";
|
||||
import { webFrame } from "electron";
|
||||
import { clusterPageRegistry } from "../../extensions/registries/page-registry";
|
||||
import { clusterPageMenuRegistry } from "../../extensions/registries";
|
||||
import { extensionLoader } from "../../extensions/extension-loader";
|
||||
import { appEventBus } from "../../common/event-bus";
|
||||
import whatInput from 'what-input';
|
||||
@ -75,10 +73,7 @@ export class App extends React.Component {
|
||||
|
||||
renderExtensionRoutes() {
|
||||
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
||||
const Component = () => {
|
||||
return <Page/>
|
||||
};
|
||||
return <Route key={routePath} path={routePath} exact={exact} component={Component}/>
|
||||
return <Route key={routePath} path={routePath} exact={exact} component={Page}/>
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import { ClusterIcon } from "../cluster-icon";
|
||||
import { Icon } from "../icon";
|
||||
import { autobind, cssNames, IClassName } from "../../utils";
|
||||
import { Badge } from "../badge";
|
||||
import { isActiveRoute, navigate } from "../../navigation";
|
||||
import { navigate, navigation } from "../../navigation";
|
||||
import { addClusterURL } from "../+add-cluster";
|
||||
import { clusterSettingsURL } from "../+cluster-settings";
|
||||
import { landingURL } from "../+landing-page";
|
||||
@ -22,8 +22,7 @@ import { Tooltip } from "../tooltip";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||
import { clusterViewURL } from "./cluster-view.route";
|
||||
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||
import { compile } from "path-to-regexp";
|
||||
import { getExtensionPageUrl, globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||
|
||||
interface Props {
|
||||
className?: IClassName;
|
||||
@ -152,13 +151,15 @@ export class ClustersMenu extends React.Component<Props> {
|
||||
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||
const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
|
||||
if (!registeredPage) return;
|
||||
const { routePath, exact } = registeredPage;
|
||||
const { extensionId, id: pageId } = registeredPage;
|
||||
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params });
|
||||
const isActive = pageUrl === navigation.location.pathname;
|
||||
return (
|
||||
<Icon
|
||||
key={routePath}
|
||||
key={pageUrl}
|
||||
tooltip={title}
|
||||
active={isActiveRoute({ path: routePath, exact })}
|
||||
onClick={() => navigate(compile(routePath)(target.params))}
|
||||
active={isActive}
|
||||
onClick={() => navigate(pageUrl)}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
.PodLogs {
|
||||
--overlay-bg: #8cc474b8;
|
||||
--overlay-active-bg: orange;
|
||||
|
||||
.logs {
|
||||
@include custom-scrollbar;
|
||||
|
||||
@ -11,14 +14,6 @@
|
||||
background: $logsBackground;
|
||||
flex-grow: 1;
|
||||
|
||||
.find-overlay {
|
||||
position: absolute;
|
||||
border-radius: 2px;
|
||||
background-color: #8cc474;
|
||||
margin-top: 4px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.VirtualList {
|
||||
height: 100%;
|
||||
|
||||
@ -29,19 +24,30 @@
|
||||
font-family: $font-monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
||||
|
||||
&:hover {
|
||||
background: $logRowHoverBackground;
|
||||
}
|
||||
|
||||
span {
|
||||
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
||||
}
|
||||
|
||||
span.overlay {
|
||||
border-radius: 2px;
|
||||
background-color: #8cc474b8;
|
||||
-webkit-font-smoothing: auto;
|
||||
background-color: var(--overlay-bg);
|
||||
|
||||
span {
|
||||
background-color: var(--overlay-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: orange;
|
||||
background-color: var(--overlay-active-bg);
|
||||
|
||||
span {
|
||||
background-color: var(--overlay-active-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,25 +55,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.new-logs-sep {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 0;
|
||||
border-top: 1px solid $primary;
|
||||
margin: $margin * 2 0;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
content: 'new';
|
||||
background: $primary;
|
||||
color: white;
|
||||
padding: $padding / 3;
|
||||
border-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
.jump-to-bottom {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import "./pod-logs.scss";
|
||||
import React from "react";
|
||||
import AnsiUp from 'ansi_up';
|
||||
import DOMPurify from "dompurify"
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { action, computed, observable, reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
@ -33,6 +35,7 @@ export class PodLogs extends React.Component<Props> {
|
||||
private logsElement = React.createRef<HTMLDivElement>(); // A reference for outer container in VirtualList
|
||||
private virtualListRef = React.createRef<VirtualList>(); // A reference for VirtualList component
|
||||
private lastLineIsShown = true; // used for proper auto-scroll content after refresh
|
||||
private colorConverter = new AnsiUp();
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
@ -185,6 +188,7 @@ export class PodLogs extends React.Component<Props> {
|
||||
const { searchQuery, isActiveOverlay } = searchStore;
|
||||
const item = this.logs[rowIndex];
|
||||
const contents: React.ReactElement[] = [];
|
||||
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(this.colorConverter.ansi_to_html(ansi));
|
||||
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
|
||||
// Case-insensitive search (lowercasing query and keywords in line)
|
||||
const regex = new RegExp(searchStore.escapeRegex(searchQuery), "gi");
|
||||
@ -195,19 +199,26 @@ export class PodLogs extends React.Component<Props> {
|
||||
pieces.forEach((piece, index) => {
|
||||
const active = isActiveOverlay(rowIndex, index);
|
||||
const lastItem = index === pieces.length - 1;
|
||||
const overlayValue = matches.next().value;
|
||||
const overlay = !lastItem ?
|
||||
<span className={cssNames({ active })}>{matches.next().value}</span> :
|
||||
<span
|
||||
className={cssNames("overlay", { active })}
|
||||
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
|
||||
/> :
|
||||
null
|
||||
contents.push(
|
||||
<React.Fragment key={piece + index}>
|
||||
{piece}{overlay}
|
||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
|
||||
{overlay}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className={cssNames("LogRow")}>
|
||||
{contents.length > 1 ? contents : item}
|
||||
{contents.length > 1 ? contents : (
|
||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(item) }} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -26,11 +26,10 @@ import { Network } from "../+network";
|
||||
import { crdStore } from "../+custom-resources/crd.store";
|
||||
import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources";
|
||||
import { CustomResources } from "../+custom-resources/custom-resources";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { isActiveRoute, navigation } from "../../navigation";
|
||||
import { isAllowedResource } from "../../../common/rbac"
|
||||
import { Spinner } from "../spinner";
|
||||
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
||||
import { compile } from "path-to-regexp";
|
||||
import { clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
|
||||
|
||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||
type SidebarContextValue = {
|
||||
@ -195,15 +194,14 @@ export class Sidebar extends React.Component<Props> {
|
||||
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
||||
if (!registeredPage) return;
|
||||
const { routePath, exact } = registeredPage;
|
||||
const url = compile(routePath)(target.params)
|
||||
const { extensionId, id: pageId } = registeredPage;
|
||||
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params });
|
||||
const isActive = pageUrl === navigation.location.pathname;
|
||||
return (
|
||||
<SidebarNavItem
|
||||
key={url}
|
||||
url={url}
|
||||
text={title}
|
||||
icon={<Icon/>}
|
||||
isActive={isActiveRoute({ path: routePath, exact })}
|
||||
key={pageUrl} url={pageUrl}
|
||||
text={title} icon={<Icon/>}
|
||||
isActive={isActive}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -22,8 +22,12 @@ export function navigate(location: LocationDescriptor) {
|
||||
}
|
||||
}
|
||||
|
||||
export function matchParams<P>(route: string | string[] | RouteProps) {
|
||||
return matchPath<P>(navigation.location.pathname, route);
|
||||
}
|
||||
|
||||
export function isActiveRoute(route: string | string[] | RouteProps): boolean {
|
||||
return !!matchPath(navigation.location.pathname, route);
|
||||
return !!matchParams(route);
|
||||
}
|
||||
|
||||
// common params for all pages
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
||||
|
||||
## 4.0.0-beta.2 (current version)
|
||||
## 4.0.0-beta.3 (current version)
|
||||
|
||||
- Extension API
|
||||
- Improved pod logs
|
||||
@ -18,6 +18,7 @@ Here you can find description of changes we've built into each release. While we
|
||||
- Fix UI staleness after network issues
|
||||
- Add +/- buttons in scale deployment popup screen
|
||||
- Update chart details when selecting another chart
|
||||
- Use latest alpine version (3.12) for shell sessions
|
||||
|
||||
## 3.6.8
|
||||
- Fix cluster connection issue when opening cluster settings for disconnected clusters
|
||||
|
||||
11
tsconfig.extensions.json
Normal file
11
tsconfig.extensions.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"files": [
|
||||
"src/extensions/extension-api.ts",
|
||||
],
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"sourceMap": false,
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"allowJs": false,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"traceResolution": false,
|
||||
@ -35,12 +35,17 @@
|
||||
"module": "CommonJS"
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"types/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"out",
|
||||
"dist",
|
||||
"coverage",
|
||||
"binaries",
|
||||
"static"
|
||||
"static",
|
||||
"src/extensions/npm"
|
||||
]
|
||||
}
|
||||
|
||||
59
yarn.lock
59
yarn.lock
@ -1657,22 +1657,6 @@
|
||||
schema-utils "^2.6.5"
|
||||
source-map "^0.7.3"
|
||||
|
||||
"@rollup/plugin-json@^4.1.0":
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3"
|
||||
integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.0.8"
|
||||
|
||||
"@rollup/pluginutils@^3.0.8":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||
dependencies:
|
||||
"@types/estree" "0.0.39"
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
@ -1890,11 +1874,6 @@
|
||||
dependencies:
|
||||
electron "*"
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/express-serve-static-core@*":
|
||||
version "4.17.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084"
|
||||
@ -5935,16 +5914,6 @@ estraverse@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
|
||||
integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
|
||||
|
||||
estree-walker@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||
|
||||
estree-walker@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
|
||||
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
@ -11269,7 +11238,7 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2:
|
||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
@ -12522,32 +12491,6 @@ roarr@^2.15.3:
|
||||
semver-compare "^1.0.0"
|
||||
sprintf-js "^1.1.2"
|
||||
|
||||
rollup-plugin-dts@^1.4.13:
|
||||
version "1.4.13"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-1.4.13.tgz#4f086e84f4fdcc1f49160799ebc66f6b09db292b"
|
||||
integrity sha512-7mxoQ6PcmCkBE5ZhrjGDL4k42XLy8BkSqpiRi1MipwiGs+7lwi4mQkp2afX+OzzLjJp/TGM8llfe8uayIUhPEw==
|
||||
optionalDependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
|
||||
rollup-plugin-ignore-import@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-ignore-import/-/rollup-plugin-ignore-import-1.3.2.tgz#5379eac73d2c7e389ebeb5b3a90ae4c15c15e6c8"
|
||||
integrity sha512-q7yH2c+PKVfb61+MTXqqyBHIgflikumC7OEB+OfQWNsSmDqE5FLZLeewcBGl1VDmjDjSXuALXsaBjyIsl3oNmQ==
|
||||
|
||||
rollup-pluginutils@^2.8.2:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.28.2:
|
||||
version "2.28.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.28.2.tgz#599ec4978144a82d8a8ec3d37670a8440cb04e4b"
|
||||
integrity sha512-8txbsFBFLmm9Xdt4ByTOGa9Muonmc8MfNjnGAR8U8scJlF1ZW7AgNZa7aqBXaKtlvnYP/ab++fQIq9dB9NWUbg==
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
rsvp@^4.8.4:
|
||||
version "4.8.5"
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user