├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── HISTORY.md ├── LICENSE ├── README.md ├── __tests__ └── gatsby-node.test.js ├── examples ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-config.js ├── package.json └── src │ ├── components │ ├── header.js │ ├── layout.css │ └── layout.js │ ├── images │ └── gatsby-icon.png │ └── pages │ └── index.js ├── gatsby-node.js ├── img ├── mailchimp_form_action.png ├── mailchimp_list.png ├── mailchimp_list_fields.png └── mc_groups.png ├── index.js ├── jest.config.js ├── package.json ├── src ├── gatsby-node.js └── index.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editorconfig: https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = tab 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | indent_style = space 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /index.js 2 | /gatsby-node.js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true, 6 | "jest": true 7 | }, 8 | "extends": ["airbnb-base", "plugin:react/recommended", "prettier"], 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parser": "babel-eslint", 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "ecmaVersion": 2018, 19 | "sourceType": "module" 20 | }, 21 | "plugins": ["react", "prettier"], 22 | "rules": { 23 | "prettier/prettier": "error", 24 | "no-unused-vars": [ 25 | "error", 26 | { 27 | "vars": "local", 28 | "args": "none" 29 | } 30 | ], 31 | "no-plusplus": "off", 32 | "no-underscore-dangle": "off", 33 | "no-restricted-syntax": ["off", "BinaryExpression[operator='in']"] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | yarn-error.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 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 | # Other 26 | *.un~ 27 | .babelrc 28 | .gitignore 29 | .npmignore 30 | CHANGELOG.md 31 | LICENSE 32 | README.md 33 | coverage 34 | decls 35 | examples 36 | flow-typed 37 | node_modules 38 | src 39 | yarn.lock -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 4, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "proseWrap": "always" 11 | } 12 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## Version History 2 | 3 | **5.2.0** 4 | 5 | - Make timeout configurable (#68) 6 | 7 | **5.1.0** 8 | 9 | - Adds ability to override default Mailchimp endpoint with a custom one 10 | 11 | **5.0.1** 12 | 13 | - Adds eslint and prettier 14 | 15 | **5.0.0** 16 | 17 | - Adds support for Mailchimp Groups. See README.md for more details on how to implement. 18 | 19 | **4.0.0** 20 | 21 | - Gatsby v2 is now default. See README.md for upgrade details. 22 | 23 | **3.0.0** 24 | 25 | - uses `gatsby-node` and [Webpack DefinePlugin](https://webpack.js.org/plugins/define-plugin/) to 26 | extract your Mailchimp API key during _compile time only_, set it to global, then use it to make 27 | the http request. Previously, we were importing your entire `gatsby-config` file 28 | 29 | **2.0.0** 30 | 31 | - return a promise, not string, from an error'd http request 32 | 33 | ## To do 34 | 35 | - ensure MC endpoint is valid 36 | - create basic MC field form (name, email, submit button) 37 | - spec 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Benjamin Hoffman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 😞 No longer actively maintained 😞 2 | _My sincerest apologies but this repo is no longer maintained. You can still use it and most people have reported that it works great, but I am no longer adding features or fixing bugs. Please use at your own risk and keep in mind it likely won't handle corner cases or your specific use case (it's hopefully fine for generic use cases)._ -- Ben (Dec 21, 2021) 3 | 4 | ## 🗣 Actively looking for repo maintainers! 📣 5 | I no longer have time to manage this package and would love if someone could help me maintain it. Please open an issue if you're interested and we'll chat! 🙏🏽 6 | 7 | --- 8 | 9 | # Subscribe emails to your Mailchimp list 10 | 11 | This Gatsby plugin helps you subscribe new email addresses to a Mailchimp email list. Mailchimp does 12 | not provide much direction on making clientside requests so the setup to achieve this with a static 13 | website (i.e. Gatsby) can be cumbersome. 14 | 15 | ## We need your help! 🙏🏽 16 | We're looking for active contributors to this repo! If you're interested, simply open an issue or PR of your own and indicate that you'd like to help. Check out our open issues and PRs. We also need to beef up testing. Contributors get to: 17 | - manage versioning and deploys by publishing new versions to NPM 18 | - determine which features get launched by merging pull requests 19 | - oversee the community through commenting on and managing pull requests and issues 20 | 21 | ⚜️ Being an active contributor is great for the community and your engineering resume.⚜️ 22 | 23 | ## How It Works Under The Hood 24 | 25 | First we scan your `gatsby-config` for your MC endpoint. Then, once you import and invoke the 26 | `addToMailchimp` method in your React component, it makes a jsonp request to your endpoint with the 27 | email, attributes, and any group fields you include in the request. 28 | 29 | ## Getting Started 30 | 31 | There are three steps involved to getting started: 32 | 33 | 1. add this plugin to your repo 34 | 35 | - In the root directory of your Gatsby project, run the following command in your terminal: 36 | 37 | ``` 38 | # npm 39 | $ npm install gatsby-plugin-mailchimp 40 | 41 | # yarn 42 | $ yarn add gatsby-plugin-mailchimp 43 | ``` 44 | 45 | 2. add your Mailchimp endpoint to your `gatsby-config.js` file (see below for instructions) 46 | 3. import & invoke the `addToMailchimp` method exported by this plugin 47 | 48 | ## Using Gatsby v1? 49 | 50 | If you are still on Gatsby v1.x, you need to use an old version of this plugin. There were a lot of 51 | changes made in Gatsby v2 that will cause this plugin to break so make sure to use the correct 52 | version of this plugin if you are still on Gatsby v1. 53 | 54 | We no longer maintain this version. 55 | 56 | Simply update your `package.json` to: 57 | 58 | ``` 59 | # npm 60 | $ npm install https://github.com/benjaminhoffman/gatsby-plugin-mailchimp.git#gatsby-v1 61 | 62 | # yarn 63 | $ yarn add https://github.com/benjaminhoffman/gatsby-plugin-mailchimp.git#gatsby-v1 64 | ``` 65 | 66 | ## Gatsby Config Instructions 67 | 68 | You need to provide this plugin with your Mailchimp account and list details in order for it to know 69 | which endpoint to save the email address to. Follow these directions: 70 | 71 | In your `gatsby-config.js` file, add the following code to the plugin section: 72 | 73 | ```javascript 74 | plugins: [ 75 | ...{ 76 | resolve: 'gatsby-plugin-mailchimp', 77 | options: { 78 | endpoint: '', // string; add your MC list endpoint here; see instructions below 79 | timeout: 3500, // number; the amount of time, in milliseconds, that you want to allow mailchimp to respond to your request before timing out. defaults to 3500 80 | }, 81 | }, 82 | ]; 83 | ``` 84 | 85 | ### Mailchimp Endpoint 86 | 87 | Your Mailchimp endpoint will look something like this: 88 | _https://example.us10.list-manage.com/subscribe/post?u=b9ef2fdd3edofhec04ba9b930&id=3l948gkt1d_ 89 | 90 | Here is how you can locate your Mailchimp endpoint. 91 | 92 | 1. Login to your Mailchimp account 93 | 2. Click "Lists" tab at the top 94 | 3. Locate the Mailchimp list you want to save email addresses to 95 | 4. Click that list 96 | 5. Click the subtab "Signup forms" 97 | 6. Click "Embedded forms" 98 | 99 | ![screenshot of how to locate your Mailchimp `u` settings](https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/master/img/mailchimp_list.png) 100 | 101 | 7. Scroll down to the section with all the HTML code 102 | 8. Locate the HTML form element. Copy the entire URL listed under the form "action" attribute\* 103 | 9. Paste that URL into your `gatsby-config`ʼs `option.endpoint` field 104 | 105 | ![screenshot of how to copy/paste your list settings URL](https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/master/img/mailchimp_form_action.png) 106 | 107 | ... that's all! 108 | 109 | ## Gatsby Import Plugin Instructions 110 | 111 | This plugin exports one method -- `addToMailchimp` -- that accepts one required argument (`email`) 112 | and two optional fields (`fields` and `endpointOverride`). 113 | 114 | - `email` is a valid email string 115 | - `fields` is an object of attributes youʼd like to save with the email address. More detailed 116 | instructions below. 117 | - `endpointOverride` is if you want to pass in a custom MC endpoint (one that is different than 118 | the one listed in your config file. See below for details) 119 | 120 | Navigate to the file where you collect email addresses (ie, the file you want to import this plugin 121 | into). When a user submits a form and includes at least their email address, invoke the 122 | `addToMailchimp` method like you would any other method. Here is an example: 123 | 124 | ```javascript 125 | import addToMailchimp from 'gatsby-plugin-mailchimp' 126 | ... 127 | 128 | export default class MyGatsbyComponent extends React.Component { 129 | // Since `addToMailchimp` returns a promise, you 130 | // can handle the response in two different ways: 131 | 132 | // Note that you need to send an email & optionally, listFields 133 | // these values can be pulled from React state, form fields, 134 | // or wherever. (Personally, I recommend storing in state). 135 | 136 | // 1. via `.then` 137 | _handleSubmit = e => { 138 | e.preventDefault(); 139 | addToMailchimp(email, listFields) // listFields are optional if you are only capturing the email address. 140 | .then(data => { 141 | // I recommend setting data to React state 142 | // but you can do whatever you want (including ignoring this `then()` altogether) 143 | console.log(data) 144 | }) 145 | .catch(() => { 146 | // unnecessary because Mailchimp only ever 147 | // returns a 200 status code 148 | // see below for how to handle errors 149 | }) 150 | } 151 | 152 | // 2. via `async/await` 153 | _handleSubmit = async (e) => { 154 | e.preventDefault(); 155 | const result = await addToMailchimp(email, listFields) 156 | // I recommend setting `result` to React state 157 | // but you can do whatever you want 158 | } 159 | 160 | render () { 161 | return ( 162 |
163 | ... 164 |
165 | ) 166 | } 167 | } 168 | ``` 169 | 170 | ## Returns 171 | 172 | This plugin returns a promise that resolves to the object that is returned by Mailchimpʼs API. The 173 | Mailchimp API will always return a status of 200. In order to know if your submission was a success 174 | or error, you must read the returned object, which has a `result` and `msg` property: 175 | 176 | ```javascript 177 | { 178 | result: string; // either `success` or `error` (helpful to use this key to update your state) 179 | msg: string; // a user-friendly message indicating details of your submissions (usually something like "thanks for subscribing!" or "this email has already been added") 180 | } 181 | ``` 182 | 183 | ## Mailchimp List Fields 184 | 185 | Sometimes you want to send to Mailchimp more than just an email address. Itʼs very common to also 186 | send a first name, last name, pathname, etc. Honestly, you can send whatever you want to store 187 | alongside the email address. Instructions below on how to create new list fields but once youʼve set 188 | them up in Mailchimp, you send them alongside the email like this: 189 | 190 | ```javascript 191 | addToMailchimp('email@example.com', { 192 | PATHNAME: '/blog-post-1', 193 | FNAME: 'Ben', 194 | LNAME: 'Coder' 195 | ... 196 | }) 197 | ``` 198 | 199 | ## Mailchimp Groups 200 | 201 | Mailchimp offers the concept of list groups. It's a bit tricky to implement here because you _must_ 202 | use the exact key and value as defined in your MC Embedded Form for those fields. 203 | 204 | To add these you must go back to your "Embedded Forms" (where you got your endpoint from) and find 205 | the form field that represents the group you want to add this user to. Next, copy the name and use 206 | that as your key in the `addToMailchimp` field param. The name field will have weird values like 207 | `group[21265][2]` or `group[21269]`. 208 | 209 | Similarly, the `input` field `value` must also be the same as you see. This means you must either 210 | set the name and value fields manually in your form or keep a mapping in your JS file. 211 | 212 | Why do we need to use these weird structure for name and value field? Because this is what Mailchimp 213 | expects. 🤷🏽‍♂️ 214 | 215 | For example, here is a screenshot of what this looks like: 216 | 217 | ![screenshot of Mailchimp Groups](https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/master/img/mc_groups.png) 218 | 219 | And the code would be: 220 | 221 | ``` 222 | # HTML 223 | /* 224 | Here we chose to name the input field the same as what's in 225 | our embedded form. But you can name it whatever you want and keep 226 | a field name map in your JS. Mailchimp expects the name and value 227 | to match what's in its Embedded Form 228 | */ 229 | 230 | 231 | # JS 232 | addToMailchimp('email@example.com', { 233 | PATHNAME: '/blog-post-1', 234 | FNAME: 'Ben', 235 | LNAME: 'Coder', 236 | 'group[21265][2]': '2', 237 | ... 238 | }) 239 | ``` 240 | 241 | See here for [thread](https://github.com/benjaminhoffman/gatsby-plugin-mailchimp/pull/31). 242 | 243 | ## Multiple Mailchimp lists 244 | 245 | Many people asked for the ability to send users to different Mailchimp lists. We added that 246 | capability! How we added this capability without a breaking change is by allowing you to _override_ 247 | the endpoint that's listed in your `gatsby-config.js` file. When you invoke `addToMailchimp`, pass 248 | in a third argument of the list you'd like to subscribe this email address to. That will override 249 | the default one listed in your config. 250 | 251 | ```javascript 252 | addToMailchimp( 253 | 'ben@gatsby-plugin-mailchimp.com', 254 | { 255 | /* list fields here*/ 256 | }, 257 | 'https://example.us10.list-manage.com/subscribe/post?u=b9ef2fdd3ed', 258 | ); 259 | ``` 260 | 261 | ## Example 262 | 263 | See directory in this repo called `/examples`. 264 | 265 | ## Gotchas 266 | 267 | 1. _email address_: pass in the email as normal (ie, `you@gmail.com`). Do _not_ encode or transform 268 | the email, as our plugin will do that for you! 269 | 270 | 2. _listFields_: many times you want to collect more than just an email address (first/last name, 271 | birthday, page pathname). I like to store this info in React state and pass it in as list fields. 272 | See below. 273 | 274 | 3. I like to save the returned data to React state so I can then display a success/error message to 275 | the user. 276 | 277 | 4. There is a 278 | [current known issue (#15)](https://github.com/benjaminhoffman/gatsby-plugin-mailchimp/issues/15) 279 | where this plugin does _not work_ if the Mailchimp List has 280 | [reCAPTCHA enabled](https://mailchimp.com/help/about-recaptcha-for-signup-forms/#Enable-reCAPTCHA/). 281 | This setting should be turned off for everything to function properly. 282 | 283 | ### Create, Remove, or Edit Mailchimp List Fields 284 | 285 | To setup or modify Mailchimp list fields, navigate to your MC list, click "Settings", then click 286 | "List fields". Then add, remove, or edit fields as you wish. Make sure to update your 287 | `addToMailchimp` listFields object after youʼve made changes in Mailchimp. 288 | 289 | ![screenshot of Mailchimp list fields settings screen](https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/master/img/mailchimp_list_fields.png) 290 | 291 | ## Contributing 292 | 293 | If you'd like to contribute, simply open a PR or open an issue! 294 | -------------------------------------------------------------------------------- /__tests__/gatsby-node.test.js: -------------------------------------------------------------------------------- 1 | const { onCreateWebpackConfig } = require('../gatsby-node'); 2 | 3 | describe('gatsby-node > onCreateWebpackConfig', () => { 4 | const plugins = {}; 5 | const actions = {}; 6 | 7 | beforeEach(() => { 8 | plugins.define = jest.fn(); 9 | actions.setWebpackConfig = jest.fn(); 10 | }); 11 | 12 | it('Throws an error when endpoint is not a string', () => { 13 | const endpoint = 123; 14 | expect(() => onCreateWebpackConfig({ plugins, actions }, { endpoint })).toThrow(Error); 15 | expect(actions.setWebpackConfig).not.toHaveBeenCalled(); 16 | expect(plugins.define).not.toHaveBeenCalled(); 17 | }); 18 | 19 | it('Throws an error when endpoint is length is lesser than 40 chars', () => { 20 | const endpoint = 'https://my-endpoint'; 21 | expect(() => onCreateWebpackConfig({ plugins, actions }, { endpoint })).toThrow(Error); 22 | expect(actions.setWebpackConfig).not.toHaveBeenCalled(); 23 | expect(plugins.define).not.toHaveBeenCalled(); 24 | }); 25 | 26 | describe('Options -> calls set setWebpackConfig and define variables correctly', () => { 27 | it('with default values', () => { 28 | const endpoint = `https://my-endpoint-with-more-than-40-chars.com`; 29 | 30 | expect(actions.setWebpackConfig).not.toHaveBeenCalled(); 31 | expect(plugins.define).not.toHaveBeenCalled(); 32 | 33 | onCreateWebpackConfig({ plugins, actions }, { endpoint }); 34 | 35 | expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); 36 | expect(plugins.define).toHaveBeenCalledTimes(1); 37 | expect(plugins.define).toHaveBeenCalledWith( 38 | expect.objectContaining({ 39 | __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__: expect.any(String), 40 | __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__: 3500, 41 | }), 42 | ); 43 | }); 44 | 45 | it('with timeout value', () => { 46 | const endpoint = `https://my-endpoint-with-more-than-40-chars.com`; 47 | const timeout = 10000; 48 | 49 | expect(actions.setWebpackConfig).not.toHaveBeenCalled(); 50 | expect(plugins.define).not.toHaveBeenCalled(); 51 | 52 | onCreateWebpackConfig({ plugins, actions }, { endpoint, timeout }); 53 | 54 | expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); 55 | expect(plugins.define).toHaveBeenCalledTimes(1); 56 | expect(plugins.define).toHaveBeenCalledWith( 57 | expect.objectContaining({ 58 | __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__: expect.any(String), 59 | __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__: timeout, 60 | }), 61 | ); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | .cache 3 | node_modules 4 | yarn-error.log 5 | 6 | # don't need this; allow for user to fresh install packages 7 | yarn.lock 8 | 9 | # Build directory 10 | /public 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /examples/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 |

2 | Gatsby 3 |

4 | 5 | ## Example of `gatsby-plugin-mailchimp` for Gatsby v2. 6 | 7 | 8 | This example repo combines `gatsby-starter-default` with `gatsby-plugin-mailchimp` for Gatsby **v2**. It's nothing special -- I didn't change anything. It's mostly used for you to see how to use this plugin. 9 | 10 | See something missing from this example? Let me know and I will update it! ... or even better, open your own PR and I'll merge it! 11 | 12 | You can follow the instructions below and submit the form to see the results. 13 | 14 | ## Steps 15 | 1. clone down this repo 16 | 2. add your mailchimp endpoint to `gatsby-config` 17 | 3. `$ yarn install` 18 | 4. `$ yarn develop` 19 | 5. go to `localhost:8000/` in your browser and open your the console 20 | 6. submit a form. see the page & form state get logged to the screen 21 | -------------------------------------------------------------------------------- /examples/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: 'Gatsby Default Starter', 4 | }, 5 | plugins: [ 6 | 'gatsby-plugin-react-helmet', 7 | { 8 | resolve: 'gatsby-plugin-mailchimp', 9 | options: { 10 | endpoint: '', // replace with your own Mailchimp endpoint for testing 11 | }, 12 | }, 13 | ], 14 | } 15 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-default", 3 | "description": "Gatsby default starter", 4 | "version": "1.0.0", 5 | "author": "Kyle Mathews ", 6 | "dependencies": { 7 | "gatsby": "next", 8 | "gatsby-plugin-mailchimp": "^5.1.2", 9 | "gatsby-plugin-react-helmet": "^3.0.7", 10 | "react": "^16.4.2", 11 | "react-dom": "^16.4.2", 12 | "react-helmet": "^5.2.0" 13 | }, 14 | "keywords": [ 15 | "gatsby" 16 | ], 17 | "license": "MIT", 18 | "scripts": { 19 | "build": "gatsby build", 20 | "develop": "gatsby develop", 21 | "serve": "gatsby serve", 22 | "format": "prettier --write '**/*.js'", 23 | "test": "echo \"Error: no test specified\" && exit 1" 24 | }, 25 | "devDependencies": { 26 | "prettier": "^1.14.2" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/gatsbyjs/gatsby-starter-default" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/src/components/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | const Header = () => ( 5 |
11 |
18 |

19 | 26 | Example usage of gatsby-plugin-mailchimp 27 | 28 |

29 |
30 |
31 | ) 32 | 33 | export default Header 34 | -------------------------------------------------------------------------------- /examples/src/components/layout.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | } 9 | article, 10 | aside, 11 | details, 12 | figcaption, 13 | figure, 14 | footer, 15 | header, 16 | main, 17 | menu, 18 | nav, 19 | section, 20 | summary { 21 | display: block; 22 | } 23 | audio, 24 | canvas, 25 | progress, 26 | video { 27 | display: inline-block; 28 | } 29 | audio:not([controls]) { 30 | display: none; 31 | height: 0; 32 | } 33 | progress { 34 | vertical-align: baseline; 35 | } 36 | [hidden], 37 | template { 38 | display: none; 39 | } 40 | a { 41 | background-color: transparent; 42 | -webkit-text-decoration-skip: objects; 43 | } 44 | a:active, 45 | a:hover { 46 | outline-width: 0; 47 | } 48 | abbr[title] { 49 | border-bottom: none; 50 | text-decoration: underline; 51 | text-decoration: underline dotted; 52 | } 53 | b, 54 | strong { 55 | font-weight: inherit; 56 | font-weight: bolder; 57 | } 58 | dfn { 59 | font-style: italic; 60 | } 61 | h1 { 62 | font-size: 2em; 63 | margin: .67em 0; 64 | } 65 | mark { 66 | background-color: #ff0; 67 | color: #000; 68 | } 69 | small { 70 | font-size: 80%; 71 | } 72 | sub, 73 | sup { 74 | font-size: 75%; 75 | line-height: 0; 76 | position: relative; 77 | vertical-align: baseline; 78 | } 79 | sub { 80 | bottom: -.25em; 81 | } 82 | sup { 83 | top: -.5em; 84 | } 85 | img { 86 | border-style: none; 87 | } 88 | svg:not(:root) { 89 | overflow: hidden; 90 | } 91 | code, 92 | kbd, 93 | pre, 94 | samp { 95 | font-family: monospace, monospace; 96 | font-size: 1em; 97 | } 98 | figure { 99 | margin: 1em 40px; 100 | } 101 | hr { 102 | box-sizing: content-box; 103 | height: 0; 104 | overflow: visible; 105 | } 106 | button, 107 | input, 108 | optgroup, 109 | select, 110 | textarea { 111 | font: inherit; 112 | margin: 0; 113 | } 114 | optgroup { 115 | font-weight: 700; 116 | } 117 | button, 118 | input { 119 | overflow: visible; 120 | } 121 | button, 122 | select { 123 | text-transform: none; 124 | } 125 | [type=reset], 126 | [type=submit], 127 | button, 128 | html [type=button] { 129 | -webkit-appearance: button; 130 | } 131 | [type=button]::-moz-focus-inner, 132 | [type=reset]::-moz-focus-inner, 133 | [type=submit]::-moz-focus-inner, 134 | button::-moz-focus-inner { 135 | border-style: none; 136 | padding: 0; 137 | } 138 | [type=button]:-moz-focusring, 139 | [type=reset]:-moz-focusring, 140 | [type=submit]:-moz-focusring, 141 | button:-moz-focusring { 142 | outline: 1px dotted ButtonText; 143 | } 144 | fieldset { 145 | border: 1px solid silver; 146 | margin: 0 2px; 147 | padding: .35em .625em .75em; 148 | } 149 | legend { 150 | box-sizing: border-box; 151 | color: inherit; 152 | display: table; 153 | max-width: 100%; 154 | padding: 0; 155 | white-space: normal; 156 | } 157 | textarea { 158 | overflow: auto; 159 | } 160 | [type=checkbox], 161 | [type=radio] { 162 | box-sizing: border-box; 163 | padding: 0; 164 | } 165 | [type=number]::-webkit-inner-spin-button, 166 | [type=number]::-webkit-outer-spin-button { 167 | height: auto; 168 | } 169 | [type=search] { 170 | -webkit-appearance: textfield; 171 | outline-offset: -2px; 172 | } 173 | [type=search]::-webkit-search-cancel-button, 174 | [type=search]::-webkit-search-decoration { 175 | -webkit-appearance: none; 176 | } 177 | ::-webkit-input-placeholder { 178 | color: inherit; 179 | opacity: .54; 180 | } 181 | ::-webkit-file-upload-button { 182 | -webkit-appearance: button; 183 | font: inherit; 184 | } 185 | html { 186 | font: 112.5%/1.45em georgia, serif; 187 | box-sizing: border-box; 188 | overflow-y: scroll; 189 | } 190 | * { 191 | box-sizing: inherit; 192 | } 193 | *:before { 194 | box-sizing: inherit; 195 | } 196 | *:after { 197 | box-sizing: inherit; 198 | } 199 | body { 200 | color: hsla(0, 0%, 0%, 0.8); 201 | font-family: georgia, serif; 202 | font-weight: normal; 203 | word-wrap: break-word; 204 | font-kerning: normal; 205 | -moz-font-feature-settings: "kern", "liga", "clig", "calt"; 206 | -ms-font-feature-settings: "kern", "liga", "clig", "calt"; 207 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt"; 208 | font-feature-settings: "kern", "liga", "clig", "calt"; 209 | } 210 | img { 211 | max-width: 100%; 212 | margin-left: 0; 213 | margin-right: 0; 214 | margin-top: 0; 215 | padding-bottom: 0; 216 | padding-left: 0; 217 | padding-right: 0; 218 | padding-top: 0; 219 | margin-bottom: 1.45rem; 220 | } 221 | h1 { 222 | margin-left: 0; 223 | margin-right: 0; 224 | margin-top: 0; 225 | padding-bottom: 0; 226 | padding-left: 0; 227 | padding-right: 0; 228 | padding-top: 0; 229 | margin-bottom: 1.45rem; 230 | color: inherit; 231 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 232 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 233 | font-weight: bold; 234 | text-rendering: optimizeLegibility; 235 | font-size: 2.25rem; 236 | line-height: 1.1; 237 | } 238 | h2 { 239 | margin-left: 0; 240 | margin-right: 0; 241 | margin-top: 0; 242 | padding-bottom: 0; 243 | padding-left: 0; 244 | padding-right: 0; 245 | padding-top: 0; 246 | margin-bottom: 1.45rem; 247 | color: inherit; 248 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 249 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 250 | font-weight: bold; 251 | text-rendering: optimizeLegibility; 252 | font-size: 1.62671rem; 253 | line-height: 1.1; 254 | } 255 | h3 { 256 | margin-left: 0; 257 | margin-right: 0; 258 | margin-top: 0; 259 | padding-bottom: 0; 260 | padding-left: 0; 261 | padding-right: 0; 262 | padding-top: 0; 263 | margin-bottom: 1.45rem; 264 | color: inherit; 265 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 266 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 267 | font-weight: bold; 268 | text-rendering: optimizeLegibility; 269 | font-size: 1.38316rem; 270 | line-height: 1.1; 271 | } 272 | h4 { 273 | margin-left: 0; 274 | margin-right: 0; 275 | margin-top: 0; 276 | padding-bottom: 0; 277 | padding-left: 0; 278 | padding-right: 0; 279 | padding-top: 0; 280 | margin-bottom: 1.45rem; 281 | color: inherit; 282 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 283 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 284 | font-weight: bold; 285 | text-rendering: optimizeLegibility; 286 | font-size: 1rem; 287 | line-height: 1.1; 288 | } 289 | h5 { 290 | margin-left: 0; 291 | margin-right: 0; 292 | margin-top: 0; 293 | padding-bottom: 0; 294 | padding-left: 0; 295 | padding-right: 0; 296 | padding-top: 0; 297 | margin-bottom: 1.45rem; 298 | color: inherit; 299 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 300 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 301 | font-weight: bold; 302 | text-rendering: optimizeLegibility; 303 | font-size: 0.85028rem; 304 | line-height: 1.1; 305 | } 306 | h6 { 307 | margin-left: 0; 308 | margin-right: 0; 309 | margin-top: 0; 310 | padding-bottom: 0; 311 | padding-left: 0; 312 | padding-right: 0; 313 | padding-top: 0; 314 | margin-bottom: 1.45rem; 315 | color: inherit; 316 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 317 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 318 | font-weight: bold; 319 | text-rendering: optimizeLegibility; 320 | font-size: 0.78405rem; 321 | line-height: 1.1; 322 | } 323 | hgroup { 324 | margin-left: 0; 325 | margin-right: 0; 326 | margin-top: 0; 327 | padding-bottom: 0; 328 | padding-left: 0; 329 | padding-right: 0; 330 | padding-top: 0; 331 | margin-bottom: 1.45rem; 332 | } 333 | ul { 334 | margin-left: 1.45rem; 335 | margin-right: 0; 336 | margin-top: 0; 337 | padding-bottom: 0; 338 | padding-left: 0; 339 | padding-right: 0; 340 | padding-top: 0; 341 | margin-bottom: 1.45rem; 342 | list-style-position: outside; 343 | list-style-image: none; 344 | } 345 | ol { 346 | margin-left: 1.45rem; 347 | margin-right: 0; 348 | margin-top: 0; 349 | padding-bottom: 0; 350 | padding-left: 0; 351 | padding-right: 0; 352 | padding-top: 0; 353 | margin-bottom: 1.45rem; 354 | list-style-position: outside; 355 | list-style-image: none; 356 | } 357 | dl { 358 | margin-left: 0; 359 | margin-right: 0; 360 | margin-top: 0; 361 | padding-bottom: 0; 362 | padding-left: 0; 363 | padding-right: 0; 364 | padding-top: 0; 365 | margin-bottom: 1.45rem; 366 | } 367 | dd { 368 | margin-left: 0; 369 | margin-right: 0; 370 | margin-top: 0; 371 | padding-bottom: 0; 372 | padding-left: 0; 373 | padding-right: 0; 374 | padding-top: 0; 375 | margin-bottom: 1.45rem; 376 | } 377 | p { 378 | margin-left: 0; 379 | margin-right: 0; 380 | margin-top: 0; 381 | padding-bottom: 0; 382 | padding-left: 0; 383 | padding-right: 0; 384 | padding-top: 0; 385 | margin-bottom: 1.45rem; 386 | } 387 | figure { 388 | margin-left: 0; 389 | margin-right: 0; 390 | margin-top: 0; 391 | padding-bottom: 0; 392 | padding-left: 0; 393 | padding-right: 0; 394 | padding-top: 0; 395 | margin-bottom: 1.45rem; 396 | } 397 | pre { 398 | margin-left: 0; 399 | margin-right: 0; 400 | margin-top: 0; 401 | padding-bottom: 0; 402 | padding-left: 0; 403 | padding-right: 0; 404 | padding-top: 0; 405 | margin-bottom: 1.45rem; 406 | font-size: 0.85rem; 407 | line-height: 1.42; 408 | background: hsla(0, 0%, 0%, 0.04); 409 | border-radius: 3px; 410 | overflow: auto; 411 | word-wrap: normal; 412 | padding: 1.45rem; 413 | } 414 | table { 415 | margin-left: 0; 416 | margin-right: 0; 417 | margin-top: 0; 418 | padding-bottom: 0; 419 | padding-left: 0; 420 | padding-right: 0; 421 | padding-top: 0; 422 | margin-bottom: 1.45rem; 423 | font-size: 1rem; 424 | line-height: 1.45rem; 425 | border-collapse: collapse; 426 | width: 100%; 427 | } 428 | fieldset { 429 | margin-left: 0; 430 | margin-right: 0; 431 | margin-top: 0; 432 | padding-bottom: 0; 433 | padding-left: 0; 434 | padding-right: 0; 435 | padding-top: 0; 436 | margin-bottom: 1.45rem; 437 | } 438 | blockquote { 439 | margin-left: 1.45rem; 440 | margin-right: 1.45rem; 441 | margin-top: 0; 442 | padding-bottom: 0; 443 | padding-left: 0; 444 | padding-right: 0; 445 | padding-top: 0; 446 | margin-bottom: 1.45rem; 447 | } 448 | form { 449 | margin-left: 0; 450 | margin-right: 0; 451 | margin-top: 0; 452 | padding-bottom: 0; 453 | padding-left: 0; 454 | padding-right: 0; 455 | padding-top: 0; 456 | margin-bottom: 1.45rem; 457 | } 458 | noscript { 459 | margin-left: 0; 460 | margin-right: 0; 461 | margin-top: 0; 462 | padding-bottom: 0; 463 | padding-left: 0; 464 | padding-right: 0; 465 | padding-top: 0; 466 | margin-bottom: 1.45rem; 467 | } 468 | iframe { 469 | margin-left: 0; 470 | margin-right: 0; 471 | margin-top: 0; 472 | padding-bottom: 0; 473 | padding-left: 0; 474 | padding-right: 0; 475 | padding-top: 0; 476 | margin-bottom: 1.45rem; 477 | } 478 | hr { 479 | margin-left: 0; 480 | margin-right: 0; 481 | margin-top: 0; 482 | padding-bottom: 0; 483 | padding-left: 0; 484 | padding-right: 0; 485 | padding-top: 0; 486 | margin-bottom: calc(1.45rem - 1px); 487 | background: hsla(0, 0%, 0%, 0.2); 488 | border: none; 489 | height: 1px; 490 | } 491 | address { 492 | margin-left: 0; 493 | margin-right: 0; 494 | margin-top: 0; 495 | padding-bottom: 0; 496 | padding-left: 0; 497 | padding-right: 0; 498 | padding-top: 0; 499 | margin-bottom: 1.45rem; 500 | } 501 | b { 502 | font-weight: bold; 503 | } 504 | strong { 505 | font-weight: bold; 506 | } 507 | dt { 508 | font-weight: bold; 509 | } 510 | th { 511 | font-weight: bold; 512 | } 513 | li { 514 | margin-bottom: calc(1.45rem / 2); 515 | } 516 | ol li { 517 | padding-left: 0; 518 | } 519 | ul li { 520 | padding-left: 0; 521 | } 522 | li > ol { 523 | margin-left: 1.45rem; 524 | margin-bottom: calc(1.45rem / 2); 525 | margin-top: calc(1.45rem / 2); 526 | } 527 | li > ul { 528 | margin-left: 1.45rem; 529 | margin-bottom: calc(1.45rem / 2); 530 | margin-top: calc(1.45rem / 2); 531 | } 532 | blockquote *:last-child { 533 | margin-bottom: 0; 534 | } 535 | li *:last-child { 536 | margin-bottom: 0; 537 | } 538 | p *:last-child { 539 | margin-bottom: 0; 540 | } 541 | li > p { 542 | margin-bottom: calc(1.45rem / 2); 543 | } 544 | code { 545 | font-size: 0.85rem; 546 | line-height: 1.45rem; 547 | } 548 | kbd { 549 | font-size: 0.85rem; 550 | line-height: 1.45rem; 551 | } 552 | samp { 553 | font-size: 0.85rem; 554 | line-height: 1.45rem; 555 | } 556 | abbr { 557 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 558 | cursor: help; 559 | } 560 | acronym { 561 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 562 | cursor: help; 563 | } 564 | abbr[title] { 565 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 566 | cursor: help; 567 | text-decoration: none; 568 | } 569 | thead { 570 | text-align: left; 571 | } 572 | td, 573 | th { 574 | text-align: left; 575 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); 576 | font-feature-settings: "tnum"; 577 | -moz-font-feature-settings: "tnum"; 578 | -ms-font-feature-settings: "tnum"; 579 | -webkit-font-feature-settings: "tnum"; 580 | padding-left: 0.96667rem; 581 | padding-right: 0.96667rem; 582 | padding-top: 0.725rem; 583 | padding-bottom: calc(0.725rem - 1px); 584 | } 585 | th:first-child, 586 | td:first-child { 587 | padding-left: 0; 588 | } 589 | th:last-child, 590 | td:last-child { 591 | padding-right: 0; 592 | } 593 | tt, 594 | code { 595 | background-color: hsla(0, 0%, 0%, 0.04); 596 | border-radius: 3px; 597 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono", 598 | "Liberation Mono", Menlo, Courier, monospace; 599 | padding: 0; 600 | padding-top: 0.2em; 601 | padding-bottom: 0.2em; 602 | } 603 | pre code { 604 | background: none; 605 | line-height: 1.42; 606 | } 607 | code:before, 608 | code:after, 609 | tt:before, 610 | tt:after { 611 | letter-spacing: -0.2em; 612 | content: " "; 613 | } 614 | pre code:before, 615 | pre code:after, 616 | pre tt:before, 617 | pre tt:after { 618 | content: ""; 619 | } 620 | @media only screen and (max-width: 480px) { 621 | html { 622 | font-size: 100%; 623 | } 624 | } 625 | -------------------------------------------------------------------------------- /examples/src/components/layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Helmet from 'react-helmet' 4 | 5 | import Header from './header' 6 | import './layout.css' 7 | 8 | // eslint-disable-next-line react/prop-types 9 | const Layout = ({ children }) => ( 10 | <> 11 | 12 | 13 | 14 |
15 |
23 | {children} 24 |
25 | 26 | ) 27 | 28 | export default Layout 29 | -------------------------------------------------------------------------------- /examples/src/images/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/37d616061cba0ac7b8b9980244ab41b4a41aa30c/examples/src/images/gatsby-icon.png -------------------------------------------------------------------------------- /examples/src/pages/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 no-alert:0 */ 2 | import React from 'react' 3 | import addToMailchimp from 'gatsby-plugin-mailchimp' 4 | 5 | export default class IndexPage extends React.Component { 6 | state = { 7 | name: null, 8 | email: null, 9 | } 10 | 11 | _handleChange = e => { 12 | console.log({ 13 | [`${e.target.name}`]: e.target.value, 14 | }) 15 | this.setState({ 16 | [`${e.target.name}`]: e.target.value, 17 | }) 18 | } 19 | 20 | _handleSubmit = e => { 21 | e.preventDefault() 22 | 23 | console.log('submit', this.state) 24 | 25 | addToMailchimp(this.state.email, this.state) 26 | .then(({ msg, result }) => { 27 | console.log('msg', `${result}: ${msg}`) 28 | 29 | if (result !== 'success') { 30 | throw msg 31 | } 32 | alert(msg) 33 | }) 34 | .catch(err => { 35 | console.log('err', err) 36 | alert(err) 37 | }) 38 | } 39 | 40 | render() { 41 | return ( 42 |
43 |

Hi people

44 |

Submit the form below and check your browser console!

45 |
46 |
47 | 53 | 59 |
60 |
61 | Favorite color: 62 |
    63 |
  • 64 | 71 | 72 |
  • 73 |
  • 74 | 81 | 82 |
  • 83 |
  • 84 | 91 | 92 |
  • 93 |
94 | 95 |
96 |
97 |
98 | ) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.onCreateWebpackConfig = function(_ref, _ref2) { 4 | var plugins = _ref.plugins, 5 | actions = _ref.actions; 6 | var endpoint = _ref2.endpoint, 7 | _ref2$timeout = _ref2.timeout, 8 | timeout = _ref2$timeout === void 0 ? 3500 : _ref2$timeout; 9 | var isString = typeof endpoint === 'string'; 10 | 11 | if (!isString) { 12 | throw new Error( 13 | 'Mailchimp endpoint required and must be of type string. See repo README for more info.', 14 | ); 15 | } else if (endpoint.length < 40) { 16 | throw new Error( 17 | 'gatsby-plugin-mailchimp: donʼt forget to add your MC endpoint to your gatsby-config file. See README for more info.', 18 | ); 19 | } 20 | 21 | actions.setWebpackConfig({ 22 | plugins: [ 23 | plugins.define({ 24 | __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__: JSON.stringify(endpoint), 25 | __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__: Number(timeout), 26 | }), 27 | ], 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /img/mailchimp_form_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/37d616061cba0ac7b8b9980244ab41b4a41aa30c/img/mailchimp_form_action.png -------------------------------------------------------------------------------- /img/mailchimp_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/37d616061cba0ac7b8b9980244ab41b4a41aa30c/img/mailchimp_list.png -------------------------------------------------------------------------------- /img/mailchimp_list_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/37d616061cba0ac7b8b9980244ab41b4a41aa30c/img/mailchimp_list_fields.png -------------------------------------------------------------------------------- /img/mc_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminhoffman/gatsby-plugin-mailchimp/37d616061cba0ac7b8b9980244ab41b4a41aa30c/img/mc_groups.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true, 5 | }); 6 | exports['default'] = void 0; 7 | 8 | var _jsonp = _interopRequireDefault(require('jsonp')); 9 | 10 | var _emailValidator = require('email-validator'); 11 | 12 | function _interopRequireDefault(obj) { 13 | return obj && obj.__esModule ? obj : { default: obj }; 14 | } 15 | 16 | /** 17 | * Make a jsonp request to user's mailchimp list 18 | * `param` object avoids CORS issues 19 | * timeout to 3.5s so user isn't waiting forever 20 | * usually occurs w/ privacy plugins enabled 21 | * 3.5s is a bit longer than the time it would take on a Slow 3G connection 22 | * 23 | * @param {String} url - concatenated string of user's gatsby-config.js 24 | * options, along with any MC list fields as query params. 25 | * 26 | * @return {Promise} - a promise that resolves a data object 27 | * or rejects an error object 28 | */ 29 | var subscribeEmailToMailchimp = function subscribeEmailToMailchimp(_ref) { 30 | var url = _ref.url, 31 | timeout = _ref.timeout; 32 | return new Promise(function(resolve, reject) { 33 | return (0, _jsonp['default'])( 34 | url, 35 | { 36 | param: 'c', 37 | timeout: timeout, 38 | }, 39 | function(err, data) { 40 | if (err) reject(err); 41 | if (data) resolve(data); 42 | }, 43 | ); 44 | }); 45 | }; 46 | /** 47 | * Build a query string of MC list fields 48 | * 49 | * @param {Object} fields - a list of mailchimp audience field labels 50 | * and their values. We uppercase because that's what MC requires. 51 | * NOTE: GROUPS stay as lowercase (ex: MC uses group field names as `group[21269]`) 52 | * 53 | * @return {String} - `&FIELD1=value1&FIELD2=value2&group[21265][2]=group1` 54 | */ 55 | 56 | var convertListFields = function convertListFields(fields) { 57 | var queryParams = ''; 58 | 59 | for (var field in fields) { 60 | if (Object.prototype.hasOwnProperty.call(fields, field)) { 61 | // If this is a list group, not user field then keep lowercase, as per MC reqs 62 | // https://github.com/benjaminhoffman/gatsby-plugin-mailchimp/blob/master/README.md#groups 63 | var fieldTransformed = field.substring(0, 6) === 'group[' ? field : field.toUpperCase(); 64 | queryParams = queryParams.concat( 65 | '&'.concat(fieldTransformed, '=').concat(fields[field]), 66 | ); 67 | } 68 | } 69 | 70 | return queryParams; 71 | }; 72 | /** 73 | * Subscribe an email address to a Mailchimp email list. 74 | * We use ES5 function syntax (instead of arrow) because we need `arguments.length` 75 | * 76 | * @param {String} email - required; the email address you want to subscribe 77 | * @param {Object} fields - optional; add'l info (columns) you want included w/ this subscriber 78 | * @param {String} endpointOverride - optional; if you want to override the default MC mailing list 79 | * that's listed in your gatsby-config, pass the list in here 80 | * 81 | * @return {Object} - 82 | * { 83 | * result: (`success` || `error`) 84 | * msg: (`Thank you for subscribing!` || `The email you entered is not valid.`), 85 | * } 86 | */ 87 | 88 | var addToMailchimp = function addToMailchimp(email, fields, endpointOverride) { 89 | var isEmailValid = (0, _emailValidator.validate)(email); 90 | var emailEncoded = encodeURIComponent(email); 91 | 92 | if (!isEmailValid) { 93 | return Promise.resolve({ 94 | result: 'error', 95 | msg: 'The email you entered is not valid.', 96 | }); 97 | } 98 | 99 | var endpoint = __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__; // eslint-disable-line no-undef 100 | 101 | var timeout = __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__; // eslint-disable-line no-undef 102 | // The following tests for whether you passed in a `fields` object. If 103 | // there are only two params and the second is a string, then we can safely 104 | // assume the second param is a MC mailing list, and not a fields object. 105 | 106 | if (arguments.length < 3 && typeof fields === 'string') { 107 | endpoint = fields; 108 | } else if (typeof endpointOverride === 'string') { 109 | endpoint = endpointOverride; 110 | } // Generates MC endpoint for our jsonp request. We have to 111 | // change `/post` to `/post-json` otherwise, MC returns an error 112 | 113 | endpoint = endpoint.replace(/\/post/g, '/post-json'); 114 | var queryParams = '&EMAIL='.concat(emailEncoded).concat(convertListFields(fields)); 115 | var url = ''.concat(endpoint).concat(queryParams); 116 | return subscribeEmailToMailchimp({ 117 | url: url, 118 | timeout: timeout, 119 | }); 120 | }; 121 | 122 | var _default = addToMailchimp; 123 | exports['default'] = _default; 124 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const pathIgnorePattterns = ['/node_modules/', '/examples/', '/img/']; 2 | 3 | module.exports = { 4 | coveragePathIgnorePatterns: pathIgnorePattterns, 5 | testPathIgnorePatterns: pathIgnorePattterns, 6 | modulePathIgnorePatterns: pathIgnorePattterns, 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-plugin-mailchimp", 3 | "author": "Benjamin Hoffman <6520022+benjaminhoffman@users.noreply.github.com>", 4 | "version": "5.2.2", 5 | "description": "A simple, lightweight Gatsby plugin to subscribe email addresses to your Mailchimp list", 6 | "main": "index.js", 7 | "license": "MIT", 8 | "homepage": "https://github.com/benjaminhoffman/gatsby-plugin-mailchimp#readme", 9 | "bugs": { 10 | "url": "https://github.com/benjaminhoffman/gatsby-plugin-mailchimp/issues" 11 | }, 12 | "scripts": { 13 | "test": "jest", 14 | "build": "babel src -d .", 15 | "watch": "babel src -d . -w", 16 | "publish": "yarn build && npm publish", 17 | "precommit": "lint-staged" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/benjaminhoffman/gatsby-plugin-mailchimp.git" 22 | }, 23 | "keywords": [ 24 | "gatsby", 25 | "gatsby-plugin", 26 | "gatsbyjs", 27 | "mailchimp", 28 | "mailchimp api" 29 | ], 30 | "dependencies": { 31 | "email-validator": "^2.0.4", 32 | "jsonp": "^0.2.1" 33 | }, 34 | "devDependencies": { 35 | "@babel/cli": "^7.8.4", 36 | "@babel/preset-env": "^7.9.6", 37 | "babel-eslint": "^10.0.1", 38 | "eslint": "^5.16.0", 39 | "eslint-config-airbnb-base": "^13.1.0", 40 | "eslint-config-prettier": "^4.2.0", 41 | "eslint-plugin-import": "^2.17.2", 42 | "eslint-plugin-prettier": "^3.0.1", 43 | "eslint-plugin-react": "^7.12.4", 44 | "husky": "^2.1.0", 45 | "jest": "^26.0.1", 46 | "lint-staged": "^8.1.5", 47 | "prettier": "^1.17.0", 48 | "webpack": "^4.29.6" 49 | }, 50 | "husky": { 51 | "hooks": { 52 | "pre-commit": "yarn precommit" 53 | } 54 | }, 55 | "lint-staged": { 56 | "*.js": [ 57 | "eslint --fix", 58 | "prettier --write", 59 | "git add" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/gatsby-node.js: -------------------------------------------------------------------------------- 1 | exports.onCreateWebpackConfig = ({ plugins, actions }, { endpoint, timeout = 3500 }) => { 2 | const isString = typeof endpoint === 'string'; 3 | if (!isString) { 4 | throw new Error( 5 | 'Mailchimp endpoint required and must be of type string. See repo README for more info.', 6 | ); 7 | } else if (endpoint.length < 40) { 8 | throw new Error( 9 | 'gatsby-plugin-mailchimp: donʼt forget to add your MC endpoint to your gatsby-config file. See README for more info.', 10 | ); 11 | } 12 | 13 | actions.setWebpackConfig({ 14 | plugins: [ 15 | plugins.define({ 16 | __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__: JSON.stringify(endpoint), 17 | __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__: Number(timeout), 18 | }), 19 | ], 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import jsonp from 'jsonp'; 2 | import { validate } from 'email-validator'; 3 | 4 | /** 5 | * Make a jsonp request to user's mailchimp list 6 | * `param` object avoids CORS issues 7 | * timeout to 3.5s so user isn't waiting forever 8 | * usually occurs w/ privacy plugins enabled 9 | * 3.5s is a bit longer than the time it would take on a Slow 3G connection 10 | * 11 | * @param {String} url - concatenated string of user's gatsby-config.js 12 | * options, along with any MC list fields as query params. 13 | * 14 | * @return {Promise} - a promise that resolves a data object 15 | * or rejects an error object 16 | */ 17 | 18 | const subscribeEmailToMailchimp = ({ url, timeout }) => 19 | new Promise((resolve, reject) => 20 | jsonp(url, { param: 'c', timeout }, (err, data) => { 21 | if (err) reject(err); 22 | if (data) resolve(data); 23 | }), 24 | ); 25 | 26 | /** 27 | * Build a query string of MC list fields 28 | * 29 | * @param {Object} fields - a list of mailchimp audience field labels 30 | * and their values. We uppercase because that's what MC requires. 31 | * NOTE: GROUPS stay as lowercase (ex: MC uses group field names as `group[21269]`) 32 | * 33 | * @return {String} - `&FIELD1=value1&FIELD2=value2&group[21265][2]=group1` 34 | */ 35 | const convertListFields = fields => { 36 | let queryParams = ''; 37 | for (const field in fields) { 38 | if (Object.prototype.hasOwnProperty.call(fields, field)) { 39 | // If this is a list group, not user field then keep lowercase, as per MC reqs 40 | // https://github.com/benjaminhoffman/gatsby-plugin-mailchimp/blob/master/README.md#groups 41 | const fieldTransformed = 42 | field.substring(0, 6) === 'group[' ? field : field.toUpperCase(); 43 | queryParams = queryParams.concat(`&${fieldTransformed}=${fields[field]}`); 44 | } 45 | } 46 | return queryParams; 47 | }; 48 | 49 | /** 50 | * Subscribe an email address to a Mailchimp email list. 51 | * We use ES5 function syntax (instead of arrow) because we need `arguments.length` 52 | * 53 | * @param {String} email - required; the email address you want to subscribe 54 | * @param {Object} fields - optional; add'l info (columns) you want included w/ this subscriber 55 | * @param {String} endpointOverride - optional; if you want to override the default MC mailing list 56 | * that's listed in your gatsby-config, pass the list in here 57 | * 58 | * @return {Object} - 59 | * { 60 | * result: (`success` || `error`) 61 | * msg: (`Thank you for subscribing!` || `The email you entered is not valid.`), 62 | * } 63 | */ 64 | const addToMailchimp = function addToMailchimp(email, fields, endpointOverride) { 65 | const isEmailValid = validate(email); 66 | const emailEncoded = encodeURIComponent(email); 67 | if (!isEmailValid) { 68 | return Promise.resolve({ 69 | result: 'error', 70 | msg: 'The email you entered is not valid.', 71 | }); 72 | } 73 | 74 | let endpoint = __GATSBY_PLUGIN_MAILCHIMP_ADDRESS__; // eslint-disable-line no-undef 75 | const timeout = __GATSBY_PLUGIN_MAILCHIMP_TIMEOUT__; // eslint-disable-line no-undef 76 | 77 | // The following tests for whether you passed in a `fields` object. If 78 | // there are only two params and the second is a string, then we can safely 79 | // assume the second param is a MC mailing list, and not a fields object. 80 | if (arguments.length < 3 && typeof fields === 'string') { 81 | endpoint = fields; 82 | } else if (typeof endpointOverride === 'string') { 83 | endpoint = endpointOverride; 84 | } 85 | 86 | // Generates MC endpoint for our jsonp request. We have to 87 | // change `/post` to `/post-json` otherwise, MC returns an error 88 | endpoint = endpoint.replace(/\/post/g, '/post-json'); 89 | const queryParams = `&EMAIL=${emailEncoded}${convertListFields(fields)}`; 90 | const url = `${endpoint}${queryParams}`; 91 | 92 | return subscribeEmailToMailchimp({ url, timeout }); 93 | }; 94 | 95 | export default addToMailchimp; 96 | --------------------------------------------------------------------------------