const exec = require('child_process').exec;
const path = require('path');
const log = require('npmlog');
const uniq = require('lodash.uniq');
const async = require('async');

const isEmpty = require('./isEmpty');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const copyAssetsAndroid = require('./android/copyAssets');
const copyAssetsIOS = require('./ios/copyAssets');

log.heading = 'rnpm-link';

/**
 * Returns an array of dependencies that should be linked/checked.
 */
const getProjectDependencies = () => {
  const pjson = require(path.join(process.cwd(), './package.json'));
  return Object.keys(pjson.dependencies).filter(name => name !== 'react-native');
};

/**
 * Updates project and linkes all dependencies to it
 *
 * If optional argument [packageName] is provided, it's the only one that's checked
 */
module.exports = function link(config, args) {

  try {
    const project = config.getProjectConfig();
  } catch (err) {
    log.error('ERRPACKAGEJSON', `No package found. Are you sure it's a React Native project?`);
    return;
  }

  const packageName = args[0];

  const dependencies =
    (packageName ? [packageName] : getProjectDependencies())
    .map(name => {
      try {
        return {
          config: config.getDependencyConfig(name),
          name,
        };
      } catch (err) {
        log.warn(
          'ERRINVALIDPROJ',
          `Project ${name} is not a react-native library`
        );
      }
    })
    .filter(dependency => dependency);

  const assets = uniq(
    dependencies.reduce(
      (assets, dependency) => assets.concat(dependency.config.assets),
      project.assets
    ),
    asset => path.basename(asset)
  );

  if (isEmpty(assets)) {
    return;
  }

  const tasks = dependencies.map((dependency) => (callback) => {
    const before = (cb) => {
      if (dependency.config.hooks && dependency.config.hooks.before) {
        exec(dependency.config.hooks.before, function before(err, stdout, stderr) {
          if (!err && !stderr) {
            console.log(stdout);
            cb();
          } else {
            throw Error(err || stderr);
          }
        });
      } else {
        cb();
      }
    };

    const after = (cb) => {
      if (dependency.config.hooks && dependency.config.hooks.after) {
        exec(dependency.config.hooks.after, function after(err, stdout, stderr) {
          if (!err && !stderr) {
            console.log(stdout);
            cb();
          } else {
            throw Error(err || stderr);
          }
        });
      } else {
        cb();
      }
    };

    async.waterfall([before, (cb) => {
      if (project.android && dependency.config.android) {
        log.info(`Linking ${dependency.name} android dependency`);
        registerDependencyAndroid(
          dependency.name,
          dependency.config.android,
          project.android
        );
      }

      if (project.ios && dependency.config.ios) {
        log.info(`Linking ${dependency.name} ios dependency`);
        registerDependencyIOS(dependency.config.ios, project.ios);
      }

      if (project.ios) {
        log.info('Linking assets to ios project');
        copyAssetsIOS(assets, project.ios);
      }

      if (project.android) {
        log.info('Linking assets to android project');
        copyAssetsAndroid(assets, project.android.assetsPath);
      }

      cb();
    }, after, callback]);
  });

  async.series(tasks);
};

@@ -951,16 +951,26 @@
 ig, args
+, callback
 ) %7B%0A%0A  t
@@ -1778,50 +1778,8 @@
 );%0A%0A
-  if (isEmpty(assets)) %7B%0A    return;%0A  %7D%0A%0A
   co
@@ -1824,24 +1824,20 @@
 cy) =%3E (
-callback
+next
 ) =%3E %7B%0A 
@@ -3113,32 +3113,91 @@
 .ios);%0A      %7D%0A%0A
+      if (isEmpty(assets)) %7B%0A        return cb();%0A      %7D%0A%0A
       if (projec
@@ -3491,24 +3491,20 @@
  after, 
-callback
+next
 %5D);%0A  %7D)
@@ -3526,14 +3526,36 @@
 es(tasks
+, callback %7C%7C () =%3E %7B%7D
 );%0A%7D;%0A

Add optional callback for the link function