mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Merge bower-json repository
This commit is contained in:
12
packages/bower-json/.editorconfig
Normal file
12
packages/bower-json/.editorconfig
Normal 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
4
packages/bower-json/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
/npm-debug.*
|
||||
|
||||
/test/reports
|
||||
48
packages/bower-json/.jshintrc
Normal file
48
packages/bower-json/.jshintrc
Normal 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
|
||||
}
|
||||
10
packages/bower-json/.travis.yml
Normal file
10
packages/bower-json/.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- '5'
|
||||
- '4'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
|
||||
script:
|
||||
- grunt travis
|
||||
17
packages/bower-json/CHANGELOG.md
Normal file
17
packages/bower-json/CHANGELOG.md
Normal 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
|
||||
60
packages/bower-json/Gruntfile.js
Normal file
60
packages/bower-json/Gruntfile.js
Normal 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']);
|
||||
};
|
||||
19
packages/bower-json/LICENSE
Normal file
19
packages/bower-json/LICENSE
Normal 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.
|
||||
159
packages/bower-json/README.md
Normal file
159
packages/bower-json/README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# bower-json [](http://travis-ci.org/bower/json) [](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).
|
||||
297
packages/bower-json/lib/json.js
Normal file
297
packages/bower-json/lib/json.js
Normal 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;
|
||||
8
packages/bower-json/lib/util/createError.js
Normal file
8
packages/bower-json/lib/util/createError.js
Normal file
@@ -0,0 +1,8 @@
|
||||
function createError(msg, code) {
|
||||
var err = new Error(msg);
|
||||
err.code = code;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
module.exports = createError;
|
||||
12
packages/bower-json/lib/util/isAsset.js
Normal file
12
packages/bower-json/lib/util/isAsset.js
Normal 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;
|
||||
41
packages/bower-json/lib/util/isComponent.js
Normal file
41
packages/bower-json/lib/util/isComponent.js
Normal 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;
|
||||
40
packages/bower-json/package.json
Normal file
40
packages/bower-json/package.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
{
|
||||
5
packages/bower-json/test/pkg-bower-json/bower.json
Normal file
5
packages/bower-json/test/pkg-bower-json/bower.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "some-pkg",
|
||||
"version": "0.0.0",
|
||||
"main": "foo.js"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "some-pkg",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
5
packages/bower-json/test/pkg-dot-bower-json/.bower.json
Normal file
5
packages/bower-json/test/pkg-dot-bower-json/.bower.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "some-installed-pkg",
|
||||
"version": "0.0.1",
|
||||
"main": "bar.js"
|
||||
}
|
||||
497
packages/bower-json/test/test.js
Normal file
497
packages/bower-json/test/test.js
Normal 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();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user