├── .changeset ├── README.md └── config.json ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── commitlint.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .mocharc.js ├── .npmrc ├── .reuse └── dep5 ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTING_USING_GENAI.md ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── commitlint.config.js ├── examples ├── sample-action-client │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── package.json │ ├── resources │ │ ├── demo.txt │ │ └── onReloadDemo.txt │ ├── src │ │ └── extension.ts │ └── tsconfig.json ├── scheduled-actions-workspace │ ├── README.md │ ├── scheduled.code-workspace │ ├── vscode-settings-actions1 │ │ ├── .vscode │ │ │ └── settings.json │ │ ├── index.js │ │ └── package.json │ └── vscode-settings-actions2 │ │ ├── .vscode │ │ └── settings.json │ │ ├── index.js │ │ └── package.json ├── telemetry-reporter-sample │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── README.md │ ├── package.json │ ├── src │ │ └── extension.ts │ └── tsconfig.json ├── vscode-using-upgrade-tool │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── package.json │ ├── sample-openui5-package │ │ ├── .vscode │ │ │ └── settings.json │ │ └── package.json │ ├── src │ │ └── extension.ts │ └── tsconfig.json └── vscode-using-workspace-api │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── package.json │ ├── sample-cap-project │ ├── .cdsrc.json │ ├── .gitignore │ ├── .pipeline │ │ └── config.yml │ ├── Jenkinsfile │ ├── README.md │ ├── db │ │ ├── data-model.cds │ │ ├── data │ │ │ └── my.bookshop-Books.csv │ │ └── src │ │ │ └── .hdiconfig │ ├── manifest.yml │ ├── mta.yaml │ ├── package.json │ ├── services-manifest.yml │ └── srv │ │ └── cat-service.cds │ ├── src │ └── extension.ts │ └── tsconfig.json ├── nyc.config.js ├── package.json ├── packages ├── app-studio-remote-access │ ├── .eslintignore │ ├── .mocharc.js │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── LICENSE │ ├── README.md │ ├── icon.png │ ├── package.json │ ├── scripts │ │ └── package-vsix.js │ ├── src │ │ ├── commands.ts │ │ ├── extension.ts │ │ ├── logger │ │ │ └── logger.ts │ │ ├── messages.ts │ │ └── tunnel │ │ │ ├── ssh-utils.ts │ │ │ └── ssh.ts │ ├── test │ │ ├── commands.spec.ts │ │ ├── extension.spec.ts │ │ ├── mockUtil.ts │ │ └── tunnel │ │ │ ├── ssh-utils.spec.ts │ │ │ └── ssh.spec.ts │ ├── tsconfig.json │ └── webpack.config.js ├── app-studio-toolkit-themes │ ├── .eslintignore │ ├── .mocharc.js │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── LICENSE │ ├── README.md │ ├── icon.png │ ├── package.json │ ├── scripts │ │ └── package-vsix.js │ ├── src │ │ ├── extension.ts │ │ └── themes │ │ │ ├── dark-default-clean.json │ │ │ ├── dark-fiori-horizon.json │ │ │ ├── light-default-clean.json │ │ │ └── light-fiori-horizon.json │ ├── test │ │ └── extension.spec.ts │ ├── tsconfig.json │ └── webpack.config.js ├── app-studio-toolkit-types │ ├── .gitignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── api.d.ts │ ├── package.json │ └── tsconfig.json ├── app-studio-toolkit │ ├── .eslintignore │ ├── .mocharc.js │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── LICENSE │ ├── README.md │ ├── assets │ │ ├── access-to-devspaces.png │ │ ├── browse-bas-landscape.png │ │ ├── connect-new-landscape.png │ │ ├── login-to-bas.png │ │ └── remote-con.png │ ├── icon.png │ ├── package.json │ ├── resources │ │ ├── common │ │ │ ├── dark │ │ │ │ └── land.svg │ │ │ ├── light │ │ │ │ └── land.svg │ │ │ ├── logo.png │ │ │ └── pane.svg │ │ └── devspace │ │ │ ├── dark │ │ │ ├── basic_error.svg │ │ │ ├── basic_not_running.svg │ │ │ ├── basic_running.svg │ │ │ ├── basic_transitioning.svg │ │ │ ├── cap_error.svg │ │ │ ├── cap_not_running.svg │ │ │ ├── cap_running.svg │ │ │ ├── cap_transitioning.svg │ │ │ ├── cloud_not_running.svg │ │ │ ├── cloud_running.svg │ │ │ ├── cloud_transitioning.svg │ │ │ ├── fiori_error.svg │ │ │ ├── fiori_not_running.svg │ │ │ ├── fiori_running.svg │ │ │ ├── fiori_transitioning.svg │ │ │ ├── hana_error.svg │ │ │ ├── hana_not_running.svg │ │ │ ├── hana_running.svg │ │ │ ├── hana_transitioning.svg │ │ │ ├── mobile_error.svg │ │ │ ├── mobile_not_running.svg │ │ │ ├── mobile_running.svg │ │ │ ├── mobile_transitioning.svg │ │ │ ├── sme_error.svg │ │ │ ├── sme_not_running.svg │ │ │ ├── sme_running.svg │ │ │ └── sme_transitioning.svg │ │ │ ├── light │ │ │ ├── basic_error.svg │ │ │ ├── basic_not_running.svg │ │ │ ├── basic_running.svg │ │ │ ├── basic_transitioning.svg │ │ │ ├── cap_error.svg │ │ │ ├── cap_not_running.svg │ │ │ ├── cap_running.svg │ │ │ ├── cap_transitioning.svg │ │ │ ├── cloud_not_running.svg │ │ │ ├── cloud_running.svg │ │ │ ├── cloud_transitioning.svg │ │ │ ├── fiori_error.svg │ │ │ ├── fiori_not_running.svg │ │ │ ├── fiori_running.svg │ │ │ ├── fiori_transitioning.svg │ │ │ ├── hana_error.svg │ │ │ ├── hana_not_running.svg │ │ │ ├── hana_running.svg │ │ │ ├── hana_transitioning.svg │ │ │ ├── mobile_error.svg │ │ │ ├── mobile_not_running.svg │ │ │ ├── mobile_running.svg │ │ │ ├── mobile_transitioning.svg │ │ │ ├── sme_error.svg │ │ │ ├── sme_not_running.svg │ │ │ ├── sme_running.svg │ │ │ └── sme_transitioning.svg │ │ │ └── login.svg │ ├── scripts │ │ └── package-vsix.js │ ├── src │ │ ├── actions │ │ │ ├── actionsConfig.ts │ │ │ ├── actionsFactory.ts │ │ │ ├── client.ts │ │ │ ├── controller.ts │ │ │ ├── impl.ts │ │ │ └── performer.ts │ │ ├── apis │ │ │ ├── isOpenedForAction.ts │ │ │ ├── parameters.ts │ │ │ ├── validateCapCapabilities.ts │ │ │ ├── validateFioriCapabilities.ts │ │ │ ├── validateHanacalcviewCapabilities.ts │ │ │ └── validateLCAP.ts │ │ ├── authentication │ │ │ ├── auth-utils.ts │ │ │ └── authProvider.ts │ │ ├── basctlServer │ │ │ └── basctlServer.ts │ │ ├── constants.ts │ │ ├── devspace-manager │ │ │ ├── common │ │ │ │ └── messages.ts │ │ │ ├── devspace │ │ │ │ ├── add.ts │ │ │ │ ├── connect.ts │ │ │ │ ├── copy.ts │ │ │ │ ├── delete.ts │ │ │ │ ├── devspace.ts │ │ │ │ ├── edit.ts │ │ │ │ ├── open.ts │ │ │ │ └── update.ts │ │ │ ├── handler │ │ │ │ └── basHandler.ts │ │ │ ├── instance.ts │ │ │ ├── landscape │ │ │ │ ├── delete.ts │ │ │ │ ├── landscape.ts │ │ │ │ ├── open.ts │ │ │ │ └── set.ts │ │ │ └── tree │ │ │ │ ├── devSpacesExplorer.ts │ │ │ │ ├── devSpacesProvider.ts │ │ │ │ └── treeItems.ts │ │ ├── extension.ts │ │ ├── logger │ │ │ └── logger.ts │ │ ├── project-type │ │ │ └── workspace-instance.ts │ │ ├── public-api │ │ │ ├── base-bas-api.ts │ │ │ ├── create-bas-toolkit-api.ts │ │ │ └── create-workspace-proxy.ts │ │ ├── telemetry │ │ │ ├── basClientFactory.ts │ │ │ ├── basTelemetryClient.ts │ │ │ ├── constants.ts │ │ │ ├── eventHeader.ts │ │ │ ├── telemetryInit.ts │ │ │ └── utils.ts │ │ └── utils │ │ │ ├── bas-utils.ts │ │ │ ├── native-require.ts │ │ │ └── optional-require.ts │ ├── test │ │ ├── actionsConfig.spec.ts │ │ ├── actionsFactory.spec.ts │ │ ├── api.spec.ts │ │ ├── apis │ │ │ ├── isOpenedForAction.spec.ts │ │ │ ├── parameters.spec.ts │ │ │ └── validateCapabilities.spec.ts │ │ ├── authentication │ │ │ ├── auth-utils.spec.ts │ │ │ └── authProvider.spec.ts │ │ ├── basctlServer.spec.ts │ │ ├── client.spec.ts │ │ ├── controller.spec.ts │ │ ├── devspace-manager │ │ │ ├── devspace │ │ │ │ ├── add.spec.ts │ │ │ │ ├── connect.spec.ts │ │ │ │ ├── copy.spec.ts │ │ │ │ ├── delete.spec.ts │ │ │ │ ├── devspace.spec.ts │ │ │ │ ├── edit.spec.ts │ │ │ │ ├── open.spec.ts │ │ │ │ └── update.spec.ts │ │ │ ├── handler │ │ │ │ └── basHandler.spec.ts │ │ │ ├── instance.spec.ts │ │ │ ├── landscape │ │ │ │ ├── delete.spec.ts │ │ │ │ ├── landscape.cont.spec.ts │ │ │ │ ├── landscape.spec.ts │ │ │ │ ├── open.spec.ts │ │ │ │ └── set.spec.ts │ │ │ └── tree │ │ │ │ ├── devSpacesExplorer.spec.ts │ │ │ │ ├── devSpacesProvider.spec.ts │ │ │ │ └── treeItems.spec.ts │ │ ├── extension.spec.ts │ │ ├── mockUtil.ts │ │ ├── performer.spec.ts │ │ ├── project-type │ │ │ └── workspace-instance.spec.ts │ │ ├── public-api │ │ │ ├── create-bas-toolkit-api.spec.ts │ │ │ └── create-workspace-proxy.spec.ts │ │ ├── telemetry │ │ │ ├── basClientFactory.spec.ts │ │ │ ├── basTelemetryClient.spec.ts │ │ │ ├── constants.spec.ts │ │ │ ├── eventHeader.spec.ts │ │ │ ├── telemetryInit.spec.ts │ │ │ └── utils.spec.ts │ │ └── utils │ │ │ ├── bas-utils.spec.ts │ │ │ ├── optional-require.spec.ts │ │ │ └── samples │ │ │ └── throwing-module.ts │ ├── tsconfig.json │ └── webpack.config.js ├── npm-dependencies-validation │ ├── .mocharc.js │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── package.json │ ├── src │ │ ├── api.ts │ │ ├── depIssuesFinder.ts │ │ ├── depIssuesFixer.ts │ │ ├── logger.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── fileUtil.ts │ │ │ ├── npmUtil.ts │ │ │ └── packageJsonUtil.ts │ ├── test │ │ ├── config.ts │ │ ├── depIssuesFinder.spec.ts │ │ ├── depIssuesFixer.spec.ts │ │ ├── packages-samples │ │ │ ├── .gitignore │ │ │ ├── negative │ │ │ │ ├── empty_no_issues │ │ │ │ │ └── package.json │ │ │ │ ├── no_version_for_dep │ │ │ │ │ ├── node_modules │ │ │ │ │ │ └── missing-math │ │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ └── not_supported │ │ │ │ │ └── package.json │ │ │ └── positive │ │ │ │ ├── fix_missing_deps │ │ │ │ └── package.json │ │ │ │ ├── mismatch_deps │ │ │ │ ├── node_modules │ │ │ │ │ ├── mismatched │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── range-parser │ │ │ │ │ │ └── package.json │ │ │ │ │ └── webpack │ │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ │ ├── missing_deps │ │ │ │ └── package.json │ │ │ │ └── missing_dev_deps │ │ │ │ └── package.json │ │ └── utils │ │ │ ├── fileUtil.spec.ts │ │ │ ├── npmUtil.spec.ts │ │ │ └── packageJsonUtil │ │ │ ├── packageJsonUtil.spec.ts │ │ │ └── projects │ │ │ ├── monoRepo │ │ │ └── package.json │ │ │ ├── npm-managed │ │ │ └── package.json │ │ │ ├── pnpm-workspace │ │ │ └── wsFolder1 │ │ │ │ └── child │ │ │ │ ├── package.json │ │ │ │ └── pnpm-workspace.yaml │ │ │ ├── yarn │ │ │ └── wsFolder1 │ │ │ │ └── child │ │ │ │ ├── .yarn │ │ │ │ └── file.txt │ │ │ │ └── package.json │ │ │ └── yarnrc-yml │ │ │ └── wsFolder1 │ │ │ └── child │ │ │ ├── .yarnrc.yml │ │ │ └── package.json │ └── tsconfig.json ├── vscode-dependencies-validation │ ├── .mocharc.js │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── nyc.config.js │ ├── package.json │ ├── resources │ │ └── example-manual-mode.png │ ├── scripts │ │ └── package-vsix.js │ ├── src │ │ ├── autofix │ │ │ ├── activate.ts │ │ │ ├── configuration.ts │ │ │ ├── eventUtil.ts │ │ │ └── packageJsonFileWatcher.ts │ │ ├── commands.ts │ │ ├── constants.ts │ │ ├── diagnostics │ │ │ ├── convertToDiagnostics.ts │ │ │ ├── debounce.ts │ │ │ ├── messages.ts │ │ │ ├── refreshDiagnostics.ts │ │ │ └── shouldBeChecked.ts │ │ ├── editorChanges.ts │ │ ├── extension.ts │ │ ├── logger │ │ │ └── logger.ts │ │ ├── npmIssuesActionProvider.ts │ │ ├── util.ts │ │ └── vscodeTypes.ts │ ├── test │ │ ├── autofix │ │ │ ├── configuration.spec.ts │ │ │ ├── eventUtil.spec.ts │ │ │ └── packageJsonFileWatcher.spec.ts │ │ ├── commands.spec.ts │ │ ├── diagnostics │ │ │ ├── convertToDiagnostics.spec.ts │ │ │ └── debouce.spec.ts │ │ ├── moduleProxies.ts │ │ ├── npmIssuesActionProvider.spec.ts │ │ ├── util.spec.ts │ │ └── vscodeMocks.ts │ ├── tsconfig.json │ └── webpack.config.js └── vscode-deps-upgrade-tool │ ├── .mocharc.js │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── CHANGELOG.old.md │ ├── README.md │ ├── nyc.config.js │ ├── package.json │ ├── scripts │ └── package-vsix.js │ ├── src │ ├── apply-upgrades.ts │ ├── extension.ts │ ├── logger.ts │ ├── metadata.ts │ └── settings.ts │ ├── test │ ├── apply-upgrades.spec.ts │ ├── fixtures │ │ └── apply-upgrades │ │ │ └── package.json │ ├── metadata.spec.ts │ └── settings.spec.ts │ ├── tsconfig.json │ └── webpack.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json5 ├── scripts ├── merge-coverage.js └── upload-vsix-to-releases.mjs ├── tsconfig.base.json ├── tsconfig.json └── webpack.config.vscode.base.js /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [ 6 | ["vscode-deps-upgrade-tool", "vscode-using-upgrade-tool"], 7 | ["@sap-devx/npm-dependencies-validation", "vscode-dependencies-validation"], 8 | [ 9 | "app-studio-toolkit", 10 | "@sap-devx/app-studio-toolkit-types", 11 | "sample-action-client", 12 | "vscode-using-workspace-api" 13 | ] 14 | ], 15 | "linked": [], 16 | "access": "restricted", 17 | "baseBranch": "main", 18 | "updateInternalDependencies": "patch", 19 | "ignore": [], 20 | "privatePackages": { "version": true, "tag": true } 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf abs 2 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | # See: https://github.community/t/duplicate-checks-on-push-and-pull-request-simultaneous-event/18012 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | commitlint: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 18 | with: 19 | fetch-depth: 0 20 | - uses: wagoid/commitlint-github-action@416045160973f9fff174ac6698412cfe7181c3f3 # v4 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # sub-packages files for https://reuse.software (avoid duplication) 2 | .reuse 3 | LICENSES 4 | LICENSE 5 | 6 | # root files for https://reuse.software 7 | !/.reuse 8 | !/LICENSES 9 | !LICENSE 10 | 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Dependency directories 34 | node_modules/ 35 | 36 | # Optional npm cache directory 37 | .npm 38 | package-lock.json 39 | 40 | # yarn lockfile (this project uses pnpm) 41 | yarn.lock 42 | 43 | # Optional eslint cache 44 | .eslintcache 45 | 46 | # Optional REPL history 47 | .node_repl_history 48 | 49 | # Output of 'npm pack' 50 | *.tgz 51 | 52 | # Yarn Integrity file 53 | .yarn-integrity 54 | 55 | # dotenv environment variables file 56 | .env 57 | 58 | # Jetbrains IDE configuration 59 | .idea 60 | 61 | # Not ignoring .vscode folder because .vscode/launch.json contains launch configurations for vscode extensions 62 | # and it's useful to share it 63 | 64 | # vscode-test 65 | .vscode-test 66 | 67 | # tsc incremental build artifacts 68 | tsconfig.tsbuildinfo 69 | 70 | # Built VSCode exts 71 | *.vsix 72 | 73 | # generated artifacts 74 | dist 75 | 76 | # Other 77 | .DS_store 78 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install lint-staged -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const deepEqualInAnyOrder = require("deep-equal-in-any-order"); 3 | const chaiAsPromised = require("chai-as-promised"); 4 | 5 | chai.use(deepEqualInAnyOrder); 6 | chai.use(chaiAsPromised); 7 | 8 | module.exports = { 9 | require: ["source-map-support/register"], 10 | }; 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | auto-install-peers=false 3 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: app-studio-toolkit 3 | Upstream-Contact: Tomer Epstein 4 | Source: https://github.com/SAP/app-studio-toolkit 5 | 6 | Files: * 7 | Copyright: 2021 SAP SE or an SAP affiliate company and app-studio-toolkit contributors 8 | License: Apache-2.0 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "dist": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "dist": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "compile:watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Continuous Integration](https://github.com/SAP/app-studio-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/SAP/app-studio-toolkit/actions/workflows/ci.yml) 2 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) 3 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP/app-studio-toolkit)](https://api.reuse.software/info/github.com/SAP/app-studio-toolkit) 4 | 5 | # app-studio-toolkit 6 | 7 | ## Description 8 | 9 | With the app-studio-toolkit extension, you can benefit from a rich tools for executing common platform tasks. This extension allows developers to execute common actions as "Launch Code-Snippet", "Run VS Code command", "Open File", "Execute handler". 10 | 11 | ## How to obtain support 12 | 13 | To get more help, support, and information please open a github [issue](https://github.com/SAP/app-studio-toolkit/issues). 14 | 15 | ## Contributing 16 | 17 | Contributing information can be found in the [CONTRIBUTING.md](CONTRIBUTING.md) file. 18 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | // override commitlint ignores as suggested by https://github.com/dependabot/dependabot-core/issues/2445#issuecomment-949633412 2 | module.exports = { 3 | extends: ["@commitlint/config-conventional"], 4 | ignores: [(message) => /^Bumps \[.+]\(.+\) from .+ to .+\.$/m.test(message)], 5 | }; 6 | -------------------------------------------------------------------------------- /examples/sample-action-client/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/dist/src/**/*.js"], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/sample-action-client/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "dist": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "dist": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | "actions": [] 12 | } 13 | -------------------------------------------------------------------------------- /examples/sample-action-client/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | out/test/** 3 | src/** 4 | .gitignore 5 | vsc-extension-quickstart.md 6 | **/tsconfig.json 7 | **/tslint.json 8 | **/*.map 9 | **/*.ts -------------------------------------------------------------------------------- /examples/sample-action-client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # sample-action-client 2 | 3 | ## 2.4.0 4 | 5 | ## 2.3.0 6 | 7 | ## 2.2.2 8 | 9 | ## 2.2.1 10 | 11 | ## 2.2.0 12 | 13 | ## 2.1.0 14 | 15 | ## 2.0.0 16 | 17 | ### Major Changes 18 | 19 | - 63e5e97: bump major version for lcm refactor 20 | This is just a version bump, not a breaking change... 21 | -------------------------------------------------------------------------------- /examples/sample-action-client/README.md: -------------------------------------------------------------------------------- 1 | # Sample Action Emitter 2 | 3 | Can perform action immediately or schedule an action to run. 4 | 5 | ## Project setup 6 | 7 | ``` 8 | pnpm install 9 | ``` 10 | 11 | ### Build 12 | 13 | ``` 14 | pnpm ci 15 | ``` 16 | 17 | ## Performing Actions 18 | 19 | There are 2 alternatives for performing an action **immediately**: 20 | 21 | - Using events: 22 | 1. Expose an API 23 | 1. Have the action-broker extension listen to this event 24 | 1. Fire the event 25 | - Directly performing the action (using a library function) 26 | 27 | When **scheduling** an action to run later: 28 | 29 | 1. _This_ extension uses a library function that adds the action to the `settings.json` workspace file. 30 | 1. The `action-broker` extension reads from the settings file on startup, performs the actions, and clears the relevant section from the settings file. 31 | 32 | ## Limitations 33 | 34 | Actions of type `EXECUTE` cannot be scheduled to run, as the `performAction()` method is not serialized in the `settings.json` file. 35 | -------------------------------------------------------------------------------- /examples/sample-action-client/resources/demo.txt: -------------------------------------------------------------------------------- 1 | File Action Demo Content -------------------------------------------------------------------------------- /examples/sample-action-client/resources/onReloadDemo.txt: -------------------------------------------------------------------------------- 1 | File Action On Reload Demo Content -------------------------------------------------------------------------------- /examples/sample-action-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/README.md: -------------------------------------------------------------------------------- 1 | # Scheduled Actions Example 2 | 3 | Executes scheduled actions defined in the projects .vscode/settings.json files and in the workspace scheduled.code-workspace file. 4 | 5 | ## Performing Actions 6 | 7 | 1. Open workspace using the scheduled.code-workspace file. 8 | 2. All actions are removed from the settings/workspace files after execution. 9 | 10 | ## Limitations 11 | 12 | Actions of type `EXECUTE` cannot be used as scheduled actions. 13 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/scheduled.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "vscode-settings-actions1" 5 | }, 6 | { 7 | "path": "vscode-settings-actions2" 8 | } 9 | ], 10 | "settings": { 11 | "actions": [ 12 | { 13 | "id": "openSettingsAction", 14 | "actionType": "COMMAND", 15 | "name": "workbench.action.openSettings" 16 | }, 17 | "extension-defined-action-id3" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions1/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [ 3 | { 4 | "id": "guideddev", 5 | "actionType": "COMMAND", 6 | "name": "loadGuidedDevelopment" 7 | }, 8 | "extension-defined-action-id1" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions1/index.js: -------------------------------------------------------------------------------- 1 | console.log("vscode-settings-action1"); 2 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-settings-actions1", 3 | "private": "true", 4 | "version": "1.2.3", 5 | "publisher": "SAPOSS", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/SAP/app-studio-toolkit" 9 | }, 10 | "main": "index.js" 11 | } 12 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions2/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [ 3 | { 4 | "id": "youi", 5 | "actionType": "COMMAND", 6 | "name": "loadYeomanUI" 7 | }, 8 | "extension-defined-action-id2" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions2/index.js: -------------------------------------------------------------------------------- 1 | console.log("vscode-settings-action2"); 2 | -------------------------------------------------------------------------------- /examples/scheduled-actions-workspace/vscode-settings-actions2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-settings-actions2", 3 | "private": "true", 4 | "version": "1.2.3", 5 | "publisher": "SAPOSS", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/SAP/app-studio-toolkit" 9 | }, 10 | "main": "index.js" 11 | } 12 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint", 6 | "ms-vscode.extension-test-runner" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/dist/src/**/*.js"], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } 12 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | src/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/eslint.config.mjs 9 | **/*.map 10 | **/*.ts 11 | **/.vscode-test.* 12 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/README.md: -------------------------------------------------------------------------------- 1 | # telemetry-reporter-sample 2 | 3 | This VSCode extension is a sample for demonstrating telemetry reporting capabilities. 4 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telemetry-reporter-sample", 3 | "displayName": "telemetry-reporter-sample", 4 | "description": "This is a sample VSCode extension which consumes BAS telemetry report API", 5 | "version": "0.0.1", 6 | "private": true, 7 | "engines": { 8 | "vscode": "^1.96.0" 9 | }, 10 | "publisher": "sample", 11 | "categories": [ 12 | "Other" 13 | ], 14 | "activationEvents": [], 15 | "main": "./dist/src/extension.js", 16 | "contributes": { 17 | "commands": [ 18 | { 19 | "command": "telemetry-reporter-sample.triggerTelemetryReport", 20 | "title": "Trigger report telemetry API" 21 | } 22 | ] 23 | }, 24 | "scripts": { 25 | "ci": "npm-run-all clean compile", 26 | "clean": "rimraf dist *.vsix", 27 | "compile": "tsc -p ./", 28 | "vscode:prepublish": "npm run compile" 29 | }, 30 | "devDependencies": { 31 | "@sap-devx/app-studio-toolkit-types": "workspace:*" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { 3 | TelemetryProperties, 4 | TelemetryMeasurements, 5 | } from "@sap-devx/app-studio-toolkit-types"; 6 | 7 | export function activate(context: vscode.ExtensionContext) { 8 | console.log( 9 | 'Congratulations, your extension "telemetry-reporter-sample" is now active!' 10 | ); 11 | 12 | const disposable = vscode.commands.registerCommand( 13 | "telemetry-reporter-sample.triggerTelemetryReport", 14 | () => { 15 | const basToolkitAPI = vscode.extensions.getExtension( 16 | "SAPOSS.app-studio-toolkit" 17 | )?.exports; 18 | 19 | if (basToolkitAPI) { 20 | const extensionVersion = context.extension.packageJSON.version; 21 | const extensionName = `${context.extension.packageJSON.publisher}.${context.extension.packageJSON.name}`; 22 | 23 | const properties: TelemetryProperties = { 24 | customProp1: "customPropValue1", 25 | }; 26 | 27 | const measurements: TelemetryMeasurements = { 28 | customMeasure1: 1, 29 | }; 30 | 31 | const reporter = basToolkitAPI.getTelemetryReporter( 32 | extensionName, 33 | extensionVersion 34 | ); 35 | reporter.report( 36 | `TestTelemetryEvent-${Date.now().toString()}`, 37 | properties, 38 | measurements 39 | ); 40 | void vscode.window.showInformationMessage( 41 | "Telemetry report triggered!" 42 | ); 43 | } 44 | } 45 | ); 46 | 47 | context.subscriptions.push(disposable); 48 | } 49 | 50 | export function deactivate() {} 51 | -------------------------------------------------------------------------------- /examples/telemetry-reporter-sample/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vscode-using-upgrade-tool 2 | 3 | ## 5.0.0 4 | 5 | ### Major Changes 6 | 7 | - a7ed1ff: bump major version for lcm refactor 8 | This is just a version bump, not a breaking change... 9 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.8.3](https://github.com/SAP/app-studio-toolkit/compare/v1.8.1...v1.8.3) (2022-05-16) 7 | 8 | **Note:** Version bump only for package vscode-using-upgrade-tool 9 | 10 | # [1.8.0](https://github.com/SAP/app-studio-toolkit/compare/v1.7.1...v1.8.0) (2022-03-23) 11 | 12 | ### Features 13 | 14 | - upgrade tool ([#188](https://github.com/SAP/app-studio-toolkit/issues/188)) ([07a7ca2](https://github.com/SAP/app-studio-toolkit/commit/07a7ca23553b3cacd877095cade6660c09b5d4b4)) 15 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-using-upgrade-tool", 3 | "private": "true", 4 | "version": "5.0.0", 5 | "publisher": "SAPOSS", 6 | "engines": { 7 | "vscode": "^1.32.0" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/SAP/app-studio-toolkit" 12 | }, 13 | "main": "./dist/src/extension.js", 14 | "scripts": { 15 | "ci": "npm-run-all clean compile", 16 | "clean": "rimraf dist *.vsix", 17 | "compile": "tsc -p ./", 18 | "package": "vsce package" 19 | }, 20 | "categories": [ 21 | "Other" 22 | ], 23 | "activationEvents": [ 24 | "*" 25 | ], 26 | "contributes": {}, 27 | "BASContributes": { 28 | "upgrade": { 29 | "nodejs": [ 30 | { 31 | "package": "@ui5/cli", 32 | "version": { 33 | "from": "^1.12.0", 34 | "to": "^2.11" 35 | } 36 | }, 37 | { 38 | "package": "eslint", 39 | "version": { 40 | "from": "^7.0.0", 41 | "to": "^8.11.0" 42 | } 43 | } 44 | ] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/sample-openui5-package/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "dependenciesValidation.enableAutoFix": true, 4 | "dependencyUpgrade.enabled": true, 5 | "dependencyUpgrade.delay.min": 0.2, 6 | "dependencyUpgrade.delay.max": 0.2, 7 | "dependencyUpgrade.logging.level": "error", 8 | "dependenciesValidation.logging.level": "error" 9 | } 10 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/sample-openui5-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openui5-sample-app", 3 | "version": "0.2.0", 4 | "description": "Sample of an OpenUI5 app", 5 | "private": true, 6 | "scripts": { 7 | "start": "ui5 serve", 8 | "lint": "eslint webapp", 9 | "karma": "karma start", 10 | "karma-ci": "rimraf coverage && karma start karma-ci.conf.js", 11 | "watch": "npm run karma", 12 | "test": "npm run lint && npm run karma-ci", 13 | "build": "ui5 build -a --clean-dest", 14 | "build-self-contained": "ui5 build self-contained -a --clean-dest", 15 | "serve-dist": "ws --compress -d dist" 16 | }, 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "@ui5/cli": "^1.14.0", 20 | "eslint": "^7.32.0", 21 | "karma": "^6.3.17", 22 | "karma-chrome-launcher": "^3.1.0", 23 | "karma-coverage": "^2.2.0", 24 | "karma-ui5": "^2.4.0", 25 | "local-web-server": "^4.2.1", 26 | "rimraf": "^3.0.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/src/extension.ts: -------------------------------------------------------------------------------- 1 | function activate(): void { 2 | // In the node dependencies upgrade scenario only metadata is provided 3 | // in the extension's package.json, so the activate method is **empty** 4 | } 5 | 6 | module.exports = { 7 | activate, 8 | }; 9 | -------------------------------------------------------------------------------- /examples/vscode-using-upgrade-tool/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vscode-using-workspace-api 2 | 3 | ## 2.4.0 4 | 5 | ## 2.3.0 6 | 7 | ## 2.2.2 8 | 9 | ## 2.2.1 10 | 11 | ## 2.2.0 12 | 13 | ## 2.1.0 14 | 15 | ## 2.0.0 16 | 17 | ### Major Changes 18 | 19 | - 63e5e97: bump major version for lcm refactor 20 | This is just a version bump, not a breaking change... 21 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/README.md: -------------------------------------------------------------------------------- 1 | # Example VSCode Extension using workspace instance 2 | 3 | An example demonstrating usage of the `@sap/artifact-managment` APIs 4 | exposed via the `App Studio Toolkit` VSCode Ext. 5 | 6 | ## pre-requisites 7 | 8 | - **Nodejs**: An `active` or `maintenance` version. 9 | - **pnpm**: version >= 6.x 10 | - CAP Development Kit `npm install -g @sap/cds-dk` (must be **globally** installed). 11 | 12 | ## Initial setup 13 | 14 | In commandline/shell at the **root** of this monorepo: 15 | 16 | 1. `pnpm install`. 17 | 2. `pnpm compile`. 18 | 19 | In "Main" VSCode window: 20 | 21 | 1. `File` --> `Open Folder...` and select this mono repo's **root**. 22 | 2. go to `Run and Debug` tab, 23 | 3. select the launch configuration called `run Toolkit using workspace api instance` in the dropdown. 24 | 4. click the `Start Debugging` green run icon. 25 | 26 | In the newly opened VSCode "Extension Development Host" Window: 27 | 28 | 1. `File` -> `Open Folder` -> select the `sample-cap-project` folder which is a **sibling** to this README.md 29 | 2. Open the output panel: `View` -> `Output` 30 | 3. Select `SAP OSS.project_using_workspace_api_instance` in the dropdown menu. 31 | 4. Inspect the printed `@sap/artifact-managment` project type hierarchy. 32 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-using-workspace-api", 3 | "private": "true", 4 | "version": "2.4.0", 5 | "publisher": "SAPOSS", 6 | "engines": { 7 | "vscode": "^1.32.0" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/SAP/app-studio-toolkit" 12 | }, 13 | "main": "./dist/src/extension.js", 14 | "scripts": { 15 | "ci": "npm-run-all clean compile", 16 | "clean": "rimraf dist *.vsix", 17 | "compile": "tsc -p ./", 18 | "package": "vsce package" 19 | }, 20 | "devDependencies": { 21 | "@sap-devx/app-studio-toolkit-types": "workspace:*" 22 | }, 23 | "categories": [ 24 | "Other" 25 | ], 26 | "activationEvents": [ 27 | "*" 28 | ], 29 | "extensionDependencies": [ 30 | "SAPOSS.app-studio-toolkit" 31 | ], 32 | "contributes": {} 33 | } 34 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/.cdsrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/.gitignore: -------------------------------------------------------------------------------- 1 | # CAP sample 2 | _out 3 | *.db 4 | connection.properties 5 | default-*.json 6 | gen/ 7 | node_modules/ 8 | target/ 9 | 10 | # Web IDE, App Studio 11 | .che/ 12 | .gen/ 13 | 14 | # MTA 15 | *_mta_build_tmp 16 | *.mtar 17 | mta_archives/ 18 | 19 | # Other 20 | .DS_Store 21 | *.orig 22 | *.log 23 | 24 | *.iml 25 | *.flattened-pom.xml 26 | 27 | # IDEs 28 | # .vscode 29 | # .idea 30 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/.pipeline/config.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # This file configures the project "Piper" pipeline of your project. 3 | # For a reference of the configuration concept and available options, please have a look into its documentation. 4 | # 5 | # The documentation for the most recent pipeline version can always be found at: 6 | # https://sap.github.io/jenkins-library/ 7 | # 8 | # This is a YAML-file. YAML is an indentation-sensitive file format. Please make sure to properly indent changes to it. 9 | ### 10 | 11 | 12 | 13 | ### General project setup 14 | general: 15 | inferBuildTool: true 16 | 17 | ### Step-specific configuration 18 | steps: 19 | mavenExecute: 20 | dockerImage: devxci/mbtci:1.0.14 21 | 22 | artifactPrepareVersion: 23 | versioningType: cloud_noTag 24 | 25 | ### Stage-specific configuration 26 | stages: 27 | 'Confirm': 28 | manualConfirmation: false 29 | 30 | # Integration: 31 | # credentials: 32 | # - alias: 'mySystemAlias' 33 | # credentialId: 'mySystemCredentialsId' 34 | 35 | # Release: 36 | # cfTargets: 37 | # - org: 'myOrg' 38 | # space: 'mySpace' 39 | # apiEndpoint: 'https://' 40 | # appName: 'myAppName' 41 | # manifest: 'manifest.yml' 42 | # credentialsId: 'myDeploymentCredentialsId' 43 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | /* 4 | * This file bootstraps the codified Continuous Delivery pipeline for extensions of SAP solutions such as SAP S/4HANA. 5 | * The pipeline helps you to deliver software changes quickly and in a reliable manner. 6 | * A suitable Jenkins instance is required to run the pipeline. 7 | * More information on getting started with Continuous Delivery can be found here: https://sap.github.io/jenkins-library/ 8 | */ 9 | 10 | @Library('piper-lib-os') _ 11 | 12 | piperPipeline script: this -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | Welcome to your new project. 4 | 5 | It contains these folders and files, following our recommended project layout: 6 | 7 | | File or Folder | Purpose | 8 | | -------------- | ------------------------------------ | 9 | | `app/` | content for UI frontends goes here | 10 | | `db/` | your domain models and data go here | 11 | | `srv/` | your service models and code go here | 12 | | `package.json` | project metadata and configuration | 13 | | `readme.md` | this getting started guide | 14 | 15 | ## Next Steps 16 | 17 | - Open a new terminal and run `cds watch` 18 | - (in VS Code simply choose _**Terminal** > Run Task > cds watch_) 19 | - Start adding content, for example, a [db/schema.cds](db/schema.cds). 20 | 21 | ## Learn More 22 | 23 | Learn more at https://cap.cloud.sap/docs/get-started/. 24 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/db/data-model.cds: -------------------------------------------------------------------------------- 1 | namespace my.bookshop; 2 | 3 | entity Books { 4 | key ID : Integer; 5 | title : String; 6 | stock : Integer; 7 | } -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/db/data/my.bookshop-Books.csv: -------------------------------------------------------------------------------- 1 | ID;title;stock 2 | 1;Wuthering Heights;100 3 | 2;Jane Eyre;500 4 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/manifest.yml: -------------------------------------------------------------------------------- 1 | # Generated manifest.yml based on template version 0.1.0 2 | # appName = sample 3 | # language=nodejs 4 | # multitenancy=false 5 | --- 6 | applications: 7 | # ----------------------------------------------------------------------------------- 8 | # Backend Service 9 | # ----------------------------------------------------------------------------------- 10 | - name: sample-srv 11 | random-route: true # for development only 12 | path: gen/srv 13 | memory: 256M 14 | buildpack: nodejs_buildpack 15 | services: 16 | - sample-db 17 | 18 | # ----------------------------------------------------------------------------------- 19 | # HANA Database Content Deployer App 20 | # ----------------------------------------------------------------------------------- 21 | - name: sample-db-deployer 22 | path: gen/db 23 | no-route: true 24 | health-check-type: process 25 | memory: 256M 26 | instances: 1 27 | buildpack: nodejs_buildpack 28 | services: 29 | - sample-db 30 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/mta.yaml: -------------------------------------------------------------------------------- 1 | ## Generated mta.yaml based on template version 0.4.0 2 | ## appName = sample 3 | ## language=nodejs; multitenant=false 4 | ## approuter= 5 | _schema-version: '3.1' 6 | ID: sample 7 | version: 1.0.0 8 | description: "A simple CAP project." 9 | parameters: 10 | enable-parallel-deployments: true 11 | 12 | build-parameters: 13 | before-all: 14 | - builder: custom 15 | commands: 16 | - npm install --production 17 | - npx -p @sap/cds-dk cds build --production 18 | 19 | modules: 20 | # --------------------- SERVER MODULE ------------------------ 21 | - name: sample-srv 22 | # ------------------------------------------------------------ 23 | type: nodejs 24 | path: gen/srv 25 | parameters: 26 | buildpack: nodejs_buildpack 27 | requires: 28 | # Resources extracted from CAP configuration 29 | - name: sample-db 30 | provides: 31 | - name: srv-api # required by consumers of CAP services (e.g. approuter) 32 | properties: 33 | srv-url: ${default-url} 34 | 35 | # -------------------- SIDECAR MODULE ------------------------ 36 | - name: sample-db-deployer 37 | # ------------------------------------------------------------ 38 | type: hdb 39 | path: gen/db 40 | parameters: 41 | buildpack: nodejs_buildpack 42 | requires: 43 | # 'hana' and 'xsuaa' resources extracted from CAP configuration 44 | - name: sample-db 45 | 46 | 47 | resources: 48 | # services extracted from CAP configuration 49 | # 'service-plan' can be configured via 'cds.requires..vcap.plan' 50 | # ------------------------------------------------------------ 51 | - name: sample-db 52 | # ------------------------------------------------------------ 53 | type: com.sap.xs.hdi-container 54 | parameters: 55 | service: hana # or 'hanatrial' on trial landscapes 56 | service-plan: hdi-shared 57 | properties: 58 | hdi-service-name: ${service-name} 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "version": "1.0.0", 4 | "description": "A simple CAP project.", 5 | "repository": "", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "dependencies": { 9 | "@sap/cds": "^5", 10 | "express": "^4", 11 | "hdb": "^0.18.3" 12 | }, 13 | "devDependencies": { 14 | "sqlite3": "^5.0.2" 15 | }, 16 | "scripts": { 17 | "start": "cds run" 18 | }, 19 | "eslintConfig": { 20 | "extends": "eslint:recommended", 21 | "env": { 22 | "es2020": true, 23 | "node": true, 24 | "jest": true, 25 | "mocha": true 26 | }, 27 | "globals": { 28 | "SELECT": true, 29 | "INSERT": true, 30 | "UPDATE": true, 31 | "DELETE": true, 32 | "CREATE": true, 33 | "DROP": true, 34 | "CDL": true, 35 | "CQL": true, 36 | "CXL": true, 37 | "cds": true 38 | }, 39 | "rules": { 40 | "no-console": "off", 41 | "require-atomic-updates": "off" 42 | } 43 | }, 44 | "cds": { 45 | "requires": { 46 | "db": { 47 | "kind": "sql" 48 | } 49 | }, 50 | "hana": { 51 | "deploy-format": "hdbtable" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/services-manifest.yml: -------------------------------------------------------------------------------- 1 | # Generated services-manifest.yml based on template version 0.1.0 2 | # appName = sample 3 | --- 4 | create-services: 5 | # ------------------------------------------------------------ 6 | - name: sample-db 7 | broker: hana # 'hanatrial' on trial landscapes 8 | plan: "hdi-shared" 9 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/sample-cap-project/srv/cat-service.cds: -------------------------------------------------------------------------------- 1 | using my.bookshop as my from '../db/data-model'; 2 | 3 | service CatalogService { 4 | @readonly entity Books as projection on my.Books; 5 | } -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/src/extension.ts: -------------------------------------------------------------------------------- 1 | import { extensions, window } from "vscode"; 2 | import { BasToolkit, sam } from "@sap-devx/app-studio-toolkit-types"; 3 | 4 | async function activate(): Promise { 5 | // Access the BAS Toolkit apis via vscode's `getExtension` 6 | const basToolkitAPI: BasToolkit = extensions.getExtension( 7 | "SAPOSS.app-studio-toolkit" 8 | )?.exports; 9 | 10 | const workspaceAPI = basToolkitAPI.workspaceAPI; 11 | // `context.extension.id` does not seem to work on Theia 12 | const outputChannel = window.createOutputChannel( 13 | "vscode-using-workspace-api" 14 | ); 15 | 16 | // note the usage of the `sam` "types namespace" 17 | const projects: sam.ProjectApi[] = await workspaceAPI.getProjects(); 18 | 19 | if (projects.length > 0) { 20 | // naively only print the **first** project found... 21 | const rootProjectDs = await projects[0].readItems(); 22 | const rootProjectText = JSON.stringify(rootProjectDs, null, "\t"); 23 | outputChannel.appendLine("Found `@sap/artifact-manager` Project:"); 24 | outputChannel.appendLine(rootProjectText); 25 | } else { 26 | outputChannel.appendLine( 27 | "No `@sap/artifact-manager` Projects found in the workspace" 28 | ); 29 | } 30 | } 31 | 32 | module.exports = { 33 | activate, 34 | }; 35 | -------------------------------------------------------------------------------- /examples/vscode-using-workspace-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /nyc.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reporter: ["text", "lcov"], 3 | "check-coverage": true, 4 | all: true, 5 | include: "**/src/**", 6 | // TODO: avoid duplication with the exclusions in each package's nyc.config.js 7 | exclude: [ 8 | "packages/vscode-dependencies-validation/src/commands.ts", 9 | "packages/vscode-dependencies-validation/src/logger/logger.ts", 10 | "packages/vscode-deps-upgrade-tool/src/logger.ts", 11 | ], 12 | // - https://reflectoring.io/100-percent-test-coverage/ 13 | branches: 100, 14 | lines: 100, 15 | functions: 100, 16 | statements: 100, 17 | // To enable **merged** coverage report all relevant file extensions must be listed. 18 | extension: [".js", ".ts"], 19 | }; 20 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | coverage 5 | test/resources 6 | *.d.ts, 7 | sample-action-client -------------------------------------------------------------------------------- /packages/app-studio-remote-access/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | }; 7 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | ** 3 | 4 | # except 5 | !LICENSES 6 | !LICENSE 7 | !.reuse 8 | !README.md 9 | !CHANGELOG.md 10 | !CHANGELOG.old.md 11 | !package.json 12 | !icon.png 13 | # bundled sources 14 | !dist/ 15 | !resources/ 16 | # the source maps point to src/ dir 17 | !src/ 18 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # app-studio-remote-access 2 | 3 | ## 4.1.0 4 | 5 | ### Minor Changes 6 | 7 | - e0c72ac: fix: BAS is disconnected during user's activity 8 | 9 | ## 4.0.1 10 | 11 | ### Patch Changes 12 | 13 | - 60d2d8e: consume @sap/bas-sdk with fixed `getKey` call 14 | 15 | ## 4.0.0 16 | 17 | ### Major Changes 18 | 19 | - 63e5e97: bump major version for lcm refactor 20 | This is just a version bump, not a breaking change... 21 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.19.0](https://github.com/SAP/app-studio-toolkit/compare/v1.18.7...v1.19.0) (2024-10-31) 7 | 8 | ### Features 9 | 10 | - allow access `Joule` from VS Code ([#319](https://github.com/SAP/app-studio-toolkit/issues/319)) ([c36696f](https://github.com/SAP/app-studio-toolkit/commit/c36696f7a11c5ba4a0ce64a654076cadbf5ef14f)) 11 | 12 | ## [1.18.3](https://github.com/SAP/app-studio-toolkit/compare/v1.18.2...v1.18.3) (2024-08-26) 13 | 14 | **Note:** Version bump only for package app-studio-remote-access 15 | 16 | # [1.16.0](https://github.com/SAP/app-studio-toolkit/compare/v1.15.23...v1.16.0) (2024-04-01) 17 | 18 | ### Features 19 | 20 | - open specific project in vscode ([#304](https://github.com/SAP/app-studio-toolkit/issues/304)) ([3ba5c51](https://github.com/SAP/app-studio-toolkit/commit/3ba5c51daf9ac897a66d789bc7e137430dc6e752)) 21 | 22 | ## [1.15.23](https://github.com/SAP/app-studio-toolkit/compare/v1.15.22...v1.15.23) (2024-03-11) 23 | 24 | **Note:** Version bump only for package app-studio-remote-access 25 | 26 | ## [1.15.15](https://github.com/SAP/app-studio-toolkit/compare/v1.15.14...v1.15.15) (2023-09-27) 27 | 28 | ### Bug Fixes 29 | 30 | - using global auth provider for multiple extensions ([#279](https://github.com/SAP/app-studio-toolkit/issues/279)) ([75fac54](https://github.com/SAP/app-studio-toolkit/commit/75fac54b0ff2ba76f059e86263d5244c6c6e753e)) 31 | 32 | ## [1.15.9](https://github.com/SAP/app-studio-toolkit/compare/v1.15.8...v1.15.9) (2023-07-30) 33 | 34 | **Note:** Version bump only for package app-studio-remote-access 35 | 36 | ## [1.15.8](https://github.com/SAP/app-studio-toolkit/compare/v1.15.7...v1.15.8) (2023-07-18) 37 | 38 | **Note:** Version bump only for package app-studio-remote-access 39 | 40 | ## [1.15.5](https://github.com/SAP/app-studio-toolkit/compare/v1.15.4...v1.15.5) (2023-06-28) 41 | 42 | **Note:** Version bump only for package app-studio-remote-access 43 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/README.md: -------------------------------------------------------------------------------- 1 | # Remote Access for SAP Business Application Studio 2 | 3 | Remotely connect to SAP Business Application Studio dev spaces directly from a local Visual Studio Code desktop application. 4 |
5 |
6 | Prerequisite:
7 | Make sure you have installed the [SAP Business Application Studio toolkit](https://marketplace.visualstudio.com/items?itemName=SAPOSS.app-studio-toolkit) extension. 8 |
9 | To ensure the best functionality, make sure you are always working (locally) on the latest officially released version of VS Code and that you regularly update the SAP Business Application Studio toolkit extension to the latest version in the VS Code marketplace. 10 |
11 |
12 | ![](https://github.com/SAP/app-studio-toolkit/blob/main/packages/app-studio-toolkit/assets/remote-con.png?raw=true) 13 |
14 | For more information, see [Access SAP Business Application Studio from VS Code](https://help.sap.com/docs/bas/sap-business-application-studio/working-remotely). 15 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-remote-access/icon.png -------------------------------------------------------------------------------- /packages/app-studio-remote-access/scripts/package-vsix.js: -------------------------------------------------------------------------------- 1 | const { packageCommand } = require("vsce/out/package"); 2 | const { expect } = require("chai"); 3 | const { resolve } = require("path"); 4 | const { readFileSync, writeFileSync, copyFileSync } = require("fs"); 5 | const { writeJsonSync } = require("fs-extra"); 6 | const rootExtDir = resolve(__dirname, ".."); 7 | const pkgJsonPath = resolve(rootExtDir, "package.json"); 8 | // Read & save the original literal representation of the pkg.json 9 | // To avoid dealing with re-formatting (prettier) later on. 10 | const pkgJsonOrgStr = readFileSync(pkgJsonPath, "utf8"); 11 | const pkgJson = JSON.parse(pkgJsonOrgStr); 12 | // During development flows the `main` should point to the compiled sourced 13 | // for fast dev feedback loops. 14 | expect(pkgJson.main).to.equal("./dist/src/extension"); 15 | // During production flows the main should point to the bundled sources 16 | // to reduce loading time. 17 | pkgJson.main = "./dist/extension"; 18 | writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2, EOF: "\n" }); 19 | 20 | // Ensure License and copywrite related files are part of the packaged .vsix 21 | const rootMonoRepoDir = resolve(__dirname, "..", "..", ".."); 22 | const licenseRootMonoRepoPath = resolve(rootMonoRepoDir, "LICENSE"); 23 | const licenseExtPath = resolve(rootExtDir, "LICENSE"); 24 | copyFileSync(licenseRootMonoRepoPath, licenseExtPath); 25 | 26 | // Time to create the VSIX. 27 | packageCommand({ 28 | cwd: rootExtDir, 29 | packagePath: undefined, 30 | baseContentUrl: undefined, 31 | baseImagesUrl: `https://github.com/SAP/app-studio-toolkit.git`, 32 | useYarn: true, 33 | ignoreFile: undefined, 34 | expandGitHubIssueLinks: undefined, 35 | }) 36 | .catch((e) => { 37 | console.error(e.message); 38 | process.exitCode = 1000; 39 | }) 40 | .finally(() => { 41 | // revert changes to the pkg.json, ensure clean git working directory 42 | writeFileSync(pkgJsonPath, pkgJsonOrgStr); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/src/extension.ts: -------------------------------------------------------------------------------- 1 | import { commands } from "vscode"; 2 | import type { ExtensionContext } from "vscode"; 3 | import { initLogger, getLogger } from "./logger/logger"; 4 | import { 5 | cleanDevspaceConfig, 6 | closeTunnels, 7 | cmdDevSpaceConnectNewWindow, 8 | } from "./commands"; 9 | 10 | export function activate(context: ExtensionContext): void { 11 | initLogger(context); 12 | 13 | context.subscriptions.push( 14 | commands.registerCommand( 15 | "remote-access.dev-space.connect-new-window", 16 | cmdDevSpaceConnectNewWindow 17 | ) 18 | ); 19 | 20 | context.subscriptions.push( 21 | commands.registerCommand( 22 | "remote-access.dev-space.clean-devspace-config", 23 | cleanDevspaceConfig 24 | ) 25 | ); 26 | 27 | context.subscriptions.push( 28 | commands.registerCommand("remote-access.close-tunnel", closeTunnels) 29 | ); 30 | 31 | const logger = getLogger().getChildLogger({ label: "activate" }); 32 | logger.info("The Remote-Acceess Extension is active."); 33 | } 34 | 35 | export function deactivate() { 36 | // kill opened ssh channel if exists 37 | closeTunnels(); 38 | } 39 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/src/logger/logger.ts: -------------------------------------------------------------------------------- 1 | import { window } from "vscode"; 2 | import type { ExtensionContext } from "vscode"; 3 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 4 | import { configureLogger, NOOP_LOGGER } from "@vscode-logging/wrapper"; 5 | 6 | export const LOGGING_LEVEL_CONFIG_PROP = 7 | "app-studio-remote-access.logging.level"; 8 | export const SOURCE_TRACKING_CONFIG_PROP = 9 | "app-studio-remote-access.logging.sourceLocationTracking"; 10 | 11 | let logger: IVSCodeExtLogger = NOOP_LOGGER; 12 | 13 | /** 14 | * Note the use of a getter function so the value would be lazy resolved on each use. 15 | * This enables concise and simple consumption of the Logger throughout our extension. 16 | */ 17 | export function getLogger(): IVSCodeExtLogger { 18 | return logger; 19 | } 20 | 21 | /* istanbul ignore next - ignoring "legacy" missing coverage to enforce all new code to be 100% */ 22 | export function initLogger(context: ExtensionContext): void { 23 | const extensionName = "app-studio-remote-access"; // If the extension name changes, change this too 24 | try { 25 | logger = configureLogger({ 26 | extName: extensionName, 27 | logPath: context.logUri.fsPath, 28 | logOutputChannel: window.createOutputChannel(extensionName), 29 | loggingLevelProp: LOGGING_LEVEL_CONFIG_PROP, 30 | sourceLocationProp: SOURCE_TRACKING_CONFIG_PROP, 31 | subscriptions: context.subscriptions, 32 | }); 33 | } catch (error) /* istanbul ignore next -- this is complex to test and will give little value */ { 34 | console.error( 35 | `Logs won't be available for the ${extensionName} extension: "`, 36 | error.message 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/src/messages.ts: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | info_obtaining_key: `Obtaining SSH key…`, 3 | info_save_pk_to_file: `Saving PK to file…`, 4 | info_update_config_file_with_ssh_connection: `Updating the config file with the SSH connection…`, 5 | info_closing_old_tunnel: `Closing the old tunnel to the dev space…`, 6 | info_staring_new_tunnel: `Starting a new tunnel to the dev space…`, 7 | 8 | err_devspace_connect_new_window: (landscape: string, reason: string) => 9 | `Can't connect the devspace ${landscape}: ${reason}`, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/test/mockUtil.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as _ from "lodash"; 3 | 4 | const Module = require("module"); 5 | const originalRequire = Module.prototype.require; 6 | 7 | export const mockVscode = (oVscodeMock: any, testModulePath?: string) => { 8 | clearModuleCache(testModulePath); 9 | 10 | Module.prototype.require = function (...args: any[]) { 11 | if (_.get(args, "[0]") === "vscode") { 12 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- dynamic import wrapper code 13 | return oVscodeMock; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- dynamic import wrapper code 17 | return originalRequire.apply(this, args); 18 | }; 19 | }; 20 | 21 | export function clearModuleCache(testModulePath?: string): void { 22 | if (testModulePath) { 23 | const key = path.resolve(testModulePath); 24 | if (require.cache[key]) { 25 | delete require.cache[key]; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/test/tunnel/ssh.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import * as sshModule from "../../src/tunnel/ssh"; 3 | 4 | describe("ssh unit test", () => { 5 | describe("closeSessions", () => { 6 | const ssh = sshModule as any; 7 | 8 | beforeEach(() => { 9 | ssh.internal.sessionMap.clear(); 10 | }); 11 | 12 | it("closeSession, empty sessions", () => { 13 | ssh.closeSessions(); 14 | expect(ssh.internal.sessionMap.size).to.equal(0); 15 | }); 16 | 17 | it("closeSession, without args", () => { 18 | ssh.internal.sessionMap.set("server-1", { 19 | close: () => {}, 20 | }); 21 | ssh.internal.sessionMap.set("server-2", { 22 | close: () => {}, 23 | }); 24 | ssh.closeSessions(); 25 | expect(ssh.internal.sessionMap.size).to.equal(0); 26 | }); 27 | 28 | it("closeSession, specific sessions provided", () => { 29 | ssh.internal.sessionMap.set("server-1", { 30 | close: () => {}, 31 | }); 32 | ssh.internal.sessionMap.set("server-2", { 33 | close: () => {}, 34 | }); 35 | ssh.internal.sessionMap.set("server-3", { 36 | close: () => {}, 37 | }); 38 | ssh.closeSessions(["server-1", "server-3"]); 39 | expect(ssh.internal.sessionMap.size).to.equal(1); 40 | expect(ssh.internal.sessionMap.has("server-2")).to.be.true; 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": ".", 7 | "esModuleInterop": true, 8 | "skipLibCheck": true 9 | }, 10 | "include": ["src/**/*", "test/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/app-studio-remote-access/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const baseConfig = require("../../webpack.config.vscode.base"); 3 | 4 | const config = Object.assign({}, baseConfig, { 5 | entry: "./dist/src/extension.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "extension.js", 9 | libraryTarget: "commonjs2", 10 | devtoolModuleFilenameTemplate: "../[resource-path]", 11 | }, 12 | module: { 13 | // https://webpack.js.org/configuration/module/#modulenoparse 14 | // used to avoid transforming native require usage in `optional-require` implementation 15 | noParse: /native-require\.(js|ts)$/, 16 | rules: [ 17 | { 18 | test: /\.ts$/, 19 | exclude: /node_modules/, 20 | use: [ 21 | { 22 | loader: "ts-loader", 23 | }, 24 | ], 25 | }, 26 | { 27 | test: /node_modules[/|\\]ssh-config[/|\\]index.js/, 28 | loader: "string-replace-loader", 29 | options: { 30 | search: "require[(]mod", 31 | replace: "__non_webpack_require__(mod", 32 | flags: "g", 33 | }, 34 | }, 35 | { 36 | test: /node_modules[/|\\]@microsoft[/|\\]dev-tunnels-ssh[/|\\]algorithms[/|\\]node[/|\\]nodeRsa.js/, 37 | loader: "string-replace-loader", 38 | options: { 39 | search: "require[(]'node-rsa", 40 | replace: "__non_webpack_require__('node-rsa", 41 | flags: "g", 42 | }, 43 | }, 44 | ], 45 | }, 46 | // 📖 -> https://webpack.js.org/configuration/externals/ 47 | externals: { 48 | // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed. 49 | vscode: "commonjs vscode", 50 | // To enable bundling for @sap/artifact-management 51 | // fsevents is a macos file system event library that is compiled during installation. 52 | fsevents: "commonjs fsevents", 53 | }, 54 | node: { 55 | // needed to bundle artifact-management successfully 56 | __dirname: false, 57 | }, 58 | }); 59 | 60 | module.exports = config; 61 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | coverage 5 | test/resources 6 | *.d.ts, 7 | sample-action-client -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | }; 7 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | ** 3 | 4 | # except 5 | !LICENSES 6 | !LICENSE 7 | !.reuse 8 | !README.md 9 | !CHANGELOG.md 10 | !CHANGELOG.old.md 11 | !package.json 12 | !icon.png 13 | # bundled sources 14 | !dist/ 15 | !resources/ 16 | # the source maps point to src/ dir 17 | !src/ 18 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # app-studio-toolkit-themes 2 | 3 | ## 6.0.1 4 | 5 | ### Patch Changes 6 | 7 | - eddbfac: enhancing fiori dark theme 8 | 9 | ## 6.0.0 10 | 11 | ### Major Changes 12 | 13 | - 63e5e97: bump major version for lcm refactor 14 | This is just a version bump, not a breaking change... 15 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.15.16](https://github.com/SAP/app-studio-toolkit/compare/v1.15.15...v1.15.16) (2023-10-23) 7 | 8 | ### Bug Fixes 9 | 10 | - dark themes ([#282](https://github.com/SAP/app-studio-toolkit/issues/282)) ([5bc540f](https://github.com/SAP/app-studio-toolkit/commit/5bc540fc3ea9eb3bd0e6e7e3a7b8f42c43d9ce58)) 11 | 12 | ## [1.15.9](https://github.com/SAP/app-studio-toolkit/compare/v1.15.8...v1.15.9) (2023-07-30) 13 | 14 | **Note:** Version bump only for package app-studio-toolkit-themes 15 | 16 | ## [1.15.6](https://github.com/SAP/app-studio-toolkit/compare/v1.15.5...v1.15.6) (2023-07-03) 17 | 18 | ### Bug Fixes 19 | 20 | - themes update fix DEVXBUGS-11227 ([#259](https://github.com/SAP/app-studio-toolkit/issues/259)) ([7d45e68](https://github.com/SAP/app-studio-toolkit/commit/7d45e6894070841ed5952e1cfcabe7da0a7aa62a)) 21 | 22 | ## [1.15.3](https://github.com/SAP/app-studio-toolkit/compare/v1.15.2...v1.15.3) (2023-06-14) 23 | 24 | **Note:** Version bump only for package app-studio-toolkit-themes 25 | 26 | ## [1.14.2](https://github.com/SAP/app-studio-toolkit/compare/v1.14.1...v1.14.2) (2023-05-31) 27 | 28 | ### Bug Fixes 29 | 30 | - status bar state colors and add morning horizon ([#239](https://github.com/SAP/app-studio-toolkit/issues/239)) ([d06b775](https://github.com/SAP/app-studio-toolkit/commit/d06b77579c566019ef3530683658ab46a593c172)) 31 | 32 | # [1.13.0](https://github.com/SAP/app-studio-toolkit/compare/v1.12.1...v1.13.0) (2023-04-30) 33 | 34 | **Note:** Version bump only for package app-studio-toolkit-themes 35 | 36 | # [1.12.0](https://github.com/SAP/app-studio-toolkit/compare/v1.11.4...v1.12.0) (2023-04-20) 37 | 38 | **Note:** Version bump only for package app-studio-toolkit-themes 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/README.md: -------------------------------------------------------------------------------- 1 | # Application Studio Toolkit Themes 2 | 3 | This VS Code extension provides the color themes for Business Application Studio. 4 | 5 | ## Installation 6 | 7 | ### From the VS Code Marketplace 8 | 9 | 1. Go to [Visual Studio Marketplace](https://marketplace.visualstudio.com). 10 | 2. Search for the `app-studio-toolkit-themes`. 11 | 3. Follow the instructions for installing an extension. 12 | 13 | ### From GitHub Releases 14 | 15 | 1. Go to [GitHub Releases](https://github.com/sap/app-studio-toolkit/releases). 16 | 2. Search for the `app-studio-toolkit-themes-x.y.z.vsix` 17 | archive under the latest release that contains it. 18 | - Replace `x.y.z` with the desired version number. 19 | 3. Follow the instructions for installing an extension from a `.vsix` 20 | file in the [VS Code user guide](https://code.visualstudio.com/docs/editor/extension-gallery#_install-from-a-vsix). 21 | 22 | ## Support 23 | 24 | Please open [issues](https://github.com/SAP/app-studio-toolkit/issues) on GitHub. 25 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit-themes/icon.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-studio-toolkit-themes", 3 | "displayName": "SAP Business Application Studio themes", 4 | "version": "6.0.1", 5 | "private": true, 6 | "description": "Provides SAP Business Application Studio themes", 7 | "categories": [ 8 | "Themes" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/SAP/app-studio-toolkit", 13 | "directory": "packages/app-studio-toolkit-themes" 14 | }, 15 | "license": "Apache-2.0", 16 | "publisher": "SAPOSS", 17 | "main": "./dist/src/extension", 18 | "scripts": { 19 | "bundle": "webpack --mode production", 20 | "ci": "npm-run-all clean compile coverage bundle package", 21 | "clean": "rimraf ./dist *.vsix ./.nyc_output", 22 | "compile": "tsc -p ./", 23 | "coverage": "nyc mocha", 24 | "package": "node ./scripts/package-vsix.js", 25 | "test": "mocha" 26 | }, 27 | "contributes": { 28 | "themes": [ 29 | { 30 | "label": "SAP Fiori Quartz Light", 31 | "uiTheme": "vs", 32 | "path": "./src/themes/light-default-clean.json" 33 | }, 34 | { 35 | "label": "SAP Fiori Quartz Dark", 36 | "uiTheme": "vs-dark", 37 | "path": "./src/themes/dark-default-clean.json" 38 | }, 39 | { 40 | "label": "SAP Fiori Evening Horizon", 41 | "uiTheme": "vs-dark", 42 | "path": "./src/themes/dark-fiori-horizon.json" 43 | }, 44 | { 45 | "label": "SAP Fiori Morning Horizon", 46 | "uiTheme": "vs", 47 | "path": "./src/themes/light-fiori-horizon.json" 48 | } 49 | ] 50 | }, 51 | "activationEvents": [ 52 | "*" 53 | ], 54 | "engines": { 55 | "vscode": "^1.76.0" 56 | }, 57 | "icon": "icon.png" 58 | } 59 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/scripts/package-vsix.js: -------------------------------------------------------------------------------- 1 | // TODO: extract a common utility instead of copying this script 2 | const { packageCommand } = require("vsce/out/package"); 3 | const { expect } = require("chai"); 4 | const { resolve } = require("path"); 5 | const { readFileSync, writeFileSync } = require("fs"); 6 | const { writeJsonSync } = require("fs-extra"); 7 | const rootExtDir = resolve(__dirname, ".."); 8 | const pkgJsonPath = resolve(rootExtDir, "package.json"); 9 | // Read & save the original literal representation of the pkg.json 10 | // To avoid dealing with re-formatting (prettier) later on. 11 | const pkgJsonOrgStr = readFileSync(pkgJsonPath, "utf8"); 12 | const pkgJson = JSON.parse(pkgJsonOrgStr); 13 | // During development flows the `main` should point to the compiled sourced 14 | // for fast dev feedback loops. 15 | expect(pkgJson.main).to.equal("./dist/src/extension"); 16 | // During production flows the main should point to the bundled sources 17 | // to reduce loading time. 18 | pkgJson.main = "./dist/extension"; 19 | writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2, EOF: "\n" }); 20 | 21 | // Time to create the VSIX. 22 | packageCommand({ 23 | cwd: rootExtDir, 24 | packagePath: undefined, 25 | baseContentUrl: undefined, 26 | baseImagesUrl: undefined, 27 | useYarn: true, 28 | ignoreFile: undefined, 29 | expandGitHubIssueLinks: undefined, 30 | }) 31 | .catch((e) => { 32 | console.error(e.message); 33 | process.exitCode = 1000; 34 | }) 35 | .finally(() => { 36 | // revert changes to the pkg.json, ensure clean git working directory 37 | writeFileSync(pkgJsonPath, pkgJsonOrgStr); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/src/extension.ts: -------------------------------------------------------------------------------- 1 | import type { ExtensionContext } from "vscode"; 2 | 3 | export function activate(context: ExtensionContext): void {} 4 | 5 | export function deactivate() {} 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/test/extension.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import * as extension from "../src/extension"; 3 | 4 | describe("extension unit test", () => { 5 | describe("package definitions", () => { 6 | let packageJson: { 7 | contributes: { 8 | themes: [{ label: string; uiTheme: string; path: string }]; 9 | }; 10 | }; 11 | 12 | before(() => { 13 | packageJson = require("../../package.json"); 14 | }); 15 | 16 | it("theme contribution verifying", () => { 17 | expect(packageJson.contributes.themes).deep.equal([ 18 | { 19 | label: "SAP Fiori Quartz Light", 20 | uiTheme: "vs", 21 | path: "./src/themes/light-default-clean.json", 22 | }, 23 | { 24 | label: "SAP Fiori Quartz Dark", 25 | uiTheme: "vs-dark", 26 | path: "./src/themes/dark-default-clean.json", 27 | }, 28 | { 29 | label: "SAP Fiori Evening Horizon", 30 | uiTheme: "vs-dark", 31 | path: "./src/themes/dark-fiori-horizon.json", 32 | }, 33 | { 34 | label: "SAP Fiori Morning Horizon", 35 | uiTheme: "vs", 36 | path: "./src/themes/light-fiori-horizon.json", 37 | }, 38 | ]); 39 | }); 40 | }); 41 | 42 | describe("activate", () => { 43 | const context: any = { 44 | subscriptions: { 45 | push: () => {}, 46 | }, 47 | }; 48 | 49 | it("performs defined actions", () => { 50 | extension.activate(context); 51 | }); 52 | }); 53 | 54 | it("deactivate", () => { 55 | extension.deactivate(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": ".", 7 | "esModuleInterop": true, 8 | "skipLibCheck": true 9 | }, 10 | "include": ["src/**/*", "test/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-themes/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const baseConfig = require("../../webpack.config.vscode.base"); 3 | 4 | const config = Object.assign({}, baseConfig, { 5 | entry: "./dist/src/extension.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "extension.js", 9 | libraryTarget: "commonjs2", 10 | devtoolModuleFilenameTemplate: "../[resource-path]", 11 | }, 12 | // 📖 -> https://webpack.js.org/configuration/externals/ 13 | externals: { 14 | // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed. 15 | vscode: "commonjs vscode", 16 | // To enable bundling for @sap/artifact-management 17 | // fsevents is a macos file system event library that is compiled during installation. 18 | fsevents: "commonjs fsevents", 19 | }, 20 | node: { 21 | // needed to bundle artifact-management successfully 22 | __dirname: false, 23 | }, 24 | }); 25 | 26 | module.exports = config; 27 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-types/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out 3 | *.tgz 4 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @sap-devx/app-studio-toolkit-types 2 | 3 | ## 2.4.0 4 | 5 | ### Minor Changes 6 | 7 | - 34d5a2b: bumping @sap/artifact-management dependency to 1.44.1 8 | 9 | ## 2.3.0 10 | 11 | ## 2.2.2 12 | 13 | ### Patch Changes 14 | 15 | - ca5d41d: bumping @sap/artifact-management dependency to 1.43.1 16 | 17 | ## 2.2.1 18 | 19 | ### Patch Changes 20 | 21 | - 6083c99: bumping @sap/artifact-management dependency to 1.42.1 22 | 23 | ## 2.2.0 24 | 25 | ### Minor Changes 26 | 27 | - bf0fd07: bumping @sap/artifact-management dependency to 1.42.0 28 | 29 | ## 2.1.0 30 | 31 | ### Minor Changes 32 | 33 | - 168dbd3: bumping @sap/artifact-management dependency to 1.41.0 34 | 35 | ## 2.0.0 36 | 37 | ### Major Changes 38 | 39 | - a7ed1ff: bump major version for lcm refactor 40 | This is just a version bump, not a breaking change... 41 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-types/README.md: -------------------------------------------------------------------------------- 1 | [![npm (scoped)](https://img.shields.io/npm/v/@sap-devx/app-studio-toolkit-types.svg)](https://www.npmjs.com/package/@sap-devx/app-studio-toolkit-types) 2 | 3 | # @sap-devx/app-studio-toolkit-types 4 | 5 | Type Definitions for [app-studio-toolkit VSCode extension's exported API](../app-studio-toolkit). 6 | 7 | ## Installation 8 | 9 | With npm: 10 | 11 | - `npm install @sap-devx/app-studio-toolkit-types` 12 | 13 | With Yarn: 14 | 15 | - `yarn add @sap-devx/app-studio-toolkit-types` 16 | 17 | ## Usage 18 | 19 | This package only exports TypeScript definitions, See 20 | [api.d.ts](./api.d.ts) for the full definitions. 21 | 22 | ## Support 23 | 24 | Please open [issues](https://github.com/SAP/app-studio-toolkit/issues) on github. 25 | 26 | ## Contributing 27 | 28 | See [CONTRIBUTING.md](../../CONTRIBUTING.md). 29 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sap-devx/app-studio-toolkit-types", 3 | "version": "2.4.0", 4 | "author": "SAP SE", 5 | "license": "Apache-2.0", 6 | "description": "The SAP Business Application Studio Toolkit VSCode Extension's API type signatures", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/SAP/app-studio-toolkit", 10 | "directory": "packages/types" 11 | }, 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "types": "./api.d.ts", 16 | "files": [ 17 | ".reuse", 18 | "LICENSES", 19 | "CHANGELOG.md", 20 | "CHANGELOG.old.md", 21 | "api.d.ts" 22 | ], 23 | "scripts": { 24 | "ci": "npm-run-all compile", 25 | "compile": "tsc -p ." 26 | }, 27 | "dependencies": { 28 | "@sap/artifact-management-types": "1.44.1", 29 | "@types/vscode": "1.75.0", 30 | "@vscode-logging/types": "2.0.0", 31 | "type-fest": "2.11.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["api.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | coverage 5 | test/resources 6 | *.d.ts -------------------------------------------------------------------------------- /packages/app-studio-toolkit/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | }; 7 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | ** 3 | 4 | # except 5 | !LICENSES 6 | !LICENSE 7 | !.reuse 8 | !README.md 9 | !CHANGELOG.md 10 | !CHANGELOG.old.md 11 | !package.json 12 | !icon.png 13 | # bundled sources 14 | !dist/ 15 | !resources/ 16 | # the source maps point to src/ dir 17 | !src/ 18 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # app-studio-toolkit 2 | 3 | ## 2.4.0 4 | 5 | ### Minor Changes 6 | 7 | - 34d5a2b: bumping @sap/artifact-management dependency to 1.44.1 8 | - e0c72ac: fix: BAS is disconnected during user's activity 9 | 10 | ## 2.3.0 11 | 12 | ### Minor Changes 13 | 14 | - d84c574: Expose Telemetry reporter API which allows to report to Azure Application Insights for BAS 15 | 16 | ## 2.2.2 17 | 18 | ### Patch Changes 19 | 20 | - d654e14: the reliability of tests has been increased 21 | - ca5d41d: bumping @sap/artifact-management dependency to 1.43.1 22 | - 60d2d8e: consume @sap/bas-sdk with fixed `getKey` call 23 | 24 | ## 2.2.1 25 | 26 | ### Patch Changes 27 | 28 | - 6083c99: bumping @sap/artifact-management dependency to 1.42.1 29 | 30 | ## 2.2.0 31 | 32 | ### Minor Changes 33 | 34 | - bf0fd07: bumping @sap/artifact-management dependency to 1.42.0 35 | 36 | ## 2.1.0 37 | 38 | ### Minor Changes 39 | 40 | - 1006d18: extension used to identify LCAP dev space was changed to be saposs.lcap-guided-development-kit 41 | - 168dbd3: bumping @sap/artifact-management dependency to 1.41.0 42 | 43 | ## 2.0.0 44 | 45 | ### Major Changes 46 | 47 | - 63e5e97: bump major version for lcm refactor 48 | This is just a version bump, not a breaking change... 49 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/assets/access-to-devspaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/assets/access-to-devspaces.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/assets/browse-bas-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/assets/browse-bas-landscape.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/assets/connect-new-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/assets/connect-new-landscape.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/assets/login-to-bas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/assets/login-to-bas.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/assets/remote-con.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/assets/remote-con.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/icon.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/common/dark/land.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/common/light/land.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/common/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/app-studio-toolkit/resources/common/logo.png -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/common/pane.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/hana_not_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/hana_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/hana_transitioning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/mobile_not_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/mobile_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/dark/mobile_transitioning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/hana_not_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/hana_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/hana_transitioning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/mobile_not_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/mobile_running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/light/mobile_transitioning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/resources/devspace/login.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/scripts/package-vsix.js: -------------------------------------------------------------------------------- 1 | const { packageCommand } = require("vsce/out/package"); 2 | const { expect } = require("chai"); 3 | const { resolve } = require("path"); 4 | const { readFileSync, writeFileSync, copyFileSync } = require("fs"); 5 | const { writeJsonSync } = require("fs-extra"); 6 | const rootExtDir = resolve(__dirname, ".."); 7 | const pkgJsonPath = resolve(rootExtDir, "package.json"); 8 | // Read & save the original literal representation of the pkg.json 9 | // To avoid dealing with re-formatting (prettier) later on. 10 | const pkgJsonOrgStr = readFileSync(pkgJsonPath, "utf8"); 11 | const pkgJson = JSON.parse(pkgJsonOrgStr); 12 | // During development flows the `main` should point to the compiled sourced 13 | // for fast dev feedback loops. 14 | expect(pkgJson.main).to.equal("./dist/src/extension"); 15 | // During production flows the main should point to the bundled sources 16 | // to reduce loading time. 17 | pkgJson.main = "./dist/extension"; 18 | writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2, EOF: "\n" }); 19 | 20 | // Ensure License and copywrite related files are part of the packaged .vsix 21 | const rootMonoRepoDir = resolve(__dirname, "..", "..", ".."); 22 | const licenseRootMonoRepoPath = resolve(rootMonoRepoDir, "LICENSE"); 23 | const licenseExtPath = resolve(rootExtDir, "LICENSE"); 24 | copyFileSync(licenseRootMonoRepoPath, licenseExtPath); 25 | 26 | // Time to create the VSIX. 27 | packageCommand({ 28 | cwd: rootExtDir, 29 | packagePath: undefined, 30 | baseContentUrl: undefined, 31 | baseImagesUrl: `https://github.com/SAP/app-studio-toolkit.git`, 32 | useYarn: true, 33 | ignoreFile: undefined, 34 | expandGitHubIssueLinks: undefined, 35 | }) 36 | .catch((e) => { 37 | console.error(e.message); 38 | process.exitCode = 1000; 39 | }) 40 | .finally(() => { 41 | // revert changes to the pkg.json, ensure clean git working directory 42 | writeFileSync(pkgJsonPath, pkgJsonOrgStr); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/actions/actionsConfig.ts: -------------------------------------------------------------------------------- 1 | import { workspace, WorkspaceConfiguration } from "vscode"; 2 | import { uniqWith, isEqual, size } from "lodash"; 3 | 4 | const key = "actions"; 5 | 6 | const addActions = (actions: any[], config: WorkspaceConfiguration): void => { 7 | const configActions = config.get(key, []); 8 | actions.splice(actions.length, 0, ...configActions); 9 | }; 10 | 11 | export const get = (): any[] => { 12 | const actions: any[] = []; 13 | 14 | workspace.workspaceFolders?.forEach((wsFolder) => { 15 | addActions(actions, workspace.getConfiguration(undefined, wsFolder.uri)); 16 | }); 17 | 18 | addActions(actions, workspace.getConfiguration()); 19 | 20 | return uniqWith(actions, isEqual); 21 | }; 22 | 23 | export const clear = (): void => { 24 | workspace.workspaceFolders?.forEach((wsFolder) => { 25 | const configurations = workspace.getConfiguration(undefined, wsFolder.uri); 26 | if (size(configurations["actions"]) > 0) { 27 | void configurations.update(key, undefined); // removes actions key if they exist 28 | } 29 | }); 30 | 31 | const configurations = workspace.getConfiguration(); 32 | if (size(configurations["actions"]) > 0) { 33 | void configurations.update(key, undefined); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/actions/client.ts: -------------------------------------------------------------------------------- 1 | import { workspace, ConfigurationTarget } from "vscode"; 2 | import { BasAction } from "@sap-devx/app-studio-toolkit-types"; 3 | import { _performAction } from "./performer"; 4 | import { getLogger } from "../logger/logger"; 5 | 6 | export const performAction = ( 7 | action: BasAction, 8 | options?: any 9 | ): Thenable => { 10 | return options?.schedule ? _scheduleAction(action) : _performAction(action); 11 | }; 12 | 13 | const logger = getLogger().getChildLogger({ label: "client" }); 14 | 15 | /** Schedule an action for execution after restart */ 16 | async function _scheduleAction(action: BasAction): Promise { 17 | const actionsSettings = workspace.getConfiguration(); 18 | const actionsList: any[] = actionsSettings.get("actions", []); 19 | actionsList.push(action); 20 | try { 21 | await actionsSettings.update( 22 | "actions", 23 | actionsList, 24 | ConfigurationTarget.Workspace 25 | ); 26 | logger.trace( 27 | `Action '${action.id}' successfuly added to scheduled actions` 28 | ); 29 | } catch (error) { 30 | logger.error( 31 | `Couldn't add '${action.id}' action to scheduled actions: ${error}` 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/actions/impl.ts: -------------------------------------------------------------------------------- 1 | import { Uri } from "vscode"; 2 | import { 3 | ICommandAction, 4 | IExecuteAction, 5 | IFileAction, 6 | ISnippetAction, 7 | } from "@sap-devx/app-studio-toolkit-types"; 8 | import { COMMAND, SNIPPET, FILE, EXECUTE } from "../constants"; 9 | 10 | /** Specific action classes */ 11 | export class ExecuteAction implements IExecuteAction { 12 | id?: string; 13 | actionType: "EXECUTE"; 14 | executeAction: (params?: any[]) => Thenable; 15 | params?: any[]; 16 | 17 | constructor() { 18 | this.actionType = EXECUTE; 19 | /* istanbul ignore next - ignoring "legacy" missing coverage to enforce all new code to be 100% */ 20 | this.executeAction = () => Promise.resolve(); 21 | this.params = []; 22 | } 23 | } 24 | 25 | export class CommandAction implements ICommandAction { 26 | id?: string; 27 | actionType: "COMMAND"; 28 | name: string; 29 | params?: any[]; 30 | 31 | constructor() { 32 | this.actionType = COMMAND; 33 | this.name = ""; 34 | this.params = []; 35 | } 36 | } 37 | 38 | export class SnippetAction implements ISnippetAction { 39 | id?: string; 40 | actionType: "SNIPPET"; 41 | contributorId: string; 42 | snippetName: string; 43 | context: any; 44 | isNonInteractive?: boolean; 45 | 46 | constructor() { 47 | this.actionType = SNIPPET; 48 | this.contributorId = ""; 49 | this.snippetName = ""; 50 | this.context = {}; 51 | } 52 | } 53 | 54 | export class FileAction implements IFileAction { 55 | id?: string; 56 | actionType: "FILE"; 57 | uri: IFileAction["uri"]; 58 | 59 | constructor() { 60 | this.actionType = FILE; 61 | this.uri = Uri.parse(""); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/actions/performer.ts: -------------------------------------------------------------------------------- 1 | import { commands, ViewColumn, window } from "vscode"; 2 | import { get } from "lodash"; 3 | import { getLogger } from "../logger/logger"; 4 | import { BasAction } from "@sap-devx/app-studio-toolkit-types"; 5 | import { COMMAND, SNIPPET, FILE, EXECUTE } from "../constants"; 6 | 7 | export async function _performAction(action: BasAction): Promise { 8 | const logger = getLogger(); 9 | if (action) { 10 | logger.trace( 11 | `performing action ${action.id} of type ${action.actionType}`, 12 | { action } 13 | ); 14 | switch (action.actionType) { 15 | case COMMAND: { 16 | const params: any = get(action, "params", []); 17 | return commands.executeCommand(action.name, ...params); 18 | } 19 | case EXECUTE: { 20 | return action.executeAction(get(action, "params", [])); 21 | } 22 | case SNIPPET: { 23 | return commands.executeCommand("loadCodeSnippet", { 24 | viewColumn: ViewColumn.Two, 25 | contributorId: action.contributorId, 26 | snippetName: action.snippetName, 27 | context: action.context, 28 | isNonInteractive: action.isNonInteractive ?? false, 29 | }); 30 | } 31 | case FILE: { 32 | return action.uri.scheme === "file" 33 | ? commands.executeCommand( 34 | "vscode.open", 35 | action.uri, 36 | window.activeTextEditor ? { viewColumn: ViewColumn.Beside } : {} 37 | ) 38 | : commands.executeCommand("vscode.open", action.uri); 39 | } 40 | default: 41 | throw new Error(`actionType is not supported`); 42 | } 43 | } 44 | throw new Error(`Action is not provided`); 45 | } 46 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/isOpenedForAction.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "../logger/logger"; 2 | import { getParameter } from "./parameters"; 3 | import { includes } from "lodash"; 4 | 5 | export async function isOpenedForAction(): Promise { 6 | const logger = getLogger().getChildLogger({ label: "isOpenedForAction" }); 7 | 8 | const pkgActionKey = "pkg-action"; 9 | const pkgActionParam = await getParameter(pkgActionKey); 10 | if (pkgActionParam === undefined) { 11 | logger.trace(`Package action does not exist!`); 12 | return undefined; 13 | } 14 | const pkgActionValues = ["release", "deploy", "remove"]; 15 | if (includes(pkgActionValues, pkgActionParam)) { 16 | logger.trace(`Package action exists! The action is: '${pkgActionParam}'`); 17 | return true; 18 | } 19 | logger.trace( 20 | `The value '${pkgActionParam}' of pkg-action parameter is invalid!` 21 | ); 22 | return false; 23 | } 24 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/parameters.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "../logger/logger"; 2 | import { optionalRequire } from "../utils/optional-require"; 3 | 4 | export async function getParameter( 5 | parameterName: string 6 | ): Promise { 7 | const logger = getLogger().getChildLogger({ label: "getParameter" }); 8 | const noSapPlugin = "NO_SAP_PLUGIN_FOUND"; 9 | const sapPlugin = optionalRequire("@sap/plugin") ?? noSapPlugin; 10 | 11 | if (sapPlugin === noSapPlugin) { 12 | logger.trace("Failed to load @sap/plugin, so returning undefined."); 13 | return; 14 | } 15 | logger.trace("@sap/plugin successfully loaded."); 16 | 17 | const configuration = await sapPlugin.window.configuration(); 18 | logger.trace("Configuration successfully received.", { configuration }); 19 | 20 | return configuration?.[parameterName] as string | undefined; 21 | } 22 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/validateCapCapabilities.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "@sap/artifact-management"; 2 | import { extensions } from "vscode"; 3 | 4 | export const CAP_EXTENSION_ID = "SAPSE.vscode-cds"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/require-await -- the new implementation does not require await. 7 | export async function hasCapCapabilities(): Promise { 8 | const logger = getLogger().getChildLogger({ label: "hasCapCapabilities" }); 9 | 10 | // Cap mode is determined by the existence of the Cap extension 11 | const hasCapCapabilities = !!extensions.getExtension(CAP_EXTENSION_ID); 12 | logger.trace("Has Cap Capabilities", { hasCapCapabilities }); 13 | 14 | return hasCapCapabilities; 15 | } 16 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/validateFioriCapabilities.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "@sap/artifact-management"; 2 | import { extensions } from "vscode"; 3 | 4 | export const FIORI_EXTENSION_ID = "SAPSE.sap-ux-application-modeler-extension"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/require-await -- the new implementation does not require await. 7 | export async function hasFioriCapabilities(): Promise { 8 | const logger = getLogger().getChildLogger({ label: "hasFioriCapabilities" }); 9 | 10 | // Fiori mode is determined by the existence of the Fiori extension 11 | const hasFioriCapabilities = !!extensions.getExtension(FIORI_EXTENSION_ID); 12 | logger.trace("Has Fiori Capabilities", { hasFioriCapabilities }); 13 | 14 | return hasFioriCapabilities; 15 | } 16 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/validateHanacalcviewCapabilities.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "@sap/artifact-management"; 2 | import { extensions } from "vscode"; 3 | 4 | export const HANA_CALC_VIEW_EXTENSION_ID = "sapse.webide-hdi-feature-vscode"; //calculation view extension 5 | 6 | // eslint-disable-next-line @typescript-eslint/require-await -- the new implementation does not require await. 7 | export async function hasHanacalcviewCapabilities(): Promise { 8 | const logger = getLogger().getChildLogger({ 9 | label: "hasHanacalcviewCapabilities", 10 | }); 11 | 12 | // Cap mode is determined by the existence of the Cap extension 13 | const hasHanacalcviewCapabilities = !!extensions.getExtension( 14 | HANA_CALC_VIEW_EXTENSION_ID 15 | ); 16 | logger.trace("Has Hana Capabilities", { hasHanacalcviewCapabilities }); 17 | 18 | return hasHanacalcviewCapabilities; 19 | } 20 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/apis/validateLCAP.ts: -------------------------------------------------------------------------------- 1 | import { getLogger } from "@sap/artifact-management"; 2 | import { extensions } from "vscode"; 3 | 4 | export const LCAP_EXTENSION_ID = "saposs.lcap-guided-development-kit"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/require-await -- the new implementation does not require await. 7 | export async function isLCAPEnabled(): Promise { 8 | return isLCAPEnabledSync(); 9 | } 10 | 11 | export function isLCAPEnabledSync(): boolean { 12 | const logger = getLogger().getChildLogger({ label: "isLCAPEnabled" }); 13 | 14 | // LCAP mode is determined by the existence of the LCAP extension 15 | const isLCAPEnabled = !!extensions.getExtension(LCAP_EXTENSION_ID); 16 | logger.trace("LCAP enabled mode", { isLCAPEnabled }); 17 | 18 | return isLCAPEnabled; 19 | } 20 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/constants.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next - coverage bug */ 2 | export const COMMAND = "COMMAND"; 3 | 4 | /* istanbul ignore next - coverage bug */ 5 | export const SNIPPET = "SNIPPET"; 6 | 7 | /* istanbul ignore next - coverage bug */ 8 | export const FILE = "FILE"; 9 | 10 | /* istanbul ignore next - coverage bug */ 11 | export const EXECUTE = "EXECUTE"; 12 | 13 | /* istanbul ignore next - coverage bug */ 14 | export const URI = "URI"; 15 | 16 | // Constants for keep alive 17 | 18 | /* istanbul ignore next - coverage bug */ 19 | export const KEEP_ALIVE_TIMEOUT = 16 * 60 * 1000; // 16 minutes (in milliseconds) 20 | 21 | /* istanbul ignore next - coverage bug */ 22 | export const EXTEND_SESSION_TIMEOUT = 15 * 60; // 15 minutes (in seconds) 23 | 24 | /* istanbul ignore next - coverage bug */ 25 | export const MAX_SESSION_TIME = 2 * 60 * 60 * 1000; // 2 hours (in milliseconds) 26 | 27 | /* istanbul ignore next - coverage bug */ 28 | export const BAS_KEEP_ALIVE_FILE = "/home/user/.webide/keep-alive"; 29 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/add.ts: -------------------------------------------------------------------------------- 1 | import { LandscapeNode } from "../tree/treeItems"; 2 | import { autoRefresh } from "../landscape/landscape"; 3 | 4 | export async function cmdDevSpaceAdd(landscape: LandscapeNode): Promise { 5 | // dummy async delay until a real implementation will created 6 | await new Promise((resolve, reject) => setTimeout(() => resolve(true), 100)); 7 | autoRefresh(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/connect.ts: -------------------------------------------------------------------------------- 1 | import { window, commands, env, Uri } from "vscode"; 2 | import { DevSpaceNode } from "../tree/treeItems"; 3 | import urljoin from "url-join"; 4 | import { getLogger } from "../../../src/logger/logger"; 5 | import { messages } from "../common/messages"; 6 | 7 | export async function closeTunnels(): Promise { 8 | await commands.executeCommand("remote-access.close-tunnel"); 9 | } 10 | 11 | export async function cmdDevSpaceConnectNewWindow( 12 | devSpace: DevSpaceNode, 13 | folderPath: string | undefined 14 | ): Promise { 15 | return commands.executeCommand( 16 | "remote-access.dev-space.connect-new-window", 17 | devSpace, 18 | folderPath 19 | ); 20 | } 21 | 22 | export async function cmdDevSpaceOpenInBAS( 23 | devSpace: DevSpaceNode 24 | ): Promise { 25 | try { 26 | return env.openExternal( 27 | Uri.parse(urljoin(devSpace.landscapeUrl, `index.html`, `#${devSpace.id}`)) 28 | ); 29 | } catch (err) { 30 | getLogger().error( 31 | messages.err_open_devspace_in_bas(devSpace.landscapeUrl, err.message) 32 | ); 33 | void window.showErrorMessage( 34 | messages.err_open_devspace_in_bas(devSpace.landscapeUrl, err.message) 35 | ); 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/copy.ts: -------------------------------------------------------------------------------- 1 | import { env, window } from "vscode"; 2 | import { messages } from "../common/messages"; 3 | import { DevSpaceNode } from "../tree/treeItems"; 4 | import { getLogger } from "../../../src/logger/logger"; 5 | 6 | export async function cmdCopyWsId(devSpace: DevSpaceNode): Promise { 7 | try { 8 | await env.clipboard.writeText(devSpace.id); 9 | void window.showInformationMessage(messages.info_wsid_copied); 10 | } catch (err) { 11 | getLogger().error(messages.err_copy_devspace_id(err.message)); 12 | void window.showErrorMessage(messages.err_copy_devspace_id(err.message)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/delete.ts: -------------------------------------------------------------------------------- 1 | import { window, commands } from "vscode"; 2 | import { DevSpaceNode } from "../tree/treeItems"; 3 | import { getLogger } from "../../logger/logger"; 4 | import { messages } from "../common/messages"; 5 | import { getJwt } from "../../authentication/auth-utils"; 6 | import { devspace } from "@sap/bas-sdk"; 7 | 8 | export async function cmdDevSpaceDelete(devSpace: DevSpaceNode): Promise { 9 | const selection = await window.showInformationMessage( 10 | messages.lbl_delete_devspace(devSpace.label, devSpace.id), 11 | ...[messages.lbl_yes, messages.lbl_no] 12 | ); 13 | if (selection == messages.lbl_yes) { 14 | try { 15 | await deleteDevSpace(devSpace.landscapeUrl, devSpace.id); 16 | await cleanDevspaceConfig(devSpace); 17 | const message = messages.info_devspace_deleted(devSpace.id); 18 | getLogger().info(message); 19 | void window.showInformationMessage(message); 20 | } catch (e) { 21 | const message = messages.err_devspace_delete(devSpace.id, e.toString()); 22 | getLogger().error(message); 23 | void window.showErrorMessage(message); 24 | } finally { 25 | void commands.executeCommand("local-extension.tree.refresh"); 26 | } 27 | } 28 | } 29 | 30 | async function deleteDevSpace( 31 | landscapeUrl: string, 32 | wsId: string 33 | ): Promise { 34 | return devspace.deleteDevSpace( 35 | landscapeUrl, 36 | await getJwt(landscapeUrl), 37 | wsId 38 | ); 39 | } 40 | 41 | async function cleanDevspaceConfig(devSpace: DevSpaceNode): Promise { 42 | return commands 43 | .executeCommand("remote-access.dev-space.clean-devspace-config", devSpace) 44 | .then(() => { 45 | getLogger().info(`Devspace ssh config info cleaned`); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/devspace.ts: -------------------------------------------------------------------------------- 1 | import { window } from "vscode"; 2 | import { getLogger } from "../../logger/logger"; 3 | import { messages } from "../common/messages"; 4 | import * as sdk from "@sap/bas-sdk"; 5 | import { getJwt } from "../../authentication/auth-utils"; 6 | import { $enum } from "ts-enum-util"; 7 | 8 | export interface DevSpaceInfo 9 | extends Omit { 10 | status: sdk.devspace.DevSpaceStatus; 11 | } 12 | 13 | function patchPack(pack: string): string { 14 | // known mishmash : `SAP HANA Public` vs. `SAP Hana` 15 | return pack.toLowerCase().startsWith(sdk.devspace.PackName.HANA.toLowerCase()) 16 | ? sdk.devspace.PackName.HANA 17 | : pack; 18 | } 19 | 20 | export async function getDevSpaces( 21 | landscapeUrl: string 22 | ): Promise { 23 | return getJwt(landscapeUrl) 24 | .then((jwt) => { 25 | return sdk.devspace 26 | .getDevSpaces(landscapeUrl, jwt) 27 | .then((devspaces: sdk.devspace.DevspaceInfo[]) => { 28 | return devspaces.reduce((acc, ds) => { 29 | acc.push({ 30 | devspaceDisplayName: ds.devspaceDisplayName, 31 | devspaceOrigin: ds.devspaceOrigin, 32 | pack: patchPack(ds.pack), 33 | packDisplayName: ds.packDisplayName, 34 | url: ds.url, 35 | id: ds.id, 36 | optionalExtensions: ds.optionalExtensions, 37 | technicalExtensions: ds.technicalExtensions, 38 | status: $enum(sdk.devspace.DevSpaceStatus).asKeyOrDefault( 39 | ds.status, 40 | "ERROR" 41 | ) as sdk.devspace.DevSpaceStatus, 42 | }); 43 | return acc; 44 | }, [] as DevSpaceInfo[]); 45 | }) as unknown as DevSpaceInfo[]; 46 | }) 47 | .catch((e) => { 48 | const message = messages.err_get_devspace(e.toString()); 49 | getLogger().error(message); 50 | void window.showErrorMessage(message); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/edit.ts: -------------------------------------------------------------------------------- 1 | import { DevSpaceNode } from "../tree/treeItems"; 2 | 3 | export async function cmdDevSpaceEdit(devSpace: DevSpaceNode): Promise {} 4 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/devspace/open.ts: -------------------------------------------------------------------------------- 1 | import { URL } from "url"; 2 | import { Uri, env } from "vscode"; 3 | 4 | export function cmdOpenInVSCode(): void { 5 | void env.openExternal( 6 | Uri.parse( 7 | `vscode://SAPOSS.app-studio-toolkit/open?landscape=${ 8 | new URL(process.env.H2O_URL || ``).hostname 9 | }&devspaceid=${(process.env.WORKSPACE_ID || ``) 10 | .split(`-`) 11 | .slice(1) 12 | .join(`-`)}` 13 | ) 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/landscape/delete.ts: -------------------------------------------------------------------------------- 1 | import { window, commands } from "vscode"; 2 | import { messages } from "../common/messages"; 3 | import { LandscapeNode } from "../tree/treeItems"; 4 | import { removeLandscape } from "./landscape"; 5 | 6 | export async function cmdLandscapeDelete( 7 | landscape: LandscapeNode 8 | ): Promise { 9 | const answer = await window.showInformationMessage( 10 | messages.lbl_delete_landscape(landscape.label), 11 | ...[messages.lbl_yes, messages.lbl_no] 12 | ); 13 | if (answer == messages.lbl_yes) { 14 | return removeLandscape(landscape.url).finally( 15 | () => void commands.executeCommand("local-extension.tree.refresh") 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/landscape/open.ts: -------------------------------------------------------------------------------- 1 | import { env, Uri } from "vscode"; 2 | import { LandscapeNode } from "../tree/treeItems"; 3 | 4 | export async function cmdLandscapeOpenDevSpaceManager( 5 | landscape: LandscapeNode 6 | ): Promise { 7 | return env.openExternal(Uri.parse(landscape.url)); 8 | } 9 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/landscape/set.ts: -------------------------------------------------------------------------------- 1 | import { window, commands } from "vscode"; 2 | import { URL } from "node:url"; 3 | import { getLanscapesConfig, updateLandscapesConfig } from "./landscape"; 4 | 5 | export async function cmdLandscapeSet(): Promise { 6 | /* istanbul ignore next */ 7 | const landscape = await window.showInputBox({ 8 | prompt: "Landscape url", 9 | ignoreFocusOut: true, 10 | validateInput: (value: string) => { 11 | try { 12 | const url = new URL(value); 13 | if (url.pathname.length > 1 || url.search || url.hash) { 14 | return "Enter the URL origin without any paths or parameters"; 15 | } 16 | } catch (e) { 17 | return (e as Error).toString(); 18 | } 19 | }, 20 | }); 21 | 22 | if (landscape) { 23 | return addLandscape(landscape).finally( 24 | () => void commands.executeCommand("local-extension.tree.refresh") 25 | ); 26 | } 27 | } 28 | 29 | export async function addLandscape(landscapeName: string): Promise { 30 | const toAdd = new URL(landscapeName).toString(); 31 | const landscapes = getLanscapesConfig(); 32 | if ( 33 | !landscapes.find((landscape) => new URL(landscape.url).toString() === toAdd) 34 | ) { 35 | landscapes.push({ url: toAdd }); 36 | return updateLandscapesConfig(landscapes); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/devspace-manager/tree/devSpacesExplorer.ts: -------------------------------------------------------------------------------- 1 | import { window, TreeItem } from "vscode"; 2 | import type { TreeView } from "vscode"; 3 | import { DevSpaceDataProvider } from "./devSpacesProvider"; 4 | 5 | export class DevSpacesExplorer { 6 | private readonly devSpacesExplorerView: TreeView; 7 | private readonly devSpacesExplorerProvider: DevSpaceDataProvider; 8 | 9 | constructor(extensionPath: string) { 10 | this.devSpacesExplorerProvider = new DevSpaceDataProvider(extensionPath); 11 | this.devSpacesExplorerView = window.createTreeView("dev-spaces", { 12 | treeDataProvider: this.devSpacesExplorerProvider, 13 | showCollapseAll: true, 14 | }); 15 | } 16 | 17 | public refreshTree(): void { 18 | this.devSpacesExplorerProvider.refresh(); 19 | } 20 | 21 | public changeTreeToLoading(): void { 22 | this.devSpacesExplorerProvider.setLoading(true); 23 | this.refreshTree(); 24 | } 25 | 26 | public changeTreeToLoaded(): void { 27 | this.devSpacesExplorerProvider.setLoading(false); 28 | this.refreshTree(); 29 | } 30 | 31 | public getDevSpacesExplorerProvider(): DevSpaceDataProvider { 32 | return this.devSpacesExplorerProvider; 33 | } 34 | 35 | public getDevSpacesExplorerView(): TreeView { 36 | return this.devSpacesExplorerView; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/extension.ts: -------------------------------------------------------------------------------- 1 | import type { ExtensionContext } from "vscode"; 2 | import { BasToolkit } from "@sap-devx/app-studio-toolkit-types"; 3 | import { baseBasToolkitAPI } from "./public-api/base-bas-api"; 4 | import { createBasToolkitAPI } from "./public-api/create-bas-toolkit-api"; 5 | import { 6 | startBasctlServer, 7 | closeBasctlServer, 8 | } from "./basctlServer/basctlServer"; 9 | import { ActionsController } from "./actions/controller"; 10 | import { initLogger, getLogger } from "./logger/logger"; 11 | import { initWorkspaceAPI } from "./project-type/workspace-instance"; 12 | import { 13 | deactivateBasRemoteExplorer, 14 | initBasRemoteExplorer, 15 | } from "./devspace-manager/instance"; 16 | import { 17 | startBasKeepAlive, 18 | shouldRunCtlServer, 19 | cleanKeepAliveInterval, 20 | } from "./utils/bas-utils"; 21 | 22 | export function activate(context: ExtensionContext): BasToolkit { 23 | initLogger(context); 24 | 25 | // should be trigered earlier on acivating because the `shouldRunCtlServer` method sets the context value of `ext.runPlatform` 26 | if (shouldRunCtlServer()) { 27 | getLogger().debug("starting basctl server"); 28 | startBasctlServer(context); 29 | } 30 | 31 | // performance: run the actions after the extension is activated 32 | setTimeout(() => { 33 | ActionsController.loadContributedActions(); 34 | ActionsController.performScheduledActions(); 35 | void ActionsController.performActionsFromURL(); 36 | startBasKeepAlive(); 37 | }); 38 | 39 | const workspaceAPI = initWorkspaceAPI(context); 40 | const basToolkitAPI = createBasToolkitAPI(workspaceAPI, baseBasToolkitAPI); 41 | 42 | initBasRemoteExplorer(context); 43 | 44 | const logger = getLogger().getChildLogger({ label: "activate" }); 45 | logger.info("The App-Studio-Toolkit Extension is active."); 46 | 47 | return basToolkitAPI; 48 | } 49 | 50 | export function deactivate(): void { 51 | closeBasctlServer(); 52 | void deactivateBasRemoteExplorer(); 53 | cleanKeepAliveInterval(); 54 | } 55 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/logger/logger.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, window } from "vscode"; 2 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 3 | import { configureLogger, NOOP_LOGGER } from "@vscode-logging/wrapper"; 4 | 5 | export const LOGGING_LEVEL_CONFIG_PROP = "app-studio-toolkit.logging.level"; 6 | export const SOURCE_TRACKING_CONFIG_PROP = 7 | "app-studio-toolkit.logging.sourceLocationTracking"; 8 | 9 | let logger: IVSCodeExtLogger = NOOP_LOGGER; 10 | 11 | /** 12 | * Note the use of a getter function so the value would be lazy resolved on each use. 13 | * This enables concise and simple consumption of the Logger throughout our extension. 14 | */ 15 | export function getLogger(): IVSCodeExtLogger { 16 | return logger; 17 | } 18 | 19 | /* istanbul ignore next - ignoring "legacy" missing coverage to enforce all new code to be 100% */ 20 | export function initLogger(context: ExtensionContext): void { 21 | const extensionName = "app-studio-toolkit"; // If the extension name changes, change this too 22 | try { 23 | logger = configureLogger({ 24 | extName: extensionName, 25 | logPath: context.logUri.fsPath, 26 | logOutputChannel: window.createOutputChannel(extensionName), 27 | loggingLevelProp: LOGGING_LEVEL_CONFIG_PROP, 28 | sourceLocationProp: SOURCE_TRACKING_CONFIG_PROP, 29 | subscriptions: context.subscriptions, 30 | }); 31 | } catch (error) /* istanbul ignore next -- this is complex to test and will give little value */ { 32 | console.error( 33 | `Logs won't be available for the ${extensionName} extension: "`, 34 | error.message 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/project-type/workspace-instance.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceImpl, WorkspaceApi } from "@sap/artifact-management"; 2 | import * as vscode from "vscode"; 3 | import { CommandExecutor } from "@sap/artifact-management"; 4 | import { isPlainObject, isString } from "lodash"; 5 | 6 | let workspaceAPI: WorkspaceApi; 7 | 8 | /** 9 | * "Factory" for the `WorkspaceApi` "original" instance. 10 | */ 11 | export function initWorkspaceAPI( 12 | context: vscode.ExtensionContext 13 | ): WorkspaceApi { 14 | workspaceAPI = new WorkspaceImpl(vscode); 15 | workspaceAPI.startWatch(); 16 | 17 | context.subscriptions.push( 18 | vscode.commands.registerCommand( 19 | "project-api.command.run", 20 | async (command, ...params) => { 21 | try { 22 | let result = await CommandExecutor.execute(command, ...params); 23 | if (isPlainObject(result)) { 24 | result = JSON.stringify(result, undefined, 2); 25 | } 26 | 27 | if (isString(result)) { 28 | return result + "\n"; 29 | } else if (result === undefined) { 30 | return "\n"; 31 | } else { 32 | return "Unsupported type of result: " + typeof result; 33 | } 34 | } catch (error) { 35 | return ( 36 | `Failed to execute the command ${command} with the error: ${error.message}` + 37 | "\n" 38 | ); 39 | } 40 | } 41 | ) 42 | ); 43 | 44 | return workspaceAPI; 45 | } 46 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/public-api/create-bas-toolkit-api.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceApi } from "@sap/artifact-management"; 2 | import { 3 | BasToolkit, 4 | BasWorkspaceApi, 5 | } from "@sap-devx/app-studio-toolkit-types"; 6 | import { createWorkspaceProxy } from "./create-workspace-proxy"; 7 | 8 | export function createBasToolkitAPI( 9 | workspaceImpl: WorkspaceApi, 10 | baseBasToolkitAPI: Omit 11 | ): BasToolkit { 12 | const workspaceAPI: BasWorkspaceApi = createWorkspaceProxy(workspaceImpl); 13 | const exportedBasToolkitAPI = { 14 | // note `...` here effectively does a "shallow" clone 15 | ...baseBasToolkitAPI, 16 | workspaceAPI, 17 | }; 18 | 19 | // "Immutability Changes Everything" 20 | // note we are not "deep" freezing because the usage of namespaces on the API 21 | // is expected to be removed. 22 | Object.freeze(exportedBasToolkitAPI); 23 | return exportedBasToolkitAPI; 24 | } 25 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/public-api/create-workspace-proxy.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceApi } from "@sap/artifact-management"; 2 | import { BasWorkspaceApi } from "@sap-devx/app-studio-toolkit-types"; 3 | 4 | export function createWorkspaceProxy( 5 | workspaceImpl: WorkspaceApi 6 | ): BasWorkspaceApi { 7 | const basWsAPI = { 8 | getProjects: (...args: Parameters) => 9 | workspaceImpl.getProjects(...args), 10 | getProject: (...args: Parameters) => 11 | workspaceImpl.getProject(...args), 12 | getProjectUris: (...args: Parameters) => 13 | workspaceImpl.getProjectUris(...args), 14 | onWorkspaceChanged: ( 15 | ...args: Parameters 16 | ) => workspaceImpl.onWorkspaceChanged(...args), 17 | getPluginManager: (...args: Parameters) => 18 | workspaceImpl.getPluginManager(...args), 19 | }; 20 | 21 | Object.freeze(basWsAPI); 22 | // TODO: discuss this with the @sap/artifact-management owners. 23 | // could it be caused by our patches (with npm-patch-package)? 24 | // @ts-expect-error -- https://github.com/microsoft/TypeScript/issues/26559 25 | return basWsAPI; 26 | } 27 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/telemetry/basClientFactory.ts: -------------------------------------------------------------------------------- 1 | import { BASTelemetryClient } from "./basTelemetryClient"; 2 | import { ITelemetryReporter } from "@sap-devx/app-studio-toolkit-types"; 3 | 4 | export class BASClientFactory { 5 | private static basTelemetryClientsMap = new Map(); 6 | 7 | public static getBASTelemetryClient( 8 | extensionId: string, 9 | extensionVersion: string 10 | ): ITelemetryReporter { 11 | const key = `${extensionId}-${extensionVersion}`; 12 | if (!BASClientFactory.basTelemetryClientsMap.has(key)) { 13 | BASClientFactory.basTelemetryClientsMap.set( 14 | key, 15 | new BASTelemetryClient(extensionId, extensionVersion) 16 | ); 17 | } 18 | return BASClientFactory.basTelemetryClientsMap.get(key)!; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/telemetry/constants.ts: -------------------------------------------------------------------------------- 1 | // Should be identical to the settings name contributed from vscode-bas-extension. 2 | export const ANALYTICS_ENABLED_SETTING_NAME = "sapbas.telemetryEnabled"; 3 | 4 | export const APPINSIGHTS_CONNECTION_STRING = 5 | "InstrumentationKey=60284eda-c8cc-4794-bdb7-d35f0abb66f9;IngestionEndpoint=https://germanywestcentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://germanywestcentral.livediagnostics.monitor.azure.com/"; 6 | 7 | export enum ExtensionRunMode { 8 | desktop = `desktop`, 9 | basRemote = `bas-remote`, 10 | basWorkspace = `bas-workspace`, 11 | basUi = `bas-ui`, 12 | wsl = `wsl`, 13 | unexpected = `unexpected`, 14 | } 15 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/telemetry/eventHeader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Hierarchical event name for a telemetry event submitted to Auzre Application Insights. 3 | */ 4 | export class EventHeader { 5 | private extensionName: string; 6 | private eventName: string; 7 | 8 | /** 9 | * Event header that is composed of two parts. 10 | * 11 | * @param extensionName Consumer module name 12 | * @param eventName Telemetry event name 13 | */ 14 | constructor(extensionName: string, eventName: string) { 15 | this.extensionName = extensionName; 16 | this.eventName = eventName; 17 | } 18 | 19 | /** 20 | * @returns Consumer module name 21 | */ 22 | public getExtensionName(): string { 23 | return this.extensionName; 24 | } 25 | 26 | /** 27 | * @returns Event name 28 | */ 29 | public getEventName(): string { 30 | return this.eventName; 31 | } 32 | 33 | /** 34 | * @returns serialized string of event header 35 | */ 36 | public toString(): string { 37 | return `${this.extensionName}/${this.eventName}`; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/telemetry/telemetryInit.ts: -------------------------------------------------------------------------------- 1 | import { setup, defaultClient, TelemetryClient } from "applicationinsights"; 2 | import { APPINSIGHTS_CONNECTION_STRING } from "./constants"; 3 | 4 | let telemetryClient: TelemetryClient | null = null; 5 | 6 | export function getTelemetryClient() { 7 | if (!telemetryClient) { 8 | setup(APPINSIGHTS_CONNECTION_STRING); 9 | 10 | // Configure sample rate. 100 is the default and means all collected data will be sent to the Application Insights service 11 | // If you want to enable sampling to reduce the amount of data, set the samplingPercentage. 0 means nothing will be sent. 12 | defaultClient.config.samplingPercentage = 100; 13 | 14 | // Disable GDPR private data that are collected by Azure AppInsight client. 15 | defaultClient.addTelemetryProcessor((envelope: any) => { 16 | envelope.tags["ai.location.ip"] = "0.0.0.0"; 17 | envelope.tags["ai.cloud.roleInstance"] = "masked"; 18 | envelope.tags["ai.cloud.role"] = "masked"; 19 | envelope.tags["ai.cloud.roleVer"] = "masked"; 20 | envelope.tags["ai.device.type"] = "masked"; 21 | 22 | return true; 23 | }); 24 | telemetryClient = defaultClient; 25 | } 26 | 27 | return telemetryClient; 28 | } 29 | 30 | // For testing purpose 31 | export function setTelemetryClient(client: TelemetryClient | null) { 32 | telemetryClient = client; 33 | } 34 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/utils/native-require.ts: -------------------------------------------------------------------------------- 1 | // by avoiding parsing (and transformations) or webpack on this file 2 | // - see webpack.config.js 3 | // we allow access to the native requirejs functionality for modules which import 4 | // this `native-require` file. 5 | export const nativeRequire: NodeRequire = require; 6 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/src/utils/optional-require.ts: -------------------------------------------------------------------------------- 1 | import { nativeRequire } from "./native-require"; 2 | /** 3 | * Naive implementation of optional-require. 4 | * It does not deal with all edge cases, e.g differentiate between: 5 | * 1. a module which does not exist. 6 | * 2. a module which exists but throws an error on initialization. 7 | * 8 | * But our use cases are simple enough that this naive implementation should suffice. 9 | * And this resolves bundling issue around the combination of: 10 | * - webpack 11 | * - optional-require npm package 12 | * - pnpm (which creates sym-links in node_modules) 13 | * - vsce vsix packager (which does **not** copy contents of sym-links). 14 | * 15 | */ 16 | export function optionalRequire( 17 | moduleName: string 18 | ): M | undefined { 19 | try { 20 | // will throw "MODULE_NOT_FOUND" if the module cannot be located 21 | return nativeRequire(moduleName) as M; 22 | } catch (e) { 23 | // our incredibly naive implementation does not currently distinguish between 24 | // "MODULE_NOT_FOUND" exceptions or exceptions thrown during the optional module loading 25 | return undefined; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/actionsConfig.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import * as sinon from "sinon"; 3 | import { mockVscode } from "./mockUtil"; 4 | 5 | let wasUpdateCalled = false; 6 | const wsConfig = { 7 | get: () => [ 8 | { 9 | id: "create", 10 | actionType: "CREATE", 11 | name: "create", 12 | }, 13 | "create", 14 | ], 15 | update: () => { 16 | wasUpdateCalled = true; 17 | }, 18 | actions: [ 19 | { 20 | id: "openSettingsAction", 21 | actionType: "COMMAND", 22 | name: "workbench.action.openGlobalSettings", 23 | }, 24 | ], 25 | }; 26 | const testVscode = { 27 | workspace: { 28 | workspaceFolders: [{}, {}], 29 | getConfiguration: () => wsConfig, 30 | onDidChangeWorkspaceFolders: () => {}, 31 | }, 32 | }; 33 | 34 | mockVscode(testVscode, "dist/src/actions/actionsFactory.js"); 35 | mockVscode(testVscode, "dist/src/actions/impl.js"); 36 | import { clear } from "../src/actions/actionsConfig"; 37 | import { SinonMock } from "sinon"; 38 | 39 | describe("actionsFactory test", () => { 40 | let sandbox: any; 41 | let workspaceMock: SinonMock; 42 | 43 | before(() => { 44 | sandbox = sinon.createSandbox(); 45 | }); 46 | 47 | after(() => { 48 | sandbox.restore(); 49 | }); 50 | 51 | beforeEach(() => { 52 | workspaceMock = sandbox.mock(testVscode.workspace); 53 | wasUpdateCalled = false; 54 | }); 55 | 56 | it("test clear actions", () => { 57 | clear(); 58 | expect(wasUpdateCalled).to.be.true; 59 | }); 60 | 61 | it("test clear empty actions", () => { 62 | wsConfig.actions = []; 63 | clear(); 64 | expect(wasUpdateCalled).to.be.false; 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/devspace-manager/devspace/add.spec.ts: -------------------------------------------------------------------------------- 1 | import { SinonMock, mock } from "sinon"; 2 | 3 | import * as devspaceModule from "../../../src/devspace-manager/devspace/add"; 4 | import { LandscapeNode } from "../../../src/devspace-manager/tree/treeItems"; 5 | import proxyquire from "proxyquire"; 6 | 7 | describe("cmdDevSpaceAdd unit test", () => { 8 | let devspaceAddProxy: typeof devspaceModule; 9 | const landscapeProxy = { 10 | autoRefresh: (rate: number, timeout: number): void => {}, 11 | }; 12 | 13 | before(() => { 14 | devspaceAddProxy = proxyquire( 15 | "../../../src/devspace-manager/devspace/add", 16 | { 17 | "../landscape/landscape": landscapeProxy, 18 | } 19 | ); 20 | }); 21 | 22 | let mockLandscape: SinonMock; 23 | beforeEach(() => { 24 | mockLandscape = mock(landscapeProxy); 25 | }); 26 | 27 | afterEach(() => { 28 | mockLandscape.verify(); 29 | }); 30 | 31 | const node: LandscapeNode = {}; 32 | 33 | it("cmdLandscapeOpenDevSpaceManager, open true", async () => { 34 | mockLandscape.expects(`autoRefresh`).returns(true); 35 | await devspaceAddProxy.cmdDevSpaceAdd(node); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/devspace-manager/devspace/edit.spec.ts: -------------------------------------------------------------------------------- 1 | import proxyquire from "proxyquire"; 2 | 3 | import * as devspaceModule from "../../../src/devspace-manager/devspace/edit"; 4 | import { DevSpaceNode } from "../../../src/devspace-manager/tree/treeItems"; 5 | 6 | describe("cmdDevSpaceEdit unit test", () => { 7 | let devspaceEditProxy: typeof devspaceModule; 8 | 9 | before(() => { 10 | devspaceEditProxy = proxyquire( 11 | "../../../src/devspace-manager/devspace/edit", 12 | {} 13 | ); 14 | }); 15 | 16 | const node: DevSpaceNode = {}; 17 | 18 | it("cmdDevSpaceEdit, ok", async () => { 19 | await devspaceEditProxy.cmdDevSpaceEdit(node); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/devspace-manager/landscape/open.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { mockVscode } from "../../../test/mockUtil"; 3 | import { SinonMock, mock } from "sinon"; 4 | 5 | const testVscode = { 6 | env: { 7 | openExternal: () => { 8 | throw new Error("not implemented"); 9 | }, 10 | }, 11 | Uri: { 12 | parse: () => { 13 | throw new Error("not implemented"); 14 | }, 15 | }, 16 | }; 17 | 18 | mockVscode(testVscode, "dist/src/devspace-manager/landscape/open.js"); 19 | import { cmdLandscapeOpenDevSpaceManager } from "../../../src/devspace-manager/landscape/open"; 20 | import { LandscapeNode } from "../../../src/devspace-manager/tree/treeItems"; 21 | 22 | describe("landscapes open unit test", () => { 23 | let mockUrl: SinonMock; 24 | let mockEnv: SinonMock; 25 | beforeEach(() => { 26 | mockUrl = mock(testVscode.Uri); 27 | mockEnv = mock(testVscode.env); 28 | }); 29 | 30 | afterEach(() => { 31 | mockUrl.verify(); 32 | mockEnv.verify(); 33 | }); 34 | 35 | const node = { 36 | label: "landscape-1", 37 | url: "https://my.landscape-1.com", 38 | } as LandscapeNode; 39 | 40 | const url = { 41 | host: node.url, 42 | }; 43 | 44 | it("cmdLandscapeOpenDevSpaceManager, open true", async () => { 45 | mockUrl.expects("parse").withExactArgs(node.url).returns(url); 46 | mockEnv.expects("openExternal").withExactArgs(url).resolves(true); 47 | expect(await cmdLandscapeOpenDevSpaceManager(node)).to.be.true; 48 | }); 49 | 50 | it("cmdLandscapeOpenDevSpaceManager, open false", async () => { 51 | mockUrl.expects("parse").withExactArgs(node.url).returns(url); 52 | mockEnv.expects("openExternal").withExactArgs(url).resolves(false); 53 | expect(await cmdLandscapeOpenDevSpaceManager(node)).to.be.false; 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/mockUtil.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as _ from "lodash"; 3 | 4 | const Module = require("module"); 5 | const originalRequire = Module.prototype.require; 6 | 7 | export const mockVscode = (oVscodeMock: any, testModulePath?: string) => { 8 | clearModuleCache(testModulePath); 9 | 10 | Module.prototype.require = function (...args: any[]) { 11 | if (_.get(args, "[0]") === "vscode") { 12 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- dynamic import wrapper code 13 | return oVscodeMock; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- dynamic import wrapper code 17 | return originalRequire.apply(this, args); 18 | }; 19 | }; 20 | 21 | export function clearModuleCache(testModulePath?: string): void { 22 | if (testModulePath) { 23 | const key = path.resolve(testModulePath); 24 | if (require.cache[key]) { 25 | delete require.cache[key]; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/public-api/create-bas-toolkit-api.spec.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceApi } from "@sap/artifact-management"; 2 | import { expect } from "chai"; 3 | import { BasToolkit } from "@sap-devx/app-studio-toolkit-types"; 4 | import { createBasToolkitAPI } from "../../src/public-api/create-bas-toolkit-api"; 5 | 6 | describe("the `createBasToolkitAPI()` utility", () => { 7 | let basToolkit: BasToolkit; 8 | 9 | before(() => { 10 | /* eslint-disable @typescript-eslint/no-unsafe-return -- test dummy mock */ 11 | const dummyReturnArgsWorkspaceImpl = { 12 | getProjects() { 13 | return 333; 14 | }, 15 | } as unknown as WorkspaceApi; 16 | 17 | const dummyBaseBasToolkitApi = { 18 | getAction() { 19 | return 666; 20 | }, 21 | } as unknown as Omit; 22 | /* eslint-enable @typescript-eslint/no-unsafe-return -- test dummy mock */ 23 | basToolkit = createBasToolkitAPI( 24 | dummyReturnArgsWorkspaceImpl, 25 | dummyBaseBasToolkitApi 26 | ); 27 | }); 28 | 29 | describe("the returned API object", () => { 30 | it("is frozen", () => { 31 | expect(basToolkit).to.be.frozen; 32 | }); 33 | 34 | it("includes functions from baseBasToolkit", () => { 35 | expect(basToolkit).to.have.property("getAction"); 36 | expect(basToolkit.getAction("foo")).to.equal(666); 37 | }); 38 | 39 | it("includes workspaceAPI namespace & methods", () => { 40 | expect(basToolkit).to.have.property("workspaceAPI"); 41 | expect(Object.keys(basToolkit.workspaceAPI)).to.have.members([ 42 | "getProjects", 43 | "getProject", 44 | "getProjectUris", 45 | "onWorkspaceChanged", 46 | "getPluginManager", 47 | ]); 48 | expect(basToolkit.workspaceAPI.getProjects()).to.equal(333); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/telemetry/basClientFactory.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import { BASTelemetryClient } from "../../src/telemetry/basTelemetryClient"; 4 | import { baseBasToolkitAPI } from "../../src/public-api/base-bas-api"; 5 | 6 | describe("BASClientFactory", function () { 7 | let createStubInstance; 8 | 9 | beforeEach(function () { 10 | createStubInstance = sinon.stub(BASTelemetryClient.prototype); 11 | }); 12 | 13 | afterEach(function () { 14 | sinon.restore(); 15 | }); 16 | 17 | it("should return the same instance for the same extensionId and extensionVersion", function () { 18 | const client1 = baseBasToolkitAPI.getTelemetryReporter( 19 | "testExtension", 20 | "1.0.0" 21 | ); 22 | const client2 = baseBasToolkitAPI.getTelemetryReporter( 23 | "testExtension", 24 | "1.0.0" 25 | ); 26 | expect(client1).to.equal(client2); 27 | }); 28 | 29 | it("should create a new instance for a different extensionId or extensionVersion", function () { 30 | const client1 = baseBasToolkitAPI.getTelemetryReporter( 31 | "testExtension", 32 | "1.0.0" 33 | ); 34 | const client2 = baseBasToolkitAPI.getTelemetryReporter( 35 | "testExtension", 36 | "2.0.0" 37 | ); 38 | expect(client1).to.not.equal(client2); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/telemetry/constants.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { 3 | ANALYTICS_ENABLED_SETTING_NAME, 4 | APPINSIGHTS_CONNECTION_STRING, 5 | ExtensionRunMode, 6 | } from "../../src/telemetry/constants"; 7 | 8 | describe("Constants and Enums", () => { 9 | it("should have the correct ANALYTICS_ENABLED_SETTING_NAME", () => { 10 | expect(ANALYTICS_ENABLED_SETTING_NAME).to.equal("sapbas.telemetryEnabled"); 11 | }); 12 | 13 | it("should have the correct APPINSIGHTS_CONNECTION_STRING", () => { 14 | expect(APPINSIGHTS_CONNECTION_STRING).to.equal( 15 | "InstrumentationKey=60284eda-c8cc-4794-bdb7-d35f0abb66f9;IngestionEndpoint=https://germanywestcentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://germanywestcentral.livediagnostics.monitor.azure.com/" 16 | ); 17 | }); 18 | 19 | describe("ExtensionRunMode Enum", () => { 20 | it("should have the correct values for each key", () => { 21 | expect(ExtensionRunMode.desktop).to.equal("desktop"); 22 | expect(ExtensionRunMode.basRemote).to.equal("bas-remote"); 23 | expect(ExtensionRunMode.basWorkspace).to.equal("bas-workspace"); 24 | expect(ExtensionRunMode.basUi).to.equal("bas-ui"); 25 | expect(ExtensionRunMode.wsl).to.equal("wsl"); 26 | expect(ExtensionRunMode.unexpected).to.equal("unexpected"); 27 | }); 28 | 29 | it("should have all expected keys", () => { 30 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("desktop"); 31 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("basRemote"); 32 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("basWorkspace"); 33 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("basUi"); 34 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("wsl"); 35 | expect(ExtensionRunMode).haveOwnPropertyDescriptor("unexpected"); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/telemetry/eventHeader.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { EventHeader } from "../../src/telemetry/eventHeader"; 3 | 4 | describe("EventHeader", () => { 5 | const extensionName = "test-extension"; 6 | const eventName = "test-event"; 7 | 8 | let eventHeader: EventHeader; 9 | 10 | beforeEach(() => { 11 | eventHeader = new EventHeader(extensionName, eventName); 12 | }); 13 | 14 | it("should correctly store and return the extension name", () => { 15 | expect(eventHeader.getExtensionName()).to.equal(extensionName); 16 | }); 17 | 18 | it("should correctly store and return the event name", () => { 19 | expect(eventHeader.getEventName()).to.equal(eventName); 20 | }); 21 | 22 | it("should return a correctly formatted string representation", () => { 23 | expect(eventHeader.toString()).to.equal(`${extensionName}/${eventName}`); 24 | }); 25 | 26 | it("should handle empty strings for extension and event names", () => { 27 | const emptyEventHeader = new EventHeader("", ""); 28 | expect(emptyEventHeader.getExtensionName()).to.equal(""); 29 | expect(emptyEventHeader.getEventName()).to.equal(""); 30 | expect(emptyEventHeader.toString()).to.equal("/"); 31 | }); 32 | 33 | it("should handle special characters in extension and event names", () => { 34 | const specialEventHeader = new EventHeader("ext@name", "event#123"); 35 | expect(specialEventHeader.toString()).to.equal("ext@name/event#123"); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/telemetry/telemetryInit.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import * as appInsights from "applicationinsights"; 4 | import { 5 | getTelemetryClient, 6 | setTelemetryClient, 7 | } from "../../src/telemetry/telemetryInit"; 8 | 9 | describe("getTelemetryClient", function () { 10 | let addTelemetryProcessorStub: sinon.SinonStub; 11 | let configStub: { samplingPercentage: number }; 12 | 13 | beforeEach(function () { 14 | configStub = { samplingPercentage: 50 }; 15 | addTelemetryProcessorStub = sinon.stub(); 16 | 17 | // @ts-expect-error - test only 18 | appInsights.defaultClient.config = configStub; 19 | appInsights.defaultClient.addTelemetryProcessor = addTelemetryProcessorStub; 20 | }); 21 | 22 | afterEach(function () { 23 | sinon.restore(); 24 | }); 25 | 26 | it("should set sampling percentage to 50 (existing telemetry default client settings)", function () { 27 | const client = getTelemetryClient(); 28 | expect(client.config.samplingPercentage).to.equal(50); 29 | }); 30 | 31 | it("should add a telemetry processor that masks sensitive data (new telemetry default client settings)", function () { 32 | setTelemetryClient(null); 33 | getTelemetryClient(); 34 | 35 | expect(addTelemetryProcessorStub.calledOnce).to.be.true; 36 | 37 | // Simulate processing an envelope 38 | const telemetryProcessor = addTelemetryProcessorStub.firstCall.args[0]; 39 | const envelope = { tags: {} }; 40 | telemetryProcessor(envelope); 41 | 42 | expect(envelope.tags).to.deep.equal({ 43 | "ai.location.ip": "0.0.0.0", 44 | "ai.cloud.roleInstance": "masked", 45 | "ai.cloud.roleVer": "masked", 46 | "ai.cloud.role": "masked", 47 | "ai.device.type": "masked", 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/utils/optional-require.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { optionalRequire } from "../../src/utils/optional-require"; 3 | 4 | describe("the optionalRequire utility", () => { 5 | it("return undefined if a module does not exist", () => { 6 | // unicode heart is not a valid module name (at least currently). 7 | const tinManHeart = optionalRequire("♥"); 8 | expect(tinManHeart).to.be.undefined; 9 | }); 10 | 11 | context("with a module that throws an error during init", () => { 12 | before(() => { 13 | delete (global as any).basketBall; 14 | }); 15 | 16 | it("silently return undefined if a module throws an error", () => { 17 | expect((global as any).basketBall).to.be.undefined; 18 | const optionalModule = optionalRequire( 19 | // local require reference not supported 20 | // so we have to pass the path relative to our `optional-require` module 21 | "../../test/utils/samples/throwing-module" 22 | ); 23 | expect(optionalModule).to.be.undefined; 24 | expect((global as any).basketBall).to.be.true; 25 | }); 26 | 27 | after(() => { 28 | delete (global as any).basketBall; 29 | }); 30 | }); 31 | 32 | it("returns the module if it exist", () => { 33 | // `fs` is built-in in nodejs and should always be available 34 | const fsModule = optionalRequire("fs"); 35 | expect(fsModule).to.exist; 36 | // @ts-expect-error -- dynamic import 37 | expect(fsModule.readFile).to.exist; 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/test/utils/samples/throwing-module.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error -- go away evil compiler, this is test dummy code... 2 | global.basketBall = true; 3 | throw "BasketBall"; 4 | -------------------------------------------------------------------------------- /packages/app-studio-toolkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": ".", 7 | "esModuleInterop": true, 8 | "skipLibCheck": true 9 | }, 10 | "include": ["src/**/*", "test/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | }; 7 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @sap-devx/npm-dependencies-validation 2 | 3 | ## 3.0.1 4 | 5 | ### Patch Changes 6 | 7 | - d654e14: the reliability of tests has been increased 8 | 9 | ## 3.0.0 10 | 11 | ### Major Changes 12 | 13 | - 63e5e97: bump major version for lcm refactor 14 | This is just a version bump, not a breaking change... 15 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.18.5](https://github.com/SAP/app-studio-toolkit/compare/v1.18.4...v1.18.5) (2024-09-01) 7 | 8 | ### Bug Fixes 9 | 10 | - [DEVXBUGS-11661] error is shown if disttags are used in package.json ([#337](https://github.com/SAP/app-studio-toolkit/issues/337)) ([16c974d](https://github.com/SAP/app-studio-toolkit/commit/16c974dff78fd21116556a69789352e2185fe3fd)) 11 | 12 | ## [1.8.3](https://github.com/SAP/app-studio-toolkit/compare/v1.8.1...v1.8.3) (2022-05-16) 13 | 14 | **Note:** Version bump only for package @sap-devx/npm-dependencies-validation 15 | 16 | # [1.8.0](https://github.com/SAP/app-studio-toolkit/compare/v1.7.1...v1.8.0) (2022-03-23) 17 | 18 | ### Features 19 | 20 | - upgrade tool ([#188](https://github.com/SAP/app-studio-toolkit/issues/188)) ([07a7ca2](https://github.com/SAP/app-studio-toolkit/commit/07a7ca23553b3cacd877095cade6660c09b5d4b4)) 21 | 22 | # [1.7.0](https://github.com/SAP/app-studio-toolkit/compare/v1.6.1...v1.7.0) (2022-02-09) 23 | 24 | ### Features 25 | 26 | - display diagnostics at correct position ([#176](https://github.com/SAP/app-studio-toolkit/issues/176)) ([4e390f6](https://github.com/SAP/app-studio-toolkit/commit/4e390f612e4582ea703cc5868a77f646b3279c3a)) 27 | 28 | # [1.6.0](https://github.com/SAP/app-studio-toolkit/compare/v1.5.1...v1.6.0) (2022-01-12) 29 | 30 | ### Features 31 | 32 | - dependencies misalignment initial ([#172](https://github.com/SAP/app-studio-toolkit/issues/172)) ([0216dbd](https://github.com/SAP/app-studio-toolkit/commit/0216dbdb8ed2ec6d96819a78b856a6ba5cb297b4)) 33 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sap-devx/npm-dependencies-validation", 3 | "version": "3.0.1", 4 | "private": true, 5 | "description": "Detects NPM dependencies issues", 6 | "author": "SAP SE", 7 | "license": "Apache-2.0", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SAP/app-studio-toolkit", 11 | "directory": "packages/npm-dependencies-validation" 12 | }, 13 | "main": "./dist/src/api", 14 | "types": "./dist/src/api.d.ts", 15 | "files": [ 16 | ".reuse", 17 | "LICENSES", 18 | "README.md", 19 | "CHANGELOG.md", 20 | "CHANGELOG.old.md", 21 | "dist/src/**/*.js", 22 | "dist/src/**/*.js.map", 23 | "src/**/*.ts" 24 | ], 25 | "scripts": { 26 | "ci": "npm-run-all clean compile coverage", 27 | "clean": "rimraf ./dist ./coverage ./.nyc_output", 28 | "compile": "tsc -p ./", 29 | "test": "mocha", 30 | "coverage": "nyc mocha" 31 | }, 32 | "dependencies": { 33 | "fs-extra": "10.0.0", 34 | "lodash": "4.17.21", 35 | "semver": "7.6.3" 36 | }, 37 | "devDependencies": { 38 | "@types/fs-extra": "9.0.13", 39 | "@types/semver": "7.5.8", 40 | "type-fest": "2.11.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/src/api.ts: -------------------------------------------------------------------------------- 1 | export { findDependencyIssues } from "./depIssuesFinder"; 2 | 3 | export { fixDependencyIssues } from "./depIssuesFixer"; 4 | 5 | export { DepIssue, MismatchDepIssue, MissingDepIssue, DepsProp } from "./types"; 6 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/src/depIssuesFixer.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel } from "./types"; 2 | import { doesPathExist } from "./utils/fileUtil"; 3 | import { invokeNPMCommand } from "./utils/npmUtil"; 4 | import { print } from "./logger"; 5 | import { createPackageJsonPaths } from "./utils/packageJsonUtil"; 6 | 7 | export const internal = { 8 | fixing: (absPath: string) => 9 | `\n${absPath}\n[${getTime()}] Fixing dependency issues...\n`, 10 | doneFixing: (absPath: string) => 11 | `\n[${getTime()}] Done fixing dependency issues. \n${absPath}\n`, 12 | }; 13 | 14 | export async function fixDependencyIssues( 15 | absPath: string, 16 | // TODO: wrap in a more generic logger interface 17 | outputChannel: OutputChannel 18 | ): Promise { 19 | const { filePath, dirPath: cwd } = createPackageJsonPaths(absPath); 20 | const shouldFix = await shouldFixDependencyIssues(filePath); 21 | if (!shouldFix) return; 22 | 23 | // it is important to indicate to the end user an `npm install` process is in progress 24 | outputChannel.show(true); 25 | print(internal.fixing(filePath), outputChannel); 26 | 27 | const config = { commandArgs: ["install"], cwd }; 28 | await invokeNPMCommand(config, outputChannel); 29 | 30 | print(internal.doneFixing(filePath), outputChannel); 31 | } 32 | 33 | function getTime(): string { 34 | return new Date().toLocaleTimeString(); 35 | } 36 | 37 | async function shouldFixDependencyIssues( 38 | packageJsonPath: string 39 | ): Promise { 40 | const packageJsonExists = await doesPathExist(packageJsonPath); 41 | if (!packageJsonExists) return false; 42 | 43 | return true; 44 | } 45 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/src/logger.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel } from "./types"; 2 | 3 | export function print(data: string, outputChannel: OutputChannel): void { 4 | outputChannel.appendLine(data); 5 | } 6 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/src/types.ts: -------------------------------------------------------------------------------- 1 | import { PackageJson } from "type-fest"; 2 | 3 | export interface OutputChannel { 4 | appendLine: (message: string) => void; 5 | show(preserveFocus?: boolean): void; 6 | } 7 | 8 | export type NpmCommandConfig = { 9 | cwd: string; 10 | commandArgs: string[]; 11 | }; 12 | 13 | // TODO: evaluate if this is still used? 14 | export type FilePaths = { 15 | filePath: string; 16 | dirPath: string; 17 | }; 18 | 19 | export type PackageJsonVersion = string; 20 | export type SemVerRange = string; 21 | export type DepsProp = "dependencies" | "devDependencies"; 22 | export type PackageJsonDeps = Pick; 23 | 24 | export type DepIssue = MismatchDepIssue | MissingDepIssue; 25 | 26 | export interface MismatchDepIssue { 27 | type: "mismatch"; 28 | name: string; 29 | isDev: boolean; 30 | expected: SemVerRange; 31 | actual: PackageJsonVersion; 32 | } 33 | 34 | export interface MissingDepIssue { 35 | type: "missing"; 36 | name: string; 37 | isDev: boolean; 38 | } 39 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/src/utils/fileUtil.ts: -------------------------------------------------------------------------------- 1 | // importing directly from `fs/promises` is not supported on nodejs 12 2 | import { promises, constants } from "fs"; 3 | const { access } = promises; 4 | 5 | export function toJsonObject(jsonContent: string): T { 6 | try { 7 | const jsonObj: T = JSON.parse(jsonContent); 8 | return jsonObj; 9 | } catch (error) { 10 | return emptyJsonObject(); 11 | } 12 | } 13 | 14 | // TODO: use fs-extra instead 15 | export async function doesPathExist(absPath: string): Promise { 16 | try { 17 | await access(absPath, constants.R_OK); 18 | return true; 19 | } catch (error) { 20 | return false; 21 | } 22 | } 23 | 24 | export function emptyJsonObject(): T { 25 | return Object.create(null) as T; 26 | } 27 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/config.ts: -------------------------------------------------------------------------------- 1 | // use for long-running tests that rely on `spawn` 2 | // - https://mochajs.org/#timeouts 3 | export const npmSpawnTestTimeout = 12000; 4 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/depIssuesFixer.spec.ts: -------------------------------------------------------------------------------- 1 | import { resolve, join, dirname } from "path"; 2 | import { expect } from "chai"; 3 | import { removeSync } from "fs-extra"; 4 | import { last, noop } from "lodash"; 5 | import { OutputChannel } from "../src/types"; 6 | import { fixDependencyIssues, findDependencyIssues } from "../src/api"; 7 | import { npmSpawnTestTimeout } from "./config"; 8 | 9 | describe("`fixDependencyIssues()` function ", () => { 10 | context("negative", () => { 11 | it("will not fix missing deps", async () => { 12 | const outputChannel: OutputChannel = { 13 | appendLine: noop, 14 | show: noop, 15 | }; 16 | await expect( 17 | fixDependencyIssues("non_existing_package_json_path", outputChannel) 18 | ).to.be.fulfilled; 19 | }); 20 | }); 21 | 22 | context("positive", () => { 23 | const packageJsonPath = resolve( 24 | "./test/packages-samples/positive/fix_missing_deps/package.json" 25 | ); 26 | 27 | afterEach(() => { 28 | const packagePath = dirname(packageJsonPath); 29 | const nodeModulesPath = join(packagePath, "node_modules"); 30 | const packageLockPath = join(packagePath, "package-lock.json"); 31 | 32 | removeSync(nodeModulesPath); 33 | removeSync(packageLockPath); 34 | }); 35 | 36 | it("will fix missing deps", async () => { 37 | const output: string[] = []; 38 | const outputChannel: OutputChannel = { 39 | appendLine: (data: string) => output.push(data), 40 | show: noop, 41 | }; 42 | const problemsBeforeFix = await findDependencyIssues(packageJsonPath); 43 | expect(problemsBeforeFix).to.not.be.empty; 44 | 45 | await fixDependencyIssues(packageJsonPath, outputChannel); 46 | expect(last(output)).to.contain("Done fixing dependency issues"); 47 | const problemsAfterFix = await findDependencyIssues(packageJsonPath); 48 | expect(problemsAfterFix).to.be.empty; 49 | }).timeout(npmSpawnTestTimeout); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/.gitignore: -------------------------------------------------------------------------------- 1 | # our samples can include "dummy" contents of `node_modules` dir 2 | !/**/node_modules/ -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/negative/empty_no_issues/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "empty_no_issues", 3 | "version": "1.0.0", 4 | "dependencies": {}, 5 | "devDependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/negative/no_version_for_dep/node_modules/missing-math/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "missing-math", 3 | "version-is-missing": "3.2.0" 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/negative/no_version_for_dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mismatch-dep", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "missing-math": "^3.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/negative/not_supported/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "not-supported", 3 | "workspaces": [] 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/fix_missing_deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "install_missing_deps", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "json-fixer": "1.6.12" 6 | }, 7 | "devDependencies": {} 8 | } 9 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/mismatch_deps/node_modules/mismatched/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mismatched", 3 | "version": "6.6.6" 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/mismatch_deps/node_modules/range-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "range-parser", 3 | "version": "2.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/mismatch_deps/node_modules/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack", 3 | "version": "1.15.0" 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/mismatch_deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mismatch-dep", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "mismatched": "3.3.3", 6 | "webpack": "legacy" 7 | }, 8 | "devDependencies": { 9 | "range-parser": "~1.2.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/missing_deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no_deps_installed", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "lodash": "~4.17.21", 6 | "json-fixer": "1.6.12" 7 | }, 8 | "devDependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/packages-samples/positive/missing_dev_deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no_deps_installed", 3 | "version": "1.0.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "typescript": "^4.4.4" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/fileUtil.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { toJsonObject } from "../../src/utils/fileUtil"; 3 | 4 | describe("fileUtil unit tests", () => { 5 | context("toJsonObject()", () => { 6 | it("will return json object for valid json string", () => { 7 | const content = `{"name": "test"}`; 8 | type TestJson = { name: string }; 9 | const result: TestJson = toJsonObject(content); 10 | expect(result).to.deep.equal({ name: "test" }); 11 | }); 12 | 13 | it("will return empty object for invalid json string", () => { 14 | const content = `{"invalid json"}`; 15 | const result: { name: string } = toJsonObject(content); 16 | expect(result).to.deep.equal({}); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/packageJsonUtil.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { 3 | isCurrentlySupported, 4 | internal, 5 | } from "../../../src/utils/packageJsonUtil"; 6 | 7 | describe("packageJsonUtil unit test", () => { 8 | context("isCurrentlySupported()", () => { 9 | it("not manged by npm, has pnpm-workspace.yaml", async () => { 10 | const packageJsonPath = 11 | "test/utils/packageJsonUtil/projects/pnpm-workspace/wsFolder1/child/package.json"; 12 | const res = await isCurrentlySupported(packageJsonPath); 13 | expect(res).to.be.false; 14 | }); 15 | 16 | it("not manged by npm, has .yarnrc.yml", async () => { 17 | const packageJsonPath = 18 | "test/utils/packageJsonUtil/projects/yarnrc-yml/wsFolder1/child/package.json"; 19 | const res = await isCurrentlySupported(packageJsonPath); 20 | expect(res).to.be.false; 21 | }); 22 | 23 | it("not manged by npm, has .yarn", async () => { 24 | const packageJsonPath = 25 | "test/utils/packageJsonUtil/projects/yarn/wsFolder1/child/package.json"; 26 | const res = await isCurrentlySupported(packageJsonPath); 27 | expect(res).to.be.false; 28 | }); 29 | 30 | it("not manged by npm, mono repo", async () => { 31 | const packageJsonPath = 32 | "test/utils/packageJsonUtil/projects/monoRepo/package.json"; 33 | const res = await isCurrentlySupported(packageJsonPath); 34 | expect(res).to.be.false; 35 | }); 36 | 37 | it("manged by npm", async () => { 38 | const packageJsonPath = 39 | "test/utils/packageJsonUtil/projects/npm-managed/package.json"; 40 | const res = await isCurrentlySupported(packageJsonPath); 41 | expect(res).to.be.true; 42 | }); 43 | }); 44 | 45 | context("readJsonFile()", () => { 46 | it("non existing package.json", async () => { 47 | const packageJsonPath = 48 | "test/utils/packageJsonUtil/projects/not-existing-folder/package.json"; 49 | const res = await internal.readJsonFile(packageJsonPath); 50 | expect(res).to.be.empty; 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/monoRepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mono-repo", 3 | "workspaces": [] 4 | } 5 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/npm-managed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-managed" 3 | } 4 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/pnpm-workspace/wsFolder1/child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pnpm-workspace" 3 | } 4 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/pnpm-workspace/wsFolder1/child/pnpm-workspace.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/pnpm-workspace/wsFolder1/child/pnpm-workspace.yaml -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarn/wsFolder1/child/.yarn/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarn/wsFolder1/child/.yarn/file.txt -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarn/wsFolder1/child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yarn" 3 | } 4 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarnrc-yml/wsFolder1/child/.yarnrc.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarnrc-yml/wsFolder1/child/.yarnrc.yml -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/test/utils/packageJsonUtil/projects/yarnrc-yml/wsFolder1/child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yarnrc-yml" 3 | } 4 | -------------------------------------------------------------------------------- /packages/npm-dependencies-validation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*", "test/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | // TODO: large timeout only for specific tests 7 | timeout: 5000, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | ** 3 | 4 | # except 5 | !LICENSES 6 | !README.md 7 | !CHANGELOG.md 8 | !CHANGELOG.old.md 9 | !package.json 10 | !icon.png 11 | # bundled sources 12 | !dist/ 13 | # the source maps point to src/ dir 14 | !src/ 15 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vscode-dependencies-validation 2 | 3 | ## 3.0.1 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [d654e14] 8 | - @sap-devx/npm-dependencies-validation@3.0.1 9 | 10 | ## 3.0.0 11 | 12 | ### Major Changes 13 | 14 | - 63e5e97: bump major version for lcm refactor 15 | This is just a version bump, not a breaking change... 16 | 17 | ### Patch Changes 18 | 19 | - Updated dependencies [63e5e97] 20 | - @sap-devx/npm-dependencies-validation@3.0.0 21 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/nyc.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | include: "**/src/**", 3 | // We are excluding files from coverage for which tests would have too little value and too high TCO. 4 | // This is due to the difficulty of running/mocking vscode during the tests. 5 | exclude: [ 6 | "src/extension.ts", 7 | "src/commands.ts", 8 | "src/logger/logger.ts", 9 | "src/diagnostics/refreshDiagnostics.ts", 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/resources/example-manual-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/app-studio-toolkit/d0c9552b3347d80396c2670eb9872f3fe5a03e36/packages/vscode-dependencies-validation/resources/example-manual-mode.png -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/scripts/package-vsix.js: -------------------------------------------------------------------------------- 1 | // TODO: extract a common utility instead of copying this script 2 | const { packageCommand } = require("vsce/out/package"); 3 | const { expect } = require("chai"); 4 | const { resolve } = require("path"); 5 | const { readFileSync, writeFileSync } = require("fs"); 6 | const { writeJsonSync } = require("fs-extra"); 7 | const rootExtDir = resolve(__dirname, ".."); 8 | const pkgJsonPath = resolve(rootExtDir, "package.json"); 9 | // Read & save the original literal representation of the pkg.json 10 | // To avoid dealing with re-formatting (prettier) later on. 11 | const pkgJsonOrgStr = readFileSync(pkgJsonPath, "utf8"); 12 | const pkgJson = JSON.parse(pkgJsonOrgStr); 13 | // During development flows the `main` should point to the compiled sourced 14 | // for fast dev feedback loops. 15 | expect(pkgJson.main).to.equal("./dist/src/extension"); 16 | // During production flows the main should point to the bundled sources 17 | // to reduce loading time. 18 | pkgJson.main = "./dist/extension"; 19 | writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2, EOF: "\n" }); 20 | 21 | // Time to create the VSIX. 22 | packageCommand({ 23 | cwd: rootExtDir, 24 | packagePath: undefined, 25 | baseContentUrl: undefined, 26 | baseImagesUrl: undefined, 27 | useYarn: true, 28 | ignoreFile: undefined, 29 | expandGitHubIssueLinks: undefined, 30 | }) 31 | .catch((e) => { 32 | console.error(e.message); 33 | process.exitCode = 1000; 34 | }) 35 | .finally(() => { 36 | // revert changes to the pkg.json, ensure clean git working directory 37 | writeFileSync(pkgJsonPath, pkgJsonOrgStr); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/autofix/configuration.ts: -------------------------------------------------------------------------------- 1 | import { VscodeWorkspace } from "../vscodeTypes"; 2 | 3 | export const ENABLE_AUTOFIX = "dependenciesValidation.enableAutoFix"; 4 | const DELAY_AUTOFIX = "dependenciesValidation.delayAutoFix"; 5 | 6 | export function isAutoFixEnabled(workspace: VscodeWorkspace): boolean { 7 | const wsConfig = workspace.getConfiguration(); 8 | return wsConfig.get(ENABLE_AUTOFIX, false); 9 | } 10 | 11 | export function getAutoFixDelay(workspace: VscodeWorkspace): number { 12 | const wsConfig = workspace.getConfiguration(); 13 | return wsConfig.get(DELAY_AUTOFIX, 0); 14 | } 15 | 16 | export const internal = { 17 | DELAY_AUTOFIX, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/autofix/eventUtil.ts: -------------------------------------------------------------------------------- 1 | import type { Uri } from "vscode"; 2 | import { debounce } from "lodash"; 3 | import { dirname } from "path"; 4 | import { IChildLogger } from "@vscode-logging/types"; 5 | import { isAutoFixEnabled } from "./configuration"; 6 | import { 7 | clearDiagnostics, 8 | findAndFixDepsIssues, 9 | isInsideNodeModules, 10 | } from "../util"; 11 | import { VscodeFileEventConfig } from "../vscodeTypes"; 12 | import { getLogger } from "../logger/logger"; 13 | 14 | function logger(): IChildLogger { 15 | return getLogger().getChildLogger({ label: "autofix_eventUtils" }); 16 | } 17 | 18 | export const debouncedHandlePkgJsonAutoFix = debounce( 19 | handlePkgJsonAutoFix, 20 | 3000 21 | ); 22 | 23 | async function handlePkgJsonAutoFix( 24 | uri: Uri, 25 | vscodeConfig: VscodeFileEventConfig 26 | ): Promise { 27 | const { workspace, diagnosticCollection, outputChannel } = vscodeConfig; 28 | if (!isAutoFixEnabled(workspace)) { 29 | return; 30 | } 31 | const fixProject = canBeFixed(uri); 32 | 33 | const projectPath = dirname(uri.fsPath); 34 | logger().trace(`${projectPath} project can be fixed - ${fixProject}`); 35 | 36 | if (fixProject) { 37 | await findAndFixDepsIssues(uri, outputChannel); 38 | 39 | logger().trace(`${projectPath} project issues are fixed`); 40 | 41 | clearDiagnostics(diagnosticCollection, uri); 42 | 43 | logger().trace(`${projectPath} project diagnostics are cleared`); 44 | } 45 | } 46 | 47 | function canBeFixed(uri: Uri): boolean { 48 | if (isInsideNodeModules(uri.fsPath)) return false; 49 | // TODO: to discuss: should we ensure this is a package.json file? 50 | return true; 51 | } 52 | 53 | export const internal = { 54 | handlePkgJsonAutoFix, 55 | }; 56 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/autofix/packageJsonFileWatcher.ts: -------------------------------------------------------------------------------- 1 | import type { Uri } from "vscode"; 2 | import { PACKAGE_JSON_FILTER } from "../constants"; 3 | import { VscodeFileEventConfig } from "../vscodeTypes"; 4 | import { debouncedHandlePkgJsonAutoFix } from "./eventUtil"; 5 | 6 | export function addPackageJsonFileWatcher( 7 | vscodeConfig: VscodeFileEventConfig 8 | ): void { 9 | const fileWatcher = 10 | vscodeConfig.workspace.createFileSystemWatcher(PACKAGE_JSON_FILTER); 11 | 12 | fileWatcher.onDidChange(handleFileEvent(vscodeConfig)); 13 | fileWatcher.onDidCreate(handleFileEvent(vscodeConfig)); 14 | } 15 | 16 | function handleFileEvent(vscodeConfig: VscodeFileEventConfig): any { 17 | return (uri: Uri) => debouncedHandlePkgJsonAutoFix(uri, vscodeConfig); 18 | } 19 | 20 | export const internal = { 21 | handleFileEvent, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/commands.ts: -------------------------------------------------------------------------------- 1 | import type { DiagnosticCollection, Uri } from "vscode"; 2 | import { fixDependencyIssues } from "@sap-devx/npm-dependencies-validation"; 3 | import { VscodeCommandsConfig, VscodeOutputChannel } from "./vscodeTypes"; 4 | import { FIX_ALL_ISSUES_COMMAND } from "./constants"; 5 | import { clearDiagnostics } from "./util"; 6 | 7 | async function fixProjectDepsIssues( 8 | outputChannel: VscodeOutputChannel, 9 | diagnosticCollection: DiagnosticCollection, 10 | uri: Uri 11 | ): Promise { 12 | // switched to output-channel only in manual mode 13 | outputChannel.show(true); 14 | await fixDependencyIssues(uri.fsPath, outputChannel); 15 | clearDiagnostics(diagnosticCollection, uri); 16 | } 17 | 18 | // commands for manual fix of dependency issues 19 | export function registerCommands(vscodeConfig: VscodeCommandsConfig): void { 20 | const { subscriptions, commands, outputChannel, diagnosticCollection } = 21 | vscodeConfig; 22 | subscriptions.push( 23 | commands.registerCommand( 24 | FIX_ALL_ISSUES_COMMAND, 25 | executeFixProjectDepsIssues(outputChannel, diagnosticCollection) 26 | ) 27 | ); 28 | } 29 | 30 | function executeFixProjectDepsIssues( 31 | outputChannel: VscodeOutputChannel, 32 | diagnosticCollection: DiagnosticCollection 33 | ) { 34 | return (uri: Uri) => 35 | fixProjectDepsIssues(outputChannel, diagnosticCollection, uri); 36 | } 37 | 38 | export const internal = { 39 | fixProjectDepsIssues, 40 | executeFixProjectDepsIssues, 41 | }; 42 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/constants.ts: -------------------------------------------------------------------------------- 1 | /** Code that is used to associate package.json diagnostic entries with code actions. */ 2 | export const NPM_DEPENDENCY_ISSUES_CODE = "npm_dependency_issues"; 3 | 4 | export const FIX_ALL_ISSUES_COMMAND = "fix.all.dependency.issues.command"; 5 | 6 | export const PACKAGE_JSON_FILTER = "**/package.json"; 7 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/diagnostics/messages.ts: -------------------------------------------------------------------------------- 1 | import type { DepIssue } from "@sap-devx/npm-dependencies-validation"; 2 | import { UnreachableCaseError } from "ts-essentials"; 3 | 4 | /** 5 | * Temp "glue" until we display errors on exact position 6 | * instead of a single error at the top of the package.json file. 7 | */ 8 | export function depIssueToDiagnosticMsg(issue: DepIssue): string { 9 | switch (issue.type) { 10 | case "missing": 11 | return `The "${issue.name}" package is not installed`; 12 | case "mismatch": 13 | return ( 14 | `The "${issue.name}" package installed version ` + 15 | `"${issue.actual}", does not match the declared range "${issue.expected}"` 16 | ); 17 | /* istanbul ignore next -- design time code */ 18 | default: { 19 | // this actually works as a design time check using the `never` type 20 | /* istanbul ignore next -- design time code */ 21 | throw new UnreachableCaseError(issue); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/diagnostics/refreshDiagnostics.ts: -------------------------------------------------------------------------------- 1 | import type { DiagnosticCollection, Uri } from "vscode"; 2 | import { readFile } from "fs-extra"; 3 | import { IChildLogger } from "@vscode-logging/types"; 4 | import { findDependencyIssues } from "@sap-devx/npm-dependencies-validation"; 5 | import { convertToDiagnostics } from "./convertToDiagnostics"; 6 | import { getLogger } from "../logger/logger"; 7 | 8 | function logger(): IChildLogger { 9 | return getLogger().getChildLogger({ label: "diagnostics" }); 10 | } 11 | 12 | /** 13 | * Analyzes package.json file for problems and updates the diagnostics collection 14 | * 15 | * @param uri package.json file path to analyze 16 | * @param dependencyIssueDiagnostics vscode diagnostic collection 17 | */ 18 | export async function refreshDiagnostics( 19 | uri: Uri, 20 | dependencyIssueDiagnostics: Pick 21 | ): Promise { 22 | const pkgJsonPath = uri.fsPath; 23 | const issues = await findDependencyIssues(pkgJsonPath); 24 | logger().trace("Dependency Issues Detected", { issues }); 25 | 26 | // In theory, we should use VSCode's file system APIs 27 | // However, these npm related flows do not (currently?) support virtual file systems or "edited in memory" files: 28 | // 1. spawning processes in `@sap-devx/npm-dependencies-validation`, e.g: `npm install` to fix the dep issues. 29 | // 2. reading from fs directly in `@sap-devx/npm-dependencies-validation` 30 | const diagnostics = await convertToDiagnostics({ 31 | pkgJsonPath, 32 | issues, 33 | readFile, 34 | }); 35 | logger().trace("VSCode Diagnostics created", { diagnostics }); 36 | dependencyIssueDiagnostics.set(uri, diagnostics); 37 | } 38 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/diagnostics/shouldBeChecked.ts: -------------------------------------------------------------------------------- 1 | import { endsWith } from "lodash"; 2 | import { isInsideNodeModules } from "../util"; 3 | 4 | export function shouldBeChecked(path: string): boolean { 5 | const isPkgJson = endsWith(path, "package.json"); 6 | const isInNodeModules = isInsideNodeModules(path); 7 | const shouldBeChecked = isPkgJson && !isInNodeModules; 8 | return shouldBeChecked; 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/logger/logger.ts: -------------------------------------------------------------------------------- 1 | import type { ExtensionContext, OutputChannel } from "vscode"; 2 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 3 | import { configureLogger, NOOP_LOGGER } from "@vscode-logging/wrapper"; 4 | 5 | export const LOGGING_LEVEL_CONFIG_PROP = "dependenciesValidation.logging.level"; 6 | export const SOURCE_TRACKING_CONFIG_PROP = 7 | "dependenciesValidation.logging.sourceLocationTracking"; 8 | 9 | let logger: IVSCodeExtLogger = NOOP_LOGGER; 10 | 11 | /** 12 | * Note the use of a getter function so the value would be lazy resolved on each use. 13 | * This enables concise and simple consumption of the Logger throughout our extension. 14 | */ 15 | export function getLogger(): IVSCodeExtLogger { 16 | return logger; 17 | } 18 | 19 | export function initLogger( 20 | context: ExtensionContext, 21 | outputChannel: OutputChannel, 22 | extensionName: string 23 | ): void { 24 | try { 25 | logger = configureLogger({ 26 | extName: extensionName, 27 | logPath: context.logPath, 28 | logOutputChannel: outputChannel, 29 | loggingLevelProp: LOGGING_LEVEL_CONFIG_PROP, 30 | sourceLocationProp: SOURCE_TRACKING_CONFIG_PROP, 31 | subscriptions: context.subscriptions, 32 | }); 33 | } catch (error) { 34 | console.error( 35 | `Logs won't be available for the ${extensionName} extension: "`, 36 | error.message 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/util.ts: -------------------------------------------------------------------------------- 1 | import type { DiagnosticCollection, Uri } from "vscode"; 2 | import { 3 | findDependencyIssues, 4 | fixDependencyIssues, 5 | } from "@sap-devx/npm-dependencies-validation"; 6 | import { isEmpty } from "lodash"; 7 | import { VscodeOutputChannel } from "./vscodeTypes"; 8 | 9 | export function isInsideNodeModules(absPath: string): boolean { 10 | return /[\\/]node_modules[\\/]/.test(absPath); 11 | } 12 | 13 | export function clearDiagnostics( 14 | diagnosticCollection: DiagnosticCollection, 15 | fileUri: Uri 16 | ): void { 17 | diagnosticCollection.delete(fileUri); 18 | } 19 | 20 | export async function findAndFixDepsIssues( 21 | packageJsonUri: Uri, 22 | outputChannel: VscodeOutputChannel 23 | ): Promise { 24 | const { fsPath } = packageJsonUri; 25 | const depIssues = await findDependencyIssues(fsPath); 26 | if (isEmpty(depIssues)) { 27 | return; 28 | } 29 | await fixDependencyIssues(fsPath, outputChannel); 30 | } 31 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/src/vscodeTypes.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ExtensionContext, 3 | OutputChannel, 4 | Uri, 5 | window, 6 | workspace, 7 | languages, 8 | commands, 9 | CodeActionKind, 10 | DiagnosticCollection, 11 | } from "vscode"; 12 | 13 | export type VscodeOutputChannel = Pick< 14 | OutputChannel, 15 | "append" | "show" | "appendLine" 16 | >; 17 | 18 | export type VscodeWorkspace = typeof workspace; 19 | 20 | export type VscodeWindow = typeof window; 21 | 22 | export type VscodeCommands = typeof commands; 23 | 24 | export type VscodeContextSubscriptions = ExtensionContext["subscriptions"]; 25 | 26 | export type VscodeUriFile = { 27 | createUri: typeof Uri.file; 28 | }; 29 | 30 | export type VscodeLanguages = typeof languages; 31 | 32 | export type VscodeConfig = VscodeCodeActionProviderConfig & 33 | VscodePackageJsonChangesConfig & 34 | VscodeCommandsConfig & 35 | VscodeFileEventConfig & 36 | VscodeUriFile; 37 | 38 | export type VscodeCodeActionProviderConfig = { 39 | subscriptions: VscodeContextSubscriptions; 40 | kind: CodeActionKind; 41 | languages: VscodeLanguages; 42 | }; 43 | 44 | export type VscodePackageJsonChangesConfig = { 45 | window: VscodeWindow; 46 | subscriptions: VscodeContextSubscriptions; 47 | workspace: VscodeWorkspace; 48 | diagnosticCollection: DiagnosticCollection; 49 | }; 50 | 51 | export type VscodeFileEventConfig = { 52 | workspace: VscodeWorkspace; 53 | diagnosticCollection: DiagnosticCollection; 54 | outputChannel: VscodeOutputChannel; 55 | }; 56 | 57 | export type VscodeCommandsConfig = { 58 | commands: VscodeCommands; 59 | subscriptions: VscodeContextSubscriptions; 60 | outputChannel: VscodeOutputChannel; 61 | diagnosticCollection: DiagnosticCollection; 62 | }; 63 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/test/autofix/configuration.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { SinonMock, createSandbox, SinonSandbox } from "sinon"; 3 | import type { WorkspaceConfiguration } from "vscode"; 4 | import { 5 | ENABLE_AUTOFIX, 6 | getAutoFixDelay, 7 | isAutoFixEnabled, 8 | internal, 9 | } from "../../src/autofix/configuration"; 10 | import { VscodeWorkspace } from "../../src/vscodeTypes"; 11 | 12 | describe("eventUtil unit tests", () => { 13 | let sandbox: SinonSandbox; 14 | let workspaceConfigurationSinonMock: SinonMock; 15 | 16 | const workspaceConfigurationMock = {}; 17 | workspaceConfigurationMock.get = () => ""; 18 | const workspaceMock = {}; 19 | workspaceMock.getConfiguration = () => workspaceConfigurationMock; 20 | 21 | beforeEach(() => { 22 | sandbox = createSandbox(); 23 | workspaceConfigurationSinonMock = sandbox.mock(workspaceConfigurationMock); 24 | }); 25 | 26 | afterEach(() => { 27 | workspaceConfigurationSinonMock.verify(); 28 | sandbox.restore(); 29 | }); 30 | 31 | context("isAutoFixEnabled()", () => { 32 | it("autofix is disabled", () => { 33 | workspaceConfigurationSinonMock 34 | .expects("get") 35 | .withExactArgs(ENABLE_AUTOFIX, false) 36 | .returns(false); 37 | const res = isAutoFixEnabled(workspaceMock); 38 | expect(res).to.be.false; 39 | }); 40 | 41 | it("autofix is enabled", () => { 42 | workspaceConfigurationSinonMock 43 | .expects("get") 44 | .withExactArgs(ENABLE_AUTOFIX, false) 45 | .returns(true); 46 | const res = isAutoFixEnabled(workspaceMock); 47 | expect(res).to.be.true; 48 | }); 49 | }); 50 | 51 | context("getAutoFixDelay()", () => { 52 | it("delay is 30 seconds", () => { 53 | const delay = 30; 54 | workspaceConfigurationSinonMock 55 | .expects("get") 56 | .withExactArgs(internal.DELAY_AUTOFIX, 0) 57 | .returns(delay); 58 | const res = getAutoFixDelay(workspaceMock); 59 | expect(res).to.equal(delay); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/test/commands.spec.ts: -------------------------------------------------------------------------------- 1 | import type { DiagnosticCollection, Uri } from "vscode"; 2 | import * as proxyquire from "proxyquire"; 3 | import { createSandbox, SinonMock, SinonSandbox } from "sinon"; 4 | import { internal } from "../src/commands"; 5 | import { outputChannelMock } from "./vscodeMocks"; 6 | import { utilProxy } from "./moduleProxies"; 7 | 8 | describe("commands unit test", () => { 9 | let sandbox: SinonSandbox; 10 | let outputChannelSinonMock: SinonMock; 11 | let utilProxySinonMock: SinonMock; 12 | 13 | beforeEach(() => { 14 | sandbox = createSandbox(); 15 | outputChannelSinonMock = sandbox.mock(outputChannelMock); 16 | utilProxySinonMock = sandbox.mock(utilProxy); 17 | }); 18 | 19 | afterEach(() => { 20 | outputChannelSinonMock.verify(); 21 | utilProxySinonMock.verify(); 22 | sandbox.restore(); 23 | }); 24 | 25 | context("fixProjectDepsIssues()", () => { 26 | let fixProjectDepsIssuesProxy: typeof internal.fixProjectDepsIssues; 27 | 28 | before(() => { 29 | const commandsModule = proxyquire("../src/commands", { 30 | "./util": utilProxy, 31 | }); 32 | 33 | fixProjectDepsIssuesProxy = commandsModule.internal.fixProjectDepsIssues; 34 | }); 35 | 36 | it("succeeded", async () => { 37 | const packageJsonPath = "root/folder/package.json"; 38 | const uri = { fsPath: packageJsonPath }; 39 | const diagnosticCollection = {}; 40 | 41 | outputChannelSinonMock.expects("show").withExactArgs(true); 42 | 43 | utilProxySinonMock 44 | .expects("clearDiagnostics") 45 | .withExactArgs(diagnosticCollection, uri); 46 | 47 | await fixProjectDepsIssuesProxy( 48 | outputChannelMock, 49 | diagnosticCollection, 50 | uri 51 | ); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/test/moduleProxies.ts: -------------------------------------------------------------------------------- 1 | export const diagnosticsProxy = { 2 | refreshDiagnostics() { 3 | return Promise.reject("refreshDiagnostics method is not implemented"); 4 | }, 5 | "@noCallThru": true, 6 | }; 7 | 8 | export const npmDepsValidationProxy = { 9 | fixDependencyIssues() { 10 | return Promise.reject("fixDependencyIssues method is not implemented"); 11 | }, 12 | findDependencyIssues() { 13 | return Promise.reject("findDependencyIssues method is not implemented"); 14 | }, 15 | "@noCallThru": true, 16 | }; 17 | 18 | export const eventUtilProxy = { 19 | debouncedHandlePkgJsonAutoFix() { 20 | return Promise.reject( 21 | "debouncedHandlePkgJsonAutoFix method is not implemented" 22 | ); 23 | }, 24 | "@noCallThru": true, 25 | }; 26 | 27 | export const configurationProxy = { 28 | isAutoFixEnabled() { 29 | throw new Error("isAutoFixEnabled method is not implemented"); 30 | }, 31 | "@noCallThru": true, 32 | }; 33 | 34 | export const utilProxy = { 35 | findAndFixDepsIssues() { 36 | return Promise.reject("findAndFixDepsIssues method is not implemented"); 37 | }, 38 | clearDiagnostics() { 39 | throw new Error("clearDiagnostics method is not implemented"); 40 | }, 41 | }; 42 | 43 | export const loggerProxyObject = { 44 | info(): any { 45 | throw new Error("info method is not implemented"); 46 | }, 47 | trace(): any { 48 | throw new Error("trace method is not implemented"); 49 | }, 50 | }; 51 | 52 | export const loggerProxy = { 53 | getLogger() { 54 | return { 55 | getChildLogger: () => loggerProxyObject, 56 | }; 57 | }, 58 | "@noCallThru": true, 59 | }; 60 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/test/npmIssuesActionProvider.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import * as proxyquire from "proxyquire"; 3 | import type { 4 | CodeAction, 5 | Range, 6 | TextDocument, 7 | CodeActionKind, 8 | CancellationToken, 9 | Disposable, 10 | languages, 11 | CodeActionProvider, 12 | } from "vscode"; 13 | import { FIX_ALL_ISSUES_COMMAND } from "../src/constants"; 14 | import { diagnosticsProxy } from "./moduleProxies"; 15 | import { codeActionContextMock } from "./vscodeMocks"; 16 | import { VscodeCodeActionProviderConfig } from "../src/vscodeTypes"; 17 | import { NPMIssuesActionProvider } from "../src/npmIssuesActionProvider"; 18 | 19 | describe("npmIssuesActionProvider unit test", () => { 20 | context("provideCodeActions()", () => { 21 | let provider: NPMIssuesActionProvider; 22 | 23 | before(() => { 24 | const npmIssuesActionProviderModule = proxyquire( 25 | "../src/npmIssuesActionProvider", 26 | { 27 | "./diagnostics": diagnosticsProxy, 28 | } 29 | ); 30 | 31 | const vscodeConfig = {}; 32 | vscodeConfig.subscriptions = []; 33 | vscodeConfig.languages = {}; 34 | vscodeConfig.languages.registerCodeActionsProvider = () => {}; 35 | vscodeConfig.kind = {}; 36 | 37 | provider = new npmIssuesActionProviderModule.registerCodeActionsProvider( 38 | vscodeConfig 39 | ); 40 | }); 41 | 42 | it("provideCodeActions", () => { 43 | const actions: CodeAction[] = provider.provideCodeActions( 44 | {}, 45 | {}, 46 | codeActionContextMock, 47 | {} 48 | ); 49 | expect(actions).to.have.lengthOf(1); 50 | const { command } = actions[0]; 51 | expect(command?.title).to.equal("Fix all dependency issues"); 52 | expect(command?.command).to.equal(FIX_ALL_ISSUES_COMMAND); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/test/vscodeMocks.ts: -------------------------------------------------------------------------------- 1 | import { NPM_DEPENDENCY_ISSUES_CODE } from "../src/constants"; 2 | import type { 3 | DiagnosticCollection, 4 | CodeActionContext, 5 | OutputChannel, 6 | CodeActionKind, 7 | Diagnostic, 8 | } from "vscode"; 9 | 10 | export const outputChannelMock = {}; 11 | outputChannelMock.show = () => ""; 12 | outputChannelMock.append = () => ""; 13 | outputChannelMock.appendLine = () => ""; 14 | 15 | export const diagnosticCollectionMock = {}; 16 | diagnosticCollectionMock.set = () => ""; 17 | 18 | const diagnosticMock = {}; 19 | diagnosticMock.code = NPM_DEPENDENCY_ISSUES_CODE; 20 | 21 | export const codeActionContextMock = { 22 | diagnostics: [diagnosticMock], 23 | triggerKind: 2, 24 | only: {}, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*", "test/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-dependencies-validation/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const baseConfig = require("../../webpack.config.vscode.base"); 3 | 4 | const config = Object.assign({}, baseConfig, { 5 | entry: "./dist/src/extension.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "extension.js", 9 | libraryTarget: "commonjs2", 10 | devtoolModuleFilenameTemplate: "../[resource-path]", 11 | }, 12 | // 📖 -> https://webpack.js.org/configuration/externals/ 13 | externals: { 14 | // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed. 15 | vscode: "commonjs vscode", 16 | }, 17 | plugins: [], 18 | }); 19 | 20 | module.exports = config; 21 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/.mocharc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.mocharc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | spec: "./dist/test/**/*spec.js", 6 | }; 7 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | ** 3 | 4 | # except 5 | !LICENSES 6 | !README.md 7 | !CHANGELOG.md 8 | !CHANGELOG.old.md 9 | !package.json 10 | !icon.png 11 | # bundled sources 12 | !dist/ 13 | # the source maps point to src/ dir 14 | !src/ 15 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vscode-deps-upgrade-tool 2 | 3 | ## 5.0.0 4 | 5 | ### Major Changes 6 | 7 | - a7ed1ff: bump major version for lcm refactor 8 | This is just a version bump, not a breaking change... 9 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/nyc.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | include: "**/src/**", 3 | /** 4 | * We are implementing tests as pure unit tests without excessive mocks for VSCode APIs 5 | * However not all source files can be tested using this approach. 6 | * This is an instance of getting 80% of the benefits for 20% of the cost 7 | */ 8 | exclude: ["src/extension.ts", "src/logger.ts"], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/scripts/package-vsix.js: -------------------------------------------------------------------------------- 1 | // TODO: extract a common utility instead of copying this script 2 | const { packageCommand } = require("vsce/out/package"); 3 | const { expect } = require("chai"); 4 | const { resolve } = require("path"); 5 | const { readFileSync, writeFileSync } = require("fs"); 6 | const { writeJsonSync } = require("fs-extra"); 7 | const rootExtDir = resolve(__dirname, ".."); 8 | const pkgJsonPath = resolve(rootExtDir, "package.json"); 9 | // Read & save the original literal representation of the pkg.json 10 | // To avoid dealing with re-formatting (prettier) later on. 11 | const pkgJsonOrgStr = readFileSync(pkgJsonPath, "utf8"); 12 | const pkgJson = JSON.parse(pkgJsonOrgStr); 13 | // During development flows the `main` should point to the compiled sourced 14 | // for fast dev feedback loops. 15 | expect(pkgJson.main).to.equal("./dist/src/extension"); 16 | // During production flows the main should point to the bundled sources 17 | // to reduce loading time. 18 | pkgJson.main = "./dist/extension"; 19 | writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2, EOF: "\n" }); 20 | 21 | // Time to create the VSIX. 22 | packageCommand({ 23 | cwd: rootExtDir, 24 | packagePath: undefined, 25 | baseContentUrl: undefined, 26 | baseImagesUrl: undefined, 27 | useYarn: true, 28 | ignoreFile: undefined, 29 | expandGitHubIssueLinks: undefined, 30 | }) 31 | .catch((e) => { 32 | console.error(e.message); 33 | process.exitCode = 1000; 34 | }) 35 | .finally(() => { 36 | // revert changes to the pkg.json, ensure clean git working directory 37 | writeFileSync(pkgJsonPath, pkgJsonOrgStr); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/src/logger.ts: -------------------------------------------------------------------------------- 1 | import type { ExtensionContext, OutputChannel } from "vscode"; 2 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 3 | // avoiding the main wrapper's entry point in-order to avoid its import of "vscode" 4 | import { NOOP_LOGGER } from "@vscode-logging/wrapper/dist/src/noop-logger"; 5 | import { CONFIG_PROPS_AND_FULL_NAME } from "./settings"; 6 | 7 | let logger: IVSCodeExtLogger = NOOP_LOGGER; 8 | 9 | /** 10 | * Note the use of a getter function so the value would be lazy resolved on each use. 11 | * This enables concise and simple consumption of the Logger throughout our extension. 12 | */ 13 | export function getLogger(): IVSCodeExtLogger { 14 | return logger; 15 | } 16 | 17 | export function initLogger( 18 | context: ExtensionContext, 19 | outputChannel: OutputChannel, 20 | extensionName: string 21 | ): void { 22 | try { 23 | // using `require` to avoid the wrapper's import of `vscode`. 24 | const configureLogger = require("@vscode-logging/wrapper").configureLogger; 25 | logger = configureLogger({ 26 | extName: extensionName, 27 | logPath: context.logPath, 28 | logOutputChannel: outputChannel, 29 | loggingLevelProp: CONFIG_PROPS_AND_FULL_NAME.LOGGING_LEVEL, 30 | sourceLocationProp: CONFIG_PROPS_AND_FULL_NAME.SOURCE_TRACKING, 31 | subscriptions: context.subscriptions, 32 | }); 33 | } catch (error) { 34 | console.error( 35 | `Logs won't be available for the ${extensionName} extension: "`, 36 | error.message 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/src/settings.ts: -------------------------------------------------------------------------------- 1 | import type { workspace } from "vscode"; 2 | 3 | export const CONFIG_PROPS_AND_FULL_NAME: Record = { 4 | ENABLED: "dependencyUpgrade.enabled", 5 | LOGGING_LEVEL: "dependencyUpgrade.logging.level", 6 | SOURCE_TRACKING: "dependencyUpgrade.logging.sourceLocationTracking", 7 | DELAY_MIN: "dependencyUpgrade.delay.min", 8 | DELAY_MAX: "dependencyUpgrade.delay.max", 9 | }; 10 | 11 | export const CONFIG_PROPS_AND_DEFAULTS = { 12 | ENABLED: false, 13 | LOGGING_LEVEL: "error", 14 | SOURCE_TRACKING: false, 15 | DELAY_MIN: 5, 16 | DELAY_MAX: 15, 17 | }; 18 | 19 | export type ConfigPropsKeys = keyof typeof CONFIG_PROPS_AND_DEFAULTS; 20 | 21 | /* istanbul ignore next -- little value in implementing tests for this function (mainly VSCode APIS...) */ 22 | export function getConfigProp( 23 | getConfiguration: typeof workspace["getConfiguration"], 24 | prop: R 25 | ): typeof CONFIG_PROPS_AND_DEFAULTS[R] { 26 | const wsConfig = getConfiguration(); 27 | const propVal = wsConfig.get( 28 | CONFIG_PROPS_AND_FULL_NAME[prop], 29 | CONFIG_PROPS_AND_DEFAULTS[prop] 30 | ); 31 | return propVal; 32 | } 33 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/test/fixtures/apply-upgrades/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apply-upgrades-fixture", 3 | "dependencies": { 4 | "@ui5/cli": "1.12.2", 5 | "lodash": "4.17.0" 6 | }, 7 | "devDependencies": { 8 | "mocha": "7.10.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src/**/*", "test/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-deps-upgrade-tool/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const baseConfig = require("../../webpack.config.vscode.base"); 3 | 4 | const config = Object.assign({}, baseConfig, { 5 | entry: "./dist/src/extension.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "extension.js", 9 | libraryTarget: "commonjs2", 10 | devtoolModuleFilenameTemplate: "../[resource-path]", 11 | }, 12 | // 📖 -> https://webpack.js.org/configuration/externals/ 13 | externals: { 14 | // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed. 15 | vscode: "commonjs vscode", 16 | }, 17 | plugins: [], 18 | }); 19 | 20 | module.exports = config; 21 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'examples/*' -------------------------------------------------------------------------------- /renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | configMigration: true, 4 | extends: [ 5 | 'config:recommended', 6 | ':pinDependencies', 7 | ':pinDevDependencies', 8 | 'helpers:pinGitHubActionDigests', 9 | 'mergeConfidence:all-badges', 10 | ], 11 | schedule: [ 12 | 'on friday and saturday', 13 | ], 14 | prHourlyLimit: 5, 15 | packageRules: [ 16 | { 17 | matchPackageNames: [ 18 | '@types/node', 19 | ], 20 | allowedVersions: '^20', 21 | }, 22 | { 23 | groupName: 'jest and related packages', 24 | groupSlug: 'jest*', 25 | matchPackageNames: [ 26 | '/jest/', 27 | ], 28 | }, 29 | { 30 | matchPackageNames: [ 31 | 'husky', 32 | 'lint-staged', 33 | 'cz-conventional-changelog', 34 | '@commitlint/cli', 35 | '@commitlint/config-conventional', 36 | 'prettier', 37 | 'npm-run-all', 38 | 'shx', 39 | 'rimraf', 40 | 'coveralls', 41 | 'nyc', 42 | 'ts-node', 43 | '/eslint/', 44 | ], 45 | matchDepTypes: [ 46 | 'devDependencies', 47 | ], 48 | matchUpdateTypes: [ 49 | 'minor', 50 | 'patch', 51 | ], 52 | groupName: 'all non-major dev cli dependencies', 53 | groupSlug: 'all-dev-cli-minor-patch', 54 | }, 55 | ], 56 | } 57 | -------------------------------------------------------------------------------- /scripts/merge-coverage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script will create a **merged** coverage reports from all sub-packages. 3 | * This report will can be found at the root `coverage` dir and will also be uploaded to coveralls.io 4 | * - based on https://github.com/istanbuljs/istanbuljs/blob/1fe490e51909607137ded25b1688581c9fd926cd/monorepo-merge-reports.js 5 | */ 6 | const { dirname, basename, join, resolve } = require("path"); 7 | const { spawnSync } = require("child_process"); 8 | 9 | const rimraf = require("rimraf"); 10 | const makeDir = require("make-dir"); 11 | const glob = require("glob"); 12 | 13 | process.chdir(resolve(__dirname, "..")); 14 | rimraf.sync(".nyc_output"); 15 | makeDir.sync(".nyc_output"); 16 | 17 | // Merge coverage data from each package so we can generate a complete reports 18 | glob.sync("packages/*/.nyc_output").forEach((nycOutput) => { 19 | const cwd = dirname(nycOutput); 20 | const { status, stderr } = spawnSync( 21 | resolve("node_modules", ".bin", "nyc"), 22 | [ 23 | "merge", 24 | ".nyc_output", 25 | join(__dirname, "..", ".nyc_output", basename(cwd) + ".json"), 26 | ], 27 | { 28 | encoding: "utf8", 29 | shell: true, 30 | cwd, 31 | } 32 | ); 33 | 34 | if (status !== 0) { 35 | console.error(stderr); 36 | process.exit(status); 37 | } 38 | }); 39 | 40 | // Create merged report 41 | const { status, stderr } = spawnSync( 42 | resolve("node_modules", ".bin", "nyc"), 43 | ["report", "--reporter=lcov"], 44 | { 45 | encoding: "utf8", 46 | shell: true, 47 | cwd: resolve(__dirname, ".."), 48 | } 49 | ); 50 | 51 | if (status !== 0) { 52 | console.error(stderr); 53 | process.exit(status); 54 | } 55 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "composite": true, 5 | "module": "commonjs", 6 | "lib": ["es7"], 7 | "target": "es2017", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "useUnknownInCatchVariables": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { 4 | "path": "./packages/app-studio-remote-access" 5 | }, 6 | { 7 | "path": "./packages/app-studio-toolkit" 8 | }, 9 | { 10 | "path": "./packages/app-studio-toolkit-themes" 11 | }, 12 | { 13 | "path": "./packages/app-studio-toolkit-types" 14 | }, 15 | { 16 | "path": "./packages/npm-dependencies-validation" 17 | }, 18 | { 19 | "path": "./packages/vscode-dependencies-validation" 20 | }, 21 | { 22 | "path": "./packages/vscode-deps-upgrade-tool" 23 | }, 24 | { 25 | "path": "./examples/sample-action-client" 26 | }, 27 | { 28 | "path": "./examples/vscode-using-workspace-api" 29 | }, 30 | { 31 | "path": "./examples/vscode-using-upgrade-tool" 32 | } 33 | ], 34 | "files": [], 35 | "include": [], 36 | "exclude": [] 37 | } 38 | -------------------------------------------------------------------------------- /webpack.config.vscode.base.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const config = { 4 | optimization: { 5 | // The Default minimization options can sometimes cause JavaScript runtime errors. 6 | // Also we don't actually need to minimize as much (not targeted for browser). 7 | // Rather we mostly need to reduce the number of fileSystem access requests 8 | // by reducing the number of files packaged inside our VSCode extensions 9 | minimize: false, 10 | }, 11 | target: "node", 12 | devtool: "source-map", 13 | resolve: { 14 | // Solution for sibling package resolution inside a monorepo 15 | // TODO: is this still needed after the transition to pnpm? 16 | modules: [ 17 | path.resolve(__dirname, "node_modules"), 18 | path.resolve(__dirname, "../node_modules"), 19 | "node_modules", 20 | ], 21 | extensions: [".js"], 22 | }, 23 | }; 24 | 25 | module.exports = config; 26 | --------------------------------------------------------------------------------