1
0
mirror of https://github.com/SomboChea/ui synced 2026-01-12 14:15:47 +07:00

Compare commits

...

31 Commits

Author SHA1 Message Date
Juan Picado @jotadeveloper
47b98c5550 chore(release): 0.1.5 2019-05-04 16:12:43 +02:00
Juan Picado @jotadeveloper
da4ac7e851 chore: update name 2019-05-04 16:02:12 +02:00
Juan Picado @jotadeveloper
c960fa76db chore: restore circleci 2019-05-04 15:58:50 +02:00
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
75 changed files with 3601 additions and 2981 deletions

View File

@@ -2,16 +2,16 @@ version: 2
aliases:
- &defaults
working_directory: ~/verdaccio
working_directory: ~/ui-theme
- &node11_executor
docker:
- image: circleci/node:11.10.1
- &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
@@ -23,21 +23,20 @@ aliases:
- &yarn_cache_key
yarn-sha-{{ checksum "yarn.lock" }}
- &restore_repo
attach_workspace:
at: ~/verdaccio
restore_cache:
keys:
- *repo_key
- &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]+)*/
only: /(v)?[0-9]+(\.[0-9]+)*/
branches:
ignore:
- /.*/
@@ -47,43 +46,45 @@ jobs:
<<: *defaults
<<: *default_executor
steps:
- *restore_repo
- 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
command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
- 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
command: yarn install --no-progress --registry https://registry.verdaccio.org --no-lockfile
- run:
name: Build project
command: yarn build
command: yarn run build
- save_cache:
key: *yarn_cache_key
paths:
- ~/.yarn
- ~/.cache/yarn
- node_modules
- persist_to_workspace:
root: ~/verdaccio
- save_cache:
key: *repo_key
paths:
- ./*
- ~/ui-theme
test_node11:
<<: *defaults
<<: *node11_executor
steps:
- *restore_repo
- run:
name: Test with Node 11
command: yarn run test
test_node8:
<<: *defaults
@@ -92,8 +93,8 @@ jobs:
- *restore_repo
- run:
name: Test with Node 8
command: |
yarn test
command: yarn test
test_node10:
<<: *defaults
<<: *node10_executor
@@ -101,18 +102,12 @@ jobs:
- *restore_repo
- run:
name: Test with Node 10
command: |
yarn test
command: yarn run test
- save_cache:
key: *coverage_key
paths:
- coverage
test_node11:
<<: *defaults
<<: *node11_executor
steps:
- *restore_repo
- run:
name: Test with Node 11
command: |
yarn run test
coverage:
<<: *defaults
<<: *default_executor
@@ -141,7 +136,7 @@ jobs:
key: *base_config_key
- run:
name: Publish
command: scripts/publish.sh
command: yarn publish
workflows:
version: 2
@@ -149,6 +144,10 @@ workflows:
jobs:
- prepare:
<<: *ignore_non_dev_branches
- test_node11:
requires:
- prepare
<<: *ignore_non_dev_branches
- test_node8:
requires:
- prepare
@@ -157,15 +156,11 @@ workflows:
requires:
- prepare
<<: *ignore_non_dev_branches
- test_node11:
requires:
- prepare
<<: *ignore_non_dev_branches
- coverage:
requires:
- test_node11
- test_node8
- test_node10
- test_node11
<<: *ignore_non_dev_branches
- publish_package:
requires:

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,6 +2,57 @@
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.5"></a>
## [0.1.5](https://github.com/verdaccio/ui/compare/v0.1.4...v0.1.5) (2019-05-04)
<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)

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
@@ -63,7 +68,7 @@ This project exists thanks to all the people who contribute. [[Contribute](CONTR
[![contrubitors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](../../graphs/contributors)
### FAQ / Contact / Troubleshoot
### FAQ / Contact / Troubleshoot
If you have any issue you can try the following options, do no desist to ask or check our issues database, perhaps someone has asked already what you are looking for.
@@ -79,4 +84,4 @@ If you have any issue you can try the following options, do no desist to ask or
### License
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)

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.4",
"version": "0.1.5",
"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": {

13
scripts/gh_publish.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Get the last tag from GitHub
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_URL/

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

@@ -7,4 +7,4 @@ lastTag=$(git describe --tags $(git rev-list --tags --max-count=1))
echo "Bumping version to new tag: ${lastTag}"
# Publish to NPM
npm publish --registry https://registry.npmjs.org/
npm publish

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 avatarComponent = <Avatar aria-label={name} src={avatar} />;
return (
<Tooltip title={name}>
{this.renderLinkForMail(email, avatarComponent, packageName, version)}
</Tooltip>
);
}
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>;
};
}
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
}));
@@ -36,7 +36,7 @@ describe('App', () => {
beforeEach(() => {
wrapper = mount(<App />);
});
test('toggleLoginModal: should toggle the value in state', () => {
const { handleToggleLoginModal } = wrapper.instance();
expect(wrapper.state().showLoginModal).toBeFalsy();

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