├── .gitignore ├── .travis.yml ├── README.md ├── index.js ├── lib ├── clean.js └── schema.js ├── package.json └── test ├── fixtures ├── bitbucket.json ├── disallowed-valid-property.json ├── express.json ├── sparse.json └── spectron.json └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | jobs: 5 | include: 6 | # Define the release stage that runs semantic-release 7 | - stage: release 8 | node_js: lts/* 9 | # Advanced: optionally overwrite your default `script` step to skip the tests 10 | # script: skip 11 | deploy: 12 | provider: script 13 | skip_cleanup: true 14 | script: 15 | - npx semantic-release 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nice-package ✨📦✨ [![Build Status](https://travis-ci.org/nice-registry/nice-package.svg?branch=master)](https://travis-ci.org/nice-registry/nice-package) 2 | 3 | > Clean up messy package metadata from the npm registry 4 | 5 | The [package data served by the npm registry](http://registry.npmjs.com/express) 6 | is messy and confusing. The folks at npm, Inc maintain a tool called 7 | [normalize-package-data](https://github.com/npm/normalize-package-data) 8 | which does a lot of work to clean this data up, but the resulting object is 9 | still a bit confusing. 10 | 11 | `nice-package` uses `normalize-package-data` as a starter, then does even more 12 | package cleanup: 13 | 14 | - uses the `doc['dist-tags'].latest` as the baseline for package metadata 15 | - derives `starsCount` from the `users` object 16 | - derives a `versions` array from the `time` object 17 | - renames `_npmUser` to `lastPublisher`, because it's a more intuitive name. 18 | - renames `maintainers` to `owners`, for consistency with the CLI commands. 19 | - normalizes GitHub repository URLs to `https` format 20 | - moves internal bookkeeping properties like `_id` and `_from` into an `other` object that can easily be omitted. 21 | - [more...](test/index.js) 22 | 23 | ## See Also 24 | 25 | - [package-stream](https://github.com/nice-registry/package-stream/): streams nice packages 26 | from the npm registry. 27 | - [nice-registry](https://github.com/nice-registry/nice-registry/): A server that dishes out nice packges. 28 | - [fetch-nice-package](https://github.com/hemanth/fetch-nice-package): fetch a nice package by name. 29 | 30 | ## Installation 31 | 32 | ```sh 33 | npm install nice-package --save 34 | ``` 35 | 36 | ## Usage 37 | 38 | `nice-package` exports a class. To create a new package instance, 39 | call `new Package(doc)`, where `doc` is a JSON package object from the npm registry: 40 | 41 | ```js 42 | const got = require('got') 43 | const Package = require('nice-package') 44 | 45 | got('https://registry.npmjs.com/express', {json: true}) 46 | .then(function (doc) { 47 | var pkg = new Package(doc) 48 | console.log(JSON.stringify(pkg, null, 2)) 49 | }) 50 | ``` 51 | 52 | You can also instantiate a nice package from `package.json` data: 53 | 54 | ```js 55 | const Package = require('nice-package') 56 | const pkg = new Package(require('node_modules/express/package.json')) 57 | 58 | pkg.dependsOn('array-flatten') 59 | // => true 60 | ``` 61 | 62 | ### Customizing the Package Object 63 | 64 | You can [pick](https://lodash.com/docs/4.17.2#pick) specific properties to return: 65 | 66 | ```js 67 | const pkg = new Package(pkgData, {pick: ['name', 'description']}) 68 | 69 | // { 70 | // name: 'tlds', 71 | // description: 'List of TLDs' 72 | // } 73 | ``` 74 | 75 | or you can [omit](https://lodash.com/docs/4.17.2#omit) properties. 76 | Sometimes you don't want the `other` data, the `readme`, etc. 77 | 78 | ```js 79 | const pkg = new Package(pkgData, {omit: ['other', 'readme', 'versions']}) 80 | ``` 81 | 82 | Note: `pick` and `omit` will also accept comma-delimited strings instead 83 | of arrays. This works nicely if you're using query params from a URL as `options` to 84 | `nice-package`: 85 | 86 | ```js 87 | const pkg = new Package(pkgData, {omit: 'other,readme,versions'}) 88 | ``` 89 | 90 | ### Convenience Methods 91 | 92 | A nice package comes with convenience methods: 93 | 94 | #### `pkg.mentions(query)` 95 | 96 | * `query` String 97 | 98 | Performs a case-insensitive search against the JSON-stringified object. Returns 99 | a Boolean indicating whether the given query is present in the object. 100 | 101 | #### `pkg.dependsOn(pkgName)` 102 | 103 | * `pkgName` String - The name of another package 104 | 105 | Returns a Boolean indicating whether the given `pkgName` is listed in `dependencies`. 106 | 107 | #### `pkg.devDependsOn(pkgName)` 108 | 109 | * `pkgName` String - The name of another package 110 | 111 | Returns a Boolean indicating whether the given `pkgName` is listed in `devDependencies`. 112 | 113 | #### `pkg.somehowDependsOn(pkgName)` 114 | 115 | * `pkgName` String - The name of another package 116 | 117 | Returns a Boolean indicating whether the given `pkgName` is listed in 118 | `dependencies` or `devDependencies`. 119 | 120 | #### `pkg.depNames` 121 | 122 | A getter method that returns an array of the `dependencies` keys. 123 | 124 | #### `pkg.devDepNames` 125 | 126 | A getter method that returns an array of the `devDependencies` keys. 127 | 128 | #### `pkg.allDepNames` 129 | 130 | A getter method that returns an array of all the `dependencies` and 131 | `devDependencies` keys. 132 | 133 | ## Validation 134 | 135 | `nice-package` uses a [JSON schema](lib/schema.js) to validate packages. 136 | 137 | The following properties are required: 138 | 139 | - `name` String 140 | - `description` String 141 | - `version` String 142 | 143 | To determine if a package is valid, use the `pkg.valid` getter method: 144 | 145 | ```js 146 | pkg.valid 147 | // => false 148 | ``` 149 | 150 | To see validation errors on a package, use the `pkg.validationErrors` getter method: 151 | 152 | ```js 153 | pkg.validationErrors 154 | ``` 155 | 156 | The result is an array of 157 | [revalidator errors](https://github.com/flatiron/revalidator#example). 158 | 159 | ## Tests 160 | 161 | ```sh 162 | npm install 163 | npm test 164 | ``` 165 | 166 | ## Dependencies 167 | 168 | - [github-url-to-object](https://github.com/github-modules/github-url-to-object): Extract user, repo, and other interesting properties from GitHub URLs 169 | - [normalize-registry-metadata](https://github.com/npm/normalize-registry-metadata): clean package metadata objects you get from registry changes feeds 170 | - [revalidator](https://github.com/flatiron/revalidator): A cross-browser / node.js validator powered by JSON Schema 171 | - [semver](https://github.com/npm/node-semver): The semantic version parser used by npm. 172 | 173 | ## Dev Dependencies 174 | 175 | - [require-dir](https://github.com/aseemk/requireDir): Helper to require() directories. 176 | - [standard](https://github.com/feross/standard): JavaScript Standard Style 177 | - [tap-spec](https://github.com/scottcorgan/tap-spec): Formatted TAP output like Mocha's spec reporter 178 | - [tape](https://github.com/substack/tape): tap-producing test harness for node and browsers 179 | 180 | ## License 181 | 182 | MIT 183 | 184 | ## Credits 185 | 186 | 💛 Thanks to [emilyrose](https://github.com/emilyrose) for giving up 187 | the `nice-package` name on npm. 188 | 189 | _Generated by [package-json-to-readme](https://github.com/zeke/package-json-to-readme)_ 190 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const revalidator = require('revalidator') 3 | const schema = require('./lib/schema') 4 | const pick = require('lodash').pick 5 | const omit = require('lodash').omit 6 | const clean = require('./lib/clean') 7 | 8 | module.exports = class Package { 9 | constructor (doc, opts) { 10 | var pkg = clean(doc) 11 | if (!pkg) return 12 | 13 | if (opts && opts.pick) { 14 | if (typeof opts.pick === 'string') opts.pick = opts.pick.split(',').map(prop => prop.trim()) 15 | pkg = pick(pkg, opts.pick) 16 | } 17 | 18 | if (opts && opts.omit) { 19 | if (typeof opts.omit === 'string') opts.omit = opts.omit.split(',').map(prop => prop.trim()) 20 | pkg = omit(pkg, opts.omit) 21 | } 22 | 23 | Object.assign(this, pkg) 24 | return this 25 | } 26 | 27 | mentions (string) { 28 | return !!JSON.stringify(this).toLowerCase().includes(string.toLowerCase()) 29 | } 30 | 31 | dependsOn (dep) { 32 | return this.depNames.indexOf(dep) > -1 33 | } 34 | 35 | devDependsOn (dep) { 36 | return this.devDepNames.indexOf(dep) > -1 37 | } 38 | 39 | somehowDependsOn (dep) { 40 | return this.dependsOn(dep) || this.devDependsOn(dep) 41 | } 42 | 43 | get depNames () { 44 | return this.dependencies ? Object.keys(this.dependencies).sort() : [] 45 | } 46 | 47 | get devDepNames () { 48 | return this.devDependencies ? Object.keys(this.devDependencies).sort() : [] 49 | } 50 | 51 | get allDepNames () { 52 | return this.depNames.concat(this.devDepNames).sort() 53 | } 54 | 55 | get valid () { 56 | return revalidator.validate(this, schema).valid 57 | } 58 | 59 | get validationErrors () { 60 | return revalidator.validate(this, schema).errors 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/clean.js: -------------------------------------------------------------------------------- 1 | const normalize = require('normalize-registry-metadata') 2 | const gh = require('github-url-to-object') 3 | const semver = require('semver') 4 | const getProp = require('lodash').get 5 | 6 | module.exports = function clean (doc) { 7 | if (!doc || typeof doc !== 'object') return 8 | 9 | // Has this object already been cleaned? 10 | if (doc.lastPublisher) return doc 11 | 12 | var pkg 13 | 14 | const latest = getProp(doc, 'dist-tags.latest') 15 | 16 | if (latest) { 17 | // this is a registry object 18 | doc = normalize(doc) 19 | // add props from lastest release that are absent from the top level 20 | pkg = Object.assign({}, doc.versions[latest], doc) 21 | } else if (doc.name) { 22 | // this is a basic object, i.e. a package.json file 23 | pkg = doc 24 | } else { 25 | // incomplete package.json object 26 | if (doc && typeof doc.valid !== 'undefined') delete doc.valid 27 | return doc 28 | } 29 | 30 | if (!pkg) return 31 | 32 | // attempt to sanitize github repo to a plain HTTPS url 33 | var repo 34 | try { 35 | repo = gh(pkg.repository.url).https_url 36 | } catch (e) { 37 | // not a github repo; no worries 38 | } 39 | if (repo) pkg.repository = repo 40 | 41 | // derive star count from users object 42 | if (pkg.users) { 43 | pkg.starsCount = Object.keys(pkg.users).length 44 | } 45 | 46 | if (pkg.time) { 47 | pkg.versions = Object.keys(pkg.time) 48 | .filter(version => !!semver.valid(version)) 49 | .map(version => { 50 | return { 51 | number: version, 52 | date: pkg.time[version] 53 | } 54 | }) 55 | 56 | if (pkg.time.created) pkg.created = pkg.time.created 57 | if (pkg.time.modified) pkg.modified = pkg.time.modified 58 | } 59 | 60 | if (pkg._npmUser) { 61 | pkg.lastPublisher = Object.assign({}, pkg._npmUser) 62 | } 63 | 64 | if (pkg.maintainers) { 65 | if (Array.isArray(pkg.maintainers)) { 66 | pkg.owners = pkg.maintainers.map(m => m) 67 | } 68 | } 69 | 70 | // create an object to store unwanted props 71 | pkg.other = {} 72 | const otherProps = [ 73 | '_attachments', 74 | '_from', 75 | '_id', 76 | '_nodeVersion', 77 | '_npmOperationalInternal', 78 | '_npmUser', 79 | '_npmVersion', 80 | '_rev', 81 | '_shasum', 82 | 'author', 83 | 'bugs', 84 | 'contributors', 85 | 'directories', 86 | 'dist-tags', 87 | 'dist', 88 | 'maintainers', 89 | 'readmeFilename', 90 | 'time', 91 | 'users' 92 | ] 93 | otherProps 94 | .filter(prop => prop in pkg) 95 | .forEach(prop => { 96 | pkg.other[prop] = pkg[prop] 97 | delete pkg[prop] 98 | }) 99 | 100 | if (Object.keys(pkg.other).length === 0) delete pkg.other 101 | 102 | // don't allow this prop to override the `pkg.valid` getter method 103 | delete pkg.valid 104 | 105 | return pkg 106 | } 107 | -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | properties: { 3 | name: { 4 | type: 'string', 5 | required: true 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nice-package", 3 | "version": "0.0.0-development", 4 | "description": "Clean up messy package metadata from the npm registry", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha && standard && standard-markdown" 8 | }, 9 | "repository": "https://github.com/nice-registry/nice-package", 10 | "keywords": [ 11 | "npm", 12 | "registry", 13 | "package", 14 | "metadata", 15 | "package.json", 16 | "normalize", 17 | "clean", 18 | "JSON" 19 | ], 20 | "author": "Zeke Sikelianos (http://zeke.sikelianos.com)", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "chai": "^3.5.0", 24 | "mocha": "^3.2.0", 25 | "require-dir": "^0.3.0", 26 | "standard": "^7.1.2", 27 | "standard-markdown": "^1.2.1", 28 | "semantic-release": "^15.13.16" 29 | }, 30 | "dependencies": { 31 | "github-url-to-object": "^4.0.4", 32 | "lodash": "^4.17.2", 33 | "normalize-registry-metadata": "^1.1.2", 34 | "revalidator": "^0.3.1", 35 | "semver": "^5.2.0" 36 | }, 37 | "engines": { 38 | "node": ">=4" 39 | }, 40 | "standard": { 41 | "env": { 42 | "mocha": true 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/fixtures/bitbucket.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitty-buck", 3 | "version": "1.0.0", 4 | "description": "my repo URL is not on GitHub and that's okay", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://bitbucket.org/monkey/business.git" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/disallowed-valid-property.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disallowed-valid-property", 3 | "description": "a package that has a property named `valid` in its package.json", 4 | "valid": false 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/sparse.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "not much here in this package.json. missing name, description, etc", 3 | "dependencies": { 4 | "request": "^2.65.0" 5 | }, 6 | "devDependencies": { 7 | "chai": "^3.3.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/spectron.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spectron", 3 | "version": "3.3.0", 4 | "description": "Easily test your Electron apps using ChromeDriver and WebdriverIO.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && mocha" 8 | }, 9 | "engines": { 10 | "node": ">=0.12.4" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/electron/spectron.git" 15 | }, 16 | "keywords": [ 17 | "electron", 18 | "chromedriver", 19 | "webdriverio", 20 | "selenium" 21 | ], 22 | "author": "Kevin Sawicki", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/electron/spectron/issues" 26 | }, 27 | "homepage": "https://github.com/electron/spectron#readme", 28 | "dependencies": { 29 | "dev-null": "^0.1.1", 30 | "electron-chromedriver": "~1.3.0", 31 | "request": "^2.65.0", 32 | "split": "^1.0.0", 33 | "webdriverio": "^4.0.4" 34 | }, 35 | "devDependencies": { 36 | "chai": "^3.3.0", 37 | "chai-as-promised": "^5.1.0", 38 | "electron": "~1.3.1", 39 | "mocha": "^2.3.3", 40 | "standard": "^5.3.1", 41 | "temp": "^0.8.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | const Package = require('..') 3 | const semver = require('semver') 4 | const fixtures = require('require-dir')('./fixtures') 5 | 6 | describe('Package', () => { 7 | var pkg = new Package(fixtures.express) 8 | 9 | it('preserves basic properties', () => { 10 | expect(pkg.name).to.equal('express') 11 | expect(pkg.description).to.exist 12 | expect(pkg.version).to.exist 13 | expect(pkg.readme).to.exist 14 | expect(pkg.created).to.exist 15 | expect(pkg.modified).to.exist 16 | }) 17 | 18 | describe('derived properties', () => { 19 | it('derives `starsCounts` from `users` object', () => { 20 | expect(pkg.starsCount).to.be.above(1500) 21 | }) 22 | 23 | it('derives `versions` from `time` object', () => { 24 | expect(pkg.versions.length).to.be.above(20) 25 | expect(pkg.versions.every(version => version.number.length > 0)).to.be.true 26 | expect(pkg.versions.every(version => !!semver.valid(version.number))).to.be.true 27 | expect(pkg.versions.every(version => version.date.length > 0)).to.be.true 28 | }) 29 | 30 | it('derives `owners` from `maintainers`', () => { 31 | expect(pkg.owners.find(owner => owner.name === 'dougwilson')).to.exist 32 | }) 33 | 34 | it('derives `lastPublisher` from `_npmUser`', () => { 35 | expect(pkg.lastPublisher).to.exist 36 | }) 37 | }) 38 | 39 | describe('other properties', () => { 40 | it('preserves unwanted properties in an `other` object', () => { 41 | expect(pkg.other._from).to.exist 42 | expect(pkg.other._id).to.exist 43 | expect(pkg.other._shasum).to.exist 44 | expect(pkg.other._npmUser).to.exist 45 | expect(pkg.other.maintainers).to.exist 46 | expect(pkg.other.time).to.exist 47 | }) 48 | 49 | it('does not preserve the other object if it is empty', () => { 50 | const pkg = new Package({name: 'foo', description: 'bar'}) 51 | expect(pkg.name).to.equal('foo') 52 | expect(pkg.other).to.not.exist 53 | }) 54 | }) 55 | 56 | describe('convenience functions', () => { 57 | it('dependsOn()', () => { 58 | expect(pkg.dependsOn('finalhandler')).to.be.true 59 | expect(pkg.dependsOn('monkeys')).to.be.false 60 | }) 61 | 62 | it('devDependsOn()', () => { 63 | expect(pkg.devDependsOn('istanbul')).to.be.true 64 | }) 65 | 66 | it('somehowDependsOn()', () => { 67 | expect(pkg.somehowDependsOn('finalhandler')).to.be.true 68 | expect(pkg.somehowDependsOn('istanbul')).to.be.true 69 | }) 70 | 71 | it('depNames getter', () => { 72 | expect(pkg.depNames).to.include('finalhandler') 73 | }) 74 | 75 | it('devDepNames getter', () => { 76 | expect(pkg.devDepNames).to.include('istanbul') 77 | }) 78 | 79 | it('allDepNames getter', () => { 80 | expect(pkg.allDepNames).to.include('finalhandler') 81 | expect(pkg.allDepNames).to.include('istanbul') 82 | }) 83 | 84 | it('mentions()', () => { 85 | expect(pkg.mentions('minimalist web framework')).to.be.true 86 | expect(pkg.mentions('MINIMALIST WEB FRAMEWORK')).to.be.true 87 | }) 88 | }) 89 | 90 | describe('validation', () => { 91 | it('is valid if all required properties are present', () => { 92 | expect(pkg.valid).to.be.true 93 | }) 94 | 95 | it('ignores the `valid` property, if present, in favor of internal getter', () => { 96 | const fakeValidPkg = new Package(fixtures['disallowed-valid-property']) 97 | expect(fakeValidPkg.valid).to.be.true 98 | }) 99 | 100 | it('requires name', () => { 101 | const oldName = pkg.name 102 | delete pkg.name 103 | expect(pkg.valid).to.be.false 104 | expect(pkg.validationErrors.length).to.equal(1) 105 | expect(pkg.validationErrors[0].property).to.equal('name') 106 | pkg.name = oldName 107 | }) 108 | }) 109 | 110 | describe('pick', () => { 111 | it('accepts an array of props to pick', () => { 112 | const opts = {pick: ['name', 'description']} 113 | const pickedPackage = new Package(pkg, opts) 114 | expect(Object.keys(pickedPackage)).to.deep.equal(['name', 'description']) 115 | }) 116 | 117 | it('accepts a comma-delimited string of props to pick', () => { 118 | const opts = {pick: 'name, description'} 119 | const pickedPackage = new Package(pkg, opts) 120 | expect(Object.keys(pickedPackage)).to.deep.equal(['name', 'description']) 121 | }) 122 | }) 123 | 124 | describe('omit', () => { 125 | it('accepts an array of props to omit', () => { 126 | const opts = {omit: ['description']} 127 | const omittedPackage = new Package(pkg, opts) 128 | const props = Object.keys(omittedPackage) 129 | expect(props).to.include('name') 130 | expect(props).to.not.include('description') 131 | }) 132 | 133 | it('accepts an comma-delimited string of props to pick', () => { 134 | const opts = {omit: 'description, name'} 135 | const omittedPackage = new Package(pkg, opts) 136 | const props = Object.keys(omittedPackage) 137 | expect(props).to.not.include('name') 138 | expect(props).to.not.include('description') 139 | expect(props).to.include('versions') 140 | }) 141 | }) 142 | 143 | describe('source data', () => { 144 | it('can reconstitute packages from an already-cleaned package object', () => { 145 | const repkg = new Package(pkg) 146 | expect(repkg.name).to.equal('express') 147 | }) 148 | 149 | it('accepts package.json data instead of registry data', () => { 150 | const packageJSONPackage = new Package(fixtures.spectron) 151 | expect(packageJSONPackage.name).to.equal('spectron') 152 | expect(packageJSONPackage.dependsOn('webdriverio')).to.be.true 153 | }) 154 | 155 | it('accepts incomplete package.json data but still exposes convenience methods', () => { 156 | const sparsePackage = new Package(fixtures.sparse) 157 | expect(sparsePackage.name).to.not.exist 158 | expect(sparsePackage.valid).to.be.false 159 | expect(sparsePackage.dependsOn('request')).to.be.true 160 | }) 161 | }) 162 | 163 | describe('repository', () => { 164 | it('turns GitHub `repository` entry into an HTTPS URL string', () => { 165 | const gitty = new Package(fixtures.express) 166 | expect(gitty.repository).to.equal('https://github.com/expressjs/express') 167 | }) 168 | 169 | it('retains original repository structure for non-GitHub URLs', () => { 170 | const bitty = new Package(fixtures.bitbucket) 171 | expect(bitty.repository.type).to.equal('git') 172 | expect(bitty.repository.url).to.equal('https://bitbucket.org/monkey/business.git') 173 | }) 174 | }) 175 | 176 | it('does not throw an error when passed a null doc', () => { 177 | const fn = function () { 178 | var pkg = new Package(null) 179 | pkg 180 | } 181 | expect(fn).to.not.throw 182 | }) 183 | 184 | it('accepts a doc object with other cobbled-on top-level properties', () => { 185 | // 186 | const cobbledFixture = Object.assign({}, fixtures.express, {fooProp: 1, barProp: 2}) 187 | expect(cobbledFixture.fooProp).to.equal(1) 188 | const cobbledPackage = new Package(cobbledFixture) 189 | expect(cobbledPackage.fooProp).to.equal(1) 190 | }) 191 | }) 192 | --------------------------------------------------------------------------------