Skip to content

Commit

Permalink
Merge pull request #1 from borodean/promise
Browse files Browse the repository at this point in the history
Promise support
  • Loading branch information
borodean authored Feb 13, 2020
2 parents dca6736 + b672cbb commit b70cd83
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 48 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*.log
*.sublime-*
borodean-jsonp-*.tgz
dist/jsonp.min.js
dist/jsonp.min.js.map
dist/*.min.js
dist/*.min.js.map
node_modules
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.npmignore
.travis.yml
borodean-jsonp-*.tgz
dist/*-*.*.*.min.*
karma.conf.js
rollup.config.js
test.js
test
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,27 @@
Loads data from the server using [JSONP][jsonp]. Example:

```js
jsonp('https://jsfiddle.net/echo/jsonp?foo=bar', function (err, data) {
import jsonp from '@borodean/jsonp';

jsonp('https://jsfiddle.net/echo/jsonp?foo=bar', (err, data) => {
if (err) throw err;
console.log(data);
});
```

# Promise version

A version that returns a promise is also available:

```js
import jsonp from '@borodean/jsonp/promise';

jsonp('https://jsfiddle.net/echo/jsonp?foo=bar').then(
data => console.log(data),
err => console.log(err)
);
```

## Installation

```
Expand All @@ -26,11 +41,18 @@ npm install @borodean/jsonp

For a browser global version check the `dist` directory of the installed module or directly download it:

- [Production version][dl] – 268 bytes, minified and gzipped
- [Source map][dl-map]
- [Production version][dl-callback] – 266 bytes, minified and gzipped
- [Source map][dl-callback-map]

Promise version:

- [Production version][dl-promise] – 277 bytes, minified and gzipped
- [Source map][dl-promise-map]

[dl]: https://github.com/borodean/jsonp/releases/download/2.0.0/jsonp-2.0.0.min.js
[dl-map]: https://github.com/borodean/jsonp/releases/download/2.0.0/jsonp-2.0.0.min.js.map
[dl-callback]: https://github.com/borodean/jsonp/releases/download/3.0.0/jsonp-3.0.0.min.js
[dl-callback-map]: https://github.com/borodean/jsonp/releases/download/3.0.0/jsonp-3.0.0.min.js.map
[dl-promise]: https://github.com/borodean/jsonp/releases/download/3.0.0/jsonp-promise-3.0.0.min.js
[dl-promise-map]: https://github.com/borodean/jsonp/releases/download/3.0.0/jsonp-promise-3.0.0.min.js.map
[jsonp]: http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/
[sauce]: https://saucelabs.com/u/borodean-jsonp
[sauce-matrix]: https://saucelabs.com/browser-matrix/borodean-jsonp.svg
4 changes: 2 additions & 2 deletions index.js → callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = function (url, options, callback) {
var script = document.createElement('script');
script.src = parameter ? (url + (~url.indexOf('?') ? '&' : '?') + parameter + '=' + key) : url; // eslint-disable-line no-implicit-coercion

script.onerror = function () {
script.onerror = function () { // eslint-disable-line unicorn/prefer-add-event-listener
delete object[key];
callback(new Error());
};
Expand All @@ -23,5 +23,5 @@ module.exports = function (url, options, callback) {
callback(null, response);
};

document.head.removeChild(document.head.appendChild(script));
document.head.removeChild(document.head.appendChild(script)); // eslint-disable-line unicorn/prefer-node-append
};
4 changes: 2 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ module.exports = config => {
browserify: {
debug: true
},
files: ['test.js'],
files: ['test/*'],
frameworks: ['browserify', 'chai', 'mocha', 'sinon'],
preprocessors: {
'test.js': ['browserify']
'test/*': ['browserify']
},
reporters: ['dots'],
singleRun: true
Expand Down
20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
{
"name": "@borodean/jsonp",
"version": "2.0.0",
"version": "3.0.0",
"description": "The smallest possible JSONP implementation",
"license": "MIT",
"author": "Vadym Borodin <[email protected]> http://borodean.com",
"main": "callback.js",
"repository": "borodean/jsonp",
"scripts": {
"build": "rollup -co dist/jsonp.min.js",
"lint": "xo --env=browser --env=mocha --global expect --global sinon --space",
"build": "rollup -c",
"lint": "xo --env=browser --env=mocha --global expect --global sinon --no-esnext --space",
"test": "karma start",
"test-local": "karma start --local"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.2",
"browserify": "^16.5.0",
"chai": "*",
"core-js": "^3.6.4",
"karma": "^4.4.1",
"karma-browserify": "^7.0.0",
"karma-chai": "^0.1.0",
Expand All @@ -22,12 +25,11 @@
"karma-sinon": "^1.0.5",
"lodash": "^4.17.15",
"mocha": "^4.1.0",
"rollup": "^0.41.4",
"rollup-plugin-commonjs": "^7.0.0",
"rollup-plugin-filesize": "^1.0.1",
"rollup-plugin-uglify": "^1.0.1",
"sinon": "^1.17.7",
"rollup": "^1.31.0",
"rollup-plugin-filesize": "^6.2.1",
"rollup-plugin-uglify": "^6.0.4",
"sinon": "^7.5.0",
"watchify": "^3.11.1",
"xo": "^0.17.1"
"xo": "^0.26.0"
}
}
26 changes: 26 additions & 0 deletions promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
var count = 0;

module.exports = function (url, options) {
options = options || {};

var object = options.object || window;
var key = options.key || 'j' + count++;
var parameter = 'parameter' in options ? options.parameter : 'callback';

var script = document.createElement('script');
script.src = parameter ? (url + (~url.indexOf('?') ? '&' : '?') + parameter + '=' + key) : url; // eslint-disable-line no-implicit-coercion

return new Promise(function (resolve, reject) {
script.onerror = function () { // eslint-disable-line unicorn/prefer-add-event-listener
delete object[key];
reject(new Error());
};

object[key] = function (response) {
delete object[key];
resolve(response);
};

document.head.removeChild(document.head.appendChild(script)); // eslint-disable-line unicorn/prefer-node-append
});
};
41 changes: 31 additions & 10 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
/* eslint-disable camelcase */

