├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── bin └── cli.js ├── bower.json ├── example.js ├── index.js ├── lib ├── keys.js ├── merge.js ├── normalizers │ ├── bin.js │ ├── bugs.js │ ├── engineStrict.js │ ├── files.js │ ├── helpers │ │ ├── git.js │ │ └── owner.js │ ├── homepage.js │ ├── index.js │ ├── keywords.js │ ├── license.js │ ├── licenses.js │ ├── main.js │ ├── name.js │ ├── person.js │ ├── repository.js │ ├── scripts.js │ └── typings.js ├── schema.js ├── utils.js └── validators │ ├── index.js │ ├── name.js │ ├── preferGlobal.js │ └── version.js ├── package.json └── test ├── .eslintrc.json ├── actual └── normalized.json ├── fixtures ├── author-condensed.json ├── authors-condensed.json ├── bin.json ├── engine-strict.json ├── licenses.json ├── main.json ├── missing.json ├── package.json ├── people-condensed.json ├── people.json ├── project-bin │ ├── bin │ │ └── foo.js │ ├── cli.js │ ├── main.js │ └── package.json ├── project-no-git │ ├── main.js │ └── package.json ├── project-no-package │ └── main.js ├── project │ ├── main.js │ └── package.json ├── verb.json └── wrong.json ├── support └── git.js ├── test-bin.js ├── test-no-git.js ├── test-no-package.js ├── test.js └── utils.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 12 | trim_trailing_whitespace = false 13 | insert_final_newline = false -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | 6 | "env": { 7 | "es6": true, 8 | "node": true 9 | }, 10 | 11 | "parserOptions": { 12 | "ecmaVersion": 9 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-parens": [2, "as-needed"], 18 | "arrow-spacing": [2, { "before": true, "after": true }], 19 | "block-spacing": [2, "always"], 20 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 21 | "comma-dangle": [2, "never"], 22 | "comma-spacing": [2, { "before": false, "after": true }], 23 | "comma-style": [2, "last"], 24 | "constructor-super": 2, 25 | "curly": [2, "multi-line"], 26 | "dot-location": [2, "property"], 27 | "eol-last": 2, 28 | "eqeqeq": [2, "allow-null"], 29 | "generator-star-spacing": [2, { "before": true, "after": true }], 30 | "handle-callback-err": [2, "^(err|error)$"], 31 | "indent": [2, 2, { "SwitchCase": 1 }], 32 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 33 | "keyword-spacing": [2, { "before": true, "after": true }], 34 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 35 | "new-parens": 2, 36 | "no-array-constructor": 2, 37 | "no-caller": 2, 38 | "no-class-assign": 2, 39 | "no-cond-assign": 2, 40 | "no-const-assign": 2, 41 | "no-control-regex": 2, 42 | "no-debugger": 2, 43 | "no-delete-var": 2, 44 | "no-dupe-args": 2, 45 | "no-dupe-class-members": 2, 46 | "no-dupe-keys": 2, 47 | "no-duplicate-case": 2, 48 | "no-empty-character-class": 2, 49 | "no-eval": 2, 50 | "no-ex-assign": 2, 51 | "no-extend-native": 2, 52 | "no-extra-bind": 2, 53 | "no-extra-boolean-cast": 2, 54 | "no-extra-parens": [2, "functions"], 55 | "no-fallthrough": 2, 56 | "no-floating-decimal": 2, 57 | "no-func-assign": 2, 58 | "no-implied-eval": 2, 59 | "no-implicit-coercion": 2, 60 | "no-inner-declarations": [2, "functions"], 61 | "no-invalid-regexp": 2, 62 | "no-irregular-whitespace": 2, 63 | "no-iterator": 2, 64 | "no-label-var": 2, 65 | "no-labels": 2, 66 | "no-lone-blocks": 2, 67 | "no-lonely-if": 2, 68 | "no-mixed-spaces-and-tabs": 2, 69 | "no-multi-spaces": 0, 70 | "no-multi-str": 2, 71 | "no-multiple-empty-lines": [2, { "max": 1 }], 72 | "no-native-reassign": 2, 73 | "no-negated-in-lhs": 2, 74 | "no-new": 2, 75 | "no-new-func": 2, 76 | "no-new-object": 2, 77 | "no-new-require": 2, 78 | "no-new-wrappers": 2, 79 | "no-obj-calls": 2, 80 | "no-octal": 2, 81 | "no-octal-escape": 2, 82 | "no-proto": 2, 83 | "no-redeclare": 2, 84 | "no-regex-spaces": 2, 85 | "no-return-assign": 2, 86 | "no-self-compare": 2, 87 | "no-sequences": 2, 88 | "no-shadow-restricted-names": 2, 89 | "no-spaced-func": 2, 90 | "no-sparse-arrays": 2, 91 | "no-this-before-super": 2, 92 | "no-throw-literal": 2, 93 | "no-trailing-spaces": 2, 94 | "no-undef": 2, 95 | "no-undef-init": 2, 96 | "no-unexpected-multiline": 2, 97 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 98 | "no-unreachable": 2, 99 | "no-unused-expressions": 2, 100 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 101 | "no-useless-call": 2, 102 | "no-with": 2, 103 | "object-curly-spacing": ["error", "always", { "objectsInObjects": true }], 104 | "one-var": [2, { "initialized": "never" }], 105 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 106 | "padded-blocks": [0, "never"], 107 | "prefer-const": [2, { "destructuring": "all", "ignoreReadBeforeAssign": false }], 108 | "quotes": [2, "single", "avoid-escape"], 109 | "radix": 2, 110 | "semi": [2, "always"], 111 | "semi-spacing": [2, { "before": false, "after": true }], 112 | "space-before-blocks": [2, "always"], 113 | "space-before-function-paren": [2, { "anonymous": "never", "named": "never", "asyncArrow": "always" }], 114 | "space-in-parens": [2, "never"], 115 | "space-infix-ops": 2, 116 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 117 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 118 | "strict": 2, 119 | "use-isnan": 2, 120 | "valid-typeof": 2, 121 | "wrap-iife": [2, "any"], 122 | "yoda": [2, "never"] 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | *.* text eol=lf 3 | *.css text eol=lf 4 | *.html text eol=lf 5 | *.js text eol=lf 6 | *.json text eol=lf 7 | *.less text eol=lf 8 | *.md text eol=lf 9 | *.yml text eol=lf 10 | 11 | *.jpg binary 12 | *.gif binary 13 | *.png binary 14 | *.jpeg binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '7' 9 | - '6' 10 | - '5' 11 | - '4' 12 | - '0.12' 13 | - '0.10' 14 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Install 2 | 3 | {%= include("install-bower", {save: true}) %} 4 | 5 | ## Usage 6 | 7 | ```js 8 | var config = require('./')(); 9 | var pkg = config.normalize(require('./package')); 10 | ``` 11 | 12 | ## Features 13 | 14 | Normalizes most package.json fields, and: 15 | 16 | - converts `repository` objects to a string 17 | - stringifies `author` object 18 | - stringifies each "person" object in `maintainers`, `contributors` and `collaborators` 19 | - converts `licenses` arrays and objects to a `license` string 20 | - removes files that don't exist from `bin`, `main` and the `files` array 21 | - adds `cli.js` to `bin` if it exists 22 | - creates `keywords` array from `name` if not defined 23 | 24 | See the [schema](lib/schema.js), [normalizers](lib/normalizers), and [unit tests](test) for more examples. 25 | 26 | ## Schema 27 | 28 | Values are normalized using a [schema](lib/schema.js) that is passed to [map-schema][]. 29 | 30 | - only properties that have a corresponding field on the schema will be normalized. 31 | - any properties that do not have a corresponding field are returned unmodified. 32 | 33 | See the [.field docs](#field) to learn how to add or overwrite a field on the schema. 34 | 35 | ## Defaults 36 | 37 | A `default` value may optionally be defined when a `.field` is registered. When `.normalize` is run and a property that is required or recommended by npm is missing, `normalize-pkg` attempts to create the field if valid data can be found in the repository. 38 | 39 | built-in fields have a default value: 40 | 41 | - `version`: `'0.1.0'` 42 | - `license`: `'MIT'` 43 | - `engines`: `{node: '>= 0.10.0'}` 44 | 45 | For example: 46 | 47 | - `name`: the [project-name][] library is used to fill in the name 48 | - `bin`: if empty, populated with `cli.js` or `bin` if either exists on the file system 49 | 50 | **Example** 51 | 52 | The following: 53 | 54 | ```js 55 | var config = require('./')(); 56 | 57 | // no package.json is passed, just an empty object 58 | var pkg = config.normalize({}); 59 | console.log(pkg); 60 | ``` 61 | 62 | **Results** 63 | 64 | Since an empty object was passed, `normalize-pkg` was smart enough to fill in missing fields looking for info in the project. In this case, specifically from parsing `.git` config and using any defaults defined on the schema. 65 | 66 | ```js 67 | { name: 'normalize-pkg', 68 | version: '0.1.0', 69 | homepage: 'https://github.com/jonschlinkert/normalize-pkg', 70 | repository: 'jonschlinkert/normalize-pkg', 71 | license: 'MIT', 72 | files: [ 'index.js' ], 73 | main: 'index.js', 74 | engines: { node: '>= 0.10.0' } } 75 | ``` 76 | 77 | ## API 78 | {%= apidocs("index.js") %} 79 | 80 | ## Options 81 | 82 | ### options.knownOnly 83 | 84 | **Type**: `boolean` 85 | 86 | **Default**: `undefined` 87 | 88 | Omit properties from package.json that do not have a field registered on the schema. 89 | 90 | ```js 91 | var Config = require('{%= name %}'); 92 | var config = new Config({knownOnly: true}); 93 | 94 | var pkg = config.normalize({name: 'my-project', foo: 'bar'}); 95 | console.log(pkg); 96 | //=> {name: 'my-project'} 97 | ``` 98 | 99 | ### options.pick 100 | 101 | **Type**: `array` 102 | 103 | **Default**: `undefined` 104 | 105 | Filter the resulting object to contain only the specified keys. 106 | 107 | ### options.omit 108 | 109 | **Type**: `array` 110 | 111 | **Default**: `undefined` 112 | 113 | Remove the specified keys from the resulting object. 114 | 115 | ### options.fields 116 | 117 | Pass a `fields` object on the options to customize any fields on the schema (also see [options.extend](#options-extend)): 118 | 119 | ```js 120 | var pkg = config.normalize(require('./package'), { 121 | extend: true, 122 | fields: { 123 | name: { 124 | normalize: function() { 125 | return 'bar' 126 | } 127 | } 128 | } 129 | }); 130 | 131 | console.log(pkg.name); 132 | //=> 'bar' 133 | ``` 134 | 135 | ### options.extend 136 | 137 | **Type**: `boolean` 138 | 139 | **Default**: `undefined` 140 | 141 | Used with [options.field](#options-field), pass ` true` if you want to extend a field that is already defined on the schema. 142 | 143 | ```js 144 | var pkg = config.normalize(require('./package'), { 145 | extend: true, 146 | fields: { 147 | name: { 148 | normalize: function() { 149 | return 'bar' 150 | } 151 | } 152 | } 153 | }); 154 | 155 | console.log(pkg.name); 156 | //=> 'bar' 157 | ``` 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-present, Jon Schlinkert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # normalize-pkg [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/normalize-pkg.svg?style=flat)](https://www.npmjs.com/package/normalize-pkg) [![NPM monthly downloads](https://img.shields.io/npm/dm/normalize-pkg.svg?style=flat)](https://npmjs.org/package/normalize-pkg) [![NPM total downloads](https://img.shields.io/npm/dt/normalize-pkg.svg?style=flat)](https://npmjs.org/package/normalize-pkg) [![Build Status](https://travis-ci.org/jonschlinkert/normalize-pkg.svg?branch=master)](https://travis-ci.org/jonschlinkert/normalize-pkg) 2 | 3 | > Normalize values in package.json using the map-schema library. 4 | 5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. 6 | 7 | ## Table of Contents 8 | 9 |
10 | Details 11 | 12 | - [Install](#install) 13 | - [Install](#install-1) 14 | - [Usage](#usage) 15 | - [Features](#features) 16 | - [Schema](#schema) 17 | - [Defaults](#defaults) 18 | - [API](#api) 19 | - [Options](#options) 20 | * [options.knownOnly](#optionsknownonly) 21 | * [options.pick](#optionspick) 22 | * [options.omit](#optionsomit) 23 | * [options.fields](#optionsfields) 24 | * [options.extend](#optionsextend) 25 | - [About](#about) 26 | 27 |
28 | 29 | ## Install 30 | 31 | Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >= 0.10.0): 32 | 33 | ```sh 34 | $ npm install --save normalize-pkg 35 | ``` 36 | 37 | ## Install 38 | 39 | Install with [bower](https://bower.io/) 40 | 41 | ```sh 42 | $ bower install normalize-pkg --save 43 | ``` 44 | 45 | ## Usage 46 | 47 | ```js 48 | var config = require('./')(); 49 | var pkg = config.normalize(require('./package')); 50 | ``` 51 | 52 | ## Features 53 | 54 | Normalizes most package.json fields, and: 55 | 56 | * converts `repository` objects to a string 57 | * stringifies `author` object 58 | * stringifies each "person" object in `maintainers`, `contributors` and `collaborators` 59 | * converts `licenses` arrays and objects to a `license` string 60 | * removes files that don't exist from `bin`, `main` and the `files` array 61 | * adds `cli.js` to `bin` if it exists 62 | * creates `keywords` array from `name` if not defined 63 | 64 | See the [schema](lib/schema.js), [normalizers](lib/normalizers), and [unit tests](test) for more examples. 65 | 66 | ## Schema 67 | 68 | Values are normalized using a [schema](lib/schema.js) that is passed to [map-schema](https://github.com/jonschlinkert/map-schema). 69 | 70 | * only properties that have a corresponding field on the schema will be normalized. 71 | * any properties that do not have a corresponding field are returned unmodified. 72 | 73 | See the [.field docs](#field) to learn how to add or overwrite a field on the schema. 74 | 75 | ## Defaults 76 | 77 | A `default` value may optionally be defined when a `.field` is registered. When `.normalize` is run and a property that is required or recommended by npm is missing, `normalize-pkg` attempts to create the field if valid data can be found in the repository. 78 | 79 | built-in fields have a default value: 80 | 81 | * `version`: `'0.1.0'` 82 | * `license`: `'MIT'` 83 | * `engines`: `{node: '>= 0.10.0'}` 84 | 85 | For example: 86 | 87 | * `name`: the [project-name](https://github.com/jonschlinkert/project-name) library is used to fill in the name 88 | * `bin`: if empty, populated with `cli.js` or `bin` if either exists on the file system 89 | 90 | **Example** 91 | 92 | The following: 93 | 94 | ```js 95 | var config = require('./')(); 96 | 97 | // no package.json is passed, just an empty object 98 | var pkg = config.normalize({}); 99 | console.log(pkg); 100 | ``` 101 | 102 | **Results** 103 | 104 | Since an empty object was passed, `normalize-pkg` was smart enough to fill in missing fields looking for info in the project. In this case, specifically from parsing `.git` config and using any defaults defined on the schema. 105 | 106 | ```js 107 | { name: 'normalize-pkg', 108 | version: '0.1.0', 109 | homepage: 'https://github.com/jonschlinkert/normalize-pkg', 110 | repository: 'jonschlinkert/normalize-pkg', 111 | license: 'MIT', 112 | files: [ 'index.js' ], 113 | main: 'index.js', 114 | engines: { node: '>= 0.10.0' } } 115 | ``` 116 | 117 | ## API 118 | 119 | **Params** 120 | 121 | * `options` **{Object}** 122 | 123 | **Example** 124 | 125 | ```js 126 | const config = new NormalizePkg(); 127 | const pkg = config.normalize({ 128 | author: { 129 | name: 'Jon Schlinkert', 130 | url: 'https://github.com/jonschlinkert' 131 | } 132 | }); 133 | console.log(pkg); 134 | //=> {author: 'Jon Schlinkert (https://github.com/jonschlinkert)'} 135 | ``` 136 | 137 | * `normalize` **{Function}**: function to be called on the value when the `.normalize` method is called 138 | * `default` **{any}**: default value to be used when the package.json property is undefined. 139 | * `required` **{Boolean}**: define `true` if the property is required 140 | 141 | **Params** 142 | 143 | * `name` **{String}**: Field name (required) 144 | * `type` **{String|Array}**: One or more native javascript types allowed for the property value (required) 145 | * `options` **{Object}** 146 | * `returns` **{Object}**: Returns the instance 147 | 148 | **Example** 149 | 150 | ```js 151 | const config = new NormalizePkg(); 152 | 153 | config.field('foo', 'string', { 154 | default: 'bar' 155 | }); 156 | 157 | const pkg = config.normalize({}); 158 | console.log(pkg); 159 | //=> {foo: 'bar'} 160 | ``` 161 | 162 | **Params** 163 | 164 | * `pkg` **{Object}**: The `package.json` object to normalize 165 | * `options` **{Object}** 166 | * `returns` **{Object}**: Returns a normalized package.json object. 167 | 168 | **Example** 169 | 170 | ```js 171 | const config = new NormalizePkg(); 172 | const pkg = config.normalize(require('./package.json')); 173 | ``` 174 | 175 | ## Options 176 | 177 | ### options.knownOnly 178 | 179 | **Type**: `boolean` 180 | 181 | **Default**: `undefined` 182 | 183 | Omit properties from package.json that do not have a field registered on the schema. 184 | 185 | ```js 186 | var Config = require('normalize-pkg'); 187 | var config = new Config({knownOnly: true}); 188 | 189 | var pkg = config.normalize({name: 'my-project', foo: 'bar'}); 190 | console.log(pkg); 191 | //=> {name: 'my-project'} 192 | ``` 193 | 194 | ### options.pick 195 | 196 | **Type**: `array` 197 | 198 | **Default**: `undefined` 199 | 200 | Filter the resulting object to contain only the specified keys. 201 | 202 | ### options.omit 203 | 204 | **Type**: `array` 205 | 206 | **Default**: `undefined` 207 | 208 | Remove the specified keys from the resulting object. 209 | 210 | ### options.fields 211 | 212 | Pass a `fields` object on the options to customize any fields on the schema (also see [options.extend](#options-extend)): 213 | 214 | ```js 215 | var pkg = config.normalize(require('./package'), { 216 | extend: true, 217 | fields: { 218 | name: { 219 | normalize: function() { 220 | return 'bar' 221 | } 222 | } 223 | } 224 | }); 225 | 226 | console.log(pkg.name); 227 | //=> 'bar' 228 | ``` 229 | 230 | ### options.extend 231 | 232 | **Type**: `boolean` 233 | 234 | **Default**: `undefined` 235 | 236 | Used with [options.field](#options-field), pass `true` if you want to extend a field that is already defined on the schema. 237 | 238 | ```js 239 | var pkg = config.normalize(require('./package'), { 240 | extend: true, 241 | fields: { 242 | name: { 243 | normalize: function() { 244 | return 'bar' 245 | } 246 | } 247 | } 248 | }); 249 | 250 | console.log(pkg.name); 251 | //=> 'bar' 252 | ``` 253 | 254 | ## About 255 | 256 |
257 | Contributing 258 | 259 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 260 | 261 |
262 | 263 |
264 | Running Tests 265 | 266 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 267 | 268 | ```sh 269 | $ npm install && npm test 270 | ``` 271 | 272 |
273 | 274 |
275 | Building docs 276 | 277 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 278 | 279 | To generate the readme, run the following command: 280 | 281 | ```sh 282 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 283 | ``` 284 | 285 |
286 | 287 | ### Related projects 288 | 289 | You might also be interested in these projects: 290 | 291 | [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.") 292 | 293 | ### Contributors 294 | 295 | | **Commits** | **Contributor** | 296 | | --- | --- | 297 | | 154 | [jonschlinkert](https://github.com/jonschlinkert) | 298 | | 16 | [doowb](https://github.com/doowb) | 299 | | 2 | [pdehaan](https://github.com/pdehaan) | 300 | 301 | ### Author 302 | 303 | **Jon Schlinkert** 304 | 305 | * [GitHub Profile](https://github.com/jonschlinkert) 306 | * [Twitter Profile](https://twitter.com/jonschlinkert) 307 | * [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) 308 | 309 | ### License 310 | 311 | Copyright © 2020, [Jon Schlinkert](https://github.com/jonschlinkert). 312 | Released under the [MIT License](LICENSE). 313 | 314 | *** 315 | 316 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on March 01, 2020._ -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path'); 4 | const argv = require('minimist')(process.argv.slice(2)); 5 | const writeJson = require('write-json'); 6 | const Normalizer = require('..'); 7 | const config = new Normalizer(); 8 | 9 | /** 10 | * Optionally specific a destination path 11 | */ 12 | 13 | const dest = argv.dest || argv.d || 'package.json'; 14 | 15 | /** 16 | * Write the file 17 | */ 18 | 19 | writeJson(dest, config.normalize(), err => { 20 | if (err) { 21 | console.error(err); 22 | process.exit(1); 23 | } 24 | console.log('done'); 25 | }); 26 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-pkg", 3 | "description": "Normalize values in package.json.", 4 | "repository": "jonschlinkert/normalize-pkg", 5 | "license": "MIT", 6 | "homepage": "https://github.com/jonschlinkert/normalize-pkg", 7 | "authors": [ 8 | "Jon Schlinkert (https://github.com/jonschlinkert)" 9 | ], 10 | "main": [ 11 | "index.js" 12 | ], 13 | "dependencies": { 14 | "arr-union": "^3.1.0", 15 | "array-unique": "^0.2.1", 16 | "component-emitter": "^1.2.0", 17 | "define-property": "^0.2.5", 18 | "export-files": "^2.1.1", 19 | "get-value": "^2.0.3", 20 | "is-primitive": "^2.0.0", 21 | "kind-of": "^3.0.2", 22 | "lazy-cache": "^1.0.3", 23 | "map-schema": "^0.2.1", 24 | "minimist": "^1.2.0", 25 | "mixin-deep": "^1.1.3", 26 | "omit-empty": "^0.3.3", 27 | "parse-git-config": "^0.4.0", 28 | "parse-github-url": "^0.3.0", 29 | "project-name": "^0.2.4", 30 | "remote-origin-url": "^0.5.1", 31 | "resolve-dir": "^0.1.0", 32 | "semver": "^5.1.0", 33 | "stringify-author": "^0.1.3", 34 | "write-json": "^0.2.2" 35 | }, 36 | "devDependencies": { 37 | "delete": "^0.3.0", 38 | "gitty": "^3.3.4", 39 | "gulp": "^3.9.1", 40 | "gulp-eslint": "^2.0.0", 41 | "gulp-format-md": "^0.1.7", 42 | "gulp-istanbul": "^0.10.3", 43 | "gulp-mocha": "^2.2.0", 44 | "mocha": "^2.4.5" 45 | }, 46 | "keywords": [ 47 | "config", 48 | "fix", 49 | "json", 50 | "normalize", 51 | "package", 52 | "package-json", 53 | "pkg", 54 | "properties", 55 | "values" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Normalizer = require('./'); 4 | const config = new Normalizer(); 5 | 6 | console.log(JSON.stringify(config.normalize(require('./test/fixtures/people')), null, 2)); 7 | 8 | console.log(JSON.stringify(config.normalize(require('./test/fixtures/bin'), { 9 | only: ['bin', 'name'] 10 | }), null, 2)); 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const Events = require('events'); 5 | const schema = require('./lib/schema'); 6 | const utils = require('./lib/utils'); 7 | 8 | /** 9 | * Create an instance of `NormalizePkg` with the given `options`. 10 | * 11 | * ```js 12 | * const config = new NormalizePkg(); 13 | * const pkg = config.normalize({ 14 | * author: { 15 | * name: 'Jon Schlinkert', 16 | * url: 'https://github.com/jonschlinkert' 17 | * } 18 | * }); 19 | * console.log(pkg); 20 | * //=> {author: 'Jon Schlinkert (https://github.com/jonschlinkert)'} 21 | * ``` 22 | * @param {Object} `options` 23 | * @api public 24 | */ 25 | 26 | class NormalizePkg extends Events { 27 | constructor(options) { 28 | super(); 29 | this.options = { ...options }; 30 | this.schema = schema(this.options); 31 | this.data = this.schema.data; 32 | this.schema.on('warning', this.emit.bind(this, 'warning')); 33 | this.schema.on('error', this.emit.bind(this, 'error')); 34 | 35 | this.schema.union = (key, config, arr) => { 36 | config[key] = utils.arrayify(config[key]); 37 | config[key] = utils.union([], config[key], utils.arrayify(arr)); 38 | }; 39 | } 40 | 41 | /** 42 | * Add a field to the schema, or overwrite or extend an existing field. The last 43 | * argument is an `options` object that supports the following properties: 44 | * 45 | * - `normalize` **{Function}**: function to be called on the value when the `.normalize` method is called 46 | * - `default` **{any}**: default value to be used when the package.json property is undefined. 47 | * - `required` **{Boolean}**: define `true` if the property is required 48 | * 49 | * ```js 50 | * const config = new NormalizePkg(); 51 | * 52 | * config.field('foo', 'string', { 53 | * default: 'bar' 54 | * }); 55 | * 56 | * const pkg = config.normalize({}); 57 | * console.log(pkg); 58 | * //=> {foo: 'bar'} 59 | * ``` 60 | * 61 | * @param {String} `name` Field name (required) 62 | * @param {String|Array} `type` One or more native javascript types allowed for the property value (required) 63 | * @param {Object} `options` 64 | * @return {Object} Returns the instance 65 | * @api public 66 | */ 67 | 68 | field(field, type, options) { 69 | if (typeof options === 'function') { 70 | options = { normalize: options }; 71 | } 72 | options = options || {}; 73 | if (options.extend === true) { 74 | options = utils.merge({}, this.schema.get(field), options); 75 | } 76 | this.schema.field(field, type, options); 77 | return this; 78 | } 79 | 80 | /** 81 | * Iterate over `pkg` properties and normalize values that have corresponding 82 | * [fields](#field) registered on the schema. 83 | * 84 | * ```js 85 | * const config = new NormalizePkg(); 86 | * const pkg = config.normalize(require('./package.json')); 87 | * ``` 88 | * @param {Object} `pkg` The `package.json` object to normalize 89 | * @param {Object} `options` 90 | * @return {Object} Returns a normalized package.json object. 91 | * @api public 92 | */ 93 | 94 | normalize(pkg, options) { 95 | if (typeof pkg === 'undefined') { 96 | pkg = path.resolve(process.cwd(), 'package.json'); 97 | } 98 | if (typeof pkg === 'string') { 99 | pkg = utils.requirePackage(pkg); 100 | } 101 | 102 | if (options && options.fields) { 103 | const fields = options.fields; 104 | delete options.fields; 105 | 106 | for (const key in fields) { 107 | if (fields.hasOwnProperty(key)) { 108 | const val = utils.merge({}, options, fields[key]); 109 | this.field(key, val.type, val); 110 | } 111 | } 112 | } 113 | 114 | this.schema.options = utils.merge({}, this.schema.options, options); 115 | const obj = this.schema.normalize(pkg, this.schema.options); 116 | this.schema.emit('normalized', obj); 117 | this.emit('normalized', obj); 118 | return obj; 119 | } 120 | } 121 | 122 | /** 123 | * NormalizePkg 124 | */ 125 | 126 | module.exports = NormalizePkg; 127 | -------------------------------------------------------------------------------- /lib/keys.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = [ 4 | 'name', 5 | 'private', 6 | 'description', 7 | 'version', 8 | 'homepage', 9 | 10 | 'author', 11 | 'authors', 12 | 'maintainers', 13 | 'contributors', 14 | 'collaborators', 15 | 16 | 'repository', 17 | 'bugs', 18 | 19 | 'license', 20 | 'licenses', 21 | 22 | 'files', 23 | 'main', 24 | 'typings', 25 | 'types', 26 | 'preferGlobal', 27 | 'bin', 28 | 'engines', 29 | 'scripts', 30 | 'dependencies', 31 | 'devDependencies', 32 | 'keywords' 33 | ]; 34 | -------------------------------------------------------------------------------- /lib/merge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const keys = require('./keys'); 4 | const { pick, merge } = require('./utils'); 5 | 6 | module.exports = function(config, schema) { 7 | if (schema.isMerged) return; 8 | 9 | schema.checked = schema.checked || {}; 10 | schema.isMerged = true; 11 | 12 | // shallow clone schema.data 13 | const schemaOpts = pick(schema.options, keys); 14 | const data = Object.assign({}, schema.data); 15 | const obj = merge({}, config, data, schemaOpts); 16 | merge(config, obj); 17 | }; 18 | -------------------------------------------------------------------------------- /lib/normalizers/bin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const utils = require('../utils'); 6 | const merge = require('../merge'); 7 | 8 | module.exports = function(bin, key, config, schema) { 9 | merge(config, schema); 10 | 11 | if (schema.checked[key]) { 12 | return bin; 13 | } 14 | 15 | const opts = utils.merge({}, schema.options); 16 | if (opts.bin === false) { 17 | bin = schema.data.get('bin') || config.bin || bin; 18 | if (typeof bin === 'string') { 19 | schema.union('files', config, bin); 20 | } 21 | schema.checked[key] = true; 22 | return bin; 23 | } 24 | 25 | if (typeof bin === 'string') { 26 | schema.union('files', config, bin); 27 | schema.checked[key] = true; 28 | return bin; 29 | } 30 | 31 | if (typeof bin === 'undefined') { 32 | bin = {}; 33 | } 34 | 35 | // ensure `name` is defined before continuing 36 | schema.update('name', config); 37 | 38 | const obj = {}; 39 | let num = 0; 40 | 41 | if (utils.isObject(bin)) { 42 | for (var prop in bin) { 43 | if (utils.exists(bin[prop])) { 44 | obj[prop] = bin[prop]; 45 | num++; 46 | } 47 | } 48 | bin = obj; 49 | } 50 | 51 | // add cli.js or "bin/" if they exist 52 | const dir = path.resolve(process.cwd(), 'bin'); 53 | if (num === 0 && utils.exists(dir)) { 54 | fs.readdirSync(dir).forEach(function(fp) { 55 | bin[config.name] = path.relative(process.cwd(), path.join(dir, fp)); 56 | num++; 57 | }); 58 | } 59 | 60 | if (num === 0 && utils.exists(path.resolve(process.cwd(), 'cli.js'))) { 61 | bin[config.name] = 'cli.js'; 62 | } 63 | 64 | if (!utils.isEmpty(bin)) { 65 | if (typeof bin === 'string') { 66 | schema.union('files', config, bin); 67 | } 68 | 69 | config[key] = bin; 70 | schema.checked[key] = true; 71 | return bin; 72 | } 73 | 74 | schema.omit(key); 75 | return; 76 | }; 77 | -------------------------------------------------------------------------------- /lib/normalizers/bugs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getOwner = require('./helpers/owner'); 4 | const utils = require('../utils'); 5 | const merge = require('../merge'); 6 | 7 | module.exports = function(val, key, config, schema) { 8 | merge(config, schema); 9 | 10 | val = val || config[key]; 11 | if (schema.checked[key]) { 12 | return val; 13 | } 14 | 15 | let owner = config.owner; 16 | if (!utils.isString(owner)) { 17 | owner = getOwner(val, key, config, schema); 18 | } 19 | 20 | if (utils.isObject(val) && utils.isString(val.url) && hasOwner(owner, val.url)) { 21 | val = config[key] = { url: utils.repo.issues(utils.repo.homepage(val.url)) }; 22 | return val; 23 | } 24 | 25 | schema.update('repository', config); 26 | if (utils.isString(config.repository) && hasOwner(owner, config.repository)) { 27 | return { url: utils.repo.issues(config.repository) }; 28 | } 29 | 30 | if (utils.isString(config.homepage) && hasOwner(owner, config.homepage)) { 31 | return { url: utils.repo.issues(config.homepage) }; 32 | } 33 | }; 34 | 35 | function hasOwner(owner, str) { 36 | return str.indexOf(owner + '/') !== -1; 37 | } 38 | -------------------------------------------------------------------------------- /lib/normalizers/engineStrict.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(val, key, config, schema) { 4 | if (typeof val === 'boolean') { 5 | schema.warning('deprecated', key, { actual: key, expected: 'engine-strict' }); 6 | config['engine-strict'] = val; 7 | } 8 | schema.omit(key); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/normalizers/files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const utils = require('../utils'); 5 | const merge = require('../merge'); 6 | 7 | module.exports = function(files, key, config, schema) { 8 | merge(config, schema); 9 | 10 | files = files || config[key]; 11 | if (schema.checked[key]) { 12 | return files; 13 | } 14 | 15 | files = utils.arrayify(files).filter(Boolean); 16 | files = utils.union([], files, config.files); 17 | const opts = utils.merge({}, schema.options); 18 | 19 | const len = files.length; 20 | let idx = -1; 21 | const res = Array.isArray(opts.files) ? opts.files : []; 22 | 23 | while (++idx < len) { 24 | const file = files[idx]; 25 | if (utils.exists(path.resolve(process.cwd(), file))) { 26 | utils.union(res, utils.stripSlash(file)); 27 | } 28 | } 29 | 30 | // commond directories 31 | addPath(res, 'lib', opts, config); 32 | addPath(res, 'dist', opts, config); 33 | addPath(res, 'bin', opts, config); 34 | 35 | // common files 36 | addPath(res, 'index.js', opts, config); 37 | addPath(res, 'utils.js', opts, config); 38 | addPath(res, 'cli.js', opts, config); 39 | 40 | if (/^(generate|verb|assemble|updater)-/.test(config.name)) { 41 | addPath(res, 'updatefile.js', opts, config); 42 | addPath(res, 'generator.js', opts, config); 43 | addPath(res, 'templates', opts, config); 44 | } 45 | 46 | if (res.length) { 47 | res.sort(); 48 | config[key] = res; 49 | schema.update('bin', config); 50 | schema.checked[key] = true; 51 | return res; 52 | } 53 | 54 | // don't use `omit`, so the key can be re-added if needed 55 | delete config[key]; 56 | return; 57 | }; 58 | 59 | function addPath(files, file, opts, config) { 60 | if (Array.isArray(opts.files) && opts.files.indexOf(file) === -1) { 61 | return; 62 | } 63 | if (files.indexOf(file) !== -1) { 64 | return; 65 | } 66 | if (utils.exists(path.resolve(process.cwd(), file))) { 67 | utils.union(files, file); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/normalizers/helpers/git.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../../utils'); 4 | 5 | module.exports = function(val, key, config, schema) { 6 | let git = schema.data.get('git'); 7 | if (git) return git; 8 | 9 | git = tryGitConfig(); 10 | if (utils.isObject(git)) { 11 | schema.data.set('git', git); 12 | return git; 13 | } 14 | }; 15 | 16 | function tryGitConfig() { 17 | try { 18 | const git = utils.repo.gitConfig({ type: 'local' }) 19 | || utils.repo.gitConfig({ type: 'global' }); 20 | 21 | return parseKeys(git); 22 | } catch (err) {} 23 | } 24 | 25 | function parseKeys(git) { 26 | return utils.merge({}, git, utils.parseGitConfig.expandKeys(git)); 27 | } 28 | -------------------------------------------------------------------------------- /lib/normalizers/helpers/owner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../../utils'); 4 | const git = require('./git'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | let owner = config.owner; 8 | let url = val; 9 | 10 | if (utils.isObject(url)) { 11 | url = val.url; 12 | } 13 | 14 | let repo = config.repository; 15 | if (utils.isObject(repo) && repo.url) { 16 | repo = repo.url; 17 | } 18 | 19 | let obj; 20 | if (utils.isString(url)) { 21 | obj = utils.repo.parseUrl(url); 22 | schema.data.set('owner', obj.owner); 23 | return obj.owner; 24 | } 25 | 26 | if (utils.isString(repo)) { 27 | obj = utils.repo.parseUrl(repo); 28 | schema.data.set('owner', obj.owner); 29 | return obj.owner; 30 | } 31 | 32 | const parsed = git(val, key, config, schema); 33 | if (utils.isObject(parsed)) { 34 | schema.data.set('git', parsed); 35 | owner = parsed.user && parsed.user.name; 36 | 37 | if (owner) { 38 | schema.data.set('owner', owner); 39 | return owner; 40 | } 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /lib/normalizers/homepage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getOwner = require('./helpers/owner'); 4 | const utils = require('../utils'); 5 | const merge = require('../merge'); 6 | 7 | module.exports = function(val, key, config, schema) { 8 | merge(config, schema); 9 | let res; 10 | 11 | val = val || config[key]; 12 | if (utils.isString(val) && /:\/\//.test(val)) { 13 | return val; 14 | } 15 | 16 | schema.update('repository', config); 17 | 18 | if (utils.isString(config.repository)) { 19 | res = utils.repo.homepage(config.repository); 20 | schema.checked[key] = true; 21 | return res; 22 | } 23 | 24 | let owner = config.owner; 25 | if (!utils.isString(owner)) { 26 | owner = getOwner(val, key, config, schema); 27 | } 28 | 29 | if (utils.isString(owner) && utils.isString(config.name)) { 30 | res = utils.repo.homepage(owner + '/' + config.name); 31 | schema.checked[key] = true; 32 | return res; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/normalizers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('export-files')(__dirname); 4 | -------------------------------------------------------------------------------- /lib/normalizers/keywords.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | 9 | val = utils.arrayify(val || config[key]); 10 | if (schema.checked[key]) { 11 | return val; 12 | } 13 | 14 | if (val.length === 0) { 15 | schema.update('name', config); 16 | val = config[key] = config.name.split(/\W+/).filter(Boolean); 17 | } 18 | 19 | const res = utils.unique(val).sort(); 20 | schema.checked[key] = true; 21 | return res; 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /lib/normalizers/license.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | val = val || config[key]; 9 | 10 | switch (utils.typeOf(val)) { 11 | case 'object': 12 | return val.type; 13 | case 'array': 14 | return val[0].type; 15 | case 'string': 16 | default: { 17 | return val; 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /lib/normalizers/licenses.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const license = require('./license'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | if (Array.isArray(val)) { 9 | schema.warning('deprecated', key, { actual: key, expected: 'license' }); 10 | schema.update('license', val[0].type, config); 11 | delete config[key]; 12 | } else { 13 | delete config[key]; 14 | return license(val, 'license', config, schema); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /lib/normalizers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | val = val || config[key]; 9 | 10 | if (typeof val === 'undefined') { 11 | val = 'index.js'; 12 | } 13 | 14 | if (!utils.exists(val)) { 15 | delete config[key]; 16 | return; 17 | } 18 | 19 | schema.update('files', config); 20 | schema.union('files', config, val); 21 | return val; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/normalizers/name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | val = val || config[key]; 9 | 10 | if (utils.isString(val)) { 11 | return val; 12 | } 13 | 14 | if (schema.checked[key]) { 15 | return val; 16 | } 17 | 18 | schema.update('repository', config); 19 | const res = config.name || utils.repo.name(process.cwd()); 20 | schema.checked[key] = true; 21 | return res; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/normalizers/person.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | /** 7 | * Stringify a person object, or array of person objects, such as 8 | * `maintainer`, `collaborator`, `contributor`, and `author`. 9 | * 10 | * @param {Object|Array|String} `val` If an object is passed, it will be converted to a string. If an array of objects is passed, it will be converted to an array of strings. 11 | * @return {String} 12 | * @api public 13 | */ 14 | 15 | module.exports = function person(val, key, config, schema) { 16 | merge(config, schema); 17 | 18 | if (Array.isArray(val)) { 19 | val = val.map(str => person(str, key, config, schema)); 20 | } 21 | 22 | if (utils.isObject(val)) { 23 | val = utils.stringify(val); 24 | } 25 | 26 | return val; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/normalizers/repository.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const git = require('./helpers/git'); 4 | const getOwner = require('./helpers/owner'); 5 | const utils = require('../utils'); 6 | const merge = require('../merge'); 7 | 8 | module.exports = function(val, key, config, schema) { 9 | merge(config, schema); 10 | let owner = config.owner; 11 | 12 | val = val || config[key]; 13 | if (schema.checked[key]) { 14 | return val; 15 | } 16 | 17 | if (utils.isObject(val) && val.url) { 18 | val = val.url; 19 | } 20 | 21 | if (!utils.isString(val)) { 22 | const parsed = git(val, key, config, schema); 23 | const remote = utils.get(parsed, 'remote.origin.url'); 24 | 25 | if (parsed.user && parsed.user.username) { 26 | owner = parsed.user.username; 27 | config.owner = owner; 28 | schema.checked.owner = true; 29 | } 30 | 31 | if (remote) { 32 | const expanded = utils.repo.expandUrl(remote); 33 | const data = utils.merge({}, expanded, config); 34 | utils.merge(config, data); 35 | schema.data.merge(data); 36 | } 37 | } 38 | 39 | if (!utils.isString(val) && config.homepage) { 40 | val = config.homepage; 41 | } 42 | 43 | if (!owner) { 44 | owner = getOwner(val, key, config, schema); 45 | } 46 | if (config.name && owner) { 47 | val = owner + '/' + config.name; 48 | } 49 | 50 | if (utils.isString(val)) { 51 | schema.checked[key] = true; 52 | return utils.repo.repository(val); 53 | } 54 | 55 | // if not returned already, val is invalid 56 | delete config[key]; 57 | return; 58 | }; 59 | -------------------------------------------------------------------------------- /lib/normalizers/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | const merge = require('../merge'); 5 | 6 | module.exports = function(val, key, config, schema) { 7 | merge(config, schema); 8 | val = val || config[key]; 9 | 10 | if (utils.isObject(val) && utils.isString(val.test) && /mocha -R \w+$/.test(val.test)) { 11 | val.test = 'mocha'; 12 | } 13 | return val; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/normalizers/typings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const glob = require('matched'); 4 | const utils = require('../utils'); 5 | 6 | /** 7 | * TypeScript declarations 8 | */ 9 | 10 | module.exports = function(val, key, config, schema) { 11 | val = val || config[key]; 12 | 13 | if (typeof val === 'undefined') { 14 | const files = glob.sync('*.d.ts', { cwd: process.cwd() }); 15 | if (files.length) { 16 | val = files[0]; 17 | } 18 | } 19 | 20 | if (!utils.exists(val)) { 21 | delete config[key]; 22 | return; 23 | } 24 | 25 | config[key] = val; 26 | return val; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Schema = require('map-schema'); 4 | const normalizers = require('./normalizers'); 5 | const validators = require('./validators'); 6 | const utils = require('./utils'); 7 | const keys = require('./keys'); 8 | 9 | /** 10 | * Normalize package.json using a schema. 11 | */ 12 | 13 | module.exports = options => { 14 | const opts = Object.assign({}, options); 15 | const omit = utils.arrayify(opts.omit); 16 | const config = Object.assign({}, opts, { keys: keys, omit: omit }); 17 | 18 | const schema = new Schema(config) 19 | .field('name', ['string'], { 20 | validate: validators.name, 21 | normalize: normalizers.name, 22 | required: true 23 | }) 24 | .field('private', 'boolean') 25 | .field('description', 'string') 26 | .field('version', 'string', { 27 | validate: validators.version, 28 | default: '0.1.0', 29 | required: true 30 | }) 31 | .field('homepage', 'string', { 32 | normalize: normalizers.homepage 33 | }) 34 | 35 | /** 36 | * Person fields 37 | */ 38 | 39 | .field('author', ['object', 'string'], { normalize: normalizers.person }) 40 | .field('authors', 'array', { normalize: normalizers.person }) 41 | .field('maintainers', 'array', { normalize: normalizers.person }) 42 | .field('contributors', 'array', { normalize: normalizers.person }) 43 | .field('collaborators', 'array', { normalize: normalizers.person }) 44 | 45 | /** 46 | * Bugs, repo and license 47 | */ 48 | 49 | .field('bugs', ['object', 'string'], { 50 | normalize: normalizers.bugs 51 | }) 52 | .field('repository', ['object', 'string'], { 53 | normalize: normalizers.repository 54 | }) 55 | .field('license', 'string', { 56 | normalize: normalizers.license, 57 | default: 'MIT' 58 | }) 59 | .field('licenses', ['array', 'object'], { 60 | normalize: normalizers.licenses, 61 | validate: validators.licenses 62 | }) 63 | 64 | /** 65 | * Files, main 66 | */ 67 | 68 | .field('files', 'array', { 69 | normalize: normalizers.files 70 | }) 71 | .field('main', 'string', { 72 | normalize: normalizers.main, 73 | validate: function(filepath) { 74 | return utils.exists(filepath); 75 | } 76 | }) 77 | .field('types', 'string', { 78 | normalize: normalizers.typings 79 | }) 80 | .field('typings', 'string', { 81 | normalize: normalizers.typings 82 | }) 83 | 84 | /** 85 | * Scripts, binaries and related 86 | */ 87 | 88 | .field('preferGlobal', 'boolean', { 89 | validate: validators.preferGlobal 90 | }) 91 | .field('bin', ['object', 'string'], { 92 | normalize: normalizers.bin 93 | }) 94 | .field('scripts', 'object', { 95 | normalize: normalizers.scripts 96 | }) 97 | 98 | /** 99 | * Engine 100 | */ 101 | 102 | .field('engines', 'object', { 103 | default: { node: '>= 0.10.0' } 104 | }) 105 | .field('engine-strict', 'boolean', { 106 | validate: validators['engine-strict'] 107 | }) 108 | .field('engineStrict', 'boolean', { 109 | normalize: normalizers.engineStrict 110 | }) 111 | 112 | /** 113 | * Dependencies 114 | */ 115 | 116 | .field('dependencies', 'object') 117 | .field('devDependencies', 'object') 118 | .field('peerDependencies', 'object') 119 | .field('optionalDependencies', 'object') 120 | 121 | /** 122 | * Project metadata 123 | */ 124 | 125 | .field('man', ['array', 'string']) 126 | .field('keywords', 'array', { 127 | normalize: normalizers.keywords 128 | }); 129 | 130 | // Add fields defined on `options.fields` 131 | schema.addFields(opts); 132 | return schema; 133 | }; 134 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { defineProperty } = Reflect; 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | const define = (obj, key, fn) => { 8 | defineProperty(obj, key, { get: fn }); 9 | }; 10 | 11 | /** 12 | * Lazily required module depedencies 13 | */ 14 | 15 | define(exports, 'union', () => require('arr-union')); 16 | define(exports, 'get', () => require('get-value')); 17 | define(exports, 'typeOf', () => require('kind-of')); 18 | define(exports, 'merge', () => require('mixin-deep')); 19 | define(exports, 'pick', () => require('object.pick')); 20 | define(exports, 'omitEmpty', () => require('omit-empty')); 21 | define(exports, 'parseGitConfig', () => require('parse-git-config')); 22 | define(exports, 'repo', () => require('repo-utils')); 23 | define(exports, 'semver', () => require('semver')); 24 | define(exports, 'stringify', () => require('stringify-author')); 25 | 26 | /** 27 | * Return true if `val` is empty 28 | */ 29 | 30 | exports.isEmpty = val => { 31 | return Object.keys(exports.omitEmpty(val)).length === 0; 32 | }; 33 | 34 | exports.unique = value => { 35 | return [...new Set([].concat(value))]; 36 | }; 37 | 38 | exports.exists = filepath => fs.existsSync(filepath); 39 | 40 | /** 41 | * Return true if `val` is an object 42 | */ 43 | 44 | exports.isObject = val => { 45 | return exports.typeOf(val) === 'object'; 46 | }; 47 | 48 | /** 49 | * Return true if `val` is a string with a non-zero length 50 | */ 51 | 52 | exports.isString = val => { 53 | return val && typeof val === 'string'; 54 | }; 55 | 56 | /** 57 | * Cast `val` to an array 58 | */ 59 | 60 | exports.arrayify = val => { 61 | return val ? (Array.isArray(val) ? val : [val]) : []; 62 | }; 63 | 64 | /** 65 | * Try to require the package.json at the given filepath 66 | */ 67 | 68 | exports.requirePackage = filepath => { 69 | filepath = path.resolve(filepath); 70 | try { 71 | var stat = fs.statSync(filepath); 72 | if (stat.isDirectory()) { 73 | filepath = path.join(filepath, 'package.json'); 74 | } else if (!/package\.json$/.test(filepath)) { 75 | filepath = path.join(path.dirname(filepath), 'package.json'); 76 | } 77 | return require(filepath); 78 | } catch (err) {} 79 | return {}; 80 | }; 81 | 82 | /** 83 | * Strip a trailing slash from a string. 84 | * 85 | * @param {String} `str` 86 | * @return {String} 87 | */ 88 | 89 | exports.stripSlash = str => str.replace(/\W+$/, ''); 90 | -------------------------------------------------------------------------------- /lib/validators/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('export-files')(__dirname); 4 | -------------------------------------------------------------------------------- /lib/validators/name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(val) { 4 | return val && typeof val === 'string'; 5 | }; 6 | -------------------------------------------------------------------------------- /lib/validators/preferGlobal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(val, key, config, schema) { 4 | schema.update('bin', config); 5 | if (!config.hasOwnProperty('bin') && val === true) { 6 | schema.warning('custom', key, 'expected "bin" to be defined when "preferGlobal" is true'); 7 | } 8 | return val === true; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/validators/version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | 5 | module.exports = function(val) { 6 | if (utils.semver.valid(val) === null) { 7 | throw new Error('invalid semver version "' + val + '" defined in package.json'); 8 | } 9 | return true; 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-pkg", 3 | "description": "Normalize values in package.json using the map-schema library.", 4 | "version": "0.5.0", 5 | "homepage": "https://github.com/jonschlinkert/normalize-pkg", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "contributors": [ 8 | "Brian Woodward (https://twitter.com/doowb)", 9 | "Jon Schlinkert (http://twitter.com/jonschlinkert)", 10 | "Peter deHaan (http://about.me/peterdehaan)" 11 | ], 12 | "funding": "https://github.com/sponsors/jonschlinkert", 13 | "repository": "jonschlinkert/normalize-pkg", 14 | "bugs": { 15 | "url": "https://github.com/jonschlinkert/normalize-pkg/issues" 16 | }, 17 | "license": "MIT", 18 | "files": [ 19 | "bin", 20 | "index.js", 21 | "lib" 22 | ], 23 | "main": "index.js", 24 | "bin": { 25 | "normalize-pkg": "bin/cli.js" 26 | }, 27 | "engines": { 28 | "node": ">=10" 29 | }, 30 | "scripts": { 31 | "test": "mocha" 32 | }, 33 | "dependencies": { 34 | "arr-union": "^3.1.0", 35 | "export-files": "^2.1.1", 36 | "get-value": "^3.0.1", 37 | "kind-of": "^6.0.3", 38 | "map-schema": "^0.3.0", 39 | "matched": "^5.0.0", 40 | "minimist": "^1.2.0", 41 | "mixin-deep": "^2.0.1", 42 | "object.pick": "^1.3.0", 43 | "omit-empty": "^1.0.0", 44 | "parse-git-config": "^3.0.0", 45 | "repo-utils": "^0.4.1", 46 | "semver": "^7.1.3", 47 | "stringify-author": "^0.1.3", 48 | "write-json": "^3.0.1" 49 | }, 50 | "devDependencies": { 51 | "delete": "^1.1.0", 52 | "gitty": "^3.7.1", 53 | "gulp-format-md": "^2.0.0", 54 | "mocha": "^7.1.0" 55 | }, 56 | "keywords": [ 57 | "config", 58 | "fix", 59 | "json", 60 | "normalize", 61 | "package", 62 | "package-json", 63 | "pkg", 64 | "properties", 65 | "values" 66 | ], 67 | "verb": { 68 | "run": true, 69 | "toc": "collapsible", 70 | "layout": "default", 71 | "tasks": [ 72 | "readme" 73 | ], 74 | "plugins": [ 75 | "gulp-format-md" 76 | ], 77 | "related": { 78 | "list": [ 79 | "update" 80 | ] 81 | }, 82 | "reflinks": [ 83 | "map-schema", 84 | "project-name", 85 | "verb", 86 | "verb-readme-generator" 87 | ], 88 | "lint": { 89 | "reflinks": true 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "../.eslintrc.json" 4 | ], 5 | "env": { 6 | "mocha": true 7 | } 8 | } -------------------------------------------------------------------------------- /test/actual/normalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo", 3 | "version": "0.4.11", 4 | "description": "Create new Assemble projects.", 5 | "homepage": "https://github.com/assemble/generator-assemble", 6 | "main": "app/index.js", 7 | "scripts": { 8 | "test": "mocha --reporter spec" 9 | } 10 | } -------------------------------------------------------------------------------- /test/fixtures/author-condensed.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-pkg", 3 | "private": true, 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/jonschlinkert/normalize-pkg", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "repository": "jonschlinkert/normalize-pkg", 8 | "license": "MIT", 9 | "files": [ 10 | "index.js" 11 | ], 12 | "main": "index.js", 13 | "engines": { 14 | "node": ">= 0.10.0" 15 | }, 16 | "keywords": [ 17 | "normalize", 18 | "pkg" 19 | ] 20 | } -------------------------------------------------------------------------------- /test/fixtures/authors-condensed.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "Brian Woodward (https://github.com/doowb)", 4 | "Jon Schlinkert (https://github.com/jonschlinkert)" 5 | ] 6 | } -------------------------------------------------------------------------------- /test/fixtures/bin.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /test/fixtures/engine-strict.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expand-pkg", 3 | "private": true, 4 | "engineStrict": true, 5 | "keywords": [ 6 | "package-json", 7 | "config", 8 | "values", 9 | "fix", 10 | "json", 11 | "pkg", 12 | "normalize", 13 | "package", 14 | "properties" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/licenses.json: -------------------------------------------------------------------------------- 1 | { 2 | "licenses": [ 3 | { 4 | "type": "Blah", 5 | "url": "foo" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /test/fixtures/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": null 3 | } -------------------------------------------------------------------------------- /test/fixtures/missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo", 3 | "private": true, 4 | "version": "0.4.11", 5 | "description": "Create new Assemble projects.", 6 | "homepage": "https://github.com/assemble/generator-assemble", 7 | "main": "app/index.js", 8 | "scripts": { 9 | "test": "mocha --reporter spec" 10 | } 11 | } -------------------------------------------------------------------------------- /test/fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expand-pkg", 3 | "private": true, 4 | "description": "Normalize values in package.json to improve compatibility, programmatic readability and usefulness with third party libs.", 5 | "version": "0.2.5", 6 | "homepage": "https://github.com/jonschlinkert/expand-pkg/", 7 | "author": { 8 | "name": "Jon Schlinkert", 9 | "url": "https://github.com/jonschlinkert" 10 | }, 11 | "repository": { 12 | "url": "https://github.com/jonschlinkert/expand-pkg.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jonschlinkert/expand-pkg/issues" 16 | }, 17 | "license": "MIT", 18 | "files": [ 19 | "index.js", 20 | "lib" 21 | ], 22 | "engines": { 23 | "node": ">= 0.10.0" 24 | }, 25 | "scripts": { 26 | "test": "mocha" 27 | }, 28 | "dependencies": { 29 | "array-unique": "^0.2.1", 30 | "define-property": "^0.2.5", 31 | "expand": "^0.3.2", 32 | "extend-shallow": "^2.0.1", 33 | "get-value": "^2.0.0", 34 | "kind-of": "^3.0.2", 35 | "lazy-cache": "^1.0.2", 36 | "matched": "^0.3.2", 37 | "mixin-deep": "^1.1.3", 38 | "object.omit": "^2.0.0", 39 | "parse-author": "^0.2.0", 40 | "parse-github-url": "^0.2.1", 41 | "project-name": "^0.2.3", 42 | "remote-origin-url": "^0.4.0", 43 | "set-value": "^0.3.1", 44 | "stringify-author": "^0.1.3" 45 | }, 46 | "devDependencies": { 47 | "gulp": "^3.9.0", 48 | "gulp-eslint": "^1.0.0", 49 | "gulp-istanbul": "^0.10.2", 50 | "gulp-mocha": "^2.1.3", 51 | "mocha": "*" 52 | }, 53 | "keywords": [ 54 | "config", 55 | "fix", 56 | "json", 57 | "normalize", 58 | "package", 59 | "package-json", 60 | "pkg", 61 | "properties", 62 | "values" 63 | ], 64 | "verb": { 65 | "related": { 66 | "list": [ 67 | "update" 68 | ] 69 | }, 70 | "plugins": "gulp-format-md", 71 | "layout": null 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/fixtures/people-condensed.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-pkg", 3 | "private": true, 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/jonschlinkert/normalize-pkg", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "authors": [ 8 | "Brian Woodward (https://github.com/doowb)", 9 | "Jon Schlinkert (https://github.com/jonschlinkert)" 10 | ], 11 | "contributors": [ 12 | "Brian Woodward (https://github.com/doowb)", 13 | "Jon Schlinkert (https://github.com/jonschlinkert)" 14 | ], 15 | "collaborators": [ 16 | "Brian Woodward (https://github.com/doowb)", 17 | "Jon Schlinkert (https://github.com/jonschlinkert)" 18 | ], 19 | "repository": "jonschlinkert/normalize-pkg", 20 | "license": "MIT", 21 | "files": [ 22 | "index.js" 23 | ], 24 | "main": "index.js", 25 | "engines": { 26 | "node": ">= 0.10.0" 27 | }, 28 | "keywords": [ 29 | "normalize", 30 | "pkg" 31 | ] 32 | } -------------------------------------------------------------------------------- /test/fixtures/people.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expand-pkg", 3 | "private": true, 4 | "author": { 5 | "name": "Jon Schlinkert", 6 | "url": "https://github.com/jonschlinkert" 7 | }, 8 | "authors": [ 9 | { 10 | "name": "Jon Schlinkert", 11 | "url": "https://github.com/jonschlinkert" 12 | }, 13 | { 14 | "name": "Brian Woodward", 15 | "url": "https://github.com/doowb" 16 | } 17 | ], 18 | "contributors": [ 19 | { 20 | "name": "Jon Schlinkert", 21 | "url": "https://github.com/jonschlinkert" 22 | }, 23 | { 24 | "name": "Brian Woodward", 25 | "url": "https://github.com/doowb" 26 | } 27 | ], 28 | "collaborators": [ 29 | { 30 | "name": "Jon Schlinkert", 31 | "url": "https://github.com/jonschlinkert" 32 | }, 33 | { 34 | "name": "Brian Woodward", 35 | "url": "https://github.com/doowb" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /test/fixtures/project-bin/bin/foo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | -------------------------------------------------------------------------------- /test/fixtures/project-bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | -------------------------------------------------------------------------------- /test/fixtures/project-bin/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() {}; 4 | -------------------------------------------------------------------------------- /test/fixtures/project-bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project", 3 | "private": true, 4 | "description": "", 5 | "version": "0.0.0", 6 | "repository": { 7 | "url": "https://github.com/jonschlinkert/test-project.git" 8 | }, 9 | "license": "MIT" 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/project-no-git/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() {}; 4 | -------------------------------------------------------------------------------- /test/fixtures/project-no-git/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-no-git", 3 | "private": true, 4 | "description": "", 5 | "version": "0.0.0", 6 | "repository": { 7 | "url": "https://github.com/jonschlinkert/project-no-git.git" 8 | }, 9 | "license": "MIT" 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/project-no-package/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() {}; 4 | -------------------------------------------------------------------------------- /test/fixtures/project/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() {}; 4 | -------------------------------------------------------------------------------- /test/fixtures/project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project", 3 | "private": true, 4 | "description": "", 5 | "version": "0.0.0", 6 | "repository": { 7 | "url": "https://github.com/jonschlinkert/test-project.git" 8 | }, 9 | "license": "MIT" 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/verb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expand-pkg", 3 | "private": true, 4 | "keywords": [ 5 | "package-json", 6 | "config", 7 | "values", 8 | "fix", 9 | "json", 10 | "pkg", 11 | "normalize", 12 | "package", 13 | "properties" 14 | ], 15 | "verb": { 16 | "related": { 17 | "list": [ 18 | "update" 19 | ] 20 | }, 21 | "plugins": "gulp-format-md", 22 | "layout": "default" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo", 3 | "version": "0.4.11", 4 | "description": "Create new Assemble projects.", 5 | "author": "Jon Schlinkert", 6 | "license": "MIT", 7 | "homepage": "https://github.com/assemble/generator-assemble", 8 | "repository": "https://github.com/assemble/generator-assemble.git", 9 | "bugs": "https://github.com/assemble/generator-assemble/issues", 10 | "main": "app/index.js", 11 | "scripts": { 12 | "test": "mocha --reporter spec" 13 | } 14 | } -------------------------------------------------------------------------------- /test/support/git.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const del = require('delete'); 5 | const gitty = require('gitty'); 6 | 7 | module.exports = function(cwd, remote, cb) { 8 | const repo = gitty(cwd); 9 | 10 | del(path.join(cwd, '.git'), function(err) { 11 | if (err) return cb(err); 12 | 13 | repo.init(function(err) { 14 | if (err) return cb(err); 15 | gitAdd(repo, remote, cb); 16 | }); 17 | }); 18 | }; 19 | 20 | function gitAdd(repo, remote, cb) { 21 | repo.add(['.'], function(err) { 22 | if (err) return cb(err); 23 | 24 | firstCommit(repo, remote, cb); 25 | }); 26 | } 27 | 28 | function firstCommit(repo, remote, cb) { 29 | repo.commit('first commit', function(err) { 30 | if (err) return cb(err); 31 | if (typeof remote === 'string') { 32 | repo.addRemote('origin', remote, cb); 33 | } else { 34 | cb(); 35 | } 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /test/test-bin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const assert = require('assert'); 7 | const del = require('delete'); 8 | const createRepo = require('./support/git'); 9 | const Normalizer = require('..'); 10 | let config; 11 | 12 | const origCwd = process.cwd(); 13 | const project = path.resolve(__dirname, 'fixtures/project-bin'); 14 | const remote = `https://github.com/jonschlinkert/${path.basename(project)}.git`; 15 | const gitPath = path.resolve(project, '.git'); 16 | 17 | describe('normalize (bin)', () => { 18 | beforeEach(() => { 19 | config = new Normalizer({ verbose: false }); 20 | }); 21 | 22 | before(cb => { 23 | process.chdir(project); 24 | createRepo(project, remote, cb); 25 | }); 26 | 27 | after(cb => { 28 | process.chdir(origCwd); 29 | del(gitPath, cb); 30 | }); 31 | 32 | describe('main', () => { 33 | it('should remove the property if the file does not exist', () => { 34 | const pkg = { main: 'foo.js' }; 35 | const res = config.normalize(pkg); 36 | assert(!res.hasOwnProperty('main')); 37 | }); 38 | 39 | it('should not remove the property if the file exists', () => { 40 | const pkg = { main: 'main.js' }; 41 | const res = config.normalize(pkg); 42 | assert(res.hasOwnProperty('main')); 43 | }); 44 | 45 | it('should add the main file to the `files` array', () => { 46 | const pkg = { main: 'main.js' }; 47 | const res = config.normalize(pkg); 48 | 49 | assert.equal(res.files.indexOf('bin'), 0); 50 | assert.equal(res.files.indexOf('cli.js'), 1); 51 | assert.equal(res.files.indexOf('main.js'), 2); 52 | }); 53 | 54 | it('should not add `main` file to files array when file does not exist', () => { 55 | const pkg = { 56 | files: [], 57 | main: 'index.js' 58 | }; 59 | 60 | const res = config.normalize(pkg); 61 | assert(res.hasOwnProperty('files')); 62 | assert(res.files.indexOf('index.js') === -1); 63 | }); 64 | 65 | it('should create files array with main', () => { 66 | const pkg = { 67 | main: 'main.js' 68 | }; 69 | 70 | const res = config.normalize(pkg); 71 | assert(res.files.length); 72 | assert(res.files.indexOf('main.js') !== -1); 73 | }); 74 | 75 | it('should not double add the file to files', () => { 76 | const pkg = { 77 | files: ['main.js'], 78 | main: 'main.js' 79 | }; 80 | 81 | const res = config.normalize(pkg); 82 | assert.equal(res.files.length, 3); 83 | assert(res.files.indexOf('main.js') !== -1); 84 | }); 85 | 86 | it('should remove main if the file does not exist', () => { 87 | const pkg = { main: 'foo.js' }; 88 | 89 | const res = config.normalize(pkg); 90 | assert(!res.main); 91 | }); 92 | 93 | it('should do nothing if not defined', () => { 94 | const pkg = {}; 95 | 96 | const res = config.normalize(pkg); 97 | assert.equal(typeof res.main, 'undefined'); 98 | }); 99 | }); 100 | 101 | describe('preferGlobal', () => { 102 | beforeEach(() => { 103 | config = new Normalizer({ verbose: false }); 104 | }); 105 | 106 | it('should not warn when preferGlobal is defined and `bin` is defined', cb => { 107 | const pkg = { preferGlobal: true, bin: 'main.js' }; 108 | let count = 0; 109 | 110 | config.on('warning', function(method, key, err) { 111 | if (key === 'preferGlobal') { 112 | count++; 113 | } 114 | }); 115 | 116 | const res = config.normalize(pkg); 117 | assert(res.preferGlobal); 118 | assert.equal(count, 0); 119 | cb(); 120 | }); 121 | 122 | it('should return bin as-is when it is a string', () => { 123 | const pkg = { bin: 'main.js' }; 124 | 125 | const res = config.normalize(pkg); 126 | assert(res.bin); 127 | assert.equal(res.bin, 'main.js'); 128 | }); 129 | }); 130 | 131 | describe('bin', () => { 132 | beforeEach(() => { 133 | config = new Normalizer({ verbose: false }); 134 | }); 135 | 136 | it('should add `bin/foo.js` to bin', () => { 137 | const res = config.normalize({}); 138 | assert.equal(res.bin[res.name], 'bin/foo.js'); 139 | }); 140 | 141 | it('should add `cli.js` to bin', cb => { 142 | del('bin/foo.js', function(err) { 143 | if (err) return cb(err); 144 | const res = config.normalize({}); 145 | assert.equal(res.bin[res.name], 'cli.js'); 146 | fs.writeFile('bin/foo.js', '#!/usr/bin/env node\n\n', cb); 147 | }); 148 | }); 149 | 150 | it('should not emit a warning when bin file string exists', cb => { 151 | const pkg = { bin: 'main.js' }; 152 | let count = 0; 153 | 154 | config.on('warning', function(method, key, err) { 155 | if (key === 'bin') { 156 | count++; 157 | } 158 | }); 159 | 160 | config.normalize(pkg); 161 | assert.equal(count, 0); 162 | cb(); 163 | }); 164 | 165 | it('should not emit a warning when bin file object exists', cb => { 166 | const pkg = { bin: { foo: 'main.js' } }; 167 | let count = 0; 168 | 169 | config.on('warning', function(method, key, err) { 170 | if (key === 'bin') { 171 | count++; 172 | } 173 | }); 174 | 175 | config.normalize(pkg); 176 | assert.equal(count, 0); 177 | cb(); 178 | }); 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /test/test-no-git.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const path = require('path'); 5 | const assert = require('assert'); 6 | const Normalizer = require('..'); 7 | const utils = require('../lib/utils'); 8 | let config; 9 | let user; 10 | 11 | const origCwd = process.cwd(); 12 | const project = path.resolve(__dirname, 'fixtures/project-no-git'); 13 | 14 | describe('no git repository', () => { 15 | beforeEach(() => { 16 | config = new Normalizer({ verbose: false }); 17 | }); 18 | 19 | beforeEach(() => { 20 | process.chdir(project); 21 | try { 22 | const obj = utils.repo.gitUser(); 23 | user = obj.username || obj.name; 24 | } catch (err) { 25 | user = 'jonschlinkert'; 26 | } 27 | }); 28 | 29 | afterEach(() => { 30 | process.chdir(origCwd); 31 | }); 32 | 33 | describe('omit', () => { 34 | it('should remove a field on options.omit', () => { 35 | config = new Normalizer({ omit: 'version' }); 36 | const res = config.normalize({}); 37 | assert.equal(typeof res.version, 'undefined'); 38 | }); 39 | 40 | it('should remove an array of fields on options.omit', () => { 41 | config = new Normalizer({ omit: ['version', 'main'] }); 42 | const res = config.normalize({}); 43 | assert.equal(typeof res.version, 'undefined'); 44 | assert.equal(typeof res.main, 'undefined'); 45 | }); 46 | }); 47 | 48 | describe('defaults', () => { 49 | it('should add default properties to config', () => { 50 | const res = config.normalize({}); 51 | assert.equal(res.name, 'project-no-git'); 52 | assert.equal(res.version, '0.1.0'); 53 | }); 54 | }); 55 | 56 | describe('name', () => { 57 | it('should use the defined project name', () => { 58 | const pkg = { name: 'foo' }; 59 | const res = config.normalize(pkg); 60 | assert(res.name); 61 | assert.equal(res.name, 'foo'); 62 | }); 63 | 64 | it('should get the project name when string is empty', () => { 65 | const pkg = { name: '' }; 66 | const res = config.normalize(pkg); 67 | assert(res.name); 68 | assert.equal(res.name, 'project-no-git'); 69 | }); 70 | 71 | it('should get the project name when missing', () => { 72 | const pkg = {}; 73 | const res = config.normalize(pkg); 74 | assert(res.name); 75 | assert.equal(res.name, 'project-no-git'); 76 | }); 77 | 78 | it('should use the normalize function defined on options', () => { 79 | const pkg = { name: 'foo' }; 80 | const opts = { 81 | fields: { 82 | name: { 83 | type: ['string'], 84 | normalize: function custom() { 85 | return 'bar'; 86 | } 87 | } 88 | } 89 | }; 90 | 91 | const res = config.normalize(pkg, opts); 92 | assert(res.name); 93 | assert.equal(res.name, 'bar'); 94 | }); 95 | }); 96 | 97 | describe('version', () => { 98 | it('should use the given version', () => { 99 | const pkg = { version: '1.0.0' }; 100 | const res = config.normalize(pkg); 101 | assert(res.version); 102 | assert.equal(res.version, '1.0.0'); 103 | }); 104 | 105 | it('should use the default version', () => { 106 | const pkg = { version: '' }; 107 | const res = config.normalize(pkg); 108 | assert(res.version); 109 | assert.equal(res.version, '0.1.0'); 110 | }); 111 | 112 | it('should emit a warning when version type is invalid', cb => { 113 | const pkg = { version: 5 }; 114 | let count = 0; 115 | 116 | config.on('warning', function(method, key, err) { 117 | if (key === 'version') { 118 | count++; 119 | } 120 | }); 121 | 122 | config.normalize(pkg); 123 | assert.equal(count, 1); 124 | cb(); 125 | }); 126 | 127 | it('should throw an error when version is invalid', cb => { 128 | const pkg = { version: 'foo' }; 129 | try { 130 | config.normalize(pkg); 131 | cb(new Error('expected an error')); 132 | } catch (err) { 133 | assert(/invalid semver/.test(err.message)); 134 | cb(); 135 | } 136 | }); 137 | }); 138 | 139 | describe('main', () => { 140 | it('should remove the property if the file does not exist', () => { 141 | const pkg = { main: 'foo.js' }; 142 | const res = config.normalize(pkg); 143 | assert(!res.hasOwnProperty('main')); 144 | }); 145 | 146 | it('should not remove the property if the file exists', () => { 147 | const pkg = { main: 'main.js' }; 148 | const res = config.normalize(pkg); 149 | assert(res.hasOwnProperty('main')); 150 | }); 151 | 152 | it('should add the main file to the `files` array', () => { 153 | const pkg = { main: 'main.js' }; 154 | const res = config.normalize(pkg); 155 | assert.equal(res.files.indexOf('main.js'), 0); 156 | }); 157 | 158 | it('should not add main file to files array when main file does not exist', () => { 159 | const pkg = { 160 | files: [], 161 | main: 'index.js' 162 | }; 163 | 164 | const res = config.normalize(pkg); 165 | assert(!res.hasOwnProperty('files')); 166 | }); 167 | 168 | it('should add main file to files array if files array is empty', () => { 169 | const pkg = { 170 | files: [], 171 | main: 'main.js' 172 | }; 173 | 174 | const res = config.normalize(pkg); 175 | assert.equal(res.files.length, 1); 176 | assert.equal(res.files[0], 'main.js'); 177 | }); 178 | 179 | it('should create files array with main if undefined', () => { 180 | const pkg = { 181 | main: 'main.js' 182 | }; 183 | 184 | const res = config.normalize(pkg); 185 | assert(res.files.length); 186 | assert(res.files.indexOf('main.js') !== -1); 187 | }); 188 | 189 | it('should not double add the file to files', () => { 190 | const pkg = { 191 | files: ['main.js'], 192 | main: 'main.js' 193 | }; 194 | 195 | const res = config.normalize(pkg); 196 | assert.equal(res.files.length, 1); 197 | assert(res.files.indexOf('main.js') !== -1); 198 | }); 199 | 200 | it('should remove main if the file does not exist', () => { 201 | const pkg = { main: 'foo.js' }; 202 | 203 | const res = config.normalize(pkg); 204 | assert(!res.main); 205 | }); 206 | 207 | it('should do nothing if not defined', () => { 208 | const pkg = {}; 209 | 210 | const res = config.normalize(pkg); 211 | assert.equal(typeof res.main, 'undefined'); 212 | }); 213 | }); 214 | 215 | describe('files', () => { 216 | it('should remove a file if it does not exist', () => { 217 | const pkg = { files: ['foo.js', 'main.js'] }; 218 | const res = config.normalize(pkg); 219 | assert.equal(res.files.length, 1); 220 | }); 221 | 222 | it('should remove the files array if it\'s empty', () => { 223 | const pkg = { files: [] }; 224 | const res = config.normalize(pkg); 225 | assert(!res.files); 226 | }); 227 | 228 | it('should remove the files array if a file that does not exist is removed', () => { 229 | const pkg = { files: ['foo.js'] }; 230 | const res = config.normalize(pkg); 231 | assert(!res.files); 232 | }); 233 | }); 234 | 235 | describe('homepage', () => { 236 | it('should add a homepage from directory name and user from global git config', () => { 237 | const res = config.normalize({}); 238 | assert.equal(res.homepage, `https://github.com/${user}/project-no-git`); 239 | }); 240 | 241 | it('should add repository when setting homepage', () => { 242 | const res = config.normalize({}); 243 | assert.equal(res.repository, user + '/project-no-git'); 244 | }); 245 | 246 | it('should use the given homepage', () => { 247 | const pkg = { homepage: 'https://github.com/assemble/assemble' }; 248 | const res = config.normalize(pkg); 249 | assert.equal(res.homepage, 'https://github.com/assemble/assemble'); 250 | }); 251 | 252 | it('should get homepage from repository.url', () => { 253 | const pkg = { 254 | homepage: '', 255 | repository: 'git://github.com/jonschlinkert/project-no-git.git' 256 | }; 257 | 258 | const res = config.normalize(pkg); 259 | assert(res.homepage); 260 | assert.equal(res.homepage, 'https://github.com/jonschlinkert/project-no-git'); 261 | }); 262 | }); 263 | 264 | describe('author', () => { 265 | it('should not add an empty author field', () => { 266 | const res = config.normalize({}); 267 | assert(!res.hasOwnProperty('author')); 268 | }); 269 | 270 | it('should not add an empty authors field', () => { 271 | const res = config.normalize({}); 272 | assert(!res.hasOwnProperty('authors')); 273 | }); 274 | 275 | it('should use the given author as a string', () => { 276 | const pkg = { author: 'Jon Schlinkert' }; 277 | const res = config.normalize(pkg); 278 | assert(res.author); 279 | assert.equal(res.author, 'Jon Schlinkert'); 280 | }); 281 | 282 | it('should convert an author object to a string', () => { 283 | const pkg = { 284 | author: { 285 | name: 'Jon Schlinkert', 286 | url: 'https://github.com/jonschlinkert' 287 | } 288 | }; 289 | 290 | const res = config.normalize(pkg); 291 | assert(res.author); 292 | assert.equal(res.author, 'Jon Schlinkert (https://github.com/jonschlinkert)'); 293 | }); 294 | }); 295 | 296 | describe('maintainers', () => { 297 | it('should not add an empty maintainers field', () => { 298 | const res = config.normalize({}); 299 | assert(!res.hasOwnProperty('maintainers')); 300 | }); 301 | }); 302 | 303 | describe('license', () => { 304 | it('should add MIT as the default license', () => { 305 | const res = config.normalize({}); 306 | assert(res.hasOwnProperty('license')); 307 | assert.equal(res.license, 'MIT'); 308 | }); 309 | 310 | it('should return license as is if it is a string', () => { 311 | const res = config.normalize({ license: 'MIT' }); 312 | assert(res.hasOwnProperty('license')); 313 | assert.equal(res.license, 'MIT'); 314 | }); 315 | 316 | it('should convert from an object to a string', () => { 317 | const res = config.normalize({ license: { type: 'MIT' } }); 318 | assert(res.hasOwnProperty('license')); 319 | assert.equal(res.license, 'MIT'); 320 | }); 321 | 322 | it('should convert from an array to a string', () => { 323 | const res = config.normalize({ license: [{ type: 'MIT' }] }); 324 | assert(res.hasOwnProperty('license')); 325 | assert.equal(res.license, 'MIT'); 326 | }); 327 | }); 328 | 329 | describe('people', () => { 330 | beforeEach(() => { 331 | config = new Normalizer({ verbose: false }); 332 | }); 333 | 334 | describe('contributors', () => { 335 | it('should not add an empty contributors field', () => { 336 | const res = config.normalize({}); 337 | assert(!res.hasOwnProperty('contributors')); 338 | }); 339 | 340 | it('should convert contributor objects to strings', () => { 341 | const pkg = { 342 | contributors: [{ 343 | name: 'Jon Schlinkert', 344 | url: 'https://github.com/jonschlinkert' 345 | }] 346 | }; 347 | const res = config.normalize(pkg); 348 | assert(res.contributors); 349 | const expected = 'Jon Schlinkert (https://github.com/jonschlinkert)'; 350 | assert.equal(res.contributors[0], expected); 351 | }); 352 | }); 353 | }); 354 | 355 | describe('repository', () => { 356 | it('should use the given repository string', () => { 357 | const pkg = { repository: 'jonschlinkert/foo' }; 358 | const res = config.normalize(pkg); 359 | assert(res.repository); 360 | assert.equal(res.repository, 'jonschlinkert/foo'); 361 | }); 362 | 363 | it('should convert repository.url to a string', () => { 364 | const pkg = { repository: { url: 'https://github.com/jonschlinkert/foo.git' } }; 365 | const res = config.normalize(pkg); 366 | assert(res.repository); 367 | assert.equal(res.repository, 'jonschlinkert/foo'); 368 | }); 369 | }); 370 | 371 | describe('bugs', () => { 372 | beforeEach(() => { 373 | config = new Normalizer({ verbose: false }); 374 | }); 375 | 376 | it('should get the bugs url from the directory name', () => { 377 | const res = config.normalize({}); 378 | assert(res.bugs); 379 | assert.equal(res.bugs.url, 'https://github.com/' + user + '/project-no-git/issues'); 380 | }); 381 | 382 | it('should fix the bugs value based on repo information', () => { 383 | const pkg = { bugs: { url: 'jonschlinkert/foo' } }; 384 | 385 | const res = config.normalize(pkg); 386 | assert(res.bugs); 387 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 388 | }); 389 | 390 | it('should use the given bugs value', () => { 391 | const opts = { bugs: { url: 'jonschlinkert/foo' } }; 392 | const res = config.normalize({}, opts); 393 | assert(res.bugs); 394 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 395 | }); 396 | 397 | it('should use the value function passed on options', () => { 398 | const pkg = { bugs: '' }; 399 | const res = config.normalize(pkg, { 400 | fields: { 401 | bugs: { 402 | type: ['string', 'object'], 403 | normalize: function custom() { 404 | return { url: 'abc' }; 405 | } 406 | } 407 | } 408 | }); 409 | assert(res.bugs); 410 | assert.equal(res.bugs.url, 'abc'); 411 | }); 412 | 413 | it('should use a custom type passed on options', () => { 414 | const pkg = { bugs: '', repository: 'https://github.com/foo/bar' }; 415 | const res = config.normalize(pkg, { 416 | extend: false, 417 | fields: { 418 | bugs: { 419 | type: ['object', 'string'], 420 | normalize: function custom(key, val, config) { 421 | const bugs = {}; 422 | bugs.url = config.homepage + '/bugs'; 423 | return bugs; 424 | } 425 | } 426 | } 427 | }); 428 | 429 | assert.equal(typeof res.bugs, 'object'); 430 | assert(res.bugs.url); 431 | assert.equal(res.bugs.url, 'https://github.com/foo/bar/bugs'); 432 | }); 433 | 434 | it('should convert bugs.url to a string when specified', () => { 435 | const pkg = { bugs: { url: 'https://github.com/jonschlinkert/foo.git' } }; 436 | const res = config.normalize(pkg, { 437 | extend: false, 438 | fields: { 439 | bugs: { 440 | type: 'string', 441 | normalize: function(val, key, config) { 442 | return val.url; 443 | } 444 | } 445 | } 446 | }); 447 | assert(res.bugs); 448 | assert.equal(res.bugs, 'https://github.com/jonschlinkert/foo.git'); 449 | }); 450 | }); 451 | 452 | describe('license', () => { 453 | beforeEach(() => { 454 | config = new Normalizer({ verbose: false }); 455 | }); 456 | 457 | it('should convert a license object to a string', () => { 458 | const pkg = { 459 | license: { 460 | type: 'MIT', 461 | url: 'https://github.com/jonschlinkert/project-no-git/blob/master/LICENSE-MIT' 462 | } 463 | }; 464 | 465 | const res = config.normalize(pkg); 466 | assert.equal(typeof res.license, 'string'); 467 | assert.equal(res.license, 'MIT'); 468 | }); 469 | }); 470 | 471 | describe('licenses', () => { 472 | beforeEach(() => { 473 | config = new Normalizer({ verbose: false }); 474 | }); 475 | 476 | it('should emit a deprecation warning when licenses is defined', cb => { 477 | const pkg = { licenses: { type: 'MIT' } }; 478 | let count = 0; 479 | 480 | config.on('warning', function(method, key, err) { 481 | if (key === 'licenses') { 482 | count++; 483 | } 484 | }); 485 | 486 | config.normalize(pkg); 487 | assert.equal(count, 1); 488 | cb(); 489 | }); 490 | 491 | it('should convert a licenses array to a license string', () => { 492 | const pkg = { 493 | licenses: [ 494 | { type: 'MIT', url: 'https://github.com/jonschlinkert/project-no-git/blob/master/LICENSE-MIT' } 495 | ] 496 | }; 497 | 498 | const res = config.normalize(pkg); 499 | assert(!res.licenses); 500 | assert(res.license); 501 | assert.equal(typeof res.license, 'string'); 502 | assert.equal(res.license, 'MIT'); 503 | }); 504 | 505 | it('should convert from an object to a string', () => { 506 | const pkg = { 507 | licenses: { type: 'MIT', url: 'https://github.com/jonschlinkert/project-no-git/blob/master/LICENSE-MIT' } 508 | }; 509 | 510 | const res = config.normalize(pkg); 511 | assert(!res.licenses); 512 | assert(res.license); 513 | assert.equal(typeof res.license, 'string'); 514 | assert.equal(res.license, 'MIT'); 515 | }); 516 | }); 517 | 518 | describe('dependencies', () => { 519 | beforeEach(() => { 520 | config = new Normalizer({ verbose: false }); 521 | }); 522 | 523 | it('should remove dependencies when empty when `omitEmpty` is true', () => { 524 | const pkg = { dependencies: {} }; 525 | const res = config.normalize(pkg, { omitEmpty: true }); 526 | assert(!res.dependencies); 527 | }); 528 | }); 529 | 530 | describe('devDependencies', () => { 531 | beforeEach(() => { 532 | config = new Normalizer({ verbose: false }); 533 | }); 534 | 535 | it('should remove empty devDependencies when omitEmpty is true', () => { 536 | const pkg = { devDependencies: {} }; 537 | const res = config.normalize(pkg, { omitEmpty: true }); 538 | assert(!res.devDependencies); 539 | }); 540 | }); 541 | 542 | describe('engineStrict', () => { 543 | beforeEach(() => { 544 | config = new Normalizer({ verbose: false }); 545 | }); 546 | 547 | it('should delete engineStrict and replace it with engine-strict', () => { 548 | const pkg = { engineStrict: true }; 549 | const res = config.normalize(pkg); 550 | assert.equal(typeof res.engineStrict, 'undefined'); 551 | assert.equal(res['engine-strict'], true); 552 | }); 553 | 554 | it('should remove engineStrict from the object', () => { 555 | const pkg = { engineStrict: true }; 556 | const res = config.normalize(pkg); 557 | assert(!res.hasOwnProperty('engineStrict')); 558 | }); 559 | }); 560 | 561 | describe('engine-strict', () => { 562 | beforeEach(() => { 563 | config = new Normalizer({ verbose: false }); 564 | }); 565 | 566 | it('should warn when engine-strict value is invalid', cb => { 567 | const pkg = { 'engine-strict': 'foo' }; 568 | let count = 0; 569 | 570 | config.on('warning', function(method, key, err) { 571 | if (key === 'engine-strict') { 572 | count++; 573 | } 574 | }); 575 | 576 | config.normalize(pkg); 577 | assert.equal(count, 1); 578 | cb(); 579 | }); 580 | }); 581 | 582 | describe('scripts', () => { 583 | beforeEach(() => { 584 | config = new Normalizer({ verbose: false }); 585 | }); 586 | 587 | it('should clean up mocha scripts', () => { 588 | const pkg = { scripts: { test: 'mocha -R spec' } }; 589 | 590 | const res = config.normalize(pkg); 591 | assert(res.scripts); 592 | assert.equal(typeof res.scripts, 'object'); 593 | assert.equal(res.scripts.test, 'mocha'); 594 | }); 595 | 596 | it('should return scripts if it is an object', () => { 597 | const pkg = { scripts: { test: 'foo' } }; 598 | 599 | const res = config.normalize(pkg); 600 | assert(res.scripts); 601 | assert.equal(typeof res.scripts, 'object'); 602 | assert.equal(res.scripts.test, 'foo'); 603 | }); 604 | }); 605 | 606 | describe('keywords', () => { 607 | beforeEach(() => { 608 | config = new Normalizer({ verbose: false }); 609 | }); 610 | 611 | it('should use the name to create keywords when the array is empty', () => { 612 | const pkg = { keywords: [] }; 613 | const res = config.normalize(pkg); 614 | assert.equal(res.keywords[0], 'git'); 615 | assert.equal(res.keywords[1], 'no'); 616 | assert.equal(res.keywords.length, 3); 617 | }); 618 | 619 | it('should sort keywords', () => { 620 | const pkg = { keywords: ['foo', 'bar', 'baz'] }; 621 | const res = config.normalize(pkg); 622 | assert.equal(res.keywords[0], 'bar'); 623 | assert.equal(res.keywords[1], 'baz'); 624 | assert.equal(res.keywords[2], 'foo'); 625 | }); 626 | 627 | it('should remove duplicates', () => { 628 | const pkg = { keywords: ['foo', 'foo', 'foo', 'foo', 'bar', 'baz'] }; 629 | const res = config.normalize(pkg); 630 | assert.equal(res.keywords.length, 3); 631 | }); 632 | }); 633 | 634 | describe('preferGlobal', () => { 635 | beforeEach(() => { 636 | config = new Normalizer({ verbose: false }); 637 | }); 638 | 639 | it('should warn when preferGlobal is defined and `bin` is not defined', cb => { 640 | const pkg = { preferGlobal: true }; 641 | let count = 0; 642 | 643 | config.on('warning', function(method, key, err) { 644 | if (key === 'preferGlobal') { 645 | count++; 646 | } 647 | }); 648 | 649 | const res = config.normalize(pkg); 650 | assert(res.preferGlobal); 651 | assert.equal(count, 1); 652 | cb(); 653 | }); 654 | 655 | it('should not warn when preferGlobal is defined and `bin` is defined', cb => { 656 | const pkg = { preferGlobal: true, bin: 'main.js' }; 657 | let count = 0; 658 | 659 | config.on('warning', function(method, key, err) { 660 | if (key === 'preferGlobal') { 661 | count++; 662 | } 663 | }); 664 | 665 | const res = config.normalize(pkg); 666 | assert(res.preferGlobal); 667 | assert.equal(count, 0); 668 | cb(); 669 | }); 670 | }); 671 | 672 | describe('bin', () => { 673 | beforeEach(() => { 674 | config = new Normalizer({ verbose: false }); 675 | }); 676 | 677 | it('should return bin as-is when it is a string', () => { 678 | const pkg = { bin: 'main.js' }; 679 | 680 | const res = config.normalize(pkg); 681 | assert(res.bin); 682 | assert.equal(res.bin, 'main.js'); 683 | }); 684 | 685 | it('should not emit a warning when bin file string exists', cb => { 686 | const pkg = { bin: 'main.js' }; 687 | let count = 0; 688 | 689 | config.on('warning', function(method, key, err) { 690 | if (key === 'bin') { 691 | count++; 692 | } 693 | }); 694 | 695 | config.normalize(pkg); 696 | assert.equal(count, 0); 697 | cb(); 698 | }); 699 | 700 | it('should not emit a warning when bin file object exists', cb => { 701 | const pkg = { bin: { foo: 'main.js' } }; 702 | let count = 0; 703 | 704 | config.on('warning', function(method, key, err) { 705 | if (key === 'bin') { 706 | count++; 707 | } 708 | }); 709 | 710 | config.normalize(pkg); 711 | assert.equal(count, 0); 712 | cb(); 713 | }); 714 | }); 715 | }); 716 | 717 | -------------------------------------------------------------------------------- /test/test-no-package.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const path = require('path'); 5 | const assert = require('assert'); 6 | const del = require('delete'); 7 | const createRepo = require('./support/git'); 8 | const Normalizer = require('..'); 9 | let config; 10 | 11 | const origCwd = process.cwd(); 12 | const remote = 'https://github.com/jonschlinkert/project-no-package.git'; 13 | const project = path.resolve(__dirname, 'fixtures/project-no-package'); 14 | const gitPath = path.resolve(project, '.git'); 15 | 16 | describe('normalize (no package.json)', () => { 17 | beforeEach(() => { 18 | config = new Normalizer({ verbose: false }); 19 | }); 20 | 21 | before(function(cb) { 22 | process.chdir(project); 23 | createRepo(project, remote, cb); 24 | }); 25 | 26 | after(function(cb) { 27 | process.chdir(origCwd); 28 | del(gitPath, cb); 29 | }); 30 | 31 | describe('omit', () => { 32 | it('should remove a field on options.omit', () => { 33 | config = new Normalizer({ omit: 'version' }); 34 | const res = config.normalize({}); 35 | assert.equal(typeof res.version, 'undefined'); 36 | }); 37 | 38 | it('should remove an array of fields on options.omit', () => { 39 | config = new Normalizer({ omit: ['version', 'main'] }); 40 | const res = config.normalize({}); 41 | assert.equal(typeof res.version, 'undefined'); 42 | assert.equal(typeof res.main, 'undefined'); 43 | }); 44 | }); 45 | 46 | describe('defaults', () => { 47 | it('should add default properties to config', () => { 48 | const res = config.normalize({}); 49 | assert.equal(res.name, 'project-no-package'); 50 | assert.equal(res.version, '0.1.0'); 51 | }); 52 | }); 53 | 54 | describe('name', () => { 55 | it('should use the defined project name', () => { 56 | const pkg = { name: 'foo' }; 57 | const res = config.normalize(pkg); 58 | assert(res.name); 59 | assert.equal(res.name, 'foo'); 60 | }); 61 | 62 | it('should get the project name when string is empty', () => { 63 | const pkg = { name: '' }; 64 | const res = config.normalize(pkg); 65 | assert(res.name); 66 | assert.equal(res.name, 'project-no-package'); 67 | }); 68 | 69 | it('should get the project name when missing', () => { 70 | const pkg = {}; 71 | const res = config.normalize(pkg); 72 | assert(res.name); 73 | assert.equal(res.name, 'project-no-package'); 74 | }); 75 | 76 | it('should use the normalize function defined on options', () => { 77 | const pkg = { name: 'foo' }; 78 | const opts = { 79 | fields: { 80 | name: { 81 | type: ['string'], 82 | normalize: function custom() { 83 | return 'bar'; 84 | } 85 | } 86 | } 87 | }; 88 | 89 | const res = config.normalize(pkg, opts); 90 | assert(res.name); 91 | assert.equal(res.name, 'bar'); 92 | }); 93 | }); 94 | 95 | describe('version', () => { 96 | it('should use the given version', () => { 97 | const pkg = { version: '1.0.0' }; 98 | const res = config.normalize(pkg); 99 | assert(res.version); 100 | assert.equal(res.version, '1.0.0'); 101 | }); 102 | 103 | it('should use the default version', () => { 104 | const pkg = { version: '' }; 105 | const res = config.normalize(pkg); 106 | assert(res.version); 107 | assert.equal(res.version, '0.1.0'); 108 | }); 109 | 110 | it('should emit a warning when version type is invalid', cb => { 111 | const pkg = { version: 5 }; 112 | let count = 0; 113 | 114 | config.on('warning', function(method, key, err) { 115 | if (key === 'version') { 116 | count++; 117 | } 118 | }); 119 | 120 | config.normalize(pkg); 121 | assert.equal(count, 1); 122 | cb(); 123 | }); 124 | 125 | it('should throw an error when version is invalid', cb => { 126 | const pkg = { version: 'foo' }; 127 | try { 128 | config.normalize(pkg); 129 | cb(new Error('expected an error')); 130 | } catch (err) { 131 | assert(/invalid semver/.test(err.message)); 132 | cb(); 133 | } 134 | }); 135 | }); 136 | 137 | describe('main', () => { 138 | it('should remove the property if the file does not exist', () => { 139 | const pkg = { main: 'foo.js' }; 140 | const res = config.normalize(pkg); 141 | assert(!res.hasOwnProperty('main')); 142 | }); 143 | 144 | it('should not remove the property if the file exists', () => { 145 | const pkg = { main: 'main.js' }; 146 | const res = config.normalize(pkg); 147 | assert(res.hasOwnProperty('main')); 148 | }); 149 | 150 | it('should add the main file to the `files` array', () => { 151 | const pkg = { main: 'main.js' }; 152 | const res = config.normalize(pkg); 153 | assert.equal(res.files.indexOf('main.js'), 0); 154 | }); 155 | 156 | it('should not add main file to files array when main file does not exist', () => { 157 | const pkg = { 158 | files: [], 159 | main: 'index.js' 160 | }; 161 | 162 | const res = config.normalize(pkg); 163 | assert(!res.hasOwnProperty('files')); 164 | }); 165 | 166 | it('should add main file to files array if files array is empty', () => { 167 | const pkg = { 168 | files: [], 169 | main: 'main.js' 170 | }; 171 | 172 | const res = config.normalize(pkg); 173 | assert.equal(res.files.length, 1); 174 | assert.equal(res.files[0], 'main.js'); 175 | }); 176 | 177 | it('should create files array with main if undefined', () => { 178 | const pkg = { 179 | main: 'main.js' 180 | }; 181 | 182 | const res = config.normalize(pkg); 183 | assert(res.files.length); 184 | assert(res.files.indexOf('main.js') !== -1); 185 | }); 186 | 187 | it('should not double add the file to files', () => { 188 | const pkg = { 189 | files: ['main.js'], 190 | main: 'main.js' 191 | }; 192 | 193 | const res = config.normalize(pkg); 194 | assert.equal(res.files.length, 1); 195 | assert(res.files.indexOf('main.js') !== -1); 196 | }); 197 | 198 | it('should remove main if the file does not exist', () => { 199 | const pkg = { main: 'foo.js' }; 200 | 201 | const res = config.normalize(pkg); 202 | assert(!res.main); 203 | }); 204 | 205 | it('should do nothing if not defined', () => { 206 | const pkg = {}; 207 | 208 | const res = config.normalize(pkg); 209 | assert.equal(typeof res.main, 'undefined'); 210 | }); 211 | }); 212 | 213 | describe('files', () => { 214 | it('should remove a file if it does not exist', () => { 215 | const pkg = { files: ['foo.js', 'main.js'] }; 216 | const res = config.normalize(pkg); 217 | assert.equal(res.files.length, 1); 218 | }); 219 | 220 | it('should remove the files array if it\'s empty', () => { 221 | const pkg = { files: [] }; 222 | const res = config.normalize(pkg); 223 | assert(!res.files); 224 | }); 225 | 226 | it('should remove the files array if a file that does not exist is removed', () => { 227 | const pkg = { files: ['foo.js'] }; 228 | const res = config.normalize(pkg); 229 | assert(!res.files); 230 | }); 231 | }); 232 | 233 | describe('homepage', () => { 234 | it('should add a homepage from git repository', () => { 235 | const res = config.normalize({}); 236 | assert(res.homepage); 237 | assert.equal(res.homepage, 'https://github.com/jonschlinkert/project-no-package'); 238 | }); 239 | 240 | it('should add repository when setting homepage', () => { 241 | const res = config.normalize({}); 242 | assert(res.homepage); 243 | assert.equal(res.repository, 'jonschlinkert/project-no-package'); 244 | }); 245 | 246 | it('should use the given homepage', () => { 247 | const pkg = { homepage: 'https://github.com/assemble/assemble' }; 248 | const res = config.normalize(pkg); 249 | assert(res.homepage); 250 | assert.equal(res.homepage, 'https://github.com/assemble/assemble'); 251 | }); 252 | 253 | it('should get homepage from repository.url', () => { 254 | const pkg = { 255 | homepage: '', 256 | repository: 'git://github.com/jonschlinkert/project-no-package.git' 257 | }; 258 | 259 | const res = config.normalize(pkg); 260 | assert(res.homepage); 261 | assert.equal(res.homepage, 'https://github.com/jonschlinkert/project-no-package'); 262 | }); 263 | }); 264 | 265 | describe('author', () => { 266 | it('should not add an empty author field', () => { 267 | const res = config.normalize({}); 268 | assert(!res.hasOwnProperty('author')); 269 | }); 270 | 271 | it('should return an author string as is', () => { 272 | const pkg = { author: 'Jon Schlinkert' }; 273 | const res = config.normalize(pkg); 274 | assert.equal(res.author, 'Jon Schlinkert'); 275 | }); 276 | 277 | it('should convert an author object to a string', () => { 278 | const pkg = { 279 | author: { 280 | name: 'Jon Schlinkert', 281 | url: 'https://github.com/jonschlinkert' 282 | } 283 | }; 284 | 285 | const res = config.normalize(pkg); 286 | assert.equal(res.author, 'Jon Schlinkert (https://github.com/jonschlinkert)'); 287 | }); 288 | }); 289 | 290 | describe('maintainers', () => { 291 | it('should not add an empty maintainers field', () => { 292 | const res = config.normalize({}); 293 | assert(!res.hasOwnProperty('maintainers')); 294 | }); 295 | }); 296 | 297 | describe('license', () => { 298 | it('should add MIT as the default license', () => { 299 | const res = config.normalize({}); 300 | assert(res.hasOwnProperty('license')); 301 | assert.equal(res.license, 'MIT'); 302 | }); 303 | 304 | it('should return license as is if it is a string', () => { 305 | const res = config.normalize({ license: 'MIT' }); 306 | assert(res.hasOwnProperty('license')); 307 | assert.equal(res.license, 'MIT'); 308 | }); 309 | 310 | it('should convert from an object to a string', () => { 311 | const res = config.normalize({ license: { type: 'MIT' } }); 312 | assert(res.hasOwnProperty('license')); 313 | assert.equal(res.license, 'MIT'); 314 | }); 315 | 316 | it('should convert from an array to a string', () => { 317 | const res = config.normalize({ license: [{ type: 'MIT' }] }); 318 | assert(res.hasOwnProperty('license')); 319 | assert.equal(res.license, 'MIT'); 320 | }); 321 | }); 322 | 323 | describe('people', () => { 324 | beforeEach(() => { 325 | config = new Normalizer({ verbose: false }); 326 | }); 327 | 328 | describe('contributors', () => { 329 | it('should not add an empty contributors field', () => { 330 | const res = config.normalize({}); 331 | assert(!res.hasOwnProperty('contributors')); 332 | }); 333 | 334 | it('should stringify authors', () => { 335 | const pkg = { 336 | contributors: [{ 337 | name: 'Jon Schlinkert', 338 | url: 'https://github.com/jonschlinkert' 339 | }] 340 | }; 341 | 342 | const res = config.normalize(pkg); 343 | assert.equal(res.contributors[0], 'Jon Schlinkert (https://github.com/jonschlinkert)'); 344 | }); 345 | }); 346 | }); 347 | 348 | describe('repository', () => { 349 | it('should use the given repository', () => { 350 | const pkg = { repository: 'jonschlinkert/foo' }; 351 | const res = config.normalize(pkg); 352 | assert(res.repository); 353 | assert.equal(res.repository, 'jonschlinkert/foo'); 354 | }); 355 | 356 | it('should use the git remote url', () => { 357 | const pkg = { repository: '' }; 358 | const res = config.normalize(pkg); 359 | assert(res.repository); 360 | assert.equal(res.repository, 'jonschlinkert/project-no-package'); 361 | }); 362 | 363 | it('should convert repository.url to a string', () => { 364 | const pkg = { repository: { url: 'https://github.com/jonschlinkert/foo.git' } }; 365 | const res = config.normalize(pkg); 366 | assert(res.repository); 367 | assert.equal(res.repository, 'jonschlinkert/foo'); 368 | }); 369 | }); 370 | 371 | describe('bugs', () => { 372 | beforeEach(() => { 373 | config = new Normalizer({ verbose: false }); 374 | }); 375 | 376 | it('should return bugs value ', () => { 377 | const pkg = { bugs: { url: 'jonschlinkert/foo' } }; 378 | 379 | const res = config.normalize(pkg); 380 | assert(res.bugs); 381 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 382 | }); 383 | 384 | it('should use the given bugs value', () => { 385 | const opts = { bugs: { url: 'jonschlinkert/foo' } }; 386 | const res = config.normalize({}, opts); 387 | assert(res.bugs); 388 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 389 | }); 390 | 391 | it('should use the value function passed on options', () => { 392 | const pkg = { bugs: '' }; 393 | const res = config.normalize(pkg, { 394 | fields: { 395 | bugs: { 396 | type: ['string', 'object'], 397 | normalize: function custom() { 398 | return { url: 'abc' }; 399 | } 400 | } 401 | } 402 | }); 403 | assert(res.bugs); 404 | assert.equal(res.bugs.url, 'abc'); 405 | }); 406 | 407 | it('should use a custom type passed on options', () => { 408 | const pkg = { bugs: '', repository: 'https://github.com/foo' }; 409 | const res = config.normalize(pkg, { 410 | extend: false, 411 | fields: { 412 | bugs: { 413 | type: ['object', 'string'], 414 | normalize: function custom(key, val, config, schema) { 415 | schema.update('repository', config); 416 | const bugs = {}; 417 | bugs.url = config.repository + '/bugs'; 418 | return bugs; 419 | } 420 | } 421 | } 422 | }); 423 | 424 | assert.equal(typeof res.bugs, 'object'); 425 | assert(res.bugs.url); 426 | assert.equal(res.bugs.url, 'https://github.com/foo/bugs'); 427 | }); 428 | 429 | it('should convert bugs.url to a string when specified', () => { 430 | const pkg = { bugs: { url: 'https://github.com/jonschlinkert/foo.git' } }; 431 | const res = config.normalize(pkg, { 432 | extend: false, 433 | fields: { 434 | bugs: { 435 | type: 'string', 436 | normalize: function(val, key, config) { 437 | return val.url; 438 | } 439 | } 440 | } 441 | }); 442 | assert(res.bugs); 443 | assert.equal(res.bugs, 'https://github.com/jonschlinkert/foo.git'); 444 | }); 445 | }); 446 | 447 | describe('license', () => { 448 | beforeEach(() => { 449 | config = new Normalizer({ verbose: false }); 450 | }); 451 | 452 | it('should convert a license object to a string', () => { 453 | const pkg = { 454 | license: { 455 | type: 'MIT', 456 | url: 'https://github.com/jonschlinkert/project-no-package/blob/master/LICENSE-MIT' 457 | } 458 | }; 459 | 460 | const res = config.normalize(pkg); 461 | assert.equal(typeof res.license, 'string'); 462 | assert.equal(res.license, 'MIT'); 463 | }); 464 | }); 465 | 466 | describe('licenses', () => { 467 | beforeEach(() => { 468 | config = new Normalizer({ verbose: false }); 469 | }); 470 | 471 | it('should emit a deprecation warning when licenses is defined', cb => { 472 | const pkg = { licenses: { type: 'MIT' } }; 473 | let count = 0; 474 | 475 | config.on('warning', function(method, key, err) { 476 | if (key === 'licenses') { 477 | count++; 478 | } 479 | }); 480 | 481 | config.normalize(pkg); 482 | assert.equal(count, 1); 483 | cb(); 484 | }); 485 | 486 | it('should convert a licenses array to a license string', () => { 487 | const pkg = { 488 | licenses: [ 489 | { type: 'MIT', url: 'https://github.com/jonschlinkert/project-no-package/blob/master/LICENSE-MIT' } 490 | ] 491 | }; 492 | 493 | const res = config.normalize(pkg); 494 | assert(!res.licenses); 495 | assert(res.license); 496 | assert.equal(typeof res.license, 'string'); 497 | assert.equal(res.license, 'MIT'); 498 | }); 499 | 500 | it('should convert from an object to a string', () => { 501 | const pkg = { 502 | licenses: { type: 'MIT', url: 'https://github.com/jonschlinkert/project-no-package/blob/master/LICENSE-MIT' } 503 | }; 504 | 505 | const res = config.normalize(pkg); 506 | assert(!res.licenses); 507 | assert(res.license); 508 | assert.equal(typeof res.license, 'string'); 509 | assert.equal(res.license, 'MIT'); 510 | }); 511 | }); 512 | 513 | describe('dependencies', () => { 514 | beforeEach(() => { 515 | config = new Normalizer({ verbose: false }); 516 | }); 517 | 518 | it('should remove dependencies when empty when `omitEmpty` is true', () => { 519 | const pkg = { dependencies: {} }; 520 | const res = config.normalize(pkg, { omitEmpty: true }); 521 | assert(!res.dependencies); 522 | }); 523 | }); 524 | 525 | describe('devDependencies', () => { 526 | beforeEach(() => { 527 | config = new Normalizer({ verbose: false }); 528 | }); 529 | 530 | it('should remove empty devDependencies when omitEmpty is true', () => { 531 | const pkg = { devDependencies: {} }; 532 | const res = config.normalize(pkg, { omitEmpty: true }); 533 | assert(!res.devDependencies); 534 | }); 535 | }); 536 | 537 | describe('engineStrict', () => { 538 | beforeEach(() => { 539 | config = new Normalizer({ verbose: false }); 540 | }); 541 | 542 | it('should delete engineStrict and replace it with engine-strict', () => { 543 | const pkg = { engineStrict: true }; 544 | const res = config.normalize(pkg); 545 | assert.equal(typeof res.engineStrict, 'undefined'); 546 | assert.equal(res['engine-strict'], true); 547 | }); 548 | 549 | it('should remove engineStrict from the object', () => { 550 | const pkg = { engineStrict: true }; 551 | const res = config.normalize(pkg); 552 | assert(!res.hasOwnProperty('engineStrict')); 553 | }); 554 | }); 555 | 556 | describe('engine-strict', () => { 557 | beforeEach(() => { 558 | config = new Normalizer({ verbose: false }); 559 | }); 560 | 561 | it('should warn when engine-strict value is invalid', cb => { 562 | const pkg = { 'engine-strict': 'foo' }; 563 | let count = 0; 564 | 565 | config.on('warning', function(method, key, err) { 566 | if (key === 'engine-strict') { 567 | count++; 568 | } 569 | }); 570 | 571 | config.normalize(pkg); 572 | assert.equal(count, 1); 573 | cb(); 574 | }); 575 | }); 576 | 577 | describe('scripts', () => { 578 | beforeEach(() => { 579 | config = new Normalizer({ verbose: false }); 580 | }); 581 | 582 | it('should clean up mocha scripts', () => { 583 | const pkg = { scripts: { test: 'mocha -R spec' } }; 584 | 585 | const res = config.normalize(pkg); 586 | assert(res.scripts); 587 | assert.equal(typeof res.scripts, 'object'); 588 | assert.equal(res.scripts.test, 'mocha'); 589 | }); 590 | 591 | it('should return scripts if it is an object', () => { 592 | const pkg = { scripts: { test: 'foo' } }; 593 | 594 | const res = config.normalize(pkg); 595 | assert(res.scripts); 596 | assert.equal(typeof res.scripts, 'object'); 597 | assert.equal(res.scripts.test, 'foo'); 598 | }); 599 | }); 600 | 601 | describe('keywords', () => { 602 | beforeEach(() => { 603 | config = new Normalizer({ verbose: false }); 604 | }); 605 | 606 | it('should use the name to create keywords when the array is empty', () => { 607 | const pkg = { keywords: [] }; 608 | const res = config.normalize(pkg); 609 | assert.equal(res.keywords[0], 'no'); 610 | assert.equal(res.keywords[1], 'package'); 611 | assert.equal(res.keywords.length, 3); 612 | }); 613 | 614 | it('should sort keywords', () => { 615 | const pkg = { keywords: ['foo', 'bar', 'baz'] }; 616 | const res = config.normalize(pkg); 617 | assert.equal(res.keywords[0], 'bar'); 618 | assert.equal(res.keywords[1], 'baz'); 619 | assert.equal(res.keywords[2], 'foo'); 620 | }); 621 | 622 | it('should remove duplicates', () => { 623 | const pkg = { keywords: ['foo', 'foo', 'foo', 'foo', 'bar', 'baz'] }; 624 | const res = config.normalize(pkg); 625 | assert.equal(res.keywords.length, 3); 626 | }); 627 | }); 628 | 629 | describe('preferGlobal', () => { 630 | beforeEach(() => { 631 | config = new Normalizer({ verbose: false }); 632 | }); 633 | 634 | it('should warn when preferGlobal is defined and `bin` is not defined', cb => { 635 | const pkg = { preferGlobal: true }; 636 | let count = 0; 637 | 638 | config.on('warning', function(method, key, err) { 639 | if (key === 'preferGlobal') { 640 | count++; 641 | } 642 | }); 643 | 644 | const res = config.normalize(pkg); 645 | assert(res.preferGlobal); 646 | assert.equal(count, 1); 647 | cb(); 648 | }); 649 | 650 | it('should not warn when preferGlobal is defined and `bin` is defined', cb => { 651 | const pkg = { preferGlobal: true, bin: 'main.js' }; 652 | let count = 0; 653 | 654 | config.on('warning', function(method, key, err) { 655 | if (key === 'preferGlobal') { 656 | count++; 657 | } 658 | }); 659 | 660 | const res = config.normalize(pkg); 661 | assert(res.preferGlobal); 662 | assert.equal(count, 0); 663 | cb(); 664 | }); 665 | 666 | it('should return bin as-is when it is a string', () => { 667 | const pkg = { bin: 'main.js' }; 668 | 669 | const res = config.normalize(pkg); 670 | assert(res.bin); 671 | assert.equal(res.bin, 'main.js'); 672 | }); 673 | }); 674 | 675 | describe('bin', () => { 676 | beforeEach(() => { 677 | config = new Normalizer({ verbose: false }); 678 | }); 679 | 680 | it('should not emit a warning when bin file string exists', cb => { 681 | const pkg = { bin: 'main.js' }; 682 | let count = 0; 683 | 684 | config.on('warning', function(method, key, err) { 685 | if (key === 'bin') { 686 | count++; 687 | } 688 | }); 689 | 690 | config.normalize(pkg); 691 | assert.equal(count, 0); 692 | cb(); 693 | }); 694 | 695 | it('should not emit a warning when bin file object exists', cb => { 696 | const pkg = { bin: { foo: 'main.js' } }; 697 | let count = 0; 698 | 699 | config.on('warning', function(method, key, err) { 700 | if (key === 'bin') { 701 | count++; 702 | } 703 | }); 704 | 705 | config.normalize(pkg); 706 | assert.equal(count, 0); 707 | cb(); 708 | }); 709 | }); 710 | }); 711 | 712 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const path = require('path'); 5 | const assert = require('assert'); 6 | const Normalizer = require('..'); 7 | const createRepo = require('./support/git'); 8 | const del = require('delete'); 9 | let config; 10 | 11 | const origCwd = process.cwd(); 12 | const remote = 'https://github.com/jonschlinkert/test-project.git'; 13 | const project = path.resolve(__dirname, 'fixtures/project'); 14 | const gitPath = path.resolve(project, '.git'); 15 | 16 | describe('normalize', () => { 17 | beforeEach(() => { 18 | config = new Normalizer({ verbose: false }); 19 | }); 20 | 21 | before(function(cb) { 22 | process.chdir(project); 23 | createRepo(project, remote, cb); 24 | }); 25 | 26 | after(function(cb) { 27 | process.chdir(origCwd); 28 | del(gitPath, cb); 29 | }); 30 | 31 | describe('Normalizer', () => { 32 | it('should instantiate with an options object', () => { 33 | config = new Normalizer({ foo: 'bar' }); 34 | assert.equal(config.options.foo, 'bar'); 35 | }); 36 | 37 | it('should instantiate without an options object', () => { 38 | config = new Normalizer(); 39 | assert.deepEqual(config.options, {}); 40 | }); 41 | }); 42 | 43 | describe('.field', () => { 44 | it('should add a custom field', () => { 45 | config = new Normalizer({ omit: 'version' }); 46 | config.field('foo', 'string', { 47 | normalize: function(val, key, config, schema) { 48 | config[key] = { a: 'b' }; 49 | return config[key]; 50 | } 51 | }); 52 | 53 | const res = config.normalize({}); 54 | assert.equal(res.foo.a, 'b'); 55 | }); 56 | 57 | it('should convert a function to a `normalize` function', () => { 58 | config = new Normalizer({ omit: 'version' }); 59 | config.field('foo', 'string', function(val, key, config, schema) { 60 | config[key] = { a: 'b' }; 61 | return config[key]; 62 | }); 63 | 64 | const res = config.normalize({}); 65 | assert.equal(res.foo.a, 'b'); 66 | }); 67 | 68 | it('should remove an array of fields on options.omit', () => { 69 | config = new Normalizer({ omit: ['version', 'main'] }); 70 | const res = config.normalize({}); 71 | assert.equal(typeof res.version, 'undefined'); 72 | assert.equal(typeof res.main, 'undefined'); 73 | }); 74 | 75 | it('should extend an existing field', () => { 76 | config = new Normalizer({ knownOnly: true }); 77 | config.schema.fields = {}; 78 | 79 | config.field('foo', 'string', { 80 | normalize: function(val) { 81 | return val + ':bar'; 82 | } 83 | }); 84 | 85 | config.field('foo', 'string', { 86 | extend: true, 87 | default: 'foo' 88 | }); 89 | 90 | const res = config.normalize({}); 91 | assert.equal(res.foo, 'foo:bar'); 92 | }); 93 | }); 94 | 95 | describe('options.omit', () => { 96 | it('should remove a field on options.omit', () => { 97 | config = new Normalizer({ omit: 'version' }); 98 | const res = config.normalize({}); 99 | assert.equal(typeof res.version, 'undefined'); 100 | }); 101 | 102 | it('should remove an array of fields on options.omit', () => { 103 | config = new Normalizer({ omit: ['version', 'main'] }); 104 | const res = config.normalize({}); 105 | assert.equal(typeof res.version, 'undefined'); 106 | assert.equal(typeof res.main, 'undefined'); 107 | }); 108 | }); 109 | 110 | describe('defaults', () => { 111 | it('should add default properties to config', () => { 112 | const res = config.normalize({}); 113 | assert.equal(res.name, 'test-project'); 114 | assert.equal(res.version, '0.1.0'); 115 | }); 116 | }); 117 | 118 | describe('package.json', () => { 119 | it('should get package.json when no args are passed', () => { 120 | const res = config.normalize(); 121 | assert.equal(res.name, 'test-project'); 122 | }); 123 | 124 | it('should get package.json from a cwd', () => { 125 | const res = config.normalize(process.cwd()); 126 | assert.equal(res.name, 'test-project'); 127 | }); 128 | 129 | it('should get the cwd when dir exists but path does not exist', () => { 130 | const res = config.normalize(path.resolve(process.cwd(), 'index.js')); 131 | assert.equal(res.name, 'test-project'); 132 | }); 133 | 134 | it('should get the cwd from a file path', () => { 135 | const res = config.normalize(path.resolve(process.cwd(), 'main.js')); 136 | assert.equal(res.name, 'test-project'); 137 | }); 138 | }); 139 | 140 | describe('name', () => { 141 | it('should use the defined project name', () => { 142 | const pkg = { name: 'foo' }; 143 | const res = config.normalize(pkg); 144 | assert(res.name); 145 | assert.equal(res.name, 'foo'); 146 | }); 147 | 148 | it('should get the project name when string is empty', () => { 149 | const pkg = { name: '' }; 150 | const res = config.normalize(pkg); 151 | assert(res.name); 152 | assert.equal(res.name, 'test-project'); 153 | }); 154 | 155 | it('should get the project name when missing', () => { 156 | const pkg = {}; 157 | const res = config.normalize(pkg); 158 | assert(res.name); 159 | assert.equal(res.name, 'test-project'); 160 | }); 161 | 162 | it('should use the normalize function defined on options', () => { 163 | const pkg = { name: 'foo' }; 164 | const opts = { 165 | fields: { 166 | name: { 167 | type: ['string'], 168 | normalize: function custom() { 169 | return 'bar'; 170 | } 171 | } 172 | } 173 | }; 174 | 175 | const res = config.normalize(pkg, opts); 176 | assert(res.name); 177 | assert.equal(res.name, 'bar'); 178 | }); 179 | }); 180 | 181 | describe('version', () => { 182 | it('should use the given version', () => { 183 | const pkg = { version: '1.0.0' }; 184 | const res = config.normalize(pkg); 185 | assert(res.version); 186 | assert.equal(res.version, '1.0.0'); 187 | }); 188 | 189 | it('should use the default version', () => { 190 | const pkg = { version: '' }; 191 | const res = config.normalize(pkg); 192 | assert(res.version); 193 | assert.equal(res.version, '0.1.0'); 194 | }); 195 | 196 | it('should emit a warning when version type is invalid', cb => { 197 | const pkg = { version: 5 }; 198 | let count = 0; 199 | 200 | config.on('warning', function(method, key, err) { 201 | if (key === 'version') { 202 | count++; 203 | } 204 | }); 205 | 206 | config.normalize(pkg); 207 | assert.equal(count, 1); 208 | cb(); 209 | }); 210 | 211 | it('should throw an error when version is invalid', cb => { 212 | const pkg = { version: 'foo' }; 213 | try { 214 | config.normalize(pkg); 215 | cb(new Error('expected an error')); 216 | } catch (err) { 217 | assert(/invalid semver/.test(err.message)); 218 | cb(); 219 | } 220 | }); 221 | }); 222 | 223 | describe('main', () => { 224 | it('should remove the property if the file does not exist', () => { 225 | const pkg = { main: 'foo.js' }; 226 | const res = config.normalize(pkg); 227 | assert(!res.hasOwnProperty('main')); 228 | }); 229 | 230 | it('should not remove the property if the file exists', () => { 231 | const pkg = { main: 'main.js' }; 232 | const res = config.normalize(pkg); 233 | assert(res.hasOwnProperty('main')); 234 | }); 235 | 236 | it('should add the main file to the `files` array', () => { 237 | const pkg = { main: 'main.js' }; 238 | const res = config.normalize(pkg); 239 | assert.equal(res.files.indexOf('main.js'), 0); 240 | }); 241 | 242 | it('should not add main file to files array when main file does not exist', () => { 243 | const pkg = { 244 | files: [], 245 | main: 'index.js' 246 | }; 247 | 248 | const res = config.normalize(pkg); 249 | assert(!res.hasOwnProperty('files')); 250 | }); 251 | 252 | it('should add main file to files array if files array is empty', () => { 253 | const pkg = { 254 | files: [], 255 | main: 'main.js' 256 | }; 257 | 258 | const res = config.normalize(pkg); 259 | assert.equal(res.files.length, 1); 260 | assert.equal(res.files[0], 'main.js'); 261 | }); 262 | 263 | it('should create files array with main if undefined', () => { 264 | const pkg = { 265 | main: 'main.js' 266 | }; 267 | 268 | const res = config.normalize(pkg); 269 | assert(res.files.length); 270 | assert(res.files.indexOf('main.js') !== -1); 271 | }); 272 | 273 | it('should not double add the file to files', () => { 274 | const pkg = { 275 | files: ['main.js'], 276 | main: 'main.js' 277 | }; 278 | 279 | const res = config.normalize(pkg); 280 | assert.equal(res.files.length, 1); 281 | assert(res.files.indexOf('main.js') !== -1); 282 | }); 283 | 284 | it('should remove main if the file does not exist', () => { 285 | const pkg = { main: 'foo.js' }; 286 | 287 | const res = config.normalize(pkg); 288 | assert(!res.main); 289 | }); 290 | 291 | it('should do nothing if not defined', () => { 292 | const pkg = {}; 293 | 294 | const res = config.normalize(pkg); 295 | assert.equal(typeof res.main, 'undefined'); 296 | }); 297 | }); 298 | 299 | describe('files', () => { 300 | it('should remove a file if it does not exist', () => { 301 | const pkg = { files: ['foo.js', 'main.js'] }; 302 | const res = config.normalize(pkg); 303 | assert.equal(res.files.length, 1); 304 | }); 305 | 306 | it('should use `options.files`', () => { 307 | const res = config.normalize({}, { files: ['main.js'] }); 308 | assert.equal(res.files.length, 1); 309 | }); 310 | 311 | it('should remove the files array if it\'s empty', () => { 312 | const pkg = { files: [] }; 313 | const res = config.normalize(pkg); 314 | assert(!res.files); 315 | }); 316 | 317 | it('should remove the files array if a file that does not exist is removed', () => { 318 | const pkg = { files: ['foo.js'] }; 319 | const res = config.normalize(pkg); 320 | assert(!res.files); 321 | }); 322 | }); 323 | 324 | describe('homepage', () => { 325 | it('should add a homepage from git repository', () => { 326 | const res = config.normalize({}); 327 | assert(res.homepage); 328 | assert.equal(res.homepage, 'https://github.com/jonschlinkert/test-project'); 329 | }); 330 | 331 | it('should add repository when setting homepage', () => { 332 | const res = config.normalize({}); 333 | assert(res.homepage); 334 | assert.equal(res.repository, 'jonschlinkert/test-project'); 335 | }); 336 | 337 | it('should use the given homepage', () => { 338 | const pkg = { homepage: 'https://github.com/assemble/assemble' }; 339 | const res = config.normalize(pkg); 340 | assert(res.homepage); 341 | assert.equal(res.homepage, 'https://github.com/assemble/assemble'); 342 | }); 343 | 344 | it('should get homepage from repository.url', () => { 345 | const pkg = { 346 | homepage: '', 347 | repository: 'git://github.com/jonschlinkert/test-project.git' 348 | }; 349 | 350 | const res = config.normalize(pkg); 351 | assert(res.homepage); 352 | assert.equal(res.homepage, 'https://github.com/jonschlinkert/test-project'); 353 | }); 354 | }); 355 | 356 | describe('author', () => { 357 | it('should not add an empty author field', () => { 358 | const res = config.normalize({}); 359 | assert(!res.hasOwnProperty('author')); 360 | }); 361 | 362 | it('should not add an empty authors field', () => { 363 | const res = config.normalize({}); 364 | assert(!res.hasOwnProperty('authors')); 365 | }); 366 | 367 | it('should use the given author as a string', () => { 368 | const pkg = { author: 'Jon Schlinkert' }; 369 | const res = config.normalize(pkg); 370 | assert(res.author); 371 | assert.equal(res.author, 'Jon Schlinkert'); 372 | }); 373 | 374 | it('should convert an author object to a string', () => { 375 | const pkg = { 376 | author: { 377 | name: 'Jon Schlinkert', 378 | url: 'https://github.com/jonschlinkert' 379 | } 380 | }; 381 | 382 | const res = config.normalize(pkg); 383 | assert(res.author); 384 | assert.equal(res.author, 'Jon Schlinkert (https://github.com/jonschlinkert)'); 385 | }); 386 | }); 387 | 388 | describe('maintainers', () => { 389 | it('should not add an empty maintainers field', () => { 390 | const res = config.normalize({}); 391 | assert(!res.hasOwnProperty('maintainers')); 392 | }); 393 | }); 394 | 395 | describe('license', () => { 396 | it('should add MIT as the default license', () => { 397 | const res = config.normalize({}); 398 | assert(res.hasOwnProperty('license')); 399 | assert.equal(res.license, 'MIT'); 400 | }); 401 | 402 | it('should return license as is if it is a string', () => { 403 | const res = config.normalize({ license: 'MIT' }); 404 | assert(res.hasOwnProperty('license')); 405 | assert.equal(res.license, 'MIT'); 406 | }); 407 | 408 | it('should convert from an object to a string', () => { 409 | const res = config.normalize({ license: { type: 'MIT' } }); 410 | assert(res.hasOwnProperty('license')); 411 | assert.equal(res.license, 'MIT'); 412 | }); 413 | 414 | it('should convert from an array to a string', () => { 415 | const res = config.normalize({ license: [{ type: 'MIT' }] }); 416 | assert(res.hasOwnProperty('license')); 417 | assert.equal(res.license, 'MIT'); 418 | }); 419 | }); 420 | 421 | describe('people', () => { 422 | beforeEach(() => { 423 | config = new Normalizer({ verbose: false }); 424 | }); 425 | 426 | describe('contributors', () => { 427 | it('should not add an empty contributors field', () => { 428 | const res = config.normalize({}); 429 | assert(!res.hasOwnProperty('contributors')); 430 | }); 431 | 432 | it('should convert contributor objects to strings', () => { 433 | const pkg = { 434 | contributors: [{ 435 | name: 'Jon Schlinkert', 436 | url: 'https://github.com/jonschlinkert' 437 | }] 438 | }; 439 | const res = config.normalize(pkg); 440 | assert(res.contributors); 441 | const expected = 'Jon Schlinkert (https://github.com/jonschlinkert)'; 442 | assert.equal(res.contributors[0], expected); 443 | }); 444 | }); 445 | }); 446 | 447 | describe('repository', () => { 448 | it('should use the given repository', () => { 449 | const pkg = { repository: 'jonschlinkert/foo' }; 450 | const res = config.normalize(pkg); 451 | assert(res.repository); 452 | assert.equal(res.repository, 'jonschlinkert/foo'); 453 | }); 454 | 455 | it('should use the git remote url', () => { 456 | const pkg = { repository: '' }; 457 | const res = config.normalize(pkg); 458 | assert(res.repository); 459 | assert.equal(res.repository, 'jonschlinkert/test-project'); 460 | }); 461 | 462 | it('should convert repository.url to a string', () => { 463 | const pkg = { repository: { url: 'https://github.com/jonschlinkert/foo.git' } }; 464 | const res = config.normalize(pkg); 465 | assert(res.repository); 466 | assert.equal(res.repository, 'jonschlinkert/foo'); 467 | }); 468 | }); 469 | 470 | describe('bugs', () => { 471 | beforeEach(() => { 472 | config = new Normalizer({ verbose: false }); 473 | }); 474 | 475 | it('should fix the bugs value based on repo information', () => { 476 | const pkg = { bugs: { url: 'jonschlinkert/foo' } }; 477 | const res = config.normalize(pkg); 478 | assert(res.bugs); 479 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 480 | }); 481 | 482 | it('should use the given bugs value', () => { 483 | const opts = { bugs: { url: 'jonschlinkert/foo' } }; 484 | const res = config.normalize({}, opts); 485 | assert(res.bugs); 486 | assert.equal(res.bugs.url, 'https://github.com/jonschlinkert/foo/issues'); 487 | }); 488 | 489 | it('should use the value function passed on options', () => { 490 | const pkg = { bugs: '' }; 491 | const res = config.normalize(pkg, { 492 | fields: { 493 | bugs: { 494 | type: ['string', 'object'], 495 | normalize: function custom() { 496 | return { url: 'abc' }; 497 | } 498 | } 499 | } 500 | }); 501 | assert(res.bugs); 502 | assert.equal(res.bugs.url, 'abc'); 503 | }); 504 | 505 | it('should use a custom type passed on options', () => { 506 | const pkg = { bugs: '', owner: 'foo' }; 507 | const res = config.normalize(pkg, { 508 | extend: false, 509 | fields: { 510 | bugs: { 511 | type: ['object', 'string'], 512 | normalize: function custom(key, val, config, schema) { 513 | schema.update('repository', config); 514 | const bugs = {}; 515 | bugs.url = config.homepage + '/bugs'; 516 | return bugs; 517 | } 518 | } 519 | } 520 | }); 521 | 522 | assert.equal(typeof res.bugs, 'object'); 523 | assert(res.bugs.url); 524 | assert.equal(res.bugs.url, 'https://github.com/foo/test-project/bugs'); 525 | }); 526 | 527 | it('should use owner passed on options', () => { 528 | const pkg = { bugs: '' }; 529 | const res = config.normalize(pkg, { 530 | owner: 'foo', 531 | extend: false, 532 | fields: { 533 | bugs: { 534 | type: ['object', 'string'], 535 | normalize: function custom(key, val, config, schema) { 536 | schema.update('repository', config); 537 | const bugs = {}; 538 | bugs.url = config.homepage + '/bugs'; 539 | return bugs; 540 | } 541 | } 542 | } 543 | }); 544 | 545 | assert.equal(typeof res.bugs, 'object'); 546 | assert(res.bugs.url); 547 | assert.equal(res.bugs.url, 'https://github.com/foo/test-project/bugs'); 548 | }); 549 | 550 | it('should convert bugs.url to a string when specified', () => { 551 | const pkg = { bugs: { url: 'https://github.com/jonschlinkert/foo.git' } }; 552 | const res = config.normalize(pkg, { 553 | extend: false, 554 | fields: { 555 | bugs: { 556 | type: 'string', 557 | normalize: function(val, key, config) { 558 | return val.url; 559 | } 560 | } 561 | } 562 | }); 563 | assert(res.bugs); 564 | assert.equal(res.bugs, 'https://github.com/jonschlinkert/foo.git'); 565 | }); 566 | }); 567 | 568 | describe('license', () => { 569 | beforeEach(() => { 570 | config = new Normalizer({ verbose: false }); 571 | }); 572 | 573 | it('should convert a license object to a string', () => { 574 | const pkg = { 575 | license: { 576 | type: 'MIT', 577 | url: 'https://github.com/jonschlinkert/test-project/blob/master/LICENSE-MIT' 578 | } 579 | }; 580 | 581 | const res = config.normalize(pkg); 582 | assert.equal(typeof res.license, 'string'); 583 | assert.equal(res.license, 'MIT'); 584 | }); 585 | }); 586 | 587 | describe('licenses', () => { 588 | beforeEach(() => { 589 | config = new Normalizer({ verbose: false }); 590 | }); 591 | 592 | it('should emit a deprecation warning when licenses is defined', cb => { 593 | const pkg = { licenses: { type: 'MIT' } }; 594 | let count = 0; 595 | 596 | config.on('warning', function(method, key, err) { 597 | if (key === 'licenses') { 598 | count++; 599 | } 600 | }); 601 | 602 | config.normalize(pkg); 603 | assert.equal(count, 1); 604 | cb(); 605 | }); 606 | 607 | it('should convert a licenses array to a license string', () => { 608 | const pkg = { 609 | licenses: [ 610 | { type: 'MIT', url: 'https://github.com/jonschlinkert/test-project/blob/master/LICENSE-MIT' } 611 | ] 612 | }; 613 | 614 | const res = config.normalize(pkg); 615 | assert(!res.licenses); 616 | assert(res.license); 617 | assert.equal(typeof res.license, 'string'); 618 | assert.equal(res.license, 'MIT'); 619 | }); 620 | 621 | it('should convert from an object to a string', () => { 622 | const pkg = { 623 | licenses: { type: 'MIT', url: 'https://github.com/jonschlinkert/test-project/blob/master/LICENSE-MIT' } 624 | }; 625 | 626 | const res = config.normalize(pkg); 627 | assert(!res.licenses); 628 | assert(res.license); 629 | assert.equal(typeof res.license, 'string'); 630 | assert.equal(res.license, 'MIT'); 631 | }); 632 | }); 633 | 634 | describe('dependencies', () => { 635 | beforeEach(() => { 636 | config = new Normalizer({ verbose: false }); 637 | }); 638 | 639 | it('should remove dependencies when empty when `omitEmpty` is true', () => { 640 | const pkg = { dependencies: {} }; 641 | const res = config.normalize(pkg, { omitEmpty: true }); 642 | assert(!res.dependencies); 643 | }); 644 | }); 645 | 646 | describe('devDependencies', () => { 647 | beforeEach(() => { 648 | config = new Normalizer({ verbose: false }); 649 | }); 650 | 651 | it('should remove empty devDependencies when omitEmpty is true', () => { 652 | const pkg = { devDependencies: {} }; 653 | const res = config.normalize(pkg, { omitEmpty: true }); 654 | assert(!res.devDependencies); 655 | }); 656 | }); 657 | 658 | describe('engineStrict', () => { 659 | beforeEach(() => { 660 | config = new Normalizer({ verbose: false }); 661 | }); 662 | 663 | it('should delete engineStrict and replace it with engine-strict', () => { 664 | const pkg = { engineStrict: true }; 665 | const res = config.normalize(pkg); 666 | assert.equal(typeof res.engineStrict, 'undefined'); 667 | assert.equal(res['engine-strict'], true); 668 | }); 669 | 670 | it('should remove engineStrict from the object', () => { 671 | const pkg = { engineStrict: true }; 672 | const res = config.normalize(pkg); 673 | assert(!res.hasOwnProperty('engineStrict')); 674 | }); 675 | }); 676 | 677 | describe('engine-strict', () => { 678 | beforeEach(() => { 679 | config = new Normalizer({ verbose: false }); 680 | }); 681 | 682 | it('should warn when engine-strict value is invalid', cb => { 683 | const pkg = { 'engine-strict': 'foo' }; 684 | let count = 0; 685 | 686 | config.on('warning', function(method, key, err) { 687 | if (key === 'engine-strict') { 688 | count++; 689 | } 690 | }); 691 | 692 | config.normalize(pkg); 693 | assert.equal(count, 1); 694 | cb(); 695 | }); 696 | }); 697 | 698 | describe('scripts', () => { 699 | beforeEach(() => { 700 | config = new Normalizer({ verbose: false }); 701 | }); 702 | 703 | it('should clean up mocha scripts', () => { 704 | const pkg = { scripts: { test: 'mocha -R spec' } }; 705 | 706 | const res = config.normalize(pkg); 707 | assert(res.scripts); 708 | assert.equal(typeof res.scripts, 'object'); 709 | assert.equal(res.scripts.test, 'mocha'); 710 | }); 711 | 712 | it('should return scripts if it is an object', () => { 713 | const pkg = { scripts: { test: 'foo' } }; 714 | 715 | const res = config.normalize(pkg); 716 | assert(res.scripts); 717 | assert.equal(typeof res.scripts, 'object'); 718 | assert.equal(res.scripts.test, 'foo'); 719 | }); 720 | }); 721 | 722 | describe('keywords', () => { 723 | beforeEach(() => { 724 | config = new Normalizer({ verbose: false }); 725 | }); 726 | 727 | it('should use the name to create keywords when the array is empty', () => { 728 | const pkg = { keywords: [] }; 729 | const res = config.normalize(pkg); 730 | assert.equal(res.keywords[0], 'project'); 731 | assert.equal(res.keywords[1], 'test'); 732 | assert.equal(res.keywords.length, 2); 733 | }); 734 | 735 | it('should sort keywords', () => { 736 | const pkg = { keywords: ['foo', 'bar', 'baz'] }; 737 | const res = config.normalize(pkg); 738 | assert.equal(res.keywords[0], 'bar'); 739 | assert.equal(res.keywords[1], 'baz'); 740 | assert.equal(res.keywords[2], 'foo'); 741 | }); 742 | 743 | it('should remove duplicates', () => { 744 | const pkg = { keywords: ['foo', 'foo', 'foo', 'foo', 'bar', 'baz'] }; 745 | const res = config.normalize(pkg); 746 | assert.equal(res.keywords.length, 3); 747 | }); 748 | }); 749 | 750 | describe('preferGlobal', () => { 751 | beforeEach(() => { 752 | config = new Normalizer({ verbose: false }); 753 | }); 754 | 755 | it('should warn when preferGlobal is defined and `bin` is not defined', cb => { 756 | const pkg = { preferGlobal: true }; 757 | let count = 0; 758 | 759 | config.on('warning', function(method, key, err) { 760 | if (key === 'preferGlobal') { 761 | count++; 762 | } 763 | }); 764 | 765 | const res = config.normalize(pkg); 766 | assert(res.preferGlobal); 767 | assert.equal(count, 1); 768 | cb(); 769 | }); 770 | 771 | it('should not warn when preferGlobal is defined and `bin` is defined', cb => { 772 | const pkg = { preferGlobal: true, bin: 'main.js' }; 773 | let count = 0; 774 | 775 | config.on('warning', (method, key, err) => { 776 | if (key === 'preferGlobal') { 777 | count++; 778 | } 779 | }); 780 | 781 | const res = config.normalize(pkg); 782 | assert(res.preferGlobal); 783 | assert.equal(count, 0); 784 | cb(); 785 | }); 786 | 787 | it('should return bin as-is when it is a string', () => { 788 | const pkg = { bin: 'main.js' }; 789 | 790 | const res = config.normalize(pkg); 791 | assert(res.bin); 792 | assert.equal(res.bin, 'main.js'); 793 | }); 794 | 795 | it('should not add bin file to `files` when `options.bin` is false', () => { 796 | const pkg = { bin: 'main.js' }; 797 | 798 | const res = config.normalize(pkg, { bin: false }); 799 | assert(!res.files); 800 | }); 801 | 802 | it('should add bin file to `files`', () => { 803 | const pkg = { bin: 'main.js' }; 804 | 805 | const res = config.normalize(pkg); 806 | assert(res.files); 807 | assert.equal(res.files[0], 'main.js'); 808 | }); 809 | }); 810 | 811 | describe('bin', () => { 812 | beforeEach(() => { 813 | config = new Normalizer({ verbose: false }); 814 | }); 815 | 816 | it('should not emit a warning when bin file string exists', cb => { 817 | const pkg = { bin: 'main.js' }; 818 | let count = 0; 819 | 820 | config.on('warning', function(method, key, err) { 821 | if (key === 'bin') { 822 | count++; 823 | } 824 | }); 825 | 826 | config.normalize(pkg); 827 | assert.equal(count, 0); 828 | cb(); 829 | }); 830 | 831 | it('should not emit a warning when bin file object exists', cb => { 832 | const pkg = { bin: { foo: 'main.js' } }; 833 | let count = 0; 834 | 835 | config.on('warning', function(method, key, err) { 836 | if (key === 'bin') { 837 | count++; 838 | } 839 | }); 840 | 841 | config.normalize(pkg); 842 | assert.equal(count, 0); 843 | cb(); 844 | }); 845 | }); 846 | }); 847 | 848 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const assert = require('assert'); 5 | const utils = require('../lib/utils'); 6 | 7 | describe('utils', () => { 8 | describe('.arrayify', () => { 9 | it('should cast a value to an array', () => { 10 | assert.deepEqual(utils.arrayify(['foo']), ['foo']); 11 | assert.deepEqual(utils.arrayify('foo'), ['foo']); 12 | assert.deepEqual(utils.arrayify(), []); 13 | }); 14 | }); 15 | }); 16 | 17 | --------------------------------------------------------------------------------