Meteor Guideで紹介されているコマンド・パッケージ・ソースコード一覧


[Meteor Guide](http://guide.meteor.com/)で紹介されているコマンド・パッケージ・ソースコード一覧です。個人的に使わなさそうなものは除外しています。また検索性重視で1ページにまとめています。随時更新。


## :book: [Introduction](http://guide.meteor.com/)


```bash
# Meteor インストール
curl https://install.meteor.com/ | sh

# 新規 Meteor アプリ作成
meteor create myapp

# ローカルで Meteor 起動
cd myapp
meteor npm install
meteor
```

## :book: [Code Style](http://guide.meteor.com/code-style.html)

```bash
meteor npm install --save-dev eslint-config-airbnb eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-plugin-jsx-a11y eslint
```

```json:package.json
{
  ...
  "scripts": {
    "lint": "eslint .",
    "pretest": "npm run lint --silent"
  },
  "eslintConfig": {
    "plugins": [
      "meteor"
    ],
    "extends": [
      "airbnb",
      "plugin:meteor/recommended"
    ],
    "rules": {
      "meteor/eventmap-params": [
        2, { "templateInstanceParamName": "instance" }
      ],
      "import/no-unresolved": [
        2, { "ignore": ["^meteor/"] }
      ]
    }
  }
}
```

```bash
# ESLint 実行
meteor npm run lint
```


```bash
# for Atom
apm install language-babel
apm install linter
apm install linter-eslint
```

## :book: [Application Structure](http://guide.meteor.com/structure.html)

### Introduction to using import and export

```javascript
import '../../api/lists/methods.js';  // import from relative path
import '/imports/startup/client';     // import module with index.js from absolute path
import './loading.html';              // import Blaze compiled HTML from relative path
import '/imports/ui/style.css';       // import CSS from absolute path
```

```javascript
export const listRenderHold = LaunchScreen.hold();  // named export
export { Todos };                                   // named export
export default Lists;                               // default export
export default new Collection('lists');             // default export
```

### Example directory layout

```
imports/
  startup/
    client/
      index.js                 # import client startup through a single index entry point
      routes.js                # set up all routes in the app
      useraccounts-configuration.js # configure login templates
    server/
      fixtures.js              # fill the DB with example data on startup
      index.js                 # import server startup through a single index entry point

  api/
    lists/                     # a unit of domain logic
      server/
        publications.js        # all list-related publications
        publications.tests.js  # tests for the list publications
      lists.js                 # definition of the Lists collection
      lists.tests.js           # tests for the behavior of that collection
      methods.js               # methods related to lists
      methods.tests.js         # tests for those methods

  ui/
    components/                # all reusable components in the application
                               # can be split by domain if there are many
    layouts/                   # wrapper components for behaviour and visuals
    pages/                     # entry points for rendering used by the router

client/
  main.js                      # client entry point, imports all client code

server/
  main.js                      # server entry point, imports all server code
```

## :book: [Migrating to Meteor 1.3](http://guide.meteor.com/1.3-migration.html)

飛ばします。

## :book: [Collections and Schemas](http://guide.meteor.com/collections.html)

```javascript
// コレクションの作成
Todos = new Mongo.Collection('Todos');
```

```javascript
// ローカルコレクションの作成
SelectedTodos = new Mongo.Collection(null);
```

### SimpleSchema

```bash
meteor add aldeed:simple-schema
```

```javascript
Lists.schema = new SimpleSchema({
  name: {type: String},
  incompleteCount: {type: Number, defaultValue: 0},
  userId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}
});
```

### Collection2

```bash
meteor add aldeed:collection2
```

```javascript
Lists.attachSchema(Lists.schema);
```

#### defaultValue and data cleaning

```javascript
class ListsCollection extends Mongo.Collection {
  insert(list, callback) {
    if (!list.name) {
      let nextLetter = 'A';
      list.name = `List ${nextLetter}`;

      while (!!this.findOne({name: list.name})) {
        // not going to be too smart here, can go past Z
        nextLetter = String.fromCharCode(nextLetter.charCodeAt(0) + 1);
        list.name = `List ${nextLetter}`;
      }
    }

    // Call the original `insert` method, which will validate
    // against the schema
    return super.insert(list, callback);
  }
}

Lists = new ListsCollection('Lists');
```

### Hooks on insert/update/remove

```javascript
class ListsCollection extends Mongo.Collection {
  // ...
  remove(selector, callback) {
    Package.todos.Todos.remove({listId: selector});
    return super.remove(selector, callback);
  }
}
```

### Migrating to a new schema

```bash
meteor add percolate:migrations
```

```javascript
Migrations.add({
  version: 1,
  up() {
    Lists.find({todoCount: {$exists: false}}).forEach(list => {
      const todoCount = Todos.find({listId: list._id})).count();
      Lists.update(list._id, {$set: {todoCount}});
    });
  },
  down() {
    Lists.update({}, {$unset: {todoCount: true}});
  }
});
```

#### Bulk changes

```javascript
Migrations.add({
  version: 1,
  up() {
    // This is how to get access to the raw MongoDB node collection that the Meteor server collection wraps
    const batch = Lists.rawCollection().initializeUnorderedBulkOp();
    Lists.find({todoCount: {$exists: false}}).forEach(list => {
      const todoCount = Todos.find({listId: list._id}).count();
      // We have to use pure MongoDB syntax here, thus the `{_id: X}`
      batch.find({_id: list._id}).updateOne({$set: {todoCount}});
    });

    // We need to wrap the async function to get a synchronous API that migrations expects
    const execute = Meteor.wrapAsync(batch.execute, batch);
    return execute();
  },
  down() {
    Lists.update({}, {$unset: {todoCount: true}});
  }
});
```

#### Running migrations

```javascript
// After running `meteor shell` on the command line:
Migrations.migrateTo('latest');
```

#### Breaking schema changes

```javascript // The "0" migration is the unmigrated (before the first migration) state
Migrations.migrateTo(0);
```

### Collection helpers

```bash
meteor add dburles:collection-helpers
```

```javascript
Lists.helpers({
  // A list is considered to be private if it has a userId set
  isPrivate() {
    return !!this.userId;
  }
});
```

```javascript
const list = Lists.findOne();
if (list.isPrivate()) {
  console.log('The first list is private!');
}
```

#### Association helpers

```javascript
Lists.helpers({
  todos() {
    return Todos.find({listId: this._id}, {sort: {createdAt: -1}});
  }
});
```

```javascript
const list = Lists.findOne();
console.log(`The first list has ${list.todos().count()} todos`);
```


## :book: [Publications and Data Loading](http://guide.meteor.com/data-loading.html)

### Defining a publication

```javascript
Meteor.publish('lists.public', function() {
  return Lists.find({
    userId: {$exists: false}
  }, {
    fields: Lists.publicFields
  });
});
```

```javascript
Meteor.publish('lists.private', function() {
  if (!this.userId) {
    return this.ready();
  }

  return Lists.find({
    userId: this.userId
  }, {
    fields: Lists.publicFields
  });
});
```

```javascript
Meteor.publish('todos.inList', function(listId) {
  // We need to check the `listId` is the type we expect
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  // ...
});
```

### Subscribing to data

```javascript
const handle = Meteor.subscribe('lists.public');
```

```javascript
const handle2 = Meteor.subscribe('todos.inList', list._id);
```

#### Subscribe in UI components

```javascript
Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList', this.getListId());
  });
});
```

#### Subscription readiness

```javascript
const handle = Meteor.subscribe('lists.public');
Tracker.autorun(() => {
  const isReady = handle.ready();
  console.log(`Handle is ${isReady ? 'ready' : 'not ready'}`);  
});
```

### FindFromPublication

```bash
meteor add percolate:find-from-publication
```

### PublishCounts

```bash
meteor add tmeasday:publish-counts 
```

```javascript
Meteor.publish('Lists.todoCount', function({ listId }) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  Counts.publish(this, `Lists.todoCount.${listId}`, Todos.find({listId}));
});
```

```javascript
Counts.get(`Lists.todoCount.${listId}`)
```

### ReactiveVar / ReactiveDict

```bash
meteor add reactive-var
meteor add reactive-dict
```

### PublishComposite

```bash
meteor add reywood:publish-composite
```

### Loading data from a REST endpoint with a publication

```javascript
const POLL_INTERVAL = 5000;

Meteor.publish('polled-publication', function() {
  const publishedKeys = {};

  const poll = () => {
    // Let's assume the data comes back as an array of JSON documents, with an _id field, for simplicity
    const data = HTTP.get(REST_URL, REST_OPTIONS);

    data.forEach((doc) => {
      if (publishedKeys[doc._id]) {
        this.changed(COLLECTION_NAME, doc._id, doc);
      } else {
        publishedKeys[doc._id] = true;
        if (publishedKeys[doc._id]) {
          this.added(COLLECTION_NAME, doc._id, doc);
        }
      }
    });
  };

  poll();
  this.ready();

  const interval = Meteor.setInterval(poll, POLL_INTERVAL);

  this.onStop(() => {
    Meteor.clearInterval(interval);
  });
});
```

### [simple:rest](https://atmospherejs.com/simple/rest) パッケージ

```bash
meteor add simple:rest
```


## :book: [Methods](http://guide.meteor.com/methods.html)

### Defining

```javascript
Meteor.methods({
  'todos.updateText'({ todoId, newText }) {
    new SimpleSchema({
      todoId: { type: String },
      newText: { type: String }
    }).validate({ todoId, newText });

    const todo = Todos.findOne(todoId);

    if (!todo.editableBy(this.userId)) {
      throw new Meteor.Error('todos.updateText.unauthorized',
        'Cannot edit todos in a private list that is not yours');
    }

    Todos.update(todoId, {
      $set: { text: newText }
    });
  }
});
```

### Calling

```javascript
Meteor.call('todos.updateText', {
  todoId: '12345',
  newText: 'This is a todo item.'
}, (err, res) => {
  if (err) {
    alert(err);
  } else {
    // success!
  }
});
```


### Advanced Methods with mdg:validated-method

```bash
meteor add mdg:validated-method
```

```javascript
export const updateText = new ValidatedMethod({
  name: 'todos.updateText',
  validate: new SimpleSchema({
    todoId: { type: String },
    newText: { type: String }
  }).validator(),
  run({ todoId, newText }) {
    const todo = Todos.findOne(todoId);

    if (!todo.editableBy(this.userId)) {
      throw new Meteor.Error('todos.updateText.unauthorized',
        'Cannot edit todos in a private list that is not yours');
    }

    Todos.update(todoId, {
      $set: { text: newText }
    });
  }
});
```

### Handling errors

```javascript
// Call the Method
updateText.call({
  todoId: '12345',
  newText: 'This is a todo item.'
}, (err, res) => {
  if (err) {
    if (err.error === 'todos.updateText.unauthorized') {
      // Displaying an alert is probably not what you would do in
      // a real app; you should have some nice UI to display this
      // error, and probably use an i18n library to generate the
      // message from the error code.
      alert('You aren\'t allowed to edit this todo item');
    } else {
      // Unexpected error, handle it in the UI somehow
    }
  } else {
    // success!
  }
});
```

## :book: [Users and Accounts](http://guide.meteor.com/accounts.html)

```bash
meteor add accounts-ui
```

```html
{{> loginButtons}}
```

```bash
# pick one or more of the below
meteor add accounts-password
meteor add accounts-facebook
meteor add accounts-google
meteor add accounts-github
meteor add accounts-twitter
meteor add accounts-meetup
meteor add accounts-meteor-developer
```

```bash
meteor add useraccounts:core useraccounts:unstyled
```

```bash
meteor add useraccounts:flow-routing
```

### Publishing custom data

```javascript
Meteor.publish('Meteor.users.initials', function ({ userIds }) {
  // Validate the arguments to be what we expect
  new SimpleSchema({
    userIds: { type: [String] }
  }).validate({ userIds });

  // Select only the users that match the array of IDs passed in
  const selector = {
    _id: { $in: userIds }
  };

  // Only return one field, `initials`
  const options = {
    fields: { initials: 1 }
  };

  return Meteor.users.find(selector, options);
});
```

### Roles and permissions

#### [alanning:roles](https://atmospherejs.com/alanning/roles) パッケージ

```bash
meteor add alanning:roles
```

```javascript
// Give Alice the 'admin' role
Roles.addUsersToRoles(aliceUserId, 'admin', Roles.GLOBAL_GROUP);

// Give Bob the 'moderator' role for a particular category
Roles.addUsersToRoles(bobsUserId, 'moderator', categoryId);
```

```javascript
const forumPost = Posts.findOne(postId);

const canDelete = Roles.userIsInRole(userId,
  ['admin', 'moderator'], forumPost.categoryId);

if (! canDelete) {
  throw new Meteor.Error('unauthorized',
    'Only admins and moderators can delete posts.');
}

Posts.remove(postId);
```

#### Per-document permissions

```javascript
Lists.helpers({
  // ...
  editableBy(userId) {
    if (!this.userId) {
      return true;
    }

    return this.userId === userId;
  },
  // ...
});
```

```javascript
const list = Lists.findOne(listId);

if (! list.editableBy(userId)) {
  throw new Meteor.Error('unauthorized',
    'Only list owners can edit private lists.');
}
```


## :book: [Testing | Meteor Guide](http://guide.meteor.com/testing.html)

```bash
meteor add practicalmeteor:mocha

meteor add xolvio:cleaner

meteor add dburles:factory

meteor add hwillson:stub-collections

meteor add velocity:meteor-stubs

# for React
meteor npm install -D enzyme

# Testing publications
meteor add johanbrook:publication-collector
```

```bash
# Running unit tests
meteor test --driver-package practicalmeteor:mocha --port 3100

# Running full-app tests
meteor test --full-app --driver-package practicalmeteor:mocha
```

### Acceptance testing

```bash
# node >= 4
npm install --global chimp
```

```json:package.json
{
  "scripts": {
    "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests",
    "chimp-test": "chimp --mocha --path=tests"
  }
}
```

#### Running acceptance tests

```bash
# In one terminal
meteor

# In another
meteor npm run chimp-watch
```

### Creating data

```bash
meteor add tmeasday:acceptance-test-driver
meteor test --full-app --driver-package tmeasday:acceptance-test-driver
```

### Continuous Integration

#### Command line

```bash
meteor add dispatch:mocha-phantomjs
meteor test --once --driver-package dispatch:mocha-phantomjs
```

```json
{
  "scripts": {
    "test": "meteor test --once --driver-package dispatch:mocha-phantomjs"
  }
}
```

#### CircleCI

```yaml:circle.yml
machine:
  node:
    version: 0.10.43
dependencies:
  override:
    - curl https://install.meteor.com | /bin/sh
    - npm install
checkout:
  post:
    - git submodule update --init
```



## :book: [URLs and Routing](http://guide.meteor.com/routing.html)

```bash
meteor add kadira:flow-router
meteor add zimme:active-route
meteor add arillo:flow-router-helpers

# for Blaze
meteor add kadira:blaze-layout
```

## :book: [User Interfaces](http://guide.meteor.com/ui-ux.html)

```bash
# Internationalization
meteor add tap:i18n 

# Animating changes in visiblity
meteor add percolate:momentum
```

## :book: [Blaze](http://guide.meteor.com/blaze.html)

### Use a reactive dict for state

```bash
meteor add reactive-dict
```

```javascript
Template.Lists_show.onCreated(function() {
  this.state = new ReactiveDict();
  this.state.setDefault({
    editing: false,
    editingTodo: false
  });
});
```

### Attach functions to the instance

```javascript
import {
  updateName,
} from '../../api/lists/methods.js';

Template.Lists_show.onCreated(function() {
  this.saveList = () => {
    this.state.set('editing', false);

    updateName.call({
      listId: this.data.list._id,
      newName: this.$('[name=name]').val()
    }, (err) => {
      err && alert(err.error);
    });
  };
});
```

```javascript
Template.Lists_show.events({
  'submit .js-edit-form'(event, instance) {
    event.preventDefault();
    instance.saveList();
  }
});
```

### Scope DOM lookups to the template instance

```javascript
Template.Lists_show.events({
  'click .js-todo-add'(event, instance) {
    instance.$('.js-todo-new input').focus();
  }
});
```

### Passing HTML content as a template argument

```bash
meteor add kadira:blaze-layout
```

```javascript
{{> Template.dynamic templateName dataContext}}
```

### Controlling re-rendering

```bash
meteor add peerlibrary:computed-field
```


## :book: [React](http://guide.meteor.com/react.html)

```bash
# npm で React をインストール
meteor npm install --save react react-dom

# Using 3rd party packages
meteor npm install --save griddle-react

# Blaze を使わない場合
meteor remove blaze-html-templates
meteor add static-html

# React 内で Blaze テンプレートを使用する場合
meteor add gadicc:blaze-react-component

# ReactMeteorData
meteor add react-meteor-data
meteor npm install --save react-addons-pure-render-mixin

# Routing
meteor add kadira:flow-router
meteor npm install --save react-mounter
## or
meteor npm install --save react-router

# Using React in Atmosphere Packages
meteor add tmeasday:check-npm-versions
```


## :book: Angular

飛ばします。

## :book: [Atmosphere vs. npm](http://guide.meteor.com/atmosphere-vs-npm.html)

飛ばします。

## :book: [Using Atmosphere Packages](http://guide.meteor.com/using-atmosphere-packages.html)

```bash
# Searching for packages
meteor search <WORD>
meteor show kadira:flow-router

# Installing Atmosphere Packages
meteor add <PACKAGE>

# See all the Atmosphere packages
meteor list

# Remove an unwanted Atmosphere package
meteor remove <PACKAGE>
```


## :book: [Writing Atmosphere Packages](http://guide.meteor.com/writing-atmosphere-packages.html)

```bash
# Create a package
meteor create --package my-package

# Testing packages
meteor test-packages ./ --driver-package practicalmeteor:mocha

# Publish a package
meteor publish
```


## :book: [Using npm Packages](http://guide.meteor.com/using-npm-packages.html)

```bash
meteor npm install

meteor npm install --save meteor-node-stubs
meteor npm install --save moment

meteor npm shrinkwrap
```

### Shrinkpack

```bash
npm install -g shrinkpack

meteor npm install moment
meteor npm shrinkwrap
shrinkpack
```

## :book: [Writing npm Packages](http://guide.meteor.com/writing-npm-packages.html)

```bash
mkdir my-package
cd my-package/
meteor npm init
```

### Including in your app

```bash
# Inside node_modules
cd my-app/node_modules/
mkdir my-package
cd my-package/
meteor npm init
git add -f ./ # or use a git submodule
```

```bash
# npm link
cd ~/
mkdir my-package
cd my-package/
meteor npm init
cd ~/my-app/
meteor npm link ~/my-package
```


## :book: [Mobile](http://guide.meteor.com/mobile.html)

```bash
meteor add-platform ios
sudo xcodebuild -license accept

meteor add-platform android
meteor remove-platform ios android
meteor list-platforms

meteor run ios
meteor run ios-device
meteor run android
meteor run android-device
```

```bash
# Installing plugins
meteor add cordova:cordova-plugin-camera@1.2.0

# Installing a plugin from Git
meteor add cordova:com.phonegap.plugins.facebookconnect@https://github.com/Wizcorp/phonegap-facebook-plugin.git#5dbb1583168558b4447a13235283803151cb04ec

# Installing a plugin from the local file system
meteor add cordova:cordova-plugin-underdevelopment@file://../plugins/cordova-plugin-underdevelopment

# Removing directly installed plugins
meteor remove cordova:cordova-plugin-camera
meteor remove cordova:com.phonegap.plugins.facebookconnect
meteor remove cordova:cordova-plugin-underdevelopment
```

```bash
# Domain whitelisting
```


## :book: [Build System](http://guide.meteor.com/build-tool.html)

```bash
meteor add ecmascript

# CoffeeScript
meteor add coffeescript

# Sass/SCSS
meteor add fourseven:scss

# LESS
meteor add less

# Stylus
metoer add stylus
```

### PostCSS and AutoPrefixer

```bash
meteor remove standard-minifier-css
meteor add juliancwirko:postcss
```

```javascript:package.json
{
  "devDependencies": {
    "autoprefixer": "^6.3.1"
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {"browsers": ["last 2 versions"]}
    }
  }
}
```


## :book: [Security](http://guide.meteor.com/security.html)

```bash
meteor add check
meteor add aldeed:simple-schema
meteor remove autopublish insecure
```

### Securing API keys

```json
{
  "facebook": {
    "clientId": "12345",
    "secret": "1234567"
  }
}
```

```bash
# Pass development settings when running your app locally
meteor --settings development.json

# Pass production settings when deploying your app to Galaxy
meteor deploy myapp.com --settings production.json
```

### API keys for OAuth

```bash
meteor add service-configuration
```

```bash
ServiceConfiguration.configurations.upsert({
  service: "facebook"
}, {
  $set: {
    clientId: Meteor.settings.facebook.clientId,
    loginStyle: "popup",
    secret: Meteor.settings.facebook.secret
  }
});
```

### SSL

```bash
meteor add force-ssl
```


## :book: [Deployment and Monitoring](http://guide.meteor.com/deployment.html)

### Custom deployment

```bash
# for example if deploying to a Ubuntu linux server:
npm install --production
meteor build /path/to/build --architecture os.linux.x86_64
```

```bash
cd my_directory
(cd programs/server && npm install)
MONGO_URL=mongodb://localhost:27017/myapp ROOT_URL=http://my-app.com node main.js
```

### Monitoring users via analytics

```bash
meteor add okgrow:analytics
```

```json:settings.json
{
  "public": {
    "analyticsSettings": {
      // Add your analytics tracking id's here
      "Google Analytics" : {"trackingId": "Your tracking ID"}
    }
  }
}
```


### Enabling SEO

```bash
# for Prerender.io
meteor add dfischer:prerenderio

# for Galaxy
meteor add mdg:seo

# To set <title> tags and other <head> content
meteor add kadira:dochead
```

ひとまずここまで。