[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\"env\", {\n      \"targets\": {\n        \"node\": \"6\"\n      }\n    }]\n  ],\n}\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n    working_directory: ~/meteor-now\n    docker:\n      - image: circleci/node:7\n    steps:\n      - checkout\n      - run: npm install\n      - run: npm test\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": \"airbnb\",\n  \"env\": {\n    \"node\": true,\n    \"es6\": true,\n  },\n  \"rules\": {\n    \"jsx-a11y/href-no-hash\": 0,\n  },\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.md",
    "content": "---\r\nname: Bug report\r\nabout: Create a report to help us improve\r\n\r\n---\r\n\r\n**Describe the bug**\r\nA clear and concise description of what the bug is.\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Go to '...'\r\n2. Click on '....'\r\n3. Scroll down to '....'\r\n4. See error\r\n\r\n**Expected behavior**\r\nA clear and concise description of what you expected to happen.\r\n\r\n**Screenshots**\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n**Desktop (please complete the following information):**\r\n - OS: [e.g. iOS]\r\n - Browser [e.g. chrome, safari]\r\n - Version [e.g. 22]\r\n\r\n**Smartphone (please complete the following information):**\r\n - Device: [e.g. iPhone6]\r\n - OS: [e.g. iOS8.1]\r\n - Browser [e.g. stock browser, safari]\r\n - Version [e.g. 22]\r\n\r\n**Additional context**\r\nAdd any other context about the problem here.\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "content": "---\r\nname: Feature request\r\nabout: Suggest an idea for this project\r\n\r\n---\r\n\r\n**Is your feature request related to a problem? Please describe.**\r\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\r\n\r\n**Describe the solution you'd like**\r\nA clear and concise description of what you want to happen.\r\n\r\n**Describe alternatives you've considered**\r\nA clear and concise description of any alternative solutions or features you've considered.\r\n\r\n**Additional context**\r\nAdd any other context or screenshots about the feature request here.\r\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\nnpm*\nbuild\n.DS_Store\n.nvmrc\n"
  },
  {
    "path": ".npmignore",
    "content": "src\nassets\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mazlix@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Mirza Joldic and Justin Krup\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## Deprecation notice:\n\n[**NOTE: meteor-now is no longer actively maintained.** Since ZEIT Now no longer focuses on serverful frameworks, it has been deprecated in favor of **meteor-hero** which allows free meteor deploys using Heroku.](https://github.com/jkrup/meteor-hero)\n\n---\n\n\n# Meteor Now [![CircleCI](https://circleci.com/gh/jkrup/meteor-now.svg?style=svg)](https://circleci.com/gh/jkrup/meteor-now) [![npm version](https://badge.fury.io/js/meteor-now.svg)](https://badge.fury.io/js/meteor-now) [![downloads](https://img.shields.io/npm/dm/meteor-now.svg)](https://npmjs.org/package/meteor-now)\n\n`meteor-now` is a tool to let you instantly deploy your Meteor apps with one command using ZEIT's [▲now](http://zeit.co/now) service. Just run `meteor-now` and instantly deploy your Meteor app like you could back in the good 'ol days of `meteor deploy`.\n\n<p align=\"center\">\n  <img src=\"https://github.com/jkrup/meteor-now/raw/master/assets/meteor-now-intro.gif\">\n</p>\n\n## Demo\n[https://meteor-test-msrbsvslpz.now.sh](https://meteor-test-msrbsvslpz.now.sh)\n\n# Install\nInstall the `now` and `meteor-now` packages:\n```\n$ npm install -g now meteor-now\n```\n\nCreate `now` account\n```\n$ now login\n> Enter your email: <your email>\n> Please follow the link sent to <your email> to log in.\n> Verify that the provided security code in the email matches Pragmatic Manta Ray.\n\n✔ Confirmed email address!\n\n> Logged in successfully. Token saved in ~/.now.json\n```\n\n# Usage\n\n## Deploying for Development / Testing\n\nIn your Meteor app directory, run `meteor-now`.\n\n```\n~/my-meteor-app/ $ meteor-now\n✔ [METEOR-NOW] - building meteor app\n✔ [METEOR-NOW] - preparing build\n✔ [METEOR-NOW] - deploying build\n✔ [METEOR-NOW] - meteor app deployed to https://meteor-test-msrbsvslpz.now.sh\n```\n\n## Deploying for Production\n\nThere are a few things you'll want to know before using `meteor-now` in production. And your deploy command will probably look more like the following:\n\n```\nmeteor-now -e MONGO_URL=mongodb://<username>:<pass>@.... -e ROOT_URL=https://mydomain.com -e NODE_ENV=production\n```\n\nIf your app uses MongoDB at all you will want to specify a `-e MONGO_URL=` for persistent storage of data. [Mlab](http://mlab.com) has a great free sandbox tier, but also not recommended for production (you will probably want to pay at some point).\n\nYou should also have a paid account with [▲now](https://zeit.co/now) so that you can specify a custom domain name, and have more than 1GB of bandwidth/mo.\n\nYou will also likely want to specify a `-e ROOT_URL=` [http://stackoverflow.com/questions/24046186/meteor-what-is-the-purpose-of-root-url-and-to-what-should-it-be-defined](http://stackoverflow.com/questions/24046186/meteor-what-is-the-purpose-of-root-url-and-to-what-should-it-be-defined)\n\nFinally you should have a `production.settings.json` in your project directory if you are using `METEOR_SETTINGS` (i.e. passing in `--settings`) and use `-e NODE_ENV=production` to tell `meteor-now` to use that.\n\n# How\n![METEOR-NOW](assets/zeit-meteor.png \"METEOR-NOW\")\n`meteor-now` use ZEITs [▲now](https://zeit.co/now) service to deploy the Meteor app in a container. Please refer to their [documentation and support](https://github.com/zeit/) for hosting related details.\n\n# Additional Info\n## Full deploy with MongoDB\n`meteor-now` lets you to deploy your Meteor app with a MongoDB included similar to how `meteor deploy` used to work. **This method is not intended for production deployments**. In order to achieve this, we bundle your Meteor app and MongoDB into a a single Dockerfile and we instruct your app to connect to the local MongoDB instance. The Dockerfile gets built in the cloud by `now` and once it's ready, the Meteor app will spin up and connect to the MongoDB instance running locally in that docker container.\n\nSome things to keep in mind here is that since MongoDB is installed on the docker container itself, your app data is not persistent and has the chance of getting deleted if a new container is created. Because `now` automatically scales your deployment with load, new docker containers are bound to be created and your is likely going to get lost.\n\nTo not have this issue, spin up your own MongoDB instance and pass the `-e MONGO_URL=...` flag when you deploy.\n## Using METEOR_SETTINGS\nCurrently there are two ways you can set the METEOR_SETTINGS environment variable in your deployments\n\n- Using `now secrets`\n\n```\n$ now secrets add meteor-settings '{ \"public\": { \"foo\": \"bar\" }}'\n$ meteor-now -e METEOR_SETTINGS=@meteor-settings -e MONGO_URL=...\n```\n\n- Using `development.settings.json` and `production.settings.json`\nFollowing the convention of the `NODE_ENV` environment variable, `meteor-now` uses `NODE_ENV` to determine which file to look for.\nIt will either look for `development.settings.json` or `production.settings.json` in your root Meteor directory.\n\nNote that `meteor-now` by default looks for `development.settings.json` unless otherwise specified by `-e NODE_ENV=production` or `-e METEOR_SETTINGS='{ \"foo\": \"bar\" }'`.\n\n## Debug\nIn order to see detailed deployment logs as they happen, pass the `-d` when you deploy.\n\n## Bundle Splitting\nThe `now` free tier has a limitation of 1mb per file. As a workaround, we split the final bundle into pieces prior to uploading. If you are on a paid plan, you can turn this off by passing the `--nosplit` flag like so `meteor-now --nosplit`.\n\n## now.json\nYou can include a regular `now.json` file in your project directory. `meteor-now` will make sure to include it along with your app when it deploys. Specifying now settings in the `package.json` file is not currently supported.\n\n## FAQ\n### Can I use this in production?\nYes– if you are paying for ▲now and using an external database! ▲now supports dynamic autoscaling of apps (with sticky-sessions), you should read all the caveats related to `now` if you are not paying for a monthly plan– You only get 1GB of bandwidth per month, and also your source files are made (somewhat) public at your url /_src\nAlso read the caveats below if you did not specify a MONGO_URL\n\n### What happens when I don't specify a MONGO_URL\nWhen you don't specify a MONGO_URL we bundle a local version of MonogoDB with your application. What this basically means is that if your application ever gets shut down, or scales up to multiple instances you will lose all data that was inserted into your DB.\n\n### How can I change my ROOT_URL\nIn order to set the ROOT_URL for your application, pass the -e flag along with the value for what you want the ROOT_URL to be. Example: `meteor-now -e ROOT_URL=www.mymeteorapp.com`\n\n### How do I set a domain name\nIn order to use a custom domain name, you would need a Pro account with now.\n\nRun `now domain add --external meteor-now.com`. You should get a response back with steps to verify your domain.\n\n```\nVerification required: Please add the following TXT record on the external DNS server: _now.meteor-now.com: ea39a62a58b3109f92024230826e37f0adc6abcd\n```\n\nIn your domain DNS settings, add a TXT record with the above information.\n\nWait a few moments for the DNS records to propagate and rerun the same command above.\n```\n$ now domains add --external meteor-now.com\nSuccess! Domain meteor-now.com verified [2s]\n```\n\nNow alias your deployment to the new domain\n```\n$ now alias https://meteor-now-site-izdolpdrvv.now.sh/ www.meteor-now.com\nwww.meteor-now.com is a custom domain.\nVerifying the DNS settings for www.meteor-now.com (see https://zeit.world for help)\nVerification OK!\nProvisioning certificate for www.meteor-now.com\nSuccess! Alias created: https://www.meteor-now.com now points to https://meteor-now-site-izdolpdrvv.now.sh [copied to clipboard]\n```\n\nYou can also pass the `--alias abc.com` option to `meteor-now` and it will automatically run the alias after the deployment completes. \n\nRead [this blog post](https://zeit.co/blog/now-alias) for more information.\n\n### Why are my XX resource not loading\nBecause now enforces SSL, you may experience some issues with 3rd party resources (such as google fonts) not being fetched by your clients due to mixed content warnings. To resolve, just make sure all your assets are being fetched with https:// protocol urls whenever available. If that's not possible, you may need to just download those assets and serve them locally through meteor's public/ directory.\n\n### I deployed my free app but it's failing to connect to MongoDB\nIf you're deploying with an included MongoDB, we've observed that sometimes MongoDB takes a while to start. In this case, Meteor complains that it can't connect to MongoDB. Give it a few more minutes and your app should start. Make sure to refresh the page.\n\n### I want to use a different docker image\nThe default docker images are `nodesource/jessie:0.10.43` for Meteor < 1.4, `node:8.9.4` for < 1.7 and `node:8.11.2` for >= 1.7.\nIf you want to use a different image, use the `--docker-image` flag.\n\n### My app requires XX can I use my own Dockerfile?\nWe're currently support passing a `--deps 'depName1,depName2'` flag so that applications that rely on things like imagemagick are able to work. We are also looking into the ability to specify your own Dockerfile in the case that you require even more customization.\n\nStay tuned to updates on the [issue](https://github.com/jkrup/meteor-now/issues/6)\n\n# Authors\n<a href=\"https://www.github.com/jkrup\"><img src=\"https://avatars2.githubusercontent.com/u/519731?v=3&s=460\" alt=\"Justin Avatar\" height=\"100\" width=\"100\"></a>|<a href=\"https://www.github.com/purplecones\"><img src=\"https://avatars1.githubusercontent.com/u/136654?v=3&s=460\" height=\"100\" width=\"100\" alt=\"Mirza Avatar\"></a>\n---|---\nJustin Krup|Mirza Joldic\n[@jkrup](https://www.github.com/jkrup)|[@purplecones](https://www.github.com/purplecones)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"meteor-now\",\n  \"version\": \"0.8.0\",\n  \"description\": \"Deploy meteor apps with one command using now.sh (https://zeit.co/now)\",\n  \"repository\": \"https://github.com/jkrup/meteor-now\",\n  \"author\": \"Justin Krup\",\n  \"contributors\": [\n    {\n      \"name\": \"Mirza Joldic\",\n      \"url\": \"https://github.com/purplecones\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"preferGlobal\": true,\n  \"bin\": \"./build/main.js\",\n  \"scripts\": {\n    \"build\": \"webpack\",\n    \"build:watch\": \"webpack --watch\",\n    \"lint\": \"eslint src\",\n    \"prepublishOnly\": \"npm run build\",\n    \"test\": \"npm run lint && jest\",\n    \"test:watch\": \"jest --watch\"\n  },\n  \"dependencies\": {\n    \"cli-spinners\": \"^1.0.1\",\n    \"colors\": \"^1.1.2\",\n    \"del\": \"^3.0.0\",\n    \"file-system\": \"^2.2.2\",\n    \"ora\": \"^1.3.0\",\n    \"prettier\": \"^1.7.4\",\n    \"split-file\": \"^2.1.0\",\n    \"tar\": \"^4.4.1\",\n    \"yargs\": \"^9.0.1\"\n  },\n  \"devDependencies\": {\n    \"babel-jest\": \"^21.2.0\",\n    \"babel-loader\": \"^7.1.2\",\n    \"babel-preset-env\": \"^1.6.1\",\n    \"eslint\": \"^4.7.2\",\n    \"eslint-config-airbnb\": \"^15.1.0\",\n    \"eslint-plugin-import\": \"^2.7.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.0.2\",\n    \"eslint-plugin-react\": \"^7.4.0\",\n    \"jest\": \"^21.2.1\",\n    \"webpack\": \"^3.8.1\",\n    \"webpack-node-externals\": \"^1.6.0\"\n  }\n}\n"
  },
  {
    "path": "src/api/__tests__/.eslintrc",
    "content": "{\n  \"env\": {\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": "src/api/__tests__/args.js",
    "content": "import {\n  getArg,\n  getArgs,\n  getEnvironmentVariables,\n  getEnvironmentVariable,\n  getRemainingVariables,\n  getRemainingOptions,\n  flattenOptions,\n} from '../args';\n\ndescribe('args test', () => {\n  test('it should return correct arg', () => {\n    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick'];\n    const arg = getArg('deps');\n    expect(arg).toBe('imagemagick');\n  });\n\n  test('it should return all default args', () => {\n    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '--alias', 'abc.com'];\n    const args = getArgs();\n    expect(args).toEqual({\n      $0: '-e',\n      _: ['node'],\n      help: false,\n      version: false,\n      deps: 'imagemagick',\n      e: 'MONGO_URL=mongodb://127.0.0.1:27017',\n      alias: 'abc.com',\n    });\n  });\n\n  test('it should return all args when passed an argv', () => {\n    const args = getArgs([\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '--deps',\n      'imagemagick',\n      '--public',\n      '--alias',\n      'abc.com',\n    ]);\n    expect(args).toEqual({\n      $0: '-e',\n      _: ['node'],\n      help: false,\n      version: false,\n      deps: 'imagemagick',\n      e: 'MONGO_URL=mongodb://127.0.0.1:27017',\n      public: true,\n      alias: 'abc.com',\n    });\n  });\n\n  test('it should return correct number of environment variables', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '--deps',\n      'abc',\n    ];\n    const environmentVariables = getEnvironmentVariables();\n    expect(environmentVariables.length).toBe(2);\n  });\n\n  test('it should return all environment variables', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '--deps',\n      'abc',\n    ];\n    const environmentVariables = getEnvironmentVariables();\n    expect(environmentVariables).toEqual([\n      { name: 'MONGO_URL', value: 'mongodb://127.0.0.1:27017' },\n      { name: 'ROOT_URL', value: 'http://localhost:3000' },\n    ]);\n  });\n\n  test('it should return a environment variable by name', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '--deps',\n      'abc',\n    ];\n    const environmentVariables = getEnvironmentVariable('ROOT_URL');\n    expect(environmentVariables).toBe('http://localhost:3000');\n  });\n\n  test('it should return all custom environment variables', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '-e',\n      'MY_SPECIAL_VAR=cats',\n      '-e',\n      'SECRET=litter',\n      '--deps',\n      'abc',\n    ];\n    const remainingVariables = getRemainingVariables();\n    expect(remainingVariables).toEqual([['-e', 'MY_SPECIAL_VAR=cats'], ['-e', 'SECRET=litter']]);\n  });\n\n  test('it should return all custom options', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '-e',\n      'MY_SPECIAL_VAR=cats',\n      '-e',\n      'SECRET=litter',\n      '--deps',\n      'abc',\n      '--public',\n      '--alias',\n      'abc.com',\n    ];\n    const remainingOptions = getRemainingOptions();\n    expect(remainingOptions).toEqual([['--public']]);\n  });\n\n  test('it should correctly flatten options', () => {\n    const flattenedOptions = flattenOptions([\n      '--public',\n      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],\n      ['--alias', 'abc.com'],\n    ]);\n    expect(flattenedOptions).toEqual([\n      '--public',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '--alias',\n      'abc.com',\n    ]);\n  });\n\n  test('it should ignore certain flags', () => {\n    process.argv = [\n      'node',\n      '-e',\n      'MONGO_URL=mongodb://127.0.0.1:27017',\n      '-e',\n      'ROOT_URL=http://localhost:3000',\n      '-e',\n      'MY_SPECIAL_VAR=cats',\n      '-e',\n      'SECRET=litter',\n      '--deps',\n      'abc',\n      '--nosplit',\n      '--alias',\n      'abc.com',\n    ];\n    const remainingVariables = getRemainingVariables();\n    expect(remainingVariables).toEqual([['-e', 'MY_SPECIAL_VAR=cats'], ['-e', 'SECRET=litter']]);\n  });\n});\n"
  },
  {
    "path": "src/api/__tests__/constants.js",
    "content": "import { getFolderName } from '../constants';\n\ndescribe('constants test', () => {\n  test('it should correctly get the projects name (mac)', async () => {\n    const folderName = getFolderName('/some/fake/path/to/a/folder', false);\n    expect(folderName).toBe('folder');\n  });\n\n  test('it should correctly get the projects name (win)', async () => {\n    const folderName = getFolderName('\\\\some\\\\fake\\\\path\\\\to\\\\a\\\\folder', true);\n    expect(folderName).toBe('folder');\n  });\n});\n"
  },
  {
    "path": "src/api/__tests__/now.js",
    "content": "import os from 'os';\nimport { constructNowOptions } from '../now';\n\ndescribe('now test', () => {\n  test('it should construct the correct now options', async () => {\n    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '-e', 'METEOR_SETTINGS=\\'{ \"foo\": \"bar\" }\\'', '--alias', 'abc.com'];\n    const nowOptions = await constructNowOptions();\n    expect(nowOptions).toEqual([\n      `${os.homedir()}/.meteor-now/build`,\n      ['-e', 'PORT=3000'],\n      ['-e', 'ROOT_URL=http://localhost:3000'],\n      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],\n      ['-e', 'METEOR_SETTINGS=\\'\\'{ \"foo\": \"bar\" }\\'\\''], [],\n    ]);\n  });\n\n  test('it should construct the correct environment variables', async () => {\n    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '-e', 'METEOR_SETTINGS=\\'{ \"foo\": \"bar\" }\\'', '-e', 'MAGIC_VAR=magical'];\n    const nowOptions = await constructNowOptions();\n    expect(nowOptions).toEqual([\n      `${os.homedir()}/.meteor-now/build`,\n      ['-e', 'PORT=3000'],\n      ['-e', 'MAGIC_VAR=magical'],\n      ['-e', 'ROOT_URL=http://localhost:3000'],\n      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],\n      ['-e', 'METEOR_SETTINGS=\\'\\'{ \"foo\": \"bar\" }\\'\\''], [],\n    ]);\n  });\n});\n"
  },
  {
    "path": "src/api/args.js",
    "content": "import yargs from 'yargs';\nimport logger from './logger';\nimport { ignoreVarsArray, ignoreOptionsArray } from './constants';\n\n// returns all args as an yargs object\nexport const getArgs = (argv = process.argv) => yargs(argv).argv;\n\n// returns the arg based on name\nexport const getArg = (argName, argv = process.argv) => getArgs(argv)[argName];\n\n// returns list of environment variables (-e flag)\n// as an array of env objects [{ name: 'MONGO_URL', value: 'mongodb...' }]\nexport const getEnvironmentVariables = () => {\n  const args = getArg('e');\n  if (!args) return null;\n  const argsArray = args instanceof Array ? args : [args];\n  return argsArray.map((e) => {\n    const envArray = e.split('=');\n    let val = envArray[1];\n    if (envArray.length > 2) {\n      val = ['\"', envArray.slice(1, envArray.length).join('='), '\"'].join('');\n    }\n    return {\n      name: envArray[0],\n      value: val,\n    };\n  });\n};\n\n// return a single env object\nexport const getEnvironmentVariable = (name, args = getEnvironmentVariables()) => {\n  if (!args) {\n    return null;\n  }\n  const variable = args.find(e => e.name === name);\n  return variable ? variable.value : null;\n};\n\n// get all variables except for MONGO_URL, ROOT_URL, METEOR_SETTINGS and PORT\n// this is in case user passed additional environment variables to their app\n// those would be passed down to the now cli command\nexport const getRemainingVariables = (environmentVariables = getEnvironmentVariables()) => {\n  if (!environmentVariables) {\n    return [];\n  }\n  // filter our vars we already handled and return an array\n  // where first value is the flag -e and second is the ENV=VALUE\n  return environmentVariables\n    .filter(v => ignoreVarsArray.indexOf(v.name) === -1)\n    .map(v => ['-e', `${v.name}=${v.value}`]);\n};\n\n// get remaining options that user has passsed to meteor-now\nexport const getRemainingOptions = () => {\n  const args = getArgs();\n  logger.debug('meteor-now args', args); /* eslint-disable no-console */\n  return (\n    Object.entries(args)\n      // filter out specified list of options\n      .filter(arg => ignoreOptionsArray.indexOf(arg[0]) === -1)\n      // filter out all environment variables\n      .filter(arg => arg[0] !== 'e')\n      // check if flag is of boolean type and just return flag name\n      // yargs sets true if only flag was present without value\n      .map((arg) => {\n        if (typeof arg[1] === 'boolean') {\n          return [arg[0]];\n        }\n        return [arg[0], arg[1]];\n      })\n      // prefix flag names with either a single dash (-) or double (--) dash\n      .map((arg) => {\n        const argWithPrefix = [...arg];\n        if (arg[0].length > 1) {\n          argWithPrefix[0] = `--${arg[0]}`;\n        } else {\n          argWithPrefix[0] = `-${arg[0]}`;\n        }\n        return argWithPrefix;\n      })\n  );\n};\n\n// eslint-disable-next-line\nexport const flattenOptions = options => [].concat.apply([], options);\n\n// returns true if use passed in -d flag otherwise false\nexport const isDebug = () => !!getArg('d');\n"
  },
  {
    "path": "src/api/constants.js",
    "content": "import os from 'os';\n\nconst isWin = /^win/.test(process.platform);\n\nexport const getFolderName = (path, isWinOverride = isWin) => {\n  const pathDelimiter = isWinOverride ? '\\\\' : '/';\n  const pathParts = path.split(pathDelimiter);\n  return pathParts[pathParts.length - 1];\n};\n\n// run immediately\nexport const projectName = (() => getFolderName(process.cwd()))();\nexport const homePath = os.homedir();\nexport const meteorNowBuildPath = isWin ? `${homePath}\\\\.meteor-now\\\\build` : `${homePath}/.meteor-now/build`;\nexport const tarFileName = `${projectName}.tar.gz`;\nexport const ignoreVarsArray = ['MONGO_URL', 'ROOT_URL', 'METEOR_SETTINGS', 'PORT'];\nexport const ignoreOptionsArray = ['deps', '_', '$0', 'help', 'version', 'nosplit', 'alias'];\nexport const logPrefix = '[METEOR-NOW] - ';\n"
  },
  {
    "path": "src/api/docker.js",
    "content": "import { writeFile } from './files';\nimport logger from './logger';\nimport { meteorNowBuildPath, projectName } from './constants';\nimport { getMicroVersion } from './meteor';\nimport { getEnvironmentVariable, getArg } from './args';\nimport { getEnvVarFromNowJson } from './now';\n\n// get docker image version\nexport const getDockerImage = async () => {\n  const dockerImage = getArg('docker-image');\n  if (dockerImage) {\n    return dockerImage;\n  }\n  const version = await getMicroVersion();\n  if (version < 4) {\n    return 'nodesource/jessie:0.10.43';\n  } else if (version < 7) {\n    return 'node:8.9.4';\n  }\n  return 'node:8.11.2';\n};\n\n// check if mongo url was passed as a env var\nexport const shouldIncludeMongo = async () => {\n  const mongoUrlPassedAsParam = getEnvironmentVariable('MONGO_URL');\n  const mongoUrlFromNowJson = await getEnvVarFromNowJson('MONGO_URL');\n  if (mongoUrlPassedAsParam || mongoUrlFromNowJson) {\n    return false;\n  }\n  return true;\n};\n\n// get the value of --deps flag\nexport const getDeps = () => getArg('deps');\n\n// construct the apt-get deps lines for the Dockerfile\nexport const getDependencyInstallScripts = (deps = getDeps('deps')) => {\n  if (!deps) {\n    return '';\n  }\n  const delimiter = deps.includes(',') ? ',' : ' ';\n  return deps\n    .split(delimiter)\n    .reduce(\n      (accumulator, currentValue) =>\n        `${accumulator}RUN apt-get install ${currentValue}\\n`,\n      '',\n    );\n};\n\n// construct the Dockerfile contents\nexport const getDockerfileContents = async () => {\n  // check if user pass any --deps to install in the image\n  const deps = getDeps();\n  // get approriate docker image vesion\n  const dockerImage = await getDockerImage();\n  // check to see if mogno should be included\n  const includeMongo = await shouldIncludeMongo();\n  return `FROM ${dockerImage}\n${deps ? getDependencyInstallScripts(deps) : ''}\n${includeMongo\n    ? `RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5\nRUN echo \"deb http://repo.mongodb.org/apt/debian jessie/mongodb-org/3.6 main\" | tee /etc/apt/sources.list.d/mongodb-org-3.6.list\nRUN apt-get update\nRUN apt-get install -y mongodb-org\nRUN apt-get install -y supervisor\nVOLUME [\"/data/db\"]`\n    : ''}\nLABEL name=\"${projectName}\"\nCOPY bundle/programs/server/package.json /usr/src/app/bundle/programs/server/package.json\nWORKDIR /usr/src/app/bundle/programs/server\nRUN npm install\nWORKDIR ../../..\nCOPY . .\n${!getArg('nosplit') ? 'RUN cat *sf-part* > bundle.tar.gz' : ''}\nRUN tar -xzf bundle.tar.gz\n${includeMongo ? 'COPY supervisord.conf /etc/supervisor/supervisord.conf' : ''}\nWORKDIR bundle\nEXPOSE 3000\n${includeMongo ? 'CMD [\"supervisord\"]' : 'CMD [\"node\", \"main.js\"]'}`;\n};\n\n// construct the supervisord contents\nexport const getSupervisordFileContents = () => `[supervisord]\nnodaemon=true\nloglevel=debug\n[program:mongo]\ncommand=mongod\n[program:node]\ncommand=node \"/usr/src/app/bundle/main.js\"`;\n\n// prepares all docker related files\nexport const prepareDockerConfig = async () => {\n  try {\n    logger.info('Preparing build');\n    const dockerfileContents = await getDockerfileContents();\n    await writeFile(`${meteorNowBuildPath}/Dockerfile`, dockerfileContents);\n\n    // if user did not pass MONGO_URL\n    if (await shouldIncludeMongo()) {\n      logger.warn(\n        'WARNING: Did not pass a MONGO_URL. Bundling a NON-PRODUCTION version of MongoDB with your application. Read about the limitations here: https://git.io/vM72E',\n      );\n      logger.warn(\n        'WARNING: It might take a few minutes for the app to connect to the bundled MongoDB instance after the deployment has completed.',\n      );\n      logger.debug('creating supervisord.conf');\n      // create a supervisord.conf file to run mongodb inside the container\n      await writeFile(\n        `${meteorNowBuildPath}/supervisord.conf`,\n        getSupervisordFileContents(),\n      );\n    }\n    logger.succeed();\n  } catch (e) {\n    // eslint-disable-next-line\n    logger.error(e);\n  }\n};\n"
  },
  {
    "path": "src/api/files.js",
    "content": "import fs from 'file-system';\nimport splitFile from 'split-file';\nimport del from 'del';\nimport tar from 'tar';\nimport logger from './logger';\nimport { meteorNowBuildPath, tarFileName } from './constants';\nimport { getArg } from './args';\n\nconst encoding = 'utf8';\n\nexport const readFile = path => fs.readFileSync(path, encoding);\nexport const writeFile = (path, data) => fs.writeFileSync(path, data, encoding);\nexport const deletePath = path => del(path, { force: true });\nexport const renameFile = (oldPath, newPath) => fs.renameSync(oldPath, newPath);\n\n// split meteor bundle into pieces\nexport const prepareBundle = async () => {\n  const bundlePath = `${meteorNowBuildPath}/${tarFileName}`;\n  await tar.x({\n    file: bundlePath,\n    cwd: meteorNowBuildPath,\n  }, ['bundle/programs/server/package.json']);\n\n  try {\n    if (getArg('nosplit')) {\n      renameFile(bundlePath, `${meteorNowBuildPath}/bundle.tar.gz`);\n    } else {\n      logger.debug('splitting bundle');\n      await splitFile.splitFileBySize(\n        `${meteorNowBuildPath}/${tarFileName}`,\n        999999,\n      );\n      await deletePath(bundlePath);\n    }\n  } catch (e) {\n    logger.error(e);\n  }\n};\n\nexport const clearBuildFolder = () => {\n  logger.debug('clearing build folder');\n  return deletePath(meteorNowBuildPath);\n};\n"
  },
  {
    "path": "src/api/logger.js",
    "content": "import colors from 'colors';\nimport { logPrefix } from './constants';\nimport { isDebug } from './args';\nimport spinner from './spinner';\n\nexport default class Logger {\n  static info(message, ...rest) {\n    if (!isDebug()) {\n      this.log('start', message, rest);\n    } else {\n      this.debug(message);\n    }\n  }\n  static debug(message, ...rest) {\n    if (isDebug()) {\n      this.log('info', message, rest);\n    }\n  }\n  static warn(message, ...rest) {\n    this.log('warn', colors.yellow(message), rest);\n  }\n  static error(message, ...rest) {\n    this.log('fail', colors.red(message), rest);\n  }\n  static succeed(message, ...rest) {\n    if (!isDebug()) {\n      this.log('succeed', message, rest);\n    }\n  }\n  static log(type, message, rest) {\n    let restOfMessage;\n    if (rest.length === 0) {\n      restOfMessage = null;\n    } else if (typeof rest[0] !== 'string') {\n      restOfMessage = JSON.stringify(rest, null, 4);\n    } else {\n      restOfMessage = rest.join(' ');\n    }\n    const prefixed = `${logPrefix}${message} ${!restOfMessage\n      ? ''\n      : restOfMessage}`;\n    if (type === 'succeed') {\n      spinner.succeed();\n    } else {\n      spinner[type](prefixed);\n    }\n  }\n}\n"
  },
  {
    "path": "src/api/meteor.js",
    "content": "import spawnProcess from './process';\nimport { meteorNowBuildPath } from './constants';\nimport { readFile } from './files';\nimport { getEnvironmentVariable } from './args';\nimport logger from './logger';\n\n// get the full meteor release version\nexport const getVersion = async () => {\n  const release = await readFile('.meteor/release');\n  return release.match(/METEOR@(.*)\\r?\\n/)[1];\n};\n\n// get the minor version number of the meteor release\nexport const getMicroVersion = async () => {\n  const version = await getVersion();\n  return parseInt(version.split('.')[1], 10);\n};\n\n// check to see if server only flag should be\n// passed to meteor build\nexport const shouldBeServerOnly = async () => {\n  const version = await getMicroVersion();\n  if (version < 3) {\n    return false;\n  }\n  return true;\n};\n\n// build the meteor app by using meteor build\nexport const buildMeteorApp = async () => {\n  try {\n    logger.info('Building meteor app (this can take several minutes)');\n    const serverOnly = await shouldBeServerOnly();\n    await spawnProcess('meteor', [\n      'build',\n      meteorNowBuildPath,\n      serverOnly ? '--server-only' : '',\n      '--architecture=os.linux.x86_64',\n    ]);\n    logger.succeed();\n  } catch (e) {\n    // eslint-disable-next-line\n    logger.error(e);\n  }\n};\n\n// get meteor settings by checking for settings.json files\n// uses NODE_ENV to determine which settings file to load\nexport const getMeteorSettings = async () => {\n  const nodeEnv = getEnvironmentVariable('NODE_ENV');\n  if (nodeEnv) {\n    const settingsFilePath = `${nodeEnv}.settings.json`;\n    try {\n      const settingsFile = await readFile(settingsFilePath);\n      return settingsFile.replace(/\\r?\\n|\\r/g, '');\n    } catch (e) {\n      return null;\n    }\n  }\n  return null;\n};\n"
  },
  {
    "path": "src/api/now.js",
    "content": "import spawnProcess from './process';\nimport {\n  getEnvironmentVariable,\n  getEnvironmentVariables,\n  getRemainingOptions,\n  getRemainingVariables,\n  flattenOptions,\n  isDebug,\n  getArg,\n} from './args';\nimport { getMeteorSettings } from './meteor';\nimport { meteorNowBuildPath } from './constants';\nimport logger from './logger';\nimport { readFile, writeFile } from './files';\n\nexport const getNowJson = async () => {\n  let nowJsonString;\n  try {\n    nowJsonString = await readFile('./now.json');\n  } catch (e) {\n    return null;\n  }\n  if (!nowJsonString) {\n    return null;\n  }\n  return JSON.parse(nowJsonString);\n};\n\nexport const getEnvVarFromNowJson = async (name) => {\n  const nowJson = await getNowJson();\n  if (!nowJson || !nowJson.env || !nowJson.env[name]) {\n    return null;\n  }\n  return nowJson.env[name];\n};\n\n// construct an array of options to be passed to the now command\nexport const constructNowOptions = async () => {\n  // get list of all environment variables user passed with the -e flag\n  const environmentVariables = await getEnvironmentVariables();\n  // construct the ROOT_URL variable\n  const rootUrl =\n    getEnvironmentVariable('ROOT_URL', environmentVariables) ||\n    'http://localhost:3000';\n  // construct the MONGO_URL variable\n  const mongoUrl =\n    getEnvironmentVariable('MONGO_URL', environmentVariables) ||\n    'mongodb://127.0.0.1:27017';\n\n  const remainingVariables = getRemainingVariables(environmentVariables);\n\n  // options passed to the now cli tool. This array will be flattened\n  // and will eventually be a string seperated by spaces.\n  const options = [\n    meteorNowBuildPath,\n    ['-e', 'PORT=3000'],\n    ...remainingVariables,\n  ];\n\n  if (!await getEnvVarFromNowJson('ROOT_URL')) {\n    options.push(['-e', `ROOT_URL=${rootUrl}`]);\n  }\n  if (!await getEnvVarFromNowJson('MONGO_URL')) {\n    options.push(['-e', `MONGO_URL=${mongoUrl}`]);\n  }\n\n  // construct the METEOR_SETTINGS, first by checking if user passed\n  // -e METEOR_SETTINGS='{ \"foo\": \"bar\" }' option to meteor-now\n  let meteorSettings = getEnvironmentVariable(\n    'METEOR_SETTINGS',\n    environmentVariables,\n  );\n  // if not, check if still no METEOR_SETTINGS exist\n  if (!meteorSettings) {\n    // check if NODE_ENV is passed and look for production.settings.json file\n    meteorSettings = await getMeteorSettings();\n  }\n  if (meteorSettings) {\n    options.push(['-e', `METEOR_SETTINGS='${meteorSettings}'`]);\n  }\n\n  // get any remaining custom flags passed in by user\n  const remainingOptions = getRemainingOptions();\n\n  if (remainingOptions) {\n    options.push(flattenOptions(remainingOptions));\n  }\n\n  return options;\n};\n\nexport const prepareNowJson = async () => {\n  const nowJson = await getNowJson();\n  logger.debug('now.json', nowJson);\n  await writeFile(\n    `${meteorNowBuildPath}/now.json`,\n    JSON.stringify(\n      Object.assign(\n        {\n          features: {\n            cloud: 'v1',\n          },\n        },\n        nowJson,\n      ),\n    ),\n  );\n};\n\n// deploy app with correct options\nexport const deploy = async () => {\n  try {\n    logger.info('Deploying build (this can take several minutes)');\n    const nowOptions = await constructNowOptions();\n    // spawn child process to execute now command. Flatten nowOptions\n    // in order to properly pass all the options to now\n    const deploymentUrl = await spawnProcess('now', flattenOptions(nowOptions));\n\n    logger.succeed();\n    if (!isDebug()) {\n      logger.info(`App url is ${deploymentUrl}`);\n      logger.succeed();\n    }\n    return deploymentUrl;\n  } catch (e) {\n    logger.error('Something went wrong with now', e);\n    return null;\n  }\n};\n\n// alias an app\nexport const alias = async (deploymentUrl) => {\n  try {\n    const aliasDomain = getArg('alias');\n    if (deploymentUrl && aliasDomain) {\n      logger.info('Aliasing deployment to', aliasDomain);\n      spawnProcess('now', ['alias', deploymentUrl, aliasDomain]);\n      logger.succeed();\n    }\n  } catch (e) {\n    logger.error('now cli process threw an error', e);\n  }\n};\n"
  },
  {
    "path": "src/api/process.js",
    "content": "import { spawn } from 'child_process';\nimport { isDebug } from './args';\nimport logger from './logger';\n\nexport default (cmd, args) => {\n  if (isDebug()) {\n    logger.debug(`$ ${cmd}`, ...args);\n  }\n\n  return new Promise((resolve, reject) => {\n    const child = spawn(cmd, args, {\n      stdio: isDebug() ? 'inherit' : [process.stdin, 'pipe', process.stderr],\n      shell: true,\n    });\n    let processData = '';\n\n    child.on('exit', (code, signal) => {\n      if (code !== 0) {\n        reject({ code, signal });\n      } else {\n        resolve(processData);\n      }\n    });\n\n    if (child.stdout) {\n      child.stdout.on('data', (data) => {\n        processData = data.toString();\n      });\n    }\n  });\n};\n"
  },
  {
    "path": "src/api/spinner.js",
    "content": "import Ora from 'ora';\nimport cliSpinners from 'cli-spinners';\n\nclass Spinner extends Ora {\n  constructor() {\n    super();\n    this.spinner = cliSpinners.dots12;\n  }\n}\n\nconst spinner = new Spinner();\n\nexport default spinner;\n"
  },
  {
    "path": "src/index.js",
    "content": "import { buildMeteorApp } from './api/meteor';\nimport { prepareDockerConfig } from './api/docker';\nimport { clearBuildFolder, prepareBundle } from './api/files';\nimport { alias, deploy, prepareNowJson } from './api/now';\n\nconst main = async () => {\n  await clearBuildFolder();\n  await buildMeteorApp();\n  await prepareDockerConfig();\n  await prepareBundle();\n  await prepareNowJson();\n  await deploy();\n  const deploymentUrl = await deploy();\n  alias(deploymentUrl);\n};\n\nmain();\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require('path');\nconst webpack = require('webpack');\nconst nodeExternals = require('webpack-node-externals');\n\nmodule.exports = {\n  entry: './src/index.js',\n  output: {\n    path: path.resolve(__dirname, 'build'),\n    filename: 'main.js',\n  },\n  target: 'node', // in order to ignore built-in modules like path, fs, etc.\n  externals: [nodeExternals()], // in order to ignore all modules in node_modules folder\n  plugins: [\n    new webpack.BannerPlugin({ banner: '#!/usr/bin/env node', raw: true }),\n  ],\n  module: {\n    loaders: [\n      { test: /\\.js$/, loader: 'babel-loader', exclude: /node_modules/ },\n    ],\n  },\n};\n"
  }
]