module.exports = {
entry: 'index.js',
format: 'iife',
moduleName: 'jsonp',
import commonjs from '@rollup/plugin-commonjs';
import filesize from 'rollup-plugin-filesize';
import {uglify} from 'rollup-plugin-uglify';

import {version} from './package.json';

const name = 'jsonp';

const createInput = (input, outputBasename) => ({
input,
output: [
createOutput(`dist/${outputBasename}.min.js`),
createOutput(`dist/${outputBasename}-${version}.min.js`)
],
plugins: [
require('rollup-plugin-commonjs')(),
require('rollup-plugin-filesize')(),
require('rollup-plugin-uglify')({
commonjs(),
filesize(),
uglify({
compress: {
collapse_vars: true,
unsafe: true
},
mangle: true
})
],
sourceMap: true
};
]
});

const createOutput = file => ({
file,
format: 'iife',
name,
sourcemap: true
});

export default [
createInput('callback.js', name),
createInput('promise.js', `${name}-promise`)
];
44 changes: 27 additions & 17 deletions test.js → test/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@

var _ = require('lodash');

var jsonp = require('.');
var jsonp = require('../callback');

var appendChild = sinon.spy(document.head, 'appendChild');

describe('jsonp', function () {
describe('jsonp/callback', function () {
this.timeout(20000);

beforeEach(function () {
sinon.spy(document.head, 'appendChild');
window.foo = {};
});

afterEach(function () {
document.head.appendChild.restore();
delete window.foo;
});

it('injects a script', function (done) {
jsonp('https://jsfiddle.net/echo/jsonp', done);
expect(appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?callback=j0');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?callback=j0');
});

it('respects query parameters', function (done) {
jsonp('https://jsfiddle.net/echo/jsonp?foo=bar', done);
expect(appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?foo=bar&callback=j1');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?foo=bar&callback=j1');
});

it('handles simultaneous requests', function (done) {
Expand Down Expand Up @@ -64,26 +72,28 @@ describe('jsonp', function () {

it('sets a custom callback query parameter', function (done) {
jsonp('https://www.reddit.com/api/info.json', {parameter: 'jsonp'}, done);
expect(appendChild.lastCall.args[0].src).to.equal('https://www.reddit.com/api/info.json?jsonp=j6');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://www.reddit.com/api/info.json?jsonp=j6');
});

it('disables the callback query parameter', function (done) {
jsonp('https://httpbin.org/status/400', {parameter: ''}, done.bind(this, null));
expect(appendChild.lastCall.args[0].src).to.equal('https://httpbin.org/status/400');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://httpbin.org/status/400');
});

it('sets a custom callback name', function (done) {
jsonp('https://jsfiddle.net/echo/jsonp', {key: 'foo'}, done);
expect(window.foo).to.be.a('function');
expect(appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?callback=foo');
it('retrieves data via a custom callback name', function (done) {
jsonp('https://jsfiddle.net/echo/jsonp?foo=bar', {key: 'foo'}, function (err, data) {
expect(err).to.be.null;
expect(data).to.deep.equal({foo: 'bar'});
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?foo=bar&callback=foo');
done();
});
});

it('sets a custom callback object', function (done) {
window.foo = {};
jsonp('https://jsfiddle.net/echo/jsonp?callback=foo.bar', {object: window.foo, key: 'bar', parameter: ''}, function () {
delete window.foo;
it('retrieves data via a custom callback object', function (done) {
jsonp('https://jsfiddle.net/echo/jsonp?foo=bar&callback=foo.bar', {object: window.foo, key: 'bar', parameter: ''}, function (err, data) {
expect(err).to.be.null;
expect(data).to.deep.equal({foo: 'bar'});
done();
});
expect(window.foo.bar).to.be.a('function');
});
});
89 changes: 89 additions & 0 deletions test/promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-disable import/no-unassigned-import */
/* eslint-disable no-unused-expressions */
/* eslint-disable promise/prefer-await-to-then */

require('core-js/features/promise');
var _ = require('lodash');

var jsonp = require('../promise');

describe('jsonp/promise', function () {
this.timeout(20000);

beforeEach(function () {
sinon.spy(document.head, 'appendChild');
window.foo = {};
});

afterEach(function () {
document.head.appendChild.restore();
delete window.foo;
});

it('injects a script', function () {
var promise = jsonp('https://jsfiddle.net/echo/jsonp');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?callback=j0');
return promise;
});

it('respects query parameters', function () {
var promise = jsonp('https://jsfiddle.net/echo/jsonp?foo=bar');
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?foo=bar&callback=j1');
return promise;
});

it('handles simultaneous requests', function () {
return Promise.all([
jsonp('https://jsfiddle.net/echo/jsonp?foo=bar&delay=1'),
jsonp('https://jsfiddle.net/echo/jsonp?baz=qux')
]).then(function (data) {
expect(data[0]).to.deep.equal({foo: 'bar'});
expect(data[1]).to.deep.equal({baz: 'qux'});
});
});

it('retrieves data and cleans up', function () {
var promise = jsonp('https://jsfiddle.net/echo/jsonp?foo=bar');
return promise.then(function (data) {
expect(data).to.deep.equal({foo: 'bar'});
expect(Object.keys(window).some(RegExp.prototype.test.bind(/^j\d+/))).to.be.false;
expect(document.querySelectorAll('script[src*="jsfiddle.net"]')).to.have.lengthOf(0);
});
});

it('fails and cleans up', function () {
var promise = jsonp('https://httpbin.org/status/400');
return promise.then(expect.fail, function (err) {
expect(err).to.be.an('error');
expect(Object.keys(window).some(RegExp.prototype.test.bind(/^j\d+/))).to.be.false;
expect(document.querySelectorAll('script[src*="jsfiddle.net"]')).to.have.lengthOf(0);
});
});

it('sets a custom callback query parameter', function () {
var promise = jsonp('https://www.reddit.com/api/info.json', {parameter: 'jsonp'});
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://www.reddit.com/api/info.json?jsonp=j6');
return promise;
});

it('disables the callback query parameter', function () {
var promise = jsonp('https://httpbin.org/status/400', {parameter: ''});
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://httpbin.org/status/400');
return promise.then(expect.fail, _.noop);
});

it('retrieves data via a custom callback name', function () {
var promise = jsonp('https://jsfiddle.net/echo/jsonp?foo=bar', {key: 'foo'});
return promise.then(function (data) {
expect(data).to.deep.equal({foo: 'bar'});
expect(document.head.appendChild.lastCall.args[0].src).to.equal('https://jsfiddle.net/echo/jsonp?foo=bar&callback=foo');
});
});

it('retrieves data via a custom callback object', function () {
var promise = jsonp('https://jsfiddle.net/echo/jsonp?foo=bar&callback=foo.bar', {object: window.foo, key: 'bar', parameter: ''});
return promise.then(function (data) {
expect(data).to.deep.equal({foo: 'bar'});
});
});
});

0 comments on commit b70cd83

Please sign in to comment.