Get List of Commits

const { findGitRepos, getCommitsFromRepos } = require ('./gitutils');

findGitRepos(['~/Code/Valtech'], 5, (err, result) => {
    if (err) throw err;

    getCommitsFromRepos(result, 30, (err, result) => {
        if (err) throw err;

        console.log(result);
    });
});
// Stolen from here:
// https://github.com/notwaldorf/tiny-care-terminal/blob/78e038069f01c36148d7d486d7775275d3df1df8/gitbot.js
const resolve = require('resolve-dir');
const subdirs = require('subdirs');
const isGit = require('is-git');
const gitlog = require('gitlog');
const path = require('path');
const async = require("async");
const git = require('git-utils');

try {
  const gitUsername = require('git-user-name')();
} catch(err) {
  console.error(`ERROR reading git-config.
    Use e.g. 'git config --global user.name "Mona Lisa"'.
    See 'man git config' for further information.
  `);
  return process.exit(0);
}

/**
 * Go through all `repos` and look for subdirectories up to a given `depth`
 * and look for repositories.
 * Calls `callback` with array of repositories.
 */
function findGitRepos(repos, depth, callback) {
  let allRepos = [];
  async.each(repos, (repo, repoDone) => {
    repo = resolve(repo);
    subdirs(repo, depth, (err, dirs) => {
      if (err) {
        switch (err.code) {
          case 'ENOENT':
            return callback(`Could not open directory directory: ${err.path}n`, null);
          case 'EACCES':
            return; //ignore if no access
          default:
            return callback(`Error "${err.code}" doing "${err.syscall}" on directory: ${err.path}n`, null);
        }
      }
      if (dirs) dirs.push(repo);
      async.each(dirs, (dir, dirDone) => {
        isGit(dir, (err, isGit) => {
          if (err) {
            return callback(err, null);
          }
          if (!dir.includes('.git') && isGit) {
            allRepos.push(dir);
          }
          dirDone();
        });
      }, repoDone);
    });
  }, err => {
    callback(err, allRepos.sort().reverse());
  });
}

/**
 * returns all commits of the last given `days`.
 * Calls `callback` with line-seperated-strings of the formatted commits.
 */
function getCommitsFromRepos(repos, days, callback) {
  let cmts = [];
  async.each(repos, (repo, repoDone) => {
    let localGitUsername = '';
    try {
      const gitUtilsRepo = git.open(repo);
      localGitUsername = gitUtilsRepo.getConfigValue('user.name') || gitUsername;
    } catch (err) {
      localGitUsername = gitUsername;
    }
    try {
      gitlog({
        repo: repo,
        all: true,
        number: 100, //max commit count
        since: `${days} days ago`,
        fields: ['abbrevHash', 'subject', 'authorDate', 'authorName'],
        author: localGitUsername
      }, (err, logs) => {
        // Error
        if (err) {
          callback(`Oh noes­čś▒nThe repo ${repo} has failed:n${err}`, null);
        }
        // Find user commits
        let commits = [];
        logs.forEach(c => {
          // filter simple merge commits
          if (c.status && c.status.length)
            commits.push(`${c.abbrevHash} - ${c.subject} (${c.authorDate}) <${c.authorName.replace('@end@n','')}>`);
        });

        // Add repo name and commits
        if (commits.length >= 1) {
          // Repo name
          cmts.push(repo);
          cmts.push(...commits);
        }

        repoDone();
      });
    } catch(err) {
      callback(err, null);
    }
  }, err => {
    callback(err, cmts.length > 0 ? cmts.join('n') : "Nothing yet. Start small!");
  });
}

module.exports.findGitRepos = findGitRepos;
module.exports.getCommitsFromRepos = getCommitsFromRepos;

What Order Does This Execute In?

setTimeout(() => {
    console.log('inside timeout');
}, 0);

process.nextTick(() => {
    console.log('inside nextTick');
});

const p = new Promise(function(resolve, reject) {
    console.log('inside promise');

    resolve();
})
    .then(() => console.log('inside then'));

console.log('after promise');

elm-types Strawman

import t from 'elm-types';

const AddTodo = t.Constructor('AddTodo', [t.String]);
const RemoveTodo = t.Constructor('AddTodo', [t.String]);
const Action = t.Union('Action', [AddTodo]);
const Todo = t.Record('Todo', {
    name: t.String,
    completed: t.Bool
});
const Model = t.Record('Model', {
    todos: t.List('todos', Todo)
});

// Throws if function is not passed correct parameter types
const update = t.Function('update', [State, Action, State], (state, action) =>
    // Throws if last object is not exaustive
    t.case(action, Action, {
        [AddTodo]: add => ({
            ...state,
            todos: [...state.todos, Todo(add, false)]
        }),
        [RemoveTodo]: remove => ({
            ...state,
            todos: state.todos.filter(todo => todo.name === remove)
        })
    });
);

const store = createStore(update, Model([]));

const App = ({ todos }) => (
    <div>
        <h1>Todo List</h1>
        <ul>
            {todos.map(todo => (
                <li onClick={() => store.dispatch(RemoveTodo(todo.name))}>
                    {todo.name}
                </li>
            ))}
        </ul>
    </div>
);

const AppProps = t.Record('AppProps', {
    todos: t.List('todos', Todo)
});

App.propTypes = t.PropTypes(AppProps);

render(
    <App {...store.getState()} />,
    document.getElementById('app')
);

flatten two ways

function flatten(source, acc = []) {
    if (source.length === 0) return acc;

    const [head, ...tail] = source;

    return flatten(tail, [...acc, ...(Array.isArray(head) ? flatten(head) : [head])]);
}

console.log(flatten([1,2,[3,4,5,[6,7],8,[9]],10]));
import * as R from 'ramda';

const flatten = R.ifElse(
    R.propSatisfies(R.equals(0), 'length'),
    R.flip(R.identity),
    R.converge((source, acc = []) => flatten(source, acc), [
	    R.tail,
        R.useWith(R.flip(R.concat), [
	        R.pipe(R.head, R.ifElse(Array.isArray, head => flatten(head, []), R.of)),
	        R.identity
        ])
    ])
);

console.log(flatten([1,2,[3,4,5,[6,7],8,[9]],10], []));

Arrow function problems

function foo(ddb) {
  return {
    listTables: (params = {}, cb = idFunc) => {
      const self = this
      let currentList = params.currentList || []
      
      return toPromise(ddb, ddb.listTables, omit(params, 'currentList'), cb)
        .then(r => {
          console.log('LISTTABLES result', r)
          currentList = currentList.concat(r.TableNames || [])
          
          if (!r.LastEvaluatedTableName || r.TableNames.length === 0) {
            return { ...r, TableNames: currentList }
          }

          return self.listTables({   // <- Fails here
              ...params,
              currentList,
              ExclusiveStartTableName: r.LastEvaluatedTableName,
            }, cb)
        })
    }
  }
}
class MyClass {
  myMethod = () => {
    // ...code
  }
}

Brookjs components strawman proposal

import { h, component } from 'brookjs'
import { editClick } from '../actions'

// Changes only occur when bound to a stream
export default component({
    render: props$ => (
        <li>
            {/* Text changes when a value is emitted. */}
            <p class="todo__name">
                {props$.map(props => props.text)}
            </p>
            {/* Maps a stream of events to actions. */}
            <button onClick={event$ => event$.map(editClick)}>
                {'Edit todo'}
            </button>
        </li>
    )
})
import { h, component } from 'brookjs'
import TodoItem from './TodoItem'

export default component({
    render: props$ => (
        <div>
            <h2>My Todos</h2>
            {/**
              * `order` provides sequence todos should appear
              * `dict` provides a performant instance lookup 
              */}
            <ul>
                {props$.map(todos => todos.order.map(key => (
                    <TodoItem
                        props$={props$.map(todos => todos.dict[key])}
                        key={key}
                        preplug={instance$ => instance$.map(action => ({
                            ...action,
                            meta: { key }
                        })} />
                )))}
            <ul>
        </div>
    )
})
import { h, component } from 'brookjs'
import TodoList from './TodoList'

export default component({
    render: props$ => (
        <div>
            <h1>Todo App</h1>
            {/**
              * Individual attributes respond to observables
              * Performance optimized inline
              */}
            <input value={props$.map(props => props.editing).skipDuplicates()}
                   onInput={event$ => event$.map(event => editTodo(event.target.value))} />
            <button onClick={event$ => event$.map(addTodo)}>Add Todo</button>
            <TodoList props$={props$.map(props => props.todos)} />
        </div>
    )
})
import { createStore, applyMiddleware } from 'redux'
import { h, observeDelta, Kefir } from brookjs
import { App } from './components'
import { selectProps } from './selectors'

const store = createStore(
    (state, action) => state, // reducer
    window.__INITIAL_STATE__ || {},
    applyMiddleware(observeDelta(
        /* register deltas here */
    )
)
const state$ = Kefir.fromESObservable(store)

/**
 * `mount` thus takes the DOM to mount
 * and the element to bind it to, and
 * returns a stream. Note that because
 * of how streams work, nothing happens
 * until the stream is observed.
 */
const view$ = mount(
    <App props$={selectProps(state$)} />,
    document.getElementById('app')
)
 
view$.observe(store.dispatch)
import { h, component, list } from 'brookjs'
import TodoItem from './TodoItem'

export default component({
    render: props$ => (
        <div>
            <h2>My Todos</h2>
            <ul>
                {/* Must be a stream of objects with `order` & `dict` */}
                {list(props$, (props$, key) => (
                    <TodoItem
                        props$={props$}
                        key={key}
                        preplug={instance$ => instance$.map(action => ({
                            ...action,
                            meta: { key }
                        })} />
                )}
            <ul>
        </div>
    )
})
import { createStore, applyMiddleware } from 'redux'
import { h, observeDelta, Kefir } from brookjs
import { App } from './components'
import { selectProps } from './selectors'

const el = document.getElementById('app')

const store = createStore(
    (state, action) => state,
    window.__INITIAL_STATE__ || {},
    applyMiddleware(observeDelta(
        /* register deltas here */
        domDelta({ el, selectProps, view: App })
    )
)

// Everything is bound to the store immediately,
// but an init action makes sure everything waits
// until the store is fully instantiated.
store.dispatch(init())

Point-Free Style – Functional Light JS v2 from Front-End Masters

function output(txt) {
	console.log(txt);
}

function printIf(predicate) {
	return function(msg) {
		if (predicate(msg)) {
			output(msg);
		}
	};
}

function isShortEnough(str) {
	return str.length <= 5;
}

function isLongEnough(str) {
	return !isShortEnough(str);
}

var msg1 = "Hello";
var msg2 = msg1 + " World";

printIf(isShortEnough)(msg1);		// Hello
printIf(isShortEnough)(msg2);
printIf(isLongEnough)(msg1);
printIf(isLongEnough)(msg2);		// Hello World
const output = console.log.bind(console);
const when = func => pred => val => {
  if (predicate(val)) {
    func(val);
  }
};
const not = func => val => !func(val);
const printIf = when(output);
const isShortEnough = str => str.length <= 5;
const isLongEnough = not(isShortEnough);