text: import Module from './module'
  import { assert, forEachValue } from '../util'
  
  /**
   * module集合器
   */
  export default class ModuleCollection {
    // rawRootModule 就是store实例化的 options
    constructor (rawRootModule) {
      // register root module (Vuex.Store options)
      // 初始化
      this.register([], rawRootModule, false)
    }
  
    get (path) {
      return path.reduce((module, key) => {
        return module.getChild(key)
      }, this.root)
    }
  
    getNamespace (path) {
      let module = this.root
      return path.reduce((namespace, key) => {
        module = module.getChild(key)
        return namespace + (module.namespaced ? key + '/' : '')
      }, '')
    }
  
    update (rawRootModule) {
      update([], this.root, rawRootModule)
    }
  
    register (path, rawModule, runtime = true) {
      if (process.env.NODE_ENV !== 'production') {
        assertRawModule(path, rawModule)
      }
  
      debugger
      /**
       * newModule返回一个对象
        {
          namespaced: false,
          runtime: false,
          _children: {}
          _rawModule:
          state: {count: 0}
          getters: {evenOrOdd: ƒ}
          actions: {increment: ƒ, decrement: ƒ, incrementIfOdd: ƒ, incrementAsync: ƒ}
          mutations: {increment: ƒ, decrement: ƒ}
          __proto__: Object
          state: {count: 0}
        }
       */
      const newModule = new Module(rawModule, runtime) //todo 返回值
      if (path.length === 0) {
        this.root = newModule
      } else {
        const parent = this.get(path.slice(0, -1))
        parent.addChild(path[path.length - 1], newModule)
      }
  
      // register nested modules
      // 注册嵌套的modules
      if (rawModule.modules) {
        forEachValue(rawModule.modules, (rawChildModule, key) => {
          this.register(path.concat(key), rawChildModule, runtime)
        })
      }
    }
  
    // todo 什么时候需要注销
    unregister (path) {
      const parent = this.get(path.slice(0, -1))
      const key = path[path.length - 1]
      if (!parent.getChild(key).runtime) return
  
      parent.removeChild(key)
    }
  }
  
  function update (path, targetModule, newModule) {
    if (process.env.NODE_ENV !== 'production') {
      assertRawModule(path, newModule)
    }
  
    // update target module
    targetModule.update(newModule)
  
    // update nested modules
    if (newModule.modules) {
      for (const key in newModule.modules) {
        if (!targetModule.getChild(key)) {
          if (process.env.NODE_ENV !== 'production') {
            console.warn(
              `[vuex] trying to add a new module '${key}' on hot reloading, ` +
              'manual reload is needed'
            )
          }
          return
        }
        update(
          path.concat(key),
          targetModule.getChild(key),
          newModule.modules[key]
        )
      }
    }
  }
  
  const functionAssert = {
    assert: value => typeof value === 'function',
    expected: 'function'
  }
  
  const objectAssert = {
    assert: value => typeof value === 'function' ||
      (typeof value === 'object' && typeof value.handler === 'function'),
    expected: 'function or object with "handler" function'
  }
  
  const assertTypes = {
    getters: functionAssert,
    mutations: functionAssert,
    actions: objectAssert
  }
  
  function assertRawModule (path, rawModule) {
    Object.keys(assertTypes).forEach(key => {
      if (!rawModule[key]) return
  
      const assertOptions = assertTypes[key]
  
      forEachValue(rawModule[key], (value, type) => {
        assert(
          assertOptions.assert(value),
          makeAssertionMessage(path, key, type, value, assertOptions.expected)
        )
      })
    })
  }
  
  function makeAssertionMessage (path, key, type, value, expected) {
    let buf = `${key} should be ${expected} but "${key}.${type}"`
    if (path.length > 0) {
      buf += ` in module "${path.join('.')}"`
    }
    buf += ` is ${JSON.stringify(value)}.`
    return buf
  }
  