Skip to content

Commit 7cd64dc

Browse files
christopher-stripedweedon-stripehofman-stripe
committed
Add TypeScript
Co-authored-by: David Weedon <dweedon@stripe.com> Co-authored-by: Mathieu Hofman <hofman@stripe.com>
1 parent 36c5e67 commit 7cd64dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5445
-136
lines changed

.eslintignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
dist/**/*.js
1+
dist
2+
node_modules
3+
examples
4+
tests/types

.eslintrc.yml

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---
2+
root: true
23
extends:
3-
- airbnb-base
4-
# overwrite any rules that may conflict with prettier
5-
- prettier
6-
- prettier/standard
7-
parser: 'babel-eslint'
4+
- eslint:recommended
5+
- plugin:@typescript-eslint/eslint-recommended
6+
- plugin:@typescript-eslint/recommended
7+
parser: '@typescript-eslint/parser'
88
plugins:
99
- jest
10+
- '@typescript-eslint'
1011
env:
1112
jest/globals: true
1213
browser: true
@@ -23,4 +24,5 @@ rules:
2324
- ignoreRestSiblings: true
2425
jest/no-disabled-tests: 2
2526
jest/no-focused-tests: 2
26-
import/prefer-default-export: 0
27+
'@typescript-eslint/no-explicit-any': 0
28+
'@typescript-eslint/no-empty-interface': 0

