Compare commits

...

29 Commits

Author SHA1 Message Date
Juan Picado @jotadeveloper
5e43ac0d49 chore(release): 0.1.4 2019-05-04 15:42:15 +02:00
Juan Picado @jotadeveloper
4e899a8574 chore: update snapshot 2019-05-04 15:41:57 +02:00
Juan Picado @jotadeveloper
01daf73bbf chore(release): 0.1.3 2019-05-04 15:39:29 +02:00
Juan Picado @jotadeveloper
a89f363115 feat: browser history is aware of basename (#45)
* feat: browser history is aware of basename

* test: fix ui options global object

* chore: fix comment
2019-05-04 12:13:29 +02:00
Daniel Ruf
c3eac3d27b test: fix formatDateDistance test (#43)
Test formatDateDistance with a date which is about 2 months in the past and another which is exactly 2 months in the past.
2019-05-02 20:57:42 +02:00
Juan Picado @jotadeveloper
9d7be476ad chore: relocate unit test (#39)
add new json snapshot serializer
2019-05-01 21:02:46 +02:00
Ayush Sharma
1070e5c3aa chore: adds actions (#40)
* chore: adds actions

* chore: updates workflow

* chore: leave node 12 placeholder

* chore: leave node 12 placeholder
2019-04-29 09:06:00 +02:00
Juan Picado @jotadeveloper
11f50919ef chore(release): 0.1.2 2019-04-28 14:33:39 +02:00
Juan Picado @jotadeveloper
ea1ebde2f1 chore: reverts commit fc11429 2019-04-28 14:32:40 +02:00
Juan Picado @jotadeveloper
c4cb559137 chore(release): 0.1.1 2019-04-28 14:02:17 +02:00
Ayush Sharma
e8fd59696e fix: version rendering issue. (#38)
* fix: object passing for react rendering

* fix: const for 'not available'

* fix: const for 'not available'
2019-04-28 13:15:45 +02:00
Ayush Sharma
fc114298ad chore: adds pull request action (#37)
* chore: improvements in actions

* chore: improvements in actions

* chore: improvements in actions

* chore: aggregates action in a script

* chore: minor fix

* chore: adds node 12 in action

* chore: keep node 12 after 8 10 11

* chore: keep node 12 after 8 10 11

* chore: name fixes
2019-04-27 22:22:52 +02:00
Juan Picado @jotadeveloper
86bda48b79 chore(release): 0.1.0 2019-04-26 06:52:55 +02:00
Jamie Kyle
d6a8f5519f feat: accept primary color to be configured (#36) 2019-04-25 21:42:48 +02:00
Juan Picado @jotadeveloper
eda98b817e chore: update readme 2019-04-25 19:53:01 +02:00
Juan Picado @jotadeveloper
12da966254 chore: update readme 2019-04-25 19:49:19 +02:00
Juan Picado @jotadeveloper
4ee6c2dfe4 chore(release): 0.0.13 2019-04-14 00:28:09 +02:00
Juan Picado @jotadeveloper
13d999369d chore: update registry domain 2019-04-14 00:25:45 +02:00
Liming Jin
cc30d0b249 fix: there can be no spaces between scope and registry (#34) 2019-04-14 00:17:21 +02:00
Juan Picado @jotadeveloper
6f108370ce fix: takes the app version from verdaccio 2019-04-13 23:27:44 +02:00
Ayush Sharma
f9da05744b chore: 4.x fixes filter action flow (#33)
* chore: separates release workflow

* chore: separates release workflow
2019-04-09 09:28:52 +02:00
Juan Picado @jotadeveloper
414aaa3d84 Update README.md 2019-04-09 00:05:45 +02:00
Ayush Sharma
4a459df463 GitHub actions (#32)
* chore: fixes snapshots test

* chore: adds github actions, issue and feat templates

* chore: fixes eslint

* chore: remvoes circle ci and adds publish action

* chore: adds github-release automation using actions
2019-04-08 23:36:39 +02:00
Ayush Sharma
96b65d969a refactor: corrects eslint and variable name spacing (#31)
* refactor: corrects eslint and variable namespacing

* chore: fixes git conflict

* chore: fixes eslint
2019-04-08 22:29:20 +02:00
Juan Picado @jotadeveloper
f000438b86 chore: update lock file 2019-04-07 08:41:08 +02:00
Juan Picado @jotadeveloper
7bde18c8d9 chore: update circleci 2019-04-06 18:14:03 +02:00
Juan Picado @jotadeveloper
3a63fba339 chore: add verdaccio and webpack dev server script 2019-04-06 17:51:26 +02:00
Juan Picado @jotadeveloper
6fcb415091 chore: remove unused code 2019-04-06 17:42:52 +02:00
Juan Picado @jotadeveloper
dc5b14821b chore(release): 0.0.4 2019-04-04 23:16:29 +02:00
74 changed files with 3554 additions and 3112 deletions

View File

@@ -1,173 +0,0 @@
version: 2
aliases:
- &defaults
working_directory: ~/verdaccio
- &node8_executor
docker:
- image: circleci/node:8
- &node10_executor
docker:
- image: circleci/node:10
- &node11_executor
docker:
- image: circleci/node:11.10.1
- &default_executor
<<: *node10_executor
- &repo_key
repo-{{ .Branch }}-{{ .Revision }}
- &coverage_key
coverage-{{ .Branch }}-{{ .Revision }}
- &base_config_key
base-config-{{ .Branch }}-{{ .Revision }}
- &yarn_cache_key
yarn-sha-{{ checksum "yarn.lock" }}
- &restore_repo
attach_workspace:
at: ~/verdaccio
- &ignore_non_dev_branches
filters:
tags:
only: /.*/
branches:
ignore:
- gh-pages
- l10n_master
- /release\/.*/
- &execute_on_release
filters:
tags:
only: /v?[0-9]+(\.[0-9]+)+([-+\.][0-9a-zA-Z]+)*/
branches:
ignore:
- /.*/
jobs:
prepare:
<<: *defaults
<<: *default_executor
steps:
- checkout
- restore_cache:
key: *base_config_key
- run:
name: 'Base environment setup'
command: |
git config --global user.email "verdacciobot@users.noreply.github.com"
git config --global user.name "Verdaccio bot for Deployments"
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
echo "machine github.com login verdacciobot password $GITHUB_TOKEN" > ~/.netrc
- save_cache:
key: *base_config_key
paths:
- ~/.npmrc
- ~/.gitconfig
- ~/.netrc
- restore_cache:
key: *yarn_cache_key
- run:
name: Install Js dependencies
command: yarn install --no-progress --registry https://registry.verdaccio.org
- run:
name: Prepare CI
command: yarn lint
- run:
name: Build project
command: yarn build
- save_cache:
key: *yarn_cache_key
paths:
- ~/.yarn
- ~/.cache/yarn
- node_modules
- persist_to_workspace:
root: ~/verdaccio
paths:
- ./*
test_node8:
<<: *defaults
<<: *node8_executor
steps:
- *restore_repo
- run:
name: Test with Node 8
command: |
yarn test
test_node10:
<<: *defaults
<<: *node10_executor
steps:
- *restore_repo
- run:
name: Test with Node 10
command: |
yarn test
test_node11:
<<: *defaults
<<: *node11_executor
steps:
- *restore_repo
- run:
name: Test with Node 11
command: |
yarn run test
coverage:
<<: *defaults
<<: *default_executor
steps:
- *restore_repo
- restore_cache:
key: *coverage_key
- run:
name: Publish coverage
command: yarn run coverage:publish
- store_artifacts:
path: coverage/clover.xml
prefix: tests
- store_artifacts:
path: coverage
prefix: coverage
- store_test_results:
path: coverage/clover.xml
publish_package:
<<: *defaults
<<: *default_executor
steps:
- *restore_repo
- restore_cache:
key: *base_config_key
- run:
name: Publish
command: scripts/publish.sh
workflows:
version: 2
workflow:
jobs:
- prepare:
<<: *ignore_non_dev_branches
- test_node8:
requires:
- prepare
<<: *ignore_non_dev_branches
- test_node10:
requires:
- prepare
<<: *ignore_non_dev_branches
- test_node11:
requires:
- prepare
<<: *ignore_non_dev_branches
- coverage:
requires:
- test_node8
- test_node10
- test_node11
<<: *ignore_non_dev_branches
- publish_package:
requires:
- coverage
<<: *execute_on_release

37
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@@ -0,0 +1,37 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Docker || Kubernetes (please complete the following information):**
- Docker verdaccio tag: [e.g. verdaccio:beta]
- Docker commands [e.g. docker pull ...]
- Docker Version [e.g. v18.05.0-ce-rc1]
**Configuration File (cat ~/.config/verdaccio/config.yaml)**
**Debugging output**
- `$ NODE_DEBUG=request verdaccio` display request calls (verdaccio <--> uplinks)
- `$ DEBUG=express:* verdaccio` enable extreme verdaccio debug mode (verdaccio api)
- `$ npm -ddd` prints:
- `$ npm config get registry` prints:
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

32
.github/issue_template.md vendored Normal file
View File

@@ -0,0 +1,32 @@
<!--
First of all, please read this section
https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#reporting-a-bug
Some advices before file an issue
* Give a brief explanation of the issue, suggestion or feature to request.
* If the issue is a question, provide as much information you have available.
* How can I do in order to reproduce it? What environment?
* Define which version the issue happens and whether previous version the behavior is correct.
* Provide your config file is really helpful. Please be aware to hide sensitive data (passwords, server IP, etc) before post.
-->
#### My reason:
#### Steps to reproduce:
#### App Version:
#### Config file:
#### Additional information:
- `$ NODE_DEBUG=request verdaccio` display request calls (verdaccio <--> uplinks)
- `$ DEBUG=express:* verdaccio` enable extreme verdaccio debug mode (verdaccio api)
- `$ npm -ddd` prints:
- `$ npm config get registry` prints:
- Verdaccio terminal output
- Which (Windows, OS X/macOS, or Linux) environment are you running verdaccio?:
- Verdaccio configuration file, eg: `cat ~/.config/verdaccio/config.yaml`
- Container Options:
- Docker?:
- Kubernetes?:
#### Additional verbose log:

230
.github/main.workflow vendored Normal file
View File

@@ -0,0 +1,230 @@
################################################
# Workflow for a branch push
################################################
workflow "build and test on branch" {
resolves = [
"branch.lint.node.10",
"branch.test.node.10",
"branch.test.node.8",
# "branch.test.node.12"
]
on = "push"
}
# node 10
action "branch.filter" {
uses = "actions/bin/filter@master"
args = "branch"
}
action "branch.install.node.10" {
needs = ["branch.filter"]
uses = "docker://node:10"
args = "yarn install"
}
action "branch.build.node.10" {
uses = "docker://node:10"
needs = ["branch.install.node.10"]
args = "yarn run build"
}
action "branch.lint.node.10" {
uses = "docker://node:10"
needs = ["branch.install.node.10"]
args = "yarn run lint"
}
action "branch.test.node.10" {
uses = "docker://node:10"
needs = ["branch.build.node.10"]
args = "yarn run test"
}
# node 8
action "branch.install.node.8" {
needs = ["branch.filter"]
uses = "docker://node:8"
args = "yarn install"
}
action "branch.build.node.8" {
uses = "docker://node:8"
needs = ["branch.install.node.8"]
args = "yarn run build"
}
action "branch.test.node.8" {
uses = "docker://node:8"
needs = ["branch.build.node.8"]
args = "yarn run test"
}
# @todo node 12
# action "branch.install.node.12" {
# needs = ["branch.filter"]
# uses = "docker://node:12"
# args = "yarn install"
# }
# action "branch.build.node.12" {
# uses = "docker://node:12"
# needs = ["branch.install.node.12"]
# args = "yarn run build"
# }
# action "branch.test.node.12" {
# uses = "docker://node:12"
# needs = ["branch.build.node.12"]
# args = "yarn run test"
# }
################################################
# Workflow for a Pull request
################################################
workflow "build and test on PR" {
resolves = [
"pr.lint.node.10",
"pr.test.node.10",
"pr.test.node.8",
# "pr.test.node.12"
]
on = "pull_request"
}
# node 10
action "pr.filter" {
uses = "actions/bin/filter@master"
args = "action 'opened|synchronize|reopened'"
}
action "pr.install.node.10" {
needs = ["pr.filter"]
uses = "docker://node:10"
args = "yarn install"
}
action "pr.build.node.10" {
uses = "docker://node:10"
needs = ["pr.install.node.10"]
args = "yarn run build"
}
action "pr.lint.node.10" {
uses = "docker://node:10"
needs = ["pr.install.node.10"]
args = "yarn run lint"
}
action "pr.test.node.10" {
uses = "docker://node:10"
needs = ["pr.build.node.10"]
args = "yarn run test"
}
# node 8
action "pr.install.node.8" {
needs = ["pr.filter"]
uses = "docker://node:8"
args = "yarn install"
}
action "pr.build.node.8" {
uses = "docker://node:8"
needs = ["pr.install.node.8"]
args = "yarn run build"
}
action "pr.test.node.8" {
uses = "docker://node:8"
needs = ["pr.build.node.8"]
args = "yarn run test"
}
# @todo node 12
# action "pr.install.node.12" {
# needs = ["pr.filter"]
# uses = "docker://node:12"
# args = "yarn install"
# }
# action "pr.build.node.12" {
# uses = "docker://node:12"
# needs = ["pr.install.node.12"]
# args = "yarn run build"
# }
# action "pr.test.node.12" {
# uses = "docker://node:12"
# needs = ["pr.build.node.12"]
# args = "yarn run test"
# }
################################################
# Workflow for a github release when a tag is
# pushed
################################################
workflow "github release" {
resolves = [
"release.github",
"release.lint",
]
on = "push"
}
action "release.filter" {
uses = "actions/bin/filter@master"
args = "tag v*"
}
action "release.install" {
uses = "docker://node:10"
needs = ["release.filter"]
args = "yarn install"
}
action "release.build" {
uses = "docker://node:10"
needs = ["release.install"]
args = "yarn run build"
}
action "release.lint" {
uses = "docker://node:10"
needs = ["release.install"]
args = "yarn run lint"
}
action "release.test" {
uses = "docker://node:10"
needs = ["release.build"]
args = "yarn run test"
}
action "release.auth" {
needs = ["release.test"]
uses = "actions/bin/filter@master"
args = ["actor", "octocat", "torvalds"]
}
action "release.npm.publish" {
needs = ["release.auth"]
uses = "docker://node:10"
args = "sh scripts/publish.sh"
secrets = [
"REGISTRY_AUTH_TOKEN",
]
env = {
REGISTRY_URL = "registry.npmjs.org"
}
}
action "release.github" {
needs = ["release.npm.publish"]
uses = "docker://node:10"
args = "sh scripts/github-release.sh"
secrets = [
"GITHUB_TOKEN",
]
}

27
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,27 @@
<!--
Before Pull Request check whether your commits follow this convention
https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#git-commit-guidelines
* If your PR fix an issue don't forget to update the unit test and documentation in /docs folder
* If your PR delivers a new feature, please, provide examples and why such feature should be considered.
* Document your changes /docs
* Add unit test
* Follow the commit guidelines in order to get a quick approval
Pick one/multiple type, if none apply please suggest one, we might be included it by default
eg: bug / feature / documentation / unit test / build
-->
**Type:**
The following has been addressed in the PR:
* There is a related issue?
* Unit or Functional tests are included in the PR
**Description:**
<!-- Resolves #??? -->

View File

@@ -2,5 +2,56 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="0.1.4"></a>
## [0.1.4](https://github.com/verdaccio/ui/compare/v0.1.3...v0.1.4) (2019-05-04)
<a name="0.1.3"></a>
## [0.1.3](https://github.com/verdaccio/ui/compare/v0.1.2...v0.1.3) (2019-05-04)
<a name="0.1.2"></a>
## [0.1.2](https://github.com/verdaccio/ui/compare/v0.1.1...v0.1.2) (2019-04-28)
<a name="0.1.1"></a>
## [0.1.1](https://github.com/verdaccio/ui/compare/v0.1.0...v0.1.1) (2019-04-28)
### Bug Fixes
* version rendering issue. ([#38](https://github.com/verdaccio/ui/issues/38)) ([e8fd596](https://github.com/verdaccio/ui/commit/e8fd596))
<a name="0.1.0"></a>
# [0.1.0](https://github.com/verdaccio/ui/compare/v0.0.13...v0.1.0) (2019-04-26)
### Features
* accept primary color to be configured ([#36](https://github.com/verdaccio/ui/issues/36)) ([d6a8f55](https://github.com/verdaccio/ui/commit/d6a8f55))
<a name="0.0.13"></a>
## [0.0.13](https://github.com/verdaccio/ui/compare/v0.0.4...v0.0.13) (2019-04-13)
### Bug Fixes
* takes the app version from verdaccio ([6f10837](https://github.com/verdaccio/ui/commit/6f10837))
* there can be no spaces between scope and registry ([#34](https://github.com/verdaccio/ui/issues/34)) ([cc30d0b](https://github.com/verdaccio/ui/commit/cc30d0b))
<a name="0.0.4"></a>
## [0.0.4](https://github.com/verdaccio/ui/compare/v0.0.3...v0.0.4) (2019-04-04)
<a name="0.0.3"></a>
## 0.0.3 (2019-04-04)

View File

@@ -3,26 +3,15 @@
# Version 4 UI Theme
[Verdaccio](https://verdaccio.org/) is a simple, **zero-config-required local private npm registry**.
No need for an entire database just to get started! Verdaccio comes out of the box with
**its own tiny database**, and the ability to proxy other registries (eg. npmjs.org),
caching the downloaded modules along the way.
For those looking to extend their storage capabilities, Verdaccio
**supports various community-made plugins to hook into services such as Amazon's s3,
Google Cloud Storage** or create your own plugin.
[Verdaccio](https://verdaccio.org/) UI is a [theme plugin](https://verdaccio.org/docs/en/dev-plugins#theme-plugin) build in React, Flow and Emotion. It uses Jest and Enzyme for Unit testing.
[![verdaccio (latest)](https://img.shields.io/npm/v/verdaccio/latest.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (next)](https://img.shields.io/npm/v/verdaccio/next.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (next)](http://img.shields.io/npm/dy/verdaccio.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (latest)](https://img.shields.io/npm/v/@verdaccio/ui-theme/latest.svg)](https://www.npmjs.com/package/verdaccio)
[![docker pulls](https://img.shields.io/docker/pulls/verdaccio/verdaccio.svg?maxAge=43200)](https://verdaccio.org/docs/en/docker.html)
[![backers](https://opencollective.com/verdaccio/tiers/backer/badge.svg?label=Backer&color=brightgreen)](https://opencollective.com/verdaccio)
[![stackshare](https://img.shields.io/badge/Follow%20on-StackShare-blue.svg?logo=stackshare&style=flat)](https://stackshare.io/verdaccio)
![circle ci status](https://circleci.com/gh/verdaccio/verdaccio.svg?style=shield&circle-token=:circle-token)
[![codecov](https://img.shields.io/codecov/c/github/verdaccio/verdaccio/master.svg)](https://codecov.io/gh/verdaccio/verdaccio)
[![discord](https://img.shields.io/discord/388674437219745793.svg)](http://chat.verdaccio.org/)
[![node](https://img.shields.io/node/v/verdaccio/latest.svg)](https://www.npmjs.com/package/verdaccio)
[![node](https://img.shields.io/node/v/@verdaccio/ui-theme/latest.svg)](https://www.npmjs.com/package/verdaccio)
![MIT](https://img.shields.io/github/license/mashape/apistatus.svg)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/verdaccio/localized.svg)](https://crowdin.com/project/verdaccio)
@@ -30,11 +19,27 @@ Google Cloud Storage** or create your own plugin.
[![Twitter followers](https://img.shields.io/twitter/follow/verdaccio_npm.svg?style=social&label=Follow)](https://twitter.com/verdaccio_npm)
[![Github](https://img.shields.io/github/stars/verdaccio/verdaccio.svg?style=social&label=Stars)](https://github.com/verdaccio/verdaccio/stargazers)
đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§
## Contributing
**This is a maintenance branch, only bug fixing, minor changes or security updates**.
We use `>=yarn@1.13.0`, keep on mind we use lock file.
For development run the following command, it will execute `webpack` and `verdaccio` to
```
yarn dev
```
The configuration file is located on `tools/_config.yaml`.
Run linting tooling and test to check your code is clean before commit.
```
yarn lint && yarn test
```
Remember we follow the [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0-beta.4/).
🤓 Feel free to participate in code reviews, let us know if you want to participate in this plugin.
đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§đźš§
## Open Collective Sponsors

View File

@@ -6,7 +6,7 @@ module.exports = {
collectCoverage: true,
testEnvironment: 'jest-environment-jsdom-global',
testURL: 'http://localhost',
testRegex: '(test/unit/webui/.*\\.spec)\\.js',
testRegex: '(test/unit/.*\\.spec)\\.js',
setupFiles: [
'./test/unit/setup.js'
],
@@ -35,6 +35,7 @@ module.exports = {
'<rootDir>/build',
],
snapshotSerializers: [
'enzyme-to-json/serializer',
"jest-emotion"
],
coveragePathIgnorePatterns: [

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/ui-theme",
"version": "0.0.3",
"version": "0.1.4",
"description": "Verdaccio User Interface",
"author": {
"name": "Verdaccio Core Team"
@@ -15,17 +15,20 @@
"@commitlint/config-conventional": "7.5.0",
"@material-ui/core": "3.9.0",
"@material-ui/icons": "3.0.2",
"@octokit/rest": "16.23.2",
"@verdaccio/babel-preset": "0.1.0",
"@verdaccio/types": "5.0.0-beta.4",
"autosuggest-highlight": "3.1.1",
"bundlesize": "0.17.1",
"codecov": "3.2.0",
"concurrently": "4.1.0",
"cross-env": "5.2.0",
"css-loader": "0.28.10",
"date-fns": "1.30.1",
"emotion": "9.2.12",
"enzyme": "3.9.0",
"enzyme-adapter-react-16": "1.10.0",
"enzyme-to-json": "3.3.5",
"eslint": "5.14.1",
"eslint-config-google": "0.12.0",
"eslint-config-prettier": "4.1.0",
@@ -42,6 +45,7 @@
"flow-bin": "0.81.0",
"flow-runtime": "0.17.0",
"friendly-errors-webpack-plugin": "1.7.0",
"get-stdin": "6.0.0",
"github-markdown-css": "2.10.0",
"html-webpack-plugin": "3.2.0",
"husky": "0.15.0-rc.8",
@@ -52,6 +56,7 @@
"jest-environment-jsdom": "24.0.0",
"jest-environment-jsdom-global": "1.1.1",
"jest-environment-node": "24.0.0",
"js-yaml": "3.13.1",
"localstorage-memory": "1.0.3",
"mini-css-extract-plugin": "0.5.0",
"node-mocks-http": "1.7.3",
@@ -85,6 +90,7 @@
"supertest": "3.4.2",
"typeface-roboto": "0.0.54",
"url-loader": "1.1.2",
"verdaccio": "4.0.0-alpha.7",
"verdaccio-auth-memory": "0.0.4",
"verdaccio-memory": "2.0.0",
"webpack": "4.20.2",
@@ -104,19 +110,21 @@
"flow": "flow check",
"release": "standard-version -a -s",
"test:clean": "npx jest --clearCache",
"test": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC FORCE_COLOR=1 jest --config ./jest.config.js --maxWorkers 2 --passWithNoTests",
"test": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest.config.js --maxWorkers 2 --passWithNoTests",
"test:size": "bundlesize",
"lint": "npm run flow && npm run lint:js && npm run lint:css",
"lint:js": "eslint .",
"coverage:publish": "codecov",
"lint:css": "stylelint 'src/webui/**/styles.js'",
"pre:webpack": "rimraf static/*",
"dev": "cross-env BABEL_ENV=ui babel-node tools/dev.server.js",
"build": "npm run pre:webpack && cross-env BABEL_ENV=ui webpack --config tools/webpack.prod.config.babel.js"
"dev:web": "cross-env BABEL_ENV=ui babel-node tools/dev.server.js",
"verdaccio:server": "node tools/verdaccio.js",
"build": "npm run pre:webpack && cross-env BABEL_ENV=ui webpack --config tools/webpack.prod.config.babel.js",
"dev": "concurrently --kill-others \"npm run dev:web\" \"npm run verdaccio:server\""
},
"engines": {
"node": ">=6.12.0",
"npm": ">=3"
"node": ">=8",
"npm": ">=5"
},
"husky": {
"hooks": {

View File

@@ -0,0 +1,8 @@
# Get the last tag from GitHub
lastTag=$(git describe --tags $(git rev-list --tags --max-count=1))
changelog=$(git show $GITHUB_SHA --unified=0 CHANGELOG.md | tail +12 | sed -e 's/^\+//')
echo "$changelog"
echo "$changelog" | node scripts/trigger-release.js $lastTag

View File

@@ -6,5 +6,8 @@ lastTag=$(git describe --tags $(git rev-list --tags --max-count=1))
# Print it to the console for verification
echo "Bumping version to new tag: ${lastTag}"
# creating .npmrc
echo "//$REGISTRY_URL/:_authToken=$REGISTRY_AUTH_TOKEN" > .npmrc
# Publish to NPM
npm publish --registry https://registry.npmjs.org/
npm publish --registry https://$REGISTRY_URL/

View File

@@ -0,0 +1,26 @@
"use strict";
const [ /* node */, /* file */, tag] = process.argv;
const getStdin = require("get-stdin");
const Octokit = require('@octokit/rest');
const octokit = new Octokit({
auth: `token ${process.env.GITHUB_TOKEN}`,
});
const [repoOwner, repoName] = process.env.GITHUB_REPOSITORY.split("/");
getStdin()
.then(changelog => octokit.repos.createRelease({
owner: repoOwner,
repo: repoName,
tag_name: tag,
body: changelog,
draft: true,
}))
.catch(err => {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});

View File

@@ -4,155 +4,12 @@
// @flow
export const DEFAULT_PORT: string = '4873';
export const DEFAULT_PROTOCOL: string = 'http';
export const DEFAULT_DOMAIN: string = 'localhost';
export const TIME_EXPIRATION_24H: string = '24h';
export const TIME_EXPIRATION_7D: string = '7d';
export const DIST_TAGS = 'dist-tags';
export const USERS = 'users';
export const DEFAULT_MIN_LIMIT_PASSWORD: number = 3;
export const DEFAULT_USER = 'Anonymous';
export const keyPem = 'verdaccio-key.pem';
export const certPem = 'verdaccio-cert.pem';
export const csrPem = 'verdaccio-csr.pem';
export const HEADERS = {
JSON: 'application/json',
CONTENT_TYPE: 'Content-type',
TEXT_PLAIN: 'text/plain',
FORWARDED_PROTO: 'X-Forwarded-Proto',
ETAG: 'ETag',
JSON_CHARSET: 'application/json; charset=utf-8',
OCTET_STREAM: 'application/octet-stream; charset=utf-8',
TEXT_CHARSET: 'text/plain; charset=utf-8',
WWW_AUTH: 'WWW-Authenticate',
GZIP: 'gzip',
};
export const CHARACTER_ENCODING = {
UTF8: 'utf8',
};
export const HEADER_TYPE = {
CONTENT_ENCODING: 'content-encoding',
CONTENT_TYPE: 'content-type',
CONTENT_LENGTH: 'content-length',
ACCEPT_ENCODING: 'accept-encoding',
};
export const ERROR_CODE = {
token_required: 'token is required',
};
export const TOKEN_BASIC = 'Basic';
export const TOKEN_BEARER = 'Bearer';
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
export const DEFAULT_UPLINK = 'npmjs';
export const ROLES = {
$ALL: '$all',
ALL: 'all',
$AUTH: '$authenticated',
$ANONYMOUS: '$anonymous',
DEPRECATED_ALL: '@all',
DEPRECATED_AUTH: '@authenticated',
DEPRECATED_ANONYMOUS: '@anonymous',
};
export const HTTP_STATUS = {
OK: 200,
CREATED: 201,
MULTIPLE_CHOICES: 300,
NOT_MODIFIED: 304,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
UNSUPPORTED_MEDIA: 415,
BAD_DATA: 422,
INTERNAL_ERROR: 500,
SERVICE_UNAVAILABLE: 503,
LOOP_DETECTED: 508,
};
export const API_MESSAGE = {
PKG_CREATED: 'created new package',
PKG_CHANGED: 'package changed',
PKG_REMOVED: 'package removed',
PKG_PUBLISHED: 'package published',
TARBALL_UPLOADED: 'tarball uploaded successfully',
TARBALL_REMOVED: 'tarball removed',
TAG_UPDATED: 'tags updated',
TAG_REMOVED: 'tag removed',
TAG_ADDED: 'package tagged',
LOGGED_OUT: 'Logged out',
};
export const SUPPORT_ERRORS = {
PLUGIN_MISSING_INTERFACE: 'the plugin does not provide implementation of the requested feature',
TFA_DISABLED: 'the two-factor authentication is not yet supported',
};
export const API_ERROR = {
PASSWORD_SHORT: (passLength: number = DEFAULT_MIN_LIMIT_PASSWORD) =>
`The provided password is too short. Please pick a password longer than ${passLength} characters.`,
MUST_BE_LOGGED: 'You must be logged in to publish packages.',
PLUGIN_ERROR: 'bug in the auth plugin system',
CONFIG_BAD_FORMAT: 'config file must be an object',
BAD_USERNAME_PASSWORD: 'bad username/password, access denied',
NO_PACKAGE: 'no such package available',
PACKAGE_CANNOT_BE_ADDED: 'this package cannot be added',
BAD_DATA: 'bad data',
NOT_ALLOWED: 'not allowed to access package',
NOT_ALLOWED_PUBLISH: 'not allowed to publish package',
INTERNAL_SERVER_ERROR: 'internal server error',
UNKNOWN_ERROR: 'unknown error',
NOT_PACKAGE_UPLINK: 'package does not exist on uplink',
UPLINK_OFFLINE_PUBLISH: 'one of the uplinks is down, refuse to publish',
UPLINK_OFFLINE: 'uplink is offline',
CONTENT_MISMATCH: 'content length mismatch',
NOT_FILE_UPLINK: "file doesn't exist on uplink",
MAX_USERS_REACHED: 'maximum amount of users reached',
VERSION_NOT_EXIST: "this version doesn't exist",
FILE_NOT_FOUND: 'File not found',
BAD_STATUS_CODE: 'bad status code',
PACKAGE_EXIST: 'this package is already present',
BAD_AUTH_HEADER: 'bad authorization header',
WEB_DISABLED: 'Web interface is disabled in the config file',
DEPRECATED_BASIC_HEADER: 'basic authentication is deprecated, please use JWT instead',
BAD_FORMAT_USER_GROUP: 'user groups is different than an array',
RESOURCE_UNAVAILABLE: 'resource unavailable',
BAD_PACKAGE_DATA: 'bad incoming package data',
USERNAME_PASSWORD_REQUIRED: 'username and password is required',
USERNAME_ALREADY_REGISTERED: 'username is already registered',
};
export const APP_ERROR = {
CONFIG_NOT_VALID: 'CONFIG: it does not look like a valid config file',
PROFILE_ERROR: 'profile unexpected error',
PASSWORD_VALIDATION: 'not valid password',
};
export const DEFAULT_NO_README = 'ERROR: No README data found!';
export const MODULE_NOT_FOUND = 'MODULE_NOT_FOUND';
export const WEB_TITLE = 'Verdaccio';
export const PACKAGE_ACCESS = {
SCOPE: '@*/*',
ALL: '**',
};
export const UPDATE_BANNER = {
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/',
};
export const STORAGE = {
PACKAGE_FILE_NAME: 'package.json',
FILE_EXIST_ERROR: 'EEXISTS',
NO_SUCH_FILE_ERROR: 'ENOENT',
DEFAULT_REVISION: '0-0000000000000000',
};

View File

@@ -1,73 +0,0 @@
/**
* @prettier
* @flow
*/
import { createDecipher, createCipher, createHash, pseudoRandomBytes } from 'crypto';
import jwt from 'jsonwebtoken';
import type { JWTSignOptions, RemoteUser } from '@verdaccio/types';
export const defaultAlgorithm = 'aes192';
export const defaultTarballHashAlgorithm = 'sha1';
export function aesEncrypt(buf: Buffer, secret: string): Buffer {
// deprecated
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
const c = createCipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
export function aesDecrypt(buf: Buffer, secret: string) {
try {
// deprecated
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
const c = createDecipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return new Buffer(0);
}
}
export function createTarballHash() {
return createHash(defaultTarballHashAlgorithm);
}
/**
* Express doesn't do ETAGS with requests <= 1024b
* we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
* could improve performance using crc32 after benchmarks.
* @param {Object} data
* @return {String}
*/
export function stringToMD5(data: Buffer | string) {
return createHash('md5')
.update(data)
.digest('hex');
}
export function generateRandomHexString(length: number = 8) {
return pseudoRandomBytes(length).toString('hex');
}
export async function signPayload(payload: RemoteUser, secretOrPrivateKey: string, options: JWTSignOptions): Promise<string> {
return new Promise(function(resolve, reject) {
return jwt.sign(
payload,
secretOrPrivateKey,
{
notBefore: '1', // Make sure the time will not rollback :)
...options,
},
(error, token) => (error ? reject(error) : resolve(token))
);
});
}
export function verifyPayload(token: string, secretOrPrivateKey: string) {
return jwt.verify(token, secretOrPrivateKey);
}

View File

@@ -1,5 +0,0 @@
// @flow
export function spliceURL(...args: Array<string>): string {
return Array.from(args).reduce((lastResult, current) => lastResult + current).replace(/([^:])(\/)+(.)/g, `$1/$3`);
}

View File

@@ -1,47 +0,0 @@
// @flow
import {stringToMD5} from '../lib/crypto-utils';
import _ from 'lodash';
// this is a generic avatar
// https://www.iconfinder.com/icons/403017/anonym_avatar_default_head_person_unknown_user_icon
// license: free commercial usage
export const GENERIC_AVATAR: string = `
data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1
ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYW
NrZ3JvdW5kPSJuZXcgLTI3IDI0IDEwMCAxMDAiIGhlaWdodD0iMTAwcHgiIGlkPSJ1bmtub3duIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3
g9Ii0yNyAyNCAxMDAgMTAwIiB3aWR0aD0iMTAwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy
8yMDAwL3N2ZyIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiIHhtbG5zOnhsaW5rPS
JodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48Zz48ZGVmcz48Y2lyY2xlIGN4PSIyMyIgY3k9Ijc0IiBpZD0iY2lyY2xlIi
ByPSI1MCIvPjwvZGVmcz48dXNlIGZpbGw9IiNGNUVFRTUiIG92ZXJmbG93PSJ2aXNpYmxlIiB4bGluazpocmVmPSIjY2lyY2xlIi8+PGN
saXBQYXRoIGlkPSJjaXJjbGVfMV8iPjx1c2Ugb3ZlcmZsb3c9InZpc2libGUiIHhsaW5rOmhyZWY9IiNjaXJjbGUiLz48L2NsaXBQYXR
oPjxnIGNsaXAtcGF0aD0idXJsKCNjaXJjbGVfMV8pIj48ZGVmcz48cGF0aCBkPSJNMzYsOTUuOWMwLDQsNC43LDUuMiw3LjEsNS44Yzc
uNiwyLDIyLjgsNS45LDIyLjgsNS45YzMuMiwxLjEsNS43LDMuNSw3LjEsNi42djkuOEgtMjd2LTkuOCAgICAgICBjMS4zLTMuMSwzLjk
tNS41LDcuMS02LjZjMCwwLDE1LjItMy45LDIyLjgtNS45YzIuNC0wLjYsNy4xLTEuOCw3LjEtNS44YzAtNCwwLTEwLjksMC0xMC45aDI
2QzM2LDg1LDM2LDkxLjksMzYsOTUuOXoiIGlkPSJzaG91bGRlcnMiLz48L2RlZnM+PHVzZSBmaWxsPSIjRTZDMTlDIiBvdmVyZmxvdz0
idmlzaWJsZSIgeGxpbms6aHJlZj0iI3Nob3VsZGVycyIvPjxjbGlwUGF0aCBpZD0ic2hvdWxkZXJzXzFfIj48dXNlIG92ZXJmbG93PSJ
2aXNpYmxlIiB4bGluazpocmVmPSIjc2hvdWxkZXJzIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjc2hvdWxkZXJzXzF
fKSIgZD0iTTIzLjIsMzVjMC4xLDAsMC4xLDAsMC4yLDBjMCwwLDAsMCwwLDAgICAgICBjMy4zLDAsOC4yLDAuMiwxMS40LDJjMy4zLD
EuOSw3LjMsNS42LDguNSwxMi4xYzIuNCwxMy43LTIuMSwzNS40LTYuMyw0Mi40Yy00LDYuNy05LjgsOS4yLTEzLjUsOS40YzAsMC0wL
jEsMC0wLjEsMCAgICAgIGMtMC4xLDAtMC4xLDAtMC4yLDBjLTAuMSwwLTAuMSwwLTAuMiwwYzAsMC0wLjEsMC0wLjEsMGMtMy43LTAuM
i05LjUtMi43LTEzLjUtOS40Yy00LjItNy04LjctMjguNy02LjMtNDIuNCAgICAgIGMxLjItNi41LDUuMi0xMC4yLDguNS0xMi4xYzMuM
i0xLjgsOC4xLTIsMTEuNC0yYzAsMCwwLDAsMCwwQzIzLjEsMzUsMjMuMSwzNSwyMy4yLDM1TDIzLjIsMzV6IiBmaWxsPSIjRDRCMDhDI
iBpZD0iaGVhZC1zaGFkb3ciLz48L2c+PC9nPjxwYXRoIGQ9Ik0yMi42LDQwYzE5LjEsMCwyMC43LDEzLjgsMjAuOCwxNS4xYzEuMSwxM
S45LTMsMjguMS02LjgsMzMuN2MtNCw1LjktOS44LDguMS0xMy41LDguMyAgICBjLTAuMiwwLTAuMiwwLTAuMywwYy0wLjEsMC0wLjEs
MC0wLjIsMEMxOC44LDk2LjgsMTMsOTQuNiw5LDg4LjdjLTMuOC01LjYtNy45LTIxLjgtNi44LTMzLjhDMi4zLDUzLjcsMy41LDQwLDIyL
jYsNDB6IiBmaWxsPSIjRjJDRUE1IiBpZD0iaGVhZCIvPjwvZz48L3N2Zz4=`;
/**
* Generate gravatar url from email address
*/
export function generateGravatarUrl(email: string = '', online: boolean = true): string {
if (online) {
if (_.isString(email) && _.size(email) > 0) {
email = email.trim().toLocaleLowerCase();
const emailMD5 = stringToMD5(email);
return `https://www.gravatar.com/avatar/${emailMD5}`;
}
return GENERIC_AVATAR;
} else {
return GENERIC_AVATAR;
}
}

View File

@@ -1,18 +1,16 @@
/* eslint-disable react/jsx-max-depth */
/**
* @prettier
*/
import React, { Component } from 'react';
import { DetailContextConsumer } from '../../pages/version/index';
import List from '@material-ui/core/List/index';
import DownloadIcon from '@material-ui/icons/CloudDownload';
import BugReportIcon from '@material-ui/icons/BugReport';
import DownloadIcon from '@material-ui/icons/CloudDownload';
import HomeIcon from '@material-ui/icons/Home';
import List from '@material-ui/core/List/index';
import Tooltip from '@material-ui/core/Tooltip/index';
import { DetailContextConsumer } from '../../pages/version/index';
import { Fab, ActionListItem } from './styles';
const ACTIONS = {
@@ -46,24 +44,14 @@ class ActionBar extends Component<any, any> {
return null;
}
return (
<a href={link} target={"_blank"}>
<a href={link} target={'_blank'}>
{component}
</a>
);
}
renderActionBarListItems = (packageMeta) => {
const {
latest: {
bugs: {
url: issue,
} = {},
homepage,
dist: {
tarball,
} = {},
} = {},
} = packageMeta;
renderActionBarListItems = packageMeta => {
const { latest: { bugs: { url: issue } = {}, homepage, dist: { tarball } = {} } = {} } = packageMeta;
const actionsMap = {
homepage,
@@ -74,11 +62,7 @@ class ActionBar extends Component<any, any> {
const renderList = Object.keys(actionsMap).reduce((component, value, key) => {
const link = actionsMap[value];
if (link) {
const fab = (
<Fab size={'small'}>
{ACTIONS[value]['icon']}
</Fab>
);
const fab = <Fab size={'small'}>{ACTIONS[value]['icon']}</Fab>;
component.push(
<Tooltip key={key} title={ACTIONS[value]['title']}>
{this.renderIconsWithLink(link, fab)}
@@ -90,19 +74,13 @@ class ActionBar extends Component<any, any> {
return (
<>
<ActionListItem alignItems={'flex-start'}>
{renderList}
</ActionListItem>
<ActionListItem alignItems={'flex-start'}>{renderList}</ActionListItem>
</>
);
};
renderActionBar = ({ packageMeta = {} }) => {
return (
<List>
{this.renderActionBarListItems(packageMeta)}
</List>
);
return <List>{this.renderActionBarListItems(packageMeta)}</List>;
};
}

View File

@@ -4,15 +4,15 @@
*/
import React from 'react';
import type { Node } from 'react';
import FileCopy from '@material-ui/icons/FileCopy';
import Tooltip from '@material-ui/core/Tooltip/index';
import type { Node } from 'react';
import { IProps } from './types';
import { ClipBoardCopy, ClipBoardCopyText, CopyIcon } from './styles';
import { copyToClipBoardUtility } from '../../utils/cli-utils';
import { TEXT } from '../../utils/constants';
import { IProps } from './types';
const CopyToClipBoard = ({ text, children }: IProps): Node => {
const renderToolTipFileCopy = () => (

View File

@@ -3,8 +3,6 @@
* @flow
*/
/* eslint react/jsx-max-depth: 0 */
import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import CardContent from '@material-ui/core/CardContent/index';
@@ -40,7 +38,7 @@ class DepDetail extends Component<any, any> {
};
}
const WrappDepDetail = withRouter(DepDetail);
const WrapperDependencyDetail = withRouter(DepDetail);
class DependencyBlock extends Component<any, any> {
render() {
@@ -68,7 +66,7 @@ class DependencyBlock extends Component<any, any> {
deps.map(dep => {
const [name, version] = dep;
return <WrappDepDetail key={name} name={name} onLoading={enableLoading} version={version} />;
return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />;
});
}

View File

@@ -4,7 +4,7 @@ import Card from '@material-ui/core/Card/index';
import CardContent from '@material-ui/core/CardContent/index';
import List from '@material-ui/core/List/index';
import ActtionBar from '../ActionBar';
import ActionBar from '../ActionBar';
import Author from '../Author';
import Developers from '../Developers';
import Dist from '../Dist';
@@ -88,7 +88,7 @@ class DetailSidebar extends Component {
}
renderActionBar = () => {
return <ActtionBar />;
return <ActionBar />;
}
}

View File

@@ -1,15 +1,18 @@
import React, {Component} from 'react';
/**
* @prettier
*/
import React, { Component } from 'react';
import Avatar from '@material-ui/core/Avatar';
import Tooltip from '@material-ui/core/Tooltip';
import Add from '@material-ui/icons/Add';
import Tooltip from '@material-ui/core/Tooltip';
import { DetailContextConsumer } from '../../pages/version';
import { Details, Heading, Content, Fab } from './styles';
interface Props {
type: 'contributors' | 'maintainers'
type: 'contributors' | 'maintainers';
}
class Developers extends Component<Props, any> {
@@ -28,11 +31,11 @@ class Developers extends Component<Props, any> {
}}
</DetailContextConsumer>
);
};
}
handleLoadMore = () => {
this.setState((prev) => ({ visibleDevs: prev.visibleDevs + 6 }));
}
this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 }));
};
renderDevelopers = (developers, packageMeta) => {
const { type } = this.props;
@@ -44,40 +47,33 @@ class Developers extends Component<Props, any> {
{developers.slice(0, visibleDevs).map(developer => (
<Details key={developer.email}>{this.renderDeveloperDetails(developer, packageMeta)}</Details>
))}
{visibleDevs < developers.length &&
<Fab onClick={this.handleLoadMore} size={'small'}><Add /></Fab>
}
{visibleDevs < developers.length && (
<Fab onClick={this.handleLoadMore} size={'small'}>
<Add />
</Fab>
)}
</Content>
</>
);
}
};
renderLinkForMail(email, avatarComponent, packageName, version) {
if(!email) {
if (!email) {
return avatarComponent;
}
return (
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={"_top"}>
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
{avatarComponent}
</a>
);
}
renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
const {
name: packageName,
version,
} = packageMeta.latest;
const { name: packageName, version } = packageMeta.latest;
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
return (
<Tooltip title={name}>
{this.renderLinkForMail(email, avatarComponent, packageName, version)}
</Tooltip>
);
}
return <Tooltip title={name}>{this.renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
};
}
export default Developers;

View File

@@ -2,13 +2,12 @@
* @prettier
*/
/* eslint-disable */
import React, { Component } from 'react';
import List from '@material-ui/core/List/index';
import { DetailContextConsumer } from '../../pages/version/index';
import { Heading, DistListItem, DistChips, DownloadButton } from './styles';
import { Heading, DistListItem, DistChips } from './styles';
import fileSizeSI from '../../utils/file-size';
class Dist extends Component<any, any> {
@@ -34,10 +33,11 @@ class Dist extends Component<any, any> {
if (value) {
const label = (
<span>
<b>{title.split('-').join(' ')}</b>: {value}
{/* eslint-disable-next-line */}
<b>{title.split('-').join(' ')}</b>:{value}
</span>
);
componentList.push(<DistChips label={label} key={key} />);
componentList.push(<DistChips key={key} label={label} />);
}
return componentList;
}, []);

View File

@@ -1,5 +1,8 @@
/* eslint-disable */
import React, {Component} from 'react';
/**
* @prettier
*/
import React, { Component } from 'react';
import Avatar from '@material-ui/core/Avatar/index';
import Grid from '@material-ui/core/Grid/index';
@@ -7,29 +10,27 @@ import List from '@material-ui/core/List/index';
import ListItemText from '@material-ui/core/ListItemText/index';
import { DetailContextConsumer } from '../../pages/version/index';
import { Heading, EngineListItem } from './styles';
import node from './img/node.png';
import npm from '../Install/img/npm.svg'
import npm from '../Install/img/npm.svg';
const ICONS = {
'node-JS': <Avatar src={node} />,
'NPM-version': <Avatar src={npm} />,
}
};
class Engine extends Component {
render() {
return (
<DetailContextConsumer>
{(context) => {
{context => {
return this.renderEngine(context);
}}
</DetailContextConsumer>
);
};
}
renderEngine = ({packageMeta}) => {
renderEngine = ({ packageMeta }) => {
const { engines } = packageMeta.latest;
if (!engines) {
return null;
@@ -37,14 +38,14 @@ class Engine extends Component {
const engineDict = {
'node-JS': engines.node,
'NPM-version': engines.npm
}
'NPM-version': engines.npm,
};
const items = Object.keys(engineDict).reduce((markup, text, key) => {
const heading = engineDict[text]
if (heading){
const heading = engineDict[text];
if (heading) {
markup.push(
<Grid item={true} xs={6} key={key}>
<Grid item={true} key={key} xs={6}>
{this.renderListItems(heading, text)}
</Grid>
);
@@ -56,23 +57,19 @@ class Engine extends Component {
return null;
}
return (
<Grid container={true}>
{items}
</Grid>
);
}
return <Grid container={true}>{items}</Grid>;
};
renderListItems = (heading, text) => {
return (
<List subheader={<Heading variant={"subheading"}>{text.split('-').join(' ')}</Heading>}>
<List subheader={<Heading variant={'subheading'}>{text.split('-').join(' ')}</Heading>}>
<EngineListItem>
{ ICONS[text] }
{ICONS[text]}
<ListItemText primary={heading} />
</EngineListItem>
</List>
);
}
};
}
export default Engine;

View File

@@ -6,7 +6,6 @@
import React from 'react';
import type { Element } from 'react';
import { version } from '../../../../package.json';
import { Wrapper, Left, Right, Earth, Flags, Love, Flag, Logo, Inner, ToolTip } from './styles';
import { goToVerdaccioWebsite } from '../../utils/windows.js';
@@ -28,7 +27,7 @@ const MADEWITH_LABEL = ' Made with';
const ON_LABEL = 'on';
const HEARTH_EMOJI = '♥';
const renderRight = () => (
const renderRight = (version = window.VERDACCIO_VERSION) => (
<Right>
{POWERED_LABEL}
<Logo img={true} name={'verdaccio'} onClick={goToVerdaccioWebsite} pointer={true} size={'md'} />

View File

@@ -6,12 +6,13 @@
import styled, { css } from 'react-emotion';
import mq from '../../utils/styles/media';
import Icon from '../Icon';
import colors from '../../utils/styles/colors';
export const Wrapper = styled.div`
&& {
background: #f9f9f9;
border-top: 1px solid #e3e3e3;
color: #999999;
background: ${colors.snow};
border-top: 1px solid ${colors.greyGainsboro};
color: ${colors.nobel01};
font-size: 14px;
padding: 20px;
}
@@ -67,7 +68,7 @@ export const Earth = styled(Icon)`
export const Flags = styled.span`
&& {
position: absolute;
background: #d3dddd;
background: ${colors.greyAthens};
padding: 1px 4px;
border-radius: 3px;
height: 20px;
@@ -82,7 +83,7 @@ export const Flags = styled.span`
left: -4px;
margin-left: -5px;
border: 5px solid;
border-color: #d3dddd transparent transparent transparent;
border-color: ${colors.greyAthens} transparent transparent transparent;
transform: rotate(90deg);
}
${ToolTip}:hover & {
@@ -93,7 +94,7 @@ export const Flags = styled.span`
export const Love = styled.span`
&& {
color: #e25555;
color: ${colors.love};
padding: 0 5px;
}
`;

View File

@@ -11,6 +11,8 @@ import ListItem from '@material-ui/core/ListItem/index';
import React from 'react';
import { DIST_TAGS } from '../../../lib/constants';
const NOT_AVAILABLE = 'Not available';
class Versions extends React.PureComponent<any> {
render() {
return (
@@ -32,7 +34,8 @@ class Versions extends React.PureComponent<any> {
<ListItem className={'version-item'} key={version}>
<ListItemText>{version}</ListItemText>
<Spacer />
<ListItemText>{isVersion && timeMap[version] ? `${formatDateDistance(timeMap[version])} ago` : packages[version]}</ListItemText>
{isVersion && <ListItemText>{timeMap[version] ? `${formatDateDistance(timeMap[version])} ago` : NOT_AVAILABLE}</ListItemText>}
{isVersion === false && <ListItemText>{packages[version]}</ListItemText>}
</ListItem>
))}
</List>

View File

@@ -3,7 +3,10 @@
*/
import { createBrowserHistory } from 'history';
import { getBaseNamePath } from './utils/url';
const history = createBrowserHistory();
const history = createBrowserHistory({
basename: getBaseNamePath(),
});
export default history;

View File

@@ -9,9 +9,13 @@
<link rel="icon" type="image/png" href="<%= htmlWebpackPlugin.options.verdaccioURL %>/-/static/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
window.__VERDACCIO_BASENAME_UI_OPTIONS = JSON.parse('<%= htmlWebpackPlugin.options.__UI_OPTIONS %>');
window.VERDACCIO_BASENAME = '<%= htmlWebpackPlugin.options.basename %>';
window.VERDACCIO_API_URL = '<%= htmlWebpackPlugin.options.verdaccioURL %>/-/verdaccio/';
window.VERDACCIO_SCOPE = '<%= htmlWebpackPlugin.options.scope %>';
window.VERDACCIO_LOGO = '<%= htmlWebpackPlugin.options.logo %>';
window.VERDACCIO_PRIMARY_COLOR = '<%= htmlWebpackPlugin.options.primary_color %>';
window.VERDACCIO_VERSION = '<%= htmlWebpackPlugin.options.version_app %>';
</script>
</head>

View File

@@ -21,7 +21,7 @@ export const copyToClipBoardUtility = (str: string) => (event: SyntheticEvent<HT
};
export function getCLISetConfigRegistry(command: string, scope: string, registryUrl: string): string {
return `${command} ${scope} registry ${registryUrl}`;
return `${command} ${scope}registry ${registryUrl}`;
}
export function getCLISetRegistry(command: string, registryUrl: string): string {

View File

@@ -22,6 +22,7 @@ const colors = {
paleNavy: '#e4e8f1',
saltpan: '#f7f8f6',
snow: '#f9f9f9',
love: '#e25555',
nobel01: '#999999',
nobel02: '#9f9f9f',
@@ -29,7 +30,7 @@ const colors = {
// Main colors
// -------------------------
primary: '#4b5e40',
primary: window.VERDACCIO_PRIMARY_COLOR || '#4b5e40',
secondary: '#20232a',
};

View File

@@ -2,3 +2,11 @@ export function getRegistryURL() {
// Don't add slash if it's not a sub directory
return `${location.origin}${location.pathname === '/' ? '' : location.pathname}`;
}
export function getBaseNamePath() {
return window.__VERDACCIO_BASENAME_UI_OPTIONS.url_prefix;
}
export function getRootPath() {
return window.__VERDACCIO_BASENAME_UI_OPTIONS.base;
}

View File

@@ -1,5 +0,0 @@
import path from 'path';
export const parseConfigurationFile = (name) => {
return path.join(__dirname, `./partials/config/yaml/${name}.yaml`);
};

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { mount } from 'enzyme';
import storage from '../../../src/webui/utils/storage';
import App from '../../../src/webui/app';
import storage from '../../src/webui/utils/storage';
import App from '../../src/webui/app';
import { generateTokenWithTimeRange } from './components/__mocks__/token';
jest.mock('../../../src/webui/utils/storage', () => {
jest.mock('../../src/webui/utils/storage', () => {
class LocalStorageMock {
constructor() {
this.store = {};
@@ -26,7 +26,7 @@ jest.mock('../../../src/webui/utils/storage', () => {
return new LocalStorageMock();
});
jest.mock('../../../src/webui/utils/api', () => ({
jest.mock('../../src/webui/utils/api', () => ({
request: require('./components/__mocks__/api').default.request
}));

View File

@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Footer /> component should load the initial state of Footer component 1`] = `"<div class=\\"css-i0nj2g e19gp4r80\\"><div class=\\"css-hzfs9b e19gp4r81\\"><div class=\\"css-d8nsp7 e19gp4r82\\"> Made with<span class=\\"css-1so4oe0 e19gp4r87\\">♥</span>on<span class=\\"css-1ie354y e19gp4r84\\"><svg class=\\"e19gp4r85 css-1kgp95j e9byyw50\\"><title>Earth</title><use xlink:href=\\"[object Object]#earth\\"></use></svg><span class=\\"css-1v4n0q4 e19gp4r86\\"><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Spain</title><use xlink:href=\\"[object Object]#spain\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Nicaragua</title><use xlink:href=\\"[object Object]#nicaragua\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>India</title><use xlink:href=\\"[object Object]#india\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Brazil</title><use xlink:href=\\"[object Object]#brazil\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>China</title><use xlink:href=\\"[object Object]#china\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg></span></span></div><div class=\\"css-1wbzdyy e19gp4r83\\">Powered by<span class=\\"e19gp4r88 css-i15wza e9byyw51\\" title=\\"Verdaccio\\"><img alt=\\"Verdaccio\\" src=\\"[object Object]\\" class=\\"css-1ncdhax e9byyw52\\"></span>/ 4.0.0-alpha.3</div></div></div>"`;
exports[`<Footer /> component should load the initial state of Footer component 1`] = `"<div class=\\"css-i0nj2g e19gp4r80\\"><div class=\\"css-hzfs9b e19gp4r81\\"><div class=\\"css-d8nsp7 e19gp4r82\\"> Made with<span class=\\"css-1so4oe0 e19gp4r87\\">♥</span>on<span class=\\"css-1ie354y e19gp4r84\\"><svg class=\\"e19gp4r85 css-1kgp95j e9byyw50\\"><title>Earth</title><use xlink:href=\\"[object Object]#earth\\"></use></svg><span class=\\"css-1v4n0q4 e19gp4r86\\"><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Spain</title><use xlink:href=\\"[object Object]#spain\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Nicaragua</title><use xlink:href=\\"[object Object]#nicaragua\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>India</title><use xlink:href=\\"[object Object]#india\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Brazil</title><use xlink:href=\\"[object Object]#brazil\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>China</title><use xlink:href=\\"[object Object]#china\\"></use></svg><svg class=\\"e19gp4r88 css-f1ndto e9byyw50\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg></span></span></div><div class=\\"css-1wbzdyy e19gp4r83\\">Powered by<span class=\\"e19gp4r88 css-i15wza e9byyw51\\" title=\\"Verdaccio\\"><img alt=\\"Verdaccio\\" src=\\"[object Object]\\" class=\\"css-1ncdhax e9byyw52\\"></span>/ v.1.0.0</div></div></div>"`;

View File

@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<LoginModal /> should load the component in default state 1`] = `"<div role=\\"dialog\\" class=\\"mui-fixed MuiModal-root-15 MuiDialog-root-1\\" id=\\"login--form-container\\" style=\\"padding-right: 0px;\\"><div class=\\"MuiBackdrop-root-17\\" aria-hidden=\\"true\\" style=\\"opacity: 1; webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiDialog-container-4 MuiDialog-scrollPaper-2\\" role=\\"document\\" style=\\"opacity: 1; webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" tabindex=\\"-1\\"><div class=\\"MuiPaper-root-19 MuiPaper-elevation24-45 MuiPaper-rounded-20 MuiDialog-paper-5 MuiDialog-paperScrollPaper-6 MuiDialog-paperWidthXs-8 MuiDialog-paperFullWidth-13\\"><form novalidate=\\"\\"><div class=\\"MuiDialogTitle-root-46\\"><h2 class=\\"MuiTypography-root-47 MuiTypography-title-53\\">Login</h2></div><div class=\\"MuiDialogContent-root-83\\"><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114 MuiInputBase-inputType-132 MuiInput-inputType-117\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-136 dialog-footer\\"><button class=\\"MuiButtonBase-root-164 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"0\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-139\\">Cancel</span><span class=\\"MuiTouchRipple-root-167\\"></span></button><button class=\\"MuiButtonBase-root-164 MuiButtonBase-disabled-165 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-disabled-158 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"-1\\" type=\\"submit\\" disabled=\\"\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-139\\">Login</span></button></div></form></div></div></div>"`;
exports[`<LoginModal /> should load the component with props 1`] = `"<div role=\\"dialog\\" class=\\"mui-fixed MuiModal-root-15 MuiDialog-root-1\\" id=\\"login--form-container\\"><div class=\\"MuiBackdrop-root-17\\" aria-hidden=\\"true\\" style=\\"opacity: 1; webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiDialog-container-4 MuiDialog-scrollPaper-2\\" role=\\"document\\" style=\\"opacity: 1; webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" tabindex=\\"-1\\"><div class=\\"MuiPaper-root-19 MuiPaper-elevation24-45 MuiPaper-rounded-20 MuiDialog-paper-5 MuiDialog-paperScrollPaper-6 MuiDialog-paperWidthXs-8 MuiDialog-paperFullWidth-13\\"><form novalidate=\\"\\"><div class=\\"MuiDialogTitle-root-46\\"><h2 class=\\"MuiTypography-root-47 MuiTypography-title-53\\">Login</h2></div><div class=\\"MuiDialogContent-root-83\\"><div class=\\"MuiTypography-root-47 MuiTypography-body1-56 MuiPaper-root-19 MuiPaper-elevation6-27 MuiSnackbarContent-root-174 loginError\\" role=\\"alertdialog\\"><div class=\\"MuiSnackbarContent-message-175\\"><div class=\\"loginErrorMsg\\" id=\\"client-snackbar\\"><svg class=\\"MuiSvgIcon-root-177 loginIcon\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\\"></path></svg><span><div><strong>Error Title</strong></div><div>Error Description</div></span></div></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114 MuiInputBase-inputType-132 MuiInput-inputType-117\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-136 dialog-footer\\"><button class=\\"MuiButtonBase-root-164 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"0\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-139\\">Cancel</span><span class=\\"MuiTouchRipple-root-167\\"></span></button><button class=\\"MuiButtonBase-root-164 MuiButtonBase-disabled-165 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-disabled-158 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"-1\\" type=\\"submit\\" disabled=\\"\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-139\\">Login</span></button></div></form></div></div></div>"`;

View File

@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NotFound /> component should load the component in default state 1`] = `ShallowWrapper {}`;
exports[`<NotFound /> component should load the component in default state 1`] = `<withRouter(WithTheme(WithWidth(NotFound))) />`;

File diff suppressed because one or more lines are too long

View File

@@ -6,8 +6,8 @@
import React from 'react';
import { shallow } from 'enzyme';
import CopyToClipBoard from '../../../../src/webui/components/CopyToClipBoard';
import { CopyIcon } from '../../../../src/webui/components/CopyToClipBoard/styles';
import CopyToClipBoard from '../../../src/webui/components/CopyToClipBoard/index';
import { CopyIcon } from '../../../src/webui/components/CopyToClipBoard/styles';
describe('<CopyToClipBoard /> component', () => {
let wrapper;

View File

@@ -2,16 +2,18 @@
import React from 'react';
import { mount } from 'enzyme';
import Footer from '../../../../src/webui/components/Footer/index';
import Footer from '../../../src/webui/components/Footer/index';
jest.mock('../../../../package.json', () => ({
jest.mock('../../../package.json', () => ({
version: '4.0.0-alpha.3'
}))
}));
describe('<Footer /> component', () => {
let wrapper;
beforeEach(() => {
window.VERDACCIO_VERSION = 'v.1.0.0';
wrapper = mount(<Footer />);
delete window.VERDACCIO_VERSION;
});
test('should load the initial state of Footer component', () => {

View File

@@ -6,7 +6,7 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { shallow } from 'enzyme';
import Header from '../../../../src/webui/components/Header';
import Header from '../../../src/webui/components/Header/index';
describe('<Header /> component with logged in state', () => {
let wrapper;

View File

@@ -3,7 +3,7 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import Help from '../../../../src/webui/components/Help/index';
import Help from '../../../src/webui/components/Help/index';
describe('<Help /> component', () => {

View File

@@ -6,7 +6,7 @@
import React from 'react';
import { mount } from 'enzyme';
import LoginModal from '../../../../src/webui/components/Login';
import LoginModal from '../../../src/webui/components/Login/index';
const eventUsername = {
target: {

View File

@@ -4,7 +4,7 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
import NoItems from '../../../../src/webui/components/NoItems/index';
import NoItems from '../../../src/webui/components/NoItems/index';
console.error = jest.fn();

View File

@@ -5,7 +5,7 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { shallow } from 'enzyme';
import NotFound from '../../../../src/webui/components/NotFound/index';
import NotFound from '../../../src/webui/components/NotFound/index';
console.error = jest.fn();

View File

@@ -4,9 +4,9 @@
import React from 'react';
import { shallow } from 'enzyme';
import Package from '../../../../src/webui/components/Package/index';
import Tag from '../../../../src/webui/components/Tag/index';
import { Version, WrapperLink, Field, OverviewItem } from '../../../../src/webui/components/Package/styles';
import Package from '../../../src/webui/components/Package/index';
import Tag from '../../../src/webui/components/Tag/index';
import { Version, WrapperLink, Field, OverviewItem } from '../../../src/webui/components/Package/styles';
/**

View File

@@ -4,8 +4,8 @@
import React from 'react';
import { mount } from 'enzyme';
import PackageList from '../../../../src/webui/components/PackageList/index';
import Help from '../../../../src/webui/components/Help/index';
import PackageList from '../../../src/webui/components/PackageList/index';
import Help from '../../../src/webui/components/Help/index';
import { BrowserRouter } from 'react-router-dom';
describe('<PackageList /> component', () => {

View File

@@ -4,7 +4,7 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
import Readme from '../../../../src/webui/components/Readme/index';
import Readme from '../../../src/webui/components/Readme/index';
describe('<Readme /> component', () => {
test('should load the component in default state', () => {

View File

@@ -6,11 +6,11 @@
import React from 'react';
import { mount } from 'enzyme';
import { Search } from '../../../../src/webui/components/Search/index';
import { Search } from '../../../src/webui/components/Search/index';
const SEARCH_FILE_PATH = '../../../../src/webui/components/Search/index';
const API_FILE_PATH = '../../../../src/webui/utils/api';
const URL_FILE_PATH = '../../../../src/webui/utils/url';
const SEARCH_FILE_PATH = '../../../src/webui/components/Search/index';
const API_FILE_PATH = '../../../src/webui/utils/api';
const URL_FILE_PATH = '../../../src/webui/utils/url';
// Global mocks
const event = {

View File

@@ -1,4 +1,4 @@
import {API_ERROR} from '../../../../../src/lib/constants';
import {API_ERROR} from '../../../../src/lib/constants';
/**
* API mock for login endpoint
* @param {object} config configuration of api call

View File

@@ -10,6 +10,7 @@ import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
global.__APP_VERSION__ = '1.0.0';
global.__VERDACCIO_BASENAME_UI_OPTIONS = {};
// mocking few DOM methods
if (global.document) {

View File

@@ -1,4 +1,4 @@
import { isTokenExpire, makeLogin } from '../../../../src/webui/utils/login';
import { isTokenExpire, makeLogin } from '../../../src/webui/utils/login';
import {
generateTokenWithTimeRange,

View File

@@ -5,7 +5,7 @@ import {
formatDateDistance,
getLastUpdatedPackageTime,
getRecentReleases
} from '../../../../src/webui/utils/package';
} from '../../../src/webui/utils/package';
import { packageMeta } from '../components/store/packageMeta';
@@ -52,13 +52,21 @@ describe('formatDate', () => {
describe('formatDateDistance', () => {
test('should calculate the distance', () => {
const dateAboutTwoMonthsAgo = () => {
const date = new Date();
date.setMonth(date.getMonth() - 1);
date.setDate(date.getDay() - 20);
return date;
};
const dateTwoMonthsAgo = () => {
const date = new Date();
date.setMonth(date.getMonth() - 2);
return date;
};
const date = dateTwoMonthsAgo();
expect(formatDateDistance(date)).toEqual('about 2 months');
const date1 = dateAboutTwoMonthsAgo();
const date2 = dateTwoMonthsAgo();
expect(formatDateDistance(date1)).toEqual('about 2 months');
expect(formatDateDistance(date2)).toEqual('2 months');
});
});

View File

@@ -1,5 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<LoginModal /> should load the component in default state 1`] = `"<div role=\\"dialog\\" class=\\"mui-fixed MuiModal-root-15 MuiDialog-root-1\\" id=\\"login--form-container\\" style=\\"padding-right: 0px;\\"><div class=\\"MuiBackdrop-root-17\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiDialog-container-4 MuiDialog-scrollPaper-2\\" role=\\"document\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" tabindex=\\"-1\\"><div class=\\"MuiPaper-root-19 MuiPaper-elevation24-45 MuiPaper-rounded-20 MuiDialog-paper-5 MuiDialog-paperScrollPaper-6 MuiDialog-paperWidthXs-8 MuiDialog-paperFullWidth-13\\"><form novalidate=\\"\\"><div class=\\"MuiDialogTitle-root-46\\"><h2 class=\\"MuiTypography-root-47 MuiTypography-title-53\\">Login</h2></div><div class=\\"MuiDialogContent-root-83\\"><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114 MuiInputBase-inputType-132 MuiInput-inputType-117\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-136 dialog-footer\\"><button class=\\"MuiButtonBase-root-164 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"0\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-139\\">Cancel</span><span class=\\"MuiTouchRipple-root-167\\"></span></button><button class=\\"MuiButtonBase-root-164 MuiButtonBase-disabled-165 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-disabled-158 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"-1\\" type=\\"submit\\" disabled=\\"\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-139\\">Login</span></button></div></form></div></div></div>"`;
exports[`<LoginModal /> should load the component with props 1`] = `"<div role=\\"dialog\\" class=\\"mui-fixed MuiModal-root-15 MuiDialog-root-1\\" id=\\"login--form-container\\"><div class=\\"MuiBackdrop-root-17\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiDialog-container-4 MuiDialog-scrollPaper-2\\" role=\\"document\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" tabindex=\\"-1\\"><div class=\\"MuiPaper-root-19 MuiPaper-elevation24-45 MuiPaper-rounded-20 MuiDialog-paper-5 MuiDialog-paperScrollPaper-6 MuiDialog-paperWidthXs-8 MuiDialog-paperFullWidth-13\\"><form novalidate=\\"\\"><div class=\\"MuiDialogTitle-root-46\\"><h2 class=\\"MuiTypography-root-47 MuiTypography-title-53\\">Login</h2></div><div class=\\"MuiDialogContent-root-83\\"><div class=\\"MuiTypography-root-47 MuiTypography-body1-56 MuiPaper-root-19 MuiPaper-elevation6-27 MuiSnackbarContent-root-174 loginError\\" role=\\"alertdialog\\"><div class=\\"MuiSnackbarContent-message-175\\"><div class=\\"loginErrorMsg\\" id=\\"client-snackbar\\"><svg class=\\"MuiSvgIcon-root-177 loginIcon\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\\"></path></svg><span><div><strong>Error Title</strong></div><div>Error Description</div></span></div></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-84 MuiFormControl-fullWidth-87\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-99 MuiFormLabel-required-104 MuiInputLabel-required-92 MuiInputLabel-root-88 MuiInputLabel-formControl-93 MuiInputLabel-animated-96\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-105\\"> *</span></label><div class=\\"MuiInputBase-root-119 MuiInput-root-106 MuiInput-underline-110 MuiInputBase-formControl-120 MuiInput-formControl-107\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-129 MuiInput-input-114 MuiInputBase-inputType-132 MuiInput-inputType-117\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-136 dialog-footer\\"><button class=\\"MuiButtonBase-root-164 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"0\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-139\\">Cancel</span><span class=\\"MuiTouchRipple-root-167\\"></span></button><button class=\\"MuiButtonBase-root-164 MuiButtonBase-disabled-165 MuiButton-root-138 MuiButton-text-140 MuiButton-flat-143 MuiButton-disabled-158 MuiButton-colorInherit-159 MuiDialogActions-action-137\\" tabindex=\\"-1\\" type=\\"submit\\" disabled=\\"\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-139\\">Login</span></button></div></form></div></div></div>"`;

File diff suppressed because one or more lines are too long

56
tools/_config.yaml Normal file
View File

@@ -0,0 +1,56 @@
web:
title: Verdaccio
# gravatar: false
# sort_packages: asc
plugins: ../
auth:
auth-memory:
users:
foo:
name: test
password: test
bar:
name: bar
password: test
store:
memory:
limit: 1000
# theme:
security:
api:
jwt:
sign:
expiresIn: 60d
notBefore: 1
web:
sign:
expiresIn: 7d
notBefore: 1
uplinks:
npmjs:
url: https://registry.npmjs.org/
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
middlewares:
audit:
enabled: true
logs:
- { type: stdout, format: pretty, level: trace }

View File

@@ -32,7 +32,7 @@ new WebpackDevServer(compiler, {
},
proxy: [{
context: ['/-/verdaccio/logo', '/-/verdaccio/packages', '/-/static/logo.png'],
target: 'http://localhost:4873',
target: 'http://localhost:8080',
}],
}).listen(4872, 'localhost', function(err) {
if (err) {

23
tools/verdaccio.js Normal file
View File

@@ -0,0 +1,23 @@
/**
* @prettier
*/
const fs = require('fs');
const startServer = require('verdaccio').default;
const yalm = require('js-yaml');
const configJsonFormat = yalm.safeLoad(fs.readFileSync('./tools/_config.yaml', 'utf8'));
const handler = function(webServer, addr, pkgName, pkgVersion) {
webServer.listen(addr.port || addr.path, addr.host, () => {
console.log(`${pkgName}:${pkgVersion} running ${addr.proto}://${addr.host}:${addr.port}`);
});
process.on('SIGTERM', () => {
webServer.close(() => {
console.log('Process terminated');
});
});
};
startServer(configJsonFormat, 8080, '', '1.0.0', 'verdaccio', handler);

View File

@@ -7,6 +7,7 @@ module.exports = {
output: {
path: `${env.APP_ROOT}/static/`,
filename: '[name].[hash].js',
// FIXME: do we need this?
publicPath: 'ToReplaceByVerdaccio/-/static',
},

View File

@@ -38,7 +38,7 @@ export default {
scope: '',
logo: 'https://verdaccio.org/img/logo/symbol/svg/verdaccio-tiny.svg',
filename: 'index.html',
verdaccioURL: '//localhost:4873',
verdaccioURL: '//localhost:8080',
template: `${env.SRC_ROOT}/webui/template/index.html`,
debug: true,
inject: true,

View File

@@ -45,11 +45,15 @@ const prodConf = {
}),
new HTMLWebpackPlugin({
title: 'ToReplaceByTitle',
__UI_OPTIONS: 'ToReplaceByVerdaccioUI',
scope: 'ToReplaceByScope',
basename: 'ToReplaceByPrefix',
logo: 'ToReplaceByLogo',
primary_color: 'ToReplaceByPrimaryColor',
filename: 'index.html',
favicon: `${env.SRC_ROOT}/webui/template/favicon.ico`,
verdaccioURL: 'ToReplaceByVerdaccio',
version_app: 'ToReplaceByVersion',
template: `${env.SRC_ROOT}/webui/template/index.html`,
debug: false,
inject: true,

5342
yarn.lock

File diff suppressed because it is too large Load Diff