├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── address.json ├── addressBook.json ├── embedded.json ├── person.json └── splitPerson.json ├── index.js ├── package.json ├── test ├── json │ ├── emb.json │ └── paths.json ├── parent │ ├── child │ │ ├── footer.json │ │ └── n.json │ └── footer.json ├── pointier_modules │ └── p2.json ├── pointy_modules │ └── p1.json └── test.js └── wrapped.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | .idea 30 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext":true, 3 | "curly": false, 4 | "eqeqeq": true, 5 | "immed": true, 6 | "latedef": true, 7 | "newcap": true, 8 | "noarg": true, 9 | "sub": true, 10 | "undef": false, 11 | "unused": false, 12 | "boss": true, 13 | "eqnull": true, 14 | "node": true 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | - 0.12 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | refify.js 2 | Copyright (C) 2015 Lasana Murray 3 | 4 | This software is released under the MIT license: 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-url]][daviddm-image] 2 | 3 | > ~~A browserify transform that resolves json pointers.~~ 4 | > A browserify transform that allows you to bundle json files inside other json files. 5 | Very loosely based on [RFC 6901](https://tools.ietf.org/html/rfc6901) 6 | 7 | ## Install 8 | 9 | ```sh 10 | $ npm install --save pointify 11 | ``` 12 | 13 | 14 | ## Usage 15 | 16 | ```js 17 | var pointify = require('pointify'); 18 | var browserify = require('browserify'); 19 | var someJSONFile = require('./someJSONFile'); 20 | 21 | var b = browserify(); 22 | b.add(someJSONFile); 23 | b.transform(pointify); 24 | b.bundle(function(err, src){ 25 | console.dir(src); 26 | //Prints {"owner":"{"name":"Gus"}}; 27 | }); 28 | 29 | //In some json file 30 | {"owner": {"$ref":"owner.json"} 31 | 32 | //or alternatively embed the $ref in a string (not part of the rfc) 33 | {"owner":"$ref owner.json"} 34 | 35 | //owner.json 36 | {"name":"Gus"} 37 | 38 | ``` 39 | 40 | ##Notes 41 | 42 | Check out [RFC6901](https://tools.ietf.org/html/rfc6901) before you use this transform. There is a nice 43 | example of its benefits [here](http://spacetelescope.github.io/understanding-json-schema/structuring.html) in relation to json schema. 44 | 45 | ~~This transform only deals with references expected to be in other files at the moment. 46 | Full support for in document schema is planned.~~ 47 | 48 | After using this on a project I no longer really see a need to support json pointer's syntax, 49 | doing so in a browserify transform seems like a lot of worth with little benifit. Maybe someday as 50 | an experiment, for now this transform is useful if you use what to have browserify combine 51 | some json files on your behalf. 52 | 53 | All it does is require the json file and place it into the document. ~~The requires are relative 54 | to the document calling require.~~ No longer uses requires, it will load the contents of the file 55 | and parse them into JSON. 56 | 57 | ## License 58 | 59 | MIT © [Lasana Murray](http://trinistorm.org) 60 | 61 | 62 | [npm-url]: https://npmjs.org/package/pointify 63 | [npm-image]: https://badge.fury.io/js/pointify.svg 64 | [travis-url]: https://travis-ci.org/metasansana/pointify 65 | [travis-image]: https://travis-ci.org/metasansana/pointify.svg?branch=master 66 | [daviddm-url]: https://david-dm.org/metasansana/pointify.svg?theme=shields.io 67 | [daviddm-image]: https://david-dm.org/metasansana/pointify 68 | 69 | -------------------------------------------------------------------------------- /examples/address.json: -------------------------------------------------------------------------------- 1 | { 2 | "street_address": "1600 Pennsylvania Avenue NW", 3 | "city": "Washington", 4 | "state": "DC", 5 | "type": "business" 6 | } 7 | -------------------------------------------------------------------------------- /examples/addressBook.json: -------------------------------------------------------------------------------- 1 | { 2 | "owner":"Shaina Purple", 3 | "addresses":[ 4 | {"$ref":"address.json"}, 5 | {"$ref":"address.json"}, 6 | {"$ref":"address.json"} 7 | ] 8 | } 9 | 10 | -------------------------------------------------------------------------------- /examples/embedded.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Lasana Murray", 3 | "alterEgo": "$ref person.json" 4 | } 5 | -------------------------------------------------------------------------------- /examples/person.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Lasana Murray", 3 | "address":{"$ref":"address.json", "phone":"253-4444"} 4 | } 5 | -------------------------------------------------------------------------------- /examples/splitPerson.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Lasana Murray", 3 | "alterEgo":{"$ref":"person.json"} 4 | } 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require('through'); 2 | var traverse = require('traverse'); 3 | var dirname = require('path').dirname; 4 | var fs = require('fs'); 5 | var starts_with = require('lodash.startswith'); 6 | 7 | function pointify(file, opts) { 8 | 9 | var data = ''; 10 | opts = opts || {}; 11 | 12 | function is_json(file) { 13 | return (/\.json$/).test(file); 14 | } 15 | 16 | function open(path) { 17 | 18 | try { 19 | return fs.readFileSync(path); 20 | } catch (e) { 21 | return null; 22 | } 23 | 24 | } 25 | 26 | function get_file_contents(path, paths) { 27 | 28 | var buf; 29 | var searched = path + '\n'; 30 | var foundDir = dirname(file) + '/' + path; 31 | 32 | buf = open(path); 33 | buf = (buf) ? buf : open(foundDir); 34 | foundDir = dirname(foundDir); 35 | 36 | if (!buf) 37 | paths.forEach(function(p) { 38 | if (!buf) { 39 | buf = open(p + '/' + path); 40 | searched += p + '\n'; 41 | if (buf) foundDir = dirname(p + '/' + path); 42 | } 43 | }); 44 | 45 | if (!buf) throw new Error('Unable to locate ' + 46 | path + ' (started processing from: ' + file + ')!\n' + 'Searched in :\n' + searched + '\nEND'); 47 | 48 | return { 49 | dir: foundDir, 50 | contents: JSON.parse(buf.toString('utf8')) 51 | }; 52 | 53 | } 54 | 55 | function merge_keys(keys, o, node) { 56 | if (keys.length > 1) 57 | keys.forEach(function(key) { 58 | if (key !== '$ref') 59 | o[key] = node[key]; 60 | }); 61 | } 62 | 63 | function marshall(data, paths) { 64 | 65 | traverse(data). 66 | forEach(function(value) { 67 | 68 | var results; 69 | var passedPaths; 70 | var isObject = (this.key === '$ref'); 71 | var isString = starts_with(value, '$ref '); 72 | var shouldProcess = (isObject || isString); 73 | var self = this; 74 | 75 | paths = (paths) ? (Array.isArray(paths)) ? 76 | paths : [paths] : []; 77 | 78 | if (isString) { 79 | value = value.split(' '); 80 | value.shift(); 81 | value = value.join(''); 82 | } 83 | 84 | if (shouldProcess) 85 | results = get_file_contents(value, paths); 86 | 87 | if (isObject) { 88 | merge_keys(Object.keys(this.parent.node), 89 | results.contents, this.parent.node); 90 | self = this.parent; 91 | } 92 | 93 | if (shouldProcess) { 94 | passedPaths = paths.slice(); 95 | passedPaths.push(results.dir); 96 | self.update(marshall(results.contents, passedPaths)); 97 | } 98 | 99 | }); 100 | 101 | return data; 102 | 103 | } 104 | 105 | function write(buf) { 106 | data += buf; 107 | } 108 | 109 | function end() { 110 | 111 | var obj; 112 | 113 | try { 114 | obj = JSON.parse(data); 115 | } catch (e) { 116 | throw new Error('Error while procesing ' + file + ': ' + e.message + '\n Contents:\n' + data); 117 | } 118 | 119 | this.queue(String(JSON.stringify(marshall(obj, opts.paths || [])))); 120 | this.queue(null); 121 | 122 | } 123 | 124 | if (!is_json(file)) return through(); 125 | return through(write, end); 126 | 127 | } 128 | 129 | module.exports = pointify; 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pointify", 3 | "version": "1.0.3", 4 | "description": "A browserify plugin for json pointer support.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "through": "^2.3.7", 8 | "lodash.startswith": "^3.0.1", 9 | "traverse": "^0.6.6" 10 | }, 11 | "devDependencies": { 12 | "browserify": "^9.0.8", 13 | "mocha": "^2.2.5", 14 | "must": "^0.12.0" 15 | }, 16 | "scripts": { 17 | "test": "./node_modules/.bin/mocha -R list --timeout 10000 test/*.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/metasansana/refify.git" 22 | }, 23 | "keywords": [ 24 | "browserify", 25 | "$ref", 26 | "json-pointer", 27 | "json", 28 | "rfc-6901", 29 | "browserify-transform" 30 | ], 31 | "license": "MIT" 32 | } 33 | -------------------------------------------------------------------------------- /test/json/emb.json: -------------------------------------------------------------------------------- 1 | { 2 | "values":"$ref p1.json" 3 | } 4 | -------------------------------------------------------------------------------- /test/json/paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "values":{"$ref":"p1.json"} 3 | } 4 | -------------------------------------------------------------------------------- /test/parent/child/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"child foot" 3 | } 4 | -------------------------------------------------------------------------------- /test/parent/child/n.json: -------------------------------------------------------------------------------- 1 | { 2 | "a":{ "$ref": "./footer.json" 3 | }} 4 | -------------------------------------------------------------------------------- /test/parent/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"parent foot" 3 | } 4 | -------------------------------------------------------------------------------- /test/pointier_modules/p2.json: -------------------------------------------------------------------------------- 1 | ["x","y","z"] 2 | -------------------------------------------------------------------------------- /test/pointy_modules/p1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Lasana Murray", 3 | "paths":{"$ref":"p2.json"} 4 | } 5 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var must = require('must'); 2 | var path = require('path'); 3 | var browserify = require('browserify'); 4 | var transform = require(__dirname + '/../wrapped.js'); 5 | var result; 6 | 7 | function bundle(file) { 8 | var b = browserify(); 9 | b.add(file); 10 | b.transform(transform(result), { 11 | paths: [__dirname + '/pointy_modules', __dirname + '/pointier_modules'] 12 | }); 13 | return b; 14 | } 15 | 16 | describe('bundle transform', function() { 17 | 18 | beforeEach(function() { 19 | result = {}; 20 | }); 21 | 22 | it('should work', function(done) { 23 | 24 | bundle(__dirname + '/../examples/person.json').bundle(function(err, src) { 25 | 26 | if (err) return done(err); 27 | 28 | must(result.data).to.be('{"name":"Lasana Murray",' + 29 | '"address":{"street_address":"1600 Pennsylvania Avenue NW",' + 30 | '"city":"Washington","state":"DC","type":"business","phone":"253-4444"}}'); 31 | done(); 32 | }); 33 | 34 | }); 35 | 36 | it('should work with arrays', function(done) { 37 | 38 | bundle(__dirname + '/../examples/addressBook.json').bundle(function(err, src) { 39 | 40 | if (err) return done(err); 41 | 42 | must(result.data).to.be( 43 | '{"owner":"Shaina Purple","addresses":' + 44 | '[{"street_address":"1600 Pennsylvania Avenue NW",' + 45 | '"city":"Washington","state":"DC","type":"business"},' + 46 | '{"street_address":"1600 Pennsylvania Avenue NW","city":"Washington",' + 47 | '"state":"DC","type":"business"},{"street_address":' + 48 | '"1600 Pennsylvania Avenue NW","city":"Washington","state":"DC",' + 49 | '"type":"business"}]}'); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should work recursively', function(done) { 55 | 56 | bundle(__dirname + '/../examples/splitPerson.json').bundle(function(err, src) { 57 | 58 | if (err) return done(err); 59 | 60 | must(result.data).to.be('{"name":"Lasana Murray",' + 61 | '"alterEgo":{"name":"Lasana Murray","address":' + 62 | '{"street_address":"1600 Pennsylvania Avenue NW","city":"Washington",' + 63 | '"state":"DC","type":"business","phone":"253-4444"}}}'); 64 | done(); 65 | }); 66 | 67 | }); 68 | 69 | it('should work with paths', function(done) { 70 | 71 | bundle(__dirname + '/json/paths.json').bundle(function(err, src) { 72 | 73 | if (err) return done(err); 74 | 75 | must(result.data).to.be('{"values":{"name":"Lasana Murray",' + 76 | '"paths":["x","y","z"]}}'); 77 | done(); 78 | }); 79 | 80 | }); 81 | 82 | it('should work with embedded strings', function(done) { 83 | 84 | bundle(__dirname + '/json/emb.json').bundle(function(err, src) { 85 | 86 | if (err) return done(err); 87 | 88 | must(result.data).to.be('{"values":{"name":"Lasana Murray",' + 89 | '"paths":["x","y","z"]}}'); 90 | done(); 91 | }); 92 | 93 | }); 94 | 95 | it('should not resolve the parent before child if they both have a matching file name', function(done) { 96 | 97 | bundle(__dirname + '/parent/child/n.json'). 98 | bundle(function(err, src) { 99 | 100 | if (err) return done(err); 101 | 102 | must(result.data).to.be('{"a":{"name":"child foot"}}'); 103 | done(); 104 | 105 | }); 106 | 107 | 108 | }); 109 | 110 | 111 | }); 112 | -------------------------------------------------------------------------------- /wrapped.js: -------------------------------------------------------------------------------- 1 | var transform = require('./index.js'); 2 | 3 | module.exports = function(result) { 4 | return function(file, opts) { 5 | 6 | var data = ''; 7 | var stream = transform(file,opts); 8 | 9 | stream.on('data', function(buf) { 10 | data += buf; 11 | }); 12 | stream.on('end', function() { 13 | result.data = data; 14 | }); 15 | 16 | return stream; 17 | }; 18 | }; 19 | --------------------------------------------------------------------------------