.travis.yml

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
language: node_js
2-
node_js:
3-
- '8'
4-
script:
5-
- npm run lint
6-
- npm run test
7-
- npm run prettier-list-different
2+
node_js: 'lts/*'
83
cache: yarn
4+
script:
5+
- yarn run typecheck
6+
- yarn run test
7+
- yarn run lint
8+
- yarn run build
9+
- yarn run test:versions
10+
- yarn run test:types

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ For more information on how to use Stripe.js once it loads, please refer to the
4242
[accept a payment](https://stripe.com/docs/payments/accept-a-payment) with
4343
Stripe.
4444

45+
## TypeScript support
46+
47+
This package includes TypeScript declarations for Stripe.js. We support projects
48+
using TypeScript versions >= 3.1.
49+
50+
Some methods in Stripe.js accept and return objects from the
51+
[Stripe API](https://stripe.com/docs/api). The type declarations in
52+
`@stripe/stripe-js` for these objects in will always track the
53+
[latest version](https://stripe.com/docs/api/versioning) of the Stripe API. If
54+
you would like to use these types but are using an older version of the Stripe
55+
API, we recommend
56+
[updating to the latest version](https://stripe.com/docs/upgrades#how-can-i-upgrade-my-api),
57+
or ignoring and overriding the type definitions as necessary.
58+
59+
Note that we may release new [minor and patch](https://semver.org/) versions of
60+
`@stripe/stripe-js` with small but backwards-incompatible fixes to the type
61+
declarations. These changes will not affect Stripe.js itself.
62+
4563
## Ensuring Stripe.js is available everywhere
4664

4765
To best leverage Stripe’s advanced fraud functionality, ensure that Stripe.js is

jest.config.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
// jest.config.js
21
module.exports = {
3-
modulePathIgnorePatterns: ['<rootDir>/dist', '<rootDir>/examples'],
2+
roots: ['<rootDir>/src'],
3+
testMatch: [
4+
'**/__tests__/**/*.+(ts|tsx|js)',
5+
'**/?(*.)+(spec|test).+(ts|tsx|js)',
6+
],
7+
transform: {
8+
'^.+\\.(ts|tsx)$': 'ts-jest',
9+
},
10+
globals: {
11+
// Suppress noise about enabling `esModuleInterop`
12+
'ts-jest': {diagnostics: {ignoreCodes: [151001]}},
13+
},
414
};

package.json

+19-10
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
"main": "dist/stripe.js",
66
"module": "dist/stripe.esm.js",
77
"jsnext:main": "dist/stripe.esm.js",
8+
"types": "types/index.d.ts",
89
"scripts": {
910
"test": "jest",
10-
"lint": "eslint src",
11-
"build": "yarn lint && yarn rollup -c",
11+
"test:versions": "./tests/versions/scripts/test.sh",
12+
"test:types": "tsc -p ./tests/types",
13+
"lint": "eslint '{src,types}/**/*.{ts,js}' && yarn prettier-list-different",
14+
"typecheck": "tsc",
15+
"build": "yarn clean && yarn rollup -c",
1216
"clean": "rimraf dist",
13-
"prettier": "prettier './**/*.js' './**/*.css' './**/*.md' --write",
14-
"prettier-list-different": "prettier './**/*.js' './**/*.css' './**/*.md' --list-different",
15-
"prepublish": "yarn run clean && yarn run build"
17+
"prettier": "prettier './**/*.{js,ts,md,html,css}' --write",
18+
"prettier-list-different": "prettier './**/*.{js,ts,md,html,css}' --list-different"
1619
},
1720
"keywords": [
1821
"Stripe",
@@ -24,23 +27,29 @@
2427
"homepage": "https://stripe.com/docs/js",
2528
"files": [
2629
"dist",
27-
"src"
30+
"src",
31+
"types"
2832
],
2933
"devDependencies": {
3034
"@babel/core": "^7.7.2",
3135
"@babel/preset-env": "^7.7.1",
36+
"@types/jest": "^24.0.25",
37+
"@typescript-eslint/eslint-plugin": "^2.15.0",
38+
"@typescript-eslint/parser": "^2.15.0",
3239
"babel-eslint": "^10.0.3",
3340
"babel-jest": "^24.9.0",
34-
"eslint": "6.6.0",
35-
"eslint-config-airbnb-base": "^14.0.0",
41+
"eslint": "^6.8.0",
3642
"eslint-config-prettier": "^6.8.0",
3743
"eslint-plugin-import": "^2.18.2",
3844
"eslint-plugin-jest": "^22.6.3",
3945
"eslint-plugin-prettier": "^3.1.1",
4046
"jest": "^24.9.0",
4147
"prettier": "^1.19.1",
4248
"rimraf": "^2.6.2",
43-
"rollup": "^1.27.0",
44-
"rollup-plugin-babel": "^4.3.3"
49+
"rollup": "^1.29.0",
50+
"rollup-plugin-babel": "^4.3.3",
51+
"rollup-plugin-typescript2": "^0.25.3",
52+
"ts-jest": "^24.3.0",
53+
"typescript": "^3.7.4"
4554
}
4655
}

rollup.config.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import babel from 'rollup-plugin-babel';
22
import pkg from './package.json';
3+
import ts from 'rollup-plugin-typescript2';
34

45
export default [
56
{
6-
input: 'src/index.js',
7+
input: 'src/index.ts',
78
output: [
89
{file: pkg.main, format: 'cjs'},
910
{file: pkg.module, format: 'es'},
1011
],
11-
plugins: [babel()],
12+
plugins: [
13+
ts({
14+
tsconfigOverride: {exclude: ['**/*.test.ts']},
15+
}),
16+
babel(),
17+
// TODO: terser
18+
],
1219
},
1320
];

src/index.js

-51
This file was deleted.

src/index.test.js renamed to src/index.test.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
/* eslint-disable global-require */
1+
/* eslint-disable @typescript-eslint/no-var-requires */
2+
3+
const dispatchScriptEvent = (eventType: string): void => {
4+
const script = document.querySelector(
5+
'script[src="https://js.stripe.com/v3"]'
6+
);
7+
8+
if (!script) {
9+
throw new Error('could not find Stripe.js script element');
10+
}
11+
12+
script.dispatchEvent(new Event(eventType));
13+
};
214

315
describe('Stripe module loader', () => {
416
afterEach(() => {
517
const script = document.querySelector(
618
'script[src="https://js.stripe.com/v3"]'
719
);
8-
if (script) {
20+
if (script && script.parentElement) {
921
script.parentElement.removeChild(script);
1022
}
1123
delete window.Stripe;
@@ -29,7 +41,7 @@ describe('Stripe module loader', () => {
2941
it('does not inject the script when Stripe is already loaded', () => {
3042
require('./index');
3143

32-
window.Stripe = jest.fn((key) => ({key}));
44+
window.Stripe = jest.fn((key) => ({key})) as any;
3345

3446
return new Promise((resolve) => setTimeout(resolve)).then(() => {
3547
expect(
@@ -58,10 +70,8 @@ describe('Stripe module loader', () => {
5870
const stripePromise = loadStripe('pk_test_foo');
5971

6072
return new Promise((resolve) => setTimeout(resolve)).then(() => {
61-
window.Stripe = jest.fn((key) => ({key}));
62-
document
63-
.querySelector('script[src="https://js.stripe.com/v3"]')
64-
.dispatchEvent(new Event('load'));
73+
window.Stripe = jest.fn((key) => ({key})) as any;
74+
dispatchScriptEvent('load');
6575

6676
return expect(stripePromise).resolves.toEqual({key: 'pk_test_foo'});
6777
});
@@ -72,9 +82,7 @@ describe('Stripe module loader', () => {
7282
const stripePromise = loadStripe('pk_test_foo');
7383

7484
return Promise.resolve().then(() => {
75-
document
76-
.querySelector('script[src="https://js.stripe.com/v3"]')
77-
.dispatchEvent(new Event('error'));
85+
dispatchScriptEvent('error');
7886

7987
return expect(stripePromise).rejects.toEqual(
8088
new Error('Failed to load Stripe.js')
@@ -86,9 +94,7 @@ describe('Stripe module loader', () => {
8694
const {loadStripe} = require('./index');
8795
const stripePromise = loadStripe('pk_test_foo');
8896
return Promise.resolve().then(() => {
89-
document
90-
.querySelector('script[src="https://js.stripe.com/v3"]')
91-
.dispatchEvent(new Event('load'));
97+
dispatchScriptEvent('load');
9298

9399
return expect(stripePromise).rejects.toEqual(
94100
new Error('Failed to load Stripe.js')

src/index.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
2+
///<reference path='../types/index.d.ts' />
3+
import {Stripe as StripeInstance, StripeConstructor} from '@stripe/stripe-js';
4+
5+
const V3_URL = 'https://js.stripe.com/v3';
6+
7+
const injectScript = (): HTMLScriptElement => {
8+
const script = document.createElement('script');
9+
script.src = V3_URL;
10+
11+
const headOrBody = document.head || document.body;
12+
13+
if (!headOrBody) {
14+
throw new Error(
15+
'Expected document.body not to be null. Stripe.js requires a <body> element.'
16+
);
17+
}
18+
19+
headOrBody.appendChild(script);
20+
21+
return script;
22+
};
23+
24+
// Execute our own script injection after a tick to give users time to
25+
// do their own script injection.
26+
const stripePromise: Promise<StripeConstructor | null> = Promise.resolve().then(
27+
() => {
28+
if (typeof window === 'undefined') {
29+
// Resolve to null when imported server side. This makes the module
30+
// safe to import in an isomorphic code base.
31+
return null;
32+
}
33+
34+
if (window.Stripe) {
35+
return window.Stripe;
36+
}
37+
38+
const script: HTMLScriptElement =
39+
document.querySelector(`script[src="${V3_URL}"]`) || injectScript();
40+
41+
return new Promise((resolve, reject) => {
42+
script.addEventListener('load', () => {
43+
if (window.Stripe) {
44+
resolve(window.Stripe);
45+
} else {
46+
reject(new Error('Failed to load Stripe.js'));
47+
}
48+
});
49+
50+
script.addEventListener('error', () => {
51+
reject(new Error('Failed to load Stripe.js'));
52+
});
53+
});
54+
}
55+
);
56+
57+
export const loadStripe = (
58+
...args: Parameters<StripeConstructor>
59+
): Promise<StripeInstance | null> =>
60+
stripePromise.then((maybeStripe) =>
61+
maybeStripe ? maybeStripe(...args) : null
62+
);

0 commit comments

Comments
 (0)