Merge bower-json repository

This commit is contained in:
Adam Stankiewicz
2016-06-10 13:22:15 +02:00
20 changed files with 1254 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

4
packages/bower-json/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/node_modules
/npm-debug.*
/test/reports

View File

@@ -0,0 +1,48 @@
{
"indent": 4,
"node": true,
"devel": true,
"mocha": true,
"bitwise": false,
"curly": false,
"eqeqeq": true,
"forin": false,
"immed": true,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": true,
"plusplus": false,
"regexp": false,
"undef": true,
"unused": true,
"quotmark": "single",
"strict": false,
"camelcase": true,
"asi": false,
"boss": true,
"debug": false,
"eqnull": true,
"es5": false,
"esnext": false,
"evil": false,
"expr": false,
"funcscope": false,
"globalstrict": false,
"iterator": false,
"lastsemic": false,
"laxbreak": true,
"laxcomma": false,
"loopfunc": true,
"multistr": false,
"onecase": true,
"regexdash": false,
"scripturl": false,
"shadow": false,
"sub": false,
"supernew": true,
"validthis": false
}

View File

@@ -0,0 +1,10 @@
sudo: false
language: node_js
node_js:
- '5'
- '4'
- '0.12'
- '0.10'
script:
- grunt travis

View File

@@ -0,0 +1,17 @@
# 0.8.1
- Revert strict name validations and allow @, spaces and slashes
# 0.8.0
- Update graceful-fs to 4.x
- Add name validations that reflect what's happening in registry
# 0.7.1
- Unpublished
# 0.7.0
- Add getIssues function to retrieve all errors and warnings
- Add readSync and findSync functions for synchronous read

View File

@@ -0,0 +1,60 @@
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
// Project configuration.
grunt.initConfig({
jshint: {
files: [
'Gruntfile.js',
'lib/**/*.js',
'test/**/*.js',
'!test/reports/**/*'
],
options: {
jshintrc: '.jshintrc'
}
},
simplemocha: {
options: {
reporter: 'spec'
},
full: { src: ['test/test.js'] },
short: {
options: {
reporter: 'dot'
},
src: ['test/test.js']
},
build: {
options: {
reporter: 'tap'
},
src: ['test/test.js']
}
},
exec: {
cover: {
command: 'STRICT_REQUIRE=1 node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
},
coveralls: {
command: 'node node_modules/.bin/coveralls < test/reports/lcov.info'
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'simplemocha:short']
}
});
// Default task.
grunt.registerTask('test', ['simplemocha:full']);
grunt.registerTask('default', ['jshint', 'test']);
grunt.registerTask('travis', ['jshint', 'exec:cover', 'exec:coveralls']);
};

View File

