<commit_before>import _ from 'lodash'
import { createTransformer, toJS, extendObservable, observable, computed, action } from 'mobx'
import config from 'config'

const ROOT_PATH = ''
const nodeSorter = (a, b) => {
  // node.isDir comes first
  // then sort by node.path alphabetically
  if (a.isDir && !b.isDir) return -1
  if (!a.isDir && b.isDir) return 1
  if (a.path < b.path) return -1
  if (a.path > b.path) return 1
  return 0
}

const state = observable({
  entities: observable.map(),
  get root () {
    return this.entities.get(ROOT_PATH)
  },
})

class FileNode {
  constructor (props) {
    this.update(props)

    state.entities.set(this.path, this)
  }

  @action
  update (props) {
    extendObservable(this, props)
  }

  @observable path
  @observable contentType
  @observable content = ''
  @observable isDir = false
  @observable isSynced = true
  @observable gitStatus = 'NONE'
  @observable size = 0

  @observable _name = undefined
  @computed
  get name () {
    return this._name || this.path.split('/').pop()
  }
  set name (v) { this._name = v }

  @computed
  get id () { return this.path }
  set id (v) { this.path = v }

  @computed get isRoot () {
    return this.path === ROOT_PATH
  }

  @computed get depth () {
    const slashMatches = this.path.match(/\/(?=[^/]+)/g)
    return slashMatches ? slashMatches.length : 0
  }

  @computed
  get parent () {
    if (this.isRoot) return null
    const pathComps = this.path.split('/')
    pathComps.pop()
    const parentPath = pathComps.join('/')
    const parent = state.entities.get(parentPath)
    // only rootFileNode has no parent, other case means something wrong
    if (!parent) { throw Error(`Missing internal node of path '${parentPath}'`) }
    return parent
  }

  @computed
  get children () {
    const depth = this.depth
    return state.entities.values()<commit_msg>:bug: FileNode ctor should ensure existence of internal nodes<commit_after>import _ from 'lodash'
import { createTransformer, toJS, extendObservable, observable, computed, action } from 'mobx'
import config from 'config'

const ROOT_PATH = ''
const nodeSorter = (a, b) => {
  // node.isDir comes first
  // then sort by node.path alphabetically
  if (a.isDir && !b.isDir) return -1
  if (!a.isDir && b.isDir) return 1
  if (a.path < b.path) return -1
  if (a.path > b.path) return 1
  return 0
}

const state = observable({
  entities: observable.map(),
  get root () {
    return this.entities.get(ROOT_PATH)
  },
})

// state.entities.intercept((change) => {
//   console.log(change)
//   if (change.type === 'add') {

//   }
//   return change
// })

class FileNode {
  constructor (props) {
    this.update(props)
    if (!this.parent && !this.isRoot) {
      const pathComps = this.path.split('/')
      pathComps.pop()
      const parentPath = pathComps.join('/')
      new FileNode({
        path: parentPath,
        isDir: true
      })
    }
    state.entities.set(this.path, this)
  }

  @action
  update (props) {
    extendObservable(this, props)
  }

  @observable path
  @observable contentType
  @observable content = ''
  @observable isDir = false
  @observable isSynced = true
  @observable gitStatus = 'NONE'
  @observable size = 0

  @observable _name = undefined
  @computed
  get name () {
    return this._name || this.path.split('/').pop()
  }
  set name (v) { this._name = v }

  @computed
  get id () { return this.path }
  set id (v) { this.path = v }

  @computed get isRoot () {
    return this.path === ROOT_PATH
  }

  @computed get depth () {
    const slashMatches = this.path.match(/\/(?=[^/]+)/g)
    return slashMatches ? slashMatches.length : 0
  }

  @computed
  get parent () {
    if (this.isRoot) return null
    const pathComps = this.path.split('/')
    pathComps.pop()
    const parentPath = pathComps.join('/')
    const parent = state.entities.get(parentPath)
    // only rootFileNode has no parent, other case means something wrong
    if (!parent) return null
    //   parent = new FileNode({ path: parentPath, isDir: true })
    // }
    return parent
  }

  @computed
  get children () {
    const depth = this.depth
    return state.entities.values()
{'fraction_non_alphanumeric': 0.13640730067243034, 'fraction_numerical': 0.0033621517771373678, 'mean_word_length': 2.878026070763501, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7428049', 'n_tokens_mistral': 1427, 'n_tokens_neox': 1394, 'n_words': 386}