Full Code of jkrup/meteor-now for AI

master 76bd127faa7b cached
26 files
40.5 KB
11.1k tokens
9 symbols
1 requests
Download .txt
Repository: jkrup/meteor-now
Branch: master
Commit: 76bd127faa7b
Files: 26
Total size: 40.5 KB

Directory structure:
gitextract_b8651m1o/

├── .babelrc
├── .circleci/
│   └── config.yml
├── .eslintrc
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── Bug_report.md
│       └── Feature_request.md
├── .gitignore
├── .npmignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── api/
│   │   ├── __tests__/
│   │   │   ├── .eslintrc
│   │   │   ├── args.js
│   │   │   ├── constants.js
│   │   │   └── now.js
│   │   ├── args.js
│   │   ├── constants.js
│   │   ├── docker.js
│   │   ├── files.js
│   │   ├── logger.js
│   │   ├── meteor.js
│   │   ├── now.js
│   │   ├── process.js
│   │   └── spinner.js
│   └── index.js
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [
    ["env", {
      "targets": {
        "node": "6"
      }
    }]
  ],
}


================================================
FILE: .circleci/config.yml
================================================
version: 2
jobs:
  build:
    working_directory: ~/meteor-now
    docker:
      - image: circleci/node:7
    steps:
      - checkout
      - run: npm install
      - run: npm test


================================================
FILE: .eslintrc
================================================
{
  "extends": "airbnb",
  "env": {
    "node": true,
    "es6": true,
  },
  "rules": {
    "jsx-a11y/href-no-hash": 0,
  },
}


================================================
FILE: .github/ISSUE_TEMPLATE/Bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Browser [e.g. stock browser, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/Feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .gitignore
================================================
node_modules
npm*
build
.DS_Store
.nvmrc


================================================
FILE: .npmignore
================================================
src
assets


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In 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.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project 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.

Project 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.

## Scope

This 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.

## Enforcement

Instances 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.

Project 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.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Mirza Joldic and Justin Krup

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
## Deprecation notice:

[**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)

---


# 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)

`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`.

<p align="center">
  <img src="https://github.com/jkrup/meteor-now/raw/master/assets/meteor-now-intro.gif">
</p>

## Demo
[https://meteor-test-msrbsvslpz.now.sh](https://meteor-test-msrbsvslpz.now.sh)

# Install
Install the `now` and `meteor-now` packages:
```
$ npm install -g now meteor-now
```

Create `now` account
```
$ now login
> Enter your email: <your email>
> Please follow the link sent to <your email> to log in.
> Verify that the provided security code in the email matches Pragmatic Manta Ray.

✔ Confirmed email address!

> Logged in successfully. Token saved in ~/.now.json
```

# Usage

## Deploying for Development / Testing

In your Meteor app directory, run `meteor-now`.

```
~/my-meteor-app/ $ meteor-now
✔ [METEOR-NOW] - building meteor app
✔ [METEOR-NOW] - preparing build
✔ [METEOR-NOW] - deploying build
✔ [METEOR-NOW] - meteor app deployed to https://meteor-test-msrbsvslpz.now.sh
```

## Deploying for Production

There 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:

```
meteor-now -e MONGO_URL=mongodb://<username>:<pass>@.... -e ROOT_URL=https://mydomain.com -e NODE_ENV=production
```

If 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).

You 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.

You 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)

Finally 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.

# How
![METEOR-NOW](assets/zeit-meteor.png "METEOR-NOW")
`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.

# Additional Info
## Full deploy with MongoDB
`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.

Some 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.

To not have this issue, spin up your own MongoDB instance and pass the `-e MONGO_URL=...` flag when you deploy.
## Using METEOR_SETTINGS
Currently there are two ways you can set the METEOR_SETTINGS environment variable in your deployments

- Using `now secrets`

```
$ now secrets add meteor-settings '{ "public": { "foo": "bar" }}'
$ meteor-now -e METEOR_SETTINGS=@meteor-settings -e MONGO_URL=...
```

- Using `development.settings.json` and `production.settings.json`
Following the convention of the `NODE_ENV` environment variable, `meteor-now` uses `NODE_ENV` to determine which file to look for.
It will either look for `development.settings.json` or `production.settings.json` in your root Meteor directory.

Note that `meteor-now` by default looks for `development.settings.json` unless otherwise specified by `-e NODE_ENV=production` or `-e METEOR_SETTINGS='{ "foo": "bar" }'`.

## Debug
In order to see detailed deployment logs as they happen, pass the `-d` when you deploy.

## Bundle Splitting
The `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`.

## now.json
You 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.

## FAQ
### Can I use this in production?
Yes– 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
Also read the caveats below if you did not specify a MONGO_URL

### What happens when I don't specify a MONGO_URL
When 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.

