normalize-package-data
Advanced tools
Comparing version 0.1.7 to 0.2.0
145
lib/fixer.js
@@ -6,4 +6,8 @@ var semver = require("semver") | ||
var url = require("url") | ||
var typos = require("./typos") | ||
var fixer = module.exports = { | ||
// default warning function | ||
warn: function() {}, | ||
fixRepositoryField: function(data) { | ||
@@ -15,3 +19,3 @@ if (data.repositories) { | ||
} | ||
if (!data.repository) return; | ||
if (!data.repository) return this.warn('No repository field.') | ||
if (typeof data.repository === "string") { | ||
@@ -36,2 +40,26 @@ data.repository = { | ||
, fixTypos: function(data) { | ||
Object.keys(typos.topLevel).forEach(function (d) { | ||
if (data.hasOwnProperty(d)) { | ||
this.warn(makeTypoWarning(d, typos.topLevel[d])) | ||
} | ||
}, this) | ||
} | ||
, fixScriptsField: function(data) { | ||
if (!data.scripts) return | ||
if (typeof data.scripts !== "object") { | ||
this.warn("scripts must be an object") | ||
delete data.scripts | ||
} | ||
Object.keys(data.scripts).forEach(function (k) { | ||
if (typeof data.scripts[k] !== "string") { | ||
this.warn("script values must be string commands") | ||
delete data.scripts[k] | ||
} else if (typos.script[k]) { | ||
this.warn(makeTypoWarning(k, typos.script[k], "scripts")) | ||
} | ||
}, this) | ||
} | ||
, fixFilesField: function(data) { | ||
@@ -42,2 +70,11 @@ var files = data.files | ||
delete data.files | ||
} else if (data.files) { | ||
data.files = data.files.filter(function(file) { | ||
if (!file || typeof file !== "string") { | ||
this.warn("Invalid filename in 'files' list: " + file) | ||
return false | ||
} else { | ||
return true | ||
} | ||
}, this) | ||
} | ||
@@ -68,17 +105,71 @@ } | ||
} | ||
if (data[bd] && !Array.isArray(data[bd])) { | ||
this.warn("Invalid 'bundleDependencies' list. " + | ||
"Must be array of package names") | ||
delete data[bd] | ||
} else if (data[bd]) { | ||
data[bd] = data[bd].filter(function(bd) { | ||
if (!bd || typeof bd !== 'string') { | ||
this.warn("Invalid bundleDependencies member: " + bd) | ||
return false | ||
} else { | ||
return true | ||
} | ||
}, this) | ||
} | ||
} | ||
, fixDependencies: function(data) { | ||
, fixDependencies: function(data, strict) { | ||
var loose = !strict | ||
objectifyDeps(data, this.warn) | ||
addOptionalDepsToDeps(data, this.warn) | ||
this.fixBundleDependenciesField(data) | ||
;['dependencies','devDependencies'].forEach(function(deps) { | ||
if (!(deps in data)) return | ||
if (!data[deps] || typeof data[deps] !== "object") { | ||
this.warn(deps + " field must be an object") | ||
delete data[deps] | ||
return | ||
} | ||
Object.keys(data[deps]).forEach(function (d) { | ||
var r = data[deps][d] | ||
if (typeof r !== 'string') { | ||
this.warn('Invalid dependency: ' + d + ' ' + JSON.stringify(r)) | ||
delete data[deps][d] | ||
} | ||
}, this) | ||
}, this) | ||
} | ||
, fixKeywordsField: function (data, warn) { | ||
, fixModulesField: function (data) { | ||
if (data.modules) { | ||
this.warn("modules field is deprecated") | ||
delete data.modules | ||
} | ||
} | ||
, fixKeywordsField: function (data) { | ||
if (typeof data.keywords === "string") { | ||
data.keywords = data.keywords.split(/,\s+/) | ||
} | ||
if (data.keywords && !Array.isArray(data.keywords)) { | ||
delete data.keywords | ||
this.warn("keywords should be an array of strings") | ||
} else if (data.keywords) { | ||
data.keywords = data.keywords.filter(function(kw) { | ||
if (typeof kw !== "string" || !kw) { | ||
this.warn("keywords should be an array of strings"); | ||
return false | ||
} else { | ||
return true | ||
} | ||
}, this) | ||
} | ||
} | ||
, fixVersionField: function(data) { | ||
, fixVersionField: function(data, strict) { | ||
// allow "loose" semver 1.0 versions in non-strict mode | ||
// enforce strict semver 2.0 compliance in strict mode | ||
var loose = !strict | ||
if (!data.version) { | ||
@@ -88,6 +179,6 @@ data.version = "" | ||
} | ||
if (!semver.valid(data.version)) { | ||
throw new Error("invalid version: "+ data.version) | ||
if (!semver.valid(data.version, loose)) { | ||
throw new Error('Invalid version: "'+ data.version + '"') | ||
} | ||
data.version = semver.clean(data.version) | ||
data.version = semver.clean(data.version, loose) | ||
return true | ||
@@ -101,6 +192,6 @@ } | ||
, fixNameField: function(data) { | ||
if (!data.name) { | ||
, fixNameField: function(data, strict) { | ||
if (!data.name && !strict) { | ||
data.name = "" | ||
return true | ||
return | ||
} | ||
@@ -110,4 +201,5 @@ if (typeof data.name !== "string") { | ||
} | ||
data.name = data.name.trim() | ||
ensureValidName(data.name) | ||
if (!strict) | ||
data.name = data.name.trim() | ||
ensureValidName(data.name, strict) | ||
} | ||
@@ -123,6 +215,10 @@ | ||
data.description = extractDescription(data.readme) | ||
if (!data.description) this.warn('No description') | ||
} | ||
, fixReadmeField: function (data) { | ||
if (!data.readme) data.readme = "ERROR: No README data found!" | ||
if (!data.readme) { | ||
this.warn("No README data") | ||
data.readme = "ERROR: No README data found!" | ||
} | ||
} | ||
@@ -151,2 +247,3 @@ | ||
else { | ||
bugsTypos(data.bugs, this.warn) | ||
var oldBugs = data.bugs | ||
@@ -187,6 +284,7 @@ data.bugs = {} | ||
function ensureValidName (name) { | ||
function ensureValidName (name, strict) { | ||
if (name.charAt(0) === "." || | ||
name.match(/[\/@\s\+%:]/) || | ||
name !== encodeURIComponent(name) || | ||
(strict && name !== name.toLowerCase()) || | ||
name.toLowerCase() === "node_modules" || | ||
@@ -265,1 +363,20 @@ name.toLowerCase() === "favicon.ico") { | ||
} | ||
function bugsTypos(bugs, warn) { | ||
if (!bugs) return | ||
Object.keys(bugs).forEach(function (k) { | ||
if (typos.bugs[k]) { | ||
warn(makeTypoWarning(k, typos.bugs[k], "bugs")) | ||
bugs[typos.bugs[k]] = bugs[k] | ||
delete bugs[k] | ||
} | ||
}) | ||
} | ||
function makeTypoWarning (providedName, probableName, field) { | ||
if (field) { | ||
providedName = field + "['" + providedName + "']" | ||
probableName = field + "['" + probableName + "']" | ||
} | ||
return providedName + " should probably be " + probableName + "." | ||
} |
module.exports = normalize | ||
var isValid = require("./is_valid") | ||
var fixer = require("./fixer") | ||
var fieldsToFix = ['name','version','description','repository' | ||
var fieldsToFix = ['name','version','description','repository','modules','scripts' | ||
,'files','bin','man','bugs','keywords','readme','homepage'] | ||
var otherThingsToFix = ['dependencies','people'] | ||
var otherThingsToFix = ['dependencies','people', 'typos'] | ||
@@ -18,5 +17,7 @@ var thingsToFix = fieldsToFix.map(function(fieldName) { | ||
function normalize (data, warn) { | ||
function normalize (data, warn, strict) { | ||
if(warn === true) warn = null, strict = true | ||
if(!strict) strict = false | ||
if(!warn) warn = function(msg) { /* noop */ } | ||
isValid(data, warn) // don't care if it's valid, we'll make it valid | ||
if (data.scripts && | ||
@@ -29,6 +30,5 @@ data.scripts.install === "node-gyp rebuild" && | ||
thingsToFix.forEach(function(thingName) { | ||
fixer["fix" + ucFirst(thingName)](data) | ||
fixer["fix" + ucFirst(thingName)](data, strict) | ||
}) | ||
data._id = data.name + "@" + data.version | ||
if (data.modules) delete data.modules // modules field is deprecated | ||
} | ||
@@ -35,0 +35,0 @@ |
@@ -20,5 +20,6 @@ { | ||
,"publicationConfig": "publishConfig" | ||
,"script": "scripts" | ||
}, | ||
"bugs": { "web": "url", "name": "url" }, | ||
"script": { "server": "start", "tests": "test" } | ||
} | ||
} |
{ | ||
"name": "normalize-package-data", | ||
"version": "0.1.7", | ||
"version": "0.2.0", | ||
"author": "Meryn Stol <merynstol@gmail.com>", | ||
@@ -15,3 +15,3 @@ "description": "Normalizes data that can be found in package.json files.", | ||
"dependencies": { | ||
"semver": "1.x", | ||
"semver": "2", | ||
"github-url-from-git": "~1.1.1" | ||
@@ -18,0 +18,0 @@ }, |
@@ -50,3 +50,7 @@ var tap = require("tap") | ||
t.same(packageData, expect) | ||
t.same(warnings, ["No repository field.","No readme data."]) | ||
t.same(warnings, [ | ||
"No description", | ||
"No repository field.", | ||
"No README data" | ||
]) | ||
t.end() | ||
@@ -77,10 +81,11 @@ }) | ||
var expect = | ||
[ 'No repository field.', | ||
'No readme data.', | ||
'bugs.url field must be a string url. Deleted.', | ||
'bugs.email field must be a string email. Deleted.', | ||
'Normalized value of bugs field is an empty object. Deleted.', | ||
'Bug string field must be url, email, or {email,url}', | ||
'Normalized value of bugs field is an empty object. Deleted.', | ||
'homepage field must be a string url. Deleted.' ] | ||
[ "No description", | ||
"No repository field.", | ||
"bugs.url field must be a string url. Deleted.", | ||
"bugs.email field must be a string email. Deleted.", | ||
"Normalized value of bugs field is an empty object. Deleted.", | ||
"No README data", | ||
"Bug string field must be url, email, or {email,url}", | ||
"Normalized value of bugs field is an empty object. Deleted.", | ||
"homepage field must be a string url. Deleted." ] | ||
t.same(warnings, expect) | ||
@@ -103,5 +108,6 @@ t.end() | ||
var expect = | ||
[ 'No repository field.', | ||
'No readme data.', | ||
'homepage field must start with a protocol.' ] | ||
[ "No description", | ||
"No repository field.", | ||
"No README data", | ||
"homepage field must start with a protocol." ] | ||
t.same(warnings, expect) | ||
@@ -122,2 +128,9 @@ t.same(a.homepage, 'http://example.org') | ||
tap.test("singularize repositories", function(t) { | ||
var d = {repositories:["git@gist.github.com:123456.git"]} | ||
normalize(d) | ||
t.same(d.repository, { type: 'git', url: 'git@gist.github.com:123456.git' }) | ||
t.end() | ||
}); | ||
tap.test('no new globals', function(t) { | ||
@@ -127,8 +140,1 @@ t.same(Object.keys(global), globals) | ||
}) | ||
tap.test("singularize repositories", function(t) { | ||
d = {repositories:["git@gist.github.com:123456.git"]} | ||
normalize(d) | ||
t.same(d.repository, { type: 'git', url: 'git@gist.github.com:123456.git' }) | ||
t.end() | ||
}); |
@@ -12,3 +12,4 @@ var test = require('tap').test | ||
var expect = | ||
[ 'dependancies should probably be dependencies.', | ||
[ 'No repository field.', | ||
'dependancies should probably be dependencies.', | ||
'dependecies should probably be dependencies.', | ||
@@ -29,10 +30,3 @@ 'depdenencies should probably be dependencies.', | ||
'contributers should probably be contributors.', | ||
'publicationConfig should probably be publishConfig.', | ||
'No repository field.', | ||
'No repository field.', | ||
'No readme data.', | ||
'bugs.url field must be a string url. Deleted.', | ||
'Normalized value of bugs field is an empty object. Deleted.', | ||
'No repository field.', | ||
'No readme data.' ] | ||
'publicationConfig should probably be publishConfig.' ] | ||
@@ -60,2 +54,14 @@ normalize({"dependancies": "dependencies" | ||
t.same(warnings, expect) | ||
warnings.length = 0 | ||
var expect = | ||
[ 'No description', | ||
'No repository field.', | ||
'bugs[\'web\'] should probably be bugs[\'url\'].', | ||
'bugs[\'name\'] should probably be bugs[\'url\'].', | ||
'bugs.url field must be a string url. Deleted.', | ||
'Normalized value of bugs field is an empty object. Deleted.', | ||
"No README data" ] | ||
normalize({name:"name" | ||
@@ -65,7 +71,32 @@ ,version:"1.2.5" | ||
t.same(warnings, expect) | ||
warnings.length = 0 | ||
var expect = | ||
[ 'No description', | ||
'No repository field.', | ||
"No README data", | ||
'script should probably be scripts.' ] | ||
normalize({name:"name" | ||
,version:"1.2.5" | ||
,script:{server:"start",tests:"test"}}, warn) | ||
t.same(warnings, expect) | ||
warnings.length = 0 | ||
expect = | ||
[ 'No description', | ||
'No repository field.', | ||
'scripts[\'server\'] should probably be scripts[\'start\'].', | ||
'scripts[\'tests\'] should probably be scripts[\'test\'].', | ||
"No README data" ] | ||
normalize({name:"name" | ||
,version:"1.2.5" | ||
,scripts:{server:"start",tests:"test"}}, warn) | ||
t.same(warnings, expect) | ||
t.end(); | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43165
1201
+ Addedsemver@2.3.2(transitive)
- Removedsemver@1.1.4(transitive)
Updatedsemver@2