Skip to content

Commit

Permalink
Add an ESM for browsers build (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
wKovacs64 committed Oct 17, 2018
1 parent 0d2676f commit 1d31e46
Show file tree
Hide file tree
Showing 23 changed files with 56,712 additions and 219 deletions.
11 changes: 4 additions & 7 deletions .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ module.exports = {
[
'@babel/preset-env',
{
loose: true,
modules: false,
targets: test
? { node: 'current' }
: {
browsers: ['> 1%', 'last 2 versions'],
node: 6,
},
targets: {
browsers: ['> 1%', 'last 2 versions'],
node: 6,
},
},
],
],
Expand Down
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ dist
es
example
lib
babel-browser-build.js
browser-es-module-loader.js
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
.prettierignore
.prettierrc
.travis.yml
.vscode
coverage
cypress.json
husky.config.js
jest.config.js
renovate.json
rollup.config.js
cypress
src
test
serve.json
yarn-error.log
yarn.lock
9 changes: 4 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
language: node_js
cache:
yarn: true
directories:
- node_modules
- ~/.npm
- ~/.cache
node_js:
- '6'
- '8'
- '10'
- 'node'
install:
- yarn install --frozen-lockfile
script:
- yarn lint
- yarn test:browser:ci
- yarn test:coverage --no-cache
- yarn test:umd
after_success:
- 'coveralls < coverage/lcov.info'
94 changes: 67 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,37 +107,73 @@ search('someAccountOrEmail')

#### Using in the browser

There is a Universal Module Definition (UMD) build provided in the package
`dist` directory for usage in the browser. When using this build, an `hibp`
object will be added to the browser's `window` object.
**Prerequisite:** This module requires a Promise implementation to exist in the
global namespace prior to being loaded. Therefore, to facilitate usage in
[browsers without native Promise support][caniuse-promise], you are responsible
for providing a polyfill. I recommend [es6-promise][es6-promise].

The recommended way to include the UMD build (when using a `<script>` tag) is to
use the [unpkg][unpkg] CDN, specifying the exact version you want. If you don't
specify a version, the `latest` tag will be used, which could be dangerous
if/when there are breaking changes made to the API. See [unpkg][unpkg] for
details and advanced version specification, but generally you will want to do
the following (replacing `x.y.z` with the version you want):
You have several options for using this library in a browser environment:

```html
<script src="https://unpkg.com/hibp@x.y.z"></script>
```
1. Bundled

The most performant and recommended method is to bundle it with client-side
code using a module bundler like [webpack][webpack]. If your build process
honors the `module` field in `package.json`, you can import the ECMAScript
module as described [above](#usage). Otherwise, the `main` field resolves to
the CommonJS module version.

1. UMD

There is also a Universal Module Definition (UMD) build provided in the
package `dist` directory for usage in the browser. When using this build, an
`hibp` object will be added to the browser's `window` object.

The recommended way to include the UMD build (when using a `<script>` tag) is
to use the [unpkg][unpkg] CDN, specifying the exact version you want. If you
don't specify a version, the `latest` tag will be used, which could be
dangerous if/when there are breaking changes made to the API. See
[unpkg][unpkg] for details and advanced version specification, but generally
you will want to do the following (replacing `x.y.z` with the version you
want):

```html
<script src="https://unpkg.com/hibp@x.y.z"></script>
```

Development and production (minified) UMD builds are also provided for manual
download if desired:

- [https://unpkg.com/hibp/dist/hibp.js][cdn-umd-dev]
- [https://unpkg.com/hibp/dist/hibp.min.js][cdn-umd-prod]

1. ESM for Browsers

Modern browsers now [support][caniuse-esm] importing ECMAScript modules via
`<script type="module">` tags. Like the UMD option above, this build is also
available the [unpkg][unpkg] CDN (and the same versioning rules apply), but
you must specify the full path including the `.mjs` file extension. For
example:
```html
<script type="module">
import { dataClasses } from 'https://unpkg.com/hibp/dist/hibp.min.mjs@x.y.z';
const logDataClasses = async () => {
console.table(await dataClasses());
};
Development and production (minified) UMD builds are also provided for manual
download if desired:
logDataClasses();
</script>
```

- [https://unpkg.com/hibp/dist/hibp.js][cdn-dev]
- [https://unpkg.com/hibp/dist/hibp.min.js][cdn-prod]
Development and production (minified) ESM builds are also provided for manual
download if desired:

Alternatively, you may bundle it in with client-side code using a module bundler
like [webpack][webpack]. If your build process honors the `module` field in
`package.json`, you can import the ECMAScript module as described
[above](#usage). Otherwise, the `main` field resolves to the CommonJS module
version.
- [https://unpkg.com/hibp/dist/hibp.mjs][cdn-mjs-dev]
- [https://unpkg.com/hibp/dist/hibp.min.mjs][cdn-mjs-prod]

**N.B.** This module requires a Promise implementation to exist in the global
namespace prior to being loaded. Therefore, to facilitate usage in [browsers
without native Promise support][caniuse-promise], you are responsible for
providing a polyfill. I recommend [es6-promise][es6-promise].
For more information on ESM in the browser, check out [Using JavaScript
modules on the web][esm-primer].

## Try It Out

Expand Down Expand Up @@ -170,8 +206,12 @@ This module is distributed under the [MIT License][license].
[search-by-range]:
https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange
[unpkg]: https://unpkg.com
[cdn-dev]: https://unpkg.com/hibp/dist/hibp.js
[cdn-prod]: https://unpkg.com/hibp/dist/hibp.min.js
[cdn-umd-dev]: https://unpkg.com/hibp/dist/hibp.js
[cdn-umd-prod]: https://unpkg.com/hibp/dist/hibp.min.js
[caniuse-esm]: https://caniuse.com/#feat=es6-module
[cdn-mjs-dev]: https://unpkg.com/hibp/dist/hibp.mjs
[cdn-mjs-prod]: https://unpkg.com/hibp/dist/hibp.min.mjs
[esm-primer]: https://developers.google.com/web/fundamentals/primers/modules
[webpack]: https://webpack.js.org
[caniuse-promise]: https://caniuse.com/#search=promise
[es6-promise]: https://github.com/stefanpenner/es6-promise
Expand Down
4 changes: 4 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"baseUrl": "http://localhost:5000",
"video": false
}
9 changes: 9 additions & 0 deletions cypress/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
env: {
'cypress/globals': true,
},
plugins: ['cypress'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
};
30 changes: 30 additions & 0 deletions cypress/integration/browser.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
describe('UMD', () => {
it('exposes the hibp namespace on the window object', () => {
cy.visit('/test/umd.html');

cy.window().then(win => {
expect(win.hibp).to.be.an('object');
expect(win.hibp.breach).to.be.a('function');
expect(win.hibp.breach).to.be.a('function');
expect(win.hibp.breachedAccount).to.be.a('function');
expect(win.hibp.breaches).to.be.a('function');
expect(win.hibp.dataClasses).to.be.a('function');
expect(win.hibp.pasteAccount).to.be.a('function');
expect(win.hibp.pwnedPassword).to.be.a('function');
expect(win.hibp.pwnedPasswordRange).to.be.a('function');
expect(win.hibp.search).to.be.a('function');
});
});
});

describe('ESM for browsers', () => {
it('exports the hibp namespace', () => {
cy.visit('/test/esm.html');

// N.B. Some assertions are on the page itself (in the <script> block). If
// any of them fail, they will throw an error and prevent the final line of
// the script from setting the results message, causing the test to fail.

cy.get('#results').should('have.text', 'success');
});
});
17 changes: 17 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

module.exports = (/* on, config */) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
};
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
1 change: 1 addition & 0 deletions cypress/support/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cypress.Screenshot.defaults({ screenshotOnRunFailure: false });
22 changes: 22 additions & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

import './defaults';

// Import commands.js using ES2015 syntax:
import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ module.exports = {
coveragePathIgnorePatterns: ['<rootDir>/node_modules/', '/test/'],
coverageReporters: ['html', 'json', 'lcov', 'text'],
modulePaths: ['<rootDir>/src', '<rootDir>/src/__mocks__'],
testPathIgnorePatterns: ['/umd/'],
testPathIgnorePatterns: ['cypress'],
testEnvironment: 'node',
};
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,21 @@
"runkitExampleFilename": "example/runkit.js",
"sideEffects": false,
"scripts": {
"clean": "rimraf dist lib es coverage",
"build": "rollup --config",
"clean": "rimraf dist lib es coverage",
"cy:open": "cypress open",
"cy:run": "cypress run",
"docs": "jsdoc2md src/*.js > API.md",
"format": "prettier --write \"**/*.{js,json,md,yml}\"",
"lint": "eslint .",
"prebuild": "npm run clean",
"prepare": "npm run -s build",
"prepublishOnly": "npm run lint && npm run test && npm run build",
"start-test-server": "serve --no-clipboard",
"test": "cross-env NODE_ENV=test jest",
"test:coverage": "npm run test -- --coverage",
"test:umd": "npm run build && jest --forceExit --config \"{}\" src/__tests__/umd",
"test:browser": "npm run build && start-server-and-test start-test-server 5000 cy:open",
"test:browser:ci": "npm run build && start-server-and-test start-test-server 5000 cy:run",
"test:watch": "npm run test -- --watch"
},
"lint-staged": {
Expand Down Expand Up @@ -79,25 +83,28 @@
"common-tags": "1.8.0",
"coveralls": "3.0.2",
"cross-env": "5.2.0",
"cypress": "3.1.0",
"eslint": "5.7.0",
"eslint-config-airbnb-base": "13.1.0",
"eslint-config-prettier": "3.1.0",
"eslint-import-resolver-jest": "2.1.1",
"eslint-plugin-cypress": "2.0.1",
"eslint-plugin-import": "2.14.0",
"glob": "7.1.3",
"husky": "1.1.2",
"jest": "23.6.0",
"jsdoc-to-markdown": "4.0.1",
"lint-staged": "7.3.0",
"prettier": "1.14.3",
"puppeteer": "1.9.0",
"rimraf": "2.6.2",
"rollup": "0.66.6",
"rollup-plugin-babel": "4.0.3",
"rollup-plugin-commonjs": "9.2.0",
"rollup-plugin-json": "3.1.0",
"rollup-plugin-node-resolve": "3.4.0",
"rollup-plugin-replace": "2.1.0",
"rollup-plugin-terser": "3.0.0"
"rollup-plugin-terser": "3.0.0",
"serve": "10.0.2",
"start-server-and-test": "1.7.4"
}
}
Loading

0 comments on commit 1d31e46

Please sign in to comment.