├── renovate.json ├── .gitignore ├── images ├── card.png ├── default.png ├── copy-install.gif ├── custom-theme-blue.png └── custom-theme-red.png ├── tsconfig.json ├── src ├── pending.view.ts ├── failure.view.ts ├── api.ts ├── success.view.ts ├── pkg.ts └── node-package.ts ├── .appveyor.yml ├── .travis.yml ├── .circleci └── config.yml ├── tsconfig.production.json ├── test ├── data │ ├── lodash.json │ ├── chalk.json │ ├── bluebird.json │ ├── request.json │ ├── @angular │ │ └── cli.json │ └── @nutmeg │ │ └── cli.json └── node-package.test.ts ├── LICENSE.md ├── package.json ├── index.html └── README.md /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | yarn.lock 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /images/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/node-package/HEAD/images/card.png -------------------------------------------------------------------------------- /images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/node-package/HEAD/images/default.png -------------------------------------------------------------------------------- /images/copy-install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/node-package/HEAD/images/copy-install.gif -------------------------------------------------------------------------------- /images/custom-theme-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/node-package/HEAD/images/custom-theme-blue.png -------------------------------------------------------------------------------- /images/custom-theme-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/node-package/HEAD/images/custom-theme-red.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.production", 3 | "include": [ 4 | "src", 5 | "test" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/pending.view.ts: -------------------------------------------------------------------------------- 1 | import { html, TemplateResult } from '@nutmeg/seed'; 2 | 3 | export class PendingView { 4 | constructor() {} 5 | 6 | public get content(): TemplateResult { 7 | return html` 8 |
9 | Loading... 10 |
11 | `; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "9" 3 | 4 | install: 5 | - ps: Install-Product node $env:nodejs_version 6 | - npm install 7 | 8 | test_script: 9 | - node --version 10 | - npm --version 11 | - npm test 12 | 13 | cache: 14 | - node_modules 15 | - '%APPDATA%\npm-cache' 16 | 17 | build: off 18 | -------------------------------------------------------------------------------- /src/failure.view.ts: -------------------------------------------------------------------------------- 1 | import { html, TemplateResult } from '@nutmeg/seed'; 2 | 3 | export class FailureView { 4 | constructor(private error: string) {} 5 | 6 | public get content(): TemplateResult { 7 | return html` 8 |
9 | ${this.error} 10 |
11 | `; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Work-around for https://github.com/travis-ci/travis-ci/issues/8836 2 | sudo: true 3 | language: node_js 4 | node_js: 5 | - node 6 | before_script: 7 | - node --version 8 | - npm --version 9 | - npm install 10 | script: 11 | - npm run test 12 | os: 13 | - linux 14 | cache: 15 | directories: 16 | - node_modules 17 | addons: 18 | firefox: latest 19 | chrome: stable 20 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | macos: 5 | xcode: "11.3.1" 6 | steps: 7 | - checkout 8 | - run: brew cask install firefox google-chrome 9 | - run: node --version 10 | - run: npm --version 11 | - restore_cache: 12 | key: dependency-cache-{{ checksum "package.json" }} 13 | - run: npm install 14 | - save_cache: 15 | key: dependency-cache-{{ checksum "package.json" }} 16 | paths: 17 | - ./node_modules 18 | - run: npm run test 19 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import { PackageData } from './pkg'; 2 | 3 | export class Api { 4 | private readonly apiHost = 'https://unpkg.com/'; 5 | 6 | public async fetch(name: string): Promise { 7 | const response = await fetch(this.url(name)); 8 | if (response.status === 200) { 9 | const data = await response.json(); 10 | return data; 11 | } else { 12 | throw await response.text(); 13 | } 14 | } 15 | 16 | private url(name: string): string { 17 | return `${this.apiHost}${name}/package.json`; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.production.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "inlineSources": true, 7 | "lib": ["esnext", "dom"], 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noUnusedParameters": true, 14 | "outDir": "dist", 15 | "skipLibCheck": true, 16 | "sourceMap": true, 17 | "sourceRoot": "src", 18 | "strict": true, 19 | "target": "esnext" 20 | }, 21 | "include": [ 22 | "src" 23 | ], 24 | "exclude": [ 25 | "node_modules", 26 | "dist" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /test/data/lodash.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lodash", 3 | "version": "4.17.5", 4 | "description": "Lodash modular utilities.", 5 | "keywords": "modules, stdlib, util", 6 | "homepage": "https://lodash.com/", 7 | "repository": "lodash/lodash", 8 | "icon": "https://lodash.com/icon.svg", 9 | "license": "MIT", 10 | "main": "lodash.js", 11 | "author": "John-David Dalton (http://allyoucanleet.com/)", 12 | "contributors": [ 13 | "John-David Dalton (http://allyoucanleet.com/)", 14 | "Mathias Bynens (https://mathiasbynens.be/)" 15 | ], 16 | "scripts": { "test": "echo \"See https://travis-ci.org/lodash-archive/lodash-cli for testing details.\"" } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-package", 3 | "version": "1.4.0", 4 | "description": "Node Package Web Component", 5 | "main": "dist/node-package.js", 6 | "module": "dist/node-package.js", 7 | "unpkg": "dist/node-package.min.js", 8 | "types": "dist/node-package.d.ts", 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/abraham/node-package.git" 15 | }, 16 | "engines": { 17 | "node": ">=8.0.0" 18 | }, 19 | "keywords": [ 20 | "node-package", 21 | "npm", 22 | "web-component", 23 | "nutmeg" 24 | ], 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/abraham/node-package/issues" 28 | }, 29 | "homepage": "https://github.com/abraham/node-package", 30 | "scripts": { 31 | "build": "npx nutmeg build .", 32 | "prebuild": "npx nutmeg clean .", 33 | "prepare": "npm run build -- --production", 34 | "pretest": "npm run build", 35 | "start": "npx nutmeg serve .", 36 | "test": "npx nutmeg test .", 37 | "watch": "npx nutmeg watch ." 38 | }, 39 | "dependencies": { 40 | "@abraham/remotedata": "1.1.0", 41 | "@nutmeg/seed": "0.17.0", 42 | "lit-html": "1.1.2" 43 | }, 44 | "devDependencies": { 45 | "@nutmeg/cli": "0.17.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/data/chalk.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chalk", 3 | "version": "2.3.1", 4 | "description": "Terminal string styling done right", 5 | "license": "MIT", 6 | "repository": "chalk/chalk", 7 | "engines": { 8 | "node": ">=4" 9 | }, 10 | "scripts": { 11 | "test": "xo && tsc --project types && nyc ava", 12 | "bench": "matcha benchmark.js", 13 | "coveralls": "nyc report --reporter=text-lcov | coveralls" 14 | }, 15 | "files": [ 16 | "index.js", 17 | "templates.js", 18 | "types/index.d.ts" 19 | ], 20 | "keywords": [ 21 | "color", 22 | "colour", 23 | "colors", 24 | "terminal", 25 | "console", 26 | "cli", 27 | "string", 28 | "str", 29 | "ansi", 30 | "style", 31 | "styles", 32 | "tty", 33 | "formatting", 34 | "rgb", 35 | "256", 36 | "shell", 37 | "xterm", 38 | "log", 39 | "logging", 40 | "command-line", 41 | "text" 42 | ], 43 | "dependencies": { 44 | "ansi-styles": "^3.2.0", 45 | "escape-string-regexp": "^1.0.5", 46 | "supports-color": "^5.2.0" 47 | }, 48 | "devDependencies": { 49 | "ava": "*", 50 | "coveralls": "^3.0.0", 51 | "execa": "^0.9.0", 52 | "import-fresh": "^2.0.0", 53 | "matcha": "^0.7.0", 54 | "nyc": "^11.0.2", 55 | "resolve-from": "^4.0.0", 56 | "typescript": "^2.5.3", 57 | "xo": "*" 58 | }, 59 | "types": "types/index.d.ts", 60 | "xo": { 61 | "envs": [ 62 | "node", 63 | "mocha" 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node-package demos 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /test/data/bluebird.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bluebird", 3 | "description": "Full featured Promises/A+ implementation with exceptionally good performance", 4 | "version": "3.5.1", 5 | "keywords": [ 6 | "promise", 7 | "performance", 8 | "promises", 9 | "promises-a", 10 | "promises-aplus", 11 | "async", 12 | "await", 13 | "deferred", 14 | "deferreds", 15 | "future", 16 | "flow control", 17 | "dsl", 18 | "fluent interface" 19 | ], 20 | "scripts": { 21 | "lint": "node scripts/jshint.js", 22 | "test": "node tools/test.js", 23 | "istanbul": "istanbul", 24 | "prepublish": "npm run generate-browser-core && npm run generate-browser-full", 25 | "generate-browser-full": "node tools/build.js --no-clean --no-debug --release --browser --minify", 26 | "generate-browser-core": "node tools/build.js --features=core --no-debug --release --zalgo --browser --minify && mv js/browser/bluebird.js js/browser/bluebird.core.js && mv js/browser/bluebird.min.js js/browser/bluebird.core.min.js" 27 | }, 28 | "homepage": "https://github.com/petkaantonov/bluebird", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/petkaantonov/bluebird.git" 32 | }, 33 | "bugs": { 34 | "url": "http://github.com/petkaantonov/bluebird/issues" 35 | }, 36 | "license": "MIT", 37 | "author": { 38 | "name": "Petka Antonov", 39 | "email": "petka_antonov@hotmail.com", 40 | "url": "http://github.com/petkaantonov/" 41 | }, 42 | "devDependencies": { 43 | "acorn": "~0.6.0", 44 | "baconjs": "^0.7.43", 45 | "bluebird": "^2.9.2", 46 | "body-parser": "^1.10.2", 47 | "browserify": "^8.1.1", 48 | "cli-table": "~0.3.1", 49 | "co": "^4.2.0", 50 | "cross-spawn": "^0.2.3", 51 | "glob": "^4.3.2", 52 | "grunt-saucelabs": "~8.4.1", 53 | "highland": "^2.3.0", 54 | "istanbul": "^0.3.5", 55 | "jshint": "^2.6.0", 56 | "jshint-stylish": "~0.2.0", 57 | "mkdirp": "~0.5.0", 58 | "mocha": "~2.1", 59 | "open": "~0.0.5", 60 | "optimist": "~0.6.1", 61 | "rimraf": "~2.2.6", 62 | "rx": "^2.3.25", 63 | "serve-static": "^1.7.1", 64 | "sinon": "~1.7.3", 65 | "uglify-js": "~2.4.16", 66 | "kefir": "^2.4.1" 67 | }, 68 | "readmeFilename": "README.md", 69 | "main": "./js/release/bluebird.js", 70 | "webpack": "./js/release/bluebird.js", 71 | "browser": "./js/browser/bluebird.js", 72 | "files": [ 73 | "js/browser", 74 | "js/release", 75 | "LICENSE" 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /test/data/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "request", 3 | "description": "Simplified HTTP request client.", 4 | "keywords": [ 5 | "http", 6 | "simple", 7 | "util", 8 | "utility" 9 | ], 10 | "version": "2.83.0", 11 | "author": "Mikeal Rogers ", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/request/request.git" 15 | }, 16 | "bugs": { 17 | "url": "http://github.com/request/request/issues" 18 | }, 19 | "license": "Apache-2.0", 20 | "engines": { 21 | "node": ">= 4" 22 | }, 23 | "main": "index.js", 24 | "files": [ 25 | "lib/", 26 | "index.js", 27 | "request.js" 28 | ], 29 | "dependencies": { 30 | "aws-sign2": "~0.7.0", 31 | "aws4": "^1.6.0", 32 | "caseless": "~0.12.0", 33 | "combined-stream": "~1.0.5", 34 | "extend": "~3.0.1", 35 | "forever-agent": "~0.6.1", 36 | "form-data": "~2.3.1", 37 | "har-validator": "~5.0.3", 38 | "hawk": "~6.0.2", 39 | "http-signature": "~1.2.0", 40 | "is-typedarray": "~1.0.0", 41 | "isstream": "~0.1.2", 42 | "json-stringify-safe": "~5.0.1", 43 | "mime-types": "~2.1.17", 44 | "oauth-sign": "~0.8.2", 45 | "performance-now": "^2.1.0", 46 | "qs": "~6.5.1", 47 | "safe-buffer": "^5.1.1", 48 | "stringstream": "~0.0.5", 49 | "tough-cookie": "~2.3.3", 50 | "tunnel-agent": "^0.6.0", 51 | "uuid": "^3.1.0" 52 | }, 53 | "scripts": { 54 | "test": "npm run lint && npm run test-ci && npm run test-browser", 55 | "test-ci": "taper tests/test-*.js", 56 | "test-cov": "istanbul cover tape tests/test-*.js", 57 | "test-browser": "node tests/browser/start.js", 58 | "lint": "standard" 59 | }, 60 | "devDependencies": { 61 | "bluebird": "^3.2.1", 62 | "browserify": "^13.0.1", 63 | "browserify-istanbul": "^2.0.0", 64 | "buffer-equal": "^1.0.0", 65 | "codecov": "^2.0.2", 66 | "coveralls": "^2.11.4", 67 | "function-bind": "^1.0.2", 68 | "istanbul": "^0.4.0", 69 | "karma": "^1.1.1", 70 | "karma-browserify": "^5.0.1", 71 | "karma-cli": "^1.0.0", 72 | "karma-coverage": "^1.0.0", 73 | "karma-phantomjs-launcher": "^1.0.0", 74 | "karma-tap": "^3.0.1", 75 | "phantomjs-prebuilt": "^2.1.3", 76 | "rimraf": "^2.2.8", 77 | "server-destroy": "^1.0.1", 78 | "standard": "^9.0.0", 79 | "tape": "^4.6.0", 80 | "taper": "^0.5.0" 81 | }, 82 | "greenkeeper": { 83 | "ignore": [ 84 | "hawk", 85 | "har-validator" 86 | ] 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /test/data/@angular/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular/cli", 3 | "version": "1.7.0", 4 | "description": "CLI tool for Angular", 5 | "main": "lib/cli/index.js", 6 | "trackingCode": "UA-8594346-19", 7 | "bin": { 8 | "ng": "./bin/ng" 9 | }, 10 | "keywords": [ 11 | "angular", 12 | "angular-cli", 13 | "Angular CLI" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/angular/angular-cli.git" 18 | }, 19 | "engines": { 20 | "node": ">= 6.9.0", 21 | "npm": ">= 3.0.0" 22 | }, 23 | "author": "Angular Authors", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/angular/angular-cli/issues" 27 | }, 28 | "homepage": "https://github.com/angular/angular-cli", 29 | "dependencies": { 30 | "@angular-devkit/build-optimizer": "0.3.1", 31 | "@angular-devkit/core": "0.3.1", 32 | "@angular-devkit/schematics": "0.3.1", 33 | "@ngtools/json-schema": "1.2.0", 34 | "@ngtools/webpack": "1.10.0", 35 | "@schematics/angular": "0.3.1", 36 | "@schematics/package-update": "0.3.1", 37 | "autoprefixer": "^7.2.3", 38 | "cache-loader": "^1.2.0", 39 | "chalk": "~2.2.0", 40 | "circular-dependency-plugin": "^4.2.1", 41 | "clean-css": "^4.1.9", 42 | "common-tags": "^1.3.1", 43 | "copy-webpack-plugin": "~4.4.1", 44 | "core-object": "^3.1.0", 45 | "denodeify": "^1.2.1", 46 | "ember-cli-string-utils": "^1.0.0", 47 | "extract-text-webpack-plugin": "^3.0.2", 48 | "file-loader": "^1.1.5", 49 | "fs-extra": "^4.0.0", 50 | "glob": "^7.0.3", 51 | "html-webpack-plugin": "^2.29.0", 52 | "karma-source-map-support": "^1.2.0", 53 | "less": "^2.7.2", 54 | "less-loader": "^4.0.5", 55 | "license-webpack-plugin": "^1.0.0", 56 | "lodash": "^4.11.1", 57 | "loader-utils": "1.1.0", 58 | "memory-fs": "^0.4.1", 59 | "minimatch": "^3.0.4", 60 | "node-modules-path": "^1.0.0", 61 | "nopt": "^4.0.1", 62 | "opn": "~5.1.0", 63 | "portfinder": "~1.0.12", 64 | "postcss": "^6.0.16", 65 | "postcss-import": "^11.0.0", 66 | "postcss-loader": "^2.0.10", 67 | "postcss-url": "^7.1.2", 68 | "raw-loader": "^0.5.1", 69 | "resolve": "^1.1.7", 70 | "rxjs": "^5.5.6", 71 | "sass-loader": "^6.0.6", 72 | "semver": "^5.1.0", 73 | "silent-error": "^1.0.0", 74 | "source-map-support": "^0.4.1", 75 | "istanbul-instrumenter-loader": "^3.0.0", 76 | "style-loader": "^0.19.1", 77 | "stylus": "^0.54.5", 78 | "stylus-loader": "^3.0.1", 79 | "uglifyjs-webpack-plugin": "^1.1.8", 80 | "url-loader": "^0.6.2", 81 | "webpack": "~3.11.0", 82 | "webpack-dev-middleware": "~1.12.0", 83 | "webpack-dev-server": "~2.11.0", 84 | "webpack-merge": "^4.1.0", 85 | "webpack-sources": "^1.0.0", 86 | "webpack-subresource-integrity": "^1.0.1" 87 | }, 88 | "optionalDependencies": { 89 | "node-sass": "^4.7.2" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/data/@nutmeg/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nutmeg/cli", 3 | "version": "0.11.2", 4 | "description": "Build, test, and publish vanilla Web Components with a little spice", 5 | "main": "dist/cli.js", 6 | "types": "dist/cli.d.ts", 7 | "bin": { 8 | "nutmeg-build": "bin/nutmeg-build", 9 | "nutmeg-clean": "bin/nutmeg-clean", 10 | "nutmeg-new": "bin/nutmeg-new", 11 | "nutmeg-serve": "bin/nutmeg-serve", 12 | "nutmeg-test": "bin/nutmeg-test", 13 | "nutmeg-watch": "bin/nutmeg-watch", 14 | "nutmeg": "bin/nutmeg" 15 | }, 16 | "directories": { 17 | "test": "test" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/abraham/nutmeg-cli.git" 22 | }, 23 | "keywords": [ 24 | "web-components", 25 | "webcomponents", 26 | "shadow-dom", 27 | "shadowdom", 28 | "lit-html", 29 | "nutmeg", 30 | "typescript" 31 | ], 32 | "author": { 33 | "name": "Abraham Williams", 34 | "email": "abraham@abrah.am", 35 | "url": "https://abrah.am" 36 | }, 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/abraham/nutmeg-cli/issues" 40 | }, 41 | "homepage": "https://github.com/abraham/nutmeg-cli", 42 | "scripts": { 43 | "build": "tsc", 44 | "prepare": "npm run build", 45 | "pretest": "mkdir tmp && npm pack . && node ./scripts/rename-pack.js", 46 | "test": "cd tmp && nutmeg new ci-test first:number second:string third:boolean --no-yarn --cli-source file:../../nutmeg-cli-latest.tgz && cd ci-test && npm run test", 47 | "test:yarn": "cd tmp && nutmeg new ci-test first:number second:string third:boolean --yarn --cli-source file:../.. && cd ci-test && npm run test", 48 | "watch": "tsc --watch" 49 | }, 50 | "engines": { 51 | "node": ">=8" 52 | }, 53 | "dependencies": { 54 | "@nutmeg/seed": "0.8.2", 55 | "@types/chai": "4.1.2", 56 | "@types/mocha": "2.2.48", 57 | "@types/sinon": "4.1.3", 58 | "@webcomponents/webcomponentsjs": "1.1.0", 59 | "babel-preset-env": "1.6.1", 60 | "chai": "4.1.2", 61 | "commander": "2.13.0", 62 | "hasbin": "1.2.3", 63 | "html-webpack-plugin": "2.30.1", 64 | "karma": "2.0.0", 65 | "karma-chai": "0.1.0", 66 | "karma-chrome-launcher": "2.2.0", 67 | "karma-firefox-launcher": "1.1.0", 68 | "karma-mocha": "1.3.0", 69 | "karma-opera-launcher": "1.0.0", 70 | "karma-safari-launcher": "1.0.0", 71 | "karma-sinon": "1.0.5", 72 | "karma-typescript": "3.0.12", 73 | "karma-typescript-es6-transform": "1.0.3", 74 | "karma-webpack": "2.0.9", 75 | "lodash.template": "4.4.0", 76 | "mocha": "5.0.0", 77 | "pascal-case": "2.0.1", 78 | "recursive-copy": "2.0.8", 79 | "shelljs": "0.8.1", 80 | "sinon": "4.2.2", 81 | "through2": "2.0.3", 82 | "ts-loader": "3.4.0", 83 | "typescript": "2.6.2", 84 | "uglifyjs-webpack-plugin": "1.1.8", 85 | "update-notifier": "2.3.0", 86 | "webpack": "3.10.0", 87 | "webpack-bundle-analyzer": "2.10.0", 88 | "webpack-dev-server": "2.11.1" 89 | }, 90 | "devDependencies": { 91 | "@types/lodash.template": "4.4.3", 92 | "@types/node": "9.4.0", 93 | "@types/shelljs": "0.7.8", 94 | "@types/through2": "2.0.33", 95 | "@types/update-notifier": "2.0.0" 96 | }, 97 | "browser": { 98 | "fs": false, 99 | "child_process": false 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <node-package> 2 | ==== 3 | [![Version Status](https://img.shields.io/npm/v/node-package.svg?style=flat&label=version&colorB=4bc524)](https://npmjs.com/package/node-package) 4 | [![macOS Build Status](https://img.shields.io/circleci/project/github/abraham/node-package.svg?style=flat&label=macos)](https://circleci.com/gh/abraham/node-package) 5 | [![Linux Build Status](https://img.shields.io/travis/abraham/node-package.svg?style=flat&label=linux)](https://travis-ci.org/abraham/node-package) 6 | [![Windows Build Status](https://img.shields.io/appveyor/ci/abraham/node-package.svg?style=flat&label=windows)](https://ci.appveyor.com/project/abraham/node-package) 7 | [![Dependency Status](https://david-dm.org/abraham/node-package.svg?style=flat)](https://david-dm.org/abraham/node-package) 8 | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/node-package.svg?style=flat&colorB=4bc524)](https://bundlephobia.com/result?p=node-package) 9 | 10 | Install 11 | ---- 12 | 13 | Polyfill tags if you need them. This will include ShadowDOM and Custom Elements support. 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | Loading this component. It would be a good idea to use a specific version instead of `latest`. 20 | 21 | ``` 22 | 23 | ``` 24 | 25 | Example 26 | ---- 27 | 28 | [Live demo](https://codepen.io/abraham/pen/eVVJrM) 29 | 30 | Usage 31 | ---- 32 | 33 | Set the `name` attribute to the name of an [NPM](https://www.npmjs.com/) package. 34 | 35 | ``` 36 | 37 | ``` 38 | 39 | ![Example](https://github.com/abraham/node-package/raw/master/images/default.png) 40 | 41 | Add the `global` attribute to add `--global` to the NPM install command. 42 | 43 | ``` 44 | 45 | ``` 46 | 47 | Will result in `npm install @angular/cli --global`. 48 | 49 | Theming 50 | ---- 51 | 52 | For advanced theming you can set the following CSS custom properties: 53 | 54 | - `--node-package-background-color` 55 | - `--node-package-color` 56 | - `--node-package-link-color` 57 | 58 | Blue theme 59 | 60 | ``` 61 | 68 | 69 | ``` 70 | 71 | ![Example with blue theme](https://github.com/abraham/node-package/raw/master/images/custom-theme-blue.png) 72 | 73 | Red theme 74 | 75 | ``` 76 | 83 | 84 | ``` 85 | 86 | ![Example with red theme](https://github.com/abraham/node-package/raw/master/images/custom-theme-red.png) 87 | 88 | Card border 89 | 90 | You can also apply custom edge designs to look more like a card. 91 | 92 | ``` 93 | 100 | 101 | ``` 102 | 103 | ![Example with card border](https://github.com/abraham/node-package/raw/master/images/card.png) 104 | 105 | Demo of install commands being copied. 106 | 107 | ![Example of copying install command](https://github.com/abraham/node-package/raw/master/images/copy-install.gif) 108 | 109 | License 110 | ---- 111 | 112 | NodePackage is released under an MIT license. 113 | 114 | Built, tested, and published with [Nutmeg](https://nutmeg.tools). 115 | -------------------------------------------------------------------------------- /src/success.view.ts: -------------------------------------------------------------------------------- 1 | import { html, svg, TemplateResult } from '@nutmeg/seed'; 2 | import { repeat } from 'lit-html/directives/repeat'; 3 | import { NodePackage } from './node-package'; 4 | import { InstallCommand, InstallSource, Pkg } from './pkg'; 5 | 6 | export class SuccessView { 7 | constructor(private component: NodePackage, private pkg: Pkg, private selectedInstallCommand: InstallSource) {} 8 | 9 | public get name(): string { 10 | return this.pkg.name; 11 | } 12 | 13 | public get content(): TemplateResult { 14 | return html` 15 |
16 | ${this.pkg.description} 17 |
18 | ${this.keywords} 19 | ${this.install} 20 | ${this.footer} 21 |
22 | Copied to clipboard 23 |
24 | `; 25 | } 26 | 27 | private selectInstallCommand(event: MouseEvent, command: InstallCommand): void { 28 | event.preventDefault(); 29 | this.component.selectedTab = command.id; 30 | this.component.render(); 31 | } 32 | 33 | private copyInstallCommand(event: MouseEvent): void { 34 | event.preventDefault(); 35 | (this.component.$('.command:not(.hidden)') as HTMLInputElement).select(); 36 | document.execCommand('copy'); 37 | this.component.$('#toast').classList.add('copied'); 38 | setTimeout(() => { 39 | this.component.$('#toast').classList.remove('copied'); 40 | }, 2750); 41 | } 42 | 43 | private keyword(keyword: string): TemplateResult { 44 | return html` 45 | #${keyword} 46 | `; 47 | } 48 | 49 | private get keywords(): TemplateResult { 50 | return html` 51 |
52 | ${repeat(this.pkg.keywords, keyword => keyword, (keyword, _index) => this.keyword(keyword))} 53 |
54 | `; 55 | } 56 | 57 | private get types(): TemplateResult { 58 | return html` 59 | Includes types 60 | `; 61 | } 62 | 63 | private installTab(command: InstallCommand): TemplateResult { 64 | const classes = `item tab ${this.selectedInstallCommand === command.id ? 'selected' : ''}`; 65 | return html` 66 | this.selectInstallCommand(event, command)}> 67 | 70 | 71 | `; 72 | } 73 | 74 | private installCommand(command: InstallCommand): TemplateResult { 75 | return html` 76 | 77 | `; 78 | } 79 | 80 | private get install(): TemplateResult { 81 | return html` 82 |
83 |
84 | ${repeat(this.pkg.installCommands(this.component.global), command => command.id, (command, _index) => this.installTab(command))} 85 |
86 |
87 | ${repeat(this.pkg.installCommands(this.component.global), command => command.id, (command, _index) => this.installCommand(command))} 88 | 91 |
92 |
93 | `; 94 | } 95 | 96 | private get footer(): TemplateResult { 97 | return html` 98 | 107 | `; 108 | } 109 | 110 | private get copy(): TemplateResult { 111 | return svg` 112 | 113 | 114 | 115 | 116 | `; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/pkg.ts: -------------------------------------------------------------------------------- 1 | export type InstallSource = 'npm' | 'git' | 'unpkg'; 2 | 3 | export const DEFAULT_INSTALL_SOURCE: InstallSource = 'npm'; 4 | 5 | export interface InstallCommand { 6 | command: string; 7 | id: InstallSource; 8 | } 9 | 10 | export class Pkg { 11 | constructor(private data: PackageData) { 12 | } 13 | 14 | public get description(): string { 15 | return this.data.description || ''; 16 | } 17 | 18 | public get git(): string { 19 | if (typeof this.data.repository === 'object' && this.data.repository.type === 'git') { 20 | return this.data.repository.url; 21 | } else { 22 | return ''; 23 | } 24 | } 25 | 26 | public get keywords(): string[] { 27 | return this.dirtyKeywords.map(keyword => keyword.trim()); 28 | } 29 | 30 | public get license(): string { 31 | if (typeof this.data.license === 'string') { 32 | return this.data.license; 33 | } else { 34 | return ''; 35 | } 36 | } 37 | 38 | public installCommands(global: boolean): InstallCommand[] { 39 | const commands: InstallCommand[] = [ 40 | { 41 | command: `npm install ${this.name}${global ? ' --global' : ''}`, 42 | id: 'npm', 43 | } 44 | ]; 45 | if (this.git) { 46 | commands.push({ 47 | command: `git clone ${this.git}`, 48 | id: 'git', 49 | }); 50 | } 51 | if (this.unpkg) { 52 | commands.push({ 53 | command: ``, 54 | id: 'unpkg', 55 | }); 56 | } 57 | return commands; 58 | } 59 | 60 | public get name(): string { 61 | return this.data.name; 62 | } 63 | 64 | public get types(): string { 65 | return this.data.types || this.data.typings || ''; 66 | } 67 | 68 | public get version(): string { 69 | return this.data.version; 70 | } 71 | 72 | public get unpkg(): string { 73 | return this.webpath ? new URL(`${this.name}/${this.webpath}`, 'https://unpkg.com/').href : ''; 74 | } 75 | 76 | private get dirtyKeywords(): string[] { 77 | if (!this.data.keywords) { 78 | return []; 79 | } 80 | if (typeof this.data.keywords === 'string') { 81 | return this.data.keywords.split(','); 82 | } 83 | return this.data.keywords; 84 | } 85 | 86 | private get webpath(): string { 87 | if (!!this.data.unpkg) { 88 | return this.data.unpkg; 89 | } else if (!!this.data.browser) { 90 | return this.data.browser; 91 | } else if (!!this.data.main) { 92 | return this.data.main; 93 | } else if (!!this.data.webpack) { 94 | return this.data.webpack; 95 | } else { 96 | return ''; 97 | } 98 | } 99 | } 100 | 101 | export interface PackageData { 102 | author?: string | PersonData; 103 | bin?: string | {[index: string]: string}; 104 | bugs?: string | BugsData; 105 | bundledDependencies?: DependenciesData; 106 | config?: ConfigData; 107 | contributors?: Array; 108 | cpu?: string[]; 109 | dependencies?: DependenciesData; 110 | description?: string; 111 | devDependencies?: DependenciesData; 112 | directories?: DirectoriesData; 113 | engines?: EnginesData; 114 | files?: string[]; 115 | homepage?: string; 116 | keywords?: string[] | string; 117 | license?: string | DeprecatedLicenseData; 118 | main?: string; 119 | man?: string | string[]; 120 | name: string; 121 | optionalDependencies?: DependenciesData; 122 | os?: string[]; 123 | peerDependencies?: DependenciesData; 124 | private?: boolean; 125 | publishConfig?: ConfigData; 126 | repository?: string | RepositoryData; 127 | scripts?: {[index: string]: string}; 128 | version: string; 129 | 130 | // Non-standard 131 | browser?: string; 132 | readmeFilename?: string; 133 | types?: string; 134 | typings?: string; 135 | unpkg?: string; 136 | webpack?: string; 137 | } 138 | 139 | export interface BugsData { 140 | url?: string; 141 | email?: string; 142 | } 143 | 144 | export interface DeprecatedLicenseData { 145 | type: string; 146 | url: string; 147 | } 148 | 149 | export interface PersonData { 150 | name: string; 151 | email?: string; 152 | url?: string; 153 | } 154 | 155 | export interface DirectoriesData { 156 | lib?: string; 157 | bin?: string; 158 | man?: string; 159 | doc?: string; 160 | example?: string; 161 | test?: string; 162 | } 163 | 164 | export interface RepositoryData { 165 | type: string; 166 | url: string; 167 | } 168 | 169 | export interface DependenciesData { 170 | [index: string]: string 171 | } 172 | 173 | export interface EnginesData { 174 | node: string; 175 | npm?: string; 176 | } 177 | 178 | export interface ConfigData { 179 | [index: string]: string; 180 | } 181 | -------------------------------------------------------------------------------- /src/node-package.ts: -------------------------------------------------------------------------------- 1 | import { Failure, fold, Initialized, Pending, RemoteData, Success } from '@abraham/remotedata'; 2 | import { html, property, Seed, svg, TemplateResult } from '@nutmeg/seed'; 3 | import { Api } from './api'; 4 | import { FailureView } from './failure.view'; 5 | import { PendingView } from './pending.view'; 6 | import { DEFAULT_INSTALL_SOURCE, InstallSource, Pkg } from './pkg'; 7 | import { SuccessView } from './success.view'; 8 | 9 | interface SuccessData { 10 | pkg: Pkg, 11 | selectedTab: InstallSource, 12 | } 13 | 14 | type State = RemoteData; 15 | 16 | export class NodePackage extends Seed { 17 | @property({ type: Boolean }) public global: boolean = false; 18 | @property({ type: String }) public name?: string; 19 | 20 | private api = new Api(); 21 | private state: State = new Initialized(); 22 | 23 | constructor() { 24 | super(); 25 | } 26 | 27 | /** The component instance has been inserted into the DOM. */ 28 | public connectedCallback() { 29 | super.connectedCallback(); 30 | } 31 | 32 | /** The component instance has been removed from the DOM. */ 33 | public disconnectedCallback() { 34 | super.disconnectedCallback(); 35 | } 36 | 37 | /** Watch for changes to these attributes. */ 38 | public static get observedAttributes(): string[] { 39 | return super.observedAttributes; 40 | } 41 | 42 | /** Rerender when the observed attributes change. */ 43 | public attributeChangedCallback(name: string, oldValue: any, newValue: any) { 44 | super.attributeChangedCallback(name, oldValue, newValue); 45 | } 46 | 47 | /** Styling for the component. */ 48 | public get styles(): TemplateResult { 49 | return html` 50 | 218 | `; 219 | } 220 | 221 | public get logo(): TemplateResult { 222 | return svg` 223 | 229 | `; 230 | } 231 | 232 | private get header(): TemplateResult { 233 | if (this.name) { 234 | return html` 235 | 243 | `; 244 | } else { 245 | return html` 246 | 252 | `; 253 | } 254 | } 255 | 256 | /** HTML Template for the component. */ 257 | public get template(): TemplateResult { 258 | return html` 259 |
260 | ${this.header} 261 | ${this.view(this.state)} 262 |
263 | `; 264 | } 265 | 266 | public set selectedTab(tab: InstallSource) { 267 | if (this.state instanceof Success) { 268 | this.state.data.selectedTab = tab; 269 | } 270 | } 271 | 272 | private pendingHandler(): TemplateResult { 273 | return new PendingView().content; 274 | } 275 | 276 | private initializedHandler(): TemplateResult { 277 | if (this.name) { 278 | this.fetchPackage(); 279 | return this.pendingHandler() 280 | } else { 281 | return new FailureView('Missing required value "name"').content; 282 | } 283 | } 284 | 285 | private errorHandler(error: string): TemplateResult { 286 | if (this.updateData) { this.fetchPackage(); } 287 | return new FailureView(error).content; 288 | } 289 | 290 | private successHandler(data: SuccessData): TemplateResult { 291 | if (this.updateData) { this.fetchPackage(); } 292 | return new SuccessView(this, data.pkg, data.selectedTab).content; 293 | } 294 | 295 | private get view(): (state: State) => TemplateResult { 296 | return fold( 297 | () => this.initializedHandler(), 298 | () => this.pendingHandler(), 299 | (error: string) => this.errorHandler(error), 300 | (data: SuccessData) => this.successHandler(data), 301 | ); 302 | } 303 | 304 | private async fetchPackage(): Promise { 305 | if (this.name) { 306 | this.state = new Pending(); 307 | try { 308 | const pkg = new Pkg(await this.api.fetch(this.name)); 309 | this.state = new Success({ selectedTab: DEFAULT_INSTALL_SOURCE, pkg }); 310 | } catch (error) { 311 | this.state = new Failure(error); 312 | } 313 | this.render(); 314 | } 315 | } 316 | 317 | private get updateData(): boolean { 318 | return this.state instanceof Success && this.state.data.pkg.name !== this.name; 319 | } 320 | } 321 | 322 | window.customElements.define('node-package', NodePackage); 323 | -------------------------------------------------------------------------------- /test/node-package.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { expect } from 'chai'; 3 | import * as sinon from 'sinon'; 4 | 5 | import { NodePackage } from '../src/node-package'; 6 | 7 | // Increase timeout for AppVeyor 8 | const TIMEOUT = 500; 9 | 10 | describe('', () => { 11 | let component: NodePackage; 12 | let stub: sinon.SinonStub; 13 | 14 | before(() => { 15 | const realFetch = window.fetch; 16 | stub = sinon.stub(window, 'fetch').callsFake((url: string) => { 17 | const localUrl = url.replace('https://unpkg.com/', './base/test/data/').replace('/package.json', '.json') 18 | return realFetch(localUrl); 19 | }); 20 | }); 21 | 22 | after(() => { 23 | stub.restore(); 24 | }); 25 | 26 | describe('valid package', () => { 27 | beforeEach(async () => { 28 | component = fixture(''); 29 | await sleep(TIMEOUT); 30 | }); 31 | 32 | describe('header', () => { 33 | it('renders the package name', () => { 34 | const links = component.$$('#header a') as NodeListOf; 35 | const name = links[0]; 36 | expect(name.innerText).to.eq('bluebird'); 37 | expect(name.href).to.eq('https://npmjs.com/package/bluebird'); 38 | }); 39 | 40 | it('renders the NPM logo', () => { 41 | const links = component.$$('#header a') as NodeListOf; 42 | const name = links[1]; 43 | expect(name.href).to.eq('https://npmjs.com/package/bluebird'); 44 | expect(name.querySelector('svg#logo')).to.exist; 45 | }); 46 | }); 47 | 48 | describe('description', () => { 49 | it('renders the package description', () => { 50 | const description = component.$('#description') as HTMLDivElement; 51 | expect(description.innerText).to 52 | .eq('Full featured Promises/A+ implementation with exceptionally good performance'); 53 | }); 54 | }); 55 | 56 | describe('keywords', () => { 57 | it('link to NPM search', () => { 58 | const keywords = component.$$('#keywords a') as NodeListOf; 59 | expect(keywords.length).to.eq(13); 60 | expect(keywords[0].href).to.eq('https://www.npmjs.com/browse/keyword/promise'); 61 | expect(keywords[0].innerText).to.eq('#promise'); 62 | }); 63 | 64 | describe('when a string', () => { 65 | beforeEach(async () => { 66 | component = fixture(''); 67 | await sleep(TIMEOUT); 68 | }); 69 | 70 | it('link to NPM search', () => { 71 | const keywords = component.$$('#keywords a') as NodeListOf; 72 | expect(keywords.length).to.eq(3); 73 | expect(keywords[0].href).to.eq('https://www.npmjs.com/browse/keyword/modules'); 74 | expect(keywords[0].innerText).to.eq('#modules'); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('install', () => { 80 | it('renders the tabs', () => { 81 | const items = component.$$('#tabs .tab') as NodeListOf; 82 | expect(items.length).to.eq(3); 83 | expect(items[0].innerText).to.eq('NPM'); 84 | expect(items[1].innerText).to.eq('GIT'); 85 | expect(items[2].innerText).to.eq('UNPKG'); 86 | }); 87 | 88 | it('shows one command', () => { 89 | expect(component.$$('#install .command').length).to.eq(3); 90 | expect(component.$$('#install .command.hidden').length).to.eq(2); 91 | }); 92 | 93 | it('shows commands as readonly', () => { 94 | expect(component.$$('#install .command[readonly]').length).to.eq(3); 95 | }); 96 | 97 | function assertCommand(index: number, command: string) { 98 | let commands = component.$$('#install .command') as NodeListOf; 99 | let tabs = component.$$('#tabs .tab') as NodeListOf; 100 | for (var i = 0; i < tabs.length; i++) { 101 | if (i === index) { 102 | expect(commands[i].value).to.eq(command); 103 | expect(commands[i].classList.contains('hidden')).to.be.false; 104 | expect(tabs[i].classList.contains('selected')).to.be.true; 105 | } else { 106 | expect(commands[i].classList.contains('hidden')).to.be.true; 107 | expect(tabs[i].classList.contains('selected')).to.be.false; 108 | } 109 | } 110 | } 111 | 112 | it('changes command to match tab', async () => { 113 | let tabs = component.$$('#tabs .tab') as NodeListOf; 114 | assertCommand(0, 'npm install bluebird'); 115 | tabs[1].click(); 116 | await sleep(TIMEOUT); 117 | assertCommand(1, 'git clone git://github.com/petkaantonov/bluebird.git'); 118 | tabs[2].click(); 119 | await sleep(TIMEOUT); 120 | assertCommand(2, ''); 121 | }); 122 | 123 | describe('without web or git', () => { 124 | beforeEach(async () => { 125 | component = fixture(''); 126 | await sleep(TIMEOUT); 127 | }); 128 | 129 | it('renders the tabs', () => { 130 | const items = component.$$('#tabs .tab') as NodeListOf; 131 | expect(items.length).to.eq(1); 132 | expect(items[0].innerText).to.eq('NPM'); 133 | }); 134 | }); 135 | 136 | describe('copy command', () => { 137 | it('copies command', async () => { 138 | const copy = sinon.spy(document, 'execCommand'); 139 | const input = component.$('#npm') as HTMLInputElement; 140 | component.$('#copy').click(); 141 | expect(copy.called).to.be.true; 142 | expect(input.selectionStart).to.eq(0); 143 | expect(input.selectionEnd).to.eq(20); 144 | expect(component.$('#toast').classList.contains('copied')).to.be.true; 145 | await sleep(2750); 146 | expect(component.$('#toast').classList.contains('copied')).to.be.false; 147 | }).timeout(5000); 148 | }); 149 | 150 | describe('with global', () => { 151 | beforeEach(async () => { 152 | component = fixture(''); 153 | await sleep(TIMEOUT); 154 | }); 155 | 156 | it('renders --global', () => { 157 | assertCommand(0, 'npm install @angular/cli --global'); 158 | }); 159 | }); 160 | }); 161 | 162 | describe('footer', () => { 163 | it('renders the details', () => { 164 | const items = component.$$('#footer .item') as NodeListOf; 165 | expect(items.length).to.eq(2); 166 | expect(items[0].innerText).to.eq('v3.5.1'); 167 | expect(items[1].innerText).to.eq('MIT'); 168 | }); 169 | 170 | describe('with types', () => { 171 | beforeEach(async () => { 172 | component = fixture(''); 173 | await sleep(TIMEOUT); 174 | }); 175 | 176 | it('renders types', () => { 177 | const items = component.$$('#footer .item') as NodeListOf; 178 | expect(items.length).to.eq(3); 179 | expect(items[1].innerText).to.eq('Includes types'); 180 | }); 181 | }); 182 | }); 183 | 184 | describe('theme', () => { 185 | describe('with default colors', () => { 186 | beforeEach(async () => { 187 | component = fixture(''); 188 | await sleep(TIMEOUT); 189 | }); 190 | 191 | it('is pretty', () => { 192 | expect(getComputedStyle(component.$('#content')).backgroundColor).equal('rgb(255, 255, 255)'); 193 | expect(getComputedStyle(component.$('#header')).color).equal('rgb(32, 33, 36)'); 194 | expect(getComputedStyle(component.$('#description')).color).equal('rgb(32, 33, 36)'); 195 | expect(getComputedStyle(component.$('#keywords .keyword')).color).equal('rgb(203, 56, 55)'); 196 | expect(getComputedStyle(component.$('#tabs .tab')).backgroundColor).equal('rgb(255, 255, 255)'); 197 | expect(getComputedStyle(component.$('#tabs .tab')).color).equal('rgb(203, 56, 55)'); 198 | expect(getComputedStyle(component.$('#install .command')).backgroundColor).equal('rgb(218, 220, 224)'); 199 | expect(getComputedStyle(component.$('#install .command')).color).equal('rgb(32, 33, 36)'); 200 | expect(getComputedStyle(component.$('#footer')).color).equal('rgb(32, 33, 36)'); 201 | }); 202 | }); 203 | 204 | describe('with custom colors', () => { 205 | beforeEach(async () => { 206 | component = fixture(` 207 |
208 | 215 | 216 |
217 | `).querySelector('node-package') as NodePackage; 218 | await sleep(TIMEOUT); 219 | }); 220 | 221 | it('is pretty', () => { 222 | expect(getComputedStyle(component.$('#content')).backgroundColor).equal('rgb(3, 169, 244)'); 223 | expect(getComputedStyle(component.$('#header')).color).equal('rgb(255, 255, 255)'); 224 | expect(getComputedStyle(component.$('#description')).color).equal('rgb(255, 255, 255)'); 225 | expect(getComputedStyle(component.$('#keywords .keyword')).color).equal('rgb(218, 220, 224)'); 226 | expect(getComputedStyle(component.$('#tabs .tab')).backgroundColor).equal('rgb(3, 169, 244)'); 227 | expect(getComputedStyle(component.$('#tabs .tab')).color).equal('rgb(218, 220, 224)'); 228 | expect(getComputedStyle(component.$('#install .command')).backgroundColor).equal('rgb(255, 255, 255)'); 229 | expect(getComputedStyle(component.$('#install .command')).color).equal('rgb(3, 169, 244)'); 230 | expect(getComputedStyle(component.$('#footer')).color).equal('rgb(255, 255, 255)'); 231 | }); 232 | }); 233 | }); 234 | }); 235 | 236 | describe('error', () => { 237 | beforeEach(async () => { 238 | stub.callsFake(() => { 239 | return new Response('Cannot find package 404@0.0.0', { status: 404 }); 240 | }); 241 | component = fixture(''); 242 | await sleep(TIMEOUT); 243 | }); 244 | 245 | afterEach(() => { 246 | stub.restore(); 247 | }); 248 | 249 | it('renders error message', () => { 250 | expect(component.$('#error').innerText).to.eq('Cannot find package 404@0.0.0'); 251 | }); 252 | }); 253 | 254 | describe('missing value', () => { 255 | beforeEach(async () => { 256 | component = fixture(''); 257 | }); 258 | 259 | it('renders error message', () => { 260 | expect(component.$('#error').innerText).to.eq('Missing required value "name"'); 261 | }); 262 | }); 263 | }); 264 | 265 | function fixture(tag: string): NodePackage { 266 | function fixtureContainer(): HTMLElement { 267 | let div = document.createElement('div'); 268 | div.classList.add('fixture'); 269 | return div; 270 | } 271 | let fixture = document.body.querySelector('.fixture') || document.body.appendChild(fixtureContainer()); 272 | fixture.innerHTML = tag; 273 | return fixture.children[0] as NodePackage; 274 | } 275 | 276 | function sleep(ms: number): Promise<{}> { 277 | return new Promise(resolve => setTimeout(resolve, ms)); 278 | } 279 | --------------------------------------------------------------------------------