@@ -0,0 +1,19 @@
Copyright (c) 2016 Twitter and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,159 @@
# bower-json [![Build Status](https://secure.travis-ci.org/bower/json.png?branch=master)](http://travis-ci.org/bower/json) [![Coverage Status](https://coveralls.io/repos/bower/json/badge.svg?branch=master&service=github)](https://coveralls.io/github/bower/json?branch=master)
Read `bower.json` files with semantics, normalisation, defaults and validation.
Install via [npm](https://www.npmjs.org/package/bower-json): `npm install --save bower-json`
## Usage
#### .read(file, options, callback)
#### .readSync(file, options)
Reads `file` and applies normalisation, defaults and validation according to the `bower.json` spec.
If the passed `file` does not exist, the callback is called with `error.code` equal to `ENOENT`.
If the passed `file` contents are not valid JSON, the callback is called with `error.code` equal to `EMALFORMED`.
If the `json` does not comply with the `bower.json` spec, the callback is called with `error.code` equal to `EINVALID`.
If `file` is a directory, `find()` will be used to search for the json file.
The `options` argument is optional and can be omitted. These options will be passed to `parse` method.
```js
var bowerJson = require('bower-json');
// Can also be used by simply calling bowerJson()
bowerJson.read('/path/to/bower.json', function (err, json) {
if (err) {
console.error('There was an error reading the file');
console.error(err.message);
return;
}
console.log('JSON: ', json);
});
```
#### .parse(json, options)
Parses an object. Useful when you want to apply normalisation and validation directly to an object.
If the `json` does not comply with the `bower.json` spec, an error is thrown with `error.code` equal to `EINVALID`.
The `options` arguments is optional and can be omitted. Available options:
- validate: Apply validation, defaults to `true`
- normalize: Apply normalisation, defaults to `false`
- clone: clone, use and return the passed in `json` object instead of using it directly, defaults to `false`
```js
var bowerJson = require('bower-json');
var json = {
name: 'my-package',
version: '0.0.1'
};
try {
bowerJson.parse(json);
} catch (err) {
console.error('There was an error parsing the object');
console.error(err.message);
}
```
#### .getIssues(json) - DEPRECATED
Validates the passed `json` object.
Returns an object with errors and warnings of this bower.json contents.
```js
var bowerJson = require('bower-json');
var json = {
name: 'myPackage',
version: '0.0.1',
main: {}
};
var issues = bowerJson.getIssues(json);
expect(issues).toEqual({
errors: ['The "main" field has to be either an Array or a String'],
warnings: ['The "name" must be lowercase']
});
#### .validate(json)
Validates the passed `json` object.
Throws an error with `error.code` equal to `EINVALID` if it does not comply with the spec.
```js
var bowerJson = require('bower-json');
var json = {
name: 'myPackage',
version: '0.0.1'
};
try {
bowerJson.validate(json);
} catch (err) {
console.error('There was an error validating the object');
console.error(err.message);
}
```
#### .normalize(json)
```js
var bowerJson = require('bower-json');
var json = {
name: 'my-package',
version: '0.0.1',
main: 'foo.js,bar.js'
};
bowerJson.normalize(json);
json.main // ['foo.js', 'bar.js']
```
#### .find(folder, callback)
#### .findSync(folder)
Finds the `json` filename inside a folder.
Checks if a `bower.json` exists, falling back to `component.json` (deprecated) and `.bower.json`.
If no file was found, the callback is called with a `error.code` of `ENOENT`.
```js
var bowerJson = require('bower-json');
bowerJson.find('/path/to/folder', function (err, filename) {
if (err) {
console.error('There is no json file in the folder');
return;
}
console.log('Filename: ', filename);
// Now that we got the filename, we can read its contents
bowerJson.read(filename, function (err, json) {
if (err) {
console.error('There was an error reading the file');
console.error(err.message);
return;
}
console.log('JSON: ', json);
});
});
```
## License
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

View File

@@ -0,0 +1,297 @@
var fs = require('graceful-fs');
var path = require('path');
var deepExtend = require('deep-extend');
var isAsset = require('./util/isAsset');
var isComponent = require('./util/isComponent');
var createError = require('./util/createError');
var possibleJsons = ['bower.json', 'component.json', '.bower.json'];
function read(file, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
// Check if file is a directory
fs.stat(file, function (err, stat) {
if (err) {
return callback(err);
}
// It's a directory, so we find the json inside it
if (stat.isDirectory()) {
return find(file, function (err, file) {
if (err) {
return callback(err);
}
read(file, options, callback);
});
}
// Otherwise read it
fs.readFile(file, function (err, contents) {
var json;
if (err) {
return callback(err);
}
try {
json = JSON.parse(contents.toString());
} catch (err) {
err.file = path.resolve(file);
err.code = 'EMALFORMED';
return callback(err);
}
// Parse it
try {
json = parse(json, options);
} catch (err) {
err.file = path.resolve(file);
return callback(err);
}
callback(null, json, file);
});
});
}
function readSync(file, options) {
var stat;
var filename;
var contents;
var json;
if (!options) {
options = {};
}
try {
stat = fs.statSync(file);
} catch (err) {
return err;
}
if (stat.isDirectory()) {
filename = findSync(file);
return readSync(filename);
}
contents = fs.readFileSync(file);
try {
json = JSON.parse(contents.toString());
} catch (err) {
err.file = path.resolve(file);
err.code = 'EMALFORMED';
return err;
}
try {
json = parse(json, options);
} catch (err) {
err.file = path.resolve(file);
return err;
}
return json;
}
function parse(json, options) {
options = deepExtend({
normalize: false,
validate: true,
clone: false
}, options || {});
// Clone
if (options.clone) {
json = deepExtend({}, json);
}
// Validate
if (options.validate) {
validate(json);
}
// Normalize
if (options.normalize) {
normalize(json);
}
return json;
}
// This function implements:
//
// https://github.com/bower/bower.json-spec
function getIssues(json) {
// For things that shouldn't happen
var errors = [];
// For things that happen but they shoudn't
var warnings = [];
if (!json.name) {
errors.push('No "name" property set');
} else {
if (!/^[a-zA-Z0-9_@][a-zA-Z0-9_@\.\- \/]*$/.test(json.name)) {
errors.push('Name must be lowercase, can contain digits, dots, dashes, "@" or spaces');
}
if (json.name.length > 50) {
warnings.push('The "name" is too long, the limit is 50 characters');
}
if (!/^[a-z0-9_][a-z0-9_\.\-]*$/.test(json.name)) {
warnings.push('The "name" is recommended to be lowercase, can contain digits, dots, dashes');
}
if (/^[\.-]/.test(json.name)) {
warnings.push('The "name" cannot start with dot or dash');
}
if (/[\.-]$/.test(json.name)) {
warnings.push('The "name" cannot end with dot or dash');
}
}
if (json.description && json.description.length > 140) {
warnings.push('The "description" is too long, the limit is 140 characters');
}
if (json.main !== undefined) {
var main = json.main;
if (typeof main === 'string') {
main = [main];
}
if (!(main instanceof Array)) {
errors.push('The "main" field has to be either an Array or a String');
} else {
var ext2files = {};
main.forEach(function (filename) {
if (typeof filename !== 'string') {
errors.push('The "main" Array has to contain only Strings');
}
if (/[*]/.test(filename)) {
warnings.push('The "main" field cannot contain globs (example: "*.js")');
}
if (/[.]min[.][^/]+$/.test(filename)) {
warnings.push('The "main" field cannot contain minified files');
}
if (isAsset(filename)) {
warnings.push('The "main" field cannot contain font, image, audio, or video files');
}
var ext = path.extname(filename);
if (ext.length >= 2) {
var files = ext2files[ext];
if (!files) {
files = ext2files[ext] = [];
}
files.push(filename);
}
});
Object.keys(ext2files).forEach(function (ext) {
var files = ext2files[ext];
if (files.length > 1) {
warnings.push('The "main" field has to contain only 1 file per filetype; found multiple ' + ext + ' files: ' + JSON.stringify(files));
}
});
}
}
return {
errors: errors,
warnings: warnings
};
}
// For backward compatibility, it throws first error
function validate(json) {
var issues = getIssues(json);
if (issues.errors && issues.errors.length > 0) {
throw createError(issues.errors[0], 'EINVALID');
}
}
function normalize(json) {
if (typeof json.main === 'string') {
json.main = [json.main];
}
return json;
}
function find(folder, files, callback) {
var err;
var file;
if (typeof files === 'function') {
callback = files;
files = possibleJsons;
}
if (!files.length) {
err = createError('None of ' + possibleJsons.join(', ') + ' were found in ' + folder, 'ENOENT');
return callback(err);
}
file = path.resolve(path.join(folder, files[0]));
fs.exists(file, function (exists) {
if (!exists) {
return find(folder, files.slice(1), callback);
}
if (files[0] !== 'component.json') {
return callback(null, file);
}
// If the file is component.json, check it it's a component(1) file
// If it is, we ignore it and keep searching
isComponent(file, function (is) {
if (is) {
return find(folder, files.slice(1), callback);
}
callback(null, file);
});
});
}
function findSync(folder, files) {
var file;
var exists;
if (files === undefined) {
files = possibleJsons;
}
if (!files.length) {
return createError('None of ' + possibleJsons.join(', ') + ' were found in ' + folder, 'ENOENT');
}
file = path.resolve(path.join(folder, files[0]));
try{
exists = fs.statSync(file);
}
catch (err) {
exists = false;
}
if (exists && exists.isFile()) {
return file;
} else {
return findSync(folder, files.slice(1));
}
}
module.exports = read;
module.exports.read = read;
module.exports.readSync = readSync;
module.exports.parse = parse;
module.exports.getIssues = getIssues;
module.exports.validate = validate;
module.exports.normalize = normalize;
module.exports.find = find;
module.exports.findSync = findSync;

View File

@@ -0,0 +1,8 @@
function createError(msg, code) {
var err = new Error(msg);
err.code = code;
return err;
}
module.exports = createError;

View File

@@ -0,0 +1,12 @@
var extName = require('ext-name');
function isAsset(filename) {
var info = extName(filename);
return info && info.mime && (
/^((image)|(audio)|(video)|(font))\//.test(info.mime) ||
/application\/((x[-]font[-])|(font[-]woff(\d?))|(vnd[.]ms[-]fontobject))/.test(info.mime)
);
}
module.exports = isAsset;

View File

@@ -0,0 +1,41 @@
var fs = require('graceful-fs');
var intersect = require('intersect');
// Function to check if a file is a component(1) file
function isComponent(file, callback) {
fs.readFile(file, function (err, contents) {
var json;
var keys;
var common;
// If an error occurs while reading the file, we ignore it
if (err) {
return callback(false);
}
try {
json = JSON.parse(contents.toString());
} catch (err) {
return callback(false);
}
// Attempt to find specific things from the component(1) spec
// Note that we don't parse the dependencies property because at any point
// we can allow / to specify directories
// Bellow only some clearly not ambiguous properties are checked
keys = Object.keys(json);
common = intersect(keys, [
'repo',
'development',
'local',
'remotes',
'paths',
'demo'
]);
// If none were found, than it's a valid component.json bower file
callback(common.length ? true : false);
});
}
module.exports = isComponent;

View File

@@ -0,0 +1,40 @@
{
"name": "bower-json",
"version": "0.8.1",
"description": "Read bower.json files with semantics, normalisation, defaults and validation",
"author": "Twitter",
"license": "MIT",
"repository": "bower/json",
"main": "lib/json",
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"deep-extend": "^0.4.0",
"ext-name": "^3.0.0",
"graceful-fs": "^4.1.3",
"intersect": "^1.0.1"
},
"devDependencies": {
"coveralls": "^2.11.2",
"expect.js": "^0.3.1",
"grunt": "^0.4.4",
"grunt-cli": "^0.1.13",
"grunt-contrib-jshint": "^0.11.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-coveralls": "^1.0.0",
"grunt-exec": "^0.4.6",
"grunt-simple-mocha": "^0.4.0",
"istanbul": "^0.3.5",
"load-grunt-tasks": "^3.3.0",
"mocha": "*",
"request": "^2.64.0",
"underscore.string": "^3.0.3"
},
"scripts": {
"test": "grunt test"
},
"files": [
"lib"
]
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{

View File

@@ -0,0 +1,5 @@
{
"name": "some-pkg",
"version": "0.0.0",
"main": "foo.js"
}

View File

@@ -0,0 +1,14 @@
{
"name": "route",
"repo": "apily/route",
"version": "0.0.1",
"description": "Route component",
"keywords": ["router", "route"],
"scripts": ["index.js"],
"dependencies": {},
"development": {
"component/assert": "*",
"visionmedia/mocha": "*"
},
"license": "MIT"
}

View File

@@ -0,0 +1,4 @@
{
"name": "some-pkg",
"version": "0.0.0"
}

View File

@@ -0,0 +1,5 @@
{
"name": "some-installed-pkg",
"version": "0.0.1",
"main": "bar.js"
}

View File

@@ -0,0 +1,497 @@
var path = require('path');
var expect = require('expect.js');
var _s = require('underscore.string');
var bowerJson = require('../lib/json');
var request = require('request');
describe('.find', function () {
it('should find the bower.json file', function (done) {
bowerJson.find(__dirname + '/pkg-bower-json', function (err, file) {
if (err) {
return done(err);
}
expect(file).to.equal(path.resolve(__dirname + '/pkg-bower-json/bower.json'));
done();
});
});
it('should fallback to the component.json file', function (done) {
bowerJson.find(__dirname + '/pkg-component-json', function (err, file) {
if (err) {
return done(err);
}
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
done();
});
});
it('should not fallback to the component.json file if it\'s a component(1) file', function (done) {
bowerJson.find(__dirname + '/pkg-component(1)-json', function (err) {
expect(err).to.be.an(Error);
expect(err.code).to.equal('ENOENT');
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname + '/pkg-component(1)-json');
done();
});
});
it('should fallback to the .bower.json file', function (done) {
bowerJson.find(__dirname + '/pkg-dot-bower-json', function (err, file) {
if (err) {
return done(err);
}
expect(file).to.equal(path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json'));
done();
});
});
it('should error if no component.json / bower.json / .bower.json is found', function (done) {
bowerJson.find(__dirname, function (err) {
expect(err).to.be.an(Error);
expect(err.code).to.equal('ENOENT');
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname);
done();
});
});
});
describe('.findSync', function () {
it('should find the bower.json file', function (done) {
var file = bowerJson.findSync(__dirname + '/pkg-bower-json');
expect(file).to.equal(path.resolve(__dirname + '/pkg-bower-json/bower.json'));
done();
});
it('should fallback to the component.json file', function (done) {
var file = bowerJson.findSync(__dirname + '/pkg-component-json');
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
done();
});
it('should fallback to the .bower.json file', function (done) {
var file = bowerJson.findSync(__dirname + '/pkg-dot-bower-json');
expect(file).to.equal(path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json'));
done();
});
it('should error if no component.json / bower.json / .bower.json is found', function (done) {
var err = bowerJson.findSync(__dirname);
expect(err).to.be.an(Error);
expect(err.code).to.equal('ENOENT');
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname);
done();
});
});
describe('.read', function () {
it('should give error if file does not exists', function (done) {
bowerJson.read(__dirname + '/willneverexist', function (err) {
expect(err).to.be.an(Error);
expect(err.code).to.equal('ENOENT');
done();
});
});
it('should give error if when reading an invalid json', function (done) {
bowerJson.read(__dirname + '/pkg-bower-json-malformed/bower.json', function (err) {
expect(err).to.be.an(Error);
expect(err.code).to.equal('EMALFORMED');
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-malformed/bower.json'));
done();
});
});
it('should read the file and give an object', function (done) {
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function (err, json) {
if (err) {
return done(err);
}
expect(json).to.be.an('object');
expect(json.name).to.equal('some-pkg');
expect(json.version).to.equal('0.0.0');
done();
});
});
it('should give the json file that was read', function (done) {
bowerJson.read(__dirname + '/pkg-bower-json', function (err, json, file) {
if (err) {
return done(err);
}
expect(file).to.equal(__dirname + '/pkg-bower-json/bower.json');
done();
});
});
it('should find for a json file if a directory is given', function (done) {
bowerJson.read(__dirname + '/pkg-component-json', function (err, json, file) {
if (err) {
return done(err);
}
expect(json).to.be.an('object');
expect(json.name).to.equal('some-pkg');
expect(json.version).to.equal('0.0.0');
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
done();
});
});
it('should validate the returned object unless validate is false', function (done) {
bowerJson.read(__dirname + '/pkg-bower-json-invalid/bower.json', function (err) {
expect(err).to.be.an(Error);
expect(err.message).to.contain('name');
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-invalid/bower.json'));
bowerJson.read(__dirname + '/pkg-bower-json-invalid/bower.json', { validate: false }, function (err) {
done(err);
});
});
});
it('should normalize the returned object if normalize is true', function (done) {
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function (err, json) {
if (err) {
return done(err);
}
expect(json.main).to.equal('foo.js');
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', { normalize: true }, function (err, json) {
if (err) {
return done(err);
}
expect(json.main).to.eql(['foo.js']);
done();
});
});
});
});
describe('.readSync', function () {
it('should give error if file does not exists', function (done) {
var err = bowerJson.readSync(__dirname + '/willneverexist');
expect(err).to.be.an(Error);
expect(err.code).to.equal('ENOENT');
done();
});
it('should give error if when reading an invalid json', function (done) {
var err = bowerJson.readSync(__dirname + '/pkg-bower-json-malformed/bower.json');
expect(err).to.be.an(Error);
expect(err.code).to.equal('EMALFORMED');
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-malformed/bower.json'));
done();
});
it('should read the file and give an object', function (done) {
var json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json');
expect(json).to.be.an('object');
expect(json.name).to.equal('some-pkg');
expect(json.version).to.equal('0.0.0');
done();
});
it('should find for a json file if a directory is given', function (done) {
var json = bowerJson.readSync(__dirname + '/pkg-component-json');
expect(json).to.be.an('object');
expect(json.name).to.equal('some-pkg');
expect(json.version).to.equal('0.0.0');
done();
});
it('should validate the returned object unless validate is false', function (done) {
var err = bowerJson.readSync(__dirname + '/pkg-bower-json-invalid/bower.json');
expect(err).to.be.an(Error);
expect(err.message).to.contain('name');
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-invalid/bower.json'));
err = bowerJson.readSync(__dirname + '/pkg-bower-json-invalid/bower.json', { validate: false });
expect(err).to.not.be.an(Error);
done();
});
it('should normalize the returned object if normalize is true', function (done) {
var json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json');
expect(json.main).to.equal('foo.js');
json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json', { normalize: true });
expect(json.main).to.eql(['foo.js']);
done();
});
});
describe('.parse', function () {
it('should return the same object, unless clone is true', function () {
var json = { name: 'foo' };
expect(bowerJson.parse(json)).to.equal(json);
expect(bowerJson.parse(json, { clone: true })).to.not.equal(json);
expect(bowerJson.parse(json, { clone: true })).to.eql(json);
});
it('should validate the passed object, unless validate is false', function () {
expect(function () {
bowerJson.parse({});
}).to.throwException(/name/);
expect(function () {
bowerJson.parse({}, { validate: false });
}).to.not.throwException();
});
it('should not normalize the passed object unless normalize is true', function () {
var json = { name: 'foo', main: 'foo.js' };
bowerJson.parse(json);
expect(json.main).to.eql('foo.js');
bowerJson.parse(json, { normalize: true });
expect(json.main).to.eql(['foo.js']);
});
});
describe('.getIssues', function () {
it('should print no errors even for weird package names', function () {
var json = { name: '@gruNt/my dependency' };
expect(bowerJson.getIssues(json).errors).to.be.empty();
});
it('should validate the name length', function () {
var json = { name: 'a_123456789_123456789_123456789_123456789_123456789_z' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" is too long, the limit is 50 characters'
);
});
it('should validate the name is lowercase', function () {
var json = { name: 'gruNt' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" is recommended to be lowercase, can contain digits, dots, dashes'
);
});
it('should validate the name starts with lowercase', function () {
var json = { name: '-runt' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" cannot start with dot or dash'
);
});
it('should validate the name starts with lowercase', function () {
var json = { name: '.grunt' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" cannot start with dot or dash'
);
});
it('should validate the name ends with lowercase', function () {
var json = { name: 'grun-' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" cannot end with dot or dash'
);
});
it('should validate the name ends with lowercase', function () {
var json = { name: 'grun.' };
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "name" cannot end with dot or dash'
);
});
it('should validate the name is valid', function () {
var json = { name: 'gru.n-t' };
expect(bowerJson.getIssues(json).warnings).to.eql([]);
});
it('should validate the description length', function () {
var json = {
name: 'foo',
description: _s.repeat('æ', 141)
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "description" is too long, the limit is 140 characters'
);
});
it('should validate the description is valid', function () {
var json = {
name: 'foo',
description: _s.repeat('æ', 140)
};
expect(bowerJson.getIssues(json).warnings).to.eql([]);
});
it('should validate that main does not contain globs', function () {
var json = {
name: 'foo',
main: ['js/*.js']
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "main" field cannot contain globs (example: "*.js")'
);
});
it('should validate that main does not contain minified files', function () {
var json = {
name: 'foo',
main: ['foo.min.css']
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "main" field cannot contain minified files'
);
});
it('should validate that main does not contain fonts', function () {
var json = {
name: 'foo',
main: ['foo.woff']
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "main" field cannot contain font, image, audio, or video files'
);
});
it('should validate that main does not contain images', function () {
var json = {
name: 'foo',
main: ['foo.png']
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "main" field cannot contain font, image, audio, or video files'
);
});
it('should validate that main does not contain multiple files of the same filetype', function () {
var json = {
name: 'foo',
main: ['foo.js', 'bar.js']
};
expect(bowerJson.getIssues(json).warnings).to.contain(
'The "main" field has to contain only 1 file per filetype; found multiple .js files: ["foo.js","bar.js"]'
);
});
});
describe('.validate', function () {
it('should validate the name property', function () {
expect(function () {
bowerJson.validate({});
}).to.throwException(/name/);
});
it('should validate the type of main', function () {
var json = {
name: 'foo',
main: {}
};
expect(function () {
bowerJson.validate(json);
}).to.throwException();
});
it('should validate the type of items of an Array main', function () {
var json = {
name: 'foo',
main: [{}]
};
expect(function () {
bowerJson.validate(json);
}).to.throwException();
});
});
describe('.normalize', function () {
it('should normalize the main property', function () {
var json = { name: 'foo', main: 'foo.js' };
bowerJson.normalize(json);
expect(json.main).to.eql(['foo.js']);
});
});
describe('packages from bower registry', function () {
var packageList,
packageListUrl = 'http://bower.herokuapp.com/packages';
this.timeout(60000);
it('can be downloaded from online source ' + packageListUrl, function(done) {
request({
url: packageListUrl,
json: true
}, function(error, response, body) {
if(error) {
throw error;
}
expect(body).to.be.an('array');
expect(body).to.not.be.empty();
packageList = body;
done();
});
});
it('should validate each listed package', function (done) {
expect(packageList).to.be.an('array');
var invalidPackageCount = 0;
packageList.forEach(function(package) {
try {
bowerJson.validate(package);
} catch(e) {
invalidPackageCount++;
console.error('validation of "' + package.name + '" failed: ' + e.message);
}
});
if(invalidPackageCount) {
throw new Error(invalidPackageCount + '/' + packageList.length + ' package names do not validate');
}
done();
});
});