### How can I change my ROOT_URL
In 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`

### How do I set a domain name
In order to use a custom domain name, you would need a Pro account with now.

Run `now domain add --external meteor-now.com`. You should get a response back with steps to verify your domain.

```
Verification required: Please add the following TXT record on the external DNS server: _now.meteor-now.com: ea39a62a58b3109f92024230826e37f0adc6abcd
```

In your domain DNS settings, add a TXT record with the above information.

Wait a few moments for the DNS records to propagate and rerun the same command above.
```
$ now domains add --external meteor-now.com
Success! Domain meteor-now.com verified [2s]
```

Now alias your deployment to the new domain
```
$ now alias https://meteor-now-site-izdolpdrvv.now.sh/ www.meteor-now.com
www.meteor-now.com is a custom domain.
Verifying the DNS settings for www.meteor-now.com (see https://zeit.world for help)
Verification OK!
Provisioning certificate for www.meteor-now.com
Success! Alias created: https://www.meteor-now.com now points to https://meteor-now-site-izdolpdrvv.now.sh [copied to clipboard]
```

You can also pass the `--alias abc.com` option to `meteor-now` and it will automatically run the alias after the deployment completes. 

Read [this blog post](https://zeit.co/blog/now-alias) for more information.

### Why are my XX resource not loading
Because 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.

### I deployed my free app but it's failing to connect to MongoDB
If 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.

### I want to use a different docker image
The 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.
If you want to use a different image, use the `--docker-image` flag.

### My app requires XX can I use my own Dockerfile?
We'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.

Stay tuned to updates on the [issue](https://github.com/jkrup/meteor-now/issues/6)

# Authors
<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>
---|---
Justin Krup|Mirza Joldic
[@jkrup](https://www.github.com/jkrup)|[@purplecones](https://www.github.com/purplecones)


================================================
FILE: package.json
================================================
{
  "name": "meteor-now",
  "version": "0.8.0",
  "description": "Deploy meteor apps with one command using now.sh (https://zeit.co/now)",
  "repository": "https://github.com/jkrup/meteor-now",
  "author": "Justin Krup",
  "contributors": [
    {
      "name": "Mirza Joldic",
      "url": "https://github.com/purplecones"
    }
  ],
  "license": "MIT",
  "preferGlobal": true,
  "bin": "./build/main.js",
  "scripts": {
    "build": "webpack",
    "build:watch": "webpack --watch",
    "lint": "eslint src",
    "prepublishOnly": "npm run build",
    "test": "npm run lint && jest",
    "test:watch": "jest --watch"
  },
  "dependencies": {
    "cli-spinners": "^1.0.1",
    "colors": "^1.1.2",
    "del": "^3.0.0",
    "file-system": "^2.2.2",
    "ora": "^1.3.0",
    "prettier": "^1.7.4",
    "split-file": "^2.1.0",
    "tar": "^4.4.1",
    "yargs": "^9.0.1"
  },
  "devDependencies": {
    "babel-jest": "^21.2.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.1",
    "eslint": "^4.7.2",
    "eslint-config-airbnb": "^15.1.0",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-jsx-a11y": "^6.0.2",
    "eslint-plugin-react": "^7.4.0",
    "jest": "^21.2.1",
    "webpack": "^3.8.1",
    "webpack-node-externals": "^1.6.0"
  }
}


================================================
FILE: src/api/__tests__/.eslintrc
================================================
{
  "env": {
    "jest": true
  }
}


================================================
FILE: src/api/__tests__/args.js
================================================
import {
  getArg,
  getArgs,
  getEnvironmentVariables,
  getEnvironmentVariable,
  getRemainingVariables,
  getRemainingOptions,
  flattenOptions,
} from '../args';

describe('args test', () => {
  test('it should return correct arg', () => {
    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick'];
    const arg = getArg('deps');
    expect(arg).toBe('imagemagick');
  });

  test('it should return all default args', () => {
    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '--alias', 'abc.com'];
    const args = getArgs();
    expect(args).toEqual({
      $0: '-e',
      _: ['node'],
      help: false,
      version: false,
      deps: 'imagemagick',
      e: 'MONGO_URL=mongodb://127.0.0.1:27017',
      alias: 'abc.com',
    });
  });

  test('it should return all args when passed an argv', () => {
    const args = getArgs([
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '--deps',
      'imagemagick',
      '--public',
      '--alias',
      'abc.com',
    ]);
    expect(args).toEqual({
      $0: '-e',
      _: ['node'],
      help: false,
      version: false,
      deps: 'imagemagick',
      e: 'MONGO_URL=mongodb://127.0.0.1:27017',
      public: true,
      alias: 'abc.com',
    });
  });

  test('it should return correct number of environment variables', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '--deps',
      'abc',
    ];
    const environmentVariables = getEnvironmentVariables();
    expect(environmentVariables.length).toBe(2);
  });

  test('it should return all environment variables', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '--deps',
      'abc',
    ];
    const environmentVariables = getEnvironmentVariables();
    expect(environmentVariables).toEqual([
      { name: 'MONGO_URL', value: 'mongodb://127.0.0.1:27017' },
      { name: 'ROOT_URL', value: 'http://localhost:3000' },
    ]);
  });

  test('it should return a environment variable by name', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '--deps',
      'abc',
    ];
    const environmentVariables = getEnvironmentVariable('ROOT_URL');
    expect(environmentVariables).toBe('http://localhost:3000');
  });

  test('it should return all custom environment variables', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '-e',
      'MY_SPECIAL_VAR=cats',
      '-e',
      'SECRET=litter',
      '--deps',
      'abc',
    ];
    const remainingVariables = getRemainingVariables();
    expect(remainingVariables).toEqual([['-e', 'MY_SPECIAL_VAR=cats'], ['-e', 'SECRET=litter']]);
  });

  test('it should return all custom options', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '-e',
      'MY_SPECIAL_VAR=cats',
      '-e',
      'SECRET=litter',
      '--deps',
      'abc',
      '--public',
      '--alias',
      'abc.com',
    ];
    const remainingOptions = getRemainingOptions();
    expect(remainingOptions).toEqual([['--public']]);
  });

  test('it should correctly flatten options', () => {
    const flattenedOptions = flattenOptions([
      '--public',
      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],
      ['--alias', 'abc.com'],
    ]);
    expect(flattenedOptions).toEqual([
      '--public',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '--alias',
      'abc.com',
    ]);
  });

  test('it should ignore certain flags', () => {
    process.argv = [
      'node',
      '-e',
      'MONGO_URL=mongodb://127.0.0.1:27017',
      '-e',
      'ROOT_URL=http://localhost:3000',
      '-e',
      'MY_SPECIAL_VAR=cats',
      '-e',
      'SECRET=litter',
      '--deps',
      'abc',
      '--nosplit',
      '--alias',
      'abc.com',
    ];
    const remainingVariables = getRemainingVariables();
    expect(remainingVariables).toEqual([['-e', 'MY_SPECIAL_VAR=cats'], ['-e', 'SECRET=litter']]);
  });
});


================================================
FILE: src/api/__tests__/constants.js
================================================
import { getFolderName } from '../constants';

describe('constants test', () => {
  test('it should correctly get the projects name (mac)', async () => {
    const folderName = getFolderName('/some/fake/path/to/a/folder', false);
    expect(folderName).toBe('folder');
  });

  test('it should correctly get the projects name (win)', async () => {
    const folderName = getFolderName('\\some\\fake\\path\\to\\a\\folder', true);
    expect(folderName).toBe('folder');
  });
});


================================================
FILE: src/api/__tests__/now.js
================================================
import os from 'os';
import { constructNowOptions } from '../now';

describe('now test', () => {
  test('it should construct the correct now options', async () => {
    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '-e', 'METEOR_SETTINGS=\'{ "foo": "bar" }\'', '--alias', 'abc.com'];
    const nowOptions = await constructNowOptions();
    expect(nowOptions).toEqual([
      `${os.homedir()}/.meteor-now/build`,
      ['-e', 'PORT=3000'],
      ['-e', 'ROOT_URL=http://localhost:3000'],
      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],
      ['-e', 'METEOR_SETTINGS=\'\'{ "foo": "bar" }\'\''], [],
    ]);
  });

  test('it should construct the correct environment variables', async () => {
    process.argv = ['node', '-e', 'MONGO_URL=mongodb://127.0.0.1:27017', '--deps', 'imagemagick', '-e', 'METEOR_SETTINGS=\'{ "foo": "bar" }\'', '-e', 'MAGIC_VAR=magical'];
    const nowOptions = await constructNowOptions();
    expect(nowOptions).toEqual([
      `${os.homedir()}/.meteor-now/build`,
      ['-e', 'PORT=3000'],
      ['-e', 'MAGIC_VAR=magical'],
      ['-e', 'ROOT_URL=http://localhost:3000'],
      ['-e', 'MONGO_URL=mongodb://127.0.0.1:27017'],
      ['-e', 'METEOR_SETTINGS=\'\'{ "foo": "bar" }\'\''], [],
    ]);
  });
});


================================================
FILE: src/api/args.js
================================================
import yargs from 'yargs';
import logger from './logger';
import { ignoreVarsArray, ignoreOptionsArray } from './constants';

// returns all args as an yargs object
export const getArgs = (argv = process.argv) => yargs(argv).argv;

// returns the arg based on name
export const getArg = (argName, argv = process.argv) => getArgs(argv)[argName];

// returns list of environment variables (-e flag)
// as an array of env objects [{ name: 'MONGO_URL', value: 'mongodb...' }]
export const getEnvironmentVariables = () => {
  const args = getArg('e');
  if (!args) return null;
  const argsArray = args instanceof Array ? args : [args];
  return argsArray.map((e) => {
    const envArray = e.split('=');
    let val = envArray[1];
    if (envArray.length > 2) {
      val = ['"', envArray.slice(1, envArray.length).join('='), '"'].join('');
    }
    return {
      name: envArray[0],
      value: val,
    };
  });
};

// return a single env object
export const getEnvironmentVariable = (name, args = getEnvironmentVariables()) => {
  if (!args) {
    return null;
  }
  const variable = args.find(e => e.name === name);
  return variable ? variable.value : null;
};

// get all variables except for MONGO_URL, ROOT_URL, METEOR_SETTINGS and PORT
// this is in case user passed additional environment variables to their app
// those would be passed down to the now cli command
export const getRemainingVariables = (environmentVariables = getEnvironmentVariables()) => {
  if (!environmentVariables) {
    return [];
  }
  // filter our vars we already handled and return an array
  // where first value is the flag -e and second is the ENV=VALUE
  return environmentVariables
    .filter(v => ignoreVarsArray.indexOf(v.name) === -1)
    .map(v => ['-e', `${v.name}=${v.value}`]);
};

// get remaining options that user has passsed to meteor-now
export const getRemainingOptions = () => {
  const args = getArgs();
  logger.debug('meteor-now args', args); /* eslint-disable no-console */
  return (
    Object.entries(args)
      // filter out specified list of options
      .filter(arg => ignoreOptionsArray.indexOf(arg[0]) === -1)
      // filter out all environment variables
      .filter(arg => arg[0] !== 'e')
      // check if flag is of boolean type and just return flag name
      // yargs sets true if only flag was present without value
      .map((arg) => {
        if (typeof arg[1] === 'boolean') {
          return [arg[0]];
        }
        return [arg[0], arg[1]];
      })
      // prefix flag names with either a single dash (-) or double (--) dash
      .map((arg) => {
        const argWithPrefix = [...arg];
        if (arg[0].length > 1) {
          argWithPrefix[0] = `--${arg[0]}`;
        } else {
          argWithPrefix[0] = `-${arg[0]}`;
        }
        return argWithPrefix;
      })
  );
};

// eslint-disable-next-line
export const flattenOptions = options => [].concat.apply([], options);

// returns true if use passed in -d flag otherwise false
export const isDebug = () => !!getArg('d');


================================================
FILE: src/api/constants.js
================================================
import os from 'os';

const isWin = /^win/.test(process.platform);

export const getFolderName = (path, isWinOverride = isWin) => {
  const pathDelimiter = isWinOverride ? '\\' : '/';
  const pathParts = path.split(pathDelimiter);
  return pathParts[pathParts.length - 1];
};

// run immediately
export const projectName = (() => getFolderName(process.cwd()))();
export const homePath = os.homedir();
export const meteorNowBuildPath = isWin ? `${homePath}\\.meteor-now\\build` : `${homePath}/.meteor-now/build`;
export const tarFileName = `${projectName}.tar.gz`;
export const ignoreVarsArray = ['MONGO_URL', 'ROOT_URL', 'METEOR_SETTINGS', 'PORT'];
export const ignoreOptionsArray = ['deps', '_', '$0', 'help', 'version', 'nosplit', 'alias'];
export const logPrefix = '[METEOR-NOW] - ';


================================================
FILE: src/api/docker.js
================================================
import { writeFile } from './files';
import logger from './logger';
import { meteorNowBuildPath, projectName } from './constants';
import { getMicroVersion } from './meteor';
import { getEnvironmentVariable, getArg } from './args';
import { getEnvVarFromNowJson } from './now';

// get docker image version
export const getDockerImage = async () => {
  const dockerImage = getArg('docker-image');
  if (dockerImage) {
    return dockerImage;
  }
  const version = await getMicroVersion();
  if (version < 4) {
    return 'nodesource/jessie:0.10.43';
  } else if (version < 7) {
    return 'node:8.9.4';
  }
  return 'node:8.11.2';
};

// check if mongo url was passed as a env var
export const shouldIncludeMongo = async () => {
  const mongoUrlPassedAsParam = getEnvironmentVariable('MONGO_URL');
  const mongoUrlFromNowJson = await getEnvVarFromNowJson('MONGO_URL');
  if (mongoUrlPassedAsParam || mongoUrlFromNowJson) {
    return false;
  }
  return true;
};

// get the value of --deps flag
export const getDeps = () => getArg('deps');

// construct the apt-get deps lines for the Dockerfile
export const getDependencyInstallScripts = (deps = getDeps('deps')) => {
  if (!deps) {
    return '';
  }
  const delimiter = deps.includes(',') ? ',' : ' ';
  return deps
    .split(delimiter)
    .reduce(
      (accumulator, currentValue) =>
        `${accumulator}RUN apt-get install ${currentValue}\n`,
      '',
    );
};

// construct the Dockerfile contents
export const getDockerfileContents = async () => {
  // check if user pass any --deps to install in the image
  const deps = getDeps();
  // get approriate docker image vesion
  const dockerImage = await getDockerImage();
  // check to see if mogno should be included
  const includeMongo = await shouldIncludeMongo();
  return `FROM ${dockerImage}
${deps ? getDependencyInstallScripts(deps) : ''}
${includeMongo
    ? `RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
RUN 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
RUN apt-get update
RUN apt-get install -y mongodb-org
RUN apt-get install -y supervisor
VOLUME ["/data/db"]`
    : ''}
LABEL name="${projectName}"
COPY bundle/programs/server/package.json /usr/src/app/bundle/programs/server/package.json
WORKDIR /usr/src/app/bundle/programs/server
RUN npm install
WORKDIR ../../..
COPY . .
${!getArg('nosplit') ? 'RUN cat *sf-part* > bundle.tar.gz' : ''}
RUN tar -xzf bundle.tar.gz
${includeMongo ? 'COPY supervisord.conf /etc/supervisor/supervisord.conf' : ''}
WORKDIR bundle
EXPOSE 3000
${includeMongo ? 'CMD ["supervisord"]' : 'CMD ["node", "main.js"]'}`;
};

// construct the supervisord contents
export const getSupervisordFileContents = () => `[supervisord]
nodaemon=true
loglevel=debug
[program:mongo]
command=mongod
[program:node]
command=node "/usr/src/app/bundle/main.js"`;

// prepares all docker related files
export const prepareDockerConfig = async () => {
  try {
    logger.info('Preparing build');
    const dockerfileContents = await getDockerfileContents();
    await writeFile(`${meteorNowBuildPath}/Dockerfile`, dockerfileContents);

    // if user did not pass MONGO_URL
    if (await shouldIncludeMongo()) {
      logger.warn(
        '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',
      );
      logger.warn(
        'WARNING: It might take a few minutes for the app to connect to the bundled MongoDB instance after the deployment has completed.',
      );
      logger.debug('creating supervisord.conf');
      // create a supervisord.conf file to run mongodb inside the container
      await writeFile(
        `${meteorNowBuildPath}/supervisord.conf`,
        getSupervisordFileContents(),
      );
    }
    logger.succeed();
  } catch (e) {
    // eslint-disable-next-line
    logger.error(e);
  }
};


================================================
FILE: src/api/files.js
================================================
import fs from 'file-system';
import splitFile from 'split-file';
import del from 'del';
import tar from 'tar';
import logger from './logger';
import { meteorNowBuildPath, tarFileName } from './constants';
import { getArg } from './args';

const encoding = 'utf8';

export const readFile = path => fs.readFileSync(path, encoding);
export const writeFile = (path, data) => fs.writeFileSync(path, data, encoding);
export const deletePath = path => del(path, { force: true });
export const renameFile = (oldPath, newPath) => fs.renameSync(oldPath, newPath);

// split meteor bundle into pieces
export const prepareBundle = async () => {
  const bundlePath = `${meteorNowBuildPath}/${tarFileName}`;
  await tar.x({
    file: bundlePath,
    cwd: meteorNowBuildPath,
  }, ['bundle/programs/server/package.json']);

  try {
    if (getArg('nosplit')) {
      renameFile(bundlePath, `${meteorNowBuildPath}/bundle.tar.gz`);
    } else {
      logger.debug('splitting bundle');
      await splitFile.splitFileBySize(
        `${meteorNowBuildPath}/${tarFileName}`,
        999999,
      );
      await deletePath(bundlePath);
    }
  } catch (e) {
    logger.error(e);
  }
};

export const clearBuildFolder = () => {
  logger.debug('clearing build folder');
  return deletePath(meteorNowBuildPath);
};


================================================
FILE: src/api/logger.js
================================================
import colors from 'colors';
import { logPrefix } from './constants';
import { isDebug } from './args';
import spinner from './spinner';

export default class Logger {
  static info(message, ...rest) {
    if (!isDebug()) {
      this.log('start', message, rest);
    } else {
      this.debug(message);
    }
  }
  static debug(message, ...rest) {
    if (isDebug()) {
      this.log('info', message, rest);
    }
  }
  static warn(message, ...rest) {
    this.log('warn', colors.yellow(message), rest);
  }
  static error(message, ...rest) {
    this.log('fail', colors.red(message), rest);
  }
  static succeed(message, ...rest) {
    if (!isDebug()) {
      this.log('succeed', message, rest);
    }
  }
  static log(type, message, rest) {
    let restOfMessage;
    if (rest.length === 0) {
      restOfMessage = null;
    } else if (typeof rest[0] !== 'string') {
      restOfMessage = JSON.stringify(rest, null, 4);
    } else {
      restOfMessage = rest.join(' ');
    }
    const prefixed = `${logPrefix}${message} ${!restOfMessage
      ? ''
      : restOfMessage}`;
    if (type === 'succeed') {
      spinner.succeed();
    } else {
      spinner[type](prefixed);
    }
  }
}


================================================
FILE: src/api/meteor.js
================================================
import spawnProcess from './process';
import { meteorNowBuildPath } from './constants';
import { readFile } from './files';
import { getEnvironmentVariable } from './args';
import logger from './logger';

// get the full meteor release version
export const getVersion = async () => {
  const release = await readFile('.meteor/release');
  return release.match(/METEOR@(.*)\r?\n/)[1];
};

// get the minor version number of the meteor release
export const getMicroVersion = async () => {
  const version = await getVersion();
  return parseInt(version.split('.')[1], 10);
};

// check to see if server only flag should be
// passed to meteor build
export const shouldBeServerOnly = async () => {
  const version = await getMicroVersion();
  if (version < 3) {
    return false;
  }
  return true;
};

// build the meteor app by using meteor build
export const buildMeteorApp = async () => {
  try {
    logger.info('Building meteor app (this can take several minutes)');
    const serverOnly = await shouldBeServerOnly();
    await spawnProcess('meteor', [
      'build',
      meteorNowBuildPath,
      serverOnly ? '--server-only' : '',
      '--architecture=os.linux.x86_64',
    ]);
    logger.succeed();
  } catch (e) {
    // eslint-disable-next-line
    logger.error(e);
  }
};

// get meteor settings by checking for settings.json files
// uses NODE_ENV to determine which settings file to load
export const getMeteorSettings = async () => {
  const nodeEnv = getEnvironmentVariable('NODE_ENV');
  if (nodeEnv) {
    const settingsFilePath = `${nodeEnv}.settings.json`;
    try {
      const settingsFile = await readFile(settingsFilePath);
      return settingsFile.replace(/\r?\n|\r/g, '');
    } catch (e) {
      return null;
    }
  }
  return null;
};


================================================
FILE: src/api/now.js
================================================
import spawnProcess from './process';
import {
  getEnvironmentVariable,
  getEnvironmentVariables,
  getRemainingOptions,
  getRemainingVariables,
  flattenOptions,
  isDebug,
  getArg,
} from './args';
import { getMeteorSettings } from './meteor';
import { meteorNowBuildPath } from './constants';
import logger from './logger';
import { readFile, writeFile } from './files';

export const getNowJson = async () => {
  let nowJsonString;
  try {
    nowJsonString = await readFile('./now.json');
  } catch (e) {
    return null;
  }
  if (!nowJsonString) {
    return null;
  }
  return JSON.parse(nowJsonString);
};

export const getEnvVarFromNowJson = async (name) => {
  const nowJson = await getNowJson();
  if (!nowJson || !nowJson.env || !nowJson.env[name]) {
    return null;
  }
  return nowJson.env[name];
};

// construct an array of options to be passed to the now command
export const constructNowOptions = async () => {
  // get list of all environment variables user passed with the -e flag
  const environmentVariables = await getEnvironmentVariables();
  // construct the ROOT_URL variable
  const rootUrl =
    getEnvironmentVariable('ROOT_URL', environmentVariables) ||
    'http://localhost:3000';
  // construct the MONGO_URL variable
  const mongoUrl =
    getEnvironmentVariable('MONGO_URL', environmentVariables) ||
    'mongodb://127.0.0.1:27017';

  const remainingVariables = getRemainingVariables(environmentVariables);

  // options passed to the now cli tool. This array will be flattened
  // and will eventually be a string seperated by spaces.
  const options = [
    meteorNowBuildPath,
    ['-e', 'PORT=3000'],
    ...remainingVariables,
  ];

  if (!await getEnvVarFromNowJson('ROOT_URL')) {
    options.push(['-e', `ROOT_URL=${rootUrl}`]);
  }
  if (!await getEnvVarFromNowJson('MONGO_URL')) {
    options.push(['-e', `MONGO_URL=${mongoUrl}`]);
  }

  // construct the METEOR_SETTINGS, first by checking if user passed
  // -e METEOR_SETTINGS='{ "foo": "bar" }' option to meteor-now
  let meteorSettings = getEnvironmentVariable(
    'METEOR_SETTINGS',
    environmentVariables,
  );
  // if not, check if still no METEOR_SETTINGS exist
  if (!meteorSettings) {
    // check if NODE_ENV is passed and look for production.settings.json file
    meteorSettings = await getMeteorSettings();
  }
  if (meteorSettings) {
    options.push(['-e', `METEOR_SETTINGS='${meteorSettings}'`]);
  }

  // get any remaining custom flags passed in by user
  const remainingOptions = getRemainingOptions();

  if (remainingOptions) {
    options.push(flattenOptions(remainingOptions));
  }

  return options;
};

export const prepareNowJson = async () => {
  const nowJson = await getNowJson();
  logger.debug('now.json', nowJson);
  await writeFile(
    `${meteorNowBuildPath}/now.json`,
    JSON.stringify(
      Object.assign(
        {
          features: {
            cloud: 'v1',
          },
        },
        nowJson,
      ),
    ),
  );
};

// deploy app with correct options
export const deploy = async () => {
  try {
    logger.info('Deploying build (this can take several minutes)');
    const nowOptions = await constructNowOptions();
    // spawn child process to execute now command. Flatten nowOptions
    // in order to properly pass all the options to now
    const deploymentUrl = await spawnProcess('now', flattenOptions(nowOptions));

    logger.succeed();
    if (!isDebug()) {
      logger.info(`App url is ${deploymentUrl}`);
      logger.succeed();
    }
    return deploymentUrl;
  } catch (e) {
    logger.error('Something went wrong with now', e);
    return null;
  }
};

// alias an app
export const alias = async (deploymentUrl) => {
  try {
    const aliasDomain = getArg('alias');
    if (deploymentUrl && aliasDomain) {
      logger.info('Aliasing deployment to', aliasDomain);
      spawnProcess('now', ['alias', deploymentUrl, aliasDomain]);
      logger.succeed();
    }
  } catch (e) {
    logger.error('now cli process threw an error', e);
  }
};


================================================
FILE: src/api/process.js
================================================
import { spawn } from 'child_process';
import { isDebug } from './args';
import logger from './logger';

export default (cmd, args) => {
  if (isDebug()) {
    logger.debug(`$ ${cmd}`, ...args);
  }

  return new Promise((resolve, reject) => {
    const child = spawn(cmd, args, {
      stdio: isDebug() ? 'inherit' : [process.stdin, 'pipe', process.stderr],
      shell: true,
    });
    let processData = '';

    child.on('exit', (code, signal) => {
      if (code !== 0) {
        reject({ code, signal });
      } else {
        resolve(processData);
      }
    });

    if (child.stdout) {
      child.stdout.on('data', (data) => {
        processData = data.toString();
      });
    }
  });
};


================================================
FILE: src/api/spinner.js
================================================
import Ora from 'ora';
import cliSpinners from 'cli-spinners';

class Spinner extends Ora {
  constructor() {
    super();
    this.spinner = cliSpinners.dots12;
  }
}

const spinner = new Spinner();

export default spinner;


================================================
FILE: src/index.js
================================================
import { buildMeteorApp } from './api/meteor';
import { prepareDockerConfig } from './api/docker';
import { clearBuildFolder, prepareBundle } from './api/files';
import { alias, deploy, prepareNowJson } from './api/now';

const main = async () => {
  await clearBuildFolder();
  await buildMeteorApp();
  await prepareDockerConfig();
  await prepareBundle();
  await prepareNowJson();
  await deploy();
  const deploymentUrl = await deploy();
  alias(deploymentUrl);
};

main();


================================================
FILE: webpack.config.js
================================================
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'main.js',
  },
  target: 'node', // in order to ignore built-in modules like path, fs, etc.
  externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
  plugins: [
    new webpack.BannerPlugin({ banner: '#!/usr/bin/env node', raw: true }),
  ],
  module: {
    loaders: [
      { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
    ],
  },
};
Download .txt
gitextract_b8651m1o/

├── .babelrc
├── .circleci/
│   └── config.yml
├── .eslintrc
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── Bug_report.md
│       └── Feature_request.md
├── .gitignore
├── .npmignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── api/
│   │   ├── __tests__/
│   │   │   ├── .eslintrc
│   │   │   ├── args.js
│   │   │   ├── constants.js
│   │   │   └── now.js
│   │   ├── args.js
│   │   ├── constants.js
│   │   ├── docker.js
│   │   ├── files.js
│   │   ├── logger.js
│   │   ├── meteor.js
│   │   ├── now.js
│   │   ├── process.js
│   │   └── spinner.js
│   └── index.js
└── webpack.config.js
Download .txt
SYMBOL INDEX (9 symbols across 2 files)

FILE: src/api/logger.js
  class Logger (line 6) | class Logger {
    method info (line 7) | static info(message, ...rest) {
    method debug (line 14) | static debug(message, ...rest) {
    method warn (line 19) | static warn(message, ...rest) {
    method error (line 22) | static error(message, ...rest) {
    method succeed (line 25) | static succeed(message, ...rest) {
    method log (line 30) | static log(type, message, rest) {

FILE: src/api/spinner.js
  class Spinner (line 4) | class Spinner extends Ora {
    method constructor (line 5) | constructor() {
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (44K chars).
[
  {
    "path": ".babelrc",
    "chars": 92,
    "preview": "{\n  \"presets\": [\n    [\"env\", {\n      \"targets\": {\n        \"node\": \"6\"\n      }\n    }]\n  ],\n}\n"
  },
  {
    "path": ".circleci/config.yml",
    "chars": 180,
    "preview": "version: 2\njobs:\n  build:\n    working_directory: ~/meteor-now\n    docker:\n      - image: circleci/node:7\n    steps:\n    "
  },
  {
    "path": ".eslintrc",
    "chars": 128,
    "preview": "{\n  \"extends\": \"airbnb\",\n  \"env\": {\n    \"node\": true,\n    \"es6\": true,\n  },\n  \"rules\": {\n    \"jsx-a11y/href-no-hash\": 0,"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.md",
    "chars": 834,
    "preview": "---\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 des"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "chars": 577,
    "preview": "---\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 pro"
  },
  {
    "path": ".gitignore",
    "chars": 41,
    "preview": "node_modules\nnpm*\nbuild\n.DS_Store\n.nvmrc\n"
  },
  {
    "path": ".npmignore",
    "chars": 11,
    "preview": "src\nassets\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3213,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "LICENSE",
    "chars": 1085,
    "preview": "MIT License\n\nCopyright (c) 2017 Mirza Joldic and Justin Krup\n\nPermission is hereby granted, free of charge, to any perso"
  },
  {
    "path": "README.md",
    "chars": 9773,
    "preview": "## Deprecation notice:\n\n[**NOTE: meteor-now is no longer actively maintained.** Since ZEIT Now no longer focuses on serv"
  },
  {
    "path": "package.json",
    "chars": 1256,
    "preview": "{\n  \"name\": \"meteor-now\",\n  \"version\": \"0.8.0\",\n  \"description\": \"Deploy meteor apps with one command using now.sh (http"
  },
  {
    "path": "src/api/__tests__/.eslintrc",
    "chars": 36,
    "preview": "{\n  \"env\": {\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": "src/api/__tests__/args.js",
    "chars": 4429,
    "preview": "import {\n  getArg,\n  getArgs,\n  getEnvironmentVariables,\n  getEnvironmentVariable,\n  getRemainingVariables,\n  getRemaini"
  },
  {
    "path": "src/api/__tests__/constants.js",
    "chars": 478,
    "preview": "import { getFolderName } from '../constants';\n\ndescribe('constants test', () => {\n  test('it should correctly get the pr"
  },
  {
    "path": "src/api/__tests__/now.js",
    "chars": 1286,
    "preview": "import os from 'os';\nimport { constructNowOptions } from '../now';\n\ndescribe('now test', () => {\n  test('it should const"
  },
  {
    "path": "src/api/args.js",
    "chars": 3022,
    "preview": "import yargs from 'yargs';\nimport logger from './logger';\nimport { ignoreVarsArray, ignoreOptionsArray } from './constan"
  },
  {
    "path": "src/api/constants.js",
    "chars": 787,
    "preview": "import os from 'os';\n\nconst isWin = /^win/.test(process.platform);\n\nexport const getFolderName = (path, isWinOverride = "
  },
  {
    "path": "src/api/docker.js",
    "chars": 3994,
    "preview": "import { writeFile } from './files';\nimport logger from './logger';\nimport { meteorNowBuildPath, projectName } from './c"
  },
  {
    "path": "src/api/files.js",
    "chars": 1293,
    "preview": "import fs from 'file-system';\nimport splitFile from 'split-file';\nimport del from 'del';\nimport tar from 'tar';\nimport l"
  },
  {
    "path": "src/api/logger.js",
    "chars": 1189,
    "preview": "import colors from 'colors';\nimport { logPrefix } from './constants';\nimport { isDebug } from './args';\nimport spinner f"
  },
  {
    "path": "src/api/meteor.js",
    "chars": 1765,
    "preview": "import spawnProcess from './process';\nimport { meteorNowBuildPath } from './constants';\nimport { readFile } from './file"
  },
  {
    "path": "src/api/now.js",
    "chars": 4011,
    "preview": "import spawnProcess from './process';\nimport {\n  getEnvironmentVariable,\n  getEnvironmentVariables,\n  getRemainingOption"
  },
  {
    "path": "src/api/process.js",
    "chars": 704,
    "preview": "import { spawn } from 'child_process';\nimport { isDebug } from './args';\nimport logger from './logger';\n\nexport default "
  },
  {
    "path": "src/api/spinner.js",
    "chars": 225,
    "preview": "import Ora from 'ora';\nimport cliSpinners from 'cli-spinners';\n\nclass Spinner extends Ora {\n  constructor() {\n    super("
  },
  {
    "path": "src/index.js",
    "chars": 479,
    "preview": "import { buildMeteorApp } from './api/meteor';\nimport { prepareDockerConfig } from './api/docker';\nimport { clearBuildFo"
  },
  {
    "path": "webpack.config.js",
    "chars": 632,
    "preview": "const path = require('path');\nconst webpack = require('webpack');\nconst nodeExternals = require('webpack-node-externals'"
  }
]

About this extraction

This page contains the full source code of the jkrup/meteor-now GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (40.5 KB), approximately 11.1k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!