Comparing version 0.19.1 to 1.2.0
@@ -144,3 +144,3 @@ `app.json` is a manifest format for describing web apps. It declares environment | ||
- `value`: a default value to use. This should always be a string. | ||
- `required`: A boolean indicating whether the given value is required for the app to function. | ||
- `required`: A boolean indicating whether the given value is required for the app to function (default: `true`). | ||
- `generator`: a string representing a function to call to generate the value. Currently the only supported generator is `secret`, which generates a pseudo-random string of characters. | ||
@@ -147,0 +147,0 @@ |
144
index.js
@@ -1,2 +0,3 @@ | ||
"use strict" | ||
"use strict"; | ||
var fs = require("fs") | ||
@@ -9,115 +10,50 @@ var url = require("url") | ||
var superagent = require("superagent") | ||
var revalidator = require("revalidator") | ||
var flatten = require("flatten") | ||
var isURL = require("is-url") | ||
var addons = require("./lib/addons") | ||
var schema = require("./lib/schema") | ||
var App = module.exports = (function() { | ||
var App = module.exports = require("./lib/app") | ||
function App(raw) { | ||
var key | ||
App.prototype.getAddonPrices = function(cb) { | ||
var _this = this | ||
App.addons.getPrices(this.addons, function(err, prices){ | ||
if (err) return cb(err) | ||
_this.prices = prices | ||
cb(null, prices) | ||
}) | ||
} | ||
if (typeof(raw) === 'string') { | ||
// Filename? | ||
if (raw.match(/\.json$/i)) { | ||
raw = fs.readFileSync(raw) | ||
} | ||
try { | ||
raw = JSON.parse(raw) | ||
} catch(err) { | ||
throw new Error("Malformed JSON") | ||
} | ||
} | ||
for (key in raw) { | ||
if (raw.hasOwnProperty(key)) { | ||
this[key] = raw[key] | ||
} | ||
} | ||
this.__defineGetter__("valid", function(){ | ||
return revalidator.validate(this, schema).valid | ||
}) | ||
this.__defineGetter__("errors", function(){ | ||
return revalidator.validate(this, schema).errors | ||
}) | ||
this.__defineGetter__("errorString", function(){ | ||
return this.errors.map(function(error) { | ||
return ["-", error.property, error.message].join(" ") | ||
}).join("\n") | ||
}) | ||
this.__defineGetter__("toJSON", function(){ | ||
var key | ||
var out = {} | ||
var validProps = Object.keys(schema.properties) | ||
for (key in this) { | ||
if (this.hasOwnProperty(key) && validProps.indexOf(key) > -1) { | ||
out[key] = this[key] | ||
} | ||
} | ||
return JSON.stringify(out, null, 2) | ||
}) | ||
return this | ||
App.fetch = function(repository, cb) { | ||
if (github(repository)) { | ||
repository = github(repository) | ||
} else if (bitbucket(repository)) { | ||
repository = bitbucket(repository) | ||
} else { | ||
return cb("A valid GitHub or Bitbucket URL is required: " + repository) | ||
} | ||
App.prototype.getAddonPrices = function(cb) { | ||
var _this = this | ||
App.addons.getPrices(this.addons, function(err, prices){ | ||
if (err) return cb(err) | ||
_this.prices = prices | ||
cb(null, prices) | ||
}) | ||
} | ||
App.new = function(raw) { | ||
return new App(raw) | ||
} | ||
App.fetch = function(repository, cb) { | ||
if (github(repository)) { | ||
repository = github(repository) | ||
} else if (bitbucket(repository)) { | ||
repository = bitbucket(repository) | ||
} else { | ||
return cb("A valid GitHub or Bitbucket URL is required: " + repository) | ||
var fetcher_url = url.format({ | ||
protocol: "https", | ||
hostname: "app-json-fetcher.herokuapp.com", | ||
query: { | ||
repository: repository.https_url | ||
} | ||
}) | ||
var fetcher_url = url.format({ | ||
protocol: "https", | ||
hostname: "app-json-fetcher.herokuapp.com", | ||
query: { | ||
repository: repository.https_url | ||
} | ||
}) | ||
superagent.get(fetcher_url, function(res){ | ||
cb(null, App.new(res.body)) | ||
}) | ||
} | ||
superagent.get(fetcher_url, function(res){ | ||
cb(null, App.new(res.body)) | ||
}) | ||
} | ||
// Hogan Templates FTW | ||
App.templates = {} | ||
if (module.parent) { | ||
App.templates.app = hogan.compile(fs.readFileSync(__dirname + '/templates/app.mustache.html').toString()) | ||
App.templates.build = hogan.compile(fs.readFileSync(__dirname + '/templates/build.mustache.html').toString()) | ||
App.templates.schema = hogan.compile(fs.readFileSync(__dirname + '/templates/schema.mustache.html').toString()) | ||
} else { | ||
App.templates.app = require('./templates/app.mustache.html') | ||
App.templates.build = require('./templates/build.mustache.html') | ||
App.templates.schema = require('./templates/schema.mustache.html') | ||
} | ||
// Hogan Templates FTW | ||
App.templates = {} | ||
if (module.parent) { | ||
App.templates.app = hogan.compile(fs.readFileSync(__dirname + '/templates/app.mustache.html').toString()) | ||
App.templates.build = hogan.compile(fs.readFileSync(__dirname + '/templates/build.mustache.html').toString()) | ||
App.templates.schema = hogan.compile(fs.readFileSync(__dirname + '/templates/schema.mustache.html').toString()) | ||
} else { | ||
App.templates.app = require('./templates/app.mustache.html') | ||
App.templates.build = require('./templates/build.mustache.html') | ||
App.templates.schema = require('./templates/schema.mustache.html') | ||
} | ||
App.example = new App(schema.example) | ||
App.addons = addons | ||
App.schema = schema | ||
return App | ||
})() | ||
App.addons = addons |
@@ -15,3 +15,4 @@ "use strict" | ||
"type": "string", | ||
"example": "This app does one little thing, and does it well." | ||
"example": "This app does one little thing, and does it well.", | ||
"maxLength": 140 | ||
}, | ||
@@ -56,3 +57,3 @@ "keywords": { | ||
"env": { | ||
"description": "A key-value object for environment variables, or [config vars](https://devcenter.heroku.com/articles/config-vars) in Heroku parlance. Keys are the names of the environment variables. Values can be strings or objects. If the value is a string, it will be used. If the value is an object, it defines specific requirements for that variable:\n\n- `description`: a human-friendly blurb about what the value is for and how to determine what it should be\n- `value`: a default value to use. This should always be a string.\n- `required`: A boolean indicating whether the given value is required for the app to function.\n- `generator`: a string representing a function to call to generate the value. Currently the only supported generator is `secret`, which generates a pseudo-random string of characters.", | ||
"description": "A key-value object for environment variables, or [config vars](https://devcenter.heroku.com/articles/config-vars) in Heroku parlance. Keys are the names of the environment variables. Values can be strings or objects. If the value is a string, it will be used. If the value is an object, it defines specific requirements for that variable:\n\n- `description`: a human-friendly blurb about what the value is for and how to determine what it should be\n- `value`: a default value to use. This should always be a string.\n- `required`: A boolean indicating whether the given value is required for the app to function (default: `true`).\n- `generator`: a string representing a function to call to generate the value. Currently the only supported generator is `secret`, which generates a pseudo-random string of characters.", | ||
"type": "object", | ||
@@ -59,0 +60,0 @@ "example": { |
{ | ||
"name": "app.json", | ||
"version": "0.19.1", | ||
"version": "1.2.0", | ||
"description": "Create, validate, and render Heroku app.json manifests", | ||
@@ -13,3 +13,7 @@ "main": "index.js", | ||
"test": "mocha", | ||
"build": "bin/build", | ||
"build": "npm run bundle; npm run minify; npm run bundle-lite; npm run minify-lite; ls -alh dist | grep app.json", | ||
"bundle": "browserify index.js --transform browserify-hogan --standalone App > dist/app.json.js", | ||
"bundle-lite": "browserify lib/app.js --transform browserify-hogan --standalone App > dist/app.json.lite.js", | ||
"minify": "cat dist/app.json.js | uglifyjs -o dist/app.json.min.js", | ||
"minify-lite": "cat dist/app.json.lite.js | uglifyjs -o dist/app.json.lite.min.js", | ||
"doc": "bin/doc" | ||
@@ -16,0 +20,0 @@ }, |
@@ -110,5 +110,11 @@ # app.json [![Build Status](https://travis-ci.org/app-json/app.json.png?branch=master)](https://travis-ci.org/app-json/app.json) | ||
If browserify isn't your thing, use the pre-compiled browser-ready bundle in | ||
[dist/app.js](/dist/app.js). Include this file in your html page and it will create | ||
[dist/app.json.js](/dist/app.json.js). Include this file in your html page and it will create | ||
`window.App` for you. | ||
You can also use Bower if that's your thing: | ||
```sh | ||
bower install app.json | ||
``` | ||
## Schema | ||
@@ -115,0 +121,0 @@ |
@@ -18,163 +18,2 @@ "use strict" | ||
describe("App.new", function() { | ||
it("accepts a filename", function() { | ||
app = App.new(__dirname + "/fixtures/valid/app.json") | ||
assert(app.valid) | ||
}) | ||
it("accepts a JSON string", function() { | ||
app = App.new(JSON.stringify(payload)) | ||
assert(app.valid) | ||
}) | ||
it("accepts a JavaScript object", function() { | ||
app = App.new(payload) | ||
assert(app.valid) | ||
}) | ||
it("throws a semi-helpful error when given a filename with malformed JSON", function() { | ||
assert.throws( | ||
function() { | ||
App.new(__dirname + "/fixtures/malformed/app.json") | ||
}, | ||
/malformed JSON/i | ||
) | ||
}) | ||
it("throws a semi-helpful error when given a malformed JSON string", function() { | ||
assert.throws( | ||
function() { | ||
App.new(fs.readFileSync(__dirname + "/fixtures/malformed/app.json").toString()) | ||
}, | ||
/malformed JSON/i | ||
) | ||
}) | ||
}) | ||
describe(".errors", function() { | ||
it("returns an array", function() { | ||
payload.name = "" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert(util.isArray(app.errors)) | ||
}) | ||
it("doesn't allow a blank string for name", function() { | ||
payload.name = "" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors[0].property, 'name') | ||
assert.equal(app.errors[0].message, "must not be empty") | ||
}) | ||
it("requires name to be at least three characters", function() { | ||
payload.name = "Hi" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors.length, 1) | ||
assert.equal(app.errors[0].property, 'name') | ||
assert.equal(app.errors[0].message, 'is too short (minimum is 3 characters)') | ||
}) | ||
it("requires name to be at under thirty characters", function() { | ||
payload.name = "12345678901234567890123456789012" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors.length, 1) | ||
assert.equal(app.errors[0].property, 'name') | ||
assert.equal(app.errors[0].message, 'is too long (maximum is 30 characters)') | ||
}) | ||
it("validates website url format", function() { | ||
payload.website = "not-a-url.com" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors.length, 1) | ||
assert.equal(app.errors[0].message, 'is not a valid url') | ||
}) | ||
it("validates repository url format", function() { | ||
payload.repository = "not-a-url.com" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors.length, 1) | ||
assert.equal(app.errors[0].property, 'repository') | ||
assert.equal(app.errors[0].message, 'is not a valid url') | ||
}) | ||
it("validates logo url format", function() { | ||
payload.logo = "not-a-url.com" | ||
app = App.new(payload) | ||
assert(!app.valid) | ||
assert.equal(app.errors.length, 1) | ||
assert.equal(app.errors[0].property, 'logo') | ||
assert.equal(app.errors[0].message, 'is not a valid url') | ||
}) | ||
it("returns an empty array if app is valid", function() { | ||
app = App.new(payload) | ||
assert(app.valid) | ||
assert(util.isArray(app.errors)) | ||
assert.equal(app.errors.length, 0) | ||
}) | ||
}) | ||
describe(".errorString", function() { | ||
it("returns a newline-delimited string of error messages", function() { | ||
payload.name = "no" | ||
payload.website = "not-a-url.com" | ||
app = App.new(payload) | ||
assert.equal(app.errors.length, 2) | ||
assert.equal(app.errorString, "- name is too short (minimum is 3 characters)\n- website is not a valid url") | ||
}) | ||
it("returns an empty string if app is valid", function() { | ||
app = App.new(payload) | ||
assert(app.valid) | ||
assert.equal(app.errorString, "") | ||
}) | ||
}) | ||
describe(".toJSON", function() { | ||
it("returns a pretty JSON string", function() { | ||
app = App.new(payload) | ||
assert(app.valid) | ||
var output = app.toJSON | ||
var app2 = App.new(output) | ||
assert.equal(typeof(output), 'string') | ||
assert(app2.valid) | ||
assert.equal(app.name, app2.name) | ||
}) | ||
it("ignores properties that are not in the schema", function() { | ||
payload.funky = true | ||
payload.junk = "stuff" | ||
app = App.new(payload) | ||
assert(app.valid) | ||
assert(app.funky) | ||
assert(app.junk) | ||
var output = app.toJSON | ||
var app2 = App.new(output) | ||
assert.equal(typeof(output), 'string') | ||
assert(app2.valid) | ||
assert(!app2.funky) | ||
assert(!app2.junk) | ||
}) | ||
}) | ||
describe(".deriveAddonsAndEnvFromHerokuApp()", function() { | ||
}) | ||
describe(".getAddonPrices()", function() { | ||
@@ -255,49 +94,2 @@ | ||
describe("App.schema", function() { | ||
it("exposes the schema as an object", function() { | ||
assert(App.schema) | ||
}) | ||
it("contains a key-value properties object", function() { | ||
assert(App.schema.properties) | ||
assert(App.schema.properties.name) | ||
assert(App.schema.properties.description) | ||
assert(App.schema.properties.keywords) | ||
}) | ||
it("exposes properties as an array for template-friendly rendering", function() { | ||
assert(App.schema.propertiesArray) | ||
assert(App.schema.propertiesArray[0].name) | ||
assert(App.schema.propertiesArray[0].description) | ||
assert(App.schema.propertiesArray[0].requiredOrOptional) | ||
}) | ||
it("exposes an exampleJSON property for use in documentation", function() { | ||
assert(App.schema.exampleJSON) | ||
assert.equal(typeof(App.schema.exampleJSON), "string") | ||
}) | ||
}) | ||
describe("App.example", function() { | ||
it("builds an example app from properties in the schema", function() { | ||
assert(App.example) | ||
}) | ||
it("is valid", function() { | ||
assert(App.example.valid) | ||
}) | ||
it("has expected properties", function() { | ||
assert(App.example.name) | ||
assert(App.example.description) | ||
assert(App.example.keywords) | ||
}) | ||
}) | ||
describe("App.templates", function() { | ||
@@ -304,0 +96,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
867545
32
12125
209
5
21