diff --git a/.dockerignore b/.dockerignore index 7bd034374..8ad2f7f8d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,22 +8,22 @@ .* # you can add exceptions like in .gitignore to maintain a whitelist: # e.g. -!.babelrc -!.eslintrc -!.prettierrc.json -!.prettierignore +!babel.config.js +!eslintrc.js +!.prettierrc !.eslintignore !.stylelintrc +!.jest.config.js +!.jestEnvironment.js +!.yarnrc.yml +!.yarn/releases/yarn-*.cjs +!.yarn/plugins/* +!.pnp.js -# do not copy over node_modules we will run `pnpm install` anyway +# just in case, yarn 2 pnp is enabled node_modules -website -jest -docs -contrib -docker-examples -website -systemd +.husky/ +.git/ # output from test runs and similar things *.log @@ -33,7 +33,21 @@ coverage/ # IDE config files jsconfig.json *.iml - # let's not get too recursive ;) Dockerfile* docker-compose*.yaml +.github/ +.husky/ +*.log +coverage/ +.vscode/ +*.md +contrib/ +docs/ +docker-examples/ +systemd/ +assets/ +jest*.js +test/ +wiki/ +debug/ diff --git a/.github/workflows/docker-apm.yml b/.github/workflows/docker-apm.yml new file mode 100644 index 000000000..c6a5a9874 --- /dev/null +++ b/.github/workflows/docker-apm.yml @@ -0,0 +1,56 @@ +name: Docker publish apm to docker.io + +on: + push: + paths: + - '.github/workflows/docker-apm.yml' + - 'src/**' + - 'conf/**' + - 'types/**' + - 'docker-bin/**' + - 'bin/**' + - 'package.json' + - 'yarn.lock' + - '.yarn/**' + - '.yarnrc.yaml' + - '.pnp.js' + - '.dockerignore' + - 'docker-compose.yml' + - 'Dockerfile' + branches: + - 'apm' + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + docker: + runs-on: ubuntu-latest + if: github.repository == 'mbtools/verdaccio' + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + - uses: docker/login-action@v3 + name: Login Docker Hub + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Prepare docker image tags + id: docker_meta + uses: docker/metadata-action@v5 + with: + images: abappm/registry + tags: | + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'apm') }} + - name: Build & Push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.prettierrc b/.prettierrc index d10be3bb6..83ad2ca06 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "endOfLine": "lf", + "endOfLine": "auto", "useTabs": false, "printWidth": 100, "tabWidth": 2, diff --git a/.vscode/settings.json b/.vscode/settings.json index e2229d6bd..c99d14a44 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,14 @@ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { + "**/node_modules/**/node_modules": true, "**/build": false, "**/coverage": true, ".idea": true, }, "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true + "editor.formatOnSave": false, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, } diff --git a/Dockerfile b/Dockerfile index 3950b62cb..686633b80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,13 @@ -FROM --platform=${BUILDPLATFORM:-linux/amd64} node:21-alpine AS builder +FROM --platform=${BUILDPLATFORM:-linux/amd64} node:20.15.1-alpine AS builder -ENV NODE_ENV=development \ - VERDACCIO_BUILD_REGISTRY=https://registry.npmjs.org +ENV NODE_ENV=production \ + VERDACCIO_BUILD_REGISTRY=https://registry.npmjs.org \ + HUSKY_SKIP_INSTALL=1 \ + CI=true \ + HUSKY_DEBUG=1 -RUN apk --no-cache add openssl ca-certificates wget && \ +RUN apk add --force-overwrite && \ + apk --no-cache add openssl ca-certificates wget && \ apk --no-cache add g++ gcc libgcc libstdc++ linux-headers make python3 && \ wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ wget -q https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk && \ @@ -11,17 +15,24 @@ RUN apk --no-cache add openssl ca-certificates wget && \ WORKDIR /opt/verdaccio-build COPY . . -RUN npm -g i pnpm@8.9.0 && \ - pnpm config set registry $VERDACCIO_BUILD_REGISTRY && \ - pnpm install --frozen-lockfile --ignore-scripts && \ - rm -Rf test && \ - pnpm run build -# FIXME: need to remove devDependencies from the build -# NODE_ENV=production pnpm install --frozen-lockfile --ignore-scripts -# RUN pnpm install --prod --ignore-scripts -FROM node:21-alpine -LABEL maintainer="https://github.com/verdaccio/verdaccio" +## build the project and create a tarball of the project for later +## global installation +RUN yarn config set npmRegistryServer $VERDACCIO_BUILD_REGISTRY && \ + yarn config set enableProgressBars true && \ + yarn config set enableScripts false && \ + yarn install --immutable && \ + yarn build +## pack the project +RUN yarn pack --dry-run \ + && yarn pack --out verdaccio.tgz \ + && mkdir -p /opt/tarball \ + && mv /opt/verdaccio-build/verdaccio.tgz /opt/tarball +## clean up and reduce bundle size +RUN rm -Rf /opt/verdaccio-build + +FROM node:20.15.1-alpine +LABEL maintainer="https://github.com/abapPM/abapPM" ENV VERDACCIO_APPDIR=/opt/verdaccio \ VERDACCIO_USER_NAME=verdaccio \ @@ -33,17 +44,33 @@ ENV PATH=$VERDACCIO_APPDIR/docker-bin:$PATH \ WORKDIR $VERDACCIO_APPDIR +# https://github.com/Yelp/dumb-init RUN apk --no-cache add openssl dumb-init RUN mkdir -p /verdaccio/storage /verdaccio/plugins /verdaccio/conf -COPY --from=builder /opt/verdaccio-build . +COPY --from=builder /opt/tarball . -RUN ls packages/config/src/conf -ADD packages/config/src/conf/docker.yaml /verdaccio/conf/config.yaml +USER root +# install verdaccio as a global package so is fully handled by npm +# ensure none dependency is being missing and is prod by default +RUN npm install -g $VERDACCIO_APPDIR/verdaccio.tgz \ + ## clean up cache + && npm cache clean --force \ + && rm -Rf .npm/ \ + && rm $VERDACCIO_APPDIR/verdaccio.tgz \ + # yarn is not need it after this step + # Also remove the symlinks added in the [`node:alpine` Docker image](https://github.com/nodejs/docker-node/blob/02a64a08a98a472c6141cd583d2e9fc47bcd9bfd/18/alpine3.16/Dockerfile#L91-L92). + && rm -Rf /opt/yarn-v1.22.19/ /usr/local/bin/yarn /usr/local/bin/yarnpkg + +# apm assets and config +ADD abappm /verdaccio/abappm +ADD config.yaml /verdaccio/conf/config.yaml + +ADD docker-bin $VERDACCIO_APPDIR/docker-bin RUN adduser -u $VERDACCIO_USER_UID -S -D -h $VERDACCIO_APPDIR -g "$VERDACCIO_USER_NAME user" -s /sbin/nologin $VERDACCIO_USER_NAME && \ - chmod -R +x $VERDACCIO_APPDIR/packages/verdaccio/bin $VERDACCIO_APPDIR/docker-bin && \ + chmod -R +x /usr/local/lib/node_modules/verdaccio/bin/verdaccio $VERDACCIO_APPDIR/docker-bin && \ chown -R $VERDACCIO_USER_UID:root /verdaccio/storage && \ chmod -R g=u /verdaccio/storage /etc/passwd @@ -55,4 +82,4 @@ VOLUME /verdaccio/storage ENTRYPOINT ["uid_entrypoint"] -CMD $VERDACCIO_APPDIR/packages/verdaccio/bin/verdaccio --config /verdaccio/conf/config.yaml --listen $VERDACCIO_PROTOCOL://0.0.0.0:$VERDACCIO_PORT +CMD verdaccio --config /verdaccio/conf/config.yaml --listen $VERDACCIO_PROTOCOL://0.0.0.0:$VERDACCIO_PORT diff --git a/abappm/README.md b/abappm/README.md new file mode 100644 index 000000000..28f0831bf --- /dev/null +++ b/abappm/README.md @@ -0,0 +1,3 @@ +# abapPM + +Assets for Verdaccio diff --git a/abappm/apm.ico b/abappm/apm.ico new file mode 100644 index 000000000..1411d409e Binary files /dev/null and b/abappm/apm.ico differ diff --git a/abappm/apm_40x40.png b/abappm/apm_40x40.png new file mode 100644 index 000000000..167d1c8a4 Binary files /dev/null and b/abappm/apm_40x40.png differ diff --git a/abappm/apm_640x512.png b/abappm/apm_640x512.png new file mode 100644 index 000000000..d973dd7ca Binary files /dev/null and b/abappm/apm_640x512.png differ diff --git a/abappm/apm_banner.png b/abappm/apm_banner.png new file mode 100644 index 000000000..269a18d3f Binary files /dev/null and b/abappm/apm_banner.png differ diff --git a/abappm/apm_banner_gray.png b/abappm/apm_banner_gray.png new file mode 100644 index 000000000..7c7908d7c Binary files /dev/null and b/abappm/apm_banner_gray.png differ diff --git a/abappm/healthcheck.sh b/abappm/healthcheck.sh new file mode 100644 index 000000000..5c2518056 --- /dev/null +++ b/abappm/healthcheck.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if pgrep -f "/usr/bin/dumb-init" > /dev/null; then + if pgrep -f "apm" > /dev/null; then + exit 0 + else + exit 1 + fi +else + exit 1 +fi diff --git a/config.yaml b/config.yaml new file mode 100644 index 000000000..d92ec8460 --- /dev/null +++ b/config.yaml @@ -0,0 +1,214 @@ +# +# This is the default configuration file. It allows all users to do anything, +# please read carefully the documentation and best practices to +# improve security. +# +# Do not configure host and port under `listen` in this file +# as it will be ignored when using docker. +# see https://verdaccio.org/docs/en/docker#docker-and-custom-port-configuration +# +# Look here for more config file examples: +# https://github.com/verdaccio/verdaccio/tree/5.x/conf +# +# Read about the best practices +# https://verdaccio.org/docs/best + +# path to a directory with all packages +storage: /verdaccio/storage/data + +# path to a directory with plugins to include +plugins: /verdaccio/plugins + +# https://verdaccio.org/docs/webui +web: + enable: true + title: apm - A Package Manager for ABAP + logo: /verdaccio/abappm/apm_banner.png + logoDark: /verdaccio/abappm/apm_banner_gray.png + favicon: /verdaccio/abappm/apm.ico + primaryColor: '#c0c0c0' + darkMode: true + gravatar: true + # by default packages are ordercer ascendant (asc|desc) + # sort_packages: asc + # convert your UI to the dark side + # darkMode: true + # html_cache: true + # by default all features are displayed + # login: true + pkgManagers: + - apm + showInfo: false + showUplinks: false + showSettings: false + # In combination with darkMode you can force specific theme + # showThemeSwitch: true + # showFooter: true + # showSearch: false + # showRaw: true + # showDownloadTarball: true + # HTML tags injected after manifest + # scriptsBodyAfter: + # - '' + # HTML tags injected before ends + metaScripts: + - '' + - '' + # - '' + # - '' + # HTML tags injected first child at + scriptsbodyBefore: + # - '
***PLAYGROUND*** Packages will be deleted every Sunday night! ***PLAYGROUND***
' + # Public path for template manifest scripts (only manifest) + # publicPath: https://playground.abappm.com/ + +auth: + htpasswd: + file: /verdaccio/storage/htpasswd + # Maximum amount of users allowed to register, defaults to "+inf". + # You can set this to -1 to disable registration. + max_users: 10 + algorithm: bcrypt + +# https://verdaccio.org/docs/configuration#uplinks +# a list of other known repositories we can talk to +uplinks: + +# Learn how to protect your packages +# https://verdaccio.org/docs/protect-your-dependencies/ +# https://verdaccio.org/docs/configuration#packages +packages: + '@*/*': + # scoped packages + access: $all + publish: $authenticated + unpublish: $authenticated + + '**': + # global packages + access: $all + publish: $authenticated + unpublish: $authenticated + +# To improve your security configuration and avoid dependency confusion +# consider removing the proxy property for private packages +# https://verdaccio.org/docs/best#remove-proxy-to-increase-security-at-private-packages + +# https://verdaccio.org/docs/configuration#server +# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections. +# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout. +# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough. +server: + keepAliveTimeout: 60 + +# https://verdaccio.org/docs/configuration#offline-publish +publish: + check_owner: true +# allow_offline: false + +# https://verdaccio.org/docs/configuration#url-prefix +# url_prefix: /verdaccio/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org'; +# url_prefix: '/my_prefix' +# // url -> https://somedomain.org/my_prefix/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org'; +# url_prefix: '/' +# // url -> https://somedomain.org/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org/first_prefix'; +# url_prefix: '/second_prefix' +# // url -> https://somedomain.org/second_prefix/' +url_prefix: '/' + +# https://verdaccio.org/docs/configuration#security +security: + api: + legacy: false + jwt: + sign: + expiresIn: 29d + # verify: + # someProp: [value] + web: + sign: + expiresIn: 1h # 1 hour by default +# verify: +# someProp: [value] + +# https://verdaccio.org/docs/configuration#user-rate-limit +# userRateLimit: +# windowMs: 50000 +# max: 1000 + +# https://verdaccio.org/docs/configuration#max-body-size +max_body_size: 1mb + +# https://verdaccio.org/docs/configuration#listen-port +# listen: +# - localhost:4873 # default value +# - http://localhost:4873 # same thing +# - 0.0.0.0:4873 # listen on all addresses (INADDR_ANY) +# - https://example.org:4873 # if you want to use https +# - "[::1]:4873" # ipv6 +# - unix:/tmp/verdaccio.sock # unix socket + +# The HTTPS configuration is useful if you do not consider use a HTTP Proxy +# https://verdaccio.org/docs/configuration#https +# https: +# key: ./path/verdaccio-key.pem +# cert: ./path/verdaccio-cert.pem +# ca: ./path/verdaccio-csr.pem + +# https://verdaccio.org/docs/configuration#proxy +# http_proxy: http://something.local/ +# https_proxy: https://something.local/ + +# https://verdaccio.org/docs/configuration#notifications +# notify: +# method: POST +# headers: [{ "Content-Type": "application/json" }] +# endpoint: https://usagge.hipchat.com/v2/room/3729485/notification?auth_token=mySecretToken +# content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}' + +# https://verdaccio.org/docs/logger +# log settings +log: { type: stdout, format: pretty, level: http } +# { type: file, path: /verdaccio/storage/verdaccio.log, level: info } + +# experiments: +# # support for npm token command +# token: false +# # enable tarball URL redirect for hosting tarball with a different server, the tarball_url_redirect can be a template string +# tarball_url_redirect: 'https://mycdn.com/verdaccio/${packageName}/${filename}' +# # the tarball_url_redirect can be a function, takes packageName and filename and returns the url, when working with a js configuration file +# tarball_url_redirect(packageName, filename) { +# const signedUrl = // generate a signed url +# return signedUrl; +# } + +# translate your registry, api i18n not available yet +# i18n: +# list of the available translations https://github.com/verdaccio/verdaccio/blob/master/packages/plugins/ui-theme/src/i18n/ABOUT_TRANSLATIONS.md +# web: en-US + +middlewares: + audit: + enabled: true +# install-counts: +# enabled: true +# redis: +# host: red-ckfit036fquc73egihn0 +# port: 6379 + +# Using Redis as storage +# https://github.com/openupm/verdaccio-redis-storage +#store: +# storage-proxy: +# database_backend: redis-storage +# search_backend: redis-storage +# packument_backend: redis-storage +# tarball_backend: '@verdaccio/local-storage' +# backends: +# '@verdaccio/local-storage': {} +# redis-storage: +# host: red-ckfit036fquc73egihn0 +# port: 6379 diff --git a/jest/config.js b/jest/config.js index ba5826292..7d95a25a9 100644 --- a/jest/config.js +++ b/jest/config.js @@ -1,5 +1,7 @@ module.exports = { moduleFileExtensions: ['ts', 'js'], + maxWorkers: 3, + testTimeout: 20000, transform: { '^.+\\.(js|ts)$': 'babel-jest', }, diff --git a/packages/core/types/src/configuration.ts b/packages/core/types/src/configuration.ts index 3f303e521..f9c1de59d 100644 --- a/packages/core/types/src/configuration.ts +++ b/packages/core/types/src/configuration.ts @@ -77,7 +77,7 @@ export type FlagsConfig = { changePassword?: boolean; }; -export type PackageManagers = 'pnpm' | 'yarn' | 'npm'; +export type PackageManagers = 'pnpm' | 'yarn' | 'npm' | 'apm'; // FUTURE: WebConf and TemplateUIOptions should be merged . export type CommonWebConf = { diff --git a/packages/plugins/ui-theme/package.json b/packages/plugins/ui-theme/package.json index 38d3e5bf1..425e9d344 100644 --- a/packages/plugins/ui-theme/package.json +++ b/packages/plugins/ui-theme/package.json @@ -39,6 +39,7 @@ "friendly-errors-webpack-plugin": "1.7.0", "harmony-reflect": "1.6.2", "highlight.js": "11.10.0", + "highlightjs-sap-abap": "0.3.0", "history": "4.10.1", "html-webpack-plugin": "5.6.0", "i18next": "20.6.1", diff --git a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json index 60ffa238b..9a583ee06 100644 --- a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json +++ b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json @@ -132,9 +132,11 @@ "title": "Contributors" }, "engines": { + "apm-version": "apm Version", "npm-version": "Npm Version", "pnpm-version": "Pnpm Version", "yarn-version": "Yarn Version", + "abap": "ABAP Release", "node-js": "Node.js" } }, diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index d15b237ff..6601001c1 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -9,6 +9,7 @@ "types": "build/index.d.ts", "scripts": { "test": "cross-env TZ=UTC jest --config jest/jest.config.js", + "test:snap": "cross-env TZ=UTC jest --config jest/jest.config.js --updateSnapshot", "test:html": "cross-env TZ=UTC jest --config jest/jest.config.js --coverage-reporters=html", "clean": "rimraf ./build", "type-check": "tsc --noEmit -p tsconfig.build.json", @@ -36,6 +37,7 @@ "dayjs": "1.11.10", "dompurify": "3.1.6", "highlight.js": "11.10.0", + "highlightjs-sap-abap": "0.3.0", "history": "4.10.1", "i18next": "20.6.1", "js-base64": "3.7.6", diff --git a/packages/ui-components/src/components/ActionBar/ActionBarAction.tsx b/packages/ui-components/src/components/ActionBar/ActionBarAction.tsx index 4ff7fdbd3..a5ece90cb 100644 --- a/packages/ui-components/src/components/ActionBar/ActionBarAction.tsx +++ b/packages/ui-components/src/components/ActionBar/ActionBarAction.tsx @@ -20,7 +20,7 @@ export const Fab = styled(FabMUI)<{ theme?: Theme }>(({ theme }) => ({ '&:hover': { color: theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue, }, - color: theme?.palette.white, + color: theme?.palette.mode === 'light' ? theme?.palette.black : theme?.palette.white, })); type ActionType = 'VISIT_HOMEPAGE' | 'OPEN_AN_ISSUE' | 'DOWNLOAD_TARBALL' | 'RAW_DATA'; diff --git a/packages/ui-components/src/components/Engines/Engines.tsx b/packages/ui-components/src/components/Engines/Engines.tsx index 2cc2fda98..11f33ee7f 100644 --- a/packages/ui-components/src/components/Engines/Engines.tsx +++ b/packages/ui-components/src/components/Engines/Engines.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { PackageMetaInterface } from '../../types/packageMeta'; -import { NodeJS, Npm, Pnpm, Yarn } from '../Icons'; +import { ABAP as Abap, Apm, NodeJS, Npm, Pnpm, Yarn } from '../Icons'; import { EngineListItem, StyledText } from './styles'; /** @@ -36,7 +36,14 @@ const EngineItem: FC = ({ title, element, engineText }) => ( interface EngineMetadata extends Omit { latest: { - engines?: { npm?: string; node?: string; pnpm?: string; yarn?: string }; + engines?: { + abap?: string; + apm?: string; + npm?: string; + node?: string; + pnpm?: string; + yarn?: string; + }; }; } @@ -61,6 +68,22 @@ const Engine: React.FC = ({ packageMeta }) => { return ( + {engines.abap ? ( + } + engineText={engines.abap} + title={t('sidebar.engines.abap')} + /> + ) : null} + + {engines.apm ? ( + } + engineText={engines.apm} + title={t('sidebar.engines.apm-version')} + /> + ) : null} + {engines.node ? ( } diff --git a/packages/ui-components/src/components/Engines/styles.ts b/packages/ui-components/src/components/Engines/styles.ts index 390edbfcb..e6729e4ed 100644 --- a/packages/ui-components/src/components/Engines/styles.ts +++ b/packages/ui-components/src/components/Engines/styles.ts @@ -6,7 +6,7 @@ import { Theme } from '../../Theme'; export const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({ fontWeight: props.theme?.fontWeight.bold, - textTransform: 'capitalize', + // textTransform: 'capitalize', // apm })); export const EngineListItem = styled(ListItem)({ diff --git a/packages/ui-components/src/components/Icons/DevsIcons/ABAP.tsx b/packages/ui-components/src/components/Icons/DevsIcons/ABAP.tsx new file mode 100644 index 000000000..0c56b995d --- /dev/null +++ b/packages/ui-components/src/components/Icons/DevsIcons/ABAP.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const icon = require('./abap.svg'); + +export function ABAP() { + return ABAP; +} diff --git a/packages/ui-components/src/components/Icons/DevsIcons/ABAPModule.tsx b/packages/ui-components/src/components/Icons/DevsIcons/ABAPModule.tsx new file mode 100644 index 000000000..e3976feba --- /dev/null +++ b/packages/ui-components/src/components/Icons/DevsIcons/ABAPModule.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const icon = require('./abapmodule.svg'); + +export function ABAPModule() { + return ABAP module; +} diff --git a/packages/ui-components/src/components/Icons/DevsIcons/abap.svg b/packages/ui-components/src/components/Icons/DevsIcons/abap.svg new file mode 100644 index 000000000..3699b6ff1 --- /dev/null +++ b/packages/ui-components/src/components/Icons/DevsIcons/abap.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ui-components/src/components/Icons/DevsIcons/abapmodule.svg b/packages/ui-components/src/components/Icons/DevsIcons/abapmodule.svg new file mode 100644 index 000000000..d4d1fdbe1 --- /dev/null +++ b/packages/ui-components/src/components/Icons/DevsIcons/abapmodule.svg @@ -0,0 +1,5 @@ + + + + module + diff --git a/packages/ui-components/src/components/Icons/DevsIcons/index.ts b/packages/ui-components/src/components/Icons/DevsIcons/index.ts index 6861201a8..bf9d5a36b 100644 --- a/packages/ui-components/src/components/Icons/DevsIcons/index.ts +++ b/packages/ui-components/src/components/Icons/DevsIcons/index.ts @@ -3,3 +3,5 @@ export { CommonJS } from './CommonJS'; export { ES6Modules } from './ES6Module'; export { NodeJS } from './NodeJS'; export { Git } from './Git'; +export { ABAP } from './ABAP'; +export { ABAPModule } from './ABAPModule'; diff --git a/packages/ui-components/src/components/Icons/Managers/Apm.tsx b/packages/ui-components/src/components/Icons/Managers/Apm.tsx new file mode 100644 index 000000000..81389a816 --- /dev/null +++ b/packages/ui-components/src/components/Icons/Managers/Apm.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const icon = require('./apm.svg'); + +export function Apm() { + return apm package manager; +} diff --git a/packages/ui-components/src/components/Icons/Managers/apm.svg b/packages/ui-components/src/components/Icons/Managers/apm.svg new file mode 100644 index 000000000..b5f4f8f6e --- /dev/null +++ b/packages/ui-components/src/components/Icons/Managers/apm.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/ui-components/src/components/Icons/Managers/index.ts b/packages/ui-components/src/components/Icons/Managers/index.ts index b3ef70245..5df53fd63 100644 --- a/packages/ui-components/src/components/Icons/Managers/index.ts +++ b/packages/ui-components/src/components/Icons/Managers/index.ts @@ -1,3 +1,4 @@ +export { Apm } from './Apm'; export { Npm } from './Npm'; export { Pnpm } from './Pnpm'; export { Yarn } from './Yarn'; diff --git a/packages/ui-components/src/components/Install/Install.tsx b/packages/ui-components/src/components/Install/Install.tsx index 30f67cf9d..cfbe3394a 100644 --- a/packages/ui-components/src/components/Install/Install.tsx +++ b/packages/ui-components/src/components/Install/Install.tsx @@ -36,8 +36,9 @@ const Install: React.FC = ({ packageMeta, packageName, configOptions }) = const hasNpm = configOptions?.pkgManagers?.includes('npm'); const hasYarn = configOptions?.pkgManagers?.includes('yarn'); - const hasPnpm = configOptions?.pkgManagers?.includes('pnpm') ?? true; - const hasPkgManagers = hasNpm || hasPnpm || hasYarn; + const hasPnpm = configOptions?.pkgManagers?.includes('pnpm'); + const hasApm = configOptions?.pkgManagers?.includes('apm'); // apm + const hasPkgManagers = hasNpm || hasPnpm || hasYarn || hasApm; // apm return hasPkgManagers ? ( <> @@ -50,6 +51,13 @@ const Install: React.FC = ({ packageMeta, packageName, configOptions }) = } > + {hasApm && ( + + )} {hasNpm && ( = ({ const isLatest = localSettings[packageName]?.latest ?? false; const isGlobal = localSettings[packageName]?.global ?? false; switch (dependencyManager) { + case DependencyManager.APM: + return ( + + + + + + } + /> + + ); case DependencyManager.NPM: return ( diff --git a/packages/ui-components/src/components/Package/styles.ts b/packages/ui-components/src/components/Package/styles.ts index 5191d9c64..db85e8cbb 100644 --- a/packages/ui-components/src/components/Package/styles.ts +++ b/packages/ui-components/src/components/Package/styles.ts @@ -72,7 +72,7 @@ export const GridRightAligned = styled(Grid)({ export const Wrapper = styled(List)<{ theme?: Theme }>(({ theme }) => ({ '&:hover': { backgroundColor: - theme?.palette?.type == 'dark' ? theme?.palette?.secondary.main : theme?.palette?.greyLight2, + theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue, }, })); diff --git a/packages/ui-components/src/components/Readme/utils.ts b/packages/ui-components/src/components/Readme/utils.ts index 58179fab9..c44ad9f01 100644 --- a/packages/ui-components/src/components/Readme/utils.ts +++ b/packages/ui-components/src/components/Readme/utils.ts @@ -7,6 +7,10 @@ const marked = new Marked( async: false, highlight(code, lang) { const hljs = require('highlight.js'); + // >>> apm + const hlabap = require('highlightjs-sap-abap'); + hljs.registerLanguage('abap', hlabap); + // <<< apm const language = hljs.getLanguage(lang) ? lang : 'plaintext'; return hljs.highlight(code, { language }).value; }, diff --git a/packages/ui-components/src/components/Search/SearchItem.tsx b/packages/ui-components/src/components/Search/SearchItem.tsx index 5606122c2..a94342821 100644 --- a/packages/ui-components/src/components/Search/SearchItem.tsx +++ b/packages/ui-components/src/components/Search/SearchItem.tsx @@ -9,6 +9,7 @@ import Stack from '@mui/material/Stack'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useConfig } from '../../'; import { cleanDescription } from './utils'; type SearchItemProps = { @@ -74,6 +75,7 @@ const SearchItem: React.FC = ({ ...props }) => { const { t } = useTranslation(); + const { configOptions } = useConfig(); const handleDelete = () => { // no action assigned by default }; @@ -87,37 +89,39 @@ const SearchItem: React.FC = ({ {version && {version}} - - - {isPrivate && ( - } - label={t('search.isPrivate')} - onDelete={handleDelete} - size="small" - /> - )} - {isRemote && !isPrivate && ( - } - label={t('search.isRemote')} - onDelete={handleDelete} - size="small" - variant="outlined" - /> - )} - {isCached && ( - } - label={t('search.isCached')} - onDelete={handleDelete} - size="small" - variant="outlined" - /> - )} - - + {configOptions?.showUplinks && ( + + + {isPrivate && ( + } + label={t('search.isPrivate')} + onDelete={handleDelete} + size="small" + /> + )} + {isRemote && !isPrivate && ( + } + label={t('search.isRemote')} + onDelete={handleDelete} + size="small" + variant="outlined" + /> + )} + {isCached && ( + } + label={t('search.isCached')} + onDelete={handleDelete} + size="small" + variant="outlined" + /> + )} + + + )} ); }; diff --git a/packages/ui-components/src/components/Search/styles.ts b/packages/ui-components/src/components/Search/styles.ts index f5efd35bb..b2993a833 100644 --- a/packages/ui-components/src/components/Search/styles.ts +++ b/packages/ui-components/src/components/Search/styles.ts @@ -14,7 +14,10 @@ export const StyledTextField = styled(TextField)<{ theme?: Theme }>((props) => ( border: 'none', }, ':after': { - borderColor: props.theme?.palette.white, + borderColor: + props.theme?.palette.mode === 'light' + ? props.theme?.palette.black + : props.theme?.palette.white, }, ':hover:before': { content: 'none', @@ -31,11 +34,15 @@ export const StyledTextField = styled(TextField)<{ theme?: Theme }>((props) => ( }, '& .MuiInputBase-input': { [`@media screen and (min-width: ${props.theme?.breakPoints.medium}px)`]: { - color: props.theme?.palette.white, + color: + props.theme?.palette.mode === 'light' + ? props.theme?.palette.black + : props.theme?.palette.white, }, }, })); export const StyledInputAdornment = styled(InputAdornment)<{ theme?: Theme }>((props) => ({ - color: props.theme?.palette.white, + color: + props.theme?.palette.mode === 'light' ? props.theme?.palette.black : props.theme?.palette.white, })); diff --git a/packages/ui-components/src/components/SideBarTitle/SideBarTitle.tsx b/packages/ui-components/src/components/SideBarTitle/SideBarTitle.tsx index d587faf3c..1b0a1d679 100644 --- a/packages/ui-components/src/components/SideBarTitle/SideBarTitle.tsx +++ b/packages/ui-components/src/components/SideBarTitle/SideBarTitle.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { Theme } from '../../Theme'; import Heading from '../Heading'; -import { CommonJS, ES6Modules, TypeScript } from '../Icons'; +import { ABAPModule, CommonJS, TypeScript } from '../Icons'; import { formatDate, formatDateDistance } from './utils'; export type ModuleType = 'commonjs' | 'module'; @@ -34,7 +34,7 @@ const ModuleJS: React.FC<{ module: ModuleType | void }> = ({ module }) => { } else if (module === 'module') { return ( - + ); } else { diff --git a/packages/ui-components/src/sections/Header/styles.ts b/packages/ui-components/src/sections/Header/styles.ts index 1fca2c8e4..21dc70bd9 100644 --- a/packages/ui-components/src/sections/Header/styles.ts +++ b/packages/ui-components/src/sections/Header/styles.ts @@ -47,7 +47,7 @@ export const SearchWrapper = styled('div')({ export const NavBar = styled(AppBar)<{ theme?: Theme }>(({ theme }) => ({ backgroundColor: theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue, - color: theme?.palette.white, + color: theme?.palette.mode === 'light' ? theme?.palette.black : theme?.palette.white, minHeight: 60, display: 'flex', justifyContent: 'center', diff --git a/packages/ui-components/src/test/test-react-testing-library.tsx b/packages/ui-components/src/test/test-react-testing-library.tsx index 5b85552cd..b75cdb9f7 100644 --- a/packages/ui-components/src/test/test-react-testing-library.tsx +++ b/packages/ui-components/src/test/test-react-testing-library.tsx @@ -1,4 +1,5 @@ import { StyledEngineProvider } from '@mui/material/styles'; +import { configure } from '@testing-library/dom'; import { render } from '@testing-library/react'; import React from 'react'; import { I18nextProvider } from 'react-i18next'; @@ -10,6 +11,8 @@ import PersistenceSettingProvider from '../providers/PersistenceSettingProvider' import { Store } from '../store/store'; import i18nConfig from './i18n-config'; +configure({ asyncUtilTimeout: 10000 }); + const renderWithStore = (ui: React.ReactElement, store: Store) => render(ui, { wrapper: ({ children }) => ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e24338c9c..134467bd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1247,6 +1247,9 @@ importers: highlight.js: specifier: 11.10.0 version: 11.10.0 + highlightjs-sap-abap: + specifier: 0.3.0 + version: 0.3.0 history: specifier: 4.10.1 version: 4.10.1 @@ -1918,6 +1921,9 @@ importers: highlight.js: specifier: 11.10.0 version: 11.10.0 + highlightjs-sap-abap: + specifier: 0.3.0 + version: 0.3.0 history: specifier: 4.10.1 version: 4.10.1 @@ -19268,6 +19274,9 @@ packages: resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==} engines: {node: '>=12.0.0'} + /highlightjs-sap-abap@0.3.0: + resolution: {integrity: sha512-nSiUvEOCycjtFA3pHaTowrbAAk5+lciBHyoVkDsd6FTRBtW9sT2dt42o2jAKbXjZVUidtacdk+j0Y2xnd233Mw==, tarball: https://registry.npmjs.org/highlightjs-sap-abap/-/highlightjs-sap-abap-0.3.0.tgz} + /history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} dependencies: