Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking: Add ESM support #437

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
34 changes: 20 additions & 14 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@
"parserOptions": {
"ecmaVersion": 12
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"ignorePatterns": ["docs/assets/js/*", "dist/**", "docs/**"],
"ignorePatterns": ["docs/assets/js/*", "**/dist/**", "docs/**"],
"rules": {
"tsdoc/syntax": "warn",
"comma-dangle": 0,
Expand All @@ -29,27 +22,40 @@
"no-underscore-dangle": 0,
"class-methods-use-this": 0,
"prefer-destructuring": 0,
"import/extensions": 0,
"no-shadow": "off",
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
"import/no-extraneous-dependencies" :["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}]
"import/no-extraneous-dependencies" :["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}],
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parserOptions": {
"ecmaVersion": 2020, // Allows modern ECMAScript features
"sourceType": "module" // Allows for the use of imports
},
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"rules": {
"import/extensions": 0,
"@typescript-eslint/no-shadow": ["error"]
// "import/no-extraneous-dependencies" :["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}]
"@typescript-eslint/no-shadow": "error",
"import/no-unresolved": "error",
"import/no-extraneous-dependencies" :["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}]
},
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": { // to allow '.js' in the imports
"typescript": {}
}
},
"plugins": [
"eslint-plugin-tsdoc",
"@typescript-eslint"
"@typescript-eslint",
"import"
]
},
{
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,15 @@ jobs:
run: npm ci

- name: Copy dist for test server
run: cp ${{github.workspace}}/dist/mailgun.web.js ${{github.workspace}}/integration_tests/browser/server/dist/mailgun.web.js
run: cp -R ${{github.workspace}}/dist/* ${{github.workspace}}/tests/integration/browser/server/dist/

- name: Show folder for test server
run: ls -la ${{github.workspace}}/tests/integration/browser/server/dist/

- name: Serve Files
uses: Eun/http-server-action@v1
with:
directory: ${{ github.workspace}}/integration_tests/browser/server/
directory: ${{ github.workspace}}/tests/integration/browser/server/
port: 3000
no-cache: false
allowed-methods: |
Expand All @@ -66,6 +69,8 @@ jobs:
logTime: "false"

- run: curl -vvvv http://localhost:3000/pages/AMD.html && cat log.txt
- run: curl -vvvv http://localhost:3000/pages/ESM.html && cat log.txt
- run: curl -vvvv http://localhost:3000/pages/ESM-dynamic.html && cat log.txt

- name: Run Browser integration tests
run: npm run test:integration:browser
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ build
.nvmrc
**/.DS_Store

integration_tests/browser/server/dist/*
!integration_tests/browser/server/dist/.gitkeep
tests/integration/browser/server/dist/*
!tests/integration/browser/server/dist/.gitkeep

.rollup.cache/
9 changes: 0 additions & 9 deletions .nycrc.json

This file was deleted.

85 changes: 74 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,94 @@ npm install mailgun.js

## Setup Client

Next, require the module and instantiate a mailgun client by calling `new Mailgun(formData)` and then using `mailgun.client` setup the client with basic auth credentials `(username: 'api', key: 'key-yourkeyhere')`.
The next step is to import the module and instantiate a mailgun client by calling `new Mailgun(formData)` and then using `mailgun.client` setup the client with basic auth credentials `(username: 'api', key: 'key-yourkeyhere')`.

NOTE: starting from version 3.0 you need to pass FormData (we need this to keep library universal). For node.js you can use `form-data` library.
NOTE: starting from version 3.0 you need to pass FormData (we need this to keep library universal). For node.js you can use built-in FormData or `form-data` library.

IMPORTANT: if you are using EU infrastructure, you need to also pass `url: 'https://api.eu.mailgun.net'` together with auth credentials as stated in [Mailgun docs](https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-via-api)
**IMPORTANT**: if you are using EU infrastructure, you need to also pass `url: 'https://api.eu.mailgun.net'` together with auth credentials as stated in [Mailgun docs](https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-via-api)

### Imports
Once the package is installed, you can import the library using `import` or `require` approach:

```js
const formData = require('form-data');
const formData = require('form-data'); // or built-in FormData
const Mailgun = require('mailgun.js');
const mailgun = new Mailgun(formData);
const mg = mailgun.client({username: 'api', key: process.env.MAILGUN_API_KEY || 'key-yourkeyhere'});
```
```js
import FormData from 'form-data';
import FormData from 'form-data'; // or built-in FormData
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({username: 'api', key: process.env.MAILGUN_API_KEY || 'key-yourkeyhere'});
```

Be aware that there are four bundles available for usage. All of them are conditionally exported by package.json and separated by environment (Browser/Node.js):
- Node.js environment:
- CommonJS ([CJS](https://nodejs.org/api/modules.html#modules-commonjs-modules)), a bundle to use with the CommonJS module system in Node.js.

Usage example:

``` JS
// In this case, the .dist/CJS/mailgun.node.cjs file is expected to be used
const Mailgun = require('mailgun.js');
const mailgun = new Mailgun(FormData);
```

- ECMAScript modules ([ESM](https://nodejs.org/download/release/v18.11.0/docs/api/esm.html#modules-ecmascript-modules)) a bundle for the **Node.js** environment

Usage example:
``` JS
// In this case, the .dist/ESM/mailgun.node.js file is expected to be used
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(FormData);
...
```
or with dynamic imports:
``` JS
// In this case, the .dist/ESM/mailgun.node.js file is expected to be used
const Mailgun = await import('mailgun.js');
const mailgun = new Mailgun.default(FormData);
...
```

- Browser environment:
- Asynchronous Module Definition ([AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)), a bundle to use with the `Require.js` module loader in the browser. This bundle requires the RequireJS module loader to be present in the environment.

Usage example for the case when the distribution is used directly in the browser:
``` HTML
<script src='http://requirejs.org/docs/release/2.3.6/comments/require.js'></script>
<script>
require(['./dist/AMD/mailgun.amd.js'], function(Mailgun) {
const mailgun = new Mailgun(FormData);
...
})
</script>
```

- ECMAScript modules ([ESM](https://nodejs.org/download/release/v18.11.0/docs/api/esm.html#modules-ecmascript-modules)) a bundle for **browser** environment.

Usage example for the case the distribution is used directly in the browser:
``` HTML
<script type="module">
import Mailgun from './dist/ESM/mailgun.browser.js';
const mailgun = new Mailgun(FormData);
...
</script>
```
or with dynamic imports:
``` HTML
<script>
import ('./dist/ESM/mailgun.browser.js').then(Mailgun =>{
const mailgun = new Mailgun.default(FormData);
...
})
</script>
```
### Using Subaccounts
Primary accounts can make API calls on behalf of their subaccounts. [API documentation](https://documentation.mailgun.com/en/latest/subaccounts.html#subaccounts)
```js
import FormData from 'form-data';
import FormData from 'form-data'; // or built-in FormData
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({username: 'api', key: process.env.MAILGUN_API_KEY || 'key-yourkeyhere'});
Expand All @@ -75,7 +138,7 @@ Primary accounts can make API calls on behalf of their subaccounts. [API documen
By leveraging client configuration options, users can effortlessly establish proxy connections that align with their network requirements.
Ex:
```js
import FormData from 'form-data';
import FormData from 'form-data'; // or built-in FormData
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(FormData);

Expand All @@ -94,15 +157,15 @@ Ex:
});
```
### Types imports
Starting from version **9.0.0.** Types can be includes as named import:
Types defined by SDK can be imported from 'definitions' submodule:
```TS
import Mailgun, { MailgunClientOptions, MessagesSendResult } from 'mailgun.js';
import { MailgunClientOptions, MessagesSendResult } from 'mailgun.js/definitions';
```

### Interfaces and Enums imports
Starting from version **9.0.0.** Interfaces and Enums can be imported in the next way:
Interfaces and Enums defined by SDK can be imported from 'definitions' submodule:
```TS
import Mailgun, { Interfaces, Enums } from 'mailgun.js';
import { Interfaces, Enums } from 'mailgun.js/definitions';
...
const mailgunClient: Interfaces.IMailgunClient = mailgun.client(clientOptions);
const yes = Enums.YesNo.YES;
Expand Down
10 changes: 5 additions & 5 deletions SetupPackage.js → SetupPackage.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ function main() {
delete sourceObj.devDependencies;
delete sourceObj['standard-version'];

Object.entries(sourceObj).forEach(([key, value]) => {
if (typeof value === 'string' && value.startsWith('./dist/')) {
sourceObj[key] = sourceObj[key].replace('./dist/', './');
}
});
// Object.entries(sourceObj).forEach(([key, value]) => {
// if (typeof value === 'string' && value.startsWith('./dist/')) {
// sourceObj[key] = sourceObj[key].replace('./dist/', './');
// }
// });
fs.writeFileSync(path.join(__dirname, 'dist/package.json'), Buffer.from(JSON.stringify(sourceObj, null, 2), 'utf-8').toString());
fs.writeFileSync(path.join(__dirname, 'dist/version.md'), Buffer.from(sourceObj.version, 'utf-8').toString());

Expand Down
File renamed without changes.
48 changes: 48 additions & 0 deletions dist/AMD/definitions.amd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// mailgun.js v11.1.0 Copyright (c) 2025 Mailgun and contributors
define(['exports'], (function (exports) { 'use strict';

var Resolution;
(function (Resolution) {
Resolution["HOUR"] = "hour";
Resolution["DAY"] = "day";
Resolution["MONTH"] = "month";
})(Resolution || (Resolution = {}));
var SuppressionModels;
(function (SuppressionModels) {
SuppressionModels["BOUNCES"] = "bounces";
SuppressionModels["COMPLAINTS"] = "complaints";
SuppressionModels["UNSUBSCRIBES"] = "unsubscribes";
SuppressionModels["WHITELISTS"] = "whitelists";
})(SuppressionModels || (SuppressionModels = {}));
var WebhooksIds;
(function (WebhooksIds) {
WebhooksIds["CLICKED"] = "clicked";
WebhooksIds["COMPLAINED"] = "complained";
WebhooksIds["DELIVERED"] = "delivered";
WebhooksIds["OPENED"] = "opened";
WebhooksIds["PERMANENT_FAIL"] = "permanent_fail";
WebhooksIds["TEMPORARY_FAIL"] = "temporary_fail";
WebhooksIds["UNSUBSCRIBED"] = "unsubscribe";
})(WebhooksIds || (WebhooksIds = {}));
var YesNo;
(function (YesNo) {
YesNo["YES"] = "yes";
YesNo["NO"] = "no";
})(YesNo || (YesNo = {}));

var index$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
get Resolution () { return Resolution; },
get SuppressionModels () { return SuppressionModels; },
get WebhooksIds () { return WebhooksIds; },
get YesNo () { return YesNo; }
});

var index = /*#__PURE__*/Object.freeze({
__proto__: null
});

exports.Enums = index$1;
exports.Interfaces = index;

}));
Loading